吾爱汇编

 找回密码
 立即注册

QQ登录

绑定QQ避免忘记帐号

查看: 2106|回复: 9

[转载技术] 第五十五章-EXECryptor v2.2.50.a脱壳-Part2

[复制链接]
Shark恒 发表于 2015-1-20 17:38 | 显示全部楼层 |阅读模式

                        第五十五章-EXECryptor v2.2.50.a脱壳-Part2
本章我们将编写一个脚本来修复ExeCryptor的IAT。可能很多童鞋一听到脚本这个词就有一种头皮发麻的感觉。(嘿嘿),因为一般来说人家编写好的脚本通常都比较长,看起来非常复杂的样子,当然会感觉到头皮发麻撒。但是我这里换一种方法,我不是直接拿一个现成的脚本给大家,而是带着大家从零开始编写这个脚本,逐步添砖加瓦,这样大家接受起来就容易的多。
我的想法很简单,就是遍历IAT,看哪一个IAT项被重定向了,如果被重定向了的话,就修复之,我们首先来构建这个脚本的基本框架。
首先我们要定义一个变量,用它来作为IAT项的指针。
var table
首先我们将IAT的起始地址保存到该table变量中
mov table,460818
这里我们就将table这个变量初始化完毕了,接下来我们需要遍历整个IAT,判断其中哪些项被重定向了,如果被重定向了,就修复之。如果没有被重定向,就继续遍历下一项。
脚本的基本框架就是这样的:
start:
cmp [table],50000000
image001.jpg ja ToSkip
log table
end
ToSkip:
add table,4
jmp start
这是一个基本框架-遍历IAT中的每一项,依次判断IAT项中的值是否大于50000000(一般来说大于50000000的话,就属于是DLL中的地址,即为正常的API函数地址,没有被重定向)如果大于50000000的话,就直接跳过该项,继续遍历下一项。
如果是小于等于50000000的话,说明是被重定向过的,则进行下面的操作:
log table
end
这里的话我们就需要对重定向的项进行修复了,要对其修复的话,首先我们要知道其实际要调用的API函数是什么,这里为了简单起见,我们只测试第一个待修复的项,所以在定位到实际要调用的API函数以后,直接退出循环。
image003.jpg
我们首先断到OEP处(如何断在OEP处,上一章已经给大家介绍过了),然后执行该脚本看看效果。
image005.jpg
image007.jpg
我们可以看到提示第7行有一个不识别的end命令,呵呵,写错了,应该是ret命令才对,我们将它改过来。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
var table
   mov table,460818
start:
   cmp [table],50000000
   ja ToSkip
   log table
   ret
ToSkip:
   add table,4
   jmp start
这里我们已经改过来了,再次执行该脚本看看效果。
image009.jpg
我们可以看到这次没有报错,并且第一个重定向的IAT项的地址成功被记录到日志中了。
image011.jpg
现在我们需要获取重定向的IAT项的值了,所以这里我们再定义content变量用于临时保存重定向的IAT项的值。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
var table
var content
mov table,460818
start:
cmp [table],50000000
      ja ToSkip:
      log table
      mov content,[table]
      log content
      ret
ToSkip:
      add table,4
      jmp start
这里添加了content这个变量以后,我们就可以把重定向过的IAT项的地址以及值都记录到日志中了。
image013.jpg
我们可以看到执行完该脚本以后,日志中记录了460818这个IAT项中的值为47FCA8,接下来我们需要将ret命令去掉,循环遍历所有项。不知道大家知不知道其实ODbgScript这个插件是可以对脚本进行单步跟踪的,以便脚本出错的时候方便我们调试。我们来看一看这个功能如何使用:
image015.jpg
我们选中菜单项Plugins-ODbgScript-Script Window,打开脚本跟踪窗口。
image017.jpg
这里我们可以看到单步跟踪的快捷键为S,给指定行设置断点的快捷键为F2,有了这两个命令就足够我们对脚本进行跟踪排错了。
下面我们按S键单步跟踪看看效果如何。
image019.jpg
这里我们可以看到EIP这一列会显示当前行EIP的值为多少,Values <---这一列的话,如果当前行有取值操作的话,就会以 值《内存地址 的方式显示出来,例如:这里显示的是47FCAB << 460818。我们也可以通过给指定行设置断点,然后单击Resume菜单项来执行多条命令,然后停在断点处。好了,现在我们关闭这个脚本窗口,继续给我们的脚本添加新的内容。
现在我们需要给它添加一个条件,当满足该条件时就停止执行该脚本并退出,这里该条件应该是遍历完整个IAT,即超出了IAT的范围,table指针大于460F28时就退出。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
var table
var content
   mov table,460818
start:
   cmp table,460F28
   ja final
   cmp [table],50000000
   ja ToSkip
   log table
   mov content,[table]
   log content
   ret
ToSkip:
   add table,4
   jmp start
final:
   ret
这里判断的条件我们就添加好了,当遍历到IAT尾部的时候,我们就跳转到final标签处,结束脚本的执行。
下面我们就可以来修复重定向的IAT项了,前面我们在定位到第一个重定向的IAT项时,是将其地址和值都记录到日志中,然后ret退出脚本,这里我们将这个ret命令去掉。
var table
var content
   mov table,460818
start:
   cmp table,460F28
   ja final
   cmp [table],50000000
   ja ToSkip
   log table
   mov content,[table]
   log content
ToSkip:
   add table,4
   jmp start
final:
   ret
这里我们就将log content后面ret命令去掉了,这样处理之后,在记录完重定向IAT项的地址以及值以后,会到达ToSkip标签处继续遍历后面的IAT项,重复上面的过程,直到遍历完整个IAT为止,我们来看看该脚本执行的效果。
image009.jpg
我们来看看日志信息。
image021.jpg
这里我们会发现有一些IAT项的值为零,我们知道,这些零是分隔符,并没有实质性的作用,所以我们还需要添加相应的命令跳过这些分隔符。
var table
var content
   mov table,460818
start:
   cmp table,460F28
   ja final
   cmp [table],50000000
   ja ToSkip
   mov content,[table]
   cmp content,0
   je ToSkip
   log content
   log table
ToSkip:
   add table,4
   jmp start
final:
   ret
这里我们添加了两条命令跳过值为零的IAT项,我们再次执行该脚本看看效果。
image023.jpg
我们可以看到所有的待修复的IAT项的地址以及内容都被打印出来了,下面我们来尝试修复这些项。
接下来我们要做的就是定位到重定向的IAT项实际要调用的API,我们可以利用内存写入断点来定位。当断下来的时候我们将API函数的地址填充到对应的IAT项中,接下来我们不让其去调用实际要调用的API函数,而是让其继续遍历IAT中的下一项。
脚本中设置内存写入断点用到的是BPWM这个命令,具体用法如下:
BPWM 地址,大小
在指定地址处,设置一个内存写入断点。”大小”是指内存中的字节大小。
例子:
BPWM 401000,FF
通过这个命令我们就可以对指定内存单元设置内存写入断点了。
mov eip,content
bpwm table,4
这里我们首先将EIP指向重定向后函数的入口点,接着对待修复的IAT项设置内存写入断点。
接下来通过COB命令继续往下执行。
COB命令的具体用法如下:
COB
---
发生中断后,让脚本继续执行(移除EOB指令)
例子:
COB
mov eip,content
bpwm table,4
cob ToRepair
run
ToRepair:
ret
这段脚本的意思就是说对待修复的IAT项设置内存写入断点,接着运行起来,当发生中断的时候就跳转ToRepair标签处。
这里ToRepair标签处,我只添加了一个ret命令,只是为了测试是否能成功断在第一个待修复的IAT项被写入的地方。
image025.jpg
我们可以看到执行完该脚本以后,正好断在了向第一个待修复的IAT项写入API函数地址的指令处,下面我们可以通过STI命令单步执行这一行,看看第一个IAT项被修复后的效果。
STL命令的用法如下:
STI
---
相当于在OD中按F7,单步步入
例子:
sti
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
mov eip,content
bpwm table,4
cob ToRepair
run
ToRepair
sti
ret
好,我们执行该修改后的脚本,执行完毕以后,可以看到第一个重定向的IAT项的值成功被修复了。
image027.jpg
我们可以看到第一个待修复的IAT项已经被修复了,下面我们要做的就是继续定位下一个待修复的IAT项,并且确保不执行当前IAT项实际要调用的API函数,以免出错。
image029.jpg
这里我们可以看到在脚本执行的过程中,产生了异常,出错了。我们在编写脚本的过程中应该充分考虑到这一点-哪些地方会产生异常,我们需要检查当前是否在处于我们预想的位置,这里我们不修改任何东西,直接用ODbgScript插件调试看看哪里出错了。
image031.jpg
我们在第19行处按F2键设置一个断点,执行这个sti命令以后,第一个待修复的IAT项就会被修复,我们开始执行该脚本看看效果。
image033.jpg
我们单击鼠标右键选择Resume,恢复脚本的执行,就可以看到断在sti这条命令处了。
image035.jpg
虽然在调试脚本过程中我们可以精确看到EIP的值,但是在脚本中我们还是要对其进行检查。
image037.jpg
我们按S键执行该sti命令的话,第一个待修复的IAT项就会被修复。
image039.jpg
按道理来说,接下来将继续修复第二个待修复的IAT项,我们按S键继续跟踪,看看会发生什么。
image041.jpg
这里我们可以看到table这个变量(该变量实际上是IAT的指针)已经指向IAT中的下一项了,我们继续跟踪。
image043.jpg
接下来是修复第二个待修复的IAT项。
image045.jpg
我们跟踪到了这里,这里将对第二个待修复的IAT项设置内存写入断点,我们继续跟踪,我们可以看到当我们执行run命令的时候出错了。
image047.jpg
我们来看一看是哪里导致的错误。
image049.jpg
这里我们再次跟踪到了第17行的run命令处,我们单击鼠标右键选择Abort菜单项,终止脚本的执行,下面我们在OD中继续往下跟踪,看看是哪里导致的错误。
image051.jpg
这里我们处于第二个重定向IAT项所指向函数的入口处,我们继续跟踪。
image053.jpg
这里是将栈顶指针指向的内容与EAX的内容进行交换。
image055.jpg
这里是将480094这个内存单元的内容保存到EAX中,此时480094这个内存单元中内容为零,也就是说这条指令其实就是将EAX置零。
这里好像看不出什么端倪来。
既然是脚本在修复第二个IAT项的时候抛出的异常,那我们就来看看第二个IAT的项的调用处吧。
image057.jpg
这里我们通过单击鼠标右键选择New origin here将EIP指向该CALL处,接着手动给该IAT项设置一个内存写入断点,然后给返回地址处设置一个BP断点,执行以后我们会发现第二个IAT项的值并没有被修复,这也就是为什么会抛出异常的原因了。也就说我们的脚本逻辑上存在问题,第一个IAT项触发内存写入断点时断在了写入正确的API函数地址的指令处,但是第二项却没有。
好,这里我想到一个解决办法,虽然不是很正规,但是确实能解决这个问题。
我们对第一个待修复的IAT项即460818这一项设置内存写入断点,接着从第一个重定向后的函数入口处即47FCA8处开始跟踪(利用OD的自动跟踪功能跟踪),OD自动跟踪大约要花5分钟左右的时间。部分跟踪指令序列如下:
00483C91 Main MOV AL,1 ; EAX=77DA6C01
00483C93 Main JMP 00483C78
00483C78 Main MOV BYTE PTR SS:[EBP-5],AL
00483C7B Main MOV AL,BYTE PTR SS:[EBP-5]
00483C7E Main POP ECX ; ECX=01000001, ESP=0012FE6C
00483C7F Main POP ECX ; ECX=77DA6C75, ESP=0012FE70
00483C80 Main POP EBP ; ESP=0012FE74, EBP=0012FE80
00483C81 Main RETN ; ESP=0012FE78
004833D5 Main TEST AL,AL ; FL=0
004833D7 Main JNZ 0047CC50
0047CC50 Main POP ECX ; ECX=00000001, ESP=0012FE7C
0047CC51 Main POP ECX ; ECX=77DA6C75, ESP=0012FE80
0047CC52 Main POP EBP ; ESP=0012FE84, EBP=0012FFB0
0047CC53 Main RETN ; ESP=0012FE88
0047691C Main MOV EAX,DWORD PTR SS:[EBP-C] ; EAX=77DA6BF0
0047691F Main MOV ESP,EBP ; ESP=0012FFB0
00476921 Main JMP 004765DC
004765DC Main JMP 0047F15C
0047F15C Main PUSH 47C9B5 ; ESP=0012FFAC
0047F161 Main JMP 00491A5F
00491A5F Main JMP 004737D4
004737D4 Main RETN ; ESP=0012FFB0
0047C9B5 Main POP EBP ; ESP=0012FFB4, EBP=0012FFF0
0047C9B6 Main RETN ; ESP=0012FFB8
0046E81D Main RETN ; ESP=0012FFBC
Memory breakpoint when writing to [00460818]
以上是触发460818处的内存写入断点之前执行的部分指令,我们需要在上面这些指令中找一条所有待修复的IAT项都会执行的指令。
0047691C Main MOV EAX,DWORD PTR SS:[EBP-C] ; EAX=77DA6BF0
就选着47691C这条指令吧,当将要执行这个指令的时候,此时EAX中正好保存正确的API函数地址,我们对这条指令设置一个硬件执行断点,看看遍历到第二个IAT项时,断在这里是什么情况。
这里我们可以看到遍历到第二个IAT项时,EAX中保存的的确是正确的API函数地址,我们继续跟踪。
image059.jpg
我们继续往下跟踪,会发现从46E81D地址处开始各个IAT项的执行流程就不同了。
0046E81D C3 RETN
第一个IAT项将跳转到修复IAT项的指令处,而第二个IAT项将跳到这里。
image061.jpg
好了,不管怎么说,我们这里还是可以利用所有IAT项都会执行的这一条指令-即47691C这条指令。
image063.jpg
这里正确的IAT函数地址将被保存到EAX中,所以下面我们来修改脚本,让其断在47691C这条指令处,执行完这条指令后,EAX就保存了正确的API函数地址,接着我们将其填充到对应的IAT项中。
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
var table
var content
mov table,460818
start:
cmp table,460F28
ja final
cmp [table],50000000
ja ToSkip
mov content,[table]
cmp content,0
je ToSkip
log content
log table
mov eip,content
bphws 47691F,”x”
cob ToRepair
ToRepair:
      log eax
mov [table],eax
ToSkip:
      add table,4
      jmp start
final:
      ret
这里我们给47691F这一行设置一个硬件执行断点,此时EAX中已经保存了正确的API函数地址了,接着我们将其填充到对应的IAT项中,循环往复直到遍历完整个IAT为止,我们来执行该脚本看看效果。
image065.jpg
这里我们可以看到修复了一会儿后,就报错了。确切的说是在尝试修复460988这一项的时候发生异常了。
image067.jpg
看来我们的脚本逻辑上还是有问题,我们重启OD。
再次定位到OEP处,接着定位到460988这一个IAT项,看看它有没有参考引用处。
image069.jpg
我们跟到这个函数里面看看。
image071.jpg
我们在47691F处设置一个硬件执行断点看看,运行起来,看看会发生什么。
image073.jpg
这里我们可以看到此时EAX中保存的依然是正确的API函数地址,那么刚刚我们脚本修复IAT的过程中报错有可能是因为该壳会检测是不是在同一个时间段内IAT中的多项同时被修复,如果是的话就报错。
好,那么现在我们将table指针初始化为460988,然后执行脚本,看看还会不会报错。
var table
var content
mov table,460988
start:
cmp table,460F28
ja final
cmp [table],50000000
ja ToSkip
mov content,[table]
cmp content,0
je ToSkip
log content
log table
mov eip,content
bphws 47691F,”x”
cob ToRepair
ToRepair:
      log eax
mov [table],eax
ToSkip:
      add table,4
      jmp start
final:
      ret
我们可以清楚的看到发生了什么。
image075.jpg
我们可以看到大约修复了5到6个IAT项后又报错了,嘿嘿,说明该壳会检测修复5到6个左右IAT项所用的时间,如果修复IAT项的时间过于连续的话,就会抛出异常。
导致我们无法继续修复,下面大家来看看我的解决方案,嘿嘿。
我的做法是监视ZwTerminateProcess(调用了该函数程序就直接退出了)这个函数,我们要做的就是当将要执行该函数的时候,我们让其继续执行脚本而不是直接退出程序。
ToRepair:
cmp eip,7C91E88E
je ToSkip
log eax
mov [table],eax
run
我的机器上ZwTerminateProcess这个API函数的地址为7C91E88E(不同的机器上这个地址可能会不同,大家根据自己机器的实际情况来决定)。当脚本执行的过程中触发了异常的话,我们判断该程序是不是在尝试调用ZwTerminateProcess这个函数来结束进程,如果是的话,我们就直接跳到ToSkip标签处去遍历下一个IAT项。
还有一种情况我们需要考虑就是:在修复了一个IAT项之后,继续执行后面的代码过程中发生了异常怎么办?即执行完了47691C这条指令之后,接着在执行后面的代码的过程中发生了异常,进而转入ZwTerminateProcess。按照现在这个逻辑就是跳转到ZwTerminateProcess处,好,跳转ZwTerminateProcess处这个操作被我们的脚本捕获到了,我们脚本的处理是跳到下一个IAT项处,但是我们当前这个IAT项并没有修复啊,所以说这个逻辑还是有问题的。我们可以这样做,就是我们人为的在47691F地址处造一个异常,让其去调用ZwTerminateProcess这个函数。
经修改后,我们的脚本就变成了这个样子:
start:
cmp table,460F28
ja final
cmp [table],50000000
ja ToSkip
mov content,[table]
cmp content,0
je ToSkip
log content
log table
mov eip,content
bphws 47691F,”x”
mov [47691F],0
mov [476920],0
cob ToRepair
run
ToRepair:
cmp eip,7C91E88E
je ToSkip
log eax
mov [table],eax
run
ToSkip:
add table,4
jmp start
final:
ret
这里我们把忽略内存访问异常这个选项的对勾去掉,执行脚本。
image077.jpg
我们可以看到IAT项都被修复了,下面我来进行dump。
image079.jpg
打开IMP REC。
image081.jpg
现在我们来修复dump文件,然后将TLS Table的指针和大小都设置为零。
image083.jpg
现在入口点就是4271B0了。
image085.jpg
我们运行修复后的dump文件,可以看到完美运行。
好了本章到此结束。
(PS:这里执行了最终的这个脚本,我并没有修复成功,触发了异常以后,我的OD并没有进入ZwTerminateProcess的流程,具体原因有待进一步分析,我个人感觉这个方法不是很通用,如果大家也搞不定的话,可以参考我之前发过的那套国外的脱壳教程全集,里面有ExeCryptor的正规脱壳方案)

本系列文章汉化版转载看雪论坛

感谢原作者:RicardoNarvaja(西班牙人)
原作者个人主页:http://www.ricardonarvaja.info/

感谢热心翻译的朋友:
1~3章译者:BGCoder
4~58章译者:安于此生

全集配套程序下载地址:
链接: http://pan.baidu.com/s/1eQzTWfo 密码: vytv




评分

参与人数 15HB +12 THX +9 收起 理由
Jawon + 2
scdzxzh + 1
Soul1999 + 1
一路走来不容易 + 1
娄胖胖 + 1
消逝的过去 + 1
星宇不笨 + 1 + 1
yexing + 1
MoeRay + 1
成丰羽 + 1 [吾爱汇编论坛52HB.COM]-感谢楼主热心分享,小小评分不成敬意!
bnjzzheng + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
hnymsh + 1
lies + 1
牵手看海 + 2 + 1 ★★★★★ 热心人,佛祖保佑你事事顺利 ,财源滚滚!!!
雨季 + 2 + 1 评分=感恩!简单却充满爱!感谢您的作品!!.

查看全部评分

吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
叶落 发表于 2015-1-20 20:24 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
hackysh 发表于 2022-2-9 17:40 | 显示全部楼层


[快捷回复]-感谢楼主热心分享!
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
头像被屏蔽
别管我了行 发表于 2022-3-3 04:39 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
ghostxu 发表于 2022-3-6 16:00 | 显示全部楼层

欢迎常来帮助新人,谢谢~
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
yexing 发表于 2022-9-26 18:51 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
yexing 发表于 2022-9-26 19:00 | 显示全部楼层

大神厉害啦
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
曾经沧海 发表于 2022-10-31 20:13 | 显示全部楼层

发教程的都是最伟大的,感谢!!!
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
曾经沧海 发表于 2023-4-7 08:04 | 显示全部楼层

感谢分享。正在试用。
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
一生逍遥 发表于 2023-4-22 17:31 | 显示全部楼层

谢谢整理,上传分享!
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

警告:本站严惩灌水回复,尊重自己从尊重他人开始!

1层
2层
3层
4层
5层
6层
7层
8层
9层
10层

免责声明

吾爱汇编(www.52hb.com)所讨论的技术及相关工具仅限用于研究学习,皆在提高软件产品的安全性,严禁用于不良动机。任何个人、团体、组织不得将其用于非法目的,否则,一切后果自行承担。吾爱汇编不承担任何因为技术滥用所产生的连带责任。吾爱汇编内容源于网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除。如有侵权请邮件或微信与我们联系处理。

站长邮箱:SharkHeng@sina.com
站长QQ:1140549900


QQ|RSS|手机版|小黑屋|帮助|吾爱汇编 ( 京公网安备11011502005403号 , 京ICP备20003498号-6 )|网站地图

Powered by Discuz!

吾爱汇编 www.52hb.com

快速回复 返回顶部 返回列表