一次ELF程序动态解密尝试
在最近的一次出题中,由于pwn水平有限(泪),所以为了提高难度,在pwn题中添加了一些逆向要素。在最近的一次尝试中实现了之前一直有想法,但没有去实现的一个动态解密的思路,写出本文给大家分享一下。
0x01 什么是动态解密
这动态解密其实是我自己取的名字,主要的操作就是让程序在运行过程中对将要执行的代码进行解密,执行完后再加密回去。这样能在很大程度上提高逆向分析的难度。因为当攻击者对程序进行逆向分析时,如果没有解密,它只能看着一块加密的数据发呆。
大概是这个样子:
0x02 实现思路
- 编写好待加密函数a();
- 编写解密函数b(),在b()函数中先解密a,再用函数指针调用a,调用完毕后再加密回去;
- 编译;
- 编写脚本(我习惯用py),对编译后的ELF对应地址加密、覆写。
这里要注意,当程序被编译完以后,是无法执行的,如果执行一定会报错,这是因为此时函数a还并未加密,如果对未加密的字节进行解密,它的效果等同于对该字节进行加密,因此程序会因为碰到不认识的机器码而报错。
0x03 实际操作
dynamicDecode.c
1 |
|
可以看到程序逻辑不是特别复杂,主要操作就是获取a的指针,给a所在的页写权限,用循环遍历函数a的每个字节,将所有数据逐字节异或一个0xfc,通过函数指针调用,然后再加密回去,关闭写权限。
最后再main调用b,实际上则是解密a,调用a,加密a。
当然,前面也提到,这个程序编译完是无法运行的:
理由也很简单,gdb进去一看就知道
此时的a是一串莫名其妙的东西。
所以现在要做的是对a函数进行加密。
获取a的地址
将elf文件拖入ida,查看a函数的起始地址和结束地址 0x1169和0x117f
enc_fun.py
1 | ELF_PATH = r'./dynamicDecode' |
此脚本的逻辑很简单,主要是将ELF文件转成hex string处理。这里要注意一点,因为一个地址对应一个字节,而一个字节对应的hex string是两个字符(例如‘A’是0x61,那它的hex string就是“61”),所以所有的地址都需要*2。其他的我批注写的应该还算明白,直接看批注就行了。
加密后的执行结果:
在ida中的函数a:
居然还有几个字节能识别成汇编,不过没什么意义。
0x04 总结
此次尝试主要是实现了我之前一直想要去做的一个想法。当然加密方式可以不必要是简单的异或,也可以是RC4,AES,甚至是RSA。如果想的话,也可以直接用内联汇编写解密函数b,这些都能让函数a的解密难度更上一层楼,而函数a可以是整个程序中的任何一个函数,如果你想可以每个函数都套一层这个,这也就提高了整个二进制文件的逆向难度。