首页
社区
课程
招聘
[原创]单步跟踪tElock v 0.98
发表于: 2008-4-15 16:06 13189

[原创]单步跟踪tElock v 0.98

2008-4-15 16:06
13189

继续:
00404C4B     8B85 80D24000  mov eax,dword ptr ss:[ebp+40D280]      ; ss:[00405BDE]=A1A6B146
00404C51     50             push eax
00404C52     35 5963677E    xor eax,7E676359
00404C57     2D 1FD2C1DF    sub eax,DFC1D21F                       ; 这两条是垃圾运算。
00404C5C     5B             pop ebx                                ; 堆栈 [0012FFA0]=A1A6B146
00404C5D     6A 01          push 1
00404C5F     58             pop eax                                ; eax=1
00404C60     6A 08          push 8
00404C62     59             pop ecx                                ; ecx=8
00404C63     0F85 12060000  jnz Hello.0040527B
00404C69     74 19          je short Hello.00404C84                        ;这里跳转了

给寄存器赋了几个新的值,继续

00404C83     90             nop
00404C84     8DB5 0DC34000  lea esi,dword ptr ss:[ebp+40C30D]      ; 地址=00404C6B, (ASCII "xPkWZ8Hau3EdYMn9")
00404C8A     8D7E 10        lea edi,dword ptr ds:[esi+10]          ; 地址=00404C7B
00404C8D     57             push edi
00404C8E     8AC3           mov al,bl                              ; bl=46 ('F')
00404C90     24 0F          and al,0F
00404C92     8A0406         mov al,byte ptr ds:[esi+eax]           ; ds:[00404C71]=48 ('H')
00404C95     AA             stos byte ptr es:[edi]                 ; 数据窗口跟随到00404C7B
00404C96     C1EB 04        shr ebx,4
00404C99   ^ E2 F3          loopd short Hello.00404C8E

这个循环经过运算把下面的数据,得到了几个新的数据:
00404C60                                19 78 50 6B 57 5A            xPkWZ
00404C70  38 48 61 75 33 45 64 59 4D 6E 39                 8Hau3EdYMn9
新的数据:
00404C70                                   48 5A 50 64 48             HZPdH
00404C80  45 50 45 90                                      EPE悕

00404C9B     58             pop eax                                ; 堆栈 [0012FFA0]=00404C7B (Hello.00404C7B)
00404C9C     50             push eax                                        ;00404C7B
00404C9D     6A 01          push 1                                                ;00000001
00404C9F     51             push ecx                                        ;00000000
00404CA0     FF95 F4BA4000  call dword ptr ss:[ebp+40BAF4]         ; ss:[00404452]=7C80EB3F (kernel32.CreateMutexA)

HANDLE CreateMutex(

    LPSECURITY_ATTRIBUTES lpMutexAttributes,        // pointer to security attributes
    BOOL bInitialOwner,        // flag for initial ownership
    LPCTSTR lpName         // pointer to mutex-object name  
   );
The CreateMutex function creates a named or unnamed mutex object.
原来上面循环计算出的数据是CreateMutexA的第三个参数mutex-object name:"HZPdHEPE "

00404CA6     66:2D 0000      sub ax,0                          ; 返回值在eax中
00404CAA     EB 01           jmp short Hello.00404CAD
00404CAC     90              nop
00404CAD     90              nop
00404CAE     EB 03           jmp short Hello.00404CB3
00404CB0     90              nop
00404CB1     90              nop
00404CB2     90              nop
00404CB3     85E4            test esp,esp
00404CB5     79 03           jns short Hello.00404CBA          ; 跳
00404CB7     90              nop
00404CB8     90              nop
00404CB9     90              nop
00404CBA     90              nop
00404CBB     EB 0D           jmp short Hello.00404CCA

纯垃圾指令

00404CCA     E8 0D010000     call Hello.00404DDC                       ;F7

前进

00404DDC    /EB 01           jmp short Hello.00404DDF
00404DDE    |90              nop
00404DDF    \1BC3            sbb eax,ebx
00404DE1     48              dec eax                           ; eax中还是CreateMutex返回的值
00404DE2     60              pushad
00404DE3     E8 06000000     call Hello.00404DEE

前进

00404DEE     2BDB            sub ebx,ebx                                        ;ebx清零
00404DF0     64:FF33         push dword ptr fs:[ebx]
00404DF3     64:8923         mov dword ptr fs:[ebx],esp        ; seh=00404DE8
00404DF6     F1              int1
00404DF7     F7F3            div ebx                                        ;除零异常

去seh地址下断点拦截下程序

00404DE8     8B6424 08       mov esp,dword ptr ss:[esp+8]      ; 修改堆栈指针
00404DEC     EB 0D           jmp short Hello.00404DFB

00404DFB     F9              stc
00404DFC     72 01           jb short Hello.00404DFF           ; 变相jmp
00404DFE     90              nop
00404DFF     35 601CC21C     xor eax,1CC21C60                  ; eax得到了这个值
00404E04     2BDB            sub ebx,ebx
00404E06     64:8F03         pop dword ptr fs:[ebx]
00404E09     5B              pop ebx                           ; 释放了seh
00404E0A     EB 01           jmp short Hello.00404E0D

释放了seh

00404E0C     90              nop
00404E0D     F8              clc
00404E0E     13C4            adc eax,esp                       ; eax=1CD51BE0
00404E10     60              pushad
00404E11     E8 06000000     call Hello.00404E1C

继续

00404E1C     64:67:FF36 0000 push dword ptr fs:[0]
00404E22     64:67:8926 0000 mov dword ptr fs:[0],esp          ; seh=00404E16
00404E28     9C              pushfd
00404E29     810C24 00010000 or dword ptr ss:[esp],100
00404E30     9D              popfd
00404E31     F8              clc

设置了seh后,程序又要设CPU的TP位了,它要自己产生单步异常,我们不能在单步跟下去了,这样会破坏了TP位,我们直接去SEH的处理地址00404E16哪里下断点,F9运行程序,壳自己产生异常后就会去哪里了

00404E16     8B6424 08       mov esp,dword ptr ss:[esp+8]      ; 修改指针
00404E1A     EB 1A           jmp short Hello.00404E36

00404E34     90              nop
00404E35     90              nop
00404E36     64:67:8F06 0000 pop dword ptr fs:[0]
00404E3C     58              pop eax
00404E3D     61              popad
00404E3E     EB 01           jmp short Hello.00404E41
00404E40     90              nop
00404E41     0BC2            or eax,edx
00404E43     E8 00000000     call Hello.00404E48
00404E48     EB 02           jmp short Hello.00404E4C

释放了seh

00404E4C     B8 FA60DB91     mov eax,91DB60FA                  ; 垃圾指令,因为这里给eax赋值,下面又从堆栈重新赋了值,这条指令就完全无用了
00404E51     8B3424          mov esi,dword ptr ss:[esp]        ; esi=00404E48 (Hello.00404E48)
00404E54     58              pop eax
00404E55     81EE 2B154100   sub esi,41152B
00404E5B     0BE4            or esp,esp
00404E5D     75 01           jnz short Hello.00404E60                ;esp的值根本就不可能为零,变相jmp

没有什么,继续

00404E5F     90              nop
00404E60     13C1            adc eax,ecx
00404E62     90              nop
00404E63     E8 0B000000     call Hello.00404E73

这个壳加了n多的无用东西,继续

00404E73     0D 45A51644     or eax,4416A545
00404E78     C3              retn

继续

00404E68     C1E8 25         shr eax,25
00404E6B     E9 0C000000     jmp Hello.00404E7C

就在运算折磨eax,多半是垃圾指令,我们只要看看下面就知道是不是了

00404E7C     1D E38F64A9     sbb eax,A9648FE3
00404E81     68 3834C539     push 39C53438
00404E86     5F              pop edi
00404E87     81F7 8A278439   xor edi,3984278A
00404E8D     0BE4            or esp,esp
00404E8F     75 01           jnz short Hello.00404E92                ;同上,变相jmp

现在连edi也折磨上了,继续

00404E91     90              nop
00404E92     F5              cmc
00404E93     13C1            adc eax,ecx
00404E95     03FE            add edi,esi
00404E97     B9 00000000     mov ecx,0
00404E9C     81C1 61F4AC23   add ecx,23ACF461
00404EA2     81F1 21F4AC23   xor ecx,23ACF421
00404EA8     EB 02           jmp short Hello.00404EAC
00404EAA     90              nop
00404EAB     90              nop
00404EAC     FC              cld
00404EAD     BA 0313B626     mov edx,26B61303
00404EB2     EB 02           jmp short Hello.00404EB6

ecx也跑不掉,什么也不做的就转来转去的在运算,如果是有效指令,至少也会要存一下的,继续

00404EB4     90              nop
00404EB5     90              nop
00404EB6     35 11BA860B     xor eax,0B86BA11
00404EBB     6BD2 1B         imul edx,edx,1B
00404EBE     3117            xor dword ptr ds:[edi],edx        ; 存了一个记一下[00404CCF]=15340151
00404EC0     C1C2 03         rol edx,3
00404EC3     F9              stc
00404EC4     83D2 3D         adc edx,3D
00404EC7     81C2 B8BBAF66   add edx,66AFBBB8
00404ECD     EB 01           jmp short Hello.00404ED0

继续折磨,我们也继续

00404ECF     90              nop
00404ED0     47              inc edi
00404ED1     47              inc edi
00404ED2     47              inc edi
00404ED3     47              inc edi
00404ED4     EB 01           jmp short Hello.00404ED7

这样的垃圾操作也来了,直接+4不加,偏要一个一个的加,不过我们要注意到前面它在[edi]存了一个值,现在edi+4就是把存储的指针加了4,这样它就指向下一个存储的地址了,od的数据窗口跟上找到这个地址。继续

00404ED6     90              nop
00404ED7     F9              stc
00404ED8     83E9 01         sub ecx,1                         ; ecx=40-1=3F,难道是计数器-1?继续看是不是循环存储
00404EDB     F9              stc
00404EDC     72 02           jb short Hello.00404EE0
00404EDE     90              nop
00404EDF     90              nop
00404EE0     1BC2            sbb eax,edx
00404EE2     F9              stc
00404EE3     2BC0            sub eax,eax                       ; eax清零了,前面对eax运算了好多,现在一个清零,前面的都成废指令了
00404EE5     48              dec eax
00404EE6     03C1            add eax,ecx
00404EE8   ^ 79 D1           jns short Hello.00404EBB          ; 真的有循环,

循环作用:把edx的运算数据存入[edi]中=00404CCF--00404DCE,大小等于ecx=0x40,F4到下一行跳出循环,继续

00404EEA    /EB 01           jmp short Hello.00404EED
00404EEC    |90              nop
00404EED    \83E8 F4         sub eax,-0C                       ; eax=0000000B
00404EF0     61              popad                             ; 上一条又是纯垃圾指令,
00404EF1     0BE4            or esp,esp
00404EF3     75 01           jnz short Hello.00404EF6          ; 变相jmp
00404EF5     90              nop
00404EF6     13C5            adc eax,ebp
00404EF8     FC              cld
00404EF9     C3              retn                                        ;返回到00404CCF

没什么东东,继续(它的返回地址,就是上一个循环刚才自修改的代码,就是解密一点,又去执行)

00404CCF     8BB5 7ED34000   mov esi,dword ptr ss:[ebp+40D37E]    ; esi=00000003
00404CD5     8BFD            mov edi,ebp
00404CD7     8D85 48C34000   lea eax,dword ptr ss:[ebp+40C348]    ; 地址=00404CA6, (ASCII "f-")
00404CDD     E8 00000000     call Hello.00404CE2

继续

00404CE2     5B              pop ebx                              ; ebx=00404CE2 (Hello.00404CE2)
00404CE3     83C3 06         add ebx,6
00404CE6     8918            mov dword ptr ds:[eax],ebx           ; 存起来了,在数据窗口跟随着
00404CE8     8B9D 62D34000   mov ebx,dword ptr ss:[ebp+40D362]    ; ss:[00405CC0]=00400000,这是程序的加载基址,我们来看看它要做什么了
00404CEE     8B87 86D34000   mov eax,dword ptr ds:[edi+40D386]    ; ds:[00405CE4]=00001000这个是RVA,我都想赶快看看,它要做什么了
00404CF4     03D8            add ebx,eax                          ; 加起来了,我们继续
00404CF6     8B8F 8AD34000   mov ecx,dword ptr ds:[edi+40D38A]    ; ds:[00405CE8]=00000200,给ecx赋值,难道它要循环作什么了??赶快继续
00404CFC     81E1 FFFFFF7F   and ecx,7FFFFFFF
00404D02     75 08           jnz short Hello.00404D0C             ; 比较看看ecx里有没有值,也就是有没有被改掉,我们当然没有动它,所以跳下去

貌似有重要的事情要发生了,继续

00404D0C     60              pushad                               ; 先存上一份
00404D0D     8BF3            mov esi,ebx                          ; ebx=00401000 (Hello.00401000)
00404D0F     BA 8652E540     mov edx,40E55286
00404D14     8BFE            mov edi,esi                          ; esi=edi=00401000 (Hello.00401000)
00404D16     0FB6DE          movzx ebx,dh                         ; ebx=00000052
00404D19     EB 01           jmp short Hello.00404D1C

它要做大事的可能了,继续

00404D1B     90              nop
00404D1C     69DB 73FA736A   imul ebx,ebx,6A73FA73
00404D22     AC              lods byte ptr ds:[esi]               ; 已经可以猜到了,如果你能够看到现在的话,看着od你就知道它下面要循环的把00401000开头的0x200的数据解密后存回去
00404D23     F6D0            not al
00404D25     34 09           xor al,9
00404D27     85FF            test edi,edi
00404D29     FEC0            inc al
00404D2B     34 A9           xor al,0A9
00404D2D     F6D0            not al
00404D2F     F6D0            not al
00404D31     02C2            add al,dl
00404D33     D2C8            ror al,cl
00404D35     8D1B            lea ebx,dword ptr ds:[ebx]
00404D37     85C9            test ecx,ecx
00404D39     F6D8            neg al
00404D3B     FEC0            inc al
00404D3D     90              nop
00404D3E     FC              cld
00404D3F     D2C8            ror al,cl
00404D41     F6D0            not al
00404D43     02C1            add al,cl
00404D45     34 E7           xor al,0E7
00404D47     02C1            add al,cl
00404D49     02C1            add al,cl
00404D4B     32C3            xor al,bl
00404D4D     02C3            add al,bl
00404D4F     D2C8            ror al,cl
00404D51     04 0B           add al,0B
00404D53     04 6D           add al,6D
00404D55     34 BD           xor al,0BD
00404D57     F8              clc
00404D58     90              nop
00404D59     85C0            test eax,eax
00404D5B     34 23           xor al,23
00404D5D     D2C8            ror al,cl
00404D5F     F6D8            neg al
00404D61     8D12            lea edx,dword ptr ds:[edx]
00404D63     34 05           xor al,5
00404D65     85FF            test edi,edi
00404D67     02C3            add al,bl
00404D69     8D09            lea ecx,dword ptr ds:[ecx]
00404D6B     0AD2            or dl,dl
00404D6D     34 1F           xor al,1F
00404D6F     0AC9            or cl,cl
00404D71     04 A9           add al,0A9
00404D73     F6D8            neg al
00404D75     02C1            add al,cl
00404D77     02C1            add al,cl
00404D79     04 01           add al,1
00404D7B     D2C8            ror al,cl
00404D7D     34 C5           xor al,0C5
00404D7F     D2C8            ror al,cl
00404D81     02C3            add al,bl
00404D83     04 8D           add al,8D
00404D85     02C2            add al,dl
00404D87     8D12            lea edx,dword ptr ds:[edx]           ; 地址=40E55286
00404D89     32C3            xor al,bl
00404D8B     85C0            test eax,eax
00404D8D     85FF            test edi,edi
00404D8F     D2C8            ror al,cl
00404D91     02C1            add al,cl
00404D93     34 FF           xor al,0FF
00404D95     F6D0            not al
00404D97     F8              clc
00404D98     90              nop
00404D99     04 E3           add al,0E3
00404D9B     0AC9            or cl,cl
00404D9D     90              nop
00404D9E     90              nop
00404D9F     90              nop
00404DA0     90              nop
00404DA1     90              nop
00404DA2     90              nop
00404DA3     90              nop
00404DA4     90              nop
00404DA5     32C2            xor al,dl
00404DA7     C0C8 01         ror al,1
00404DAA     AA              stos byte ptr es:[edi]               ; 运算结束,存起来了
00404DAB     69D2 A5B0CD4B   imul edx,edx,4BCDB0A5
00404DB1     F9              stc
00404DB2     72 02           jb short Hello.00404DB6
00404DB4     90              nop
00404DB5     90              nop
00404DB6     D1C2            rol edx,1
00404DB8     69DB 701FEE6A   imul ebx,ebx,6AEE1F70
00404DBE     03DA            add ebx,edx
00404DC0     49              dec ecx                              ; 计数器-1
00404DC1   ^ 0F8F 5BFFFFFF   jg Hello.00404D22                   ; 循环开始(不要忘记d 0040100)
00404DC7     8D85 4CC64000   lea eax,dword ptr ss:[ebp+40C64C]    ; 地址=00404FAA
00404DCD     FFE0            jmp eax                              ; 跳到上面的地址

循环作用:把00401000开头的0x200的数据解密
你只要看看注释的这三处就可以了,我对他的解密算法并不感兴趣,F4到下一行,继续

00404FAA     61              popad

恢复寄存器到循环前的关键值
EAX 00001000
ECX 00000200
EDX 7C92EB94 ntdll.KiFastSystemCallRet
EBX 00401000 Hello.00401000
ESP 0012FFA4
EBP FFFF895E
ESI 00000003
EDI FFFF895E
EIP 00404FAB Hello.00404FAB

00404FAB     56              push esi
00404FAC     57              push edi
00404FAD     8BFB            mov edi,ebx                          ; ebx=00401000 (Hello.00401000)
00404FAF     8BF7            mov esi,edi
00404FB1     8B9D 76D34000   mov ebx,dword ptr ss:[ebp+40D376]
00404FB7     AC              lods byte ptr ds:[esi]               ; 原来它还要在解密一次
00404FB8     EB 02           jmp short Hello.00404FBC
00404FBA     90              nop
00404FBB     90              nop
00404FBC     34 DB           xor al,0DB
00404FBE     2C F3           sub al,0F3
00404FC0     32C1            xor al,cl
00404FC2     8807            mov byte ptr ds:[edi],al
00404FC4     D2C8            ror al,cl
00404FC6     32C3            xor al,bl
00404FC8     021F            add bl,byte ptr ds:[edi]
00404FCA     12D9            adc bl,cl
00404FCC     F6C1 01         test cl,1
00404FCF     75 0F           jnz short Hello.00404FE0
00404FD1     D1EB            shr ebx,1
00404FD3     F7C3 08000000   test ebx,8
00404FD9     75 05           jnz short Hello.00404FE0
00404FDB     D3C3            rol ebx,cl
00404FDD     8D1CDB          lea ebx,dword ptr ds:[ebx+ebx*8]
00404FE0     AA              stos byte ptr es:[edi]               ; 运算结果存起来
00404FE1     49              dec ecx                              ; 计数器-1
00404FE2   ^ 7F D3           jg short Hello.00404FB7              ; 循环开始

循环作用:把00401000开头的0x200的数据再解密一次(可以想道我们已经到关键地方了,这里面存着的一定是关键数据)
跳出循环继续(你有没有在数据窗口跟踪??)

00404FE4     5F              pop edi
00404FE5     5E              pop esi
00404FE6     8B8F 8AD34000   mov ecx,dword ptr ds:[edi+40D38A]    ; ds:[00405CE8]=00000200
00404FEC     8B87 86D34000   mov eax,dword ptr ds:[edi+40D386]    ; ds:[00405CE4]=00001000
00404FF2     F7C1 00000080   test ecx,80000000
00404FF8     74 6A           je short Hello.00405064              ; 跳了,ecx又赋了0x200,难道还要解密一次?

继续

00405064     83C7 0C         add edi,0C
00405067     4E              dec esi                              ; esi=3-1=2
00405068     7E 06           jle short Hello.00405070
0040506A   ^ FFA5 48C34000   jmp dword ptr ss:[ebp+40C348]        ; ss:[00404CA6]=00404CE8 (Hello.00404CE8)

它跳回去了,回到了解密00401000的地方,不同的是这次是解密00402000的数据:

00404CE8     8B9D 62D34000   mov ebx,dword ptr ss:[ebp+40D362]    ; ss:[00405CC0]=00400000,这是程序的加载基址,我们来看看它要做什么了
00404CEE     8B87 86D34000   mov eax,dword ptr ds:[edi+40D386]    ; ds:[00405CE4]=00001000这个是RVA,我都想赶快看看,它要做什么了/这里的值不同了是00402000

其他的部分都是相同的esi是计数器00402000完了后又到00403000
用Peditor可以看到这是三个段,所以上面的代码就是解密壳的前三个段

解密后00401000的内容:
00401000  6A 00 68 00 30 40 00 68 0F 30 40 00 6A 00 E8 07  j.h.0@.h_0@.j.?
00401010  00 00 00 6A 00 E8 06 00 00 00 FF 25 08 20 40 00  ...j.?...%
00401020  FF 25 00 20 40 00 00 00 00 00 00 00 00 00 00 00  %. @...........
00401030  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
解密后00402000的内容:
00402000  76 20 00 00 00 00 00 00 5C 20 00 00 00 00 00 00  v ......\ ......
00402010  54 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00  T ..............
00402020  08 20 00 00 4C 20 00 00 00 00 00 00 00 00 00 00  ..L ..........
00402030  84 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00  ?... ..........
00402040  00 00 00 00 00 00 00 00 00 00 00 00 76 20 00 00  ............v ..
00402050  00 00 00 00 5C 20 00 00 00 00 00 00 BB 01 4D 65  ....\ ......?Me
00402060  73 73 61 67 65 42 6F 78 41 00 55 53 45 52 33 32  ssageBoxA.USER32
00402070  2E 64 6C 6C 00 00 75 00 45 78 69 74 50 72 6F 63  .dll..u.ExitProc
00402080  65 73 73 00 4B 45 52 4E 45 4C 33 32 2E 64 6C 6C  ess.KERNEL32.dll
00402090  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
解密后00403000的内容:
00403000  41 20 4D 65 73 73 61 67 65 42 6F 78 20 21 00 48  A MessageBox !.H
00403010  65 6C 6C 6F 2C 20 57 6F 72 6C 64 20 21 00 00 00  ello, World !...

00405064     83C7 0C         add edi,0C
00405067     4E              dec esi                              ; esi=3-1=2
00405068     7E 06           jle short Hello.00405070

00403000后,计数器esi=0,程序从这里跳,继续前进

00405070    /E9 02010000     jmp Hello.00405177

继续

00405177     8BB5 56D34000   mov esi,dword ptr ss:[ebp+40D356]    ; ss:[00405CB4]=00000000
0040517D     85F6            test esi,esi
0040517F     0F84 8B000000   je Hello.00405210

esi得到了0值,跳转了

00405210     8B95 62D34000   mov edx,dword ptr ss:[ebp+40D362]    ; ss:[00405CC0]=00400000 (Hello.00400000)
00405216     8BB5 52D34000   mov esi,dword ptr ss:[ebp+40D352]    ; ss:[00405CB0]=00002010
0040521C     85F6            test esi,esi
0040521E     0F84 06040000   je Hello.0040562A
00405224     03F2            add esi,edx                          ; esi=00402010
00405226     83A5 52D44000 0>and dword ptr ss:[ebp+40D452],0
0040522D     8B46 0C         mov eax,dword ptr ds:[esi+C]         ; ds:[0040201C]=0000206A
00405230     8366 0C 00      and dword ptr ds:[esi+C],0           ; 把上一条指令的值清零
00405234     85C0            test eax,eax
00405236     0F84 EE030000   je Hello.0040562A
0040523C     03C2            add eax,edx
0040523E     8BD8            mov ebx,eax                          ; eax=0040206A (Hello.0040206A), ASCII "USER32.dll"

通过计算esi为间接指针,eax指向了00402000段中的0040206A  ASCII "USER32.dll",得到连接库的字符串名

00405240     50              push eax
00405241     FF95 D0D24000   call dword ptr ss:[ebp+40D2D0]       ; kernel32.GetModuleHandleA
00405247     85C0            test eax,eax
00405249     0F85 BA000000   jnz Hello.00405309

使用GetModuleHandleA函数得到了USER32.DLL的句柄,测试是否为零
HMODULE GetModuleHandle(

    LPCTSTR lpModuleName         // address of module name to return handle for  
   );
The GetModuleHandle function returns a module handle for the specified module if the file has been mapped into the address space of the calling process.

00405309     8985 4AD34000  mov dword ptr ss:[ebp+40D34A],eax  ; 把句柄存到了[00405CA8]
0040530F     8D85 28CC4000  lea eax,dword ptr ss:[ebp+40CC28]  ; 地址=00405586, (ASCII "GDI32.DLLUSER32.DLLSHELL32.DLLKERNEL32.DLL")
00405315     60             pushad
00405316     33C9           xor ecx,ecx
00405318     2AF6           sub dh,dh
0040531A     8A13           mov dl,byte ptr ds:[ebx]
0040531C     F6C2 40        test dl,40
0040531F     74 03          je short Hello.00405324
00405321     80E2 5F        and dl,5F
00405324     0AD2           or dl,dl
00405326     74 1E          je short Hello.00405346                        ;最后从这里跳出了循环
00405328     43             inc ebx
00405329     FEC6           inc dh
0040532B     41             inc ecx
0040532C     3A5408 FF      cmp dl,byte ptr ds:[eax+ecx-1]
00405330   ^ 74 E8          je short Hello.0040531A
00405332     3A5408 08      cmp dl,byte ptr ds:[eax+ecx+8]
00405336   ^ 74 E2          je short Hello.0040531A

这个循环把00402000段中的user32.dll字符串和壳存储的比了一下,是不是一样

00405346     0AF6           or dh,dh
00405348     895424 1C      mov dword ptr ss:[esp+1C],edx
0040534C     61             popad
0040534D     C685 D7CC4000 >mov byte ptr ss:[ebp+40CCD7],0
00405354     74 24          je short Hello.0040537A
00405356     80EC 08        sub ah,8
00405359     B0 01          mov al,1
0040535B     FECC           dec ah
0040535D     74 04          je short Hello.00405363
0040535F     D0E0           shl al,1
00405361   ^ EB F8          jmp short Hello.0040535B
00405363     8AA5 52CC4000  mov ah,byte ptr ss:[ebp+40CC52]
00405369     0885 52CC4000  or byte ptr ss:[ebp+40CC52],al
0040536F     84C4           test ah,al
00405371     75 07          jnz short Hello.0040537A
00405373     808D D7CC4000 >or byte ptr ss:[ebp+40CCD7],1      ; 都是在存储了几个值
0040537A     33C0           xor eax,eax                        ; 把eax值赋0
0040537C     8803           mov byte ptr ds:[ebx],al           ; 用0把00402000段中的user32.dll字符串覆盖了
0040537E     43             inc ebx                            ; Hello.0040206A
0040537F     3803           cmp byte ptr ds:[ebx],al
00405381   ^ 75 F7          jnz short Hello.0040537A                        ;循环覆盖

继续

00405383     83A5 4ED34000 >and dword ptr ss:[ebp+40D34E],0    ; 清零后在这里放0
0040538A     8B95 62D34000  mov edx,dword ptr ss:[ebp+40D362]  ; ss:[00405CC0]=00400000 (Hello.00400000)
00405390     8B06           mov eax,dword ptr ds:[esi]         ; ds:[00402010]=00002054
00405392     85C0           test eax,eax
00405394     75 0B          jnz short Hello.004053A1

又要来00402000段作文章了

004053A1     03C2           add eax,edx
004053A3     0385 4ED34000  add eax,dword ptr ss:[ebp+40D34E]  ; 这个内存地址就是前面放了0的那个。
004053A9     8B18           mov ebx,dword ptr ds:[eax]         ; ebx得到了指向MessageBoxA函数字符串的RVA
004053AB     F7C3 00000080  test ebx,80000000                                ;检测高位,看看是序号还是名称,这个的话就是导入表的IMAGE_THUNK_DATA结构的检测
004053B1     74 06          je short Hello.004053B9

这个和前面的操作是相连的,只有把user32.dll字符串清零,才会在那个内存地址写0,这里也才不会出错

004053B9     8B7E 10        mov edi,dword ptr ds:[esi+10]
004053BC     03FA           add edi,edx                        ; edi=00402008,这里存储的是MessageBoxA函数字符串的RVA
004053BE     80A5 D6CC4000 >and byte ptr ss:[ebp+40CCD6],0FF   ; ss:[00405634]=0xFF
004053C5     0F84 30010000  je Hello.004054FB
004053CB     80A5 D7CC4000 >and byte ptr ss:[ebp+40CCD7],0FF   ; ss:[00405635]=0xFF
004053D2     0F84 23010000  je Hello.004054FB
004053D8     89BD 5AD44000  mov dword ptr ss:[ebp+40D45A],edi  ; edi=00402008
004053DE     8B85 52D44000  mov eax,dword ptr ss:[ebp+40D452]  ; eax=00000000
004053E4     40             inc eax
004053E5     0F84 10010000  je Hello.004054FB
004053EB     48             dec eax
004053EC     0F85 B2000000  jnz Hello.004054A4                 ; 测试eax是否为零居然这样测试

还是在00402000段的这几个字符串绕着,但是它还没有装载源程序的函数,我们继续

004053F2     60             pushad
004053F3     8BF7           mov esi,edi                        ; edi=00402008
004053F5     2BC0           sub eax,eax
004053F7     40             inc eax
004053F8     833F 00        cmp dword ptr ds:[edi],0
004053FB     8D7F 04        lea edi,dword ptr ds:[edi+4]       ; 地址=0040200C
004053FE   ^ 75 F7          jnz short Hello.004053F7
00405400     48             dec eax
00405401     0F84 EC000000  je Hello.004054F3
00405407     8BD8           mov ebx,eax
00405409     6BC0 31        imul eax,eax,31

计算了一下,继续

0040540C     6A 04          push 4
0040540E     68 00100000    push 1000
00405413     50             push eax                                ;
00405414     6A 00          push 0
00405416     FF95 ECBA4000  call dword ptr ss:[ebp+40BAEC]     ; ss:[0040444A]=7C809A81 (kernel32.VirtualAlloc)
0040541C     85C0           test eax,eax
0040541E     0F84 CF000000  je Hello.004054F3

eax成了申请的大小,原来前面是根据计算看看要申请多大的内存,重要的了,申请了一块内存并测试是否成功
LPVOID VirtualAlloc(

    LPVOID lpAddress,        // address of region to reserve or commit  
    DWORD dwSize,        // size of region
    DWORD flAllocationType,        // type of allocation
    DWORD flProtect         // type of access protection
   );       
The VirtualAlloc function reserves or commits a region of pages in the virtual address space of the calling process. Memory allocated by this function is automatically initialized to zero.

00405424     8BFE           mov edi,esi
00405426     8BCB           mov ecx,ebx                        ; ebx=00000001
00405428     8BF8           mov edi,eax                        ; eax=003C0000这个是申请的内存的句柄
0040542A     8985 56D44000  mov dword ptr ss:[ebp+40D456],eax  ; 句柄存起来
00405430     8BCB           mov ecx,ebx
00405432     6BDB 29        imul ebx,ebx,29
00405435     03DF           add ebx,edi                        ; 句柄值+29=003C0000
00405437     891C24         mov dword ptr ss:[esp],ebx
0040543A     B0 B8          mov al,0B8                         ; eax=003C00B8

把句柄折腾了一番,该存的存起来,继续

0040543C     6A 00          push 0
0040543E     50             push eax
0040543F     53             push ebx
00405440     0FB74424 08    movzx eax,word ptr ss:[esp+8]
00405445     50             push eax                           ; 这里压0
00405446     8D85 14BB4000  lea eax,dword ptr ss:[ebp+40BB14]  ; 地址=00404472
0040544C     0FB618         movzx ebx,byte ptr ds:[eax]        ; ds:[00404472]=08 (Backspace)
0040544F     FF0C24         dec dword ptr ss:[esp]             ; 这里-1=0xFFFFFFFF
00405452     7E 09          jle short Hello.0040545D                ;跳了

压了好几个值,记以下:
0012FF74   FFFFFFFF
0012FF78   003C0029
0012FF7C   003C00B8
0012FF80   00000000

0040545D     40             inc eax                            ; eax=00404473
0040545E     8A38           mov bh,byte ptr ds:[eax]           ; ds:[00404473]=0B
00405460     883F           mov byte ptr ds:[edi],bh           ; 这句很关键它的存储位置就是壳申请的内存
00405462     47             inc edi
00405463     FECB           dec bl
00405465   ^ 7F F6          jg short Hello.0040545D

这个很关键,她用bl作计数器,把下面的数据,移到了申请的内存:
00404470           0B E4 75 01 EB C1 E0 56                    

00405467     5B             pop ebx
00405468     5B             pop ebx
00405469     58             pop eax
0040546A     AA             stos byte ptr es:[edi]             ; 又接着移进去了一个双字,不要忘了数据窗口d 003C0000看着壳申请的这块内存
0040546B     FF0424         inc dword ptr ss:[esp]
0040546E     832424 0F      and dword ptr ss:[esp],0F
00405472     4B             dec ebx
00405473     891F           mov dword ptr ds:[edi],ebx
00405475     43             inc ebx
00405476     83C3 04        add ebx,4
00405479     83C7 04        add edi,4                          ; 增加存储地址
0040547C     B8 40FF30C3    mov eax,C330FF40
00405481     AB             stos dword ptr es:[edi]
00405482     B0 B8          mov al,0B8
00405484     49             dec ecx
00405485   ^ 7F B7          jg short Hello.0040543E

在这里又相继往003C0000里面存了几个字节,现在里面如下:
003C0000  0B E4 75 01 EB C1 E0 56 B8 28 00 3C 00 40 FF 30  鋟_肓郪?.<.@0
003C0010  C3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ?..............
继续

00405487     AA             stos byte ptr es:[edi]
00405488     E8 00000000    call Hello.0040548D                ; F7
0040548D     58             pop eax
0040548E     AB             stos dword ptr es:[edi]
0040548F     B8 90FF30C3    mov eax,C330FF90
00405494     AB             stos dword ptr es:[edi]
00405495     58             pop eax
00405496     61             popad

在这里又继续往003C0000里面存了一些数据,可以看看,这些数据的来源很广泛,来自好几个地方,还有常量,这么多分散的数据集聚了起来,在一个动态分配的内存空间内,到底壳要做什么了呢?
003C0000的内容如下:
003C0000  0B E4 75 01 EB C1 E0 56 B8 28 00 3C 00 40 FF 30  鋟_肓郪?.<.@0
003C0010  C3 B8 8D 54 40 00 90 FF 30 C3 00 00 00 00 00 00  酶峊@.?0?.....

00405497     83A5 FBCA4000 >and dword ptr ss:[ebp+40CAFB],0    ; ss:[00405459]=00000000
0040549E     89BD 52D44000  mov dword ptr ss:[ebp+40D452],edi  ; edi=003C0029
004054A4     8D85 14BB4000  lea eax,dword ptr ss:[ebp+40BB14]  ; 地址=00404472
004054AA     FFB5 FBCA4000  push dword ptr ss:[ebp+40CAFB]     ; ss:[00405459]=00000000
004054B0     0FB608         movzx ecx,byte ptr ds:[eax]        ; ds:[00404472]=08 (Backspace)
004054B3     FF0C24         dec dword ptr ss:[esp]
004054B6     7E 05          jle short Hello.004054BD

操作存存取取得搞了一下,继续

004054BD     890C24         mov dword ptr ss:[esp],ecx         ; ecx=00000008
004054C0     FF85 FBCA4000  inc dword ptr ss:[ebp+40CAFB]      ; ss:[00405459]=00000000+1
004054C6     83A5 FBCA4000 >and dword ptr ss:[ebp+40CAFB],0F
004054CD     8BBD 52D44000  mov edi,dword ptr ss:[ebp+40D452]  ; ss:[00405DB0]=003C0029
004054D3     8B85 5AD44000  mov eax,dword ptr ss:[ebp+40D45A]  ; ss:[00405DB8]=00402008
004054D9     0385 4ED34000  add eax,dword ptr ss:[ebp+40D34E]  ; ss:[00405CAC]=00000000
004054DF     8B8D 56D44000  mov ecx,dword ptr ss:[ebp+40D456]  ; ss:[00405DB4]=003C0000
004054E5     8908           mov dword ptr ds:[eax],ecx
004054E7     58             pop eax
004054E8     83C0 09        add eax,9
004054EB     0185 56D44000  add dword ptr ss:[ebp+40D456],eax
004054F1     EB 08          jmp short Hello.004054FB

在这里还是在搞一些运算,我们可以观察到,每次完成一部分操作,壳都会往一些类似于ss:[ebp+40D34E]的地方存入一些数据,或进行一些其他的操作,然后到后面的操作又依赖于他们,这些地址都是用来存标志,存暂时数据等等的。

004054FB     03BD 4ED34000  add edi,dword ptr ss:[ebp+40D34E]
00405501     85DB           test ebx,ebx
00405503     0F84 C7000000  je Hello.004055D0
00405509     F7C3 00000080  test ebx,80000000
0040550F     6A 00          push 0
00405511     75 0F          jnz short Hello.00405522
00405513     8D5C13 02      lea ebx,dword ptr ds:[ebx+edx+2]   ; 地址=0040205E, (ASCII "MessageBoxA")终于熬到这里了,壳开始读原程序的函数字符串了
00405517     803B 00        cmp byte ptr ds:[ebx],0                ;测试有没有读到
0040551A     0F84 93000000  je Hello.004055B3
00405520     EB 45          jmp short Hello.00405567

开头的运算是在得到指向函数字符串地址的RVA

00405567     81E3 FFFFFF7F  and ebx,7FFFFFFF
0040556D     53             push ebx
0040556E     FFB5 4AD34000  push dword ptr ss:[ebp+40D34A]     ; 地址里就是前面得到的user32.dll的句柄
00405574     FF95 E0BA4000  call dword ptr ss:[ebp+40BAE0]     ; kernel32.GetProcAddress
0040557A     40             inc eax
0040557B     48             dec eax                            ; 这里测试一下又没有成功
0040557C     75 33          jnz short Hello.004055B1

终于调用GetProcAddress函数来动态的获取函数了
FARPROC GetProcAddress(

    HMODULE hModule,        // handle to DLL module  
    LPCSTR lpProcName         // name of function
   );
The GetProcAddress function returns the address of the specified exported dynamic-link library (DLL) function.
我们要关注的是它会把得到的函数地址放到什么地方,也就是恢复后的源程序IAT地址了

004055B1     8907           mov dword ptr ds:[edi],eax         ; 存到了003C0029
004055B3     58             pop eax
004055B4     48             dec eax                            ; eax=0xFFFFFFFF
004055B5     74 0D          je short Hello.004055C4
004055B7     40             inc eax
004055B8     F8             clc
004055B9     66:8943 FE     mov word ptr ds:[ebx-2],ax
004055BD     8803           mov byte ptr ds:[ebx],al
004055BF     43             inc ebx
004055C0     3803           cmp byte ptr ds:[ebx],al
004055C2   ^ 75 F9          jnz short Hello.004055BD           ; 用0把00402000段中的MessageBoxA字符串覆盖了
004055C4     8385 4ED34000 >add dword ptr ss:[ebp+40D34E],4    ; ss:[00405CAC]=00000004
004055CB   ^ E9 BAFDFFFF    jmp Hello.0040538A

把地址先放到动态内存里,同时毁灭痕迹,那个动态内存真是关键的关键了,里面的内容包罗万象
然后跑回去从读取函数的那里又执行了一遍,又存了些数据进003C0000,里面现在如下:
003C0000  0B E4 75 01 EB C1 E0 56 B8 28 00 3C 00 40 FF 30  鋟_肓郪?.<.@0
003C0010  C3 B8 8D 54 40 00 90 FF 30 C3 00 00 00 00 00 00  酶峊@.?0?.....
003C0020  00 00 00 00 00 00 00 00 00 0B 05 D5 77 00 00 00  ........._誻...

004055D0     83C6 14        add esi,14
004055D3     8B95 62D34000  mov edx,dword ptr ss:[ebp+40D362]                ;edx=004000000
004055D9   ^ E9 48FCFFFF    jmp Hello.00405226

这次跑得更远,回去从获得连接库的地方又执行一次,这次是去获得kernel32.dll库,它又分配了一块内存句柄是003CD0000,获取了kernel32.dll库的ExitProcess函数
00405503    /0F84 C7000000  je Hello.004055D0
这一句话检测这个库的函数完了没有,壳就是一个库就申请一块内存,一块内存里就存这个库的所有函数地址,最后又来到了这里:
004055D0     83C6 14        add esi,14
004055D3     8B95 62D34000  mov edx,dword ptr ss:[ebp+40D362]                ;edx=004000000
004055D9   ^ E9 48FCFFFF    jmp Hello.00405226
还要回去看看连接库是不是都加载完了
00405226     83A5 52D44000 >and dword ptr ss:[ebp+40D452],0
0040522D     8B46 0C        mov eax,dword ptr ds:[esi+C]       ; ds:[0040201C]=0000206A
00405230     8366 0C 00     and dword ptr ds:[esi+C],0         ; 把上一条指令的值清零
00405234     85C0           test eax,eax                                ;eax=0,已经没有了
00405236     0F84 EE030000  je Hello.0040562A                        ;跳
这里检测到连接库已经完了,跳转到0040562A执行

0040562A     8BBD 5AD34000  mov edi,dword ptr ss:[ebp+40D35A]  ; edi=00000000
00405630     85FF           test edi,edi
00405632     EB 03          jmp short Hello.00405637
00405634     90             nop
00405635     01EB           add ebx,ebp
00405637     74 32          je short Hello.0040566B                        ;跳

继续

0040566B     B9 03030000    mov ecx,303                        ; 给ecx赋值,难道要循环了?
00405670     8DBD 30CD4000  lea edi,dword ptr ss:[ebp+40CD30]  ; 地址=0040568E
00405676     8BF7           mov esi,edi
00405678     8A140E         mov dl,byte ptr ds:[esi+ecx]       ; ds:[00405991]=D7
0040567B     8B9D 7AD34000  mov ebx,dword ptr ss:[ebp+40D37A]  ; ss:[00405CD8]=36EB9B3B
00405681     AC             lods byte ptr ds:[esi]
00405682     32C3           xor al,bl
00405684     D1CB           ror ebx,1
00405686     6BDB 11        imul ebx,ebx,11
00405689     32C2           xor al,dl
0040568B     AA             stos byte ptr es:[edi]
0040568C   ^ E2 F3          loopd short Hello.00405681         ; 一段自修改代码,还原的数据就是循环下面的代码,我们可以看到的

循环作用:修改代码,范围:0040568E--00405990,大小0x303,跳出循环

0040568E     FF95 F8BA4000  call dword ptr ss:[ebp+40BAF8]     ; kernel32.GetCurrentProcessId

The GetCurrentProcessId function returns the process identifier of the calling process.

DWORD GetCurrentProcessId(VOID)

00405694     8BD8           mov ebx,eax
00405696     50             push eax
00405697     6A 00          push 0
00405699     68 FF0F1F00    push 1F0FFF
0040569E     FF95 FCBA4000  call dword ptr ss:[ebp+40BAFC]     ; kernel32.OpenProcess
004056A4     40             inc eax
004056A5     48             dec eax
004056A6     74 2B          je short Hello.004056D3                        ;测试函数是否执行成功

The OpenProcess function returns a handle of an existing process object.

HANDLE OpenProcess(

    DWORD dwDesiredAccess,        // access flag
    BOOL bInheritHandle,        // handle inheritance flag
    DWORD dwProcessId         // process identifier
   );

得到自身的进程标志符,然后打开它(它要做什么呢?)。

004056A8     6A 00          push 0
004056AA     54             push esp                                ;0012FFA0
004056AB     6A 04          push 4
004056AD     68 00100000    push 1000
004056B2     FFB5 62D34000  push dword ptr ss:[ebp+40D362]        ;00400000
004056B8     50             push eax                                ;就是前面获得的自身进程句柄
004056B9     FF95 00BB4000  call dword ptr ss:[ebp+40BB00]     ; kernel32.VirtualProtectEx
004056BF     83C4 04        add esp,4                                ;修改堆栈指针
004056C2     40             inc eax
004056C3     48             dec eax
004056C4     74 0D          je short Hello.004056D3                ;监测函数是否成功

The VirtualProtectEx function changes the access protection on a region of committed pages in the virtual address space of a specified process. Note that this function differs from VirtualProtect, which changes the access protection on the calling process only.

BOOL VirtualProtectEx(

    HANDLE hProcess,        // handle of process
    LPVOID lpAddress,        // address of region of committed pages
    DWORD dwSize,        // size of region
    DWORD flNewProtect,        // desired access protection
    PDWORD lpflOldProtect         // address of variable to get old protection  
   );       

把00401000段的读写属性更改

004056C6     8BBD 62D34000  mov edi,dword ptr ss:[ebp+40D362]  ; ss:[00405CC0]=00400000 (Hello.00400000)
004056CC     037F 3C        add edi,dword ptr ds:[edi+3C]      ; ds:[0040003C]=000000C0,00400000就是文件的PE结构,+3C就可以定为到PE文件头了
004056CF     66:0967 06     or word ptr ds:[edi+6],sp          ; 把IMAGE_FILE_HEADER结构的NumberOfSections字段修改为FFA4
004056D3     C1EE 00        shr esi,0
004056D6     F8             clc
004056D7     55             push ebp                           ; ebp是壳定位数据的基址
004056D8     E8 2D000000    call Hello.0040570A

0040570A     58             pop eax
0040570B     50             push eax                            ; eax=004056DD (Hello.004056DD)
0040570C     830424 37      add dword ptr ss:[esp],37           ; 0012FF9C   00405714  
00405710     FFE0           jmp eax

这里,修改了堆栈的值,继续

004056DD     E8 00000000    call Hello.004056E2
004056E2     2BC0           sub eax,eax                         ; eax=00000000
004056E4     830424 1D      add dword ptr ss:[esp],1D
004056E8     64:FF30        push dword ptr fs:[eax]
004056EB     64:8920        mov dword ptr fs:[eax],esp          ; seh=004056ff
004056EE    /EB 01          jmp short Hello.004056F1
004056F0    |90             nop
004056F1    \8DC0           lea eax,eax                         ; 非法使用寄存器/这里异常了

seh=004056FF异常要来了,到seh地址拦截程序

004056FD     90             nop
004056FE     90             nop
004056FF     8B6424 08      mov esp,dword ptr ss:[esp+8]
00405703     33C0           xor eax,eax                                ;清零
00405705     FF6424 08      jmp dword ptr ss:[esp+8]            ; Hello.00405714

修改堆栈指针后,跳

00405712     90             nop
00405713     90             nop
00405714     64:8F00        pop dword ptr fs:[eax]
00405717     58             pop eax                                ;释放seh
00405718     EB 02          jmp short Hello.0040571C
0040571A     90             nop
0040571B     90             nop
0040571C     58             pop eax                                ;从堆栈得到一个值,这个值就是前面call里修改的哪个。
0040571D     5D             pop ebp
0040571E     EB 02          jmp short Hello.00405722
00405720     90             nop
00405721     90             nop
00405722     23C6           and eax,esi
00405724     FC             cld
00405725     F9             stc
00405726     72 02          jb short Hello.0040572A                ;变相jmp
00405728     90             nop
00405729     90             nop
0040572A     F9             stc
0040572B     72 02          jb short Hello.0040572F                ;变相jmp
0040572D     90             nop
0040572E     90             nop
0040572F     83E0 5D        and eax,5D
00405732     EB 0A          jmp short Hello.0040573E

0040573E     E8 82000000    call Hello.004057C5

继续前进

004057C5     F9             stc
004057C6     72 02          jb short Hello.004057CA             ; 变相jmp
004057C8     90             nop
004057C9     90             nop
004057CA     8BC4           mov eax,esp
004057CC     60             pushad
004057CD     E8 06000000    call Hello.004057D8

继续

004057D8     33D2           xor edx,edx                         ; edx=00000000
004057DA     64:FF32        push dword ptr fs:[edx]
004057DD     64:8922        mov dword ptr fs:[edx],esp          ; seh=004057D2
004057E0     F1             int1
004057E1     FF02           inc dword ptr ds:[edx]                        ;这里异常

设置seh=004057D2,然后异常,我们到异常地址拦截程序

004057D2     8B6424 08      mov esp,dword ptr ss:[esp+8]        ; 修改堆栈指针
004057D6     EB 0D          jmp short Hello.004057E5

继续

004057E5    /EB 03          jmp short Hello.004057EA
004057E7    |FFEB           jmp far ebx                         ; 非法使用寄存器
004057E9    |90             nop
004057EA    \03C1           add eax,ecx                                        ;eax=ecx=004057D2
004057EC     BA 00000000    mov edx,0
004057F1     64:8F02        pop dword ptr fs:[edx]
004057F4     5A             pop edx                                        ;释放seh
004057F5     EB 02          jmp short Hello.004057F9
004057F7     90             nop
004057F8     90             nop
004057F9     60             pushad
004057FA     E8 06000000    call Hello.00405805

继续

00405805     64:67:FF36 000>push dword ptr fs:[0]
0040580B     64:67:8926 000>mov dword ptr fs:[0],esp            ; seh=004057FF
00405811     9C             pushfd
00405812     810C24 0001000>or dword ptr ss:[esp],100
00405819     9D             popfd
0040581A     F8             clc
0040581B   ^ 73 DC          jnb short Hello.004057F9

单步异常来了,到异常地址去拦截

004057FF     8B6424 08      mov esp,dword ptr ss:[esp+8]        ; 修改指针
00405803     EB 1A          jmp short Hello.0040581F

0040581D     90             nop
0040581E     90             nop
0040581F     64:67:8F06 000>pop dword ptr fs:[0]
00405825     58             pop eax
00405826     61             popad
00405827     EB 03          jmp short Hello.0040582C

释放seh,继续

0040582B     90             nop
0040582C     15 C0CFECB4    adc eax,B4ECCFC0                                ;垃圾操作
00405831     E8 00000000    call Hello.00405836
00405836     EB 03          jmp short Hello.0040583B
00405838     FFEB           jmp far ebx                         ; 非法使用寄存器
0040583A     90             nop
0040583B     8BC2           mov eax,edx                                        ;从这里判断上面是垃圾指令
0040583D     48             dec eax
0040583E     8B3C24         mov edi,dword ptr ss:[esp]
00405841     58             pop eax
00405842     81EF A5144100  sub edi,4114A5
00405848     F9             stc
00405849     72 01          jb short Hello.0040584C
0040584B     90             nop
0040584C     13C7           adc eax,edi
0040584E     BE 1916F2C8    mov esi,C8F21619
00405853     81F6 AB05B3C8  xor esi,C8B305AB
00405859     F8             clc
0040585A     73 02          jnb short Hello.0040585E
0040585C     90             nop
0040585D     90             nop
0040585E     33C3           xor eax,ebx
00405860     03F7           add esi,edi                         ; esi=00405743
00405862     BA 00000000    mov edx,0
00405867     81C2 42FBFEA0  add edx,A0FEFB42
0040586D     81F2 5FFBFEA0  xor edx,A0FEFB5F
00405873     F9             stc
00405874     72 02          jb short Hello.00405878
00405876     90             nop
00405877     90             nop
00405878     83D0 BD        adc eax,-43
0040587B     E8 08000000    call Hello.00405888

除了给esi赋值,其它绕来绕去的计算貌似都没有实际意义,因为只是在计算也不存储,而且有时算了半天又用另一个值把它覆盖了,这就是垃圾指令了,是来混淆人的,继续

00405888     8BC3           mov eax,ebx                         ; eax=00000000
0040588A     C3             retn                                        ;返回到00405880

00405880    /E9 09000000    jmp Hello.0040588E

继续

0040588E     68 FF7A5207    push 7527AFF
00405893     5B             pop ebx
00405894     85E4           test esp,esp
00405896     79 03          jns short Hello.0040589B
00405898     90             nop
00405899     90             nop
0040589A     90             nop
0040589B     F5             cmc
0040589C     1BC7           sbb eax,edi
0040589E     6BDB 23        imul ebx,ebx,23
004058A1     311E           xor dword ptr ds:[esi],ebx          ; ebx=0046D0DD存到了00405743
004058A3     D1C3           rol ebx,1
004058A5     F9             stc
004058A6     83D3 3B        adc ebx,3B
004058A9     92             xchg eax,edx
004058AA     48             dec eax
004058AB     92             xchg eax,edx                        ; edx-1
004058AC     EB 02          jmp short Hello.004058B0

没有发现什么,继续

004058AE     90             nop
004058AF     90             nop
004058B0     23C7           and eax,edi
004058B2     FC             cld
004058B3     8BC3           mov eax,ebx
004058B5     05 26A3471B    add eax,1B47A326
004058BA     93             xchg eax,ebx
004058BB     EB 01          jmp short Hello.004058BE
004058BD     90             nop
004058BE     FC             cld
004058BF     1BC1           sbb eax,ecx
004058C1     8BC6           mov eax,esi                         ; esi=00405743)
004058C3     05 04000000    add eax,4
004058C8     96             xchg eax,esi                        ; 这里才是有效的操作,它把esi的存储地址+4,这样就可以存下一个了
004058C9     85E4           test esp,esp
004058CB     79 03          jns short Hello.004058D0
004058CD     90             nop
004058CE     90             nop
004058CF     90             nop
004058D0     05 98FCCC74    add eax,74CCFC98
004058D5    /EB 01          jmp short Hello.004058D8
004058D7     90             nop
004058D8     33C0           xor eax,eax                         ; 用eax算了半天,这里一个清零,所有的操作白费
004058DA     48             dec eax
004058DB     03C2           add eax,edx                                ;这里eax获得了比edx小1的值,1B
004058DD   ^ 79 BF          jns short Hello.0040589E                ;循环开始

除了标注的地方,其它都是废操作
循环作用:修改代码,范围00405743--004057B0,大小0x1D

004058DF     F8             clc
004058E0     73 02          jnb short Hello.004058E4
004058E2     90             nop
004058E3     90             nop
004058E4     61             popad
004058E5     EB 01          jmp short Hello.004058E8
004058E7     90             nop
004058E8     23C1           and eax,ecx
004058EA     C3             retn                                ; 返回地址00405743,也就是去执行刚才修改后的代码

继续

00405743     8B9D 82D34>mov ebx,dword ptr ss:[ebp+40D382]   ; ss:[00405CE0]=FFFFEFFF
00405749     33F6       xor esi,esi
0040574B     F7D3       not ebx                             ; ebx=00001000
0040574D     0BF3       or esi,ebx                          ; esi=ebx=00001000
0040574F     75 08      jnz short Hello.00405759
00405751     8D9D A2B64>lea ebx,dword ptr ss:[ebp+40B6A2]
00405757     EB 06      jmp short Hello.0040575F
00405759     039D 62D34000  add ebx,dword ptr ss:[ebp+40D362]   ; [00405CC0]=00400000,ebx=基址+RVA指向了00401000段,
0040575F     895C24 F0      mov dword ptr ss:[esp-10],ebx       ; 把值存一下
00405763     8DBD 84D24000  lea edi,dword ptr ss:[ebp+40D284]   ; 地址=00405BE2
00405769     33C0           xor eax,eax
0040576B     B9 9E030000    mov ecx,39E                         ; ecx=39E
00405770     F3:AA          rep stos byte ptr es:[edi]          ; 把0x39E的数据从00405BE2--00405F80,全部用0覆盖了,毁灭数据
00405772     8DBD A2B64000  lea edi,dword ptr ss:[ebp+40B6A2]   ; 地址=00404000
00405778     B9 58170000    mov ecx,1758
0040577D     F3:AA          rep stos byte ptr es:[edi]          ; 这次更凶,从00404000--00405758的数据全部用0覆盖,大小0x1758(真是太残忍了)
0040577F     66:AB          stos word ptr es:[edi]

毁掉了壳的大部分代码
00405781     8DBD A2B64000  lea edi,dword ptr ss:[ebp+40B6A2]   ; 地址=00404000
00405787     85F6           test esi,esi
00405789     75 08          jnz short Hello.00405793
0040578B     C707 33C040C3  mov dword ptr ds:[edi],C340C033
00405791     EB 0B          jmp short Hello.0040579E
00405793     C607 E9        mov byte ptr ds:[edi],0E9           ; ds:[00404000]=E9
00405796     47             inc edi
00405797     2BDF           sub ebx,edi
00405799     83EB 04        sub ebx,4
0040579C     891F           mov dword ptr ds:[edi],ebx          ; 往00404000开始的地方重新放入数据
0040579E     8DBD FACD4000  lea edi,dword ptr ss:[ebp+40CDFA]   ; 地址=00405758
004057A4     B9 2C000000    mov ecx,2C
004057A9     F3:AA          rep stos byte ptr es:[edi]          ; 又要毁掉我们刚执行的这几行代码

004057AD    /EB 02          jmp short Hello.004057B1
004057AF    |90             nop
004057B0    |90             nop
004057B1    \61             popad
004057B2   - FF6424 D0      jmp dword ptr ss:[esp-30]           ; Hello.00401000

我们结束了

最后怎样脱这个壳呢?把它从入口全部dump下来,再单独把没有毁坏的00402000段dump下来,替换掉前面dump下来的文件中的00402000段数据,修改导入表指针,大小到00402000段的正确位置,没有问题了收工。
dump下来的文件对齐是按照1000来对齐的,用Hex Workshop把00004000后的数据删掉,修改文件的Size of Image字节可以小一点。

希望有人能看到这里,写完这个,连我都没有再往上翻看了,头大呀,希望还能对刚学脱壳的有帮助。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (9)
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
加密对象:罗云彬的〈Windows环境下32位汇编语言程序设计〉第三章提供的对话框程序

源代码:
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Sample code for < Win32ASM Programming >
; by 罗云彬, http://asm.yeah.net
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Hello.asm
; 使用 Win32ASM 写的 Hello, world 程序
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 使用 nmake 或下列命令进行编译和链接:
; ml /c /coff Hello.asm
; Link /subsystem:windows Hello.obj
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .386
                .model flat,stdcall
                option casemap:none
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; Include 文件定义
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
include                windows.inc
include                user32.inc
includelib        user32.lib
include                kernel32.inc
includelib        kernel32.lib
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .data

szCaption        db        'A MessageBox !',0
szText                db        'Hello, World !',0
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                .code
start:
                invoke        MessageBox,NULL,offset szText,offset szCaption,MB_OK
                invoke        ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                end        start

这个是老壳了,有一些基本的壳的功能
单步跟踪再一条一条得写这个文章很痛苦,希望对初学脱壳的有所帮助

00405BD6 H>^\E9 25E4FFFF   jmp Hello.00404000                        ;跳转到00404000
00405BDB     0000            add byte ptr ds:[eax],al
00405BDD     00E5            add ch,ah
程序开始就跳转到00404000,察看PEditor,这是壳的最后一个段的开始地址,RAW是00000A00。
00404000     60              pushad                                   ; 跳转到程序的第四节开始执行raw=A00
00404001     F8              clc                                      ; clear carry flag
00404002     E8 02000000     call Hello.00404009                      ; 变相跳转 F7
00404007     E8 00E80000     call 0041280C
0040400C     0000            add byte ptr ds:[eax],al
0040400E     5E              pop esi                                  ; 和上一条指令配合取得了当前的虚拟地址esi=0040400E
0040400F     2BC9            sub ecx,ecx
00404011     58              pop eax                                  ; 同样的取得了另一个CALL的地址00404007
00404012     74 02           je short Hello.00404016                  ; 跳入了指令中间,有花指令,这是一个变相jmp
00404002处的指令跳转到代码的中间,有花指令并且是近距离调用也就是变相的jmp,nop掉00404007和00404008两个字节的代码,去除花指令后如下,程序中还有大量的类似代码,下面出现的代码都是已经nop后的。
00404000     60              pushad                             ; 跳转到程序的第四节开始执行raw=A00
00404001     F8              clc                                ; clear carry flag
00404002     E8 02000000     call Hello.00404009                ; 变相跳转
00404007     90              nop
00404008     90              nop
00404009     E8 00000000     call Hello.0040400E                ; 变相跳转
0040400E     5E              pop esi                            ; 和上一条指令配合取得了当前的虚拟地址esi=0040400E
0040400F     2BC9            sub ecx,ecx
00404011     58              pop eax                            ; 同样的取得了另一个CALL的地址00404007
00404012     74 02           je short Hello.00404016            ; 跳入了指令中间,有花指令,这是一个变相jmp

00404009     E8 00000000     call Hello.0040400E               
0040400E     5E              pop esi           
call调用会把下一条指令的地址放入内存作为返回地址,pop esi指令得到了call调用的返回地址esi=0040400E,je short Hello.00404016 这一条指令看起来是条件跳转指令,但它是变相的jmp,sub ecx,ecx这一条指令让ZF标志位等于1,je(ZF=1)总是跳转。
00404012    /74 02           je short Hello.00404016            ; 跳入了指令中间,有花指令,这是一个变相jmp
00404014    |90              nop
00404015    |90              nop
00404016    \B9 51190000     mov ecx,1951                       ; 放入常量1951
0040401B     8BC1            mov eax,ecx
0040401D     F8              clc                                ; clear carry flag
0040401E     73 02           jnb short Hello.00404022           ; 和上一条指令一起就是变相的jmp
00404020     90              nop
00404021     90              nop
00404022     83C6 33         add esi,33                                  ;esi=0040400E+33=00404041
00404025     8D4481 67       lea eax,dword ptr ds:[ecx+eax*4+67>; eax=00007EFC
00404029     E8 02000000     call Hello.00404030

clc指令是CF标志位=0,jnb(CF=0)跳转,所以这两条指令在一起,jnb总是要跳转,是变相的jmp。eax=00007EFC这个值也是常量,它的形成不是动态的,是壳刻意求出的。在OD命令行输入d 00404000,找到esi指向的数据00404041,在数据窗口动态的跟踪。

00404029     E8 02000000     call Hello.00404030
0040402E     90              nop
0040402F     90              nop
00404030     3006            xor byte ptr ds:[esi],al                    ; 把FC xor([00404041]=14)改为E8,是自修改代码
00404032     46              inc esi
00404033     5A              pop edx
00404034     EB 01           jmp short Hello.00404037
00404036     90              nop
00404037     D4 09           aam 9
00404039     49              dec ecx
0040403A   ^ 7F E9           jg short Hello.00404025                     ; 这段循环是一段自修改的代码,动态的改变了从00404041-00405991x1951字节的代码
0040403C     67:E3 02        jcxz short Hello.00404041                   ; jump if cx=0

把00404041原来等于14改为了E8,在数据窗口可以看到变化,同时我们00404034代码的下面就是00404041指令了,更改这里,就更改了代码,所以这是一段自修改代码。
00404039     49              dec ecx
0040403A   ^ 7F E9           jg short Hello.00404025                     
这段循环唯一的条件跳转就是ecx=0,jg指令没有执行时能跳出循环,ecx中是常量0x1951,所以这段循环的作用是:修改代码,范围00404041---00405991,大小0x1951

0040403A   ^\7F E9           jg short Hello.00404025                     
0040403C    /67:E3 02        jcxz short Hello.00404041                   ; jump if cx=0 ,变相jmp
0040403F    |90              nop
00404040    |90              nop
00404041    \E8 7E1B0000     call Hello.00405BC4                         ; 调用里修改了堆栈地址=00405992,自修改的结束位置 F7
00404046     C3              retn

上一个循环在结束时,cx已经等于0,而jcxz指令时cx=0时则跳转,所以这个跳转总是实现,是变相jmp。jcxz short Hello.00404041 ;跳转的地址就是刚才自修改后的代码。
00405BC4:
00405BC4     E8 00000000     call Hello.00405BC9                                变相jmp F7
00405BC9     812C24 37020000 sub dword ptr ss:[esp],237                  ; 减去后刚好等于了00405992,被自修改代码的结束地址
00405BD0     FF6424 04       jmp dword ptr ss:[esp+4]
执行00405BD0时的堆栈如下所示:
0012FF9C   00405992  Hello.00405992
0012FFA0   00404046  返回到 Hello.00404046 来自 Hello.00405BC4
0012FFA4   7C930738  ntdll.7C930738
ss:[esp+4]就是0012FFA0=00404046,它是00404041    \E8 7E1B0000     call Hello.00405BC4   调用时压入地返回地址。我们jmp到了00404046指令这里,执行retn指令我们就来到了调用里计算的地址00405992。

00405992     0BE4            or esp,esp
00405994     75 01           jnz short Hello.00405997                                ;变相jmp
00405996     90              nop
00405997     F9              stc                                         ; set carry flag  cf=1
00405998     72 02           jb short Hello.0040599C                     ; jump if cf=1,变相jmp
0040599A     90              nop
0040599B     90              nop
0040599C     85E4            test esp,esp
0040599E     79 03           jns short Hello.004059A3                                ;变相jmp
004059A0     90              nop
004059A1     90              nop
004059A2     90              nop
004059A3     90              nop
004059A4     23C4            and eax,esp                                                ;eax=00000E00
004059A6     EB 0C           jmp short Hello.004059B4
004059A8    |E8 D9010000     call Hello.00405B86
004059AD    |EB 02           jmp short Hello.004059B1
004059AF    |90              nop
004059B0    |90              nop
004059B1    |90              nop
004059B2    |90              nop
004059B3    |90              nop
004059B4    \E8 39000000     call Hello.004059F2

上面的代码很多都是垃圾代码,跳去跳来的,只有004059A4这条不知道有没有用,我们记下eax的值看看后面有没有用,要注意壳可能每执行一条有用的指令都放入几条垃圾指令。

004059F2     F8              clc
004059F3     73 02           jnb short Hello.004059F7                        ;和上一条指令在一起就是变相jmp
004059F5     90              nop
004059F6     90              nop
004059F7     0BC0            or eax,eax
004059F9     60              pushad
004059FA     E8 06000000     call Hello.00405A05                       

都是垃圾跳转,变相的在转来转去。

004059FA     E8 06000000     call Hello.00405A05                                :要记住执行一个调用就会压入它的下一条指令地址作为返回地址。
004059FF     8B6424 08       mov esp,dword ptr ss:[esp+8]                ; esp=0012FF74
00405A03     EB 0D           jmp short Hello.00405A12
00405A05     2BDB            sub ebx,ebx
00405A07     64:FF33         push dword ptr fs:[ebx]
00405A0A     64:8923         mov dword ptr fs:[ebx],esp                  ; 构建seh,004059FF
00405A0D     F1              int1
00405A0E     FF03            inc dword ptr ds:[ebx]

00405A05     2BDB            sub ebx,ebx
00405A07     64:FF33         push dword ptr fs:[ebx]
00405A0A     64:8923         mov dword ptr fs:[ebx],esp                  
这三条指令在构建seh,esp放入的处理异常地址就是call  Hello.00405A05的返回地址,即004059FF。发生异常后,程序会跑到这里来执行。马上
00405A0E     FF03            inc dword ptr ds:[ebx]
产生了壳的第一个异常。在004059FF上F2下断点,SHIFT+F9忽略异常,程序将中断在这里。
004059FF     8B6424 08       mov esp,dword ptr ss:[esp+8]                ; esp=0012FF74
00405A03     EB 0D           jmp short Hello.00405A12
修改了esp指针,再次指向了seh结构,提前说一下这是为释放seh作准备。

00405A12    /EB 03           jmp short Hello.00405A17
00405A14    |FFEB            jmp far ebx                                 ; 非法使用寄存器
00405A16    |90              nop
00405A17    \33C9            xor ecx,ecx
00405A19     64:8F01         pop dword ptr fs:[ecx]
00405A1C     59              pop ecx                                     ; 恢复seh
00405A1D    /EB 02           jmp short Hello.00405A21

恢复了系统的seh。

00405A1D    /EB 02           jmp short Hello.00405A21
00405A1F    |90              nop
00405A20    |90              nop
00405A21    \0BC6            or eax,esi
00405A23     60              pushad
00405A24     E8 06000000     call Hello.00405A2F

保存了数据进入了一个有趣的地方。

00405A24     E8 06000000     call Hello.00405A2F
00405A29     8B6424 08       mov esp,dword ptr ss:[esp+8]                ; esp=0012FF54
00405A2D     EB 1A           jmp short Hello.00405A49
00405A2F     64:67:FF36 0000 push dword ptr fs:[0]
00405A35     64:67:8926 0000 mov dword ptr fs:[0],esp                    ; seh=00405A29
00405A3B     9C              pushfd
00405A3C     810C24 00010000 or dword ptr ss:[esp],100
00405A43     9D              popfd
00405A44     F8              clc
00405A45   ^ 73 DC           jnb short Hello.00405A23                    ; 变相jmp

这是一个不同寻常的循环,如果你不知道它的作用的话,你永远都走不出来,他会在你的堆栈里建立无数个异常处理地址为00405A29的seh结构,每执行一次就建立一个。
00405A3B     9C              pushfd
00405A3C     810C24 00010000 or dword ptr ss:[esp],100
00405A43     9D              popfd
pushfd是把cpu的标志寄存器压入堆栈,00405A3C处则把压入的标志寄存器值的第九位进行or。popfd则把or后的值重新放入了标志寄存器。标志寄存器的第九位是Trap Flag (TF),1表示允许单步调试,0表示不允许单步调试。本来程序中为0,执行00405A3C后就变成了1了。在00405A29中下断点,F9执行程序,马上程序异常,SHIFT+9忽略异常,将中断在00405A29处修改esp指针后,跳转到00405A49。

00405A47     90              nop
00405A48     90              nop
00405A49     64:67:8F06 0000 pop dword ptr fs:[0]
00405A4F     58              pop eax                                     ; 恢复seh
00405A50     61              popad
00405A51     F9              stc
00405A52     72 02           jb short Hello.00405A56                     ; 变相jmp
00405A54     90              nop
00405A55     90              nop
00405A56     98              cwde
00405A57     E8 00000000     call Hello.00405A5C
00405A5C     EB 01           jmp short Hello.00405A5F

进入循环前有pushad,00405A50的popad恢复到原来的寄存器值,中间的指令都是垃圾操作的指令。变相的搞了jmp后,来到了00405A5F

00405A5E     90              nop
00405A5F     83F0 60         xor eax,60                                  ; 取得常量eax=60
00405A62     E8 06000000     call Hello.00405A6D
00405A67     E9 07000000     jmp Hello.00405A73
00405A6C     98              cwde
00405A6D     1D 8CA52DB6     sbb eax,B62DA58C                            ; 得到常量eax=49D25AD4
00405A72     C3              retn

执行retn后将来到00405A67的jmp,我们就来到了00405A73

00405A73     B8 BFBD8A06     mov eax,68ABDBF                             ; 取得常量eax=68ABDBF
00405A78     8B3424          mov esi,dword ptr ss:[esp]                  ; Hello.00405A5C
00405A7B     58              pop eax                                                ;eax=ss:[esp]=00405A5C
00405A7C     81EE 57144100   sub esi,411457                                        ;esi=FFFF4605
00405A82     EB 02           jmp short Hello.00405A86

可以看到eax得到的常量没有参加任何的操作就被00405A7B的操作给覆盖了,可见前面eax获得的常量都是垃圾,是干扰指令,可以nop掉,让代码清爽一些。

00405A84     90              nop
00405A85     90              nop
00405A86     83D0 B6         adc eax,-4A                                 ; eax=00405A13
00405A89     B9 86FA6644     mov ecx,4466FA86
00405A8E     81F1 32E92744   xor ecx,4427E932                            ; 得到常量ecx=004113B4
00405A94     EB 02           jmp short Hello.00405A98

又在ecx中刻意放了一个常量,不知道有没有用,先做个标记。

00405A94    /EB 02           jmp short Hello.00405A98
00405A96    |FF20            jmp dword ptr ds:[eax]
00405A98    \48              dec eax                                                ;又减掉了1,eax=00405A12
00405A99     03C7            add eax,edi                                                ;edi=0,eax的值不变
00405A9B     03CE            add ecx,esi                                                ;得到ecx=004059B9,你还记得esi的值是怎么来的吗?
00405A9D     68 CABD7C38     push 387CBDCA
00405AA2     5B              pop ebx                                                ;ebx得到了常量387CBDCA
00405AA3     81F3 C7BD7C38   xor ebx,387CBDC7                                        ;ebx得到了常量0000000D
00405AA9     0BE4            or esp,esp
00405AAB     75 01           jnz short Hello.00405AAE                                ;变相jmp

本来可以直接在ebx中放入0000000D的,但是壳还是要执行一些垃圾操作干扰:
00405A9D     68 CABD7C38     push 387CBDCA
00405AA2     5B              pop ebx                                                ;ebx得到了常量387CBDCA
00405AA3     81F3 C7BD7C38   xor ebx,387CBDC7                                        ;ebx得到了常量0000000D

00405AAB    /75 01           jnz short Hello.00405AAE
00405AAD    |90              nop
00405AAE    \15 0768EF13     adc eax,13EF6807
00405AB3     BF 32ADCA45     mov edi,45CAAD32
00405AB8     0BE4            or esp,esp
00405ABA     75 01           jnz short Hello.00405ABD
00405ABC     90              nop
00405ABD     F5              cmc
00405ABE     6BFF 0B         imul edi,edi,0B
00405AC1     3139            xor dword ptr ds:[ecx],edi                        ;edi=FFB57126  ds:[004059B9]=FFB5997D执行后改为0000E85B
00405AC3     C1C7 05         rol edi,5
00405AC6     F9              stc
00405AC7     83D7 47         adc edi,47
00405ACA     4B              dec ebx                                                ;ebx=d-1=c
00405ACB     EB 01           jmp short Hello.00405ACE

中间都是在放入大量的常量然后是运算,我们不用管他可能是垃圾指令,我们只要注意他有没有把结果存到什么地方,在这里没有,重点注意这里修改了一处代码,而且把ebx减了一,会不会改一个减去一次,ebx是计数器,我们要注意。

00405ACB    /EB 01           jmp short Hello.00405ACE
00405ACD    |90              nop
00405ACE    \98              cwde
00405ACF     23C0            and eax,eax                               
00405AD1     8BC7            mov eax,edi                                ;算了半天,把eax的值覆盖了,前面对eax的操作都可以肯定是垃圾指令了
00405AD3     05 9C993C04     add eax,43C999C
00405AD8     97              xchg eax,edi
00405AD9     F9              stc
00405ADA     72 02           jb short Hello.00405ADE

没有发现任何用处的运算,继续。

00405ADA    /72 02           jb short Hello.00405ADE
00405ADC    |90              nop
00405ADD    |90              nop
00405ADE    \F8              clc
00405ADF     E8 08000000     call Hello.00405AEC
00405AE4    /E9 0E000000     jmp Hello.00405AF7
00405AE9    |8BC2            mov eax,edx
00405AEB    |40              inc eax
00405AEC    |A9 DD177E13     test eax,137E17DD                        ;这里有一个检测,但是下一条指令返回到了00405AE4,和检测结果没有任何关系,前面所有的运算都是垃圾指令。
00405AF1    |C3              retn

从00405AE4jmp到了00405AF7

00405AF7     0BC2            or eax,edx
00405AF9     40              inc eax
00405AFA     8BC1            mov eax,ecx                                ;这里eax获得了一个新的值,eax=ecx=004059B9
00405AFC     05 04000000     add eax,4
00405B01     91              xchg eax,ecx
00405B02     EB 01           jmp short Hello.00405B05

终于有效操作出现了,如果你还记得004059B9就是前面被修改了的代码地址,
00405AFA     8BC1            mov eax,ecx                               
00405AFC     05 04000000     add eax,4
00405B01     91              xchg eax,ecx
重新的到要修改的地址,把它加上4,这样就指向了下一个双字004059BD,再存到了ecx,简单的说就是增加了要修改的代码地址。继续。

00405B02    /EB 01           jmp short Hello.00405B05
00405B04    |90              nop
00405B05    \13C7            adc eax,edi                                ;垃圾操作再次来临,eax=FB2B187C
00405B07     F8              clc
00405B08     EB 02           jmp short Hello.00405B0C

继续。

00405B08    /EB 02           jmp short Hello.00405B0C
00405B0A    |90              nop
00405B0B    |90              nop
00405B0C    \1BC5            sbb eax,ebp
00405B0E     90              nop
00405B0F     B8 00000000     mov eax,0                                ;从这一条开始重新给eax赋0,一切重新开始,注意了
00405B14     48              dec eax
00405B15     03C3            add eax,ebx                                ;变相操作,eax=ebx-1=c-1=b,还记得ebx=c吗,我也要忘记了,赶快回头看看吧。
00405B17   ^ 79 A5           jns short Hello.00405ABE                ;循环开始

最后来了个千里大迂回,回到了修改代码的地方,把ecx指向的004059BD地址的代码修改为B95F0000,这么多的垃圾操作最后就是为了隐藏这个修改的循环。我们来看一下如果没有垃圾操作,有效的指令是那些。
00405AC1     3139            xor dword ptr ds:[ecx],edi        ;修改指令
00405ACA     4B              dec ebx                                ;计数器,从D减着1下来
00405AFA     8BC1            mov eax,ecx                                
00405AFC     05 04000000     add eax,4
00405B01     91              xchg eax,ecx                                ;以上三条增加要修改的代码地址,从004059B9开始加着4下来
00405B0F     B8 00000000     mov eax,0
00405B14     48              dec eax
00405B15     03C3            add eax,ebx                                ;计较是否退出循环
00405B17   ^ 79 A5           jns short Hello.00405ABE

终于搞明白了,被修改后的数据如下:
004059B9  5B E8 00 00 00 00 5F B9  
004059C1  4A 19 00 00 43 66 0B C9  
004059C9  75 02 CD 20 C0 04 0B 01
004059D1  00 0C 0B 80 34 0B F7 FE  
004059D9  04 0B EB FF C9 7F E6 8B  
004059E1  C1 83 C1 26 F3 AA 66 AB  
004059E9  FF E3 CB F3        
我们把od往上翻几页,道004059B9的代码行就可以看到被修改后的代码,在od中以红色表示,闹了半天也是一段自修改代码

00405B17   ^\79 A5           jns short Hello.00405ABE
00405B19     EB 02           jmp short Hello.00405B1D
00405B1B     90              nop
00405B1C     90              nop
00405B1D     1D 9FD35E57     sbb eax,575ED39F
00405B22     61              popad
00405B23     EB 02           jmp short Hello.00405B27

执行popad,所有寄存器重新被赋值,一切从头开始。

00405B23    /EB 02           jmp short Hello.00405B27
00405B25    |FF20            jmp dword ptr ds:[eax]
00405B27    \F8              clc
00405B28     72 47           jb short Hello.00405B71                        ;永远也不会执行的跳转
00405B2A     C3              retn                                                ;看一眼堆栈,返回地址是004059B9

程序回过头去从被修改后的地方重新执行。

004059B9     5B              pop ebx                                        ;ebx=00404046
004059BA     E8 00000000     call Hello.004059BF
004059BF     5F              pop edi                                        ;edi=当前的指令地址004059BF
004059C0     B9 4A190000     mov ecx,194A                                        ;ecx=常量194A
004059C5     43              inc ebx
004059C6     66:0BC9         or cx,cx                                        ;构建了一个循环,cx是计数器了,这里就是在判断cx是不是等于零
004059C9     75 02           jnz short Hello.004059CD
004059CB     90              nop
004059CC     90              nop
004059CD     C0040B 01       rol byte ptr ds:[ebx+ecx],1
004059D1     000C0B          add byte ptr ds:[ebx+ecx],cl
004059D4     80340B F7       xor byte ptr ds:[ebx+ecx],0F7
004059D8     FE040B          inc byte ptr ds:[ebx+ecx]                        ;以上四条指令,修改00405991的数据
004059DB     EB FF           jmp short Hello.004059DC
004059DD     C9              leave
004059DE   ^ 7F E6           jg short Hello.004059C6

看下面的两条指令:
004059DB     EB FF           jmp short Hello.004059DC
004059DD     C9              leave
004059DB指令跳到了自己的中间:机器码EB FF C9组成了两条指令,EB FF就是004059DB这一条,FF C9是jmp的地址004059DC这一条,跳转后才能看到。
004059DC     FFC9            dec ecx                                        ;计数器cx-1=00001949
004059DE   ^ 7F E6           jg short Hello.004059C6
需要注意的是,这个循环是倒着修改数据的:修改代码,范围00405991---00404048,大小0x194A
壳的第一个循环的修改数据我们来回顾一下:修改代码,范围00404041---00405991,大小0x1951
原来除了前面的几个字节,是顺着修改了一遍,又倒着修改了一遍。

004059DE   ^\7F E6           jg short Hello.004059C6
004059E0     8BC1            mov eax,ecx                                 ; eax=ecx=0
004059E2     83C1 26         add ecx,26                                  ; 得到常量ecx=26
004059E5     F3:AA           rep stos byte ptr es:[edi]                  ; es:[edi]=004059BF,从这个地址开始的0x26字节的数据全部清零,事实  上,这些数据就是我们前面执行的代码,它要去除痕迹。
004059E7     66:AB           stos word ptr es:[edi]                        ;连上一条指令也一并除去es:[edi]指向的就是上一条指令地址004059E5
004059E9     FFE3            jmp ebx                                        ;大跳转到00404047

回到了循环修改的代码位置开始执行。

00404047     83C3 07         add ebx,7                                   ; ebx=00404047+7=0040404E
0040404A     F3:             prefix rep:
0040404B     EB FF           jmp short Hello.0040404C                                ;又是跳转到自己中间的指令,执行后所有的代码全部都有变化了
0040404D   - E3 83           jecxz short Hello.00403FD2
0040404F     C3              retn

执行跳转后实际的代码如下:

0040404C     FFE3            jmp ebx
0040404E     83C3 1B         add ebx,1B                                  ; EBX=0040404E+1B=00404069
00404051     53              push ebx
00404052     E8 00000000     call Hello.00404057
00404057     5E              pop esi                                     ; 和上一条指令配合得到了当前指令的地址
00404058     8D76 23         lea esi,dword ptr ds:[esi+23]               ; esi=0040407A
0040405B     8BFE            mov edi,esi
0040405D     B9 0C080000     mov ecx,80C                                               
00404062     5A              pop edx                                                ;edx=00404069
00404063     AC              lods byte ptr ds:[esi]                                ;把ds:[esi]=0040407A=9D装入AL
00404064     FEC8            dec al                                                ;把上面装入的值
00404066     EB FF           jmp short Hello.00404067                                ;跳到本身
00404068    /E2 32           loopd short Hello.0040409C

我们再也不要期望着能nop掉花指令了,壳都是采取跳转到指令自己的方法,比如说00404066也是跳转到了本身,一旦执行,代码又将来个大变化如下:
00404067    /FFE2            jmp edx                                     ; Hello.00404069
00404069    \32C1            xor al,cl                                                ;修改eax=90
0040406B     EB FF           jmp short Hello.0040406C                                ;跳到本身
0040406D     F0:D00424       lock rol byte ptr ss:[esp],1                ; 不允许锁定前缀

0040406C     FFF0            push eax                                    ; eax=90存起来
0040406E     D00424          rol byte ptr ss:[esp],1                     ; ss[esp]指向的就是刚才压入的eax值90=1001000,左移后=10001
00404071     58              pop eax                                     ; 在堆栈中改变了eax的值,重新得到它
00404072     04 C7           add al,0C7
00404074     AA              stos byte ptr es:[edi]                      ; 把这个修改后的值eax=E8,重新存到了取出它的地址0040407B
00404075     EB FF           jmp short Hello.00404076                                ;跳转到本身
00404077     C9              leave

00404076     FFC9            dec ecx                                                ;修改了计数器ecx=80C,然后跳了回去
00404078   ^ 7F E9           jg short Hello.00404063
0040407A     E8 00000000     call Hello.0040407F

从00404063到00404078又构成了一个循环,依然是:修改代码,范围0040407A---00404890,大小0x80C。鼠标点下一行代码0040407A,F4执行到这里,跳出循环。
结束循环后,马上又从修改的代码0040407A处开始执行

0040407A     E8 00000000     call Hello.0040407F
0040407F     5D              pop ebp                                     ; 和上一条指令一起取得当前指令的地址ebp=0040407F
00404080     8D45 46         lea eax,dword ptr ss:[ebp+46]               ; eax=004040C5
00404083     50              push eax                                    ; 存起来
00404084     33C0            xor eax,eax
00404086     64:FF30         push dword ptr fs:[eax]
00404089     64:8920         mov dword ptr fs:[eax],esp                  ; 构建了一个seh=004040C5
0040408C     CC              int3                                                        ;int3引发异常

很轻松就可以过了,CTRL+G到004040C5,F2下断点,SHIFT+F9忽略异常,就马上中断在004040C5中了,以后所有异常的处理方法如果没有特殊说明都是这样。

004040C5     8B4424 04       mov eax,dword ptr ss:[esp+4]                ; eax=0012FCB0
004040C9     8B4C24 0C       mov ecx,dword ptr ss:[esp+C]                ; ecx=0012FCD0
004040CD     FF81 B8000000   inc dword ptr ds:[ecx+B8]                   ; 堆栈 ds:[0012FD88]=0040408C (Hello.0040408C)+1
004040D3     8B00            mov eax,dword ptr ds:[eax]                  ; eax=[0012FCB0]=80000003
004040D5     3D 940000C0     cmp eax,C0000094
004040DA     75 24           jnz short Hello.00404100                                ;跳了

004040DA    /75 24           jnz short Hello.00404100
004040DC    |FF81 B8000000   inc dword ptr ds:[ecx+B8]
004040E2    |33C0            xor eax,eax
004040E4    |2141 04         and dword ptr ds:[ecx+4],eax
004040E7    |2141 08         and dword ptr ds:[ecx+8],eax
004040EA    |2141 0C         and dword ptr ds:[ecx+C],eax
004040ED    |2141 10         and dword ptr ds:[ecx+10],eax
004040F0    |8161 14 F00FFFF>and dword ptr ds:[ecx+14],FFFF0FF0
004040F7    |8161 18 00DC000>and dword ptr ds:[ecx+18],0DC00
004040FE    |EB 60           jmp short Hello.00404160
00404100    \3D 04000080     cmp eax,80000004
00404105     74 0C           je short Hello.00404113
00404107     3D 03000080     cmp eax,80000003
0040410C     74 12           je short Hello.00404120                     ; 满足条件了

00404120     8B81 B4000000   mov eax,dword ptr ds:[ecx+B4]
00404126     8D40 24         lea eax,dword ptr ds:[eax+24]
00404129     8941 04         mov dword ptr ds:[ecx+4],eax                            ; [0012FCD4]=eax=0040407F
0040412C     8B81 B4000000   mov eax,dword ptr ds:[ecx+B4]
00404132     8D40 1F         lea eax,dword ptr ds:[eax+1F]
00404135     8941 08         mov dword ptr ds:[ecx+8],eax                            ; [0012FCD8]=eax=0040409E
00404138     8B81 B4000000   mov eax,dword ptr ds:[ecx+B4]
0040413E     8D40 1A         lea eax,dword ptr ds:[eax+1A]
00404141     8941 0C         mov dword ptr ds:[ecx+C],eax                            ; [0012FCDC]=eax=00404099
00404144     8B81 B4000000   mov eax,dword ptr ds:[ecx+B4]
0040414A     8D40 11         lea eax,dword ptr ds:[eax+11]
0040414D     8941 10         mov dword ptr ds:[ecx+10],eax                           ; [12FCE0]=eax=00404090
00404150     33C0            xor eax,eax
00404152     8161 14 F00FFFF>and dword ptr ds:[ecx+14],FFFF0FF0                  ;ds:[0012FCE4]=FFFFFFFF
00404159     C741 18 5501000>mov dword ptr ds:[ecx+18],155                         ;[0012FCE8]=00000155
00404160     C3              retn

给这连续的六个堆栈赋值
跳转到了7C9237C6

7C9237C6     64:8F05 0000000>pop dword ptr fs:[0]                        ; 0012FF9C
7C9237CD     8BE5            mov esp,ebp
7C9237CF     5D              pop ebp
7C9237D0     C2 1400         retn 14

跳转到了7C9237D0

7C92378B     5F              pop edi                                                ;edi=00404886
7C92378C     5E              pop esi                                                ; esi=0012FCB0
7C92378D     5B              pop ebx                                                ; ebx=0012FF9C
7C92378E     C2 1400         retn 14

跳转到了7C957860

7C957860     F605 5AC3997C 8>test byte ptr ds:[7C99C35A],80                        ;[7C99C35A]=00,测试了高位是否为1
7C957867     8BF8            mov edi,eax
7C957869     0F85 16720100   jnz ntdll.7C96EA85
7C95786F     395D 08         cmp dword ptr ss:[ebp+8],ebx                      ; [0012FCA0]=00000000,ebx=0012FF9C
7C957872     0F84 1B720100   je ntdll.7C96EA93
7C957878     8BC7            mov eax,edi                                                ;两者都是0
7C95787A     33C9            xor ecx,ecx
7C95787C     2BC1            sub eax,ecx                                                ;结果都是0
7C95787E   ^ 0F85 8631FFFF   jnz ntdll.7C94AA0A                                        ;这个是不会跳转了
7C957884     F646 04 01      test byte ptr ds:[esi+4],1                        ;堆栈 ds:[0012FCB4]=00
7C957888     0F85 4F720100   jnz ntdll.7C96EADD
7C95788E     C645 FF 01      mov byte ptr ss:[ebp-1],1                                ;堆栈 ss:[0012FC97]=00
7C957892     5F              pop edi
7C957893     5B              pop ebx
7C957894     8A45 FF         mov al,byte ptr ss:[ebp-1]                        ;得到了1
7C957897     5E              pop esi
7C957898     C9              leave
7C957899     C2 0800         retn 8

跳转到了7C92EAFA

7C92EAFA     0AC0            or al,al                                                ;al总是=1
7C92EAFC     74 0C           je short ntdll.7C92EB0A
7C92EAFE     5B              pop ebx
7C92EAFF     59              pop ecx
7C92EB00     6A 00           push 0
7C92EB02     51              push ecx
7C92EB03     E8 11EBFFFF     call ntdll.ZwContinue                                ;F8

回到了程序

004040A3     90              nop
004040A4     90              nop
004040A5     33DB            xor ebx,ebx                       ; ebx=0
004040A7     F7F3            div ebx                                ;异常

在堆栈中可以看到seh=004040C5

004040C5     8B4424 04       mov eax,dword ptr ss:[esp+4]      ; eax=0012FCBC
004040C9     8B4C24 0C       mov ecx,dword ptr ss:[esp+C]      ; ecx=0012FCD0
004040CD     FF81 B8000000   inc dword ptr ds:[ecx+B8]         ; 堆栈 ds:[0012FD88]=004040A7+1
004040D3     8B00            mov eax,dword ptr ds:[eax]        ; eax=[0012FCB0]=80000003
004040D5     3D 940000C0     cmp eax,C0000094
004040DA     75 24           jnz short Hello.00404100
004040DC     FF81 B8000000   inc dword ptr ds:[ecx+B8]                        ;[0012FD88]=004040A7+1
004040E2     33C0            xor eax,eax
004040E4     2141 04         and dword ptr ds:[ecx+4],eax
004040E7     2141 08         and dword ptr ds:[ecx+8],eax
004040EA     2141 0C         and dword ptr ds:[ecx+C],eax
004040ED     2141 10         and dword ptr ds:[ecx+10],eax
004040F0     8161 14 F00FFFF>and dword ptr ds:[ecx+14],FFFF0FF>
004040F7     8161 18 00DC000>and dword ptr ds:[ecx+18],0DC00
004040FE     EB 60           jmp short Hello.00404160
00404100     3D 04000080     cmp eax,80000004
00404105     74 0C           je short Hello.00404113
00404107     3D 03000080     cmp eax,80000003
0040410C     74 12           je short Hello.00404120           ; 满足条件了
0040410E     6A 01           push 1
00404110     58              pop eax
00404111     EB 4D           jmp short Hello.00404160
00404113     E8 01000000     call Hello.00404119

这次是在堆栈里放入了0,从[0012FCD4]到[0012FCE8]

F9运行,程序异常中断在004046A8

004046A8     8DC0            lea eax,eax                          ; 非法使用寄存器

堆栈中的seh=0040468A,我们来到这里

0040468A     8B6424 08       mov esp,dword ptr ss:[esp+8]         ; 重置堆栈指针esp=0012FF7C
0040468E     EB 01           jmp short Hello.00404691
00404690     90              nop
00404691     EB 1B           jmp short Hello.004046AE

004046AE     64:67:8F06 0000 pop dword ptr fs:[0]
004046B4     EB 02           jmp short Hello.004046B8
004046B6     90              nop
004046B7     90              nop
004046B8     59              pop ecx                                        ;释放了seh
004046B9     61              popad
004046BA     F5              cmc

pop指令后,重置了所有的寄存器如下:
EAX 00002683
ECX 00000000
EDX 0000065F
EBX 7C804A83 kernel32.7C804A83
ESP 0012FFA4
EBP 0040407F Hello.0040407F
ESI 00405992 Hello.00405992
EDI 00405992 Hello.00405992
EIP 004046BB Hello.004046BB

004046BB     8D7415 00       lea esi,dword ptr ss:[ebp+edx]       ; esi=004046DE
004046BF     83C2 22         add edx,22                           ; edx=00000681
004046C2     8BFE            mov edi,esi
004046C4     B9 B4120000     mov ecx,12B4                         ; ecx=12B4,我们随时都要注意对ecx的赋值指令,ecx是大多数循环的计数器
004046C9     2ADB            sub bl,bl                            ; ebx=7C984A00
004046CB     AC              lods byte ptr ds:[esi]               ; 数据窗口调整到00404600,找到[004046DE]
004046CC     32C3            xor al,bl
004046CE     FEC0            inc al
004046D0     34 B9           xor al,0B9                           ; 变为了eax=000026AE
004046D2     EB 02           jmp short Hello.004046D6
004046D4     90              nop
004046D5     90              nop
004046D6     04 DF           add al,0DF
004046D8     AA              stos byte ptr es:[edi]               ; 把修改后的数据存了回去,
004046D9     8AD8            mov bl,al
004046DB     49              dec ecx
004046DC   ^ 7F ED           jg short Hello.004046CB              ; 开始无尽的循环

也是一段自修改代码,修改的代码就紧接着循环,可以在od中红色的看到,F4到下一行调处循环,修改代码,范围004046DE--00405991,大小0x12B4(这段代码不只还要修改多少次,才能执行完)

004046DC   ^\7F ED           jg short Hello.004046CB              ; 开始无尽的循环
004046DE     8D7415 00       lea esi,dword ptr ss:[ebp+edx]       ; esi=00404700
004046E2     83C2 1E         add edx,1E
004046E5     8BFE            mov edi,esi
004046E7     B9 92120000     mov ecx,1292                         ; 又在ecx中放入了0x1292,看来循环又要来了
004046EC     66:33DB         xor bx,bx
004046EF     AC              lods byte ptr ds:[esi]               ; 数据窗口要随时追踪着,找到[00404700]=65的位置
004046F0     32C3            xor al,bl
004046F2     04 01           add al,1
004046F4     34 7D           xor al,7D
004046F6     C0C0 07         rol al,7
004046F9     AA              stos byte ptr es:[edi]
004046FA     8AD8            mov bl,al
004046FC     49              dec ecx
004046FD   ^ 7F F0           jg short Hello.004046EF              ; 和上一个循环一样,
004046FF     F9              stc

也是一段自修改代码,修改的代码就紧接着循环,可以在od中红色的看到,F4到下一行调处循环,修改代码,范围00404700--00405991,大小0x1292(这段代码不只还要修改多少次,才能执行完)
(壳在发疯)

004046FF     F9              stc
00404700     8D7415 00       lea esi,dword ptr ss:[ebp+edx]       ; esi=0040471E
00404704     83C2 22         add edx,22
00404707     8BFE            mov edi,esi
00404709     2BDB            sub ebx,ebx
0040470B     B9 74120000     mov ecx,1274
00404710     AC              lods byte ptr ds:[esi]
00404711     32C3            xor al,bl
00404713     FEC0            inc al
00404715     34 D1           xor al,0D1
00404717     04 35           add al,35
00404719     AA              stos byte ptr es:[edi]
0040471A     86D8            xchg al,bl
0040471C   ^ E2 F2           loopd short Hello.00404710

垃圾呀,和上面的循环还是一样:修改代码,范围0040471E--00405991,大小0x1274

0040471C   ^\E2 F2           loopd short Hello.00404710
0040471E     8D7415 00       lea esi,dword ptr ss:[ebp+edx]       ; esi=00404740
00404722     32DB            xor bl,bl
00404724     83C2 28         add edx,28
00404727     8BFE            mov edi,esi
00404729     B9 52120000     mov ecx,1252
0040472E     AC              lods byte ptr ds:[esi]
0040472F     32C3            xor al,bl
00404731     FEC0            inc al
00404733     34 B9           xor al,0B9
00404735     C0C0 05         rol al,5
00404738     AA              stos byte ptr es:[edi]
00404739     8AD8            mov bl,al
0040473B     49              dec ecx
0040473C   ^ 7F F0           jg short Hello.0040472E

又来一次,不多说,还是一样:修改代码,范围00404740--00405991,大小0x1252(od的数据窗口要注意跟着)

0040473C   ^\7F F0           jg short Hello.0040472E
0040473E     40              inc eax                              ; eax=00002649+1
0040473F     48              dec eax                              ; 上一条指令加1,这一条指令减1,又是垃圾指令
00404740     8D7415 00       lea esi,dword ptr ss:[ebp+edx]       ; 又来了,还没有结束。esi=00404768
00404744     83C2 23         add edx,23
00404747     8BFE            mov edi,esi
00404749     83E3 00         and ebx,0
0040474C     B9 2A120000     mov ecx,122A                         ; 计数器=122A
00404751     AC              lods byte ptr ds:[esi]
00404752     32C3            xor al,bl
00404754     FEC0            inc al
00404756     F9              stc
00404757     72 01           jb short Hello.0040475A
00404759     90              nop
0040475A     34 C9           xor al,0C9
0040475C     04 93           add al,93
0040475E     AA              stos byte ptr es:[edi]
0040475F     32D8            xor bl,al
00404761     32C3            xor al,bl
00404763     32D8            xor bl,al
00404765     49              dec ecx
00404766   ^ 7F E9           jg short Hello.00404751              ; 除了加了一条花指令没有任何的新意。

修改代码,范围00404768--00405991,大小0x122A

00404766   ^\7F E9           jg short Hello.00404751              ; 除了加了一条花指令没有任何的新意。
00404768     8D7415 00       lea esi,dword ptr ss:[ebp+edx]       ; esi=0040478B
0040476C     83C2 23         add edx,23
0040476F     8BFE            mov edi,esi
00404771     B9 07120000     mov ecx,1207                         ; 计数器=1207
00404776     33DB            xor ebx,ebx
00404778     AC              lods byte ptr ds:[esi]
00404779     32C3            xor al,bl
0040477B     FEC0            inc al
0040477D     34 13           xor al,13
0040477F     EB 01           jmp short Hello.00404782
00404781     CC              int3                                 ; 这条指令被跳过,不会执行的。
00404782     C0C0 03         rol al,3
00404785     AA              stos byte ptr es:[edi]
00404786     8AD8            mov bl,al
00404788     49              dec ecx
00404789   ^ 7F ED           jg short Hello.00404778              ; 循环开始

修改代码,范围0040478B--00405991,大小0x1207,全部都是修改一点,执行了循环又修改一点,没有任何意义的垃圾部分。

00404789   ^\7F ED           jg short Hello.00404778              ; 循环开始
0040478B     8D7415 00       lea esi,dword ptr ss:[ebp+edx]       ; esi=004047AE
0040478F     8BFE            mov edi,esi
00404791     80E3 00         and bl,0
00404794     B9 E4110000     mov ecx,11E4                         ; 计数器=0x11E4
00404799     AC              lods byte ptr ds:[esi]
0040479A     32C3            xor al,bl
0040479C     FEC0            inc al
0040479E     34 67           xor al,67
004047A0     2ADB            sub bl,bl
004047A2     74 02           je short Hello.004047A6
004047A4     90              nop
004047A5     90              nop
004047A6     04 37           add al,37
004047A8     AA              stos byte ptr es:[edi]
004047A9     86C3            xchg bl,al
004047AB     49              dec ecx
004047AC   ^ 7F EB           jg short Hello.00404799              ; 循环开始

修改代码,范围004047AE--00405991,大小0x11E4

004047AC   ^\7F EB           jg short Hello.00404799              ; 循环开始
004047AE     55              push ebp                             ; 终于有新指令了
004047AF     E8 0E000000     call Hello.004047C2                  ; F7
004047B4     90              nop
004047B5     E8 0E000000     call Hello.004047C8                  ; F7
004047BA     812C24 B1F8FFFF sub dword ptr ss:[esp],-74F          ; [0012FFA0]=0040407F-74F=004047CE
004047C1     C3              retn
004047C2     E8 FAFFFFFF     call Hello.004047C1                  ; F7
004047C7     C3              retn
004047C8     E8 F5FFFFFF     call Hello.004047C2
004047CD     C3              retn

要记住call会再堆栈中放入下一条指令地址作为返回地址,而retn是按照堆栈的返回地址返回,所以这里就是绕来绕去没有什么意义的指令,最后004047BA指令修改了堆栈的值,下一个retn执行就跳到了修改后的值004047CE处了。

004047CD     C3              retn
004047CE     F8              clc
004047CF     73 02           jnb short Hello.004047D3             ; 和上一条指令在一起,就是一个变相的jmp
004047D1     90              nop
004047D2     90              nop
004047D3     B9 0D000000     mov ecx,0D                           ; ecx=0x0D
004047D8     8DB5 BF030000   lea esi,dword ptr ss:[ebp+3BF]       ; esi=0040443E
004047DE     8D7C8E FC       lea edi,dword ptr ds:[esi+ecx*4-4]   ; edi=0040446E
004047E2     AD              lods dword ptr ds:[esi]              ; 调整数据窗口d 00404400找到这两个值,跟踪它们
004047E3     50              push eax                             ; 我们看看eax里出现了什么???GetProcAddress函数地址。
004047E4     8366 FC 00      and dword ptr ds:[esi-4],0           ; 把函数地址存入堆栈后,马上把它原来的内存地址清零,所以你一定要在od的数据窗口中追踪的这几个内存地址,就能清楚 的看到。
004047E8   ^ E2 F8           loopd short Hello.004047E2           ; 循环的读出函数地址存到堆栈

终于到达了壳的一个重要位置,从0040443E中读出13个函数的地址,存到堆栈后马上清除内存的原数据。执行完循环后,堆栈中压入的函数如下:
0012FF70   7C80B357  kernel32.GetModuleFileNameA
0012FF74   7C801A24  返回到 kernel32.CreateFileA
0012FF78   7C80180E  kernel32.ReadFile
0012FF7C   7C809B77  kernel32.CloseHandle
0012FF80   7C801A5D  kernel32.VirtualProtectEx
0012FF84   7C81E079  kernel32.OpenProcess
0012FF88   7C80994E  kernel32.GetCurrentProcessId
0012FF8C   7C80EB3F  kernel32.CreateMutexA
0012FF90   7C809B14  kernel32.VirtualFree
0012FF94   7C809A81  kernel32.VirtualAlloc
0012FF98   7C81CAA2  返回到 kernel32.ExitProcess
0012FF9C   7C801D77  kernel32.LoadLibraryA
0012FFA0   7C80AC28  kernel32.GetProcAddress
我们应该打开一个Win32API的帮助文件,以便在遇到不知道的函数时,快速的找到它的作用。

004047E8   ^\E2 F8           loopd short Hello.004047E2           ; 循环的读出函数地址存到堆栈
004047EA     8D75 81         lea esi,dword ptr ss:[ebp-7F]        ; esi=00404000
004047ED     BB 92190000     mov ebx,1992                                ;这次它就是计数器
004047F2     8D51 FF         lea edx,dword ptr ds:[ecx-1]         ; edx=FFFFFFFF
004047F5     33C0            xor eax,eax
004047F7     AC              lods byte ptr ds:[esi]               ; 数据窗口要找到00404000的数据
004047F8     32C2            xor al,dl
004047FA     D1E8            shr eax,1                            ; eax=0000004F
004047FC     73 08           jnb short Hello.00404806             ; 没有跳转
004047FE     EB 01           jmp short Hello.00404801
00404800     90              nop
00404801     35 E195C7CD     xor eax,CDC795E1
00404806     41              inc ecx
00404807     80E1 07         and cl,7
0040480A   ^ 75 EE           jnz short Hello.004047FA
0040480C     C1EA 08         shr edx,8
0040480F     33D0            xor edx,eax
00404811     4B              dec ebx
00404812   ^ 7F E1           jg short Hello.004047F5

这段循环从00404000开始,每次读出一字节,经过计算后的得到一个值,不断的循环着计算这一个累加值。范围00404000--00405992,大小0x1992,循环计算得到一个值,存在edx中。F4到下一行跳出循环。

00404812   ^\7F E1           jg short Hello.004047F5
00404814     74 02           je short Hello.00404818
00404816     90              nop
00404817     90              nop
00404818     B9 0D000000     mov ecx,0D                           ; 计数器=0x0D
0040481D     58              pop eax                              ; 从堆栈得到前面压入的壳的地址
0040481E     AB              stos dword ptr es:[edi]              ; 把他们倒着存入0040446E开头的内存地址
0040481F     83EF 08         sub edi,8
00404822   ^ E2 F9           loopd short Hello.0040481D

不要忘了,壳前面读出的函数地址依然还在堆栈中的,这里又循环着把它们倒着存了回去。

00404822   ^\E2 F9           loopd short Hello.0040481D
00404824     66:3395 551B000>xor dx,word ptr ss:[ebp+1B55]        ; [00405BD4]=00B5,edx中存着的就是前面计算得到的累加值
0040482B     8995 551C0000   mov dword ptr ss:[ebp+1C55],edx      ; [00405CD4]=83F9602A
00404831     F9              stc
00404832     72 01           jb short Hello.00404835              ; 变相jmp
00404834     90              nop
00404835     8DBD 3D1D0000   lea edi,dword ptr ss:[ebp+1D3D]      ; edi=00405DBC
0040483B     8B95 251D0000   mov edx,dword ptr ss:[ebp+1D25]      ; edx=[B6745D5E]
00404841     B9 C4010000     mov ecx,1C4                          ; 计数器=0x1C4
00404846     D1C2            rol edx,1
00404848     3017            xor byte ptr ds:[edi],dl             ; 和从[00405DBC]开始的值进行xor运算,改变了他的值,(数据窗口中注意跟随)
0040484A     42              inc edx
0040484B     47              inc edi
0040484C     49              dec ecx
0040484D   ^ 7F F7           jg short Hello.00404846                        ;循环计算

先是把原来在edx中的累加值又执行了一次xor后存到了[00405CD4]中,然后又从内存中读出了一个预定的值进行一系列的计算,计算中通过与内存中的数据进行xor运算,改变内存中的数据。修改数据,范围00405DBC--00405F7F,大小0x1C4.
通过在数据窗口的跟踪,我们来看看解密后的数据是如下:
00405DB0                                      B4 ED CE F3              错误
00405DC0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00405DD0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00405DE0  00 00 49 6E 74 65 72 6E 61 6C 20 4C 6F 61 64 65  ..Internal Loade
00405DF0  72 20 45 72 72 6F 72 21 00 54 68 69 73 20 50 72  r Error!.This Pr
00405E00  6F 67 72 61 6D 20 64 6F 65 73 20 6E 6F 74 20 72  ogram does not r
00405E10  75 6E 20 6F 6E 20 6D 61 63 68 69 6E 65 73 20 77  un on machines w
00405E20  69 74 68 20 61 63 74 69 76 65 20 73 79 73 74 65  ith active syste
00405E30  6D 20 64 65 62 75 67 67 65 72 21 00 45 72 72 6F  m debugger!.Erro
00405E40  72 20 77 68 69 6C 65 20 6C 6F 61 64 69 6E 67 20  r while loading
00405E50  61 20 44 4C 4C 21 00 45 72 72 6F 72 20 77 68 69  a DLL!.Error whi
00405E60  6C 65 20 6C 6F 63 61 74 69 6E 67 20 61 20 44 4C  le locating a DL
00405E70  4C 20 66 75 6E 63 74 69 6F 6E 21 00 44 65 63 6F  L function!.Deco
00405E80  6D 70 72 65 73 73 69 6F 6E 20 65 72 72 6F 72 21  mpression error!
00405E90  00 43 52 43 20 65 72 72 6F 72 21 20 46 69 6C 65  .CRC error! File
00405EA0  20 63 6F 6E 74 65 6E 74 20 68 61 73 20 62 65 65   content has bee
00405EB0  6E 20 6D 6F 64 69 66 69 65 64 2E 20 49 66 20 79  n modified. If y
00405EC0  6F 75 20 72 75 6E 20 61 20 73 79 73 74 65 6D 0D  ou run a system.
00405ED0  0A 64 65 62 75 67 67 65 72 2C 20 63 6C 65 61 72  .debugger, clear
00405EE0  20 61 6C 6C 20 62 72 65 61 6B 70 6F 69 6E 74 73   all breakpoints
00405EF0  20 62 65 66 6F 72 65 20 72 75 6E 6E 69 6E 67 20   before running
00405F00  74 68 69 73 20 70 72 6F 67 72 61 6D 21 00 49 6E  this program!.In
00405F10  74 65 67 72 69 74 79 20 63 68 65 63 6B 20 66 61  tegrity check fa
00405F20  69 6C 65 64 21 20 54 68 69 73 20 46 69 6C 65 20  iled! This File
00405F30  68 61 73 20 62 65 65 6E 20 6D 6F 64 69 66 69 65  has been modifie
00405F40  64 2E 0D 0A 52 65 61 73 6F 6E 20 6D 69 67 68 74  d...Reason might
00405F50  20 62 65 20 61 20 70 6F 73 73 69 62 6C 65 20 76   be a possible v
00405F60  69 72 75 73 20 69 6E 66 65 63 74 69 6F 6E 21 00  irus infection!.
00405F70  3A 0E AC D2 AC 67 B2 3B 3D 29 3A 83 E8 6B 4A D0  :_琯?=):冭kJ_
可以清晰地看是一些出错的提示信息。File content has been modified. If you run a system. .debugger, clearall breakpoints before runninghis program!看看这个提示信息,很明显是发现了调试器的提示信息。

0040484D   ^\7F F7           jg short Hello.00404846
0040484F     8DB5 1B080000   lea esi,dword ptr ss:[ebp+81B]       ; esi=0040489A
00404855     B9 F8100000     mov ecx,10F8                         ; 计数器=0x10F8
0040485A     BA 14000000     mov edx,14                               
0040485F     8BFE            mov edi,esi
00404861     B3 6F           mov bl,6F
00404863     AC              lods byte ptr ds:[esi]               ; 装入数据,(数据窗口跟随)
00404864     328415 06080000 xor al,byte ptr ss:[ebp+edx+806]     ; 和这一内存开头的地址[00404899]=1B作xor运算
0040486B     4A              dec edx
0040486C     7F 05           jg short Hello.00404873
0040486E     BA 14000000     mov edx,14
00404873     2C 8B           sub al,8B
00404875     32C1            xor al,cl
00404877     8807            mov byte ptr ds:[edi],al             ; 运算的结果存入了[0040489A]改变他们的数据。
00404879     D2C8            ror al,cl
0040487B     32C3            xor al,bl
0040487D     021F            add bl,byte ptr ds:[edi]
0040487F     AA              stos byte ptr es:[edi]               ; 这里才是最后的存入(存入地址不变)
00404880     49              dec ecx                                        ;计数器减1
00404881   ^ 7F E0           jg short Hello.00404863                        ;循环开始

edx=14的值是一个因子,通过不断的循环减少它,00404864的指令的以用下面不同的14个不同地址数据来作为类似密匙的作用来参与运算,解密数据。
00404880                    54 D6 95 75 85 FD 41 9F 70 48      
00404890  DC 92 B7 E4 8E 39 A3 6E 49 1B                  
这个循环的作用还是:解密代码,范围0040489A--00405992,大小0x10F8(我们前面已经看到壳已经无数遍的在变幻这个范围的代码了)。

00404881   ^ 7F E0           jg short Hello.00404863                        ;循环开始
00404883     F8              clc
00404884     73 14           jnb short Hello.0040489A             ; 变相jmp

解密出来后,马上调转到解密代码地址0040489A开始执行。

0040489A     60              pushad                            ; 保存了所有的寄存器(无论是做好事还是坏事,现保存了寄存器再说)
0040489B     8BFC            mov edi,esp                       ; edi=esp=0012FF84
0040489D     81EF 00020000   sub edi,200                       ; edi=0012FD84
004048A3     33C0            xor eax,eax                       ; eax清零
004048A5     AA              stos byte ptr es:[edi]            ; 把[0012FD84]的地址填充al的值(数据窗口看到时存储指令,就要提前定位到哪里找到0012FD84,跟踪者看看)
004048A6     FEC0            inc al                            ; 把al不停+1,这样填充值就是从00--FF了
004048A8   ^ 75 FB           jnz short Hello.004048A5          ; 循环,直到al到FF后重新回到0

用从00到FF的值填充堆栈,范围0012FD84--0012FE83,大小0xFF

004048A8   ^\75 FB           jnz short Hello.004048A5        ; 循环,直到al到FF后重新回到0
004048AA     8BFC            mov edi,esp                     ; edi=esp=0012FF84
004048AC     81EF 00010000   sub edi,100                     ; edi=0012FE84
004048B2     33DB            xor ebx,ebx
004048B4     8DB5 F11E0000   lea esi,dword ptr ss:[ebp+1EF1] ; esi=00405F70
004048BA     B9 10000000     mov ecx,10
004048BF     A4              movs byte ptr es:[edi],byte ptr>; 要把[00405F70]=3A的数据移动到[0012FE84]堆栈的地址空间去
004048C0     FEC3            inc bl                                ;这个指针决定了BL要到FF重新归0才会跳出循环,也就要要移动0xFF大小
004048C2     74 05           je short Hello.004048C9
004048C4     49              dec ecx                                ;这个指针归零后,004048B4指令让他重新指向00405F70,也就是说移入堆栈的是是00405F70---00405F80的数据,不断的循环放入。
004048C5   ^ 75 F8           jnz short Hello.004048BF
004048C7   ^ EB EB           jmp short Hello.004048B4

用00405F70--00405F80的值填充堆栈,范围0012FE84--0012FF83,大小0xFF(和上面的循环所填充的地址是相连的)。你还可以看到它刚好填充到了当前堆栈指针esp的位置。

004048C7   ^\EB EB           jmp short Hello.004048B4
004048C9     8BFC            mov edi,esp                     ; edi=esp=0012FF84
004048CB     81EF 00020000   sub edi,200                     ; edi=0012FD84(这个地址就是被刚被填充了00-FF的开头地址)
004048D1     8BF4            mov esi,esp                     ; esi=esp=0012FF84
004048D3     81EE 00010000   sub esi,100                     ; esi=0012FE84(这个地址就是刚被填充了循环数据的地址)
004048D9     33C9            xor ecx,ecx
004048DB     33C0            xor eax,eax
004048DD     02040F          add al,byte ptr ds:[edi+ecx]
004048E0     02040E          add al,byte ptr ds:[esi+ecx]
004048E3     8A1C07          mov bl,byte ptr ds:[edi+eax]
004048E6     8A3C0F          mov bh,byte ptr ds:[edi+ecx]
004048E9     881C0F          mov byte ptr ds:[edi+ecx],bl
004048EC     883C07          mov byte ptr ds:[edi+eax],bh
004048EF     FEC1            inc cl
004048F1   ^ 75 EA           jnz short Hello.004048DD        ; 又在这两个堆栈地址计算一通后,重新填了进去

没有发现什么有意义的操作,继续

004048F1   ^\75 EA           jnz short Hello.004048DD        ; 又在这两个堆栈地址计算一通后,重新填了进去
004048F3     8DBD 5D1C0000   lea edi,dword ptr ss:[ebp+1C5D] ; edi=00405CDC
004048F9     8BF4            mov esi,esp                     ; esi=0012FF84
004048FB     81EE 00020000   sub esi,200                     ; esi=0012FD84
00404901     BA BC000000     mov edx,0BC
00404906     33C9            xor ecx,ecx
00404908     33C0            xor eax,eax
0040490A     FEC1            inc cl
0040490C     02040E          add al,byte ptr ds:[esi+ecx]
0040490F     8A1C0E          mov bl,byte ptr ds:[esi+ecx]
00404912     8A3C06          mov bh,byte ptr ds:[esi+eax]
00404915     881C06          mov byte ptr ds:[esi+eax],bl
00404918     883C0E          mov byte ptr ds:[esi+ecx],bh
0040491B     02DF            add bl,bh
0040491D     0FB6DB          movzx ebx,bl
00404920     8A041E          mov al,byte ptr ds:[esi+ebx]
00404923     301F            xor byte ptr ds:[edi],bl        ; 注意这里,终于不是在堆栈里绕了,它更改了[00405CDC]的值(数据窗口跟踪)
00404925     47              inc edi
00404926     4A              dec edx                         ; 计数器是edx=0xBC
00404927   ^ 75 E1           jnz short Hello.0040490A

这个循环又在填充内存了:范围00405CDC--00405D98,大小0xBC

00404929     61              popad                              ; 恢复了寄存器
0040492A     8BC5            mov eax,ebp                        ; eax=ebp=0040407F
0040492C     2B85 3D1C0000   sub eax,dword ptr ss:[ebp+1C3D]    ; ss:[00405CBC]=00004000
00404932     83E8 7F         sub eax,7F
00404935     8985 411C0000   mov dword ptr ss:[ebp+1C41],eax    ; eax=00400000,这个是程序的基址,被存入了[00405CC0]
0040493B     8D7D 81         lea edi,dword ptr ss:[ebp-7F]      ; edi=00404000
0040493E     57              push edi
0040493F     BB 00040000     mov ebx,400
00404944     8D77 08         lea esi,dword ptr ds:[edi+8]       ; esi=00404008
00404947     68 04010000     push 104
0040494C     57              push edi                                        ;00404000
0040494D     50              push eax                                        ;00400000
0040494E     FF95 EF030000   call dword ptr ss:[ebp+3EF]        ; 开始调用函数了GetModuleFileNameA

开始做有意义的事了
DWORD GetModuleFileName(

    HMODULE hModule,        // handle to module to find filename for
    LPTSTR lpFilename,        // pointer to buffer for module path
    DWORD nSize         // size of buffer, in characters
   );
The GetModuleFileName function retrieves the full path and filename for the executable file containing the specified module.
在数据窗口中跟踪00404000,就可以看到函数返回的程序路径了

0040494E     FF95 EF030000   call dword ptr ss:[ebp+3EF]        ; 开始调用函数了GetModuleFileNameA
00404954     56              push esi
00404955     33C0            xor eax,eax
00404957     8D48 FF         lea ecx,dword ptr ds:[eax-1]       ; ecx=FFFFFFFF
0040495A     FC              cld
0040495B     F2:AE           repne scas byte ptr es:[edi]       ; 每扫描一个数据就把ecx减1,最后再not,就
0040495D     F7D1            not ecx                            ; 上面四条指令求出返回的路径长度
0040495F     8D51 FF         lea edx,dword ptr ds:[ecx-1]       ; 返回的字符串以0结束,这里把它去掉,存入edx

得到返回的完整路径

0040495F     8D51 FF         lea edx,dword ptr ds:[ecx-1]       ; 返回的字符串以0结束,这里把它去掉,存入edx
00404962     FD              std
00404963     4F              dec edi
00404964     B0 5C           mov al,5C                          ; 5C就是\的ASCII码,
00404966     F2:AE           repne scas byte ptr es:[edi]                ;通过扫描完整路径中的\来得到单独文件名
00404968     FC              cld
00404969     47              inc edi
0040496A     85C9            test ecx,ecx
0040496C     74 02           je short Hello.00404970
0040496E     41              inc ecx
0040496F     47              inc edi
00404970     2BD1            sub edx,ecx
00404972     8BCA            mov ecx,edx
00404974     83E1 1F         and ecx,1F
00404977     8BF7            mov esi,edi                        ; 壳把返回路径的其他部分去掉,得出程序的名字

得到单独的文件名

00404979     8DBD 3D1D0000   lea edi,dword ptr ss:[ebp+1D3D]                   ; edi=00405DBC
0040497F     F3:A4           rep movs byte ptr es:[edi],byte ptr ds:[esi]      ; 把esi指向的文件名移动到edi指向的00405DBC
00404981     5E              pop esi
00404982     5F              pop edi

保存了单独的文件名

00404983     6A 00           push 0
00404985     68 80000000     push 80
0040498A     6A 03           push 3
0040498C     6A 00           push 0
0040498E     6A 01           push 1
00404990     68 00000080     push 80000000
00404995     57              push edi
00404996     FF95 EB030000   call dword ptr ss:[ebp+3EB]                       ; 这里是CreateFileA函数,eax是返回的文件句柄

HANDLE CreateFile(

    LPCTSTR lpFileName,        // pointer to name of the file
    DWORD dwDesiredAccess,        // access (read-write) mode
    DWORD dwShareMode,        // share mode
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,        // pointer to security attributes
    DWORD dwCreationDistribution,        // how to create
    DWORD dwFlagsAndAttributes,        // file attributes
    HANDLE hTemplateFile         // handle to file with attributes to copy  
   );
The CreateFile function creates or opens the following objects and returns a handle that can be used to access the object:
壳按照得到的文件路径,打开了这个文件

0040499C     50              push eax
0040499D     6A 00           push 0
0040499F     57              push edi                                          ; 00404000
004049A0     53              push ebx                                          ; 00000400
004049A1     56              push esi                                          ; 00404008
004049A2     50              push eax                                          ; 00000030
004049A3     FF95 E7030000   call dword ptr ss:[ebp+3E7]                       ; 这里是ReadFile函数

BOOL ReadFile(

    HANDLE hFile,        // handle of file to read
    LPVOID lpBuffer,        // address of buffer that receives data  
    DWORD nNumberOfBytesToRead,        // number of bytes to read
    LPDWORD lpNumberOfBytesRead,        // address of number of bytes read
    LPOVERLAPPED lpOverlapped         // address of structure for data
   );       
The ReadFile function reads data from a file, starting at the position indicated by the file pointer. After the read operation has been completed, the file pointer is adjusted by the number of bytes actually read
壳用ReadFile函数从文件中读取了0x400字节的数据到00404008,我们在od中要在数据窗口中d 00404000,找到00404008开头的0x400的读入数据,我们还用Hex Workshop打开这个文件,和00404000比较看看。

004049A9     FF95 E3030000   call dword ptr ss:[ebp+3E3]       ; kernel32.CloseHandle

关闭打开的文件句柄

004049A9     FF95 E3030000   call dword ptr ss:[ebp+3E3]    ; 这里是CloseHandle函数
004049AF     8B46 3C         mov eax,dword ptr ds:[esi+3C]  ; esi是DOS文件头的开头,+3C就到了指向PE文件头的指针处e_lfanew了(可以去看PE文件的数据结构)
004049B2     33C9            xor ecx,ecx
004049B4     03C6            add eax,esi                    ; 基址+RVA就到了指向PE标志的地方了
004049B6     2148 58         and dword ptr ds:[eax+58],ecx  ; PE文件头+0x58我们就来到了IMAGE_OPTIONAL_HEADER32结构的CheckSum字段,把它清零了。
004049B9     8D41 FF         lea eax,dword ptr ds:[ecx-1]   ; eax=FFFFFFFF
004049BC     BF 2083B8ED     mov edi,EDB88320
004049C1     33D2            xor edx,edx
004049C3     8A16            mov dl,byte ptr ds:[esi]       ; esi=00404008从文件的开始读取了一个字节
004049C5     32D0            xor dl,al
004049C7     D1EA            shr edx,1
004049C9     73 02           jnb short Hello.004049CD
004049CB     33D7            xor edx,edi
004049CD     41              inc ecx
004049CE     80E1 07         and cl,7
004049D1   ^ 75 F4           jnz short Hello.004049C7
004049D3     C1E8 08         shr eax,8
004049D6     33C2            xor eax,edx
004049D8     46              inc esi
004049D9     4B              dec ebx
004049DA   ^ 7F E5           jg short Hello.004049C1        ; 开始循环
004049DC     F7D0            not eax
004049DE     3185 5F1B0000   xor dword ptr ss:[ebp+1B5F],ea>; [00405BDE]=39C521A3

先是定位到了CheckSum字段,把它清零,把读入的0x400大小的数据,分别按字节计算,得到一个累加值放在eax中,知道这些后,我们就可以跳出循环了,F4到下一行004049DC。最后程序把这个值not后,存了起来在[00405BDE],最后eax的值eax=C63ADE5C

004049E4     6A 0C          push 0C
004049E6     59             pop ecx                          ; ecx=0x0C
004049E7     8DB5 C3030000  lea esi,dword ptr ss:[ebp+3C3]   ; esi=00404442
004049ED     FF76 04        push dword ptr ds:[esi+4]        ; ds:[00404446]=7C81CAA2 (kernel32.ExitProcess)
004049F0     AD             lods dword ptr ds:[esi]          ; eax=7C801D77 (kernel32.LoadLibraryA)
004049F1     85C0           test eax,eax
004049F3     74 05          je short Hello.004049FA
004049F5   ^ E2 F9          loopd short Hello.004049F0       ; 检测一下所有的函数地址还在不在
004049F7     F9             stc
004049F8     72 08          jb short Hello.00404A02          ; 变相jmp

注意在数据窗口跟踪,全部没有问题,那么程序就继续了

00404A03     8DB5 D3030000  lea esi,dword ptr ss:[ebp+3D3]   ; esi=00404452
00404A09     8B36           mov esi,dword ptr ds:[esi]       ; ds:[00404452]=7C80EB3F (kernel32.CreateMutexA)
00404A0B     AC             lods byte ptr ds:[esi]
00404A0C     04 34          add al,34
00404A0E     75 0C          jnz short Hello.00404A1C
00404A10     81ED 21B74000  sub ebp,40B721
00404A16     0F85 C40B0000  jnz Hello.004055E0
00404A1C     8D9D E11C0000  lea ebx,dword ptr ss:[ebp+1CE1]  ; ebx=00405D60
00404A22     6A 40          push 40
00404A24     59             pop ecx                          ; ecx=0x40
00404A25     8033 CC        xor byte ptr ds:[ebx],0CC
00404A28     43             inc ebx
00404A29   ^ E2 FA          loopd short Hello.00404A25       ; 从00405D60开始的数据与0xCC进行xor运算,大小0x40

范围00405D60--00405D90,大小0x40,原来那里全是00的数据,xor后全部是CC的数据了

00404A2B     8D85 E11C0000  lea eax,dword ptr ss:[ebp+1CE1]  ; eax=00405D60就是刚才的修改地址
00404A31     33DB           xor ebx,ebx                      ; ebx=0
00404A33     3818           cmp byte ptr ds:[eax],bl         ; 用刚才修改的CC的数据与0比较
00404A35     74 24          je short Hello.00404A5B
00404A37     75 01          jnz short Hello.00404A3A
00404A39     90             nop
00404A3A     8038 CC        cmp byte ptr ds:[eax],0CC        ; 和CC比较,这个就相等了
00404A3D     74 1C          je short Hello.00404A5B

进行比较后跳转

00404A5B     8BF8           mov edi,eax
00404A5D     6A 10          push 10
00404A5F     F7D0           not eax                          ; not后eax=FFBFA29F
00404A61     59             pop ecx                          ; ecx=前面一条指令压入堆栈的0x10
00404A62     D3E8           shr eax,cl                       ; eax=0000FFBF
00404A64     F3:AB          rep stos dword ptr es:[edi]      ; 把00405D60的数据从CC CC CC CC全部又变为eax的值
00404A66     E8 01000000    call Hello.00404A6C                        ;继续前进

没有意义的几段代码,最后00405D60---00405D9F成了这样的:
00405D60  BF FF 00 00 BF FF 00 00 BF FF 00 00 BF FF 00 00  ?..?..?..?..

00404A6C     8A85 561B0000  mov al,byte ptr ss:[ebp+1B56]    ; ss:[00405BD5]=00
00404A72     8885 7D0A0000  mov byte ptr ss:[ebp+A7D],al     ; [00404AFC]=F9
00404A78     892C24         mov dword ptr ss:[esp],ebp       ; ebp=0040407F (Hello.0040407F0)这个地址好像是一个类似指针的东西,壳前面通过在他的基础上+ 一个数,就可以指向它要操作的数据
00404A7B     90             nop
00404A7C     8D85 110A0000  lea eax,dword ptr ss:[ebp+A11]   ; 地址=00404A90
00404A82     66:2BDB        sub bx,bx
00404A85     50             push eax
00404A86     33C0           xor eax,eax
00404A88     64:FF30        push dword ptr fs:[eax]
00404A8B     64:8920        mov dword ptr fs:[eax],esp       ; seh=00404A90,壳又要产生异常了
00404A8E     EB 11          jmp short Hello.00404AA1

设置了seh,马上又要异常了

00404AA1     66:F7F3        div bx                                        ;跳转过来就异常了

ctrl+g输入00404A90,在那里下断点,F9忽略异常,马上就断下来了

00404A90     8B6424 08      mov esp,dword ptr ss:[esp+8]     ; 修改堆栈指针,指向另一个位置
00404A94     8B6C24 08      mov ebp,dword ptr ss:[esp+8]     ; ebp=0040407F壳前面把它保存了,就要随时的使用它来定位到相关的数据
00404A98     8D85 310A0000  lea eax,dword ptr ss:[ebp+A31]   ; 地址=00404AB0(这里也是ebp作为基址+一个数来定位到想去的地方)
00404A9E     50             push eax
00404A9F     C3             retn                             ; 和上一条指令在一起,这就是一个jmp,跳转到压入的地址00404AB0

00404AB0     8D85 430A0000  lea eax,dword ptr ss:[ebp+A43]   ; 地址=00404AC2
00404AB6     894424 04      mov dword ptr ss:[esp+4],eax     ; 这个很关键,注意看堆栈窗口,这是在修改seh的处理地址
00404ABA     64:67:8926 000>mov dword ptr fs:[0],esp         ; 放入fs:[0],这就意味着seh=上一条指令压入的地址00404AC2
00404AC0     EB 12          jmp short Hello.00404AD4                ;前进

建了seh,看来又有异常了

00404AD4     9C             pushfd                           ; 注意了,这是在保存CPU的标志寄存器,壳可以做很多事的。
00404AD5     810C24 0001000>or dword ptr ss:[esp],100        ; 要把第九位置1,这是标志寄存器的Trap Flag (TF),1表示允许单步调试,0表示不允许单步调试,壳设置了1
00404ADC     75 02          jnz short Hello.00404AE0
00404ADE     90             nop
00404ADF     90             nop
00404AE0     9D             popfd                            ; 把修改的值放回标志寄存器
00404AE1     EB 01          jmp short Hello.00404AE4

这里壳把TF位置1,壳要自己产生单步异常了,我们不能再单步运行了,直接去seh的地址设断点,断下程序。

00404AC2     8B6424 08      mov esp,dword ptr ss:[esp+8]     ; 修改堆栈指针
00404AC6     8B6C24 08      mov ebp,dword ptr ss:[esp+8]     ; 又要用到0040407F这个基址来定位数据了
00404ACA     8D85 6C0A0000  lea eax,dword ptr ss:[ebp+A6C]   ; 地址=00404AEB
00404AD0     50             push eax
00404AD1     C3             retn                             ; 变相jmp,跳转到了00404AEB

前进

00404AEB     8D85 840A0000  lea eax,dword ptr ss:[ebp+A84]   ; 地址=00404B03
00404AF1     894424 04      mov dword ptr ss:[esp+4],eax     ; 把上面的地址放入seh的数据位置
00404AF5     64:67:8926 000>mov dword ptr fs:[0],esp         ; 设置seh=00404B03
00404AFB     E9 00000000    jmp Hello.00404B00
00404B00     EB 1F          jmp short Hello.00404B21

也是设置seh=00404B03

00404B21     2BC0           sub eax,eax                      ; 清零
00404B23     74 02          je short Hello.00404B27
00404B25     90             nop
00404B26     90             nop
00404B27     CC             int3                                        ;这里异常了
00404B28     90             nop

去seh地址下断点拦截下程序

00404B03     8B6424 08      mov esp,dword ptr ss:[esp+8]     ; 修改堆栈指针
00404B07     8B6C24 08      mov ebp,dword ptr ss:[esp+8]     ; 又要用到0040407F这个基址来定位数据了
00404B0B     8D85 AF0A0000  lea eax,dword ptr ss:[ebp+AAF]   ; 地址=00404B2E
00404B11     50             push eax
00404B12     EB 02          jmp short Hello.00404B16
00404B14     90             nop
00404B15     90             nop
00404B16     81B5 591C0000 >xor dword ptr ss:[ebp+1C59],83ACE8EB   ;[00405CD8]=1788A84B  xor后A0 40 24 94
00404B20     C3             retn                                   ; 返回到前面eax压入的地址

修改了一处数据,前进

00404B2E     8D85 C20A0000  lea eax,dword ptr ss:[ebp+AC2]         ; 地址=00404B41
00404B34     894424 04      mov dword ptr ss:[esp+4],eax
00404B38     64:67:8926 000>mov dword ptr fs:[0],esp               ; 一样的设置了seh=00404B41
00404B3E     EB 1E          jmp short Hello.00404B5E

设了seh,异常又要来了

00404B5E     2BDB           sub ebx,ebx
00404B60     8BD3           mov edx,ebx                            ; ebx,edx清零
00404B62     B8 72472388    mov eax,88234772
00404B67     F7F3           div ebx                                ; 这里来了个整数除以零的异常
00404B69     85D2           test edx,edx
00404B6B     0F84 94000000  je Hello.00404C05

去seh地址下断点拦截下程序

00404B41     8B6424 08      mov esp,dword ptr ss:[esp+8]           ; 改指针
00404B45     8B6C24 08      mov ebp,dword ptr ss:[esp+8]           ; 得到0040407F
00404B49     8D85 F20A0000  lea eax,dword ptr ss:[ebp+AF2]         ; 地址=00404B71
00404B4F     50             push eax
00404B50     EB 01          jmp short Hello.00404B53
00404B52     90             nop
00404B53     8185 591C0000 >add dword ptr ss:[ebp+1C59],6307582F   ; [00405CD8]=F72B98CF,这个地址也是前面已经改过一次了

修改了一处数据(和前面相同的一处),前进

00404B71     8D85 050B0000  lea eax,dword ptr ss:[ebp+B05]         ; 地址=00404B84
00404B77     894424 04      mov dword ptr ss:[esp+4],eax
00404B7B     64:67:8926 000>mov dword ptr fs:[0],esp               ; seh=00404B84
00404B81     EB 11          jmp short Hello.00404B94

设了seh,异常又要来了

00404B94     66:B8 0043     mov ax,4300                            ; eax=00404300
00404B98     EB 02          jmp short Hello.00404B9C
00404B9A     90             nop
00404B9B     90             nop
00404B9C     81B5 591C0000 >xor dword ptr ss:[ebp+1C59],FD87DD26   ; [00405CD8]=0AAC45E9,又改一次
00404BA6     CD 68          int 68                                                ;这里异常
00404BA8     66:05 7B0C     add ax,0C7B

修改了一处数据(和前面相同的一处),去seh地址下断点拦截下程序

00404B84     8B6424 08      mov esp,dword ptr ss:[esp+8]           ; 改指针
00404B88     8B6C24 08      mov ebp,dword ptr ss:[esp+8]           ; 得到基址
00404B8C     8D85 310B0000  lea eax,dword ptr ss:[ebp+B31]         ; 地址=00404BB0
00404B92     50             push eax
00404B93     C3             retn                                   ; 返回到00404BB0

继续前进

00404BB0     8D85 450B0000  lea eax,dword ptr ss:[ebp+B45]         ; 地址=00404BC4
00404BB6     894424 04      mov dword ptr ss:[esp+4],eax
00404BBA     64:67:8926 000>mov dword ptr fs:[0],esp               ; seh=00404BC4
00404BC0     EB 1F          jmp short Hello.00404BE1

设了seh,异常又要来了

00404BE1    /EB 01          jmp short Hello.00404BE4
00404BE3    |90             nop
00404BE4    \33DB           xor ebx,ebx
00404BE6     8BC3           mov eax,ebx                            ; ebx,eax清零
00404BE8     66:BE 4746     mov si,4647
00404BEC     66:BF 4D4A     mov di,4A4D
00404BF0     CC             int3                                                ;这里异常了int3就是一个异常
00404BF1     90             nop

去seh地址下断点拦截下程序

00404BC4     8B6424 08      mov esp,dword ptr ss:[esp+8]           ; 改指针
00404BC8     8B6C24 08      mov ebp,dword ptr ss:[esp+8]           ; 得到基址,用它壳可以定为到自己预先的数据位置
00404BCC     8D85 7A0B0000  lea eax,dword ptr ss:[ebp+B7A]         ; 地址=00404BF9
00404BD2     50             push eax
00404BD3     EB 01          jmp short Hello.00404BD6
00404BD5     90             nop
00404BD6     81AD 591C0000 >sub dword ptr ss:[ebp+1C59],D3C0AAAE   ; 还是那个地址ss:[00405CD8]=36EB9B3B
00404BE0     C3             retn                                   ; 前进到00404BF9

修改了一处数据(和前面相同的一处),前进

00404BF9     64:67:8F06 000>pop dword ptr fs:[0]                   ; 释放了seh结构,我们的seh之旅要告一段落了
00404BFF     83C4 04        add esp,4
00404C02     5D             pop ebp                                ; ebp得到基址0040407F
00404C03     EB 16          jmp short Hello.00404C1B

释放了seh,前进

00404C1B     8BDD           mov ebx,ebp
00404C1D     81ED 21B74000  sub ebp,40B721                         ; 这里呢要操作那个关键的基质,所以前一条指令,先把它存到ebx,后面可能还是要用它来定位数据的
00404C23     8DBD E4BA4000  lea edi,dword ptr ss:[ebp+40BAE4]      ; 地址=00404442,这里还是用计算后的ebp值来定位数据,作为基址
00404C29     8BB5 D8D24000  mov esi,dword ptr ss:[ebp+40D2D8]      ; ss:[00405C36]=77D5050B (user32.MessageBoxA)
00404C2F     AC             lods byte ptr ds:[esi]                 ; 数据窗口跟随,eax=00404B8B
00404C30     04 34          add al,34                              ; al=BF
00404C32     0F84 A8090000  je Hello.004055E0                      ; 没有跳转
注意下面:
00404C38     6A 0A          push 0A
00404C3A     59             pop ecx                                ; ecx得到了上一条指令的0x0A
00404C3B     8B37           mov esi,dword ptr ds:[edi]             ; esi=7C801D77 (kernel32.LoadLibraryA)
00404C3D     83C7 04        add edi,4
00404C40     AC             lods byte ptr ds:[esi]
00404C41     04 34          add al,34
00404C43     0F84 8D060000  je Hello.004052D6                                ;没有跳转
00404C49   ^ E2 F0          loopd short Hello.00404C3B             ; 这段循环就是把前面出现的函数的首字节拿出
这是一个检测断点的循环,它把前面出现的像LoadLibraryA等等函数的系统地址的首字节拿出来,把它+0x34看看是不是等于00,哪一个数据+34会等于零呢? 就是0xCC,0xCC+0x34=0x100,也就是说这是在检测这些系统地址的首字节是不是0xCC,而我们要知道当我们在这些函数上下着断点的话,它的首字节就是0xCC,所以这段循环就是看看我们有没有在函数上设了断点。我的没有设,所以壳没有跳转。
2008-4-15 16:08
0
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
发送时太慢了,分成了两部分,后面发送颠倒了,把它交换了连起来才是完整的一篇,还不能发附件,以后连加了壳的hello.exe程序再发上来
2008-4-15 17:10
0
雪    币: 100
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
太长了 看不懂饿 最好做个视频教程!
2008-4-15 17:59
0
雪    币: 207
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
长是长了点,毕竟是单步跟踪么,从开头到oep的完整记录,我也不会做视频教程,如果还有刚学脱壳的朋友可以看一下。
2008-4-15 23:00
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
好长,我现在是刚学习脱壳,但没没有汇编基础,感觉学习脱壳有点难度,在硬着头皮啃。关键是不知道在哪里应该跳过, 不明白是什么意思。希望楼主给点学习这方面知识的指点!!!
2008-9-7 16:18
0
雪    币: 846
活跃值: (221)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
7
又多一个懂脱壳的了,楼主下一步该学怎么写脚本跑壳了
2008-9-7 17:18
0
雪    币: 317
活跃值: (93)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
8
强大,我见过最详细的脱文啦!
2008-9-7 23:45
0
雪    币: 97697
活跃值: (200839)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
9
support.
2008-9-8 00:18
0
雪    币: 231
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
qdk
10
[QUOTE='UD]Arthas;441463']加密对象:罗云彬的〈Windows环境下32位汇编语言程序设计〉第三章提供的对话框程序

源代码:
;>>>>>>>>>>>>>>>>>>>>>>>>...[/QUOTE]

顶顶顶顶

我正在脱相同的壳,就卡在除零异常那里(第一次比较认真的脱壳,对各种anti还不是很了解)。

看到你这篇文章,真是醍醐灌顶,如饮甘琼。

非常感谢
2008-9-14 03:10
0
游客
登录 | 注册 方可回帖
返回
//