【文章标题】: VMProtect进入虚拟机时
【文章作者】: BUG
【作者邮箱】: bughoho@hotmail.com
【作者QQ号】: 393277421
【使用工具】: OD and IDA5.0
【操作平台】: XP SP2
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
  进入虚拟机时当前堆栈
  
  0013FEA8   00000000  偏移
  0013FEAC   7FFD6000  ebx
  0013FEB0   00000000  edi
  0013FEB4   00900C90  eax
  0013FEB8   00000246  e_flag
  0013FEBC   7C92EB94  edx
  0013FEC0   00000001  ecx
  0013FEC4   7C92EB94  edx
  0013FEC8   00000A28  esi
  0013FECC   0013FEE4  ebp
  
  0013FED4   93CA0014  Key
  
  根据forgot当时的堆栈
  
  [00000000]
  [EDI]
  [EFlags]
  [ESI]
  [EAX]
  [EBP]
  [EDX]
  [ECX]
  [EBX]
  [EAX]
  [VMSEG_CODE]
  [key0]//这里为E95243D1
  
  可以看出这个寄存器压入的位置是乱序的,并且在结构中的位置也是随机生成的
  
  进入虚拟机之前压入所有寄存器的值,
  然后用伪代码存放到自己的VM_Register中
  
  /*笔记*/
  esi                   存放的加密伪代码在当前位置
  edi                   寄存器的基地址
  ebx                   存放的加密伪代码起始地址,然后每解密一句就把bl变形,得到一个新的地址,
                        粗略看了下,好象当前解密的是1字节的就使用1字节解密,4字节就使用4字节解密
  /*笔记*/
  
  struct VM_Context
  {
     DWORD VM_Stack[256];//240*4+已占用堆栈长度*16,总长度1024字节
     //随机排序的
  //VM_Register:
     DWORD e_flag3;//+00  //多个地址保存多个符号?或者一行伪代码用一个地址保存符号?
     DWORD AddResult;//+04//加法结果?
     DWORD edi;//+08
     DWORD ;//+0c
     DWORD offset;//+10//弹出虚拟机进入时压入的偏移,通过这个偏移来找VM_CODE
     DWORD vmencode_code;//+14 //加密的伪代码存放地址.....又存放了符号?
     DWORD ebp;//+18
     DWORD XorResult;//+1c//异或结果
     DWORD eax;//+20
     DWORD ebx;//+24
     DWORD e_flag;//+28
     DWORD ecx;//+2c
     DWORD edx;//+30
     DWORD edx2;//+34
     DWORD e_flag2;//+38
     DWORD esi;//+3c
  }
  
    VMPBEGIN
    addr = (DWORD)str;
    _asm
    {
      pushad
      mov eax,addr
      nop
      popad
    }
    func2();
    VMPEND
  
  反汇编得到
  jump到执行虚拟机的地方
  
  .vmp0:0040F40B                 push    ebp
  .vmp0:0040F40C                 push    esi
  .vmp0:0040F40D                 push    edx
  .vmp0:0040F40E                 push    ecx
  .vmp0:0040F40F                 push    edx
  .vmp0:0040F410                 pushf
  .vmp0:0040F411                 push    eax
  .vmp0:0040F412                 push    edi
  .vmp0:0040F413                 push    ebx
  .vmp0:0040F414                 push    0
  .vmp0:0040F419                 mov     esi, [esp+28h]  ; esi = VMEncode_Code
  .vmp0:0040F41D                 mov     edx, offset VM_Stack ; 1024字节?
  .vmp0:0040F422                 call    ds:GetCurrentThreadId()
  .vmp0:0040F428                 mov     ebx, eax
  .vmp0:0040F42A                 mov     ecx, 100h
  .vmp0:0040F42F                 mov     edi, edx
  .vmp0:0040F431                 pushf
  .vmp0:0040F432                 cld
  .vmp0:0040F433                 repne scasd
  .vmp0:0040F435                 jz      short loc_40F444
  .vmp0:0040F435
  .vmp0:0040F437                 mov     eax, 100h
  .vmp0:0040F43C                 xchg    eax, ecx
  .vmp0:0040F43D                 mov     edi, edx
  .vmp0:0040F43F                 repne scasd
  .vmp0:0040F441                 mov     [edi-4], ebx    ; 堆栈第一项 = 当前线程ID
  .vmp0:0040F441
  .vmp0:0040F444
  .vmp0:0040F444 loc_40F444:                             ; CODE XREF: sub_40F40B+2Aj
  .vmp0:0040F444                 mov     ebp, edi
  .vmp0:0040F446                 sub     edi, edx
  .vmp0:0040F448                 shl     edi, 1
  .vmp0:0040F44A                 lea     edi, [edx+edi*8+3C0h] ; VM虚拟机环境寄存器基地址
  .vmp0:0040F451                 popf
  .vmp0:0040F451
  .vmp0:0040F452
  .vmp0:0040F452 loc_40F452:                             ; CODE XREF: sub_40F40B+581j
  .vmp0:0040F452                 mov     ebx, esi
  .vmp0:0040F454                 add     esi, [esp+0]    ; VMEncode_Code += 偏移,就是上面最后压入的一个0
  .vmp0:0040F454
  .vmp0:0040F457
  .vmp0:0040F457 executeEip:                             ; CODE XREF: sub_40F40B-E3EBj
  .vmp0:0040F457                                         ; sub_40F40B-5j
  .vmp0:0040F457                                         ; sub_40F40B+73j
  .vmp0:0040F457                                         ; sub_40F40B+81j
  .vmp0:0040F457                                         ; sub_40F40B+8Bj
  .vmp0:0040F457                                         ; sub_40F40B+92j ...
  .vmp0:0040F457                 mov     cl, [esi]       ; 解密当前的p_code
  .vmp0:0040F459                 add     cl, bl
  .vmp0:0040F45B                 xor     cl, 64h
  .vmp0:0040F45E                 neg     cl
  .vmp0:0040F460                 add     esi, 1
  .vmp0:0040F463                 sub     cl, 6Fh
  .vmp0:0040F466                 neg     cl
  .vmp0:0040F468                 xor     cl, 31h
  .vmp0:0040F46B                 add     bl, cl          ; 变换一下密钥
  .vmp0:0040F46D                 movzx   eax, cl         ; eax = 解密后的p_code
  .vmp0:0040F470                 jmp     dword ptr ds:JumpCommand[eax*4] ;
  
  看了下前面执行的一点点伪代码
  伪代码:
  vPop offset;//还没明白做什么的
  vPop ebx
  vPop edi
  vPop eax
  vPop e_flag
  vPop edx
  vPop ecx
  vPop edx
  vPop esi
  vPop ebp
  vPop VM_Context->vmencode_code
  vvPushdw dword      //压入一个DWORD到真实堆栈(写在伪代码中解密得到)
  vAdd [esp],[esp+4]   //add...
  vPop VM_Context->e_flag //保存符号
  
  vvPushdwResultAddr      //压入结果的地址
  vvPushdwResult          //压入结果
  
  vPop VM_Context->AddResult;//保存结果
  
  vvPushVM_Addresult           //从VM结构中将加法结果压入到真实堆栈
  
  vXor [esp],[esp-4]          //[esp-4]表示弹出的操作数..
                              //做异或操作,Key和这个操作数做异或操作
                              //VM的操作是 1 not 0;1 not 0;0 and 0 = 0; 1 xor 1 = 0;
  vPop VM_Context->XorResult//保存异或结果
  
  vvPushdw dword            //压入操作数
  vXor [esp],[esp-4]        //Key和这个操作数做异或操作
  vPop VM_Context->e_flag2  //把符号保存到这个地址.
  
  vvPushdw dword            //压入操作数
  vvPushVM_Addresult        //从VM结构中将加法结果压入到真实堆栈
  
  vXor [esp],[esp-4]        //做异或操作
  vPop VM_Context->e_flag3  //保存符号
  vXor [esp],[esp-4]        //Key和上一次保存的值做异或
  vPop VM_Context->e_flag2  //保存符号
  
  vvPushdwResultAddr        //压入结果的地址
  vvPushdwResult            //压入结果
  
  vXor [esp],[esp-4]        //Key和上一次保存的值做异或
  vPop VM_Context->e_flag*  //保存符号
  
  vvPushbyte                //压入操作数.长度byte
  
  vadd [esp],[esp-4]        //Key和上一次保存的值做异或
  vPop VM_Context->e_flag2  //保存符号
  
  ....单步时,OD卡住了....
  
  反正跟到这里时还没看到我想看到的伪代码.
  
--------------------------------------------------------------------------------
【经验总结】
  总结下,进入虚拟机时VMP随机把寄存器压入堆栈,存放当前ThreadID到VM_Stack.
  然后通过自己的伪代码取出存放到自己的VM_REG中去(当然这个随机顺序是生成EXE时定下来的).
  结构中的位置也是随机生成的.
  
  and al,0x3C     一得到VM结构一些寄存器或其他位置的骗移
  
  解密伪代码有一个庞大的算法,写出来不易..
  VM_CODE的地址当KEY,然后每解一个操作码会变形这个KEY的低字节,每一个伪代码都是不同的变形算法.
  这些涉及到立即数的地方应该是VMP随机生成的.
  换句话说,写解密算法得把每一个命令的函数里的那一小块加密代码全搞出来
  结论是..不是人干的活.
  
  VMP的伪代码都是在堆栈中做运算的,带vv的是涉及到传输VM数据的伪代码.
  另外相关伪代码会保存标志寄存器.防止出错.
  
  就研究了这么点...
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2007年03月15日 0:38:37