首页
论坛
专栏
课程

[原创]不使用 ImportREC 来脱 PECompact 2.x 版本的壳

2009-2-16 14:57 9118

[原创]不使用 ImportREC 来脱 PECompact 2.x 版本的壳

2009-2-16 14:57
9118
【文章标题】: 不使用 ImportREC 来脱 PECompact 2.x 版本的壳
【文章作者】: CCDebuger
【下载地址】: 自己搜索下载
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
  
  写这篇文章主要是看到了这个求助帖:http://bbs.pediy.com/showthread.php?t=81974,就把里面提到的程序拿来看了一下。这里我把不用 ImportREC 来脱 PECompact 2.x 版本壳的过程写下来,再来一个脱壳脚本,简化一下流程。现在进入正题吧。
  载入程序后断 BP VirtualAlloc,两次中断后会返回到这里:
  

00EA0C30 6A 40 PUSH 40
00EA0C32 68 00100000
PUSH 1000
00EA0C37 51
PUSH ECX
00EA0C38 6A 00 PUSH 0
00EA0C3A
FF95 371F9400 CALL DWORD PTR SS:[EBP+941F37]
00EA0C40 8985 271F9400 MOV DWORD PTR SS:[EBP+941F27],EAX ; 返回到这
00EA0C46 56 PUSH ESI
00EA0C47 E8 F6030000 CALL 00EA1042
00EA0C4C 8D8D
BD1D9400 LEA ECX,DWORD PTR SS:[EBP+941DBD]
00EA0C52 85C0 TEST EAX,EAX
00EA0C54 0F85 94000000 JNZ 00EA0CEE
00EA0C5A 56
PUSH ESI
00EA0C5B E8 40030000 CALL 00EA0FA0 ; ; 这里面用解码后的内容填充原程序的各个区段,F7跟进
00EA0C60 56 PUSH ESI
00EA0C61 E8 55020000 CALL 00EA0EBB
00EA0C66 90
NOP
00EA0C67 90 NOP
00EA0C68 90 NOP
00EA0C69 90 NOP
00EA0C6A 90 NOP
00EA0C6B 90 NOP
00EA0C6C 90 NOP
00EA0C6D 90 NOP
00EA0C6E 90 NOP
00EA0C6F 90 NOP
00EA0C70 90 NOP
00EA0C71 90 NOP
00EA0C72 90 NOP
00EA0C73 90 NOP
00EA0C74 8B4E 34 MOV ECX,DWORD PTR DS:[ESI+34]
00EA0C77 85C9 TEST ECX,ECX
00EA0C79 0F84 89000000 JE 00EA0D08
00EA0C7F 034E 08
ADD ECX,DWORD PTR DS:[ESI+8]
00EA0C82 51 PUSH ECX
00EA0C83 56 PUSH ESI
00EA0C84 E8 47060000 CALL 00EA12D0 ; 处理输入表,F7跟进
00EA0C89 85C0 TEST EAX,EAX
00EA0C8B 74 7B JE SHORT 00EA0D08

    一、区段:
  跟进 00EA0C5B 的 CALL:
  

00EA0FA0 55 PUSH EBP ; 一路按F8
00EA0FA1 8BEC MOV EBP,ESP
00EA0FA3 83C4 E8 ADD ESP,-18
00EA0FA6 53
PUSH EBX
00EA0FA7 57 PUSH EDI
00EA0FA8 56 PUSH ESI
省略码...
00EA101B 5A POP EDX
00EA101C 8B75 FC MOV ESI,DWORD PTR SS:[EBP-4]
00EA101F 8B3B MOV EDI,DWORD PTR DS:[EBX]
00EA1021 03FA ADD EDI,EDX ; 各个区段的开始偏移加上基址,从最后一个开始加。个区段的起始偏移就在EDI中
00EA1023 8B4B 08 MOV ECX,DWORD PTR DS:[EBX+8]
00EA1026 8BC1 MOV EAX,ECX

  可以直接按 Ctrl+S 搜索以下命令序列:
  
  MOV R32,DWORD PTR [R32-CONST]
  MOV R32,DWORD PTR [R32]
  ADD R32,R32
  
  区段:
  1000
  00281000
  00315000
  0036D000
  0037C000
  00387000
  00389000
  0038A000
  0038B000
  0038C000
  0038D000
  0038E000
  0038F000
  
  二、输入表:
  跟进 00EA0C84 那个处理输入表的CALL:
  

00EA12D0 55 PUSH EBP ; 一路按F8到下面那个CALL
00EA12D1 8BEC MOV EBP,ESP
00EA12D3 53 PUSH EBX
00EA12D4 57 PUSH EDI
00EA12D5 56 PUSH ESI
00EA12D6 8B75 0C MOV ESI,DWORD PTR SS:[EBP+C]
00EA12D9 8B5D 08 MOV EBX,DWORD PTR SS:[EBP+8]
00EA12DC 33C0 XOR EAX,EAX
00EA12DE 3946 10 CMP DWORD PTR DS:[ESI+10],EAX
00EA12E1 75 04 JNZ SHORT 00EA12E7
00EA12E3 3906
CMP DWORD PTR DS:[ESI],EAX
00EA12E5 74 24 JE SHORT 00EA130B
00EA12E7 0306
ADD EAX,DWORD PTR DS:[ESI]
00EA12E9 74 03 JE SHORT 00EA12EE
00EA12EB 0343 08
ADD EAX,DWORD PTR DS:[EBX+8]
00EA12EE 8B4E 0C MOV ECX,DWORD PTR DS:[ESI+C]
00EA12F1 034B 08 ADD ECX,DWORD PTR DS:[EBX+8]
00EA12F4 8B7E 10 MOV EDI,DWORD PTR DS:[ESI+10]
00EA12F7 85FF TEST EDI,EDI
00EA12F9 74 03 JE SHORT 00EA12FE
00EA12FB 037B 08
ADD EDI,DWORD PTR DS:[EBX+8]
00EA12FE 50 PUSH EAX
00EA12FF 57 PUSH EDI
00EA1300 51 PUSH ECX
00EA1301 53 PUSH EBX
00EA1302 E8 1F000000 CALL 00EA1326 ; 到这个CALL时按F7跟进去
00EA1307 40 INC EAX
00EA1308 75 08 JNZ SHORT 00EA1312

    跟进 00EA1302 处的 CALL:
  

00EA1326 55 PUSH EBP ; 一路按F8
00EA1327 8BEC MOV EBP,ESP
00EA1329 83C4 E8 ADD ESP,-18
00EA132C 53
PUSH EBX
省略码...
00EA1370 5B POP EBX
00EA1371 C9 LEAVE
00EA1372 C2 1000 RETN 10
00EA1375 90
NOP
都是 NOP...

    直接按 Ctrl+S 搜索命令序列:
  
  MOV DWORD PTR [R32],R32
  MOV DWORD PTR [R32],R32
  
  会搜到这里:
  

00EA13F4 85C0 TEST EAX,EAX
00EA13F6 ^ 0F84 6FFFFFFF JE 00EA136B ; 下面两句改掉,这样不会用得到的函数地址填充输入表,可以得到原始的输入表,只要填一下dump文件的输入表相关 RVA 及大小即可
00EA13FC 8906 MOV DWORD PTR DS:[ESI],EAX ; 搜到这里。关键,改成 mov eax,[edx]
00EA13FE 8902 MOV DWORD PTR DS:[EDX],EAX ; 改成 mov dword ptr [esi],eax
00EA1400 83C2 04 ADD EDX,4
00EA1403 83C6 04
ADD ESI,4

    三、OEP
  现在设 BP VirtualFree,F9 断下后删除断点,ALT+F9 返回,在一路 F8,会到这里:
  

008C1034 8985 3F139400 MOV DWORD PTR SS:[EBP+94133F],EAX ; 来到这里
008C103A 8BF0 MOV ESI,EAX
008C103C 8B4B 14 MOV ECX,DWORD PTR DS:[EBX+14]
008C103F 5A POP EDX
008C1040 EB 0C JMP SHORT 008C104E
008C1042 03CA
ADD ECX,EDX
008C1044 68 00800000 PUSH 8000
008C1049 6A 00
PUSH 0
008C104B 57
PUSH EDI
008C104C FF11 CALL DWORD PTR DS:[ECX]
008C104E 8BC6 MOV EAX,ESI
008C1050 5A POP EDX
008C1051 5E POP ESI
008C1052 5F POP EDI
008C1053 59 POP ECX
008C1054 5B POP EBX
008C1055 5D POP EBP
008C1056 FFE0 JMP EAX ; 这里跳向 OEP

  在地址 008C1056 上按 F4,再按一下 F7,就到 OEP 了:
  

005DC009 > E8 07BF0000 CALL 005E7F15 ; 这就是 OEP 了
005DC00E ^ E9 17FEFFFF JMP 005DBE2A
005DC013 6A 0C
PUSH 0C
005DC015 68 101A6F00
PUSH 006F1A10
005DC01A
E8 817A0000 CALL 005E3AA0

  
  四、修正 dump 文件
  现在 dump,修正 OEP 和 IAT 的起始 RVA 和大小即可。OEP = 005DC009 - 00400000 = 001DC009。根据输入表的结构(5 个字段组成,最后以 20 个 0 字节结尾),我们用 16 进制工具如 010Editor 在 dump 后的文件第一个区段中搜索 20 个 0 字节,再结合输入表结构,能很快定位的 IAT 的起始 RVA 和 大小:
  
  31:1058h: 70 1A 31 00 00 00 00 00 00 00 00 00 32 1C 31 00  p.1.........2.1.
  31:1068h: 88 18 28 00 D8 1A 31 00 00 00 00 00 00 00 00 00  ?(.?1.........
  31:1078h: A0 1C 31 00 F0 18 28 00 80 1A 31 00 00 00 00 00  ?1.?(.€.1.....
  31:1088h: 00 00 00 00 8A 1D 31 00 98 18 28 00 78 1B 31 00  ....?1.?(.x.1.
  31:1098h: 00 00 00 00 00 00 00 00 96 1D 31 00 90 19 28 00  ........?1.?(.
  31:10A8h: E4 1B 31 00 00 00 00 00 00 00 00 00 CA 1D 31 00  ?1.........?1.
  31:10B8h: FC 19 28 00 50 13 31 00 00 00 00 00 00 00 00 00  ?(.P.1.........
  31:10C8h: 3C 2C 31 00 68 11 28 00 3C 17 31 00 00 00 00 00  <,1.h.(.<.1.....
  31:10D8h: 00 00 00 00 70 39 31 00 54 15 28 00 40 12 31 00  ....p91.T.(.@.1.
  31:10E8h: 00 00 00 00 00 00 00 00 82 3D 31 00 58 10 28 00  ........?1.X.(.
  31:10F8h: 38 12 31 00 00 00 00 00 00 00 00 00 9C 3D 31 00  8.1.........?1.
  31:1108h: 50 10 28 00 C8 1A 31 00 00 00 00 00 00 00 00 00  P.(.?1.........
  31:1118h: E0 3D 31 00 E0 18 28 00 E8 11 31 00 00 00 00 00  ?1.?(.?1.....
  31:1128h: 00 00 00 00 B4 3E 31 00 00 10 28 00 E0 16 31 00  ....?1...(.?1.
  31:1138h: 00 00 00 00 00 00 00 00 BC 3F 31 00 F8 14 28 00  ........?1.?(.
  31:1148h: 28 12 31 00 00 00 00 00 00 00 00 00 06 40 31 00  (.1..........@1.
  31:1158h: 40 10 28 00 18 17 31 00 00 00 00 00 00 00 00 00  @.(...1.........
  31:1168h: AE 40 31 00 30 15 28 00 DC 1B 31 00 00 00 00 00  瓳1.0.(.?1.....
  31:1178h: 00 00 00 00 C8 40 31 00 F4 19 28 00 88 1B 31 00  ....菮1.?(.?1.
  31:1188h: 00 00 00 00 00 00 00 00 7C 42 31 00 A0 19 28 00  ........|B1.?(.
  31:1198h: 7C 16 31 00 00 00 00 00 00 00 00 00 86 42 31 00  |.1.........咮1.
  31:11A8h: 94 14 28 00 48 13 31 00 00 00 00 00 00 00 00 00  ?(.H.1.........
  31:11B8h: 52 43 31 00 60 11 28 00 6C 1B 31 00 00 00 00 00  RC1.`.(.l.1.....
  31:11C8h: 00 00 00 00 86 43 31 00 84 19 28 00 00 00 00 00  ....咰1.?(.....
  31:11D8h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  
  由此我们知道 IAT 的起始地址 RVA 是 00311058,大小是 190。
  
  写个脚本来完成以上工作,同时也兼容一下 DLL 的脱壳。用脚本跑完后查看一下记录窗口,先把整个表复制出来,再去掉无用的内容,剩下的就是这样:
  
Log data
地址       消息
           区段 RVA = 0038F000
           区段 RVA = 0038E000
           区段 RVA = 0038D000
           区段 RVA = 0038C000
           区段 RVA = 0038B000
           区段 RVA = 0038A000
           区段 RVA = 00389000
           区段 RVA = 00387000
           区段 RVA = 0037C000
           区段 RVA = 0036D000
           区段 RVA = 00315000
           区段 RVA = 00281000
           区段 RVA = 001000
           IAT 中某函数的 RVA 地址 = 00311C0A
           IAT 可能的结束 RVA 地址 = 003111D4
           OEP RVA = 001DC009

  脚本已经生成了 dump 文件,保存为 Unpacked.exe,并已修正了 OEP。其它的你就自己填了。
  
  附上脱壳脚本。用脚本脱壳后要对 dump 的文件进行修复,要涉及到一些 PE 知识。不了解的话,请自己找一下讲述 PE 格式的文档参考一下。脚本没经过多少测试,如果有问题,不要打我,呵呵

/*
Script written by CCDebuger
Script
: PECompact 2.x_No_ImportREC
版本 : v0.1
日期
: 15-2-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
*/

var tmp1
var tmp2
var VirtualAlloc
var
section
var iatloc
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

//设 VirtualAlloc 断点,返回到相位置

gpa
"VirtualAlloc", "kernel32.dll"
mov VirtualAlloc, $RESULT
bp VirtualAlloc

VirtualAlloc_Next
:
esto
rtu

/*
查找命令序列:
ADD EDI,EDX
MOV ECX,DWORD PTR DS:[EBX+8]
MOV
EAX,ECX
*/
find eip, #03FA8B4B088BC1#
mov section, $RESULT
cmp section, 0
je VirtualAlloc_Next
bp section
bc VirtualAlloc
lc
eob logsection
esto

logsection
:
log edi, "区段 RVA = "
rtr

bc
section
cob
log
"区段 RVA = 001000" //最开始的那个段
sto

isdll
:
mov
tmp1, [signVA + 5E], 2 //DLL 特征值
cmp tmp1, 1
je dll
mov unpackname, "Unpacked.exe"
jmp findIAT

dll
:
/*
查找命令序列:
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
add dllreloc, 0D
//asm dllreloc, "cmp eax,eax"
bp dllreloc
esto
bc
log
esi, "重定位表 RVA = "
mov [dllreloc + 2], 0EB, 1
/*
查找命令序列:
JMP SHORT 00AB0BC9
POP ESI
POP EDI
POP EBX
LEAVE
RETN
4
令序处理重定位表数的返回部分
*/
find eip, #EB??5E5F5BC9C2????#
add $RESULT, 2
bp $RESULT
esto
bc
mov [dllreloc + 2], 074, 1
//asm dllreloc, "cmp eax,ebx"
mov unpackname, "Unpacked.dll"

findIAT:
/*
查找命令序列:
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

nextapi
:
esto
esto
mov apiloc, [esi-4]
cmp
apiloc, 800000
jae nextapi

log apiloc
, "IAT 中某函数的 RVA 地址 = "
div apiloc, 01000
mul apiloc, 01000
add apiloc, imgbase
//IAT 表以 5 个字段组成,最后以 20 0 组成的特性来
//这个不大准的。IAT 结束地址只能做参考。以根这个参考,用16进制工具开 dump 后的文件,下搜搜看 20 0 ,应该在附近,不会
find apiloc
, #0000000000000000000000000000000000000000#
mov apiloc, $RESULT
add apiloc, 1
sub apiloc, imgbase
log apiloc
, "IAT 可能的结束 RVA 地址 = "
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"
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 tmp2, signVA
add tmp2, 0F8 //第一个区段

last
:
cmp
tmp1, 0
je lab1
mov [tmp2 + 10], [tmp2 + 8] //V.size 等于 R.size
mov [tmp2 +14], [tmp2 + 0C] //拟偏移等于 RW 偏移
mov [tmp2 + 24], 0E00000E0 //设置区段属
add tmp2, 28 //指向一个区段
sub tmp1, 1
jmp last

lab1
:
mov [
signVA + 28], oeprva //填写 OEP
dm imgbase
, modsize, unpackname
msg
"已经到 OEP 了。程序已 dump 后另存为 Unpacked.exe 或 Unpacked.dll。OEP 已修正,现在请参考记录窗口中的数据,重建各个区段及修正 IAT RVA 的起始地址和大小。若是 DLL,请根据记录窗口中的重定位表 RVA 数据,再对照各个区段的 RVA,自己填写。重定位表的大小就是对应区段减掉区段末尾全是0的数据后的大小。重建区段后,必要时需重建资源。"

exit:
ret

errorver:
msg "运行此脚本需要 ODbgScript 1.65 或更高的版本,您的版本过低,请更新 ODbgScript 后再试。"
ret


--------------------------------------------------------------------------------
【版权声明】: 本文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢!

2020安全开发者峰会(2020 SDC)议题征集 中国.北京 7月!

最新回复 (17)
stalker 8 2009-2-16 15:55
2
0
沙发,学习!
大菜一号 21 2009-2-16 17:08
3
0
cc很久没出手了,学习
hmilywen 2009-2-17 09:19
4
0
学习~
weolar 10 2009-2-17 10:20
5
0
[QUOTE=;]...[/QUOTE]
很久没看到CC的了~~学习学习
快雪时晴 4 2009-2-17 18:59
6
0
CC一定是在闭关练功,等着,会陆续来礼物的
Lancia 2009-2-17 19:27
7
0
CC太强大了,,感谢好文!
夜凉如水 3 2009-2-18 13:03
8
0
感谢cc的作品 太强了
linhanshi 2009-2-19 15:05
9
0
Support.
狐狸少爷 2009-2-19 15:49
10
0
学习 支持  
美丽破船 5 2009-2-22 18:39
11
0
哇~~~
好久不见C版出来了,嘿嘿~~~
C版出品,必属精品,收藏学习了·~~
美丽破船 5 2009-2-22 18:41
12
0
CC版,出手就是精品,哈哈,收藏学习了
yuyuchun 2009-2-22 20:19
13
0
一出手就知道是大老级的作品!
RegKiller 10 2009-5-1 06:39
14
0
楼主请看下这东西脱壳后如何完美重建各区段?

脱壳后如果出现 runtime error R6002 程序检查RAV是否可写,不可写程序不载入,我们强行更改成可写!
可以把下面改掉:

005B85B4 |. 59 pop ecx ; 111.00612800
005B85B5 |. 85C0 test eax, eax
005B85B7 |. 74 0A je short 005B85C3
005B85B9 |. FF75 08 push dword ptr [ebp+8]
005B85BC |. FF15 00286100 call dword ptr [612800] ; 111.005B5394
005B85C2 |. 59 pop ecx

把 005B85B7 je 更改 nop

关于这东西的脱壳问题不大,问题在于如果脱壳后不用 FreeRES v0.94 重建资源,那么用 PE Explorer 1.9.8 等资源工具修改后程序无法运行,如果用FreeRES v0.94 重建资源,那么脱壳后的文件等于说是多了一个垃圾区段。

希望楼主可以交流一下本程序脱壳后的各区段完美重建及资源段无重复区段的可修改方法。
上传的附件:
CCDebuger 24 2009-5-1 10:56
15
0
用我这个帖子里的脚本直接脱:
http://bbs.pediy.com/showthread.php?t=82168
脱完先用 FixRes dump 资源,再把脱壳后的程序第二个段的特征值改为 40000040,把 TLS 目录和大小都改为0,现在删除垃圾段,拼接 dump 的资源即可。
附件是我用脚本脱完后删除垃圾区段,重建资源,修改了 PE 头中相关信息的脱壳文件,你可以对照一下。还可以用 PE 优化工具再把文件对齐优化为 200,可以再减小一点体积。
上传的附件:
RegKiller 10 2009-5-2 07:40
16
0
[QUOTE=CCDebuger;614969]用我这个帖子里的脚本直接脱:
http://bbs.pediy.com/showthread.php?t=82168
脱完先用 FixRes dump 资源,再把脱壳后的程序第二个段的特征值改为 40000040,把 TLS 目录和大小都改为0,现在删除垃圾段,拼接 dump 的资源即可。
附件是我...[/QUOTE]

这个确实可以修改资源了,而且各区段也比较干净。我想问一下你脱壳后删除了哪几个垃圾区段?
CCDebuger 24 2009-5-2 10:08
17
0
用我那个脚本脱完 dump 资源后,删掉了最后的 CanBeDel 段和倒数第二个段,再拼接 dump 出来的资源段。
liugeshao 2009-5-3 15:44
18
0
谢谢分享  看看  学习下
游客
登录 | 注册 方可回帖
返回