壳 (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 壳,我记得 52 上有哪位大佬说脱 UPX 壳最好的办法就是 upx -d。机脱我们不做介绍,这更多考验搜索能力和 PY

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

脱壳常用方法

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

基本步骤:

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

UPX 手脱方法

再说一遍,UPX 最完美的仍是用 upx -d 机脱,或者修复 UPX 自解压信息后再机脱。

Note

机脱已经在如何解决魔改后的加密算法处有所说明


©2025-Present Watermelonabc | 萌 ICP 备 20251229 号

Powered by Hexo & Stellar 1.33.1 & Vercel & HUAWEI Cloud
您的访问数据将由 Vercel 和自托管的 Umami 进行隐私优先分析,以优化未来的访问体验

本博客总访问量:capoo-2

| 开往-友链接力 | 异次元之旅

中文独立博客列表 | 博客录 随机博客

AI 参与指数(IIIA)2 级

猫猫🐱 发表了 55 篇文章 · 总计 229.3k 字