【文章作者】: sskey
【软件名称】: ASPR-test.exe
【下载地址】: 见附件
【保护方式】: ASProtect 2.0x Registered -> Alexey Solodovnikov
【使用工具】: od,lordPE,ImportREC,winxp
【程序语言】: vc
----------------------------------------------------------------------
【说明】:
实验程序是syscom大侠一篇文章里的附件,有stolen code,对于stolen code一般
可以用手动还原(所偷的代码少时,可以手动跟出或用未加壳的同类语言编的程序对比来还原),
但所偷代码比较多时,就只有用补区段的方法了,我正是为了了解一下脱壳时的补区段,才下了
这个附件,结果弄得我很郁闷,断断续续用了4天的时间才搞定,还到论坛上发了求助的帖子,没
人回(更郁闷!!!),后终于想通,是我在补区段后没修正好Voffset,呵呵(听到后你们都晕了吧,
被这么简单的问题困扰了几天,太有才了 .......)
口水有点多了,下面来分享一下脱壳过程吧(高手就飘过了)
【脱壳流程】:
1、找oep
2、修复call xxxxxx
3、补区段
4、去掉壳代码校验(Route CHECK)
【详细过程】:
1、找oep
od载入后停在这里:
00401000 > 68 01C04800 push 0048C001 //壳入口
00401005 E8 01000000 call 0040100B
0040100A C3 retn
0040100B C3 retn
0040100C 47 inc edi
0040100D EF out dx, eax
0040100E 6D ins dword ptr es:[edi], dx
0040100F 60 pushad
00401010 FD std
00401011 FE ??? ; 未知命令
00401012 7A FB jpe short 0040100F
可以用多次内存断点来找,不过对这个壳来说不是很精确(会超过oep几行),下面这种方法比较
准确:忽略所有异常,然后直接shift+F9让程序运行起来,然后ALt+L来到记录窗口查看经过些什么异常:
71A10000 模块 C:\windows\system32\WS2HELP.dll
00AFA8ED 访问违规: 正在写入到 [00000000]
00AF9ED6 访问违规: 正在写入到 [00000000]
00AFA0E7 访问违规: 正在写入到 [00000000]
00AFA225 访问违规: 正在写入到 [00000000]
00AF7E76 访问违规: 正在写入到 [00000000]
00AF7FCB 访问违规: 正在写入到 [00000000]
00AF8145 访问违规: 正在写入到 [00000000]
00AF8221 访问违规: 正在写入到 [00000000]
00AF8392 访问违规: 正在写入到 [00000000]
00AFA3A9 访问违规: 正在写入到 [00000000]
00AFA490 访问违规: 正在写入到 [00000000]
00AFA5FD 访问违规: 正在写入到 [00000000]
00AF6AFE INT3 命令位于00AF6AFE
00AF7884 访问违规: 正在写入到 [00000000]
00AF7A7B 访问违规: 正在写入到 [00000000]
00AF871A 访问违规: 正在写入到 [00000000]
00AF8901 访问违规: 正在写入到 [00000000]
00AF985E 访问违规: 正在写入到 [00000000]
00AF8C31 访问违规: 正在写入到 [00000000]
00AF8E9A 访问违规: 正在写入到 [00000000]
00AF9101 访问违规: 正在写入到 [00000000]
00AF92FA 访问违规: 正在写入到 [00000000]
00AF9407 访问违规: 正在写入到 [00000000]
00AF9554 访问违规: 正在写入到 [00000000]
00AFB3B0 访问违规: 正在写入到 [00000000]
00AFB4AC 访问违规: 正在写入到 [00000000]
00AFB833 访问违规: 正在写入到 [00000000]
00AFBAB5 访问违规: 正在写入到 [00000000]
00AFBD03 访问违规: 正在写入到 [00000000]
00AFBED0 访问违规: 正在写入到 [00000000]
00AF6AFE INT3 命令位于00AF6AFE
5ADC0000 模块 C:\windows\system32\uxtheme.dll
00AF8575 访问违规: 正在写入到 [00000000]
00AF9BF7 访问违规: 正在写入到 [00000000]
10000000 模块 E:\aspr\5\BetterJPEG\BJPEG07.dll
73D30000 模块 C:\windows\system32\MFC42.DLL
75FF0000 模块 C:\windows\system32\MSVCP60.dll
61BE0000 模块 C:\windows\system32\MFC42LOC.DLL
74680000 模块 C:\windows\system32\MSCTF.dll
7C810659 ID 00000ACC 的新线程已创建
7C810659 ID 00000AD0 的新线程已创建
线程 00000ACC 已终止, 退出代码 0
线程 00000AD0 已终止, 退出代码 0
我的是2个int3异常和20次左右的内存访问异常,那么我们就可以利用这些异常找到OEP,这里当然选int3
异常了(只有2次,比你shift+f9 20次快多了),好了,重新载入od,异常设置里int3选不忽略,然后shift+f9
两次,再在代码段下访问断点,shift+f9就来到真正的oep处了:
00431EB8 55 push ebp //oep
00431EB9 8BEC mov ebp, esp
00431EBB 6A FF push -1
00431EBD 68 F08D4600 push 00468DF0
00431EC2 68 04734300 push 00437304
00431EC7 64:A1 00000000 mov eax, dword ptr fs:[0]
00431ECD 50 push eax
00431ECE 64:8925 0000000>mov dword ptr fs:[0], esp
00431ED5 83EC 58 sub esp, 58
00431ED8 53 push ebx
00431ED9 56 push esi
00431EDA 57 push edi
00431EDB 8965 E8 mov dword ptr [ebp-18], esp
00431EDE FF15 88134600 call dword ptr [461388] ; kernel32.GetVersion
00431EE4 33D2 xor edx, edx
00431EE6 8AD4 mov dl, ah
00431EE8 8915 84B34700 mov dword ptr [47B384], edx
可以看到oep处没有被偷,那再来看看输入表,数据窗口中跟随00431ede处中ds段的地址,然后上下翻看一下,
发现输入表也没有被加密:
00461000 77DA6BF0 ADVAPI32.RegCloseKey //IAT-start
00461004 77DA761B ADVAPI32.RegOpenKeyExA
00461008 77DA7883 ADVAPI32.RegQueryValueExA
0046100C 77DAEBE7 ADVAPI32.RegSetValueExA
00461010 77DAEDE5 ADVAPI32.RegDeleteValueA
00461014 77DCC123 ADVAPI32.RegDeleteKeyA
00461018 77DAEAF4 ADVAPI32.RegCreateKeyExA
0046101C 00000000
00461020 77195140 COMCTL32.ImageList_GetImageCount
00461024 77195323 COMCTL32.ImageList_Replace
00461028 77195650 COMCTL32.ImageList_GetIconSize
0046102C 77195572 COMCTL32.ImageList_Remove
(一阵窃喜,这么容易的壳),那我们dump吧,呵呵,要是现在dump就上当了,我们在oep处上下看一下,你就
不会说简单了:
00431F2E E8 B2000000 call 00431FE5
00431F33 59 pop ecx
00431F34 33F6 xor esi, esi
00431F36 8975 FC mov dword ptr [ebp-4], esi
00431F39 E8 1E6C0000 call 00438B5C
00431F3E E8 BDE09100 call 00D50000 //到壳里去了
00431F43 51 push ecx
00431F44 A3 E8CA4700 mov dword ptr [47CAE8], eax
00431F49 E8 DC6A0000 call 00438A2A
00431F4E A3 B8B34700 mov dword ptr [47B3B8], eax
00431F53 E8 85680000 call 004387DD
00431F58 E8 C7670000 call 00438724
00431F5D E8 33FBFFFF call 00431A95
00431F62 8975 D0 mov dword ptr [ebp-30], esi
00431F65 8D45 A4 lea eax, dword ptr [ebp-5C]
00431F68 50 push eax
00431F69 E8 92E09100 call 00D50000 //这里也是
00431F6E 19E8 sbb eax, ebp
还有n多处,ctrl+f:call 00d50000,搜索一下,搜出来的数量很惊人,200多处,函数调用的高级保护......
2、修复call xxxxxxxx
当然我们可以在00D50000处下短点,然后跟出它每次调用什么函数,但数量太大,我们就想到了patch,前段日子学了
一下脚本,干脆就自己写了个脚本来修复call xxxxxxxx:
*****************************************************************
var tmp1
var tmp2
var vmadd
var oepbase
dbh
gpa "GetSystemTime","kernel32.dll"
mov tmp1,$RESULT
cmp tmp1,0
je erro
bp tmp1
esto
bc tmp1
rtu
mov tmp1,eip
gmemi tmp1,MEMORYBASE
mov tmp1,$RESULT
cmp tmp1,0
je erro
mov vmadd,tmp1
gpa "GetVersion","kernel32.dll"
mov tmp1,$RESULT
cmp tmp1,0
je erro
bp tmp1
esto
bc tmp1
rtu
mov tmp1,eip
gmemi tmp1,MEMORYBASE
mov oepbase,$RESULT
cmp oepbase,0
je erro
mov tmp2,00460b00
mov [tmp2],#BA00104000803AE875128B420103C283C0053D0000D500750360FFE24281FA00#
add tmp2,20
mov [tmp2],#10460076E0EB36B9001046008B55F43911751D890D0008460081C43C02000061#
add tmp2,20
mov [tmp2],#8B1D00084600895A0266C702FF15EBCC83C10481F9AC1646007CD4EB009090#
mov tmp1,vmadd
find tmp1,#68A443B0008D85C4FEFFFF#
mov tmp2,$RESULT
cmp tmp2,0
je erro
asm tmp2,"push 00460b27"
mov eip,00460b00
bp 00460b5d
run
cmp eip,00460b5d
jne erro
bc eip
fill 00460b00,70,00
mov [tmp2],#68A443B000#
find oepbase,#558BEC6AFF68F08D46006804734300#
mov tmp1,$RESULT
cmp tmp1,0
je erro
mov eip, tmp1
jmp end
erro:
msg "error!"
end:
msg "now,you can dump!"
ret
******************************************************************************
讲一下脚本的处理思路:从代码段的开头到结尾搜索call 00d50000这条指令,找到后直接跳去壳代码里去执行,
不过得到函数的地址我们用脚本写回代码段,而不是壳里了,具体代码我也不多说了..........
用od重新加载目标程序,忽略所有异常,然后直接运行脚本,完成后我们就可以dump了,然后再建一下输入表,
注意有一个输入表指针ImportREC不识别,用它的反汇编可以看一下它的代码,就是GetProcAddress
3、补区段
这时运行dump.exe会提示"xxxxxxxx地址的内存不可读.......",我们跟踪一下原程序可发现:
00432348 68 0000D900 push 0D90000
0043234D C3 retn //变形的jmp
0043234E 9E sahf
0043234F 03CB add ecx, ebx
00432351 3AF8 cmp bh, al
jmp的变形,又到壳代码去了,还有多处,我不帖出代码了,这些其实是solen code,代码少的话,可以手工还原,
但这个壳的stolen code是不少的,那就只有用补区段的方法了,现在问题有来了,你怎么知道哪些区段要补,我
的思路是这样的,先让原程序走到oep,这时代码已基本解压完毕,然后在壳在内存中申请的所有区段下访问断点,
然后一直shift+F9,直到程序启动,这时直接ALT+M到内存窗口看用了哪些段,再区域转存下来,(当然也可以在下
好所有的访问断点后,每次shift+f9中断的时候记下访问了哪个段):
地址 大小 类型
00990000 00008000 (32768.) Priv 00021004
00A90000 00003000 (12288.) Priv 00021004
.
.
.
00DE0000 00001000 (4096.) Priv 00021040
总共下断30多个,shift+f9完后,我数了下,用了22个段,记录下来,然后就是体力活了,用 lordpe区域转存把
那22个段全dump下来,然后再用lordpe的pe编辑功能,把刚才那些段全部添加到dump.exe文件的尾部,(注意要按地址
的先后顺序添加,要不到时就会出错了),添加完后还有很重要的一步就是修改刚才添加段的Voffset:
如:00990000--> 改为:00990000-00400000=00590000
00DE0000--> 改为:00DE0000-00400000=009E0000
(我脱这个壳时就是因为这里没弄对,还以为自己没脱成功,弄得我郁闷了好几天..........)
修改完Voffset后,保存再重建一下pe,(完了没有,我看都看累了),还有最后一步
4、去掉壳代码校验(Route CHECK)
Route check:代码完整性校验,不修改它的话脱掉的程序是运行不起的,怎么找了,用特征码吧:
MOV EAX,[EAX+34]
CALL EAX
SUB [EBP+C],EAX
MOV EAX,[EBP+C]
二进制搜索:8B 40 34 FF D0 29 45 0C 8B 45 0C
地址 大小
我的是在:00AE0000 00030000 段中
找到后把上面几句改为:
MOV EAX,[EAX+34] => NOP
CALL EAX => NOP
SUB [EBP+C],EAX => MOV EAX,[ESP+58]
MOV EAX,[EBP+C] => SUB EAX,5
(具体的原因请参考syscom的帖子,我再写上来就显得重复了)
再复制到可执行文件
运行一下,正常,打完收工了.........
【尾声】:
打字速度慢,弄完这篇文章,用了3个小时左右,发这篇帖子,为自己在脱壳学习的历程上留个记恋吧
因为脱壳的学习可能要告一段落了,没时间啊..........,不过论坛还是会经常来的,随时掌握动态嘛 .......
脱壳过程中参考了syscom的帖子,Volx的脚本,发现他的脚本只补了一个区段,(大部分stolen code都被他的脚本自己
修复了),实在是强 .........
最后祝各位:happy everyday!
感谢观看!!!
----------------------------------------------------------------------
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
上传的附件: