UPX脱壳
简介
upx 壳是一种压缩壳,在 CTF 比赛中比较常见。
针对 upx 壳有专业的脱壳工具,一般我们可以直接使用工具脱壳。
但是在比赛中往往不会让你那么简单就把壳脱掉,出题人会通过修改加壳程序的一些特征导致脱壳工具无法使用。
这个时候我们可以通过恢复修改掉的地方让工具继续奏效。
当然我们也可以直接手脱,手动脱壳我们就需要找到加壳程序的 OEP 然后 dump 文件内存。
OEP 原始程序入口点。EP(Entry Point),意即程序的入口点。而 OEP 是程序的原始入口点,一个正常的程序只有 EP,只有入口点被修改的程序(加壳),才会拥有OEP。
工具脱壳
UPX(Ultimate Packer for eXecutables)是一个开源的可执行文件压缩器,用于减小可执行文件的大小,同时保持其功能。UPX 支持多种操作系统,包括 Linux、Windows 和 macOS。
upx 脱壳工具使用请参考这篇文章。
upx 脱壳工具使用:【逆向】UPX工具使用及加壳_upx.exe-CSDN博客
常规upx工具脱壳
直接使用 upx 脱壳工具进行脱壳
1 |
|
例题:[SWPUCTF 2023 秋季新生赛]UPX
- 分析
exeinfo 查看发现有 upx 壳。
- 脱壳
直接使用 upx -d 命令进行脱壳。
可以看到脱壳成功。
exeinfo 查看确认一下
确认脱壳成功
ida 打开查看反编译代码,发现 flag
upx魔改壳
常规 upx壳 只要利用 upx 脱壳工具直接执行 upx -d 命令即可脱壳。
不过只要做一些简单的修改就可以让 upx 工具失效。
这种魔改壳在 CTF 中很常见。
- 修改区段名
我们先来查看一下未修改前的文件信息。
可以看到图中显示了 upx1 和 3.91 upx 这两个特征标识。
然后查看一下加壳后的区段窗口,发现只有三个区段。最明显的特征upx0
和upx1
.
我们只需要修改它,就可以让 upx 脱壳工具无法脱壳。
我们利用 010 Editor 编辑文件将upx
改成rpx
尝试脱壳
结果:
可以看到失败了。
只要改回原样就可以进行脱壳。
- 修改标识
可以看到 3.96.UPX! 特征码。我们可以修改 3.96 版本号开始24个字节的内容。都不会对程序运行产生影响。
修改标识之后,upx 脱壳工具就无法脱壳。不过可以利用 UPX Unpacker for Dummies 工具进行脱壳也可以直接手脱。
后面我们会讲如何手动脱壳。
例题:[LitCTF 2024]hello_upx
- 分析
exe文件,查壳发现存在upx壳。
直接使用脱壳机,脱壳失败。
提示信息文件被修改,用010_Editor打开
发现有四处地方被修改
这里我们列出正常 upx 加壳文件的区段信息进行对比
UPX0
和UPX1
是加 UPX 壳后的两个区段名。其中 UPX1 区段包含了需要解压的数据块。.rsrc
是程序资源信息区段名,这个区段含有原资源段的完整头部以及图标、Manifest、版本等未被压缩的资源,当然还有 UPX 自身需要的导入信息等(如果程序自身不含资源段,加壳后就是 UPX2)
分析修改
upx0
被改成了小写upx1
被改成了小写upx2
被改成了小写upx!
被改成了小写
- 恢复
将upx0、upx1、upx2和upx!全部修改为大写
- 脱壳
可以看到脱壳成功。
分析代码逻辑
- exp
根据代码逻辑构造exp
1 |
|
手脱
手动脱壳的目标:找到原始程序入口地址(OEP)。
EXE
自己写的文件加壳,手脱。
入口不是pushad
,只能一步一步单步步过。
pushad
意味着 upx 壳解压缩代码的入口。
64位程序中没有puahad
,而是用几个push
汇编代码替代。32位程序中存在pushad
。
遵循单步定律,向下跳转允许实现,向上跳转不允许实现。
向下的红色小箭头就是向下的跳转,线为红色即是跳转成立,线为白色就是跳转不成立。
同理向上的红色小箭头就是向上的跳转。
一直单步步过,直到发现这样的多个push
指令为止。
很明显这里就是 UPX 解压缩代码的入口点。
根据esp定律下断点寻找OEP
f8 单步一下执行一下,让rsp
发送变化
查看寄存器窗口,发现rsp
产生了变化
右击rsp
,选中在栈中转到。
右击栈顶,选中断点,选择硬件访问断点,选择4字节。
设置硬件断点后 f9 运行。
之后发现下面有一个比较大幅度的jmp
跳转,并且已经显示main
特征。
判断这个特征会跳转到 OEP。
选中jmp
指令,然后 f4 运行到此处。
之后直接单步执行就会跳到 OEP。
OEP:
接下来 dump 文件
点击 Scylla 插件,然后点击 dump 之后将文件保存。
但是这样 dump 后的文件是无法运行的,所以我们要修复文件。
- 修复文件
先点击 Scylla 中的 IAT Autosearch,然后点击 Get Imports 。
,在 Imports 列表中右键 delete 删掉带有红叉的。
之后点击 Fix Dump 选中之前的 Dump 文件修复即可。
脱壳之后运行程序测试。
成功执行程序,dump文件成功。
exeinfo 确认脱壳
发现仍然保留特征信息
ida打开确认
确认已脱壳
ELF
例题
自己写一段代码编译加壳。
1 |
|
编译,这里需要静态编译,要不然文件太小加不了壳。
加壳,可以看到加壳成功
exeinfo 扫一下,可以看到显示 upx 壳
- 寻找OEP
ida打开文件
直接在start
启动函数位置下断点,然后动态调试。
发现ret
指令直接 F4 然后 F7 进入,直到发现endbr64
指令(即源程序代码开头)。
下翻,翻到ret
指令直接 f4 运行到这里,然后 f8 单步。
重复以上过程,如果没找到ret
指令就执行最近的jmp
跳转。
当看到下面这个syscall
汇编中断代码时我们就快到 OEP 了。
之后 f4 运行到ret
位置,之后继续单步。
- 跳转到 OEP
看起来都是数据,按快捷键 c 将数据解释为代码。
之后看到endbr64
,elf64 位程序的入口。
然后查看程序代码,发现将byte_401775
函数地址送入rdi
。
由它来调用初始化程序,进而调用main
函数。
这里的byte_401775
就是原程序main
函数。
进入main
函数查看
按快捷键c
解释为代码,然后在endbr64
处按快捷键p
创建函数。
之后就可以f5
反编译了。
main
函数反编译代码
CTF 中一般就可以在这里分析程序代码解题了。
不过接下来我们要 dump 文件。
- dump内存到文件
先回到 OEP 这里。
确保rip
执向endbr64
汇编代码。
alt+f7 快捷键打开并运行脚本(脚本放后面了)。
dump64 位程序选用 64 位文件。
执行,等待执行完毕。
执行完毕
dump 文件的存放路径在脚本中设置。
执行文件
可执行,dump文件成功。
exeinfo扫一下,确认脱壳。
显示无壳,则脱壳成功。
idc dump内存文件代码
感谢大佬的代码
- 64位程序dump内存代码
1 |
|
- 32位程序dump内存代码
1 |
|
后言
参考链接:手动去upx特征_upx -d-CSDN博客
参考链接:linux 下 upx 脱壳笔记
参考链接:[三叶草二进制招新培训](三叶草二进制招新培训9 - UPX脱壳_哔哩哔哩_bilibili)
参考链接:如何用x64dbg UPX手动脱壳(64位)