目录

PE文件实验(2):工具脱压缩壳(UPX)

脱壳流程总结

  1. 查壳:识别是什么壳子
  2. 定位OEP
    1. 堆栈法:因为壳解压后,最终会直接Jmp到原OEP处执行,那么必然要保证执行OEP时的寄存器环境跟执行EP时的一模一样,所以压缩壳的第一行代码往往就是pushad,将所有寄存器压栈
      1. 我们可以搜索程序中popad的机器码,看看哪里有调用,跳过去看,疑似代码的地方下断点
      2. 在开头pushad下断,看看此时操作Esp,下1字节的内存访问断点
    2. 单步跟踪法:这个没什么好说的,在EP处下断,单步走,最后一定会走到Jmp xxx,大跳到一个目标地址处

当然,现代化的壳作者聪明了,不会傻到直接写Jmp xxx让你看出来是跳转OEP

AsPack使用push、ret实现控制流跳转

这里的手法跟构造一个假跳板类似,可以向堆栈中压入一个加密后的Eip、xor堆栈Eip,再ret

腾讯游戏安全竞赛用过一个手法,先向堆栈压入Eip的高2字节(低2字节也行),再手动补齐剩下的2字节

3. API调用定位:使用VS编译的程序,`OEP`往往会调用一些API,获取环境变量啥的,之后再调用`main`,我们可以自己编译一个未加壳程序,看看`OEP`会调哪些API,之后针对加壳程序,在目标函数下断、栈回溯即可
  1. 定位到OEP后,Dump程序
  2. 修复导入表

32位程序脱压缩壳(单步跟踪定位)

  1. 经过以上理论后,我们现在来说实战手脱压缩壳,将PE32_Pack.exe直接拖到x32Dbg中,在入口点处断下

/upxunpacking/Image/1-EP%E5%A4%84%E6%96%AD%E4%B8%8B.png

  1. 此时我们来分析EP处的汇编:
pushad 								// 保存寄存器环境
mov esi,pe32_pack.D1F000     		// 将UPX1节区首地址保存到esi
lea edi,dword ptr ds:[esi-1E000]    // 计算出UPX0节区首地址,保存到edi
push edi                                       
or ebp,FFFFFFFF                                   
jmp pe32_pack.D21592                           

/upxunpacking/Image/2-EP%E5%A4%84%E6%B1%87%E7%BC%96%E5%88%86%E6%9E%90.png

  1. 我们继续单步往下走,会遇到大量的向上跳转指令,也就是循环操作,这里会调用解压算法:

/upxunpacking/Image/3-%E8%A7%A3%E5%8E%8B%E7%AE%97%E6%B3%95%E5%BE%AA%E7%8E%AF.png

  1. 继续单步,壳会加载DLL:

/upxunpacking/Image/4-%E5%A3%B3%E5%8A%A0%E8%BD%BDDLL.png

  1. 调用GetProcAddress取导出函数,修复导入表
  2. or eax, eax判断返回值是否为空,不为空则写入到ebx,这里的ebx就是导入表地址
  3. 这里会经历很多循环,引入导入多个DLL、多个函数

/upxunpacking/Image/5-GetProcAddress%E4%BF%AE%E5%A4%8D%E5%AF%BC%E5%85%A5%E8%A1%A8.png

  1. 继续往下跟,就会发现popad,紧跟着的Jmp xxx就是跳转到OEP

/upxunpacking/Image/6-popad%E5%90%8EJmp%E8%B7%B3%E8%BD%ACOEP.png

  1. 如图所示,就是使用VS2019编译出来程序的OEP

/upxunpacking/Image/7-VS2019%E7%BC%96%E8%AF%91%E7%A8%8B%E5%BA%8F%E7%9A%84OEP.png

  1. 断在此处后,我们打开Scylla,直接点击Dump按钮,转储内存

/upxunpacking/Image/8-ScyllaDump%E5%86%85%E5%AD%98.png

  1. 接下来是修复导入表:
    1. 点击IAT AutoSearch按钮,自动搜索导入表特征
    2. 点击Get Imports按钮,解析导入表信息
    3. Fix Dump按钮,选择步骤10的Dmp文件进行修复

/upxunpacking/Image/9-Scylla%E4%BF%AE%E5%A4%8D%E5%AF%BC%E5%85%A5%E8%A1%A8.png

  1. 到了这一步,手动脱一个压缩壳就完成了,正常来说此时的程序是可以直接双击运行的,但是笔者这里的测试程序会报错,暂不深究问题
  2. 在Dump、修复IAT之后,此时我们的程序拖到IDA中,就可以比较舒服地进行静态分析了

32位程序脱压缩壳(堆栈法)

  1. 我们继续使用堆栈法脱壳,把PE32_Pack.exe拖到x32Dbg中:

/upxunpacking/Image/10-%E5%A0%86%E6%A0%88%E6%B3%95%E6%8B%96%E5%85%A5x32dbg.png

  1. 这里的pushad会将寄存器环境压入堆栈,我们单步走一步,查看堆栈变化:

/upxunpacking/Image/11-pushad%E5%90%8E%E5%A0%86%E6%A0%88%E5%8F%98%E5%8C%96.png

  1. 我们对着堆栈下一个单字节访问断点

/upxunpacking/Image/12-%E5%A0%86%E6%A0%88%E4%B8%8B%E5%8D%95%E5%AD%97%E8%8A%82%E8%AE%BF%E9%97%AE%E6%96%AD%E7%82%B9.png

  1. 之后大胆放行,会直接断在如图所示位置:

/upxunpacking/Image/13-%E6%94%BE%E8%A1%8C%E5%90%8E%E6%96%AD%E5%9C%A8popad%E9%99%84%E8%BF%91.png

  1. 此时我们发现,上一行就是popad指令,下方不远处就是jmp xxx
  2. 之后的流程保持一样,不重复赘述

32位程序脱压缩壳(popad指令)

  1. 这个手法没什么好说,直接硬搜popad的机器码

/upxunpacking/Image/14-%E6%90%9C%E7%B4%A2popad%E6%9C%BA%E5%99%A8%E7%A0%811.png

/upxunpacking/Image/15-%E6%90%9C%E7%B4%A2popad%E6%9C%BA%E5%99%A8%E7%A0%812.png

脱壳后程序大小

  1. 不管是压缩壳、加密壳,脱壳后得到的PE文件往往都会比原始文件大(甚至大得多)
  2. 这是因为Dump时,会将壳代码同时Dump出来,正常脱壳流程中,并不会将壳代码剥离
  3. 后续再修导入表,这一哪怕壳代码残留,也不影响我们分析主要代码
UPX 脱壳 逆向工程