frida 安卓逆向初探 + 0824 NepCTF2024 部分题目复现
Reverse
ezAndroid
文件备份
apk 的解析在官方 wp 中写的都很详细,这里就不重新写一遍。总之,在和 ddms/monitor 怎么样都不能显示进程之后用 frida 试了一下,最后也是功成圆满做了出来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| Java.perform(function() { Process.enumerateModules({ onMatch: function(module) { if (module.name.startsWith('lib')) console.log(module.name); }, onComplete: function() { console.log('end'); } });
const libc = Module.findBaseAddress('libc.so'); if (libc) { console.log('libc.so base address:', libc);
const randPtr = Module.findExportByName('libc.so', 'rand');
if (randPtr) { console.log('rand() address:', randPtr);
Interceptor.attach(randPtr, { onLeave: function(retval) { console.log('rand() returned ', retval.toInt32()); } }); } else { console.error('rand() not found in libc.so!'); } } else { console.error('libc.so not found!'); }
const mamba = Module.findBaseAddress('libBlackMamBa.so'); if (mamba) { console.log('libBlackMamBa.so base address:', mamba); var funcaddr = mamba.add(0x8b4); console.log(funcaddr); if (funcaddr != null) { Interceptor.attach(funcaddr, { onEnter: function(args) { console.log('arg0:', args[0]); console.log('arg1:', args[1]); console.log('arg2:', args[2]); }, onLeave: function(retval) { console.log(retval.toInt32()); } }) } var dumpaddr = mamba.add(0x978); Interceptor.attach(dumpaddr, { onEnter: function(args) { const context = this.context; console.log('tmp1=v5 = ', context.x22); } }); var dumpaddr = mamba.add(0x9a4); Interceptor.attach(dumpaddr, { onEnter: function(args) { const context = this.context; console.log('rand mod = ', context.x8.toInt32()); } }); var dumpaddr = mamba.add(0x9e4); Interceptor.attach(dumpaddr, { onEnter: function(args) { const context = this.context; console.log('tmp1 = ', context.x8); } }); var dumpaddr = mamba.add(0x9c0); Interceptor.attach(dumpaddr, { onEnter: function(args) { const context = this.context; console.log('tmp2 = ', context.x8); } });
} else { console.error('libBlackMamBa.so not found!'); } });
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| import frida import sys
JS_CODE = ''.join(open('test.js').readlines())
def on_message(message, data): print(message)
def main(): device = frida.get_usb_device()
mode_start = False
if mode_start == True: pid = device.spawn(["com.example.test"]) session = device.attach(pid) else: session = device.attach("test")
script = session.create_script(JS_CODE)
script.on("message", on_message)
if mode_start == True: device.resume(pid)
script.load()
print("Press Ctrl+C to stop...") sys.stdin.read()
if __name__ == "__main__": main()
|
感觉总体上用 frida 去 hook 某些地址上某些寄存器的值/函数的返回值都有一些固定的模板?问大语言模型应该都能找到用例。
这个脚本主要 hook 后就做了两件事:确定输入字符串是如何转化为数值的和异或加密的 rand() 返回值,最后可以发现是将输入字符串假定为二进制字符串,最后加密和数值比较。frida 的操作流程在 52 上能找到很详细的解析,这里也不重复了。
除此之外,在 python 脚本里还有一个细节:script.load() 需要放在 device.resume() 上,因为 apk 中导入 so 文件是在类创建之后才进行的。
easyobf
附件
用 cutter 可以恢复 __libc_start_main 和 main,把 main 当中的巨量花指令 patch 完就可以分析了(真轻松呢)
patch结果
官方 wp 说里面还混了 ollvm,后面分析感觉是其它静态链接的函数被 ollvm 了?main 里面貌似没有这种特征。
Pwn
附件合集
NepBox
用 ptrace 实现的简单沙箱,侧信道即可完成 orw
作为第一次打的比赛,当时遇到一大串的 ptrace 人都晕了,现在看来其实非常 simple,还是要大胆尝试
NepSSH
sigaction 实现了自定义的 signalid 10 的处理程序(一个简单的自创 malloc-free),可以实现任意地址分配。docker 一直起不来最后就没有做测试。
一个小趣事:这题是通过 socket listen 实现的主逻辑,如果有可能的话这题开两个不同的连接会触发非线程安全的 bug,但是这里不能利用。
FileManager
非常有意思的题,在 pwn 里考察了 /proc 伪文件系统的利用,具体可以参考官方 wp
hrpos
一个伪语言解释器,逆几下会发现这个语言支持 malloc/free 的同时有很严重的 UAF 问题,但是最后懒得写交互了
godrouter
web pwn,利用点在注册时构造一下用户名密码可以导致 sprintf 取出用户权限时字段被覆盖,后面再根据用户名留了一个格式化字符串,但是也没写完