一、学习目标:PEP2.55主程序
二、使用工具:OllyDbg v1.10,ImportREC 1.6 Final,LordPE,ResHacker 3.4
三、作者邮箱:DarkBull#126.com
四、分析过程:
1.寻找OEP
首先设置OD停在系统断点,忽略所有异常,载入主程序,运行以下脚本。
// PEP 2.55 Find OEP Script
// Writen By DarkBull
data:
var modbase
var ep
var oep
var temp
code:
mov modbase,400000
mov temp,[modbase+3C]
mov ep,[modbase+temp+28]
add ep,modbase
bprm ep,1
bc ep
mov [modbase+temp+74],36521DC3
loop1:
run
cmp eip,ep
jne loop1
bpmc
gpa "CreateThread","kernel32.dll"
iscrtr:
go $RESULT
cmp eip,$RESULT
jne iscrtr
mov trproc,[esp+0C]
bp trproc
istrproc:
run
cmp eip,trproc
jne istrproc
bc trproc
mov temp,[esp+4]
mov temp,[temp]
findp1:
find temp,#FF751CFF7518FF7514FF7510FF750CFF75086AFFE8#
cmp $RESULT,0
je error
mov [$RESULT+3],0FF90046A
bprm 00401000,1
ischk:
esto
cmp [eip],0C6830633
jne ischk
bpmc
find eip,#61C3#
go $RESULT
rtr
rtr
findoep:
find eip,#5A5BFFE3#
add $RESULT,2
go $RESULT
sti
sti
cmt eip,"OEP"
ret
error:
msg "出错了!@#$%^&*"
ret
脚本运行后,停在以下代码处:
3E2842BA 9C PUSHFD
3E2842BB F7DE NEG ESI
3E2842BD F7DE NEG ESI
3E2842BF 9D POPFD
3E2842C0 55 PUSH EBP ; OEP
3E2842C1 76 00 JBE SHORT Private_.3E2842C3
3E2842C3 E9 00000000 JMP Private_.3E2842C8
3E2842C8 50 PUSH EAX
3E2842C9 58 POP EAX
3E2842CA 8BEC MOV EBP,ESP
3E2842CC EB 00 JMP SHORT Private_.3E2842CE
3E2842CE E9 02000000 JMP Private_.3E2842D5
3E2842D3 F4 HLT ; Privileged command
3E2842D4 55 PUSH EBP
3E2842D5 76 00 JBE SHORT Private_.3E2842D7
3E2842D7 83C4 F0 ADD ESP,-10
继续运行到如下代码处:
3E28436D /E9 01000000 JMP Private_.3E284373
3E284372 |DC7D 00 FDIVR QWORD PTR SS:[EBP]
3E284375 E8 F22F18C2 CALL Private_.0040736C
3E28437A D9D0 FNOP
3E28437C E9 01000000 JMP Private_.3E284382
........
3E284382 D9D0 FNOP
3E284384 E8 FF3832C2 CALL Private_.005A7C88
3E284389 75 00 JNZ SHORT Private_.3E28438B
3E28438B - E9 2D6D32C2 JMP Private_.005AB0BD
STolen OEP 整理如下:
Private_.> 55 PUSH EBP
005AB0A9 8BEC MOV EBP,ESP
005AB0AB 83C4 F0 ADD ESP,-10
005AB0AE B8 B8835A00 MOV EAX,Private_.005A83B8
005AB0B3 E8 B4C2E5FF CALL Private_.0040736C
005AB0B8 E8 CBCBFFFF CALL Private_.005A7C88
005AB0BD E8 329CE5FF CALL Private_.00404CF4
005AB0C2 8BC0 MOV EAX,EAX
2.修复IAT
API被Hook到壳的代码段里,见下面代码:
004072A5 8D40 00 LEA EAX,DWORD PTR DS:[EAX]
004072A8 - E9 39B7E33D JMP Private_.3E2429E6
004072AD 43 INC EBX
004072AE 8BC0 MOV EAX,EAX
004072B0 - E9 EDB4E33D JMP Private_.3E2427A2
004072B5 94 XCHG EAX,ESP
004072B6 8BC0 MOV EAX,EAX
004072B8 - E9 F2B3E33D JMP Private_.3E2426AF
004072BD 65:8BC0 MOV EAX,EAX ; Superfluous prefix
004072C0 - E9 3FB2E33D JMP Private_.3E242504
虽然入口不同,但进入后解码过程基本相同,见下面代码:
<Private_> 55 PUSH EBP ; VM_Entry
3E1C3DB9 8BEC MOV EBP,ESP
3E1C3DBB - E9 96C90B00 JMP Private_.3E280756
3E1C3DC0 BB 044C8CD4 MOV EBX,D48C4C04
3E1C3DC5 15 5D9DE52E ADC EAX,Private_.2EE59D5D
3E1C3DCA A3 6EBE0747 MOV DWORD PTR DS:[4707BE6E],EAX
3E1C3DCF 8F ??? ; Unknown command
3E1C3DD0 D7 XLAT BYTE PTR DS:[EBX+AL]
3E1C3DD1 1860 A0 SBB BYTE PTR DS:[EAX-60],AH
3E1C3DD4 5A POP EDX
3E1C3DD5 E8 41F1B089 CALL C7CD2F1B
3E1C3DDA 6A C3 PUSH -3D
3E1C3DDC FE ??? ; Unknown command
3E1C3DDD FC CLD
3E1C3DDE C9 LEAVE
3E1C3DDF 0D 128462B2 OR EAX,B2628412
3E1C3DE4 FA CLI
3E1C3DE5 43 INC EBX
3E1C3DE6 5D POP EBP
<Private_> C2 0400 RET 4 ; VM_Ret
我们只需在VM_Ret处下断点,断下后有三种情况:
(1)直接返回到函数地址。
(2)返回到壳代码处,继续执行到函数地址。
(3)返回到被壳自行加载的kelnel32.dll的函数地址。
找到函数地址后,将它写到005E9000地址,再将被Hook的API指向IAT地址,用ImportREC重建IAT。
3.修复rsrc
壳Hook了“FindResource"、“SizeofResource”、“LoadResource”几个函数,使用自己的模拟函数加载资源。被加密的资源大概有167项数据。偶没办法全部还原,只有在运行程序时,观察以上几个函数调用和返回值,将资源类型、资源名称、资源大小保存下来,再用ResHacker添加到程序中。举例如下:
0041A54C 50 PUSH EAX
0041A54D 53 PUSH EBX
0041A54E E8 69D1FEFF CALL <JMP.&kernel32.FindResourceA>
堆栈内容如下:
0012FBC0 00400000 ASCII "MZP"
0012FBC4 3F57D618 ASCII "TfrmMain" ; ResName
0012FBC8 0000000A ; ResType
返回后EAX=9B,是壳加密后的资源数据索引。
0041E39F 50 PUSH EAX
0041E3A0 56 PUSH ESI
0041E3A1 E8 C694FEFF CALL <JMP.&kernel32.LoadResource>
堆栈内容如下:
0012FB78 00400000 ASCII "MZP"
0012FB7C 0000009B
返回后EAX指向资源数据。
保存资源数据后,添加资源文件头部。
附:资源文件.res格式如下(E文):
offset field data type description
0 datasize dword 4 bytes, 32 bit unsigned integer the size of the data that follows the header (not including any padding). 
4 headersize dword 4 bytes, 32 bit unsigned integer the size of the header structure.
8 restype ordinal or unicode string array of 16 bit unsigned integers (2 bytes each) resource type id (standard or custom). 
ofsof(restype)+sizeof(restype)+padding resname ordinal or unicode string array of 16 bit unsigned integers (2 bytes each) resource name.
ofsof(resname)+sizeof(resname)+padding dataversion dword 4 bytes, 32 bit unsigned integer determines the format of the information within the resource header that follows. not currently used. should be zeroed.
ofsof(dataversion)+4 memoryflags word 2 bytes, 16 bit unsigned integer the state of the resource. 
ofsof(memoryflags)+2 languageid word 2 bytes, 16 bit unsigned integer the language that the strings are written with. 
ofsof(languageid)+2 version dword 4 bytes, 32 bit unsigned integer it has no significance to the system. used by resource editors. usually zeroed. 
ofsof(version)+4 characteristics dword 4 bytes, 32 bit unsigned integer it has no significance to the system. used by resource editors. usually zeroed.