脱壳

基础概念

加壳

加壳的全称是可执行程序资源压缩。加壳过的程序可以直接运行,但是不能查看源代码。要经过脱壳才可以查看源代码。

解密过程完全隐蔽,都在内存中进行。它们附加在原程序上通过Windows加载器载入内存后,先于原始程序执行,得到控制权,执行过程中对原始程序进行解密、还原,还原完成后再把控制权交还给原始程序,执行原来的代码部分。加上外壳后,原始程序代码在磁盘文件中一般是以加密后的形式存在的,只在执行时在内存中还原。

壳的类型通常分为压缩壳和加密壳两类。压缩壳的特点是减小软件体积大小,加密保护不是重点。加密壳种类比较多,不同的壳侧重点不同,一些壳单纯保护程序,另一些壳提供额外的功能,如提供注册机制、使用次数、时间限制等。

特征

(Heur)Packer: Packer detected[Last section EP + "pushal" at EP]    //在程序入口点处检测到 pushal 指令将所有通用寄存器的值压入栈中。

OEP

OEP:程序入口点。软件加壳一般隐藏了程序真实的OEP(或者用了假的OEP), 我们需要寻找程序真正的OEP,才可以完成脱壳。

一般加壳程序在使用Ollydbg等动态调试工具时,会停在壳的预处理块。即处在对于程序原始代码块的解压或解密操作之前,在运行完程序自脱壳模块后,会停留在程序加壳之前的OEP位置,此时是dump程序的最佳时期。脱壳时在真实OEP处下int3断点,就可以捕捉到程序代码段完全恢复的状态。因此,寻找加壳程序的正确OEP,也成了手动脱壳时的第一要务。

IAT

IAT:(Import Address Table),导入地址表。

由于导入函数就是被程序调用但其执行代码又不在程序中的函数,这些函数的代码位于一个或者多个DLL中。当PE文件被装入内存的时候,Windows装载器才将DLL 装入,并将调用导入函数的指令和函数实际所处的地址联系起来(动态连接),这操作就需要导入表完成。其中导入地址表就指示函数实际地址。多数加壳软件在运行时会重建导入地址表,因此获取加壳程序正确的导入地址表也是手动脱壳操作中的一个关键问题。

dump

"dump 程序" 指的是将内存中运行的程序状态(特别是已被动态解密或脱壳的代码)提取并保存为可执行文件的过程。

断点

Int3断点:Int3 是一条汇编指令,其机器码为 0xCC,所以也常被称为 CC 指令。
断电原理:当被调试进程执行到 Int3 指令时,会产生一个异常(在 Linux 中会向进程发送信号 SIGTRAP)。调试器会捕捉这个异常,从而使程序停在断点处。

脱壳方法

单步跟踪法

通过Ollydbg的单步(F8)、单步进入(F7)和运行到(F4)功能,完整走过程序的自脱壳过程,跳过一些循环恢复代码的片段,并用单步进入确保程序不会略过OEP。这样可以在软件自动脱壳模块运行完毕后,到达OEP,并dump程序。

ESP定律

call命令:

  • 1:向堆栈中压入下一行将要执行的代码地址
    2:JMP到call子程序地址处(程序流程跳转过去)
    例如
00401029 E8 DA240A00 call 004A3508
0040102E 5A          pop edx

在执行了00401029后,程序会将0040102E(下一条指令地址)压入堆栈,然后 JMP 到 004A3508 地址处
RETN命令

  • 将当前ESP指的地址出栈,JMP到这个地址.

ESP定律的原理在于程序中堆栈平衡的合理利用。由于在程序自解密或者自解压过程中,不少壳会先将当前寄存器内容压栈,如使用pushad,在解压结束后,会将之前的寄存器值出栈,如使用popad。因此在寄存器出栈时,往往程序代码被自动恢复,此时硬件断点触发。然后在程序当前位置,只需要少许单步跟踪,就很容易到达正确的OEP位置。

内存镜像法(二次断点法)

通过OD的ALT+M快捷键,进入到程序虚拟内存区段。然后通过加两次内存一次性断点,到达程序正确OEP的位置。

内存镜像法的原理在于对于程序资源段和代码段下断点,一般程序自解压或者自解密时,会首先访问资源段获取所需资源,然后在自动脱壳完成后,转回程序代码段。这时候下内存一次性断点,程序就会停在OEP处

一步到达OEP

一些压缩壳往往popad指令距离OEP或者Magic Jump特别近,因此使用Ollydbg的搜索功能,可以搜索壳的特征汇编代码,达到一步断点到达OEP的效果。

最后一次异常法

最后一次异常法的原理是,程序在自解压或自解密过程中,可能会触发无数次的异常。如果能定位到最后一次程序异常的位置,可能就会很接近自动脱壳完成位置。现在最后一次异常法脱壳可以利用Ollydbg的异常计数器插件,先记录异常数目,然后重新载入,自动停在最后一次异常处。

模拟跟踪法

模拟跟踪法的原理就是使用Ollydbg下条件断点,SFX相当于是一个自解压段,在自解压段结束时(eip的值转到代码段时),已经距离OEP很近,但是这种跟踪方法会比较耗时。

“SFX”法

“SFX”法利用了Ollydbg自带的OEP寻找功能,可以选择直接让程序停在OD找到的OEP处,此时自解压已经完成,可以直接dump程序。

dump

x64dbgScylla插件

先点“IAT Autosearch”,再点“Get Imports”,在“Imports”中删除掉带有红色叉叉的,再点击“Dump”,之后“Fix Dump”选中之前的Dump文件,修复成功。

脱壳实例

upx壳

查壳

OD载入

单步跟踪法

让程序一直向下执行,遇到向下的跳转直接执行。遇到向上的跳转需要处理

选中mov eax,0x1,F4直接运行到这里。

如果遇到跳转下面是个跳转(如jmp等)或者是call命令选择运行到再下一个命令。
重复以上操作,知道遇到特征地方如以下特征

popad是一个出栈操作,显然是恢复之前寄存器状态,且jmp跳转范围大。一般来说很大范围的跳转就是调到OEP了。以下是跳转后

如上图,基址是00400000,故OEP就是7738C

接下来就是进行脱壳
方式一
右键-->用OllyDump脱壳调试进程 (脱壳过程中有时会出现等待时间长的假死情况,耐心等待即可)

Esp定律

pushad指令执行后,ESP寄存器的值发生改变,要记住这个值。

选中ESP的值,右键-->follow in Dump(在转储中跟随) ,就会在下图跟随。

也可以选择在command框内输入 dd + 地址进行同样操作(或者hr + 地址)

如上图,选中这一串数据之后,右键-->breakpoint-->hardwire on access(硬件 存取)-->word或者dword都可以
然后点击运行就会发现停止到跳转处,执行完跳转就是OEP,看前面代码发现是个出栈 (如下图)

然后提取脱壳文件就行。

2次内存镜像法

view-->memory

(如上图)找到第一个.rsrc下F2断点。

一次直达法

发现程序开始是pushad则知道对应有popad
Ctrl+F快速查找

工具

upx -d '文件名'

手脱ASPACK壳

单步法


如上图,发现步过程序直接运行,所以F7跟进

(如上图)步过程序运行,仍需要F7跟进

有时,可能不是再jnz时跳转到脱壳后程序,可能还需要再执行几次F8

ESP定律

常规方式,需要注意的是运行后需要删除断点:调试-->hardware breakpoints-->删除即可

一步直达法

先按一下F8让程序执行一条汇编指令
popad:Ctrl+F查找,如果不是Ctrl+L查找下一个

2次内存镜像断电法

查看-->内存
在第一个.rsrc 下F2断点。(原理是程序自上而下执行,.rsrc解压完后code也就解压完了)然后运行
再看内存,在code那里下断点

然后运行,按照单步法很快就能找到popad

模拟跟踪法

查看-->内存

(如上图)对地址进行跟踪,然后F8几次就出来了

SFX跟踪

options-->debug options-->SFX

选择后重载就会自动定制在OEP处

手脱Nspack壳 (北斗)

2次内存镜像


发现上面没有.rsrc直接字code断点就可以

posted @ 2025-07-28 15:14  MillionMind  阅读(7)  评论(0)    收藏  举报