|
[注意]招聘帖 - 上海天联世纪信息技术有限公司
我想知道我可不可以,在《人才资源库》版块,我发着一个介绍。 |
|
|
|
[转帖]软件破解常用汇编指令
我想说几句题外话!不爱听的就当没有见过 你去记上面的哪个东西根本没有意思,还是好的打基础吧。 IBM PC汇编程序设计,好好去学一下,Intel的三卷集下来,闲着就多翻翻。 罗云彬的Win32汇编语言设计买一本来,或者是网上的Win32教程找来,比什么都强, 操作系统原理,去找一本来看看,不知道什么是基础,这就是基础, 我并不是反对楼主贴的东西,只是看到下面的跟贴,我觉得不应该是这样的,万丈高楼平地起,千万不可空中楼阁,更何况这个口诀连空中楼阁都算不上 |
|
|
|
|
|
[求助]retn、retn 4和retn 0C有什么区别??
所谓的调用方式是指: 在调用一个函数时,约定的传递参数方式和平衡堆栈方式,例如: 有一个函数,它有两个参数 xyz(first,second) 当我们使用stdcall调用方式时,也就是Windows调用API的常见方式(又几个函数例外),它的调用方式如下: xxxxxxx1 push second xxxxxxx2 push first xxxxxxx3 call xyz 参数传递从右到左 堆栈的平衡也是有被调用函数负责, 如果是c调用方式如下: xxxxxxx1 push second xxxxxxx2 push first xxxxxxx3 call xyz xxxxxxx4 add esp 08 参数传递从右到左 堆栈的平衡要有调用函数负责, 如果是pascal调用方式如下: xxxxxxx1 push first xxxxxxx2 push second xxxxxxx3 call xyz 参数传递从左到右 堆栈的平衡有被调用函数负责。 这是简要的解释,不明白就去google上搜篇文章看看吧。 |
|
[求助]dll显式加载与隐式加载的区别?
首先说明我说的是用户级的显式和隐式DLL加载 最明显的加载:最就是在程序的导入表中直接就有你要加载的DLL,就是我们通常做的哪样 隐藏一点加载:在程序中用LoadLibrary函数来加载要用的DLL再用GetProcAddress来得到想要的函数地址 再隐藏一点的加载:通过系统创建进程时在程序中放置的返回Kernel32.Dll的地址,按页边界来寻找Kernel32.Dll库,再从它的导出表中得到LoadLibrary和GetProcAddress函数,然后来得到想要的Dll和函数,这个方法可以不用任何函数来得到Kernel32.dll,其它的DLL还是要用LoadLibrary来得到的。 再再隐藏一点的加载:不知道了,呵呵,编程大牛们好像可以自己写一个LoadLibrary 的加载函数的,不过再怎么变,到了系统底层你还是一样要用里面的函数的。 |
|
[求助]retn、retn 4和retn 0C有什么区别??
使用retn, 因为你说的是在call的头部返回,也就是被调用过程的第一条指令处返回(我没有曲解你的意思吧)。 这是还没有分配局部变量,也没有往堆栈中压入数值,那么esp指令指向的依然是call指令的返回地址,所以你直接返回就可以了,使用retn xxxxxxx1 call xxxxxx10 xxxxxxx5 当执行call指令时,系统会把xxxxxxx5作为返回地址压入堆栈 而在被调用过程完成它的工作后,retn指令就是读取堆栈顶部的地址,然后返回到哪里 所以,通常情况下,在被调用过程里,分配了多少局部变量,也就是形如 add esp -xx 或着 sub esp xx 这就是在分配局部变量 最后过程最后也会释放多少 但是有时程序故意要修改返回地址让程序返回到另一个地方,最简单的就是这样 push xxxxxx20 retn 这样由于修改了堆栈数据,retn指令是只管读出堆栈顶部的值作为返回地址,我们就返回 到了xxxxxx20的地方了。 如果你要在被调用过程的中间,只是返回到原来的返回地址,那么你注意好堆栈平衡就可以了。 |
|
[原创]单步跟踪ASProtect Version 1.4(未完,更新到申请TLS的部分)
跟踪ASProtect比跟踪TeLock有意思多了,ASProtect有很多精秒代码,而且还可以复习好多东西 动态获取kernel32,dll库,得到里面的函数 动态申请内存后把自身搬过去执行,然后你就可以看到它手动进行代码的重定位, 其实壳要使用n多的函数,所以你还可以看到壳利用GetProAddresshe和LoadLibrary来得到一大堆的它想要的函数, 申请TLS 寻找E8和E9然后修改后面的数据,E8和E9就是call和jmp指令,这样就可以跳到其他地方了 把多个call的地址放在内存里,然后在循环里放到寄存器里,然后在进行跳转, 有意思的地方太多了,要继续 |
|
[原创]单步跟踪ASProtect Version 1.4(未完,更新到申请TLS的部分)
分配的动态地址的基址=003C0000 004045CB 55 push ebp ; ebp=004043F0 004045CC 8BEC mov ebp,esp 004045CE 60 pushad 004045CF 55 push ebp 004045D0 8B75 08 mov esi,dword ptr ss:[ebp+8] ; 堆栈 ss:[0012FF9C]=00404B38 004045D3 8B7D 0C mov edi,dword ptr ss:[ebp+C] ; 堆栈 ss:[0012FFA0]=003C0000 这里我们要说的就是【ebp+8】与【ebp+C】这样的读取方式,它实际上读取的就是在执行call前压入堆栈的参数,【ebp+8】是第一个参数,【ebp+C】是第二个我们可以简单的说一下为什么?看一下堆栈结构 0012FF94 \004043F0 返回到 ASP_Firs.004043F0 这个是保存push ebp时压入的值 [ebp+0] 0012FF98 004044C0 返回到 ASP_Firs.004044C0 这个是调用call时压入的返回地址 [ebp+4] 0012FF9C 00404B38 ASP_Firs.00404B38 这个是call前ebx压入的参数 [ebp+8] 0012FFA0 003C0000 这个是call前eax压入的参数 [ebp+C] 前面的两个值都是固定要push进去的,所以你要读取call前压入堆栈的数据就要从[ebp+8]开始 004045D6 FC cld 004045D7 B2 80 mov dl,80 004045D9 8A06 mov al,byte ptr ds:[esi] ; 读出的数据来自【00404B38】 004045DB 46 inc esi 004045DC 8807 mov byte ptr ds:[edi],al ; 存入到【003C0000】,也就是动态分配的内存里 004045DE 47 inc edi 004045DF 02D2 add dl,dl 004045E1 75 05 jnz short ASP_Firs.004045E8 004045E3 8A16 mov dl,byte ptr ds:[esi] 004045E5 46 inc esi 004045E6 12D2 adc dl,dl 004045E8 ^ 73 EF jnb short ASP_Firs.004045D9 004045EA 02D2 add dl,dl 004045EC 75 05 jnz short ASP_Firs.004045F3 004045EE 8A16 mov dl,byte ptr ds:[esi] 004045F0 46 inc esi 004045F1 12D2 adc dl,dl 004045F3 73 4A jnb short ASP_Firs.0040463F 004045F5 33C0 xor eax,eax 004045F7 02D2 add dl,dl 004045F9 75 05 jnz short ASP_Firs.00404600 004045FB 8A16 mov dl,byte ptr ds:[esi] 004045FD 46 inc esi 004045FE 12D2 adc dl,dl 00404600 0F83 D6000>jnb ASP_Firs.004046DC 00404606 02D2 add dl,dl 00404608 75 05 jnz short ASP_Firs.0040460F 0040460A 8A16 mov dl,byte ptr ds:[esi] 0040460C 46 inc esi 0040460D 12D2 adc dl,dl 0040460F 13C0 adc eax,eax 00404611 02D2 add dl,dl 00404613 75 05 jnz short ASP_Firs.0040461A 00404615 8A16 mov dl,byte ptr ds:[esi] 00404617 46 inc esi 00404618 12D2 adc dl,dl 0040461A 13C0 adc eax,eax 0040461C 02D2 add dl,dl 0040461E 75 05 jnz short ASP_Firs.00404625 00404620 8A16 mov dl,byte ptr ds:[esi] 00404622 46 inc esi 00404623 12D2 adc dl,dl 00404625 13C0 adc eax,eax 00404627 02D2 add dl,dl 00404629 75 05 jnz short ASP_Firs.00404630 0040462B 8A16 mov dl,byte ptr ds:[esi] 0040462D 46 inc esi 0040462E 12D2 adc dl,dl 00404630 13C0 adc eax,eax 00404632 74 06 je short ASP_Firs.0040463A 00404634 57 push edi 00404635 2BF8 sub edi,eax 00404637 8A07 mov al,byte ptr ds:[edi] 00404639 5F pop edi 0040463A 8807 mov byte ptr ds:[edi],al 0040463C 47 inc edi 0040463D ^ EB A0 jmp short ASP_Firs.004045DF 我们要说明的是,关于壳是如何解密运算的我们并不感兴趣,我们只要知道它在做什么就可以了 这段代码作用就是经过运算把一些代码从【00404B38】开始的内存地址,移动到【003C0000】 要跳出这样的一个循环,我们要从循环的开始寻找能够跳出循环的条件转移,虽然一般F4到下一行 就可以了,但是并不完全是这样的。这里跳出循环的就是下一行,我们F4下去,就看到edi= 003C000C,这就说明循环里一共移动了0xC的数据(我们从OD的数据窗口跟踪也可以看到)。 00404644 02D2 add dl,dl 00404646 75 05 jnz short ASP_Firs.0040464D 00404648 8A16 mov dl,byte ptr ds:[esi] 0040464A 46 inc esi 0040464B 12D2 adc dl,dl 0040464D 13C0 adc eax,eax 0040464F 02D2 add dl,dl 00404651 75 05 jnz short ASP_Firs.00404658 00404653 8A16 mov dl,byte ptr ds:[esi] ; 又从esi=【00404B45】里读取了一个字节 00404655 46 inc esi 00404656 12D2 adc dl,dl 00404658 ^ 72 EA jb short ASP_Firs.00404644 0040465A 83E8 02 sub eax,2 0040465D 75 28 jnz short ASP_Firs.00404687 0040465F B9 0100000>mov ecx,1 00404664 02D2 add dl,dl 00404666 75 05 jnz short ASP_Firs.0040466D 00404668 8A16 mov dl,byte ptr ds:[esi] 0040466A 46 inc esi 0040466B 12D2 adc dl,dl 0040466D 13C9 adc ecx,ecx 0040466F 02D2 add dl,dl 00404671 75 05 jnz short ASP_Firs.00404678 00404673 8A16 mov dl,byte ptr ds:[esi] 00404675 46 inc esi 00404676 12D2 adc dl,dl 00404678 ^ 72 EA jb short ASP_Firs.00404664 0040467A 56 push esi 0040467B 8BF7 mov esi,edi 0040467D 2BF5 sub esi,ebp 0040467F F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[es> 00404681 5E pop esi 00404682 ^ E9 58FFFFF>jmp ASP_Firs.004045DF 00404687 48 dec eax 00404688 C1E0 08 shl eax,8 0040468B 8A06 mov al,byte ptr ds:[esi] 0040468D 46 inc esi 0040468E 8BE8 mov ebp,eax 00404690 B9 0100000>mov ecx,1 00404695 02D2 add dl,dl 00404697 75 05 jnz short ASP_Firs.0040469E 00404699 8A16 mov dl,byte ptr ds:[esi] 0040469B 46 inc esi 0040469C 12D2 adc dl,dl 0040469E 13C9 adc ecx,ecx 004046A0 02D2 add dl,dl 004046A2 75 05 jnz short ASP_Firs.004046A9 004046A4 8A16 mov dl,byte ptr ds:[esi] 004046A6 46 inc esi 004046A7 12D2 adc dl,dl 004046A9 ^ 72 EA jb short ASP_Firs.00404695 004046AB 3D 007D000>cmp eax,7D00 004046B0 73 1A jnb short ASP_Firs.004046CC 004046B2 3D 0005000>cmp eax,500 004046B7 72 0E jb short ASP_Firs.004046C7 004046B9 41 inc ecx 004046BA 56 push esi 004046BB 8BF7 mov esi,edi 004046BD 2BF0 sub esi,eax 004046BF F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[es> 004046C1 5E pop esi 004046C2 ^ E9 18FFFFF>jmp ASP_Firs.004045DF 004046C7 83F8 7F cmp eax,7F 004046CA 77 03 ja short ASP_Firs.004046CF 004046CC 83C1 02 add ecx,2 004046CF 56 push esi 004046D0 8BF7 mov esi,edi 004046D2 2BF0 sub esi,eax 004046D4 F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[es> 004046D6 5E pop esi 004046D7 ^ E9 03FFFFF>jmp ASP_Firs.004045DF 004046DC 8A06 mov al,byte ptr ds:[esi] 004046DE 46 inc esi 004046DF 33C9 xor ecx,ecx 004046E1 C0E8 01 shr al,1 004046E4 74 12 je short ASP_Firs.004046F8 004046E6 83D1 02 adc ecx,2 004046E9 8BE8 mov ebp,eax 004046EB 56 push esi ; esi=00404B5D 004046EC 8BF7 mov esi,edi 004046EE 2BF0 sub esi,eax 004046F0 F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[esi] 004046F2 5E pop esi 004046F3 ^ E9 E7FEFFF>jmp ASP_Firs.004045DF F4到下一行,跳出这个循环后,可以看到edi已经=003D7E00了,已经填充到这里,我们用OD的数据窗口可以看到,继续 004046F8 5D pop ebp 004046F9 2B7D 0C sub edi,dword ptr ss:[ebp+C] ; 得到了移进去的数据大小edi=0017E00 004046FC 897D FC mov dword ptr ss:[ebp-4],edi ; 把它存进去,注意它存储的位置,popad后,这个值就被eax得到了 004046FF 61 popad 00404700 5D pop ebp ; 恢复ebp=004043F0,只是壳定位数据的基址 00404701 C2 0800 retn 8 计算了移进去的数据,然后就返回了 004044BB E8 0B01000>call ASP_Firs.004045CB ; 这个call移动数据到新分配的内存里 004044C0 6A 40 push 40 ;我们返回到了这里 004044C2 68 0010000>push 1000 004044C7 FFB5 0F040>push dword ptr ss:[ebp+40F] ;00035000 004044CD 6A 00 push 0 004044CF FF95 F7030>call dword ptr ss:[ebp+3F7] ; kernel32.VirtualAlloc 再分配一块内存 004044D5 8985 38040>mov dword ptr ss:[ebp+438],eax ; 这次=00D10000,存到00404828 004044DB 8985 D7010>mov dword ptr ss:[ebp+1D7],eax ; 还存到了004045C7,我们翻看一下前面的一块存到了004045C3 004044E1 64:67:A1 0>mov eax,dword ptr fs:[0] ; 把FS:【0】的值取到eax=0012FFE0,我们应该知道FS:【0】指向的就是seh的结构,不过这里壳还没有设置seh,所以0012FFE0指向的是系统的SEH结构 004044E6 8985 34040>mov dword ptr ss:[ebp+434],eax ; 存入【00404824】 这里我们要说的是壳把原来系统seh结构的地址放到【00404824】 又把00D1000放到【00404828】 实际上已经在内存里构造了一个EXCEPTION_REGISTRATION结构,如果有必要的时候把【00404824】放到FS:【0】就可以构建好程序的seh结构了,这是不是伏笔呢,我们不知道。继续 004044EC 8B55 5B mov edx,dword ptr ss:[ebp+5B] ; ss:[0040444B]=00404048 还记得吗这个就是壳前期一直在堆栈里保留的四个数据之一 004044EF 8B85 D7010>mov eax,dword ptr ss:[ebp+1D7] ; ss:[004045C7]=00D10000,得到新分配的内存地址 004044F5 8902 mov dword ptr ds:[edx],eax ; eax=00D10000 存到了【00404048】,我们要记好,因为感觉壳好像很在意这个地址 004044F7 8B85 0F040>mov eax,dword ptr ss:[ebp+40F] ; ss:[004047FF]=00035000 004044FD 8942 04 mov dword ptr ds:[edx+4],eax ; eax=00035000存到了【0040404C】 00404500 8D85 A6030>lea eax,dword ptr ss:[ebp+3A6] ; 地址=00404796 00404506 8B40 55 mov eax,dword ptr ds:[eax+55] ; ds:[004047EB]=7C809B14 (kernel32.VirtualFree) 00404509 8942 08 mov dword ptr ds:[edx+8],eax ; 存到了【00404050】,和上面两个接着的 0040450C 8B85 F3030>mov eax,dword ptr ss:[ebp+3F3] ; ss:[004047E3]=7C801D77 (kernel32.LoadLibraryA) 00404512 8942 10 mov dword ptr ds:[edx+10],eax 00404515 8B85 EF030>mov eax,dword ptr ss:[ebp+3EF] ; ss:[004047DF]=7C80B529 (kernel32.GetModuleHandleA) 0040451B 8942 14 mov dword ptr ds:[edx+14],eax 这上面连起来就是把新分配的内存地址和大小存起来,还有函数的地址: 00404040 45 35 50 E9 82 00 00 00 00 00 D1 00 00 50 03 00 E5P閭.....?.P_. 00404050 14 9B 80 7C 00 00 00 00 77 1D 80 7C 29 B5 80 7C _泙|....w_€|)祤| 0040451E 8B95 D3010>mov edx,dword ptr ss:[ebp+1D3] ; ss:[004045C3]=003C0000 00404524 BB F801000>mov ebx,1F8 ; 得到常量0x1F8,下面把它作为偏移量来定位内存地址 00404529 8B7C1A 0C mov edi,dword ptr ds:[edx+ebx+C] ; ds:[003C0204]=00001000 0040452D 0BFF or edi,edi 0040452F 74 1E je short ASP_Firs.0040454F 00404531 8B4C1A 10 mov ecx,dword ptr ds:[edx+ebx+10] ; ds:[003C0208]=00014000 00404535 0BC9 or ecx,ecx 00404537 74 11 je short ASP_Firs.0040454A 00404539 03BD D7010>add edi,dword ptr ss:[ebp+1D7] ; ss:[004045C7]=00D10000 0040453F 8B741A 14 mov esi,dword ptr ds:[edx+ebx+14] 00404543 03F2 add esi,edx 00404545 C1F9 02 sar ecx,2 00404548 F3:A5 rep movs dword ptr es:[edi],dword ptr ds:[esi] 前面都是在得到源地址esi和目标地址edi,循环计数器ecx这个值,关键的是最后的移动指令这一句,把0x5000双字的数据从003C0400移动到00D11000 总的是00D11000--00D25000 0040454A 83C3 28 add ebx,28 0040454D ^ EB DA jmp short ASP_Firs.00404529 跳回去再移动一次,这次是0x180双字的数据从003D4400移动到00D3B000,00D3B000--00D3B600 第三次,0x180双字的数据从003D4A00移动到00D3E000 第四次,0x100双字的数据从003D5000移动到00D3F000 ;这次都是00 第五次,0x800双字的数据从003D5400移动到00D40000 .。。。。不知道还有几次,我F4跳出循环了(OD的数据窗口要注意使用)。 把数据一块一块的移动到了后面分配的内存里 0040454F 8B85 D3010>mov eax,dword ptr ss:[ebp+1D3] ; ss:[004045C3]=003C0000 00404555 50 push eax 00404556 8B95 D7010>mov edx,dword ptr ss:[ebp+1D7] ; ss:[004045C7]=00D10000 0040455C 52 push edx ;把两块内存的地址压入堆栈 0040455D 8B18 mov ebx,dword ptr ds:[eax] ; 读出了【003C0000】 0040455F 03DA add ebx,edx 00404561 8B85 EB030>mov eax,dword ptr ss:[ebp+3EB] ; ss:[004047DB]=7C80AC28 (kernel32.GetProcAddress) 00404567 8903 mov dword ptr ds:[ebx],eax ; eax=7C80AC28 (kernel32.GetProcAddress)把地址存入了【00D438A4】 00404569 8B85 EF030>mov eax,dword ptr ss:[ebp+3EF] 0040456F 8943 04 mov dword ptr ds:[ebx+4],eax 00404572 8B85 F3030>mov eax,dword ptr ss:[ebp+3F3] 00404578 8943 08 mov dword ptr ds:[ebx+8],eax ; kernel32.LoadLibraryA 我们通过在OD数据窗口观察00D43870下面的数据,我们就会发现,这里就是壳的导入表的IMAGE_THUNK_DATA结构,下面则是IMAGE_IMPORT_BY_NAME结构,壳这样做实际上是在填充壳的这三个函数的IAT表,你可以提前用Peditor观察壳程序,看看它的导出表。 00D438A0 25 73 00 90 28 AC 80 7C 29 B5 80 7C 77 1D 80 7C %s.?瑎|)祤|w_€| 00D438B0 00 00 00 00 6B 65 72 6E 65 6C 33 32 2E 64 6C 6C ....kernel32.dll 00D438C0 00 00 00 47 65 74 50 72 6F 63 41 64 64 72 65 73 ...GetProcAddres 00D438D0 73 00 00 00 47 65 74 4D 6F 64 75 6C 65 48 61 6E s...GetModuleHan 00D438E0 64 6C 65 41 00 00 00 4C 6F 61 64 4C 69 62 72 61 dleA...LoadLibra 00D438F0 72 79 41 00 00 00 00 00 00 00 00 00 00 00 00 00 ryA............. 0040457B 5F pop edi 0040457C 5E pop esi 0040457D 8B46 04 mov eax,dword ptr ds:[esi+4] ; 读出第一块内存的第二双字 00404580 03C7 add eax,edi 00404582 8985 CE010>mov dword ptr ss:[ebp+1CE],eax 00404588 8B55 5B mov edx,dword ptr ss:[ebp+5B] ; ss:[0040444B]=00404048 (ASP_Firs.00404048) 0040458B 8B85 CE010>mov eax,dword ptr ss:[ebp+1CE] ; ss:[004045BE]=00D43000 00404591 8942 0C mov dword ptr ds:[edx+C],eax 00404594 9B wait 都是在读值,存值,继续 00404595 D9BD 3C040>fstcw word ptr ss:[ebp+43C] 0040459B 8D9D 14040>lea ebx,dword ptr ss:[ebp+414] ; 地址=00404804 004045A1 53 push ebx ; ebx=00404804 (ASP_Firs.00404804) 004045A2 6A 00 push 0 004045A4 6A 00 push 0 004045A6 6A 01 push 1 004045A8 57 push edi ; edi=00D10000 004045A9 8B5E 08 mov ebx,dword ptr ds:[esi+8] 压入了几个值,继续 004045AC 03DF add ebx,edi 004045AE 53 push ebx 004045AF 68 0080000>push 8000 004045B4 6A 00 push 0 004045B6 56 push esi ;003C0000 004045B7 FF95 FB030>call dword ptr ss:[ebp+3FB] ; kernel32.VirtualFree 释放了第一块内存 004045BD 68 0030D40>push 0D43000 004045C2 C3 retn ; 返回到上一条指令压入的地址 要去第二块内存运行了 我们来回想一下,先是把壳的数据搬到第一块内存,然后又从第一块移动到第二块,再跑到第二块执行程序 00D43000 90 nop 00D43001 60 pushad 00D43002 E8 4006000>call 00D43647 00D43647 8B2C24 mov ebp,dword ptr ss:[esp] 00D4364A 81ED 4B294>sub ebp,44294B ;ebp=009006BC 00D43650 C3 retn ;返回到上一个call的返回地址00D43007 壳里面都是要用ebp作为一个定位数据的基址,这里先把ebp的值放好 00D43007 /EB 44 jmp short 00D4304D 00D4304D BB 4429440>mov ebx,442944 00D43052 03DD add ebx,ebp ; ebx=00D43000,得到了执行代码的开始地址 00D43054 2B9D 71294>sub ebx,dword ptr ss:[ebp+442971] ; ebx=00D10000,得到了分配的内存地址 00D4305A 83BD D8304>cmp dword ptr ss:[ebp+4430D8],0 00D43061 899D 2F2E4>mov dword ptr ss:[ebp+442E2F],ebx 00D43067 0F85 3E050>jnz 00D435AB 00D4306D 8D85 E0304>lea eax,dword ptr ss:[ebp+4430E0] ; 地址=00D4379C, (ASCII "kernel32.dll") 00D43073 50 push eax 00D43074 FF95 EC314>call dword ptr ss:[ebp+4431EC] ; kernel32.GetModuleHandleA 00D4307A 8985 DC304>mov dword ptr ss:[ebp+4430DC],eax ; 存到了00D43798 要获取Kernel32.dll的模块句柄,存到内存中 00D43790 08 00 00 00 00 00 00 00 00 00 80 7C 6B 65 72 6E .........€|kern 00D437A0 65 6C 33 32 2E 64 6C 6C 00 56 69 72 74 75 61 6C el32.dll.Virtual 00D437B0 41 6C 6C 6F 63 00 56 69 72 74 75 61 6C 46 72 65 Alloc.VirtualFre 00D437C0 65 00 56 69 72 74 75 61 6C 50 72 6F 74 65 63 74 e.VirtualProtect 00D437D0 00 45 78 69 74 50 72 6F 63 65 73 73 00 00 00 00 .ExitProcess.... 00D437E0 00 75 73 65 72 33 32 2E 64 6C 6C 00 4D 65 73 73 .user32.dll.Mess 00D437F0 61 67 65 42 6F 78 41 00 77 73 70 72 69 6E 74 66 ageBoxA.wsprintf 00D43080 8BF8 mov edi,eax 00D43082 8D9D ED304>lea ebx,dword ptr ss:[ebp+4430ED] ; 地址=00D437A9, (ASCII "VirtualAlloc") 00D43088 53 push ebx 00D43089 50 push eax 00D4308A FF95 E8314>call dword ptr ss:[ebp+4431E8] ; kernel32.GetProcAddress 00D43090 8985 79294>mov dword ptr ss:[ebp+442979],eax ; 存入00D43035 要获取VirtualAlloc函数 00D43096 8D9D FA304>lea ebx,dword ptr ss:[ebp+4430FA] ; 地址=00D437B6, (ASCII "VirtualFree") 00D4309C 53 push ebx 00D4309D 57 push edi 00D4309E FF95 E8314>call dword ptr ss:[ebp+4431E8] ; kernel32.GetProcAddress 00D430A4 8985 7D294>mov dword ptr ss:[ebp+44297D],eax ; 存到00D43039 获得VirtualFree函数 把他们存到了连续的内存地址: 00D43030 00 00 00 00 00 81 9A 80 7C 14 9B 80 7C 00 00 00 .....仛€|_泙|... 00D430AA 8B85 2F2E4>mov eax,dword ptr ss:[ebp+442E2F] 00D430B0 8985 D8304>mov dword ptr ss:[ebp+4430D8],eax ; eax=00D10000 00D430B6 6A 04 push 4 00D430B8 68 0010000>push 1000 00D430BD 68 4605000>push 546 00D430C2 6A 00 push 0 00D430C4 FF95 79294>call dword ptr ss:[ebp+442979] ; kernel32.VirtualAlloc 00D430CA 8985 75294>mov dword ptr ss:[ebp+442975],eax ; eax=003C0000,存到00D43031 再次分配了一块内存,继续存进去: 00D43030 00 00 00 3C 00 81 9A 80 7C 14 9B 80 7C 00 00 00 ...<.仛€|_泙|... 00D430D0 8D9D 452A4>lea ebx,dword ptr ss:[ebp+442A45] 00D430D6 50 push eax ;新分配的内存地址 00D430D7 53 push ebx ;00D43101 00D430D8 E8 7405000>call 00D43651 带着新值,继续 00D43651 55 push ebp 00D43652 8BEC mov ebp,esp 00D43654 60 pushad 00D43655 55 push ebp 00D43656 8B75 08 mov esi,dword ptr ss:[ebp+8] 00D43659 8B7D 0C mov edi,dword ptr ss:[ebp+C] ; 分别取得call的两个参数 下面又是大段的运算,又要把数据从这里移动到新分配的003C0000 00D4365C FC cld 00D4365D B2 80 mov dl,80 00D4365F 8A06 mov al,byte ptr ds:[esi] 00D43661 46 inc esi 00D43662 8807 mov byte ptr ds:[edi],al 00D43664 47 inc edi 00D43665 02D2 add dl,dl 00D43667 75 05 jnz short 00D4366E 00D43669 8A16 mov dl,byte ptr ds:[esi] 00D4366B 46 inc esi 00D4366C 12D2 adc dl,dl 00D4366E ^ 73 EF jnb short 00D4365F 00D43670 02D2 add dl,dl 00D43672 75 05 jnz short 00D43679 00D43674 8A16 mov dl,byte ptr ds:[esi] 00D43676 46 inc esi 00D43677 12D2 adc dl,dl 00D43679 73 4A jnb short 00D436C5 00D4367B 33C0 xor eax,eax 00D4367D 02D2 add dl,dl 00D4367F 75 05 jnz short 00D43686 00D43681 8A16 mov dl,byte ptr ds:[esi] 00D43683 46 inc esi 00D43684 12D2 adc dl,dl 00D43686 0F83 D6000>jnb 00D43762 00D4368C 02D2 add dl,dl 00D4368E 75 05 jnz short 00D43695 00D43690 8A16 mov dl,byte ptr ds:[esi] 00D43692 46 inc esi 00D43693 12D2 adc dl,dl 00D43695 13C0 adc eax,eax 00D43697 02D2 add dl,dl 00D43699 75 05 jnz short 00D436A0 00D4369B 8A16 mov dl,byte ptr ds:[esi] 00D4369D 46 inc esi 00D4369E 12D2 adc dl,dl 00D436A0 13C0 adc eax,eax 00D436A2 02D2 add dl,dl 00D436A4 75 05 jnz short 00D436AB 00D436A6 8A16 mov dl,byte ptr ds:[esi] 00D436A8 46 inc esi 00D436A9 12D2 adc dl,dl 00D436AB 13C0 adc eax,eax 00D436AD 02D2 add dl,dl 00D436AF 75 05 jnz short 00D436B6 00D436B1 8A16 mov dl,byte ptr ds:[esi] 00D436B3 46 inc esi 00D436B4 12D2 adc dl,dl 00D436B6 13C0 adc eax,eax 00D436B8 74 06 je short 00D436C0 00D436BA 57 push edi 00D436BB 2BF8 sub edi,eax 00D436BD 8A07 mov al,byte ptr ds:[edi] 00D436BF 5F pop edi 00D436C0 8807 mov byte ptr ds:[edi],al 00D436C2 47 inc edi 00D436C3 ^ EB A0 jmp short 00D43665 00D436C5 B8 0100000>mov eax,1 00D436CA 02D2 add dl,dl 00D436CC 75 05 jnz short 00D436D3 00D436CE 8A16 mov dl,byte ptr ds:[esi] 00D436D0 46 inc esi 00D436D1 12D2 adc dl,dl 00D436D3 13C0 adc eax,eax 00D436D5 02D2 add dl,dl 00D436D7 75 05 jnz short 00D436DE 00D436D9 8A16 mov dl,byte ptr ds:[esi] 00D436DB 46 inc esi 00D436DC 12D2 adc dl,dl 00D436DE ^ 72 EA jb short 00D436CA 00D436E0 83E8 02 sub eax,2 00D436E3 75 28 jnz short 00D4370D 00D436E5 B9 0100000>mov ecx,1 00D436EA 02D2 add dl,dl 00D436EC 75 05 jnz short 00D436F3 00D436EE 8A16 mov dl,byte ptr ds:[esi] 00D436F0 46 inc esi 00D436F1 12D2 adc dl,dl 00D436F3 13C9 adc ecx,ecx 00D436F5 02D2 add dl,dl 00D436F7 75 05 jnz short 00D436FE 00D436F9 8A16 mov dl,byte ptr ds:[esi] 00D436FB 46 inc esi 00D436FC 12D2 adc dl,dl 00D436FE ^ 72 EA jb short 00D436EA 00D43700 56 push esi 00D43701 8BF7 mov esi,edi 00D43703 2BF5 sub esi,ebp 00D43705 F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[esi] 00D43707 5E pop esi 00D43708 ^ E9 58FFFFF>jmp 00D43665 00D4370D 48 dec eax 00D4370E C1E0 08 shl eax,8 00D43711 8A06 mov al,byte ptr ds:[esi] 00D43713 46 inc esi 00D43714 8BE8 mov ebp,eax 00D43716 B9 0100000>mov ecx,1 00D4371B 02D2 add dl,dl 00D4371D 75 05 jnz short 00D43724 00D4371F 8A16 mov dl,byte ptr ds:[esi] 00D43721 46 inc esi 00D43722 12D2 adc dl,dl 00D43724 13C9 adc ecx,ecx 00D43726 02D2 add dl,dl 00D43728 75 05 jnz short 00D4372F 00D4372A 8A16 mov dl,byte ptr ds:[esi] 00D4372C 46 inc esi 00D4372D 12D2 adc dl,dl 00D4372F ^ 72 EA jb short 00D4371B 00D43731 3D 007D000>cmp eax,7D00 00D43736 73 1A jnb short 00D43752 00D43738 3D 0005000>cmp eax,500 00D4373D 72 0E jb short 00D4374D 00D4373F 41 inc ecx 00D43740 56 push esi 00D43741 8BF7 mov esi,edi 00D43743 2BF0 sub esi,eax 00D43745 F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[esi] 00D43747 5E pop esi 00D43748 ^ E9 18FFFFF>jmp 00D43665 00D4374D 83F8 7F cmp eax,7F 00D43750 77 03 ja short 00D43755 00D43752 83C1 02 add ecx,2 00D43755 56 push esi 00D43756 8BF7 mov esi,edi 00D43758 2BF0 sub esi,eax 00D4375A F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[esi] 00D4375C 5E pop esi 00D4375D ^ E9 03FFFFF>jmp 00D43665 00D43762 8A06 mov al,byte ptr ds:[esi] 00D43764 46 inc esi 00D43765 33C9 xor ecx,ecx 00D43767 C0E8 01 shr al,1 00D4376A 74 12 je short 00D4377E 00D4376C 83D1 02 adc ecx,2 00D4376F 8BE8 mov ebp,eax 00D43771 56 push esi 00D43772 8BF7 mov esi,edi 00D43774 2BF0 sub esi,eax 00D43776 F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[esi] 00D43778 5E pop esi 00D43779 ^ E9 E7FEFFF>jmp 00D43665 F4到下一行,跳出循环,看到edi=003C0546,可见移过来的数据并不多,这个call前面我们就已经进去过了。 00D4377E 5D pop ebp 00D4377F 2B7D 0C sub edi,dword ptr ss:[ebp+C] 00D43782 897D FC mov dword ptr ss:[ebp-4],edi ; edi=00000546,得到总的移过来的数目 00D43785 61 popad ; 上面存着的值,执行这条指令后,就存放在eax中了 00D43786 5D pop ebp 00D43787 C2 0800 retn ;返回到00D430DD 把数量作为返回值 00D430DD 8BC8 mov ecx,eax 00D430DF 8DBD 452A4>lea edi,dword ptr ss:[ebp+442A45] 00D430E5 8BB5 75294>mov esi,dword ptr ss:[ebp+442975] 00D430EB F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[esi] ; 把这0x546的数据又从003C0000移动回到了00D43101,就是改变我们下面一点的代码了 用里面的数据改变下面的代码,是自修改 00D430ED 8B85 75294>mov eax,dword ptr ss:[ebp+442975] 00D430F3 68 0080000>push 8000 00D430F8 6A 00 push 0 00D430FA 50 push eax ;003C0000 00D430FB FF95 7D294>call dword ptr ss:[ebp+44297D] ; kernel32.VirtualFree 00D43101 8D85 512C4>lea eax,dword ptr ss:[ebp+442C51] ; 地址=00D4330D 00D43107 50 push eax 00D43108 C3 retn ; 返回到上面的地址 释放了申请的内存,原来又申请的内存只是一个中转,用来自修改代码,完了就释放,然后就返回了 00D4330D 8B9D 552A4>mov ebx,dword ptr ss:[ebp+442A55] 00D43313 0BDB or ebx,ebx 00D43315 74 0A je short 00D43321 00D43321 8DB5 712A4>lea esi,dword ptr ss:[ebp+442A71] 00D43327 833E 00 cmp dword ptr ds:[esi],0 ; ds:[00D4312D]=00001000 00D4332A 0F84 D3000>je 00D43403 00D43330 8DB5 712A4>lea esi,dword ptr ss:[ebp+442A71] 00D43336 8B46 04 mov eax,dword ptr ds:[esi+4] 00D43339 6A 04 push 4 00D4333B 68 0010000>push 1000 00D43340 50 push eax 00D43341 6A 00 push 0 00D43343 FF95 79294>call dword ptr ss:[ebp+442979] ; kernel32.VirtualAlloc 00D43349 8985 75294>mov dword ptr ss:[ebp+442975],eax ; 存内存基址=003C0000 又申请 00D43030 00 00 00 3C 00 81 9A 80 7C 14 9B 80 7C 00 00 00 ...<.仛€|_泙|... 这四个内存地址就是用来存取分配的内存地址和VirtualAlloc和VirtualFree两个函数 00D4334F 56 push esi 00D43350 8B1E mov ebx,dword ptr ds:[esi] 00D43352 039D D8304>add ebx,dword ptr ss:[ebp+4430D8] 00D43358 50 push eax ;003C0000 00D43359 53 push ebx ;00D11000 00D4335A E8 F202000>call 00D43651 ;这个call就是前面移动数据的哪一个了 进入call后,程序又把数据计算后移动到了003C0000,只是这次比较多0x29C00 00D4335F 80BD 70294>cmp byte ptr ss:[ebp+442970],0 00D43366 75 4C jnz short 00D433B4 00D43368 FE85 70294>inc byte ptr ss:[ebp+442970] ; 上面把里面的值比0,这里又把它加1,貌似这是一个标志位00D4302C 00D4336E 8B3E mov edi,dword ptr ds:[esi] 00D43370 03BD D8304>add edi,dword ptr ss:[ebp+4430D8] ; edi=00D11000 00D43376 FF37 push dword ptr ds:[edi] ; ds:[00D11000]=40103804 00D43378 C607 C3 mov byte ptr ds:[edi],0C3 00D4337B FFD7 call edi ; edi=00D11000 这里是要跳到00D11000出执行,先把开头的第一个字节改为C3,我们来看看C3原来就是retn指令,继续 00D11000 C3 retn 只有一条返回指令返回到00D4337D 00D4337D 8F07 pop dword ptr ds:[edi] 00D4337F 50 push eax 00D43380 51 push ecx 00D43381 56 push esi 00D43382 53 push ebx 00D43383 8BC8 mov ecx,eax 00D43385 83E9 06 sub ecx,6 00D43388 8BB5 75294>mov esi,dword ptr ss:[ebp+442975] 00D4338E 33DB xor ebx,ebx 保存了寄存器后,又给寄存器赋值: EAX 00029C00 ECX 00029BFA EDX 7C92EB94 ntdll.KiFastSystemCallRet EBX 00000000 ESP 0012FF58 EBP 009006BC ESI 003C0000 EDI 00D11000 EIP 00D43396 00D43390 0BC9 or ecx,ecx 00D43392 /74 1C je short 00D433B0 00D43394 |78 1A js short 00D433B0 ;ecx是循环计数器,看看是不是为零了,是就跳出循环。 00D43396 |AC lods byte ptr ds:[esi] ;读入字节 00D43397 |3C E8 cmp al,0E8 00D43399 |74 08 je short 00D433A3 ;等于E8吗? 00D4339B |3C E9 cmp al,0E9 00D4339D |74 04 je short 00D433A3 ;等于E9吗? 00D4339F |43 inc ebx ;这里则是记录已经比了几个还没有找到 00D433A0 |49 dec ecx 00D433A1 ^|EB ED jmp short 00D43390 这段就是从003C0000处按字节依次读入数据,然后比较看看是不是E8和E9,不是就把ebx加1,把ecx总循环的计数器减1,然后继续读,也就是说这段循环在003C0000位置寻找E8和E9 00D433A3 291E sub dword ptr ds:[esi],ebx ;ebx=E8 F4下来,找到的一个在【003C01CA】=E8 找到后就把他后面的四个字节用ebx的值填充,现在我们来想一下壳到底在做什么?我们来看看把数据窗口d 003C0000,鼠标找到003C01CA,也就是循环里面找到的第一个E8,然后右键选择反汇编,我们发现了,原来E8就是call 指令的机器码(实际上call有好多种,机器码也不同,细节你可以去看intel的Intel Architecture Software Developer Manual,第二卷就可以找到了,这里我们为了简化): 003C01C4 BB 0A000000 mov ebx,0A 003C01C9 54 push esp 003C01CA E8 23010000 call 003C02F2 ;这一句 003C01CF F64424 2C 01 test byte ptr ss:[esp+2C],1 003C01D4 74 05 je short 003C01DB 找到E8后,00D433A3这一句修改他后面的四个字节,实际上就是把E8 23010000的后面给改掉,这样的话,call的调用地址就改了,现在我们执行这一句指令,同时就可以看到数据窗口里,call的这一句已经变成: 003C01CA E8 59FFFFFF call 003C0128 E8是call指令,E9呢?原来是jmp指令 我们来算算他一共要改多少处,ecx=00029BFA,每次减去5,等于零跳出循环,那么00029BFA/5=8598,呵呵,还真是不少。 00D433A5 83C3 05 add ebx,5 00D433A8 83C6 04 add esi,4 00D433AB 83E9 05 sub ecx,5 ; 把计数器,内存地址指针修改到相应的位置,继续循环 00D433AE ^ EB E0 jmp short 00D43390 F4到下一行跳出循环,下面就是改完收工,重新pop出原来的值到寄存器 00D433B0 \5B pop ebx 00D433B1 5E pop esi 00D433B2 59 pop ecx 00D433B3 58 pop eax 00D433B4 8BC8 mov ecx,eax 00D433B6 8B3E mov edi,dword ptr ds:[esi] 00D433B8 03BD D8304>add edi,dword ptr ss:[ebp+4430D8] 00D433BE 8BB5 75294>mov esi,dword ptr ss:[ebp+442975] 00D433C4 C1F9 02 sar ecx,2 00D433C7 F3:A5 rep movs dword ptr es:[edi],dword ptr ds:[esi] 把那些修改了call和jmp指令的数据按照每次四字节,从003C0000移动到00D11000,大小0x0000A700 00D433C9 8BC8 mov ecx,eax ; eax就是给ecx赋值的=00029C00 00D433CB 83E1 03 and ecx,3 ; and后ecx等于零 00D433CE F3:A4 rep movs byte ptr es:[edi],byte ptr d>;由于ecx上面and后等于0,所以一个字节也没有移动 00D433D0 5E pop esi ; 00D4312D 00D433D1 8B85 75294>mov eax,dword ptr ss:[ebp+442975] 00D433D7 68 0080000>push 8000 00D433DC 6A 00 push 0 00D433DE 50 push eax 00D433DF FF95 7D294>call dword ptr ss:[ebp+44297D] ; kernel32.VirtualFree 00D433E5 83C6 08 add esi,8 00D433E8 833E 00 cmp dword ptr ds:[esi],0 00D433EB ^ 0F85 45FFF>jnz 00D43336 ;循环来了 现在又把它释放掉,循环跳回去到分配内存处,我们来想一下这个循环的作用,它就是分配一块内存,把数据计算了放进去,然后又把数据放回来释放,中间的话可能会去执行修改call的循环,我们跟着他回去过一下。在这里就是判断是否去执行修改call循环,现在我们发现由于第一次给它赋了1,这个比较都是会跳转的。那个循环只会去执行一次。 00D4335F 80BD 70294>cmp byte ptr ss:[ebp+442970],0 00D43366 75 4C jnz short 00D433B4 00D43368 FE85 70294>inc byte ptr ss:[ebp+442970] ; 上面把里面的值比0,这里又把它加1,貌似这是一个标志位00D4302C F4跳出循环 00D433F1 8B9D 552A4>mov ebx,dword ptr ss:[ebp+442A55] 00D433F7 0BDB or ebx,ebx 00D433F9 74 08 je short 00D43403 ; 读取一个值,判断是否为零 00D433FB 8B03 mov eax,dword ptr ds:[ebx] 00D433FD 8785 592A4>xchg dword ptr ss:[ebp+442A59],eax 00D43403 8B95 D8304>mov edx,dword ptr ss:[ebp+4430D8] ; ss:[00D43794]=00D10000 我们一定要利用好OD的数据窗口,要经常的在提示栏的数据上点右键,选择在数据窗口跟随地址。这里我们同样跟随一下就会发现,读取的地址数据周围有很重要的字符,我们修正一下显示的对齐方式,d 00D43700得到像下面的数据是这样的: 00D43780 7D 0C 89 7D FC 61 5D C2 08 00 87 DB 00 00 00 00 }.墋黙]?.囒.... 00D43790 08 00 00 00 00 00 D1 00 00 00 80 7C 6B 65 72 6E .....?..€|kern 00D437A0 65 6C 33 32 2E 64 6C 6C 00 56 69 72 74 75 61 6C el32.dll.Virtual 00D437B0 41 6C 6C 6F 63 00 56 69 72 74 75 61 6C 46 72 65 Alloc.VirtualFre 00D437C0 65 00 56 69 72 74 75 61 6C 50 72 6F 74 65 63 74 e.VirtualProtect 00D437D0 00 45 78 69 74 50 72 6F 63 65 73 73 00 00 00 00 .ExitProcess.... 00D437E0 00 75 73 65 72 33 32 2E 64 6C 6C 00 4D 65 73 73 .user32.dll.Mess 00D437F0 61 67 65 42 6F 78 41 00 77 73 70 72 69 6E 74 66 ageBoxA.wsprintf 00D43800 41 00 4C 4F 41 44 45 52 20 45 52 52 4F 52 00 54 A.LOADER ERROR.T 你也应该看到了,这可是重要的地方了,我们要小心的继续 00D43409 8B85 512A4>mov eax,dword ptr ss:[ebp+442A51] 00D4340F 2BD0 sub edx,eax ; edx=00910000 00D43411 74 75 je short 00D43488 00D43413 8BC2 mov eax,edx 00D43415 C1E8 10 shr eax,10 ; eax=0000091 00D43418 33DB xor ebx,ebx 00D4341A 8BB5 5D2A4>mov esi,dword ptr ss:[ebp+442A5D] 00D43420 03B5 D8304>add esi,dword ptr ss:[ebp+4430D8] ; esi=00D40000 00D43426 833E 00 cmp dword ptr ds:[esi],0 ; ds:[00D40000]=00001000 00D43429 74 5D je short 00D43488 00D4342B 8B4E 04 mov ecx,dword ptr ds:[esi+4] 连续使用了esi内连续内存的数值,我们来看一下: 00D40000 00 10 00 00 48 01 00 00 00 30 0C 30 2C 30 38 30 ._..H_...0.0,080 00D40010 3C 30 40 30 44 30 48 30 4C 30 50 30 54 30 62 30 <0@0D0H0L0P0T0b0 00D4342E 83E9 08 sub ecx,8 00D43431 D1E9 shr ecx,1 ; ecx=000000A0这是重定位项的数目,把它作为循环的计数器 00D43433 8B3E mov edi,dword ptr ds:[esi] 00D43435 03BD D8304>add edi,dword ptr ss:[ebp+4430D8] ; edi=00D11000,得到当前代码的虚拟地址 00D4343B 83C6 08 add esi,8 ; esi=00D40008,从这里开始就是重定位项了 00D4343E 66:8B1E mov bx,word ptr ds:[esi] ; 读出重定位的项目 00D43441 C1EB 0C shr ebx,0C 00D43444 83FB 01 cmp ebx,1 ; 单独取出高4位进行判断 00D43447 74 0C je short 00D43455 00D43449 83FB 02 cmp ebx,2 00D4344C 74 16 je short 00D43464 00D4344E 83FB 03 cmp ebx,3 00D43451 74 20 je short 00D43473 ; 判断出重定位项的类型,一般都是3类 00D43453 EB 2C jmp short 00D43481 00D43455 66:8B1E mov bx,word ptr ds:[esi] 00D43458 81E3 FF0F0>and ebx,0FFF 00D4345E 66:01041F add word ptr ds:[edi+ebx],ax 00D43462 EB 1D jmp short 00D43481 00D43464 66:8B1E mov bx,word ptr ds:[esi] 00D43467 81E3 FF0F0>and ebx,0FFF 00D4346D 66:01141F add word ptr ds:[edi+ebx],dx 00D43471 EB 0E jmp short 00D43481 00D43473 66:8B1E mov bx,word ptr ds:[esi] 00D43476 81E3 FF0F0>and ebx,0FFF ; 得到重定位数据在页面中的地址 00D4347C 01141F add dword ptr ds:[edi+ebx],edx ; 给这个地址加上00910000,重定位过来 00D4347F EB 00 jmp short 00D43481 00D43481 83C6 02 add esi,2 ; 修改指向下一个字,重定位项是按照字的 00D43484 ^ E2 B8 loopd short 00D4343E ; 还没有结束,好回去继续 00D43486 ^\EB 9E jmp short 00D43426 ; 一个循环只是完成了一块的数据,回去看看是不是已经重定位完了 00D40000 00 10 00 00 48 01 00 00 00 30 0C 30 2C 30 38 30 ._..H_...0.0,080 00D40010 3C 30 40 30 44 30 48 30 4C 30 50 30 54 30 62 30 <0@0D0H0L0P0T0b0 00D40020 6A 30 72 30 7A 30 82 30 8A 30 92 30 9A 30 A2 30 j0r0z0????? 00D40030 AA 30 B2 30 BA 30 C2 30 CA 30 D2 30 DA 30 E2 30 ???????? 00D40040 EA 30 F2 30 FA 30 02 31 0A 31 12 31 1A 31 22 31 ???_1.1_1_1"1 00D40050 2A 31 32 31 3A 31 42 31 4A 31 52 31 5A 31 62 31 *121:1B1J1R1Z1b1 00D40060 6A 31 72 31 7A 31 82 31 8A 31 92 31 9A 31 A2 31 j1r1z1????? 00D40070 AA 31 B2 31 BA 31 E6 31 EE 31 F6 31 FE 31 06 32 ???????_2 00D40080 0E 32 16 32 1E 32 27 32 48 32 50 32 B8 32 BF 32 _2_2_2'2H2P2?? 由于壳自己把数据从程序空间搬到了动态内存里面,上面的就是重定位表,壳在进行代码的重定位,还是一样的话,不熟悉PE文件结构的,我也没有办法,赶快去补课吧! 完成一个块后,跳回去又是下一个块直到结束;列一下他们的IMAGE_BASE_RELOCATION结构 00D40000 00 10 00 00 48 01 00 00 ._..H_.. 00D40140 00 20 00 00 CC 00 00 00 ?.. 00D40210 00 30 00 00 80 00 00 00 0..€..._ 00D40290 00 40 00 00 68 00 00 00 @..h...? 00D402F0 00 50 00 00 54 02 00 00 00D40550 00 60 00 00 FC 01 00 00 .`..?... 00D40740 00 70 00 00 A4 00 00 00 太多了,F4跳出来 00D43488 8BB5 612A4>mov esi,dword ptr ss:[ebp+442A61] ; ss:[00D4311D]=0002E000 00D4348E 8B95 D8304>mov edx,dword ptr ss:[ebp+4430D8] ; ss:[00D43794]=00D10000 00D43494 03F2 add esi,edx ; 这里就是定位到导入表了(当然我是从下面才判断出来的) 00D43496 8B46 0C mov eax,dword ptr ds:[esi+C] ; esi=00D3E000,取出它+C地方的值 00D43499 85C0 test eax,eax 00D4349B 0F84 0A010>je 00D435AB 00D434A1 03C2 add eax,edx 00D434A3 8BD8 mov ebx,eax ; eax=00D3E368, (ASCII "kernel32.dll")从这一句我们可以判断出了,我们来到导入表了 esi得到的导入表IMAGE_IMPORT_DESCRIPTOR结构,【esi+C】就是Name1字段了,得到库字符串名的RVA 加上基址,就得到了库的名字,这里是kernel32.dll库。 00D434A5 50 push eax 00D434A6 FF95 EC314>call dword ptr ss:[ebp+4431EC] ;这是GetModuleHandleA函数 00D434AC 85C0 test eax,eax 00D434AE 75 07 jnz short 00D434B7 获取Kernel32.dll库的模块句柄 00D434B7 8985 4D294>mov dword ptr ss:[ebp+44294D],eax ; 值保存到00D43009 00D434BD C785 51294>mov dword ptr ss:[ebp+442951],0 ; 在存入地址的后面再放入一个字节的0 00D434C7 8B95 D8304>mov edx,dword ptr ss:[ebp+4430D8] 00D434CD 8B06 mov eax,dword ptr ds:[esi] ; ds:[00D3E000]=这个是导入表的OriginalFirstThunk字段 00D434CF 85C0 test eax,eax ; 看看是不是零,一般都是零啥 00D434D1 75 03 jnz short 00D434D6 00D434D3 8B46 10 mov eax,dword ptr ds:[esi+10] ; +10么就来到FirstThunk字段了 00D434D6 03C2 add eax,edx ; 加上基址,现在eax指向IMAGE_THUNK_DATA结构了 00D434D8 0385 51294>add eax,dword ptr ss:[ebp+442951] ; 加上要定位的数组序号,先是0,从第一个开始 00D434DE 8B18 mov ebx,dword ptr ds:[eax] ; 取出IMAGE_THUNK_DATA结构的值 00D434E0 8B7E 10 mov edi,dword ptr ds:[esi+10] ; 再次让edi得到数组的RVA 00D434E3 03FA add edi,edx 00D434E5 03BD 51294>add edi,dword ptr ss:[ebp+442951] ; 重新定位数组在edi,这是要把得到的系统地址放到里面,构建IAT表 00D434EB 85DB test ebx,ebx ; 测试是不是已经完了, 00D434ED 0F84 A2000>je 00D43595 00D434F3 F7C3 00000>test ebx,80000000 ; 这是壳在获取自身的导出表,这里在比较IMAGE_THUNK_DATA的高位 00D434F9 /75 04 jnz short 00D434FF ; 没有跳,说明这是IMAGE_IMPORT_BY_NAME结构RVA 00D434FB |03DA add ebx,edx ; 加上基址,得到IMAGE_IMPORT_BY_NAME结构 00D434FD |43 inc ebx 00D434FE |43 inc ebx ; 加上2字节的位置,去掉IMAGE_IMPORT_BY_NAME的Hint字段 00D434FF \53 push ebx ; 我们的第一个函数是GetCurrentThreadId 00D43500 81E3 FFFFF>and ebx,7FFFFFFF 00D43506 53 push ebx 00D43507 FFB5 4D294>push dword ptr ss:[ebp+44294D] 00D4350D FF95 E8314>call dword ptr ss:[ebp+4431E8] ; ss:[00D438A4]=7C80AC28 (kernel32.GetProcAddress) 00D43513 85C0 test eax,eax ; 测试是否成功 00D43515 5B pop ebx 00D43516 75 6F jnz short 00D43587 取得函数系统地址 00D43587 8907 mov dword ptr ds:[edi],eax ; 存到IMAGE_THUNK_DATA数组,构建IAT表 00D43589 8385 51294>add dword ptr ss:[ebp+442951],4 ; 增加定位数组的序号 00D43590 ^ E9 32FFFFF>jmp 00D434C7 我们通过在数据窗口就可以看到要读取的函数数量非常多,我们F4跳出循环后,已经读取了如下的函数: 00D3E370 47 65 74 43 75 72 72 65 GetCurre 00D3E380 6E 74 54 68 72 65 61 64 49 64 00 00 00 00 44 65 ntThreadId....De 00D3E390 6C 65 74 65 43 72 69 74 69 63 61 6C 53 65 63 74 leteCriticalSect 00D3E3A0 69 6F 6E 00 00 00 4C 65 61 76 65 43 72 69 74 69 ion...LeaveCriti 00D3E3B0 63 61 6C 53 65 63 74 69 6F 6E 00 00 00 00 45 6E calSection....En 00D3E3C0 74 65 72 43 72 69 74 69 63 61 6C 53 65 63 74 69 terCriticalSecti 00D3E3D0 6F 6E 00 00 00 00 49 6E 69 74 69 61 6C 69 7A 65 on....Initialize 00D3E3E0 43 72 69 74 69 63 61 6C 53 65 63 74 69 6F 6E 00 CriticalSection. 00D3E3F0 00 00 56 69 72 74 75 61 6C 46 72 65 65 00 00 00 ..VirtualFree... 00D3E400 56 69 72 74 75 61 6C 41 6C 6C 6F 63 00 00 00 00 VirtualAlloc.... 00D3E410 4C 6F 63 61 6C 46 72 65 65 00 00 00 4C 6F 63 61 LocalFree...Loca 00D3E420 6C 41 6C 6C 6F 63 00 00 00 00 56 69 72 74 75 61 lAlloc....Virtua 00D3E430 6C 51 75 65 72 79 00 00 00 00 57 69 64 65 43 68 lQuery....WideCh 00D3E440 61 72 54 6F 4D 75 6C 74 69 42 79 74 65 00 00 00 arToMultiByte... 00D3E450 4D 75 6C 74 69 42 79 74 65 54 6F 57 69 64 65 43 MultiByteToWideC 00D3E460 68 61 72 00 00 00 6C 73 74 72 6C 65 6E 41 00 00 har...lstrlenA.. 00D3E470 00 00 6C 73 74 72 63 70 79 6E 41 00 00 00 6C 73 ..lstrcpynA...ls 00D3E480 74 72 63 70 79 41 00 00 00 00 4C 6F 61 64 4C 69 trcpyA....LoadLi 00D3E490 62 72 61 72 79 45 78 41 00 00 00 00 47 65 74 54 braryExA....GetT 00D3E4A0 68 72 65 61 64 4C 6F 63 61 6C 65 00 00 00 47 65 hreadLocale...Ge 00D3E4B0 74 53 74 61 72 74 75 70 49 6E 66 6F 41 00 00 00 tStartupInfoA... 00D3E4C0 47 65 74 50 72 6F 63 41 64 64 72 65 73 73 00 00 GetProcAddress.. 00D3E4D0 00 00 47 65 74 4D 6F 64 75 6C 65 48 61 6E 64 6C ..GetModuleHandl 00D3E4E0 65 41 00 00 00 00 47 65 74 4D 6F 64 75 6C 65 46 eA....GetModuleF 00D3E4F0 69 6C 65 4E 61 6D 65 41 00 00 00 00 47 65 74 4C ileNameA....GetL 00D3E500 6F 63 61 6C 65 49 6E 66 6F 41 00 00 00 00 47 65 ocaleInfoA....Ge 00D3E510 74 4C 61 73 74 45 72 72 6F 72 00 00 00 00 47 65 tLastError....Ge 00D3E520 74 43 6F 6D 6D 61 6E 64 4C 69 6E 65 41 00 00 00 tCommandLineA... 00D3E530 46 72 65 65 4C 69 62 72 61 72 79 00 00 00 46 69 FreeLibrary...Fi 00D3E540 6E 64 46 69 72 73 74 46 69 6C 65 41 00 00 00 00 ndFirstFileA.... 00D3E550 46 69 6E 64 43 6C 6F 73 65 00 00 00 45 78 69 74 FindClose...Exit 00D3E560 50 72 6F 63 65 73 73 00 00 00 57 72 69 74 65 46 Process...WriteF 00D3E570 69 6C 65 00 00 00 55 6E 68 61 6E 64 6C 65 64 45 ile...UnhandledE 00D3E580 78 63 65 70 74 69 6F 6E 46 69 6C 74 65 72 00 00 xceptionFilter.. 00D3E590 00 00 53 65 74 46 69 6C 65 50 6F 69 6E 74 65 72 ..SetFilePointer 00D3E5A0 00 00 00 00 53 65 74 45 6E 64 4F 66 46 69 6C 65 ....SetEndOfFile 00D3E5B0 00 00 00 00 52 74 6C 55 6E 77 69 6E 64 00 00 00 ....RtlUnwind... 00D3E5C0 52 65 61 64 46 69 6C 65 00 00 00 00 52 61 69 73 ReadFile....Rais 00D3E5D0 65 45 78 63 65 70 74 69 6F 6E 00 00 00 00 47 65 eException....Ge 00D3E5E0 74 53 74 64 48 61 6E 64 6C 65 00 00 00 00 47 65 tStdHandle....Ge 00D3E5F0 74 46 69 6C 65 53 69 7A 65 00 00 00 47 65 74 53 tFileSize...GetS 00D3E600 79 73 74 65 6D 54 69 6D 65 00 00 00 47 65 74 46 ystemTime...GetF 00D3E610 69 6C 65 54 79 70 65 00 00 00 43 72 65 61 74 65 ileType...Create 00D3E620 46 69 6C 65 41 00 00 00 43 6C 6F 73 65 48 61 6E FileA...CloseHan 00D3E630 64 6C 65 dle 00D43595 8906 mov dword ptr ds:[esi],eax ; eax=00D3E1A8,这是循环写入Kernel32.dll所有函数地址的保存内存地址 00D43597 8946 0C mov dword ptr ds:[esi+C],eax ; Name1字段也同样的写入这个值 00D4359A 8946 10 mov dword ptr ds:[esi+10],eax ; FirstThunk字段也是一样的了 00D4359D 83C6 14 add esi,14 ; 指向下一个IID表 00D435A0 8B95 D8304>mov edx,dword ptr ss:[ebp+4430D8] ; 得到基址 00D435A6 ^ E9 EBFEFFF>jmp 00D43496 把最后的地址存到三个字段后,定位到下一个IID表,跳转回去要看看是不是可以还有其他连接库 第二次是User32.dll库,获取了如下的函数:存到00D3E1AC 00D3E640 00 00 47 65 74 4B 65 79 62 6F 61 72 64 54 79 70 ..GetKeyboardTyp 00D3E650 65 00 00 00 4C 6F 61 64 53 74 72 69 6E 67 41 00 e...LoadStringA. 00D3E660 00 00 4D 65 73 73 61 67 65 42 6F 78 41 00 00 00 ..MessageBoxA... 00D3E670 43 68 61 72 4E 65 78 74 41 00 CharNextA. 第三次是Advapi32.dll库,获取了如下的函数:存到了00D3E1C0 00D3E680 52 65 67 51 75 65 RegQue 00D3E690 72 79 56 61 6C 75 65 45 78 41 00 00 00 00 52 65 ryValueExA....Re 00D3E6A0 67 4F 70 65 6E 4B 65 79 45 78 41 00 00 00 52 65 gOpenKeyExA...Re 00D3E6B0 67 43 6C 6F 73 65 4B 65 79 00 gCloseKey. 接下来还有很多的库和函数,我们不再列举,F4跳出循环 edi最后指向了00D3E364 00D435AB 8B85 652A4400 mov eax,dword ptr ss:[ebp+442A65] ; ss:[00D43121]=0002A8E8 00D435B1 50 push eax 00D435B2 0385 D8304400 add eax,dword ptr ss:[ebp+4430D8] ; 把RVA+基址得到了00D3A8E8 00D435B8 5B pop ebx 00D435B9 0BDB or ebx,ebx 00D435BB 8985 112F4400 mov dword ptr ss:[ebp+442F11],eax ; 把eax=00D3A8E8放到里面更改了里面的代码,这句代码就在我们的下面 00D435C1 61 popad 00D435C2 75 08 jnz short 00D435CC 00D435CC 68 E8A8D300 push 0D3A8E8 ; 这句就是上面修改的代码 00D435D1 C3 retn 修改了一个call地址,然后跳转到哪里执行 00D3A8E8 55 push ebp 00D3A8E9 8BEC mov ebp,esp 00D3A8EB 83C4 B4 add esp,-4C ; 0x4C的局部变量 00D3A8EE B8 C8A6D300 mov eax,0D3A6C8 ; 给eax赋常量0D3A6C8 00D3A8F3 E8 E0AEFDFF call 00D157D8 ;进入 00D3A8F8 E8 9F8CFDFF call 00D1359C 00D3A8FD 8D40 00 lea eax,dword ptr ds:[eax] 00D157D8 BA A4B0D300 mov edx,0D3B0A4 00D157DD 837D 0C 01 cmp dword ptr ss:[ebp+C],1 ; 读出了第二个参数比较是否为零 00D157E1 75 2A jnz short 00D1580D 00D157E3 50 push eax 00D157E4 52 push edx ; 把新得到的两个常量保存起来 00D157E5 C605 C8C4D30>mov byte ptr ds:[D3C4C8],1 ; ds:[00D3C4C8]=00 00D157EC 8B4D 08 mov ecx,dword ptr ss:[ebp+8] ; 读出第一个参数00D10000(不知道是在哪里压入的参数了!!) 00D157EF 890D D4C4D30>mov dword ptr ds:[D3C4D4],ecx 00D157F5 894A 04 mov dword ptr ds:[edx+4],ecx ;00D3B0A8 00D157F8 C742 08 0000>mov dword ptr ds:[edx+8],0 00D157FF C742 0C 0000>mov dword ptr ds:[edx+C],0 00D15806 E8 81FFFFFF call 00D1578C 这里只是在内存中保存了几个数,还不知道是要做什么的,我们继续 00D1578C 53 push ebx 00D1578D 81C4 F8FEFFF>add esp,-108 ; 分配0x108的局部变量 00D15793 68 05010000 push 105 ; esp=0012FE1C 00D15798 8D4424 04 lea eax,dword ptr ss:[esp+4] 00D1579C 50 push eax ; eax=0012FE20 00D1579D A1 D4C4D300 mov eax,dword ptr ds:[D3C4D4] ; ecx=00D10000 这个是前面一个call ecx压入的值 00D157A2 50 push eax 00D157A3 E8 A4FEFFFF call 00D1564C ; jmp to kernel32.GetModuleFileNameA 调用函数了: 0012FE14 00D10000 |hModule = 00D10000 0012FE18 0012FE20 |PathBuffer = 0012FE20 0012FE1C 00000105 \BufSize = 105 (261.) 我们要注意到它把Buffer设在了堆栈里,更要注意的是D10000本身就是函数自己请求的动态内存,这个函数返回0 而且我们注意到程序并没有检测返回值,而是要进入另一个call 00D157A8 8BC4 mov eax,esp ; esp=0012FE20 00D157AA E8 B5EEFFFF call 00D14664 00D14664 55 push ebp 00D14665 8BEC mov ebp,esp 00D14667 81C4 E0FEFFF>add esp,-120 ; 再次分配了0x120的局部变量 00D1466D 53 push ebx 00D1466E 56 push esi ; 压入了ebx=00D2E5B4,esi=003C0000 00D1466F 8945 FC mov dword ptr ss:[ebp-4],eax ; eax的值是从调用处来的,移动到【ebp-4】,就是第一个局部变量 00D14672 68 05010000 push 105 00D14677 8D85 E3FEFFF>lea eax,dword ptr ss:[ebp-11D] ; 堆栈地址=0012FCFB,我们要注意到这个堆栈地址,它并不是标准的对齐方式 00D1467D 50 push eax 00D1467E 6A 00 push 0 00D14680 E8 8BCAFFFF call 00D11110 ; jmp to kernel32.GetModuleFileNameA 0012FCE4 00000000 |hModule = NULL 0012FCE8 0012FCFB |PathBuffer = 0012FCFB 0012FCEC 00000105 \BufSize = 105 (261.) 还是调用这个函数,这次的hModule是NULL得到了当前程序的完整路径, 00D14685 C645 EE 00 mov byte ptr ss:[ebp-12],0 00D14689 8D45 F8 lea eax,dword ptr ss:[ebp-8] ; eax=0012FE10 00D1468C 50 push eax 00D1468D 68 3F000F00 push 0F003F 00D14692 6A 00 push 0 00D14694 68 2048D100 push 0D14820 ; ASCII "Software\Borland\Locales" 00D14699 68 01000080 push 80000001 00D1469E E8 CDCAFFFF call 00D11170 ; jmp to ADVAPI32.RegOpenKeyExA 打开了指定的注册表的值 0012FCDC 80000001 |hKey = HKEY_CURRENT_USER 0012FCE0 00D14820 |Subkey = "Software\Borland\Locales" 0012FCE4 00000000 |Reserved = 0 0012FCE8 000F003F |Access = KEY_ALL_ACCESS 0012FCEC 0012FE10 \pHandle = 0012FE10 我的电脑里根本就没有它要打开的这个注册表项。 00D146A3 85C0 test eax,eax 00D146A5 74 1E je short 00D146C5 ;没有办法跳,我的这里是失败的 00D146A7 8D45 F8 lea eax,dword ptr ss:[ebp-8] 00D146AA 50 push eax 00D146AB 68 3F000F00 push 0F003F 00D146B0 6A 00 push 0 00D146B2 68 3C48D100 push 0D1483C ; ASCII "Software\Borland\Delphi\Locales" 00D146B7 68 01000080 push 80000001 00D146BC E8 AFCAFFFF call 00D11170 ; jmp to ADVAPI32.RegOpenKeyExA 00D146C1 85C0 test eax,eax 00D146C3 75 72 jnz short 00D14737 ;这里肯定是跳了 再来一次看看: 0012FCDC 80000001 |hKey = HKEY_CURRENT_USER 0012FCE0 00D1483C |Subkey = "Software\Borland\Delphi\Locales" 0012FCE4 00000000 |Reserved = 0 0012FCE8 000F003F |Access = KEY_ALL_ACCESS 0012FCEC 0012FE10 \pHandle = 0012FE10 不用说在我这里还是没有,我的电脑里没有关于Borland的任何软件。由于我的没有,我也就不再去跟踪程序怎么处理有的这一类情况了,继续 00D14737 8B45 FC mov eax,dword ptr ss:[ebp-4] 00D1473A 50 push eax 00D1473B 8D85 E3FEFFF>lea eax,dword ptr ss:[ebp-11D] 00D14741 50 push eax 00D14742 E8 01CAFFFF call 00D11148 ; jmp to kernel32.lstrcpyA 堆栈如下: 0012FCE8 0012FCFB |String1 = 0012FCFB 0012FCEC 0012FE20 \String2 = "ASCII "F:\Unpack\goal\ASP_FirstWindow1.4.exe" 把这个call得到的文件路径,复制到前一个call的堆栈。 0012FCF0 00 3A 5C 55 6E :\Un 0012FD00 70 61 63 6B 5C 67 6F 61 6C 5C 41 53 50 5F 46 69 pack\goal\ASP_Fi 0012FD10 72 73 74 57 69 6E 64 6F 77 31 2E 34 2E 65 78 65 rstWindow1.4.exe 0012FD20 00 . 00D14747 6A 05 push 5 00D14749 8D45 F3 lea eax,dword ptr ss:[ebp-D] 00D1474C 50 push eax 00D1474D 6A 03 push 3 00D1474F E8 DCC9FFFF call 00D11130 ; jmp to kernel32.GetThreadLocale 调用了The GetThreadLocale function returns the calling thread's current locale. 返回值是00000804,同时这是一个没有参数的函数,所以前面压入堆栈的值,并不是给这个函数的。 00D14754 50 push eax 00D14755 E8 AEC9FFFF call 00D11108 ; jmp to kernel32.GetLocaleInfoA 前面的堆栈数是这个函数的参数如下: 0012FCE0 00000804 |LocaleId = 804 0012FCE4 00000003 |InfoType = 3 0012FCE8 0012FE0B |Buffer = 0012FE0B 0012FCEC 00000005 \BufSize = 5 The GetLocaleInfo function retrieves information about a locale. 返回值是4,堆栈中的值如下: 0012FE00 43 48 53 00 CHS. 00D1475A 33F6 xor esi,esi 00D1475C 80BD E3FEFFF>cmp byte ptr ss:[ebp-11D],0 ; 堆栈 ss:[0012FCFB]=00 这个地址就是文件的路径地址 00D14763 0F84 AF00000>je 00D14818 00D14818 8BC6 mov eax,esi ; 得到0 00D1481A 5E pop esi 00D1481B 5B pop ebx ; esi=003C0000,ebx=00D2E5B4 00D1481C 8BE5 mov esp,ebp ; 释放局部变量,恢复指针 00D1481E 5D pop ebp 00D1481F C3 retn ;返回到00D157AF 00D157AA E8 B5EEFFFF call 00D14664 ; 里面调用了好几个函数,在我的电脑上最终得到的就是当前程序的完整文件路径 00D157AF 8BD8 mov ebx,eax 00D157B1 891D B4B0D30>mov dword ptr ds:[D3B0B4],ebx 00D157B7 85DB test ebx,ebx 00D157B9 75 0A jnz short 00D157C5 00D157BB A1 A8B0D300 mov eax,dword ptr ds:[<00D157F5>; ds:[00D3B0A8]=00D10000 00D157C0 A3 B4B0D300 mov dword ptr ds:[D3B0B4],eax 00D157C5 B8 A4B0D300 mov eax,0D3B0A4 00D157CA E8 EDF0FFFF call 00D148BC 00D148BC 8B15 10B0D30>mov edx,dword ptr ds:[D3B010] 00D148C2 8910 mov dword ptr ds:[eax],edx 00D148C4 A3 10B0D300 mov dword ptr ds:[D3B010],eax ; eax=00D3B0A4存到了【00D3B010】 00D148C9 C3 retn 保存了几个值,继续 00D157CF 81C4 0801000>add esp,108 00D157D5 5B pop ebx 00D157D6 C3 retn 恢复堆栈,继续 00D1580B 5A pop edx 00D1580C 58 pop eax ; eax=00D3A6C8,edx=00D3B0A4 00D1580D FF35 D8C4D30>push dword ptr ds:[D3C4D8] 00D15813 B9 BCB0D300 mov ecx,0D3B0BC ; ecx=00D3B0BC 00D15818 E8 1BDCFFFF call 00D13438 00D13438 51 push ecx 00D13439 56 push esi 00D1343A 57 push edi 00D1343B BE 9CC4D300 mov esi,0D3C49C 00D13440 8D7D C4 lea edi,dword ptr ss:[ebp-3C] 00D13443 B9 0B000000 mov ecx,0B 00D13448 F3:A5 rep movs dword ptr es:[edi],dwo>;esi里都是00,所以edi被填充了0x0B的00 我们记录一下它覆盖的地址0012FF4C--0012FF74 00D1344A 8F05 BCC4D30>pop dword ptr ds:[D3C4BC] 00D13450 8F05 B8C4D30>pop dword ptr ds:[D3C4B8] 00D13456 892D B0C4D30>mov dword ptr ds:[D3C4B0],ebp 00D1345C 891D B4C4D30>mov dword ptr ds:[D3C4B4],ebx 00D13462 8905 A4C4D30>mov dword ptr ds:[D3C4A4],eax 00D13468 8915 ACC4D30>mov dword ptr ds:[D3C4AC],edx 00D1346E 8D4D C4 lea ecx,dword ptr ss:[ebp-3C] 00D13471 890D 9CC4D30>mov dword ptr ds:[D3C49C],ecx 这些指令在内存里填充出了如下的结构: 00D3C490 00 00 00 00 00 00 00 00 00 00 00 00 4C FF 12 00 ............L_. 00D3C4A0 00 00 00 00 C8 A6 D3 00 00 00 00 00 A4 B0 D3 00 ....圈?....ぐ? 00D3C4B0 88 FF 12 00 B4 E5 D2 00 00 00 3C 00 00 00 D1 00 ?_.村?..<...? 00D13477 31C9 xor ecx,ecx 00D13479 837D 0C 00 cmp dword ptr ss:[ebp+C],0 ; 堆栈 ss:[0012FF94]=00000001 00D1347D 75 02 jnz short 00D13481 00D13481 890D A8>mov dword ptr ds:[D3C4A8],ecx 00D13487 E8 A4FE>call 00D13330 继续 00D13330 31D2 xor edx,edx 00D13332 8D45 F4 lea eax,dword ptr ss:[ebp-C] 00D13335 64:8B0A mov ecx,dword ptr fs:[edx] 00D13338 64:8902 mov dword ptr fs:[edx],eax 00D1333B 8908 mov dword ptr ds:[eax],ecx 00D1333D C740 04>mov dword ptr ds:[eax+4],0D13294 00D13344 8968 08 mov dword ptr ds:[eax+8],ebp 00D13347 8905 A0>mov dword ptr ds:[D3C4A0],eax ; 这段代码构建了一个SEH结构,但是它采用了一小点的迂回方法,但是最终我们还是可以在堆栈中看到 00D1334D C3 retn 间接的去构建SEH结构,我们只要看到FS[0]就要想到SEH,这里你不注意的话,还是有可能错过了的 0012FF7C 0012FFE0 指针到下一个 SEH 记录 0012FF80 00D13294 SE 句柄 00D1348C 8B45 0C mov eax,dword ptr ss:[ebp+C] ; 上面的哪一个比较数据 00D1348F 40 inc eax 00D13490 A2 C0C4>mov byte ptr ds:[D3C4C0],al ; 把结果存到【00D3C4C0],数据窗口注意跟踪这段内存 00D13495 48 dec eax 00D13496 59 pop ecx 00D13497 8B11 mov edx,dword ptr ds:[ecx] 00D13499 8915 C4>mov dword ptr ds:[D3C4C4],edx ; 继续的在往上面的连续内存地址存入, 00D1349F 74 03 je short 00D134A4 00D134A1 FF1481 call dword ptr ds:[ecx+eax*4] 再往内存里存入东西,就是前面我们列出过的内存 00D3C490 00 00 00 00 00 00 00 00 00 00 00 00 4C FF 12 00 ............L_. 00D3C4A0 7C FF 12 00 C8 A6 D3 00 00 00 00 00 A4 B0 D3 00 |_.圈?....ぐ? 00D3C4B0 88 FF 12 00 B4 E5 D2 00 00 00 3C 00 00 00 D1 00 ?_.村?..<...? 00D3C4C0 02 00 00 00 24 57 D1 00 01 00 00 00 00 00 00 00 _...$W?_....... 00D156D4 B8 0800>mov eax,8 00D156D9 85C0 test eax,eax 00D156DB 74 1D je short 00D156FA 00D156DD E8 82FF>call 00D15664 ; jmp to kernel32.TlsAlloc 00D156E2 A3 CCC4>mov dword ptr ds:[<Tls index>],eax ; 存到了00D3C4CC,eax=00000005 00D156E7 85C0 test eax,eax 00D156E9 7D 0A jge short 00D156F5 ; 函数是否成功 调用TlsAlloc函数 The TlsAlloc function allocates a thread local storage (TLS) index. Any thread of the process can subsequently use this index to store and retrieve values that are local to the thread. 继续 00D156F5 E8 8AFF>call 00D15684 00D156FA C3 retn 我们看看,获得TLS index后,它要做什么 00D15684 53 push ebx 00D15685 B8 0800>mov eax,8 00D1568A 85C0 test eax,eax 00D1568C 74 43 je short 00D156D1 00D1568E 833D CC>cmp dword ptr ds:[<Tls index>],0 ; 比较是不是零,我们已经说了,我这里得到的是05 00D15695 7D 0A jge short 00D156A1 00D156A1 68 0800>push 8 00D156A6 6A 40 push 40 00D156A8 E8 A7FF>call 00D15654 ; jmp to kernel32.LocalAlloc 00D156AD 8BD8 mov ebx,eax 00D156AF 85DB test ebx,ebx ;检测是否成功 00D156B1 75 0C jnz short 00D156BF The LocalAlloc function allocates the specified number of bytes from the heap,分配了8字节,返回值等于00144800 00D156BF 53 push ebx 00D156C0 A1 CCC4>mov eax,dword ptr ds:[<Tls index>] 00D156C5 50 push eax 00D156C6 E8 B1FF>call 00D1567C ; jmp to kernel32.TlsSetValue 00D156CB 891D E0>mov dword ptr ds:[<heap addr>],ebx 00D156D1 5B pop ebx ; 00D2E5B4 00D156D2 C3 retn 关于这个函数和TLS的最简单的资料: The TlsSetValue function stores a value in the calling thread's thread local storage (TLS) slot for a specified TLS index. Each thread of a process has its own slot for each TLS index. TLS indexes are typically allocated by the TlsAlloc function during process or DLL initialization. Once allocated, each thread of the process can use a TLS index to access its own TLS storage slot for that index. The storage slot for each thread is initialized to NULL. A thread specifies a TLS index in a call to TlsSetValue, to store a value in its slot. The thread specifies the same index in a subsequent call to TlsGetValue, to retrieve the stored value. 调用时的,堆栈参数值: 0012FF1C 00000005 |TlsIndex = 5 0012FF20 00144800 \pValue = 00144800 保存heap地址后返回 00D156F5 E8 8AFF>call 00D15684 00D156FA C3 retn ;返回到这里,再次返回 00D134A1 FF1481 call dword ptr ds:[ecx+eax*4] ; 作用分配TLS,分配heap addr,把堆地址存到TLS 00D134A4 8B5424 >mov edx,dword ptr ss:[esp+4] ; 来到这里,堆栈 ss:[0012FF34]=00000000 00D134A8 85D2 test edx,edx 00D134AA 74 05 je short 00D134B1 00D134B1 833D 1C>cmp dword ptr ds:[D3C01C],0 ; 地址附近的整片内存都是00 00D134B8 75 0D jnz short 00D134C7 00D134BA C605 24>mov byte ptr ds:[D3C024],1 ; 00D3C024 00D134C1 D93D 00>fstcw word ptr ds:[<FPU cw>] ; 存储FPU控制字 00D134C7 8B45 0C mov eax,dword ptr ss:[ebp+C] ; 堆栈 ss:[0012FF94]=00000001 00D134CA 48 dec eax 00D134CB 0F85 CB>jnz 00D1359C 00D134D1 E8 02FF>call 00D133D8 继续 00D133D8 55 push ebp 00D133D9 8BEC mov ebp,esp 00D133DB 53 push ebx 00D133DC 56 push esi 00D133DD 57 push edi 00D133DE A1 A4C4>mov eax,dword ptr ds:[D3C4A4] ;这个就是壳放了好多各方数据在其中的地址,数据窗口要跟着 00D133E3 85C0 test eax,eax 00D133E5 74 4B je short 00D13432 00D133E7 8B30 mov esi,dword ptr ds:[eax] ; 00000043 00D133E9 33DB xor ebx,ebx 00D133EB 8B78 04 mov edi,dword ptr ds:[eax+4] ; ds:[00D3A6CC]=00D3A6D0 00D133EE 33D2 xor edx,edx 00D133F0 55 push ebp 00D133F1 68 1E34>push 0D1341E 00D133F6 64:FF32 push dword ptr fs:[edx] 00D133F9 64:8922 mov dword ptr fs:[edx],esp ; 又构建了壳的第二个SEH 取出大量的内存值,给寄存器赋值,同时建立了壳的第二个SEH 0012FF10 0012FF7C 指针到下一个 SEH 记录 0012FF14 00D1341E SE 句柄 00D13400 8B04DF mov eax,dword ptr ds:[edi+ebx*8] ; ds:[00D3A6D0]=00D15850,ebx成了寻址因子=0 00D13403 43 inc ebx ; 给它加一 00D13404 891D A8>mov dword ptr ds:[<index 00D13400>>; 存储到00D3C490这一片,已经汇聚了很多各种数据的内存地址 00D1340A 85C0 test eax,eax 00D1340C /74 02 je short 00D13410 00D1340E |FFD0 call eax ;eax=00D15850 00D15850 832D DC>sub dword ptr ds:[D3C4DC],1 ; 得到0xFFFFFFFF 00D15857 C3 retn 我们再来粘贴一次00D3C490这一片内存: 00D3C490 00 00 00 00 00 00 00 00 00 00 00 00 4C FF 12 00 ............L_. 00D3C4A0 7C FF 12 00 C8 A6 D3 00 01 00 00 00 A4 B0 D3 00 |_.圈?_...ぐ? 00D3C4B0 88 FF 12 00 B4 E5 D2 00 00 00 3C 00 00 00 D1 00 ?_.村?..<...? 00D3C4C0 02 00 00 00 24 57 D1 00 01 00 00 00 05 00 00 00 _...$W?_..._... 00D3C4D0 00 00 00 00 00 00 D1 00 00 00 00 00 FF FF FF FF ......?.... 00D3C4E0 >00 48 14 00 00 00 00 00 00 00 00 00 00 00 00 00 .H_............. 这里面包含了太多的数据,而且来自各个方向,每一个数据几乎都有一段“历史”才存进来的。 00D13410 3BF3 cmp esi,ebx ; 果然在这里增加了寻址因子,跳回去读取其他地方的数据 00D13412 ^ 7F EC jg short 00D13400 我们要来仔细观察一下这个循环 00D13400 8B04DF mov eax,dword ptr ds:[edi+ebx*8] ; ds:[00D3A6D0]=00D15850,ebx成了寻址因子 00D13403 43 inc ebx ; 给它加一 00D13404 891D A8>mov dword ptr ds:[<index 00D13400>>; 存储到00D3C490这一片,已经汇聚了很多各种数据的内存地址 00D1340A 85C0 test eax,eax 00D1340C 74 02 je short 00D13410 00D1340E FFD0 call eax 00D13410 3BF3 cmp esi,ebx ; 果然在这里增加了寻址因子,跳回去读取其他地方的数据 00D13412 ^ 7F EC jg short 00D13400 这个循环古怪的地方在于call eax这一句,它通过修改ebx让eax每次读取到不同的数据,而执行这一句时,eax就会跳转到了不同的地方执行,而且,它的跳出循环的条件是esi=ebx,esi里面已经放入了43,ebx是每次加一,所以,这段循环要执行43次,这意味着我们要进入43个call,我的老天,杀了我吧,没办法呀只有继续。 |
|
[原创]单步跟踪ASProtect Version 1.4(未完,更新到申请TLS的部分)
谢了,我只所以产生发单步跟踪记录过程,就是因为我觉得,实际上那些告诉 到这里F7,到哪里F8,这里要修改,那里是seh的,到xxxxx处dump的类似的文章,对于新手的话很难学到东西,而我的单步跟踪问题就是坛主说的,你很难再去把他写的结构清晰明了,毕竟每一条指令都包含着,我的这一篇比起上一篇来说在一些基本的细节上,已经是不再注释了,但还是很混乱。 新手看我这个文章,要想学到东西的话,要等着我把加密的程序发上来,这个时候就能感受到它的好处了,你可以边开着OD,边看我的记录,每一小段指令,只要是有必要注释的我全部都注释着,让你能很好的学习。 比如说我觉得我上面的这一段代码,新手看的话,尤其要学习00404708到004047A0之间的部分,这是一段动态获取Kernel32.dll连接库,然后再得到里面的LoadLibrary和GetProAddress函数, 有了他们什么函数就都可以得到了,当然虽然在上面我加了很好的注释,但是你还是要熟悉PE结构,熟悉导出表。 这段代码不止在当年,即使是现在也是一段精妙的代码,学会它有很大的意义和好处,推荐新手多看看 |
|
[原创]单步跟踪tElock v 0.98
长是长了点,毕竟是单步跟踪么,从开头到oep的完整记录,我也不会做视频教程,如果还有刚学脱壳的朋友可以看一下。 |
|
[原创]单步跟踪tElock v 0.98
发送时太慢了,分成了两部分,后面发送颠倒了,把它交换了连起来才是完整的一篇,还不能发附件,以后连加了壳的hello.exe程序再发上来 |
|
|
|
[讨论]刘涛涛先生是著名程序员还是顶级黑客
晕了 ,你上网去看看吧,他有什么东西么!!!!!!!!!!!!!! |
|
[原创]单步跟踪tElock v 0.98
加密对象:罗云彬的〈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,所以这段循环就是看看我们有没有在函数上设了断点。我的没有设,所以壳没有跳转。 |
操作理由
RANk
{{ user_info.golds == '' ? 0 : user_info.golds }}
雪币
{{ experience }}
课程经验
{{ score }}
学习收益
{{study_duration_fmt}}
学习时长
基本信息
荣誉称号:
{{ honorary_title }}
能力排名:
No.{{ rank_num }}
等 级:
LV{{ rank_lv-100 }}
活跃值:
在线值:
浏览人数:{{ visits }}
最近活跃:{{ last_active_time }}
注册时间:{{ user_info.create_date_jsonfmt }}
勋章
兑换勋章
证书
证书查询 >
能力值