/*
Script written by CCDebuger
Script : PECompact 2.x Unpacker
版本 : v0.2
日期 : 15-03-2009
调试环境 : OllyDbg 1.1, ODBGScript 1.65, WINXP, WIN2000
调试选项 : 设置 OllyDbg 忽略所有异常选项
工具 : OllyDbg, ODBGScript 1.65
感谢 : Oleh Yuschuk - author of OllyDbg
SHaG - author of OllyScript
hnhuqiong - author of ODbgScript
Epsylon3 - author of ODbgScript
*/
var tmp1
var tmp2
var tmp3
var OrgCode
var jumpflag
var ProcName
var ResetImageBase
var VirtualAlloc
var section
var SecName
var SecBase
var SecNum
var IATRVA
var IATSize
var RelocRVA
var RelocSize
var AllocVA
var AllocVATemp
var AllocVAReal
var VirtualFree
var imgbase
var signVA
var modsize
var dllreloc
var oep
var oeprva
var apiloc
var unpackname
msgyn "为保证脚本能正确运行,请忽略所有异常。本脚本只能用于 PECompact 2.x 版本加壳的 EXE 或 DLL"
cmp $RESULT, 0
je exit
cmp $VERSION, "1.65"
jb errorver
bc
bphwcall
dbh
GMI eip, MODULEBASE //get imagebase
mov imgbase, $RESULT
gmi eip, MODULESIZE
mov modsize,$RESULT
mov tmp1, [imgbase+3C] //获取 PE 签名的偏移
add tmp1, imgbase //tmp1=签名 VA
mov signVA, tmp1
gpi PROCESSNAME
mov ProcName, $RESULT
gpi EXEFILENAME
//设 VirtualAlloc 断点,返回到相关位置
gpa "VirtualAlloc", "kernel32.dll"
mov VirtualAlloc, $RESULT
bp VirtualAlloc
alloc 1000
mov AllocVA, $RESULT
mov AllocVATemp, AllocVA
mov AllocVAReal, AllocVA + 400 //用来保存区段的实际大小
VirtualAlloc_Next:
esto
rtu
/*
查找命令序列:
00AB10BD 8B3B MOV EDI,DWORD PTR DS:[EBX] ; 各个区段的 RVA 地址送EDI
00AB10BF 03FA ADD EDI,EDX ; 区段RVA+基址
00AB10C1 8B4B 08 MOV ECX,DWORD PTR DS:[EBX+8] ; 区段大小送ECX
00AB10C4 8BC1 MOV EAX,ECX ; 这里设断
*/
find eip, #03FA8B4B088BC1#
mov section, $RESULT
cmp section, 0
je VirtualAlloc_Next
add section, 5
bp section
bc VirtualAlloc
lc
eob logsection
esto
logsection:
mov tmp1, edi
sub tmp1, imgbase
log tmp1, "区段 RVA = "
mov tmp2, ecx
mov [AllocVAReal], tmp1
add AllocVAReal, 4
mov [AllocVAReal], tmp2
add AllocVAReal, 4
div tmp2, 1000
mul tmp2, 1000
add tmp2, 1000
log tmp2, "区段大小 = "
mov [AllocVA], tmp1
add AllocVA, 4
mov [AllocVA], tmp2
add AllocVA, 4
rtr
bc section
cob
log "区段 RVA = 001000" //加上最开始的那个段
mov [AllocVA], 1000
mov tmp1, [AllocVA - 8]
sub tmp1, 1000
add AllocVA, 4
mov [AllocVA], tmp1
log tmp1, "区段大小 = "
mov tmp1, AllocVA
FixSectionSize:
cmp AllocVATemp, tmp1 - 4
je continue
mov tmp2, [tmp1] //区段大小
mov tmp3, [tmp1 - 0C] //下一个区段 RVA
sub tmp3, [tmp1 - 4] //两个区段间的大小
sub tmp1, 8 //下一个区段大小
cmp tmp3, tmp2 //实际大小与获取的大小比较
jb FixSecSize
jmp FixSectionSize
FixSecSize:
mov [tmp1 + 8], tmp3
jmp FixSectionSize
continue:
sto
isdll:
mov tmp1, [signVA + 5E], 2 //DLL 特征值
cmp tmp1, 1
je dll
mov tmp2, [signVA + 0A0] //重定位表RVA
cmp tmp2, 0
jne dll
eval "UN_{ProcName}.exe"
mov unpackname, $RESULT
jmp findIAT
dll:
eval "UN_{ProcName}.dll"
mov unpackname, $RESULT
/*查找命令序列:
00950D08 56 PUSH ESI
00950D09 E8 30030000 CALL 0095103E
00950D0E 56 PUSH ESI
00950D0F E8 45020000 CALL 00950F59 ;后面的内容
00950D14 56 PUSH ESI
00950D15 E8 45010000 CALL 00950E5F
00950D1A 90 NOP
*/
find eip, #56E8????????56E8????????90#
mov tmp2, $RESULT
Next_Reloc:
/*
查找命令序列:
MOV EAX,DWORD PTR DS:[EDI+4]
MOV EBX,DWORD PTR DS:[EDI+8]
CMP EAX,EBX
JE SHORT 00AB0C12
*/
find eip, #8B47??8B5F??3BC374??#
mov dllreloc, $RESULT
cmp dllreloc, 0
je findIAT
mov tmp1, dllreloc + 08
bp tmp1
esto
bc
mov jumpflag, ebx
sub jumpflag, eax
opcode tmp1
mov OrgCode, $RESULT_1
mov [tmp1], #9090#
add dllreloc, 0D
bp dllreloc
esto
bc
log esi, "重定位表 RVA = "
mov RelocRVA, esi
mov tmp3, AllocVAReal
GetRelocSize:
cmp RelocRVA, [tmp3 - 8]
je FindRelocSize
sub tmp3, 8
jmp GetRelocSize
FindRelocSize:
mov RelocSize, [tmp3 - 4]
log RelocSize, "重定位表大小 = "
/*
查找命令序列:
JMP SHORT 00AB0BC9
POP ESI
POP EDI
POP EBX
LEAVE
RETN 4
以上命令序列就是处理重定位表函数的返回部分
*/
find eip, #EB??5E5F5BC9C2????#
add $RESULT, 2
bp $RESULT
cmp jumpflag, 0
je MustJump
cmp tmp2,0
jne RunToRelocRet
MustJump:
mov [dllreloc + 2], 0EB, 1
RunToRelocRet:
mov ResetImageBase, tmp2
esto
bc
eval "{OrgCode}"
asm tmp1, $RESULT
mov [dllreloc + 2], 074, 1
rtr
sto
findIAT:
find eip, #8B4E??85C90F84#
mov tmp1, $RESULT
add tmp1, 3
bp tmp1
esto
bc
mov IATRVA, ecx
cmp IATRVA, 0
je FindOEP
log IATRVA, "输入表 RVA = "
mov tmp1, imgbase
add tmp1, IATRVA
//根据 IAT 表以 5 个字段组成,最后以 20 个 0 字节组成的特性来搜索
find tmp1, #0000000000000000000000000000000000000000#
mov IATSize, $RESULT
sub IATSize, tmp1
add IATSize, 015
log IATSize, "输入表大小 = "
/*
查找命令序列:
MOV DWORD PTR DS:[ESI],EAX
MOV DWORD PTR DS:[EDX],EAX
即处理输入表的部分。
*/
find eip, #89068902#
mov iatloc, $RESULT
cmp iatloc,0
je exit
asm iatloc, "mov eax,[edx]"
add iatloc, $RESULT
asm iatloc, "mov dword ptr [esi],eax"
bp iatloc
esto
bc iatloc
rtr
sto
find eip, #485E5F5BC9C2????# //在返回地址上设断
add $RESULT, 5
bp $RESULT
esto
bc
asm iatloc, "mov dword ptr [edx],eax"
sub iatloc, $RESULT
asm iatloc, "mov dword ptr [esi],eax"
FindOEP:
gpa "VirtualFree", "kernel32.dll"
mov VirtualFree, $RESULT
BP VirtualFree
nextoep:
esto
rtu
rtr
sto
find eip, #5A5E5F595B5DFFE0#
mov oep, $RESULT
cmp oep, 0
je nextoep
bc VirtualFree
add oep, 6
bp oep
esto
sti
bc
mov oeprva, eip
sub oeprva, imgbase
log oeprva, "OEP RVA = "
cmt eip, "这就是 OEP 了"
mov [signVA + 3C], 1000 //文件对齐设为1000
mov [signVA + 54], 1000 //头部大小设为1000
mov tmp1, 0
mov tmp1, [signVA + 6], 2 //区段数目
mov SecBase, signVA
add SecBase, 0F8 //第一个区段
cmp tmp1, 0
je lab1
fill SecBase, 200, 0
mov tmp2, AllocVA + 4
sub tmp2, AllocVATemp
div tmp2, 8
mov SecNum, tmp2
mov tmp2, 1
mov tmp4, SecNum
last:
cmp AllocVATemp, AllocVA - 4
je SetLastSec
itoa tmp2
mov SecName, $RESULT
mov [SecBase], SecName //区段名称
mov [SecBase + 08], [AllocVA] //VirtualSize
mov [SecBase + 010], [AllocVA] //SizeOfRawData
sub AllocVA, 4 //指向区段偏移
mov [SecBase + 0C], [AllocVA] //VirtualAddress
mov [SecBase + 014], [AllocVA] //PointerToRawData
mov [SecBase + 024], 0E00000E0 //设置区段属性
sub AllocVA, 4 //指向下一个区段大小
add tmp2, 1
add SecBase, 28 //指向一个区段
mov tmp3, [AllocVA - 4] //下一个区段的偏移地址
mov tmp1, tmp3
sub tmp3, [AllocVA + 4] //两个区段相减
cmp tmp3, [AllocVA + 8] //判断两个区段间的大小是否相符
je last
sub tmp3, [AllocVA + 8] //取新区段的大小
sub tmp1, tmp3 //取新区段的偏移 RVA
itoa tmp2
mov SecName, $RESULT
mov [SecBase], SecName //区段名称
mov [SecBase + 08], tmp3 //VirtualSize
mov [SecBase + 010], tmp3 //SizeOfRawData
mov [SecBase + 0C], tmp1 //VirtualAddress
mov [SecBase + 014], tmp1 //PointerToRawData
mov [SecBase + 024], 0E00000E0 //设置区段属性
add SecNum, 1
add tmp2, 1
add SecBase, 28 //指向一个区段
jmp last
SetLastSec:
itoa tmp2
mov SecName, $RESULT
mov [SecBase], SecName //区段名称
mov [SecBase + 08], [AllocVA] //VirtualSize
mov [SecBase + 010], [AllocVA] //SizeOfRawData
sub AllocVA, 4 //指向区段偏移
mov [SecBase + 0C], [AllocVA] //VirtualAddress
mov [SecBase + 014], [AllocVA] //PointerToRawData
mov [SecBase + 024], 0E00000E0 //设置区段属性
add SecNum,1
add SecBase, 28 //指向一个区段
mov SecName, "CanBeDel"
mov tmp1, modsize
mov tmp2, [AllocVA + 4] //原程序最后一个区段的大小
add tmp2, [AllocVA] //加上最后一个区段的偏移,得到新区段的偏移 RVA
sub tmp1, tmp2 //最后一个区段的大小
mov [SecBase], SecName //区段名称
mov [SecBase + 08], tmp1 //VirtualSize
mov [SecBase + 010], tmp1 //SizeOfRawData
mov [SecBase + 0C], tmp2 //VirtualAddress
mov [SecBase + 014], tmp2 //PointerToRawData
mov [SecBase + 024], 0E00000E0 //设置区段属性
mov [signVA + 6], SecNum, 2 //设置区段数目
lab1:
free AllocVATemp, 1000
mov [signVA + 28], oeprva //填写 OEP
mov [signVA + 80], IATRVA //输入表 RVA 地址
mov [signVA + 84], IATSize //输入表大小
mov [signVA + 0A0], RelocRVA //重定位表 RVA 地址
mov [signVA + 0A4], RelocSize //重定位表大小
cmp ResetImageBase, 0
je dumpproc
mov [signVA + 34],imgbase //更改镜像基址
dumpproc:
dm imgbase, modsize, unpackname
msg "已经到 OEP 了。程序已 dump 后另存为 UN_+原文件名。OEP 、输入表、重定位(若有)都已修正,区段都已重建。若要优化大小,请查看输出表及资源是否在最后那个“CanBeDel”段中,若在,请重建输出表及资源后再删除最后的那个“CanBeDel”段。"
exit:
ret
errorver:
msg "运行此脚本需要 ODbgScript 1.65 或更高的版本,您的版本过低,请更新 ODbgScript 后再试。"
ret