基础脱壳

0. 前言

0.1 脱壳的常见概念

壳:是一种对应用程序加密压缩处理的技术

一般对可执行文件加壳的目的:

  1. 软件加壳,保护数据、防止破解
  2. 外挂加壳,保护数据、防止破解
  3. 病毒加壳,防止被查杀。

常见的壳分为压缩壳加密壳两种,upx属于压缩壳。

  • 脱壳:表示通过反汇编将壳去掉的过程。
  • OEP: 程序最开始执行的地方。
  • 原始OEP: 程序加了壳后,壳是先运行的,所以OEP是壳程序的入口点,而原始OEP就是程序原来的入口点。
  • dump内存:将内存中的数据或代码转储(dump)到本地。
  • IAT:导入地址表,windows下可执行文件中文件格式中的一个字段,描述的是导入信息函数地址,在文件中是一个RVA数组,在内存中是一个函数地址数组。
  • 修复IAT: 在脱壳后,不管是加密壳还是压缩壳,都有一个从内存dump到本地存储并保存为文件,而IAT在文件中是一个RVA数组,在内存中是一个函数地址数组,我们就需要将从内存dump出来的IAT从函数地址数组转换成RVA数组,这样程序才能修复。
  • 脱壳的环境:这个单独出来说,主要原因就是不同的系统脱壳时遇到的问题可能是不一样的,因为脱壳时要修改IAT,而不同系统中同一个模块的API导出的顺序是不一样的,所以修复时一般都会出现点问题。因此,我建议脱壳的环境应该是在32位系统的虚拟机中,以下的所有操作应该在32位系统的虚拟机中操作,64位系统下可能会出现意想不到的问题。
  • pushad: 将所有的32位通用寄存器压入堆栈 ESP-32 。
  • popad: 将所有的32位通用寄存器出栈 ESP+32。

0.2 脱壳的方法

  • 单步跟踪:一步一步分析每一条汇编指令,吃透每一行汇编背后所代表的意思,将壳代码读懂,从而找到原始OEP然后脱壳。这种方法是最锻炼人的,也是最难的。 说白了就是一点点的调试,需要深厚的汇编功底和耐心。
  • 平衡堆栈(又称ESP定律,技巧法):一般加壳的程序,都是先运行壳,然后再内存中还原程序,然后跳转到原始OEP,开始执行原程序代码。既然先运行壳,那就必然进入壳程序然后再退出壳程序,期间要遵守堆栈平衡(进入前和退出后的栈指针是相同的),也就是说壳退出后,必然会操作堆栈指针为进入之前的堆栈指针。在进入壳程序时,就可以在堆栈设置访问断点,让程序跑起来,当程序暂停的时候,就是壳程序即将执行完的时候,然后再其附近单步跟踪就可以找到原始OEP了。 这种方法比较适用于upx这种只对代码和数据压缩了的壳,如果还对代码加密了,那么就不是太好找了。加密的话就需要结合单步跟踪法。

不管是哪种脱壳方法,都需要遵循脱壳三步法,脱壳三步法分为以下三步:

  1. 寻找原始OEP: 这一步骤的主要作用就是要确定原始程序代码到底在哪里,能找到原始程序的代码,说明壳代码执行完了,我们只有找到原始OEP才能进行下一步的动作。
  2. dump内存到文件: 当我们找到原始OEP,调试运行到原始OEP时,只要代码被还原,我们就可以在这个地方进行dump内存,将内存中被还原的代码和数据抓取下来,重新保存成一个文件,这样脱完壳时,我们就可以用静态分析工具分析程序了。
  3. 修复文件: 这一步主要就是修复IAT,对从内存中转储到本地的文件进行修复。

1. 手脱UPX壳

UPX壳:

  1. 经过UPX压缩的win32/pe文件,包含三个区段:UPX0, UPX1, .rsrc或UPX0, UPX1, UPX2(原文件本身无资源时)。UPX0:在文件中没有内容,它的”Virtual size”加上UPX1的构成了原文件全部区段需要的内存空间,相当于区段合并。
  2. UPX1:起始位置为需解压缩的源数据,目标地址为UPX0基址。紧接着源数据块是”UPX stub”,即壳代码。一个典型的pushad/popad结构,所以人们常用”ESP定律”来脱UPX。
  3. rsrc/UPX2:在原文件有资源时,含有原资源段的完整头部和极少部分资源数据(类型为ICON、GROUP_ICON、VERSION和MANIFEST),以保证explorer.exe能正常显示图标、版本信息。还有就是UPX自己的Imports内容,导出表的库名和函数名(如果有的话)。

就是因为UPX是一个典型的pushad/popad结构,又要遵守堆栈平衡,即pushad之前的ESP需要跟popad之后的ESP相同,所以当我们看到UPX壳进入pushad后,对堆栈添加访问断点,当壳即将及执行结束即执行到popad要去还原堆栈时,必然会触发断点暂停下来,然后单步调试往下走,原始OEP就在附近了。

工具:PEID和OD

方法1:单步跟踪

方法2:ESP定律法

在关键指令的下一处,esp的内容设置硬件断点(word或者dword)

方法3:2次内存镜像法

第一次,程序的第一个`.rsrc`区段;第二次,程序的第一个`UPX0`区段。

程序把res段解密之后,code段肯定也就解压完成了

方法4:一步直达法

适用于绝大多数upx、ASPACK壳。有进栈就有出出栈,找到出栈的命令:popad等

2. 手脱ASPACK壳

6种方法脱

方法1:单步跟踪

方法2:ESP定律

方法3:一步直达

方法4:2次内存镜像

方法5:模拟跟踪

  • 找到:SFX,imports,relocations
  • tc eip<xxxxxx(找到的地址)

方法6:SFX

选项=》调试设置=》SFX=》块方式跟踪  => 重新加载

3. 手脱Nspack壳(北斗)

方法1:单步跟踪

方法2:ESP定律法

方法3:内存镜像

直接在code处下断点,再单步调试

方法4:模拟跟踪

方法5:直接下断点at GetVersion

适用于北斗3.0之前的壳

4. 手脱FSG壳

0%