惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
博客园 - 三生石上(FineUI控件)
GbyAI
GbyAI
大猫的无限游戏
大猫的无限游戏
M
MIT News - Artificial intelligence
Microsoft Azure Blog
Microsoft Azure Blog
月光博客
月光博客
Engineering at Meta
Engineering at Meta
I
InfoQ
T
Tailwind CSS Blog
N
Netflix TechBlog - Medium
S
SegmentFault 最新的问题
H
Help Net Security
博客园 - 【当耐特】
WordPress大学
WordPress大学
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
美团技术团队
博客园 - 叶小钗
T
The Blog of Author Tim Ferriss
腾讯CDC
雷峰网
雷峰网
Martin Fowler
Martin Fowler
The GitHub Blog
The GitHub Blog
D
Docker

Watermelonabc的Blog

又一个黑客模拟游戏 又一个黑客模拟游戏 家庭辅导一晚,有点感想 家庭辅导一晚,有点感想 福州大学单片机原理与应用(跨学科)复习 福州大学单片机原理与应用(跨学科)复习 福州大学毛概期末复习 福州大学毛概期末复习 x86 汇编语言详解 - 2 x86 汇编语言详解 -2 CCB 2026 Half-Final Guangzhou CCB 2026 Half-Final Guangzhou 视频也可以含恶意代码吗 视频也可以含恶意代码吗 Hello,2026 Hello,2026 2025 福州大学算法与数据结构复习 2025 福州大学算法与数据结构复习 2025 福州大学习概期末复习 2025 福州大学习概期末复习 Frida 入门 Frida 入门 记一场贴吧交流 记一场贴吧交流 虚拟机保护 虚拟机保护 2025 强网杯 S9 Reverse 复现 2025 强网杯 S9 Reverse 复现 你如何在 FZU 取到快递 你如何在 FZU 取到快递
程序脱壳
2025-07-11 · via Watermelonabc的Blog

壳 (packer) 是一种程序压缩与保护手段,有时称为 “可执行程序资源压缩”。加壳过的程序可以直接运行,但是不能查看源代码,要经过脱壳才可以查看。

加壳是利用特殊的算法,对 EXE、DLL 文件里的资源进行压缩、加密。压缩之后的文件可以独立运行,解压过程完全隐蔽,都在内存中完成。

原始程序代码在磁盘文件中一般是以加密后的形式存在的,只在执行时在内存中还原,这样就可以比较有效地防止破解者对程序文件的非法修改,同时也可以防止程序被静态反编译。当然,很多恶意软件为了 “隐藏自己” 免受安全软件的查杀,也会使用加壳器。

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

壳的加载

  • 保存入口参数
  1. 加壳程序初始化时保存各寄存器的值
  2. 外壳执行完毕,恢复各寄存器值
  3. 最后再跳到原程序执行

通常用 pushad / popadpushfd / popfd 指令对来保存和恢复现场环境

  • 获取所需函数 API
  1. 一般壳的输入表中只有 GetProcAddressGetModuleHandleLoadLibrary 这几个 API 函数
  2. 如果需要其他 API 函数,则通过 LoadLibraryA(W)LoadLibraryExA(W) 将 DLL 文件映射到调用进程的地址空间中
  3. 如果 DLL 文件已被映射到调用进程的地址空间里,就可以调用 GetModuleHandleA(W) 函数获得 DLL 模块句柄
  4. 一旦 DLL 模块被加载,就可以调用 GetProcAddress 函数获取输入函数的地址
  • 解密各区块数据
  1. 处于保护源程序代码和数据的目的,一般会加密源程序文件的各个区块。在程序执行时外壳将这些区块数据解密,以让程序正常运行
  2. 外壳一般按区块加密,按区块解密,并将解密的数据放回在合适的内存位置
  • 跳转回原程序入口点
  1. 在跳转回入口点之前,一般会恢复填写原 PE 文件输入表 (IAT),并处理好重定位项(主要是 DLL 文件)
  2. 因为加壳时外壳自己构造了一个输入表,因此在这里需要重新对每一个 DLL 引入的所有函数重新获取地址,并填写到 IAT 表中
  3. 做完上述工作后,会跳转到原始入口点 (OEP),将控制权移交原程序,并继续执行

如何脱壳

脱壳分为机脱和手脱。

如果加壳器有对应的脱壳脚本且原程序未对加壳结果进行修改,那么机脱是最优雅且快捷的,比如 UPX 壳。脱 UPX 壳最好的办法就是 upx -d[1] 机脱我们不多做介绍,这更多考验搜索能力和 PY。

当然,有的壳是没有(完整的)脱壳机的,而有些谨慎的程序员也会给壳添油加醋,这时就不得不进行麻烦的手脱了。

UPX 部分手脱

UPX 作为入门最常见的壳,官方程序自带脱壳功能。然而,仅仅是 upx -d 实在没有技术含量,因此进阶一点的 UPX 题目就会再次修改 UPX 加壳后的程序,使得 upx -d 失效。

upx -d 脱壳主要利用加壳时候写到加壳程序里的 PE 信息,直接抹掉原始加壳的留下的 PE 头信息就无法直接 upx -d 脱壳了。

  1. 修改 / 删除特殊区段名
    UPX 压缩过的程序有特殊区段名 UPX,十六进制表示为 55 50 58。如果修改 / 删除区段名,那么 upx -d 就无法识别了,而程序仍然能运行。UPX 区段保存着程序的压缩信息,在一定程度上可以验证程序的完整性。
    UPX 压缩的程序还有一个区段 .rsrc,这是程序的头部和部分资源数据段,有时是 UPX2
    解决办法就是将修改的区段名恢复成 UPX.rsrc,一般来讲,程序中有 3 - 4 个 UPX 特征区段名,要看看它们是否全部修复。
    另一个更强硬的手段就是直接删除 UPX 特定区段的字节,这样做甚至会损坏程序的运行。
    总结起来,你需要的关注的地方有这些:1)UPX0UPX1.rsrc/UPX2;2)5.01 UPX! $,数字代表程序使用的 UPX 版本,UPX! $ 标志从此处开始解压原始程序。

手动脱壳常用方法

脱壳的主要目的是:找到 OEP,在 OEP 处设置硬件断点。如果想更近一步,就要输出脱壳后的程序到文件,并修复运行。

基本步骤:

graph LR
  A[查壳] --> B[寻找 OEP]
  B --> C[脱壳/Dump 原程序]
  C --> D[修复导入表]

PECompact 手脱

PECompact 是一种简单的压缩壳,手脱起来还是比较简单易懂的 -

  1. [转贴] UPX 手动脱壳后的半完美修复 ↩︎