脱壳
我们知道压缩壳实现原理了,所以大概知道怎么拖了,只要我们找到解压后的代码入口点,在把他dump下来基本上就成功一半了,因为dump下来的IAT此时存放的是函数地址,而不是函数名的RVA,而且你也不知道对方有没有抹掉导入表,所以需要修复导入表
脱壳步骤大致如下
-
查找OEP OEP的识别:经验、案例收集
1.1 VC编译程序的OEP:API断点QueryPerformanceCounter,栈回溯往上找
1.2 VS2003-VS2015编译程序的OEP:call+jmp 通用方法: ESP定律
ESP定律 :伪入口点找到程序返回的位置,也就是自己写压缩壳的时候,需要保存寄存器环境,等回到入口点执行时就要pop寄存器 这时候在push寄存器下一个硬件访问断点就可以找到了
1.3 API:收集不同编译器编译后的真实入口点,找到必来的API,然后下API断点往上查找
单步跟踪:单步跟踪(步过循环,只向下跳转),遇到一个跨节的大JMP一般就是真正的OEP -
dmp文件
注意:这里一定要在入口点再dup文件,因为有些全局变量一开始是未初始化状态,如果程序已经正在运行,那么这时候dmp全局变量的值可能是错的
3.修复PE:修复对应导入表
脱壳案例
用exeinfope.exe查看一下是什么壳,发现是upx
用x32db加载程序
看到pushad,可以用ESP定律查看试一下,入栈后下个硬件访问断点,f9运行
看到此时已经,pop了,接下来可以判断应该是进入入口点了,继续F8 jmp过去,如下图,入口点位置如下
此时dump下来,运行exe失败,这个时候就要自己查看一下原因了,把dump下来的加载进x32dbg查看一下, 如下图
看到这个很像IAT的位置中的地址无效,查看一下原PE这个地址是什么,如下图
发现运行完这个函数之后ret就是api函数入口地址,这就相当于IAT填的是混淆代码,反正最终是会运行到api入口地址去的,所以多运行api试一下,找找规律,把IAT的api入口地址填进行,我这个规律就是ret以后就是api入口地址,用python写一个脚本去修复IAT,下载python2.7版本设置环境变量,然后把pythonSDk放到x32dbg插件的目录下,重新启动x32dbg
python脚代码如下
#coding:utf-8
import x64dbgpy.pluginsdk._scriptapi.debug
from x64dbgpy.pluginsdk._scriptapi import *
SetHardwareBreakpoint(0x0047148b)
Run()
dwAddr = 0x00475000
dwCountOfApis = 0x48
for i in range(0, dwCountOfApis):
# 获取IAT表项
dwIATAddr = dwAddr + i*4
# 读取堆地址
dwHeapAddr = ReadDword(dwIATAddr)
# 判断是否是0项
if(dwHeapAddr == 0):
continue
# 单步执行代码到ret
SetEIP(dwHeapAddr)
while True:
StepIn()
byte = ReadByte(GetEIP())
if(byte == 0xC3):
StepIn()
break
#eip为真正的API的地址,保存到IAT表
WriteDword(dwIATAddr, GetEIP())
然后执行代码,此时IAT填的都是真正的地址,这时候用工具ImportREC把导入表(IAT)给保存下来,记得工具要管理员权限运行,不然会找不到PE文件,填好OEP和IAT的首地址和大小,获取一下看看是否有效,有效的话直接点击成load tree,因为有些是转发函数(也就是函数实现在另一个dll里面),所以会无效,这个也必须修复一下,填真实的dll和函数名就修复了,如下图
最后把dump下来的文件用ImportREC修复导入表,点击加载load tree----勾选上add new section----点击fix dump,此时会生成一个新的修复导入表的新PE 如下图