-
-
[原创]单步跟踪tElock v 0.98
-
发表于: 2008-4-15 16:06 13191
-
继续:
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直播授课