脱壳(PEspin 0.3x)
脱壳总结:
① 找OEP 难点在于硬件断点有时会失效,解决方法通过特征定位
② 找到清除硬件断点的异常函数 配置异常环境(尽可能接收所有的异常),通过在异常点设置硬件断点,来判断前一个异常函 数是否有清除代码,可以最终判断出第三个异常(STI 特权指令异常)的异常函数有清除代 码。
③ 解密IAT 按照通用方法,在OEP处的第一个API函数所在的IAT设置硬件写入断点,展开分析。 首先会找到填充IAT的点,然后再通过跟踪找到获取API的地方,之后写脚本(脚本中需要加 一个分支,nop掉硬件断点清除代码)
④ 修复代码段,重建IAT 使用通用导入表修复工具,重建IAT
⑤ dump、修复IAT
开始脱壳
(1)用PEID查看程序信息
只知道这个PESpin壳,是什么编译器写的也不知道,不过这是一个GUI窗口程序,区段都被改名为15pb了,文件也只有23k的大小,小巧精湛,推测是汇编写的。
(2)找到OEP
常见API下断点,当断下之后看其返回地址是否是来自主模块,是的话再去调用上一层
GetVersion
GetModuleHandleA
GetSystemTimeOfFileTime
我首先用OD在GetVersionAPI下断点没用,然后测试GetModuleHandleA 就成功,找到OEP界面如下:
值得注意的是 下面的一串jmp代码,查看机器码为 FF25
xxx。这说明这是调用IAT地址的代码。(FF15、FF25)
点击FF25的代码,右键数据窗口跟随,在内存地址上下一个硬件写入断点,重新运行起来后发现没有断下来,说明硬件断点被壳干掉了。
(3)分析OEP处的IAT
在OEP处发现IAT加密了,接下来应该解密IAT,常用方法
① 在加密的IAT处下访问断点,重新调试,在断点断下附近分析
② 在壳获取IAT的过程中的API下断点
GetProcAddress,
GetModuleHandleA/W,
LoadLibraryA/W 使用第一种方法,
断点未断下,猜测硬件断点被反。
反硬件断点的方法:
①异常回调中可以将调试寄存器清0
②SetThreadContext以及这个函数的下层函数
③SetUnhandledExceptionFilter 设置顶层异常函数
函数下断未断下,说明是异常回调起的作用
(4)配置OD找到清除硬件断点的地方
1、选项->调试设置->异常 ,设置如下
2、插件->StrongOD->Options
3、接下来就是重新调试,按F9和Shift+F9(跳过异常) 先查看大概又多少个异常断点,经过我的测试,有八、九个异常。
发出前四个异常的图,第3和第4个异常为同一段代码。因为经过测试,清除硬件断点的地方是第三个异常的回调函数清除了硬件断点。
需要排查找出对硬件断点清0的地方 在异常点设置硬件断点,看能不能断下能不能断下,能断下说明当前异常点前面的异常处理 函数中没有对硬件断点清0
4、每次在一个异常处下硬件断点后,查看OD左下角提示,查看是因为异常断下的,还是因为硬件断点断下的。如果是硬件断点先断下,说明此异常之前,硬件断点没有被清除。
5、经过测试,发现在第四个异常处,也就是STI指令的时候,硬件断点就没有断下,显示的是指令异常,说明在第四个异常处之前,硬件断点就已经被干掉了。也就是说,被第三个异常的回调函数干掉了。
6、找到重新走到第三个异常指令STI处,点击OD 查看->SEH链,双击地址进入异常回调函数
5、OD动态分析,或者dump内存用IDA分析,可以知道这里就是清0硬件断点的地方。
(5)找到操作IAT地址的两个地方
为了方便调试,在第三个异常处下一个断点,然后把OD的环境恢复默认的,每次来到第三个异常处,进入异常回调,把407F57之下的清0操作都NOP掉(Del键)。同时要先进入OEP,在FF25的代码地址里下硬件写入断点,先找到写入IAT的地方,随后单步跟踪一会儿(头一次跟踪可能要十多二十分钟,熟悉后一分钟不到),熟悉壳的花指令代码后就能找到获取IAT的地方。
第一此按F9要按两次,来到一个为jmp xxx的地方,因为有花指令,要按Ctrl+UP 键才能看到写入IAT 的地方,记录地址
注:红色的代码,说明这部分代码是被动态解密出来的。
(6)找到操作IAT的地方,后就写OD脚本,要注意的是,脚本里面要把清0硬件断点的地方干掉。
// 1.定义变量
var dwClearBreak // 清除硬件断点的地方
var dwGetIatAddr // 得到IAT地址的偏移
var dwWriteIatAddr // 写入IAT地址的偏移
var dwOEP // 入口点
var dwTemp // 临时变量
mov dwClearBreak, 407F57 // 清除硬件断点的地方
mov dwGetIatAddr, 405F9F // 得到IAT地址的偏移
mov dwWriteIatAddr, 40618C // 写入IAT地址的偏移
mov dwOEP, 401120 // 入口点
// 2.设置断点
bc // 清除所有软件断点
bphwc // 清除所有硬件断点
bpmc // 清除所有内存断点
bphws dwGetIatAddr, "x" // 设置硬件执行断点
bphws dwWriteIatAddr, "x"
bphws dwClearBreak, "x"
bphws dwOEP, "x"
// 3. 构造循环
run
cmp dwClearBreak, eip
fill dwClearBreak, 1E, 90 // 将清除硬件断点的地方NOP
_case1:
run
cmp dwGetIatAddr, eip
jnz _case2
mov dwTemp, eax // 获取IAT地址
jmp _case1
_case2:
cmp dwWriteIatAddr, eip
jnz _case3
mov [edi], dwTemp // 填入IAT
jmp _case1
_case3:
cmp dwOEP, eip
jnz _case1
MSG "到达OEP!"
(7)修复IAT
脚本跑完来到OEP后,重新找到IAT的地址看看,发现不对劲,每个地址之间被00分隔开了。这时候dump内存并没有达到脱壳的目的。
Ctrl+M转到内存窗口,打开通用输入表修复工具(UIF),把数据填好,修复IAT,然后dump内存,脱壳完毕。
(8)程序地址
链接:https://pan.baidu.com/s/18q4r6st7ciOundzUqBqc4A
提取码:h6yj
解压码:1234 (大多杀软会报毒)