ExeCryptor v2.2.6虚拟机不完全分析


追ExeCryptor的stolen codes不大容易,跟一跟就会发现总是在某个循环里打转。碰到
类似这样的代码就表示进入循环了:

00587B19    BD 01000000             mov ebp,1
00587B1E    F0:0FC12D FCA95500      lock xadd dword ptr ds:[55A9FC],ebp       ; LOCK prefix

004FE66F    89EA                    mov edx,ebp
004FE671    80F2 9D                 xor dl,9D
004FE674    50                      push eax
004FE675    68 B4F5DE83             push 83DEF5B4
004FE67A    58                      pop eax
004FE67B  - E9 72C90700             jmp dumped_.0057AFF2
...
00519630    02D0                    add dl,al
00519632  - 0F80 82BAFDFF           jo dumped_.004F50BA
00519638    58                      pop eax
00519639    C0CA 07                 ror dl,7
0051963C    53                      push ebx
0051963D    68 EFBB226E             push 6E22BBEF
00519642  - E9 8EBBFDFF             jmp dumped_.004F51D5
...
00534234    23D3                    and edx,ebx
00534236    5B                      pop ebx
00534237    B8 00010000             mov eax,100
0053423C    E9 E1650700             jmp dumped_.005AA822


ExeCryptor的文档里使用了虚拟机一词:Protection options中的Virtualization level
定义了虚拟机保护级别。关于oep保护,文档的说明是:

Original entry point code will be transformed and protected like it is wrapped 
with crypt_start/crypt_end macros. Note: if you protect Delphi application you 
need to use EXECryptor.pas in your project for correct working of this option. 

也就是说,对oep代码的保护与使用SDK中的crypt_start/crypt_end宏是一样的。可以自己
写几句代码,使用SDK试试。最好就用Demo版,不选择Anti-debug,这样方便一些。Demo下的
虚拟机代码更简单,我没有细看,但想必不会因为protection level的不同而导致对opcode
进行不同的解释。

对虚拟机代码的分析我用的是dumped_.exe,这样干扰少些。


1. 虚拟机代码的基本结构

   在壳代码中定义以下数组:

   _2fimh7mp:005A3A18 RegEbp          dd 100h dup(0) 
   _2fimh7mp:005A3E18 RegEdi          dd 100h dup(0)
   _2fimh7mp:005A4220 RegEsi          dd 100h dup(0) 
   _2fimh7mp:005777EC RegEdx          dd 100h dup(0)
   _2fimh7mp:00577BEC RegEbx          dd 100h dup(0)
   _2fimh7mp:005A525C RegEcx          dd 100h dup(0)
   _2fimh7mp:005A4E54 RegEax          dd 100h dup(0)
   _2fimh7mp:005A4A48 RegEsp          dd 100h dup(0)

   用于保存进出虚拟机前后的寄存器。

   _2fimh7mp:005A463C DecryptedData   dd 100h dup(0)

   用于保存解出的opcode。

   _2fimh7mp:005A3908 busy            db 100  dup(0)

   这是个字节数组。所有的数组都包含256个item。ExeCryptor使用了多线程,不同线程中
   的代码都会调用虚拟机解释引擎。这个byte数组用于表示对应的slot(这个词是我瞎编的;-)
   是否可用,如果为1,代码将选择别的slot。

   下面的代码用来选择slot:

   00587B19    BD 01000000             mov ebp,1
   00587B1E    F0:0FC12D FCA95500      lock xadd dword ptr ds:[55A9FC],ebp       ; LOCK prefix
   004FE66F    89EA                    mov edx,ebp
   004FE671    80F2 9D                 xor dl,9D
   00519630    02D0                    add dl,al
   00519639    C0CA 07                 ror dl,7
   00534234    23D3                    and edx,ebx
   00534237    B8 00010000             mov eax,100
   005496E9    F0:0FB0A2 08395A00      lock cmpxchg byte ptr ds:[edx+5A3908],ah   ; LOCK prefix
   005496F1  - 0F84 E238F6FF           je <dumped_.loc_4ACFD9>
   005496F7    E9 00F30000             jmp dumped_.005589FC

   如果可用,ebp为index,用来访问上面所有的数组。

   虚拟机执行流程大致为:

   . 保存寄存器
   . 解密opcode及参数
   . 执行opcode
   . 恢复寄存器

   在执行过程中会检测TF标记,如果被跟踪,将破坏访问数据的指针。

   调用虚拟机执行代码的形式为:

   _2fimh7mp:0054E10E                 call    l_execute_pcode_2
   _2fimh7mp:0054E113                 dd 13F66194h  ; 密文dword1          
   _2fimh7mp:0054E117                 dd 652004C7h  ; 密文dword2

   call时指向密文数据的指针作为返回地址入栈。有1个dword的,也有2个dword。一般
   在IDA中看看后续的代码可以分清。在解码后的判断中也可识别。
 
   opcode大约30种,我没有跟完,只看了追stolen codes相关部分(所以不完全;-)。

   值得注意的是,在对密文opcode解码的过程中,执行到第4轮时使用了call(即前面的
   54E10E处)时的eflag值。不知道这里我是否理解错了。开始以为是执行到判断处时
   的eflag,但看了几次,的确用的是call时进入虚拟机后pushfd的值。

   虚拟机解释代码是固定的,但进入的路径(即前面call的地址)很多,我碰到了10余处。

   本来想跟出所有的PCode,写个程序patch dumped_.exe,这样一来就无法实现了。
   有趣的是stolen codes本身(即被crypt_start/crypt_end保护的代码)基本是明文,
   被虚拟机执行的都是壳代码,也许这可以映证前面的判断是对的,壳代码难以判断
   被抽取代码的运行状态。

   对这点我没有把握:-(。


2. 下面跟一个PCode执行实例

   _2fimh7mp:0057915A                 call    near ptr l_execute_pcode_1 ; 
   _2fimh7mp:0057915F                 dd 0BAC63BB5h           
   _2fimh7mp:00579163 dword_579163    dd 0D08139CEh           

   从call处跟进。

   0054D5E7  - 0F82 36BBFAFF           jb dumped_.004F9123
   0054D5ED    9C                      pushfd
   0054D5EE    50                      push eax
   0054D5EF    51                      push ecx
   0054D5F0  ^ E9 3458FDFF             jmp dumped_.00522E29
   ...
   005348F5    8BCA                    mov ecx,edx
   005348F7    870C24                  xchg dword ptr ss:[esp],ecx
   005348FA    56                      push esi
   005348FB    8BF5                    mov esi,ebp
   005348FD  - E9 0C48FCFF             jmp dumped_.004F910E
   
   004F910E    873424                  xchg dword ptr ss:[esp],esi
   004F9111    BD 01000000             mov ebp,1
   004F9116    F0:0FC12D FCA95500      lock xadd dword ptr ds:[55A9FC],ebp   ; LOCK prefix
   004F911E  - E9 A62A0A00             jmp dumped_.0059BBC9

   寻找可用的slot。

   004FE66F    89EA                    mov edx,ebp
   004FE671    80F2 9D                 xor dl,9D
   004FE674    50                      push eax
   004FE675    68 B4F5DE83             push 83DEF5B4
   004FE67A    58                      pop eax
   004FE67B  - E9 72C90700             jmp dumped_.0057AFF2
   ...
   00519630    02D0                    add dl,al
   00519632  - 0F80 82BAFDFF           jo dumped_.004F50BA
   00519638    58                      pop eax
   00519639    C0CA 07                 ror dl,7
   0051963C    53                      push ebx
   0051963D    68 EFBB226E             push 6E22BBEF
   00519642  - E9 8EBBFDFF             jmp dumped_.004F51D5
   ...
   00534234    23D3                    and edx,ebx
   00534236    5B                      pop ebx
   00534237    B8 00010000             mov eax,100
   0053423C    E9 E1650700             jmp dumped_.005AA822

   005496E9    F0:0FB0A2 08395A00      lock cmpxchg byte ptr ds:[edx+5A3908],ah    ; LOCK prefix
   005496F1  - 0F84 E238F6FF           je <dumped_.loc_4ACFD9>  ; slot空闲
   005496F7    E9 00F30000             jmp dumped_.005589FC

   004ACFD9 >  C1E2 02                 shl edx,2               ; *4,用于访问dword数组
   004ACFDC    E9 1F010000             jmp <dumped_.loc_4AD100>

   004AD100 >  89D5                    mov ebp,edx                    
   004AD102    8F85 183A5A00           pop dword ptr ss:[ebp+5A3A18]  ; 保存ebp
   004AD108    5A                      pop edx
   004AD109    872C24                  xchg dword ptr ss:[esp],ebp
   004AD10C    8BC5                    mov eax,ebp
   004AD10E  - E9 22ED0A00             jmp dumped_.0055BE35
   ...
   0055BE35    5D                      pop ebp
   0055BE36    89BD 183E5A00           mov dword ptr ss:[ebp+5A3E18],edi ; 保存edi
   0055BE3C    E8 1752FEFF             call dumped_.00541058

   逐个保存register,省略。进入是pushfd的值保存在edi内。
   
   ...
   0053B94F    8BC4                    mov eax,esp
   0053B951    8985 484A5A00           mov dword ptr ss:[ebp+5A4A48],eax ; 保存esp
   0053B957    50                      push eax
   0053B958    68 E8FFBEEE             push EEBEFFE8
   0053B95D    58                      pop eax
   0053B95E    81F0 51CB835E           xor eax,5E83CB51
   0053B964    E9 CDAD0100             jmp dumped_.00556736

   00556736    81C0 B3B41B50           add eax,501BB4B3
   0055673C    E8 0709FAFF             call dumped_.004F7048

   004F7048    871C24                  xchg dword ptr ss:[esp],ebx
   004F704B    5B                      pop ebx
   004F704C    870424                  xchg dword ptr ss:[esp],eax  ; push eax
   004F704F  - E9 F5E40500             jmp dumped_.00555549

   这是被混淆过的代码,eax最终为58E96C,作为返回地址入栈。

   00555549  ^\E9 8C34FEFF             jmp dumped_.005389DA ; 解码
   
   005389DA    8B06                    mov eax,dword ptr ds:[esi]  ; 取密文dword1
   005389DC    8BD0                    mov edx,eax
   005389DE    C1C8 10                 ror eax,10    ; *
   005389E1    66:C1C2 03              rol dx,3      ; *
   005389E5    E9 A3C50300             jmp dumped_.00574F8D
   ...
   0057B8BF    03D6                    add edx,esi   ; *
   0057B8C1    5E                      pop esi
   0057B8C2    57                      push edi
   0057B8C3    E8 A7CAFBFF             call dumped_.0053836F
   ...
   005A1129    81C7 E5BACC97           add edi,97CCBAE5
   005A112F    9D                      popfd
   005A1130    33D7                    xor edx,edi   ; *
   005A1132  ^ E9 2B05FDFF             jmp dumped_.00571662

   标有*的为解码代码,这里执行完1轮。共16轮(不会是什么密码学算法吧,眼拙没看出来)。

   004F8C79    57                      push edi  ; 第4轮时使用eflag
   004F8C7A    5A                      pop edx
   ...
   004F8AFD    84F1                    test cl,dh ; 检测TF位
   ...
   0054A6AA    0F95C1                  setne cl
   0054A6AD    D3C8                    ror eax,cl ; eax为上轮结果,搞点破坏
   0054A6AF    E9 83480200             jmp dumped_.0056EF37
   ...
   00589A79    23D7                    and edx,edi     ; 即and edx,000008C1
   005A96BE    81CA 3EF7FFFF           or edx,FFFFF73E ;
   005A96C4    23C2                    and eax,edx
  
   可以看到eflag为解码过程的确有影响。

   ...

   0057151F    33D7                    xor edx,edi
   00571521    5F                      pop edi
   00571522    66:33C2                 xor ax,dx
   00571525    8985 3C465A00           mov dword ptr ss:[ebp+5A463C],eax ;  保存解码结果
   0057152B    57                      push edi
   0057152C  ^ E9 92FAFEFF             jmp dumped_.00560FC3
   ...
   0058AE3C    03F7                    add esi,edi  ; add esi,4 移动指针->下1个密文dword
   ...
   00599C43    85C1                    test ecx,eax ; test eax,E0000000h

   检测解码结果,若真,代表某些特殊情况,会mov eax,3F,跳过参数处理。我只
   碰到过1次。3F似乎等于NOP。这里为false。

   00523DBB    85C2                    test edx,eax ; test edx,000000C0h
   00523DBD    5A                      pop edx
   00523DBE    0F84 356C0300           je dumped_.0055A9F9
   00523DC4  ^ E9 C5FDFFFF             jmp dumped_.00523B8E  ; 走这里

   若这个检测为true,表示有第2个dword需要处理。这里为true。

   00565180    8B16                    mov edx,dword ptr ds:[esi] ; 取第2个dword
   00565182    C1E8 08                 shr eax,8
   00565185    25 FF0F0000             and eax,0FFF
   ...
   00541AC7    23D1                    and edx,ecx ;  and  edx,000FFFFFh,取低20位
   ...
   00569C44    C1E2 0C                 shl edx,0C
   00569C47    09D0                    or eax,edx
   00569C49    8985 F87F5700           mov dword ptr ss:[ebp+577FF8],eax ;保存参数
   ...
   0051BF33    85C2                    test edx,eax ; test eax,00000080h
   0051BF35    5A                      pop edx
   0051BF36    0F84 BDEA0300           je dumped_.0055A9F9
   0051BF3C   /E9 02F10200             jmp dumped_.0054B043

   这里还有个检查,没看懂,如果51BF3C过去,似乎没什么实质的影响。

   ...
   0055A9F9    C3                      retn ; -> 58E96C 前面入栈的地址

   00582560    8B00                    mov eax,dword ptr ds:[eax] ; 取解码结果
   00582562    89C3                    mov ebx,eax ; 保存到ebx内
   00582564  ^ E9 F083FEFF             jmp dumped_.0056A959
   ...
   00584C9E    25 FF000000             and eax,0FF ; 最低字节为opcode类型
   00584CA3    E8 8E5DF9FF             call dumped_.0051AA36
   
   0051AA36    871C24                  xchg dword ptr ss:[esp],ebx
   0051AA39    5B                      pop ebx
   0051AA3A    C1EB 08                 shr ebx,8 ; ebx保留高24位
   0051AA3D    83F8 00                 cmp eax,0 ; 开始逐个判断opcode类型
   0051AA40    0F85 7C330500           jnz dumped_.0056DDC2
   0051AA46    E9 5DBB0100             jmp dumped_.005365A8

   这里opcode = 0x44,到这里:

   0057F98E    FFB5 F87F5700           push dword ptr ss:[ebp+577FF8] ; psuh解出的参数
   0057F994    83AD 484A5A00 04        sub dword ptr ss:[ebp+5A4A48],4 ; RegEsp保存值减4
   0057F99B    33D2                    xor edx,edx
   0057F99D    8BC7                    mov eax,edi
   0057F99F    E9 71340000             jmp dumped_.00582E15

   下面再次检测TF标记,恢复各寄存器后退出虚拟机。

   所以,opcode 0x44 = push imm32


3. 解码程序及stolen codes

   我写了个程序来帮助分析,包含了跟stolen codes用到的opcode。别的有兴趣的
   朋友自己解决了;-)。

   
   #include "stdafx.h"
   #include <stdio.h>
   #include <windows.h>

   #pragma pack(push)
   #pragma pack(1)

   typedef struct _SEED 
   {
  DWORD  dwSeed1;
  DWORD  dwSeed2;
   } SEED,*PSEED;

   typedef struct _CIPHER
   {
  DWORD  dwCipher1;
  DWORD  dwCipher2;
  DWORD  dwAddr;    // for debug,调用地址
   } CIPHER,*PCIPHER;

   #pragma pack(pop)

   SEED seeds[] = {
    {0x0012D6B7,0x3BA8D443}, {0x0895445C,0x125AF2C9},
    {0xA25E6A75,0x9261DE28}, {0xF967E9C6,0x931AB604},
    {0xE74E7E14,0x5C0720F2}, {0x1B032895,0x070A5A10},
    {0x429532EE,0xD3AA7A83}, {0x8E1FB211,0xB626113A},
    {0x2EE5122A,0xE1719AC6}, {0xF3E3C5AB,0xED28B712},
    {0x39502C58,0x536224AA}, {0x5B542ED3,0x9A2010C6},
    {0xC28D35D5,0x01434C1A}, {0x4349B4B5,0x05092628},
    {0x742FA3CB,0xF63BAA9C}, {0x8DCCEE88,0x962B5A4E},
    {0xFDD60B98,0x41FD37AD}, {0x5687BC5F,0x4BA7B00D},
    {0x2EE9B07D,0xDA1C3F7F}, {0xD533FEC6,0x64DF11FF},
    {0x5B786F02,0x7CFD0370}, {0xF602CC3E,0x6EDA23B9},
    {0x766CE221,0xFE351A14}, {0xB6C81829,0xA313B9D8},
    {0xAA9CC06B,0xF64D7535}, {0xB6FA5ED0,0x2477B71B},
    {0x263A9ED7,0x79A0668D}, {0x09E0F41A,0xEDC2AB89},
    {0x8731DF1C,0x47B09DD0}, {0x4FA72153,0x2BE82BC9},
    {0xACE42C68,0xA5875C45}, {0xF7CF2D22,0xC0CCC058}};
    

   DWORD Decrypt(DWORD dwCipher,PSEED pSeed,int nNumOfSeeds,DWORD dwEFlag)
   {
  // dwCipher: 密文数据
  // pSeed->dwSeed1: 用于add操作
  // pSeed->dwSeed2: 用于xor操作
  // dwEFlag: 执行call操作时的eflag值

  DWORD  dwData = dwCipher;
    
  for(int i = 0; i < nNumOfSeeds; i++)
  {
    
    DWORD dwSeed1 = (pSeed + i)->dwSeed1;
    DWORD dwSeed2 = (pSeed + i)->dwSeed2;

    _asm
    {
      mov    eax,dwData
      mov    edx,dwData
      
      ror       eax,0x10  
      rol       dx,0x3
      add    edx,dwSeed1
      xor    edx,dwSeed2
      xor       ax, dx
      mov    dwData,eax
    }
    
    // 在第4轮后,eflag参与了计算
    
    if(3 == i)
    {
       // 8C1 = 1000 1100 0001 -> OF SF ZF CF
       // 73E = 0111 0011 1110

                   // 保留了4个标记位信息

                   dwData &= (dwEFlag & 0x000008C1) | 0xFFFFF73E;
      
    }
  }
  
  return dwData;
   }


   int main(int argc, char* argv[])
   {
  CIPHER  stCipher;
    
  int nNumOfSeeds = sizeof(seeds) / sizeof(SEED);
  ZeroMemory(&stCipher,sizeof(stCipher));
  
  //

  stCipher.dwCipher1 = 0xBAC63BB5;  // 密文dword1
  stCipher.dwCipher2 = 0xD08139CE;  // 密文dword2
  stCipher.dwAddr = 0x57915F;    // 密文dword1地址,用于调试可以不设

  //
  DWORD dwEFlag = 0x00000246;    // 执行call时的eflag

  DWORD dwPlain = Decrypt(stCipher.dwCipher1,seeds,nNumOfSeeds,dwEFlag);

  printf("解码结果 = %08X\r\n",dwPlain);  

  // 检测解码结果
  
  DWORD  dwParameter = 0;
  BOOL  bParamAvailable = FALSE;
  // 
  
  if(dwPlain & 0xE0000000)  // 似乎是些特殊代码,跳过参数处理
  {
    dwPlain = 0x3F;  // 不需要参数处理
  }
  else
  {
    if(dwPlain & 0x000000C0)
    {
      bParamAvailable = TRUE;
      dwParameter = ((dwPlain >> 0x8) & 0xFFF) | 
        ((stCipher.dwCipher2 & 0x000FFFFF) << 0xC);
      
      // 注意还有个调整参数的动作(51BF33),似乎没有作用
    }
  }

  //

  BYTE cRegister = 0;
  PCHAR lpszRegister[] = {"eax","ecx","edx","ebx","esp","ebp","esi","edi"};


  switch (dwPlain & 0x000000FF)
  {
  case 0x0:  // push reg32

    printf("%08X: opcode 0x00 -> push %s\r\n",
      stCipher.dwAddr,
      lpszRegister[(dwPlain >> 0x8) & 0x7]);

    break;

  case 0x1:  // pop reg32

    printf("%08X: opcode 0x01 -> pop %s\r\n",
      stCipher.dwAddr,
      lpszRegister[(dwPlain >> 0x8) & 0x7]);
    
    break;
    
  case 0x2:  // mov reg32,reg32

    printf("%08X: opcode 0x02 -> mov %s,%s\r\n",
      stCipher.dwAddr,
      lpszRegister[(dwPlain >> 0xB) & 0x7],
      lpszRegister[(dwPlain >> 0x8) & 0x7]);
    
    break;
    
  case 0x3:
    break;

  case 0x4:
    break;

  case 0x5:  // xchg reg32,[esp]
    
    printf("%08X: opcode 0x05 -> xchg %s,[esp] 返回%08X\r\n",
      stCipher.dwAddr,
      lpszRegister[(dwPlain >> 0x8) & 0x7],
      stCipher.dwAddr + 4);

    break;

  case 0x6:  // ret

    printf("%08X: opcode 0x06 -> ret\r\n",stCipher.dwAddr);
    break;

  case 0x7:
    break;
    
  case 0xC0:  // jmp imm32
    
    printf("%08X: opcode 0xC0 -> jmp %08x\r\n",
      stCipher.dwAddr,
      dwParameter);
    
    break;
    
  case 0x82:
    break;
    
  case 0x44:  // push imm32
    
    printf("%08X: opcode 0x44 -> push立即数%08X,返回%08X\r\n",
      stCipher.dwAddr,
      dwParameter,
      stCipher.dwAddr + 8);
    
    break;


  case 0x84:  // 似乎和0x44相同? push imm32

    printf("%08X: opcode 0x84 -> push立即数%08X,返回%08X\r\n",
      stCipher.dwAddr,
      dwParameter,
      stCipher.dwAddr + 8);

    break;

  case 0x10:
    break;

  case 0x11:
    break;

  case 0x12:
    break;

  case 0x13:
    break;

  case 0x14:
    break;
    
  case 0x15:  // smc
    
    printf("%08X: opcode 0x15-> SMC修改%s所指代码为%02x,返回%08x\r\n",
      stCipher.dwAddr,
      lpszRegister[(dwPlain >> 0x8) & 0x7],
      (dwPlain >> 0xB) & 0x000000FF,
      stCipher.dwAddr + 4);

    break;

  case 0x16:
    break;

  case 0x17:
    break;

  case 0x83:

    // 这个不太肯定,处理代码直接测试ebp(当前slot的offset)而不是外界的数据
    // 004AC8F7 test    ebp, 1  ; 把offset当eflag测试CF,还是测试ebp的奇偶性(这样是jp)
    // 004AC8FD jz      loc_53D934  ; 这里直接返回call的下一行
    // 004AC903 jmp     loc_52467F  ; 这里跳到解出的参数地址

    // 不过对于ExeCryptor这种代码是干扰代码,最终会走到同样的地址
    // 所以这里是否精确不大重要,但跟踪时要跟到test的结果才知道返回哪里

    printf("%08X: opcode 0x83-> jc %08x\r\n",
      stCipher.dwAddr,dwParameter);

    break;

  
  case 0x20:  // 与0x20的比较是>=,(在5282C9)放在case内是否合适? 
    break;

  case 0x3F:

    printf("%08X: opcode 0x3F-> nop 返回%08x\r\n",
      stCipher.dwAddr,
      stCipher.dwAddr + 8);

    break;

    
  default:

    if(0x8 == (dwPlain & 0x000000F8))  
    {
      DWORD dwType = dwPlain & 0x7;
      DWORD dwSrcRegister = (dwPlain >> 0x8) & 0x7;
      DWORD dwDstRegister = (dwPlain >> 0xB) & 0x7;
      
      switch(dwType)  // 这里还有别的标记没有跟
      {
      case 0:  // add reg32,reg32 (575755)

        printf("%08X: opcode 0x8 -> add %s,%s,返回%08x\r\n",
          stCipher.dwAddr,
          lpszRegister[dwDstRegister],
          lpszRegister[dwSrcRegister],
          stCipher.dwAddr + 4);
        break;


      case 7:  // cmp reg32,reg32

        printf("%08X: opcode 0x8 -> cmp %s,%s,返回%08x\r\n",
          stCipher.dwAddr,
          lpszRegister[dwDstRegister],
          lpszRegister[dwSrcRegister],
          stCipher.dwAddr + 4);
        
        break;
        

      default:
        break;

      }
    }
    else
    {
      DWORD dwConvert = (dwPlain + 0x40) & 0x87;

      if(0x80 == dwConvert)  // add reg32,imm32
      {
        printf("%08X: opcode 0x48 -> add %s,%08x 返回%08x\r\n",
          stCipher.dwAddr,
          lpszRegister[((dwPlain & 0x000000FF) >> 3) & 0x7],
          dwParameter,
          stCipher.dwAddr + 8);
      }
      else if(0x81 == dwConvert)  // xor reg32,imm32
      {
        printf("%08X: opcode 0x49 -> xor %s,%08x 返回%08x\r\n",
          stCipher.dwAddr,
          lpszRegister[((dwPlain & 0x000000FF) >> 3) & 0x7],
          dwParameter,
          stCipher.dwAddr + 8);
      }
    }

    break;
  }

  return 0;
   }

   代码可能有错,在与0x11比较后,对解码结果的最低字节做了些变换进行判断,
   我放到default内了,可能不一定合适。不对的地方请包涵,指正。


   追出的ExeCryptor v2.2.6主程序stolen code为:


   004C74D0 >  55              push ebp                           
   004C74D1    8BEC            mov ebp,esp
   004C74D3    83C4 F4         add esp,-0C
   004C74D6    B8 20724C00     mov eax,dumped_.004C7220
   004C74DB    E8 6CF5F3FF     call <dumped_.Sysinit::__linkproc__ InitExe(void)>
   004C74E0 >  A1 D4634E00     mov eax,dword ptr ds:[4E63D4]                         
   004C74E5 >  8B00            mov eax,dword ptr ds:[eax]                                
   004C74E7    E8 0CB8F8FF     call <dumped_.Forms::TApplication::Initialize(void)>
   004C74EC    E8 C7A6FFFF     call <dumped_.sub_4C1BB8>
   004C74F1    A1 D4634E00     mov eax,dword ptr ds:[4E63D4]
   004C74F6    8B00            mov eax,dword ptr ds:[eax]
   004C74F8    BA 54754C00     mov edx,offset <dumped_.aExecryptor>      ; ASCII "EXECryptor"
   004C74FD    E8 FAB3F8FF     call <dumped_.Forms::TApplication::SetTitle(System::AnsiString)>
   004C7502    E8 B1A6FFFF     call <dumped_.sub_4C1BB8>
   004C7507    E8 ACA6FFFF     call <dumped_.sub_4C1BB8>
   004C750C >  8B0D B0614E00   mov ecx,dword ptr ds:[4E61B0]      
   004C7512    A1 D4634E00     mov eax,dword ptr ds:[4E63D4]
   004C7517    8B00            mov eax,dword ptr ds:[eax]
   004C7519    8B15 98214C00   mov edx,dword ptr ds:[<off_4C2198>]   
   004C751F    E8 ECB7F8FF     call <dumped_.Forms::TApplication::CreateForm(System::TMetaClass *,void *)>
   004C7524 >  A1 D4634E00     mov eax,dword ptr ds:[4E63D4]   
   004C7529    8B00            mov eax,dword ptr ds:[eax]
   004C752B    E8 60B8F8FF     call <dumped_.Forms::TApplication::Run(void)>
   004C7530    90              nop
   004C7531    90              nop
   004C7532    90              nop
   004C7533    90              nop
   004C7534    90              nop
   004C7535 >  90              nop            
   004C7536    90              nop
   004C7537    90              nop
   004C7538    90              nop
   004C7539    90              nop
   004C753A    90              nop
   004C753B    90              nop
   004C753C    90              nop
   004C753D    90              nop
   004C753E    90              nop
   004C753F    90              nop
   004C7540    90              nop
   004C7541    90              nop
   004C7542    90              nop
   004C7543    90              nop
   004C7544    90              nop
   004C7545    E8 D2C5F3FF     call <dumped_.System::__linkproc__ Halt0(void)>
   004C754A    0000            add byte ptr ds:[eax],al

   最后,修复oep处的stolen codes不足以解决跨平台问题。主程序很多地方使用
   了SDK,还会返回壳内(因此也还会启动那些anti-debug线程)。要想剥干净,可以
   把被抽掉的代码全部修复。

   我是不想玩了,已经快精神错乱了......