对UPX的一次深入
0x00 口胡
这次不知道怎么说,心血来潮的成分更多一点吧。自己也有过很多次这种心血来潮,希望这次能够实践下去。。。
记得刚入二进制坑的时候,就接触了脱壳,自己的脱壳技巧也是停留在那时的水平,也只是会一些压缩壳,而且也只是傻逼式的用工具,根本不懂原理,以及为什么这么做。。。
这次准备以UPX为引导,准备实现以下目标:
1. 了解熟悉UPX加壳流程
2. 逆向分析UPX脱壳桩代码
3. 分析从OEP处dump的话具体是dump了个什么东西
4. 分析dump后重建IAT的原理
5. 根据加壳和脱壳流程,写出自己的脱壳脚本(python或IDApython)
6. 有时间的话,去看看各种压缩算法,估计没时间
GO!
0x01 了解熟悉UPX加壳流程
近阶段时间有限,要干和想干的事情太多,加上近机场ctf我都没开题,所以在前段日子看了近三周源码后决定先搁置一段时间,好好修炼一下pwn,真心被打的有点小郁闷。。。
网上资料真心少啊,本来以为前人有做这方面工作的,结果,并没找到,没办法,自己撸源码。。。
UPX作为一个经典的压缩壳,自己对待它的源码还是很细致的剖析了一下的,所以耗费了大把时间,也想学习下一个小的工程程序它的大体架构是怎样的,毕竟不能只做脚本小子:p。但只看了加壳流程的大半部分,不过,应该可以扯很长时间了。。。
没捷径,一步步来吧,从main开始,由于没看完,所以别妄想我能够从总体上把握所谓的加壳的整体架构和流程之类的,毕竟不是战略家,科科。。。
开扯
参数check
- _set_abort_behavior:首先设定程序崩溃时所采取的动作,详见msdn,用户友好,并没有什么影响
- acc_wildargv:并不知道干嘛的
- upx_sanity_check():里面是一系列的断言,进行初步检测
- opt->reset():opt是声明的一个全局变量,是从options.h中导出的(extern struct options_t *opt;),这是个蛮复杂的结构,从他的具体结构中(注释和变量命名),我们可以看到UPX所支持的一系列操作,该语句是对该结构的一些变量进行初始化赋值,设定一些默认值。
- 紧接着是对后缀进行处理。我们知道windows一般都是带.exe这种后缀的,下面的#if,#else预处理就是把这个后缀去掉
- set_term(stderr);设置输出终端为标准错误流
- 下面的预处理是判断两种压缩算法是否可用,不可用输出错误信息,然后直接退出
- 设置随机种子,下面好像没有用到
- 参数解析,只对几个很简单的参数(help这种)进行操作,如果存在,输出相关信息
- 一个并不知道干嘛的预处理
- 重置终端为标准输入(默认)
- 参数判断,并作相应处理
- 只有一个参数,显示提示信息后退出
- 重置输出为错误输出
- 还是参数检查-_-
- 紧接着的语句说明upx支持批量加壳
参数到这就算检查完了,参数通过检查,则把参数信息记录下来后开始工作,不出意料应该是记录到opt全局变量中去。然后就是重置输出为标准输出,然后进入新世界,do_files
do_files(i,argc,argv)
- 对版本的检查,版本大于1显示信息。
- 初始化静态变量infoheader
- 从批量的待加壳文件中取出一个,进行单个加壳操作->do_one_file,后面跟一大堆捕获异常的块,就不看了。
do_one_file(iname,oname)
- 检查输入文件的合法性和权限,当输入只读且要在源文件基础上输出会报错
- InputFile fi;fi.st = st;fi.sopen(iname, O_RDONLY | O_BINARY, SH_DENYWR);/二进制和只读方式打开文件/
- // open output file/创建一个临时输出文件,根据抛出的异常提示信息,可以看出打开输出文件时面临哪些问题/OutputFile fo;
1 | PackMaster pm(&fi, opt);/*根据opt和fi创建加壳管理对象,这个对象很重要*/ |
1 | void PackMaster::pack(OutputFile *fo) |
fi的使命已完成,置空并进入doPack(fo)进行加壳1
2fi = NULL;/*前面都是一些check*/
p->doPack(fo);/*得到具体的加壳对象后,下面开始加壳*/
doPack(fo)
1 | void Packer::doPack(OutputFile *fo) |
doPack()的具体实现就不贴了,到这开始就开始与文件结构有紧密联系了,总的思路就是不同的文件格式有不同的压缩和加壳策略,对应着doPack()的不同实现。
对于文件格式的信息,给出以前学PE的图片,结合图片来看。
ih指IMAGE_FILE_HEADER