OEP是004958EC。单步过程中主要注意以下几处:
1. 以下位置:
004B3599 E8 82010000 call 004B3720 ; 获取GetProcAddress函数地址
004B359E 85C0 test eax, eax
004B35A0 0F84 DC020000 je 004B3882
004B35A6 89C6 mov esi, eax
004B35A8 8D9D FE020000 lea ebx, dword ptr [ebp+2FE]
004B35AE 53 push ebx
004B35AF 8D9D 45020000 lea ebx, dword ptr [ebp+245]
004B35B5 53 push ebx
004B35B6 57 push edi
004B35B7 FFD6 call esi ; 调用GetProcAddress获取GlobalFindAtomA
004B35B9 85C0 test eax, eax
004B35BB 0F84 C1020000 je 004B3882 ; 这里改成jmp直接跳走,或到这里时改Z标志置1
004B35C1 FFD0 call eax
在004B35BB处改一下,让它直接跳走。
如果不跳走的后果:
程序调用GlobalFindAtomA检测"PROGRAM_ID_2164362263"的Atom是否存在
如不存在(说明这是自身的第一个实例):
GlobalAddAtomA创建Atom
GetCommandLine得到自身文件名,然后CreateProcessA再运行自身另一个实例。之后这第一个进程(也就是我们调试的这个进程)就调用FatalExit自己退出了。
也就是说,这是“双进程”,第一个实例负责启动第二个实例并退出,而第二个实例才真正运行起来。
所以我们在这里要人为跳走(或者在调用GlobalFindAtomA后修改结果让其跳走),也就是欺骗程序让它认为自己就是第二个实例了,把“双进程”转变为“单进程”。
2.注意壳程序检测BeingDebugged标志进行anti-debug的部分
004B392F 33C9 xor ecx, ecx
004B3931 83C1 10 add ecx, 10
004B3934 BB FFFFFF77 mov ebx, 77FFFFFF
004B3939 64:8B83 1900008>mov eax, dword ptr fs:[ebx+88000019] ; fs:[18],TEB
004B3940 8B4448 10 mov eax, dword ptr [eax+ecx*2+10] ; TEB[30],PEB
004B3944 0FB640 02 movzx eax, byte ptr [eax+2] ; PEB[02],BeingDebugged标志。这句单步过去后,要把eax置0
004B3948 F7D0 not eax
004B394A 83E0 01 and eax, 1
004B394D 8BD8 mov ebx, eax
上面这几行很简单,就是检测PEB中的BeingDebugged标志,判断自身是否被调试。
所以单步过了004B3944这一行之后,要把eax置0
不置0的后果:
mov ebx, eax后,下面单步一会会到这里(跳到堆栈里):
0012FFBC F6FB idiv bl
0012FFBE C3 retn
如果之前不修改,这里就会出现bl==0,从而出现一个除0错误,使程序退出。
当然,也可以直接在这里把ebx置1。
3.处理SEH
004B3A8F E8 00000000 call 004B3A94
004B3A94 830424 34 add dword ptr [esp], 34
004B3A98 64:FF30 push dword ptr fs:[eax]
004B3A9B 64:8920 mov dword ptr fs:[eax], esp
这里加了一个SEH处理程序,地址为004B3AC8,之后在下面:
004B3AC6 FFE1 jmp ecx
这里F8一下又到堆栈里:
0012FF9C 0F34 sysenter
如果又单步,则又产生一个异常,结果程序又终止了。
在非调试环境下,这个异常会调用004B3AC8这个SEH处理程序。
所以在这里,我们直接把EIP改到004B3AC8处,直接在这里继续运行(这个程序的SEH比较简单,所以可以直接这么做):
004B3AC8 58 pop eax ; 改此处为新EIP
004B3AC9 58 pop eax
004B3ACA 58 pop eax
004B3ACB 58 pop eax
过了这几处,接下来的就容易了,一直到:
004B20FF 60 pushad
004B2100 68 54204B00 push 004B2054 ; ASCII "KERNEL32.DLL"
004B2105 B8 48204B00 mov eax, <&KERNEL32.GetModuleHandleA>
004B210A FF10 call dword ptr [eax]
004B210C 68 B3204B00 push 004B20B3 ; ASCII "GlobalAlloc"
004B2111 50 push eax
004B2112 B8 44204B00 mov eax, <&KERNEL32.GetProcAddress>
004B2117 FF10 call dword ptr [eax]
这里就是很标准的壳流程了,向下滚到:
004B2299 61 popad
004B229A BA EC584900 mov edx, 004958EC ; OEP
004B229F FFE2 jmp edx
在004B229F 处F4,再F8一下,就到OEP了:
004958EC 55 push ebp ; UnPackMe.004B356E
004958ED 8BEC mov ebp, esp
004958EF 83C4 F0 add esp, -10
004958F2 53 push ebx
004958F3 B8 84564900 mov eax, 00495684
004958F8 E8 C712F7FF call 00406BC4
这样直接dump出来就OK了。因为我发帖数太少,还没权限传附件,所以不能把脱壳后的程序给你

但是大致流程和需要注意的地方就这样,其他的就只是交替F7/F8/F4而已。
脱壳后PEID查壳显示Borland Delphi v3.0