本文为 Android CTF 系列的第 2 篇文章,祝您阅读愉快。
本系列文章的目的为:在读者拥有一定编程/代码审计知识的情况下,使读者拥有解出大多数Android CTF题的思路。如读者对系列中提到的工具或知识不了解,可以点击下方合集查看往期文章,您需要的所有内容都在此处。
如您喜欢,请点赞在看关注,这是对笔者的最大支持,也是让笔者加速更新的动力源泉。
开源是一种精神,它为自由平等而生。
维护一个共享、有序、安全、开源、高质量、低抄袭的技术学习环境,人人有责。
1 读题-确定方向
老规矩看源码+模拟器打开APP。
模拟器里的APP很直白:
源码也很直白,JNI是只有用到Native层才会出现的:
展开一看,毫无疑问是libNative.so,而注册方法名为getResult:
对Native函数不了解的读者可以查看我之前写的这几篇文章,从逆向分析和书写两个角度都讲过:
把so文件解压出来使用IDA打开,先在左边的函数列表里搜索方法名,这里也是很直白静态注册了:
2 分析Native函数
移动到此方法按下F5查看反汇编c代码。先补全第一个入参a1为JNIEnv*,以便IDA优化c代码。补全后的函数如下所示:
看了一波,感觉是要强行还原算法,一种刷力扣题的恶心感翻了上来。。。
先梳理一遍最外层的流程。
v5、v6、v7分别是一个unsigned int长度的char指针。
经过Init函数之后,判断First(v5)是否为真(我看了一下,就是对比值的函数),为真则继续。
将v6的每一个char与v5异或然后赋值回v6。将v6与a5(定义在其它地方)比较,如相同则继续。
将v7的每一个char与v6异或然后赋值回v7,然后返回v7与常量字符串的strcmp值。
先来分析Init函数:
这个函数写得十分拗口,但是实际上就是将a4均匀分配到result、a2、a3中(原始函数中的v5、v6、v7),最后再填充一个0(因为result的类型错误,修正为char*就能看多了)。由于a5的长度已知为15,则三个输出长度都为5+1=6,最后的值为 。
分割之后,v5进入First函数并需要返回True。First函数就很简单:
对每个输入char*2再与0x80异或,如果结果为LN^dl即为真(不包括最后一位)。
简单倒推一下即可:
for i in "LN^d":
print(chr(int((ord(i)^0x80)/2)),end="")
print("l")
返回值为fgorl,则原始v5为fgorl,而此函数传值为char指针,则执行后v5会变为LN^dl。
继续往下算v6。v6^v5=a5,a5定义如下:
为 (空格),5,-,0x16,0x61, 。
照样反过来:
li=[" ","5","-",chr(0x16),chr(0x61)]
li2="LN^dl"
for i in range(4):
print(chr(ord(li[i])^ord(li2[i])))
print(chr(0x61))
输出为l{sra。
v7的算法和v6一模一样,我省略过程了,结果为asoy}
最后一次排列v5v6v7,得到flag为:
flag{sosorryla}
原文始发于微信公众号(重生之成为赛博女保安):一文掌握简单Native分析:以攻防世界#Android2.0为例
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论