赤座灯里 Android逆向合集

来自吾爱论坛里的赤座灯里牛牛出的Android题,出的题都太有水平了,不会做(笑死)。

1. 一枚安卓Crackme

看到MainActivity的代码流程,第一思路就是逆向算法。

发现还是错误。查看一下布局发现输入框是这个输入框,但是按钮的id却不相等,也就是说正确的按钮根本不是MainActivity中的按钮,自然不会走该按钮的逻辑。

搜索“R.id.v”无果,仔细看MainActivity发现它继承的是Activity类,而不是我们经常看到的AppCompatActivity。进去Activity发现暗藏玄机,几乎所有代码都在lib0.0.so中实现。

进入lib0.0.so文件,查找使用Native层实现的setContentView(),按钮id对应上了。

在内部类中做了一个初始化操作,查看字节数组发现是变种Base64。

onClick()也是使用JNI实现的。

往下拉提示解密过后的字符串为52pojie2020CM

中间就是加密过程了,泻药真的看不懂算法。这其实是Base64逆算法。

但从掌握的信息就可以猜到应该是输入的字符串通过变种Base64解密后得到52pojie2020CM

2. 丢个大佬萌秒破的CM

这回可不能给它骗了。

如果输入框中不是“Congratulations~!”,就执行if语句,加载So文件,执行So中的x()方法。接下来进入So世界。

因为我的手机的是arm64-v8a架构的,所以使用的是资源id为0x7f0c0001,但它的资源名实在是太大众化了,那就直接查看res/raw/目录下的文件,有两个文件,一个是x.y,另一个是y.z。盲猜一个是32位另一个是64位。将两个文件分别以二进制的形式与0xA进行异或,得到是ELF文件格式。使用IDA打开知道y.z是64位ELF文件。其实getCacheDir()函数获取的目录就是/data/user/0/com.akari.crackme/cache/,解密后的So文件就在其中,以输入的字符串命名。使用此目录,不需要申请文件读写权限。

查找xx()方法无果,看JNI_OnLoad()

在输入框中输入“WELL_DONE~!”还是不对。pbBuf要如何等于“WELL_DONE~!”呢?查看交叉调用发现它在JNI_OnLoad()中被调用到。原来JNI_OnLoad()还做了一个RC4加解密操作。解析 RC4 加密算法(C语言、python)

初始化pbBuf变量:

pbBuf是输入字符串的16进制形式,进行RC4加密后,pbBuf要等于“WELL_DONE~!”。而“WELL_DONE~!”的16进制形式为57454c4c5f444f4e457e21。

i从1开始,Sbox数组初始化为[0,255]共256个字节,j初始化为0x76,k设置为vvvvvvvvvvvvvvvv。但发现还是不对啊,这字符串根本输入不了好吗。哭了,应该是我分析So文件分析错了。

3. 找自信CM

先略过onClick(),看完onCreate(),它还加载了一个So库,调用了JNI层的showPic()方法。

这个path肯定不是sub_5018()中的path,肯定还经过一些修改。那在执行showPic()函数之前,肯定经过了JNI_OnLoad(),查看一下它做了什么。

猜测在图片中有一个加密的So库,将这个So拿出来。

由于实在不知道它的偏移是多少,所以先对整个JPG文件的字节流按位取反,再搜索ELF文件头找到这个So。原来它的偏移是0x13195。

已知ELF文件头的偏移与ELF文件的大小,可以计算得出,从0x13195到0x17771都是ELF文件的内容。将它拷贝出来使用IDA打开即可。

回看showPic()函数,它调用了这个So的$()函数。这个So没有JNI_OnLoad(),所以直接看$()。原来$()就是JNI_OnLoad()

我傻掉了,不会是动态加载函数吧。根据字符串稀里糊涂地被我找到了一处,但是还有些字符串不知道去了哪里。

也就是check()的返回值是666,因此byte_5084一定为false,它说它自己是char类型啊喂?

…然后呢??尝试hook times变量绕过if判断,使用Frida调试时发现出错,一开始还以为是程序自己的So库与系统So库同名导致的,但修改So库名称后还是出现同样的错误,后来怀疑是反调试,但在我们分析的过程中并没有遇到反调试。查资料说有可能是Android 8.1.0系统的问题,把系统刷回7.1.1完美解决。但我们咋办…

很想知道当times为666时,输入666会怎么样呢?

4. 找自信安卓CM

在导出表中并没有找到eq()函数,所以很可能在JNI_OnLoad()中动态注册。进入JNI_OnLoad(),发现它进行了ptrace()反调试。如果检测到调试器则进入假函数中。

我们去真函数中看一下。

这个算法应该看懂了,它的意思就是将输入的字符串以“-”分割成三块,这三块要符合以下条件:

1
v11[0] ^ 5 + v11[1] ^ 5 == v11[2] ^ 5

这个只能遍历了。遍历了一下午…去找了预备役数学老师,发现根本没有这样的正整数好吗,这是费马大定理啊!

也就是说这个条件是不成立的,我们找错地方了。

如果实在看不懂它的算法就只能通过hook ptrace()将它的返回值修改为0,使得执行流程转到真函数中再进行动态调试。(不过这道题动态调试也并没有什么用,本质还是逆算法)

如何hook ptrace()函数成为我们要关注的对象。

首先ptrace()函数应该是在libc.so中,编写js脚本通过frida找到ptrace()在内存中的地址。可以看到有两个,其中ptrace()调用的就是__ptrace(),在上面的源码中也能看到。

我们应该不需要深入到__ptrace(),那就直接hook ptrace()

返回值为-1,刚好对应上我们在IDA看到如果小于等于-1则进入FAKE FUNCTION中。那现在直接将函数的返回值置为0,在IDA中去到真和假函数的起始处分别下断点。

使用frida的spawn模式启动App,因为ptrace()在程序启动时就已经进行检测了。使用IDA进行调试。在启动时遇到:

1
armlinux debugger: more than one special register present

一般是server端不匹配,选择android_server就可以用了。

当被检测到ptrace()小于等于-1时,会去到off_1DB4中。

救命无论怎么都去到假函数咋办,我脚本写错了吗

5. 萌新发个有奖安卓cm

这里继承的Activity并没有找到,应该是没做什么吧。

先踩一下坑。被调戏了。

后面不知道怎么做了。楼主写了WP ,看不懂就是了。