额……实在不知道出什么题,正好最近在看LLVM Pass,就顺手糊了一个题(雾
Usage.md里写了使用方法,实际上就是运行了一个LLVM Pass,处理的ir是main.bc。
主逻辑实际上在EasyPass.so里,main.bc里是一些有用的数据。
题目使用一个LLVM Pass处理了作为ir的main.bc,加密逻辑使用与非虚拟机,通过llvm::Value::getName()
获取ir里的函数名作为opcode,一个函数名加密一位flag,加密逻辑为前半段异或后半段,后半段异或0xff。
想要知道此函数获取了什么数据作为opcode,可以查询LLVM的文档或下载相应版本的LLVM进行调试。
至于如何从main.bc中获取函数名,可以将main.bc编译后获取符号表,按以下方式操作:
1 | $ llc main.bc -o main.s |
或者直接去看main.bc的hex,在偏移为0x1e9e的地方也可获得函数名。
或者也可以调试获取opcode。
由于笨人出题的时候没看LLVM Pass的版本直接出了,导致我出好之后才发现自己的LLVM Pass竟然是10.0.0的远古版本……
而且更诡异的是,新版LLVM Pass竟然不兼容旧版本,所以这个pass是没办法在新版LLVM Pass上跑的……
而且更更诡异的是,后来新的LLVM发行版都没有Ubuntu的,所以笨人直接下的release只有10.0.0的远古版本……………………总而言之都是LLVM的错!
不过其实解题很简单,如果不知道pass里对ir的处理函数做了什么,安装一个10.0.0的LLVM调试一下这个pass就好
解题部分抄自dr3的wp
这可不是我偷懒,是他答应我写的!
如何解题
运行尝试
由于出题人的高超技巧,使得这个pass只能在非常稳(yuan)定(gu)的llvm 10上面运行。然而,由于我的kali最旧最旧的版本也是13,而奇怪的是这个老版本压根就没法运行
反正我是不懂得,没办法,装呗
https://packages.ubuntu.com/focal/llvm-10
直接用这个装就行,带dpkg的都可以
静态分析
进入load函数,可以发现注册了一个pass
最后的pass传到了sub_2660
打开发现函数很大,没法直接反编译
函数的头部全是函数调用。但是仔细看可以发现每个函数都只做了
一件事,就是return了一个1
所以其实就是干扰的花指令,全部nop掉就可以
1 | for i in range(0x2673,0x6C9B): |
之后就可以f5
首先是通过getName找到了16个string放到了数组里
之后进入一个函数对数组进行加密
最后从数组的最后几位进行对比
getName
其实开始不知道得到了什么,不过好在配好了环境可以调试
虽然是llvm加载的so,但是配置好了输入的参数就可以直接进行调试
就像下面这张图一样
之后其实可以发现,那个循环就是获得了函数的名称。
因此,加密之后应该是固定的。只需要把加密算法抠出来,之后直接初始化flag,然后用z3跑一下就能出来了
还有一点就是提取数据的时候注意要小端序
以下是完整脚本
1 | from z3 import * |
然后就可以了
出题部分
实际上出题人没活了,用的还是单指令虚拟机(x
本来预期解是插桩打log观察逻辑,奈何你们逆向壬全是一个z3走天下哼哼,下次必要制裁你们z3党
这里贴个纯享版代码(x,方便以后继续整活(不是
1 |
|
虚拟机的逻辑和预期exp等在github仓库都有,可以自己去看。
产生opcode的代码在miniLctf的github仓库
注意r0和r1作为寄存器是用random生成的,当生成的opcode达到一定数量时就会有很大的概率生成两个一样的字符,而这必然会导致错误(检查了好久问题在哪……)所以生成opcode之后自己看一下,如果一个循环内有一样的寄存器要手动改一下。
总而言之是个简单题 = =
关于本文
本文作者 云之君, 许可由 CC BY-NC 4.0.