本文中的程序:http://www.fractalpc.com/resources/SetupFractalPC30.exe
取得完整的输入表:
断点:GetModuleHandleA,普通断点要下在函数末尾,否则会被检测到,当堆栈出现如下内容时,返回:
0012FF98   00459CF5  返回到 FractalP.00459CF5
0012FF9C   00000000
0012FFA0   00B20000
0012FFA4   0012FFE0  指向下一个 SEH 记录的指针
壳会检测单步执行,如果能避开检测的话,会来到这里:
0045B7F6    F6C3 02         test    bl, 2
0045B7F9    74 21           je      short 0045B81C        这里决定是否把函数代码抽走,改成jmp
0045B7FB    FFB5 DF1C0000   push    dword ptr [ebp+1CDF]
0045B801    6A 10           push    10
0045B803    50              push    eax
0045B804    FFB5 DF1C0000   push    dword ptr [ebp+1CDF]
下面是循环的底部:
0045B827    C1E2 02         shl     edx, 2
0045B82A    83C6 04         add     esi, 4
0045B82D    57              push    edi
0045B82E    51              push    ecx
0045B82F    6A 03           push    3                        push 3,所以每个函数会储存3次
0045B831    59              pop     ecx
0045B832    8D7E F4         lea     edi, dword ptr [esi-C]
0045B835    F3:AB           rep     stos dword ptr es:[edi]   这里储存需要用到的函数,数据窗口定位在edi上
0045B837    59              pop     ecx
0045B838    5F              pop     edi
0045B839    35 BB82506D     xor     eax, 6D5082BB
0045B83E    890413          mov     dword ptr [ebx+edx], eax
0045B841  ^ E9 58FFFFFF     jmp     0045B79E
0045B846    41              inc     ecx
0045B847  ^ E9 94FEFFFF     jmp     0045B6E0
0045B84C    FFB5 E31C0000   push    dword ptr [ebp+1CE3]     在这里下断,运行,数据窗口会看见函数列表,复制出来.

然后,找到oep,在oep处中断,把复制出来的输入函数列表挪到原来的输入表的位置,记住每个函数只复制一次就行了,不同的dll中间需要用一行00000000隔开,以利于区分.

壳没有抽取代码,其主要表现形式有三种:
第一种:
CALL [004XXXXX] 变成 CALL [00XXXXXX] 的形式,当然这个形式里面的 00XXXXXX 表现为不同的地址,这里的00XXXXXX是壳里的地址,由于我们跳开了抽取函数代码,所以最后会返回到函数的地址,修复的方法是找到所有这样的地址,然后跟踪一下
进入一个这样的call:
首先传递一些参数:
0045C013    6A 00           push    0
0045C015    9C              pushfd
0045C016    50              push    eax
0045C017    53              push    ebx
0045C018    8B5C24 10       mov     ebx, dword ptr [esp+10]
0045C01C    53              push    ebx
0045C01D    83EB 06         sub     ebx, 6
0045C020    68 E9260000     push    26E9
0045C025    68 0000B300     push    0B30000       变形call
0045C02A    C3              retn
来到00B30000,这里通过堆栈,利用产生的另外几个区段的数据计算出函数地址:
00B30000    55              push    ebp
00B30001    56              push    esi
00B30002    57              push    edi
00B30003    51              push    ecx
00B30004    52              push    edx
00B30005    50              push    eax
00B30006    BD 1A994500     mov     ebp, 45991A
00B3000B    016C24 18       add     dword ptr [esp+18], ebp
00B3000F    2B9D 962A0000   sub     ebx, dword ptr [ebp+2A96]
00B30015    8BBD DE2A0000   mov     edi, dword ptr [ebp+2ADE]
00B3001B    8B8D DA2A0000   mov     ecx, dword ptr [ebp+2ADA]
00B30021    8BC3            mov     eax, ebx
00B30023    2B85 D22A0000   sub     eax, dword ptr [ebp+2AD2]
00B30029    D3E8            shr     eax, cl
00B3002B    8B0487          mov     eax, dword ptr [edi+eax*4]
00B3002E    8BF0            mov     esi, eax
00B30030    81E6 FFFF0000   and     esi, 0FFFF
00B30036    C1E8 10         shr     eax, 10
00B30039    8BF8            mov     edi, eax
00B3003B    8D043E          lea     eax, dword ptr [esi+edi]
00B3003E    BA 06639227     mov     edx, 27926306
00B30043    D1E8            shr     eax, 1
00B30045    8BC8            mov     ecx, eax
00B30047    69C9 8466DA44   imul    ecx, ecx, 44DA6684
00B3004D    2BD1            sub     edx, ecx
00B3004F    8B8D FA2A0000   mov     ecx, dword ptr [ebp+2AFA]
00B30055    51              push    ecx
00B30056    8B0CC1          mov     ecx, dword ptr [ecx+eax*8]
00B30059    33CA            xor     ecx, edx
00B3005B    3BCB            cmp     ecx, ebx
00B3005D    59              pop     ecx
00B3005E    74 0C           je      short 00B3006C
00B30060    77 05           ja      short 00B30067
00B30062    8D78 01         lea     edi, dword ptr [eax+1]
00B30065  ^ EB D4           jmp     short 00B3003B
00B30067    8D70 FF         lea     esi, dword ptr [eax-1]
00B3006A  ^ EB CF           jmp     short 00B3003B
00B3006C    8B5C24 1C       mov     ebx, dword ptr [esp+1C]
00B30070    3354C1 04       xor     edx, dword ptr [ecx+eax*8+4]
00B30074    0FB61B          movzx   ebx, byte ptr [ebx]
00B30077    2BD3            sub     edx, ebx
00B30079    8BB5 EA2A0000   mov     esi, dword ptr [ebp+2AEA]
00B3007F    8B0496          mov     eax, dword ptr [esi+edx*4]
00B30082    35 67B6BDD0     xor     eax, D0BDB667
00B30087    50              push    eax
00B30088    8A00            mov     al, byte ptr [eax]
00B3008A    04 0F           add     al, 0F
00B3008C    74 11           je      short 00B3009F
00B3008E    F6D8            neg     al
00B30090    2C 23           sub     al, 23
00B30092    74 0B           je      short 00B3009F
00B30094    FEC8            dec     al
00B30096    74 07           je      short 00B3009F
00B30098    FEC8            dec     al
00B3009A    74 03           je      short 00B3009F
00B3009C    F8              clc
00B3009D    EB 1E           jmp     short 00B300BD
00B3009F    8B0424          mov     eax, dword ptr [esp]
00B300A2    8B00            mov     eax, dword ptr [eax]
00B300A4    330424          xor     eax, dword ptr [esp]
00B300A7    C1E8 18         shr     eax, 18
00B300AA    84C0            test    al, al
00B300AC  ^ 74 EE           je      short 00B3009C
00B300AE    8B0424          mov     eax, dword ptr [esp]
00B300B1    8A40 01         mov     al, byte ptr [eax+1]
00B300B4    34 C3           xor     al, 0C3
00B300B6  ^ 74 E4           je      short 00B3009C
00B300B8    83C4 04         add     esp, 4
00B300BB    50              push    eax
00B300BC    F9              stc
00B300BD    58              pop     eax
00B300BE    73 02           jnb     short 00B300C2
00B300C0    33C0            xor     eax, eax
00B300C2    50              push    eax
00B300C3    5B              pop     ebx
00B300C4    58              pop     eax
00B300C5    5A              pop     edx
00B300C6    59              pop     ecx
00B300C7    5F              pop     edi
00B300C8    5E              pop     esi
00B300C9    5D              pop     ebp
00B300CA    830424 08       add     dword ptr [esp], 8
00B300CE    C2 0400         retn    4
返回到这里:
0045C00F    5B              pop     ebx
0045C010    58              pop     eax
0045C011    9D              popfd
0045C012    C3              retn

这只是其中一种00XXXXXX地址 的 CALL [00XXXXXX] 的返回地址,如果里面的00XXXXXX 不同的话返回地址是不同的,跟一下就知道了,由于已经制造好了一个输入表,因此可以在这里比较输入表和这里的esp的值即产生的函数地址,相同的话,就把这句代码修复好.
比如:401000 call [00XXXXXX] 得到 esp 值是 74142545 而输入表 中 402000的地址也是 74142545 ,那就改成:401000 call [402000],具体操作可以通过path一段代码来自动完成.


第二种情况:
MOV EXX [004XXXXX] 变成 一个call
比如:mov     eax, dword ptr [402000]   mov     EDX, dword ptr [402000]  会变成:call 00XXXXXX ,当然这里的00XXXXXX 也不是唯一的。不过在运行中的表现是一样的。
 
跟进这个call:
同样先传递一些参数:
0045924A   /E9 19270000     jmp     0045B968

0045B968    9C              pushfd
0045B969    60              pushad
0045B96A    8B5C24 24       mov     ebx, dword ptr [esp+24]
0045B96E    43              inc     ebx
0045B96F    53              push    ebx
0045B970    83EB 06         sub     ebx, 6
0045B973    8BD3            mov     edx, ebx
0045B975    68 76200000     push    2076
0045B97A    68 0000B300     push    0B30000           这里同样是变形的call
0045B97F    C3              retn

不同的是从这个变形的call返回后来到这样的地址:
此时,ebx是运算得到的函数地址,而edx则决定了MOV EXX [004XXXXX] 中的EXX 是EAX还是EBX或者EDX、EDI、ESI、EPB。

从上到下的顺序是按照寄存器的顺序生成的
EAX 7FFDF000
ECX 0012FA04
EDX 00009164
EBX 00000000
ESP 0012F9E8
EBP 0045991A FractalP.0045991A
ESI 00B300D1
EDI 0012FD6C

0045B9AC    5A              pop     edx
0045B9AD    8D9402 0A000000 lea     edx, dword ptr [edx+eax+A]
0045B9B4    FFE2            jmp     edx
0045B9B6    895C24 1C       mov     dword ptr [esp+1C], ebx
0045B9BA    EB 27           jmp     short 0045B9E3
0045B9BC    895C24 18       mov     dword ptr [esp+18], ebx
0045B9C0    EB 21           jmp     short 0045B9E3
0045B9C2    895C24 14       mov     dword ptr [esp+14], ebx
0045B9C6    EB 1B           jmp     short 0045B9E3
0045B9C8    895C24 10       mov     dword ptr [esp+10], ebx
0045B9CC    EB 15           jmp     short 0045B9E3
0045B9CE    895C24 0C       mov     dword ptr [esp+C], ebx
0045B9D2    EB 0F           jmp     short 0045B9E3
0045B9D4    895C24 08       mov     dword ptr [esp+8], ebx
0045B9D8    EB 09           jmp     short 0045B9E3
0045B9DA    895C24 04       mov     dword ptr [esp+4], ebx
0045B9DE    EB 03           jmp     short 0045B9E3
0045B9E0    891C24          mov     dword ptr [esp], ebx
0045B9E3    61              popad
0045B9E4    FF4424 04       inc     dword ptr [esp+4]
0045B9E8    9D              popfd

现在可以在0045B9B4    FFE2            jmp     edx 下断点然后根据edx和ebx的值来修复了,或者改成jmp  008D0010到代码处,自动的修复:
例子代码如下:
008D0000    A1 FC018D00     mov     eax, dword ptr [8D01FC]  
这里的8D01FC处的地址为存放所有存在这种变形call的地址的第一个地址-4
008D0005    83C0 04         add     eax, 4
008D0008    A3 FC018D00     mov     dword ptr [8D01FC], eax
008D000D    FF20            jmp     dword ptr [eax]
008D000F    90              nop
008D0010    B9 00500310     mov     ecx, 10035000           这里10035000是IAT起始地址,不是前面说的程序的
008D0015    8B01            mov     eax, dword ptr [ecx]
008D0017    3BC3            cmp     eax, ebx
008D0019    90              nop
008D001A    74 0C           je      short 008D0028
008D001C    83C1 04         add     ecx, 4
008D001F    81F9 B8530310   cmp     ecx, 100353B8           这里100353B8是IAT结束地址+4,不是前面说的程序的
008D0025  ^ 75 EE           jnz     short 008D0015
008D0027    90              nop                            这里下断,防止壳产生IAT里没有的函数,不过不会用到。
下面开始根据edx的值判断mov EXX 的修复,其中的值是另外程序的,不是前面的地址,可以根据顺序改过来。
008D0028    81FA 9C250B10   cmp     edx, 100B259C 
008D002E    75 0F           jnz     short 008D003F
008D0030    A1 FC018D00     mov     eax, dword ptr [8D01FC]
008D0035    8B00            mov     eax, dword ptr [eax]
008D0037    C600 A1         mov     byte ptr [eax], 0A1
008D003A    8948 01         mov     dword ptr [eax+1], ecx
008D003D  ^ EB C1           jmp     short 008D0000
008D003F    81FA A2250B10   cmp     edx, 100B25A2
008D0045    75 11           jnz     short 008D0058
008D0047    A1 FC018D00     mov     eax, dword ptr [8D01FC]
008D004C    8B00            mov     eax, dword ptr [eax]
008D004E    66:C700 8B0D    mov     word ptr [eax], 0D8B
008D0053    8948 02         mov     dword ptr [eax+2], ecx
008D0056  ^ EB A8           jmp     short 008D0000
008D0058    81FA A8250B10   cmp     edx, 100B25A8
008D005E    75 11           jnz     short 008D0071
008D0060    A1 FC018D00     mov     eax, dword ptr [8D01FC]
008D0065    8B00            mov     eax, dword ptr [eax]
008D0067    66:C700 8B15    mov     word ptr [eax], 158B
008D006C    8948 02         mov     dword ptr [eax+2], ecx
008D006F  ^ EB 8F           jmp     short 008D0000
008D0071    81FA AE250B10   cmp     edx, 100B25AE
008D0077    75 11           jnz     short 008D008A
008D0079    A1 FC018D00     mov     eax, dword ptr [8D01FC]
008D007E    8B00            mov     eax, dword ptr [eax]
008D0080    66:C700 8B1D    mov     word ptr [eax], 1D8B
008D0085    8948 02         mov     dword ptr [eax+2], ecx
008D0088  ^ EB E5           jmp     short 008D006F
008D008A    81FA B4250B10   cmp     edx, 100B25B4
008D0090    75 11           jnz     short 008D00A3
008D0092    A1 FC018D00     mov     eax, dword ptr [8D01FC]
008D0097    8B00            mov     eax, dword ptr [eax]
008D0099    66:C700 8B25    mov     word ptr [eax], 258B
008D009E    8948 02         mov     dword ptr [eax+2], ecx
008D00A1  ^ EB E5           jmp     short 008D0088
008D00A3    81FA BA250B10   cmp     edx, 100B25BA
008D00A9    75 11           jnz     short 008D00BC
008D00AB    A1 FC018D00     mov     eax, dword ptr [8D01FC]
008D00B0    8B00            mov     eax, dword ptr [eax]
008D00B2    66:C700 8B2D    mov     word ptr [eax], 2D8B
008D00B7    8948 02         mov     dword ptr [eax+2], ecx
008D00BA  ^ EB E5           jmp     short 008D00A1
008D00BC    81FA C0250B10   cmp     edx, 100B25C0
008D00C2    75 11           jnz     short 008D00D5
008D00C4    A1 FC018D00     mov     eax, dword ptr [8D01FC]
008D00C9    8B00            mov     eax, dword ptr [eax]
008D00CB    66:C700 8B35    mov     word ptr [eax], 358B
008D00D0    8948 02         mov     dword ptr [eax+2], ecx
008D00D3  ^ EB E5           jmp     short 008D00BA
008D00D5    81FA C6250B10   cmp     edx, 100B25C6
008D00DB    75 11           jnz     short 008D00EE
008D00DD    A1 FC018D00     mov     eax, dword ptr [8D01FC]
008D00E2    8B00            mov     eax, dword ptr [eax]
008D00E4    66:C700 8B3D    mov     word ptr [eax], 3D8B
008D00E9    8948 02         mov     dword ptr [eax+2], ecx
008D00EC  ^ EB E5           jmp     short 008D00D3
008D00EE  - EB FE           jmp     short 008D00EE                   如果没有成功会跳到这里死掉。
运行后,程序可能暂时运行无相应,另外这段代码里没有判断是否读取完所有的变形的call的地址,所以直接用到最后会提示0000000无法读取。

第三种:

call到壳里的某个地址,其地址和第二种的接近,但是不可执行,应该是中间CCCCCCCCC代码的变形,不用理它。

如果程序过期,无法运行并脱壳,可以在这里跳开:
0045BB9B    F3:AB           rep     stos dword ptr es:[edi]
0045BB9D    53              push    ebx
0045BB9E    57              push    edi
0045BB9F    56              push    esi
0045BBA0    FF73 08         push    dword ptr [ebx+8]
0045BBA3    56              push    esi
0045BBA4    50              push    eax
0045BBA5    FFD2            call    edx                         进入这里
0045BBA7    83C4 0C         add     esp, 0C
0045BBAA    8985 E9220000   mov     dword ptr [ebp+22E9], eax
0045BBB0    5E              pop     esi
0045BBB1    5F              pop     edi
0045BBB2    5B              pop     ebx

10001934 >  55              push    ebp                              ; FractalP.0045991A
10001935    8BEC            mov     ebp, esp
10001937    83EC 10         sub     esp, 10
1000193A    8B45 10         mov     eax, dword ptr [ebp+10]
1000193D    FF05 E88B0410   inc     dword ptr [10048BE8]
10001943    833D E88B0410 0>cmp     dword ptr [10048BE8], 2
1000194A    53              push    ebx
1000194B    56              push    esi
1000194C    57              push    edi
1000194D    A3 90B50410     mov     dword ptr [1004B590], eax
10001952    7C 56           jl      short 100019AA  
上面NOP掉,没有任何提示,程序就运行了,壳的注册功能已经被废掉了                 
10001954    E8 286A0200     call    10028381
10001959    8BF0            mov     esi, eax

比较乱套,凑合着看吧。辛苦了。