[原创]softdefender脱壳
最近学习脱壳,在网上下了个softdefender加壳工具共享版。加壳工具本身就是用softdefender加的壳。就拿来试了下。希望高手多指教。
OD版本是OllyICE,用StrongOD隐藏插件隐藏OD。除BreakOnTls RemoveEPone-shot BreakOnLdr其它都选上。
PhantOm options选上protect DRx.
1.双进程转单进程
softdefender双进程转单进程参考peaceclub的方法。贴子中有详细介绍,为了完整性,我也还是再描述一下。
用kerberos加载程序,点击inject。生成rep文件。
SoftDefender | 004CCD53 | GetCommandLineA() returns: 002523A0 SoftDefender | 004CCDBF | CreateProcessA(00000000, 004CC24C: "F:\tools\hack\", 00000000, 00000000, 00000001, 00000000, 00000000, 00000000, 004CC350, 004CC23C) returns: 00000001 SoftDefender | 004CCDD7 | ExitProcess(00000000)
在CreateProcessA前面一个api GetCommandLineA 004CCD53 处下硬件执行断点。
断下后往上拉就会看到一个je.
004CCD32 0010 add byte ptr [eax], dl 004CCD34 40 inc eax 004CCD35 00E8 add al, ch 004CCD37 0F84 9A000000 je 004CCDD7 ;修改z标志位跳转实现 004CCD3D E8 01000000 call 004CCD43
在004CCD37处下硬件执行断点。把开始那断点删除。
重新加载,断下后修改z标志位让跳转实现。这样就变成单进程了。
2.跳过iat加密
alt+m打开内存镜像,在00401000段下内存访问断点。运行后断下处为解压代码处。
004C9E10 8917 mov dword ptr [edi], edx 004C9E12 83C7 04 add edi, 0x4 004C9E15 49 dec ecx 004C9E16 ^ 75 F3 jnz short 004C9E0B
右键删除内存断点。
这时可以在数据窗口中ctrl+g 到00401000。观察数据变化
往下拉到函数返回
004C9F5A F7D8 neg eax 004C9F5C 83C4 0C add esp, 0xC 004C9F5F 5A pop edx 004C9F60 59 pop ecx 004C9F61 5B pop ebx 004C9F62 5E pop esi 004C9F63 5F pop edi 004C9F64 5D pop ebp 004C9F65 C3 retn
在retn处f4。
然后再次alt+m打开内存镜像,在00401000段下内存访问断点。这次断下来就是准备处理iat处。
004D3B4A 8B17 mov edx, dword ptr [edi] 004D3B4C 8B47 10 mov eax, dword ptr [edi+0x10] 004D3B4F 0BD0 or edx, eax 004D3B51 0F84 0B050000 je 004D4062
右键删除内存断点。
这时候就一直f8单步。注意观察寄存器的变化。可以看到寄存器esi出现kernel32.dll,再单步到edi出现函数名IsBadReadPtr.并且有一个未实现的比较远的跳转.
004D3DE2 E8 11F4FFFF call 004D31F8 ;这个call调用完edi出现函数名 004D3DE7 8B8424 44040000 mov eax, dword ptr [esp+0x444] 004D3DEE 85C0 test eax, eax 004D3DF0 0F84 D7010000 je 004D3FCD ;改为jmp 004D3DF6 8B4424 18 mov eax, dword ptr [esp+0x18] 004D3DFA 85C0 test eax, eax
这个跳转就是决定iat是否加密的跳转.将je改为jmp.
再继续f8单步,这样就可以看到直接获取iat写入esi.
这时也可以在数据窗口中观察esi地址。查看iat是否加密。
再继续就会出现jmp往回跳循环处理。
004D401A 8B5C24 34 mov ebx, dword ptr [esp+0x34] 004D401E 8B7C24 20 mov edi, dword ptr [esp+0x20] 004D4022 83C5 04 add ebp, 0x4 004D4025 83C6 04 add esi, 0x4 004D4028 ^ E9 BAFCFFFF jmp 004D3CE7 004D402D E8 35E4FFFF call 004D2467 ;此处 为校验call f7步入
在jmp的下一条语句处f4。这个call如果再音步,那么恭喜,又得重新来一次了。
这个call这里有校验,如果仅仅是将这个call nop掉,在处理下一个dll时,还会在上面调用一次,所以这里用f7进去,将call内的有一个je也更改为jmp.
004D24E9 5F pop edi 004D24EA 5E pop esi 004D24EB 85C0 test eax, eax 004D24ED 5B pop ebx 004D24EE 74 2B je short 004D251B ;改为jmp 004D24F0 E8 93EBFFFF call 004D1088
单步到上层函数,又会碰到jmp往回跳,循环处理dll.
004D405D ^\E9 E8FAFFFF jmp 004D3B4A ;循环处理dll 004D4062 5F pop edi 004D4063 5E pop esi 004D4064 5D pop ebp 004D4065 B8 01000000 mov eax, 0x1 004D406A 5B pop ebx 004D406B 81C4 30040000 add esp, 0x430 004D4071 C2 0400 retn 0x4 ;f4执行
这里就可以直接在retn处f4。就可以看到iat全部处理完成。这时候就可以找oep了。
3.找oep
ctrl+g到00401000
00401000 53 push ebx 00401001 56 push esi 00401002 57 push edi 00401003 FF15 78E24400 call dword ptr [0x44E278] ; KERNEL32.GetVersion 00401009 3D 00000080 cmp eax, 0x80000000 0040100E 0F83 9A000000 jnb 004010AE 00401014 68 94000000 push 0x94 00401019 BB 01000000 mov ebx, 0x1 0040101E E8 D06C0200 call 00427CF3
发现代码很像vc的程序。
先找一段vc程序入口特征码
004184AE |. 8B45 EC mov eax, dword ptr [ebp-0x14] 004184B1 |. 8B08 mov ecx, dword ptr [eax] 004184B3 |. 8B09 mov ecx, dword ptr [ecx] 004184B5 |. 894D 98 mov dword ptr [ebp-0x68], ecx 004184B8 |. 50 push eax 004184B9 |. 51 push ecx 8B 45 EC 8B 08 8B 09 89 4D 98 50 51
ctrl+b查找,果然找到特征码,往上拉就发现oep。
00429664 55 push ebp ;oep 00429665 8BEC mov ebp, esp 00429667 6A FF push -0x1 00429669 68 E02C4500 push 00452CE0 0042966E 68 48C74200 push 0042C748 00429673 64:A1 00000000 mov eax, dword ptr fs:[0] 00429679 50 push eax 0042967A 64:8925 0000000>mov dword ptr fs:[0], esp 00429681 83EC 58 sub esp, 0x58 00429684 53 push ebx 00429685 56 push esi 00429686 57 push edi 00429687 8965 E8 mov dword ptr [ebp-0x18], esp 0042968A FF15 78E24400 call dword ptr [0x44E278] ; KERNEL32.GetVersion
然后迫不急待在oep下硬件执行断点,f9运行。结果程序跑飞了。
看来oep代码被偷了。重新来一次。
这次就在KERNEL32.GetVersion处下断,现在代码就变成这样
00429664 0000 add byte ptr [eax], al 00429666 0000 add byte ptr [eax], al 00429668 0000 add byte ptr [eax], al 0042966A 0000 add byte ptr [eax], al 0042966C 00FF add bh, bh 0042966E 15 28491600 adc eax, 0x164928 00429673 64:A1 00000000 mov eax, dword ptr fs:[0] ;硬件执行断点 00429679 50 push eax 0042967A 64:8925 0000000>mov dword ptr fs:[0], esp 00429681 83EC 58 sub esp, 0x58 00429684 53 push ebx 00429685 56 push esi 00429686 57 push edi 00429687 8965 E8 mov dword ptr [ebp-0x18], esp 0042968A FF15 78E24400 call dword ptr [0x44E278] ; KERNEL32.GetVersion
还得再来一次,在偷代码的下的语句下硬件执行断点,大家可以跳过两次失败的硬件断点,直接在这下断点。我提示的晚吗?
得想个办法。让eip到oep.并且堆栈数据还原。具体壳是怎么偷取代码怎么处理的我就不分析了。因为我不会分析。
对比两次的代码就只push了四次。只需要pop出来就完事了。ecx为0,就借用一下。
在oep处新建eip.写代码
00429664 59 pop ecx 00429665 59 pop ecx 00429666 59 pop ecx ;只能pop三次,最后一个是ebp的值 00429667 33C9 xor ecx, ecx ;记得还原原来的值 00429669 8BE5 mov esp, ebp ;还原esp 0042966B 5D pop ebp ;还原ebp 0042966C 00FF add bh, bh ;执行到这里,这句不用执行了。
这样堆栈就还原了。然后再次将eip新建到oep处。将原先的代码还原。
55 8B EC 6A FF 68 E0 2C 45 00 68 48 C7 42 00
4.dump修复
loadpe修正镜像大小,完全脱壳
importrec输入oep.搜索iat.获取输入表.全部有效.修复.
这样就可以直接运行了。优化没做。接下来看看破解。
破解在这里有介绍
