新手的第一帖,献丑了,
有错误的地方师傅们在评论区直说就好,
最开始的起因是一个攻防世界的题目,这个题目使用的upx加壳,题目为easyre-150。bin文件附件里也有。
如果直接upx脱壳的话,题目基本没有啥难度,但是在题目做完以后我去翻看这个原本加壳后的文件发现很有趣。
一个稍不一般的upx-loader
于是便去简单的了解了一下。
首先直接看到文件die查壳查到是upx_easyre150的壳,
然后直接脱壳,看到文件:
在动调的时候我们可以轻松改变,走向got key的位置,
然后会进入函数lol,
大概十个这样子,注意判定跳转前的两句:
这个其实永远不会进入到另一侧的语句运行,我们动调的时候改动下, 就可以看到flag,
当然,很坑的格式:要套上RCTF{},
就是一个很简单的题目,
下面我们去考虑下手动脱壳的方法?
在此之前,我们先写一下这一部分出现的知识点:
linux 下的proc文件体系实际上并不是文件,而是我们在运行中的进程,每个进程都会产生一个由其运行的pid为文件名的文件, 其中,/proc/[pid]/
目录下的文件:
cwd :
这是一个符号链接,指向这个进程的运行目录。
exe :
这也是个软链接, 指向这个进程对应的可执行文件。
fd :
这是一个目录,里面包含进程打开的文件的文件描述符,并且这些描述符都是软链接,指向实际文件。
maps:
这个文件包含当前进程在虚拟内存中的空间分布已经对应的地址,访问权限。
主要写一下里面出现的系统调用作用和调用号
32位 linux 程序系统调用 int 80
那么就开始处理:
进入ida看到其实函数极少, 动调以后大致可以了解他们的内容, 这里简单进行了一下命名:
这里就是函数入口处了,看起来没啥好看的,动调以后也会发现其实就没啥,进入main函数:
main函数比较大,主要分成几个部分:
首先是获取pid, 然后组成一个"/proc/[pid]/exe"
的一个字符串,打开这个文件,
我们上述提到过,这个地址对应是一个链接,指向这个程序自身的绝对地址,
简单点说就是打开自己这个程序本身。
同样我们也可以在 /proc/[pid]/fd/
中看到
后面的位置主要在两个循环中,处理了一个字符串:
将其处理为:"AAAAAAAACDMVJO1A4NH"
然后打开这个名字的文件,我们可以在/proc/[pid]/fd/
中看到:
注意两个文件的权限,
程序自身为r-x, 而新建的文件为-wx
但是我们同样也可以查到,这个是程序自身新建的一个文件,其内容为空:
同时注意右上角时间标志,刚刚建立的文件,
以下使用elf代表文件本身,然后file代表这个新建立的文件
程序内的一个函数,主要作用就是讲fd 的len个长度的内容写入到addr中,该是比较好理解,
在其后进入一个循环,这里除了一下系统调用以外主要是高亮起来的一个较复杂的函数,不出所料应该就是upx loader
大致如下:
先是efl文件复制到一块地址空间,然后从这个addr进入loader去解压缩,最后写入到file中,
我们查看:
同样注意时间,这个是刚刚运行完write后写入的,
应该是运行一次循环,然后就会跳出了, 进入判定,
if语句中需要我们注意的是sys_execve函数,改变了运行的程序,
动调的时候会有问题:
这个if语句是进不去的, 要验证的是"! ", 而实际运行后的位置是"UPX!", 这个是UPX压缩的标志, 我们直接去手动该下,让他进入if语句中运行:
if语句中,先是关闭两个文件,然后又打开了file文件的读取,如下示:
但是这次是r-x权限了
然后复制了一段数据,使用sys_unlink,
这里我们要知道,unlink不会直接关闭一个文件, 会先检查下文件是否还在读写,当这个文件不进行读写的时候,再将其关闭,
于是我们的fd文件夹:
然后执行sys_execve
,
注意这里的参数已经改为了:
即,把切换运行的elf文件为我们的file文件,
然后我们执行:
并且可以看到我们的动调的位置:
已经是在运行我们的脱壳后的文件了。
接下来就是直接去查看那个AAAA..的文件,然后会发现和我们脱壳完了以后的文件是一致的。
另外要注意的, 这个unlink得调用,使得这个文件在函数运行完成后就会被删除,
妙啊
由于第二种方式的脱壳并不是我们常见的upx脱壳方式,我也觉得很是怪异,于是去进一步的看了一下upx,
包括以下:
die是如何查到upx的壳的?
一个正常的以前手动脱的壳应该是啥样?
upx加壳,这个加载器loader,是如何产生出来的?
从die中找到的upx那个查壳的脚本:
一个.sq
后缀的文件,我也没怎么查到是要用啥语法高亮, 看着非常像c, 就用了c的高亮吧
啧, 括号分行写, 我觉得是c
然后看起来像是,用了个查找字符串 找那个"UPX"标志,然后还有个一大堆的, 但是"e8" ,应该是call的字节码吧,中间省略,后面估计也是字节码,应该是是为了查那个loader,
然后拿出010, 把这个题目文件的"UPX!"标志给去掉,
然后die查壳:
没有查出来UPX了,
成功,那么这样的话, 题目就必须动调,而且还看不出来有壳, 难度就上升很多很多。
另外我们看一下一个标准使用upx的程序应该有的loader,
我们随手拿来一个简单的文件进行加壳:
然后看一下:
如果我们多看几个应该是差不多的结构,都是这些东西,
说明, 这个loader是一个固定生成的, 只是call指令会有偏移,会重新计算,
这个时候,我们继续去使用010 将其中的"UPX!" 标志去除掉。
载入die, 发现仍然可以判断出来是upx, 估计是前面我们看到分析中间另一部分的那些"e8"之类的,的确是在查找loader,
这也证明了我们这个题目并不是标准的一个upx直接进行压缩的,估计应该是出题人自己写的,
另外那个我们动调中间判断失败也是由于出现upx标志,可能题目原本应该是没有这个标志的,应该是被改过吧,
另外upx是一个开源工具,在github我们可以找到他的源码,
另外17年2月有篇文章:i春秋-Tangerine-upx源码分析
大佬简单讲述了整个流程和最后产生loader的位置,
里面的一部分位置和现在的版本有些许差别,但是整体流程都是一致的,感兴趣的朋友可以看看。
目前我只是跟着这个文章的思路捋了一遍整个的流程,等下一步继续分析再写相关的文章吧。
主要的loader已经预设好在文件upx/src/stub/amd64-linux.elf-entry.h
中,
然后除了对于call指令进行矫正之类的, opcode和这里的数据基本一致,这里就不再赘述,
其实仔细的调试了下这个题目了解到了不少和linux内的一些东西,
且最后也发现了这个题目的前面的加载部分,应该是一个作者自己写的, 如果我们讲其中的upx标志去掉,还是一个比较有难度的题目,
而且我们也可以使用这样的框架,讲其中的loader函数部修改,实现另外一种加壳, 自己做出来一个稍微有难度的题目。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2020-3-20 18:07
被令则编辑
,原因: