在对基于 Android NDK 的 APP 进行逆向之前,我们首先要熟悉 ARM 汇编
一个还不错的 ARM 汇编博客
这次的 APP 是《Android 软件安全与逆向分析》中的教学 APP
我们的目标是破解这个 APP,在没有注册码的情况下也能使用程序的完整功能
首先我们用 jadx-gui 查看程序的结构
看到程序有 5 个类,其中我们感兴趣的是 MainActivity、MyApp 和 RegActivity,先来分析这三个
1. MainActivity
这个启动程序后看到的第一个 Activity,这个 Activity 通过判断 Application 里的 m 值来确定显示的字符串是什么版本的,如果是未注册版则会调用 doRegister 函数,要求用户注册。
一个自然的想法是在 Smali 文件中修改 m 值,尝试后发现只能修改显示的字符串,但并不能用到完整的功能
2. MyApp
这个类继承于 Application 类,所以这个类会在应用开始时首先加载。一般来说,继承于 Application 类的类会对程序做一些初始化的操作。例如这里加载了共享库 "juan" 并调用了原生函数 initSN
3. RegAcitivity
这是用于注册的 Activity,由于我们是要破解这个 APP,所以并不关心这个 Activity
分析完这三个类之后,发现破解的重点在于 initSN 函数,从函数名上看,这是用于初始化序列号的,我们只要修改这个函数,使所有的字符串都能用于注册就应该能够达到破解的目的
用 IDA 载入共享库
- 现在函数列表里找找有没有看起来像 initSN 的函数,通常来说如果这些函数是静态注册的,那么在函数列表中就会有函数名类似于 “包名_函数名” 的函数,仔细查找后并没有发现
- 如果这些函数不是静态注册的,那么只能是在 JNI_OnLoad 函数中注册了其他函数与原 JAVA 层中的函数相关联
- 查看 JNI_OnLoad 函数,导入 jni.h 并在结构体中导入 JNINativeMethod 和 JNINativeInterface 结构体
-
F5
-
看到第 13 行调用了 RegisterNatives 函数,在 jni.h 文件中查找 RegisterNatives 函数
- 从 RegisterNatives 函数的原型可以看到,函数接受三个参数,其中第三个参数是 JNINativeMethod 结构体数组,对应于程序中的实参就是 _data_start
-
双击 _data_start
可以看到在 JNI_OnLoad 函数中将 initSN 函数映射成了 n1 函数,将 saveSN 函数映射成了 n2 函数,将 work 函数映射成了 n3 函数
-
查看 n1 函数
- 根据刚开始的分析,这里的 result 很大可能就是 Application 里的 m 值。而且,这段代码的判断逻辑有点特别,它读取
/sdcard/reg.dat
文件中的内容,将内容先与 “25d55ad283aa400af464c76d713c07ad” 相比较,如果相等则将 result 赋为 1,不相等则继续下面的比较,直到判断是否与 “18e56d777d194c4d589046d62801501c” 相等,是则将 result 赋为 4,否则将 result 赋为 0,即试用版 - 分析出判断逻辑之后,我们其实只要把最后一次判断的结果改为恒为相等就行了,找到对应的汇编代码
将CMP R0, #0
修改为CMP R0, R0
,这样判断结果就恒为真了 - 记下这条语句的地址
0x000015FC
,用 WinHex 打开该 so 文件,定位到该地址,将00 00 50 E3
(CMP R0, #0
对应的机器码)修改为00 00 50 E1
(CMP R0, R0
对应的机器码),并保存 - 重新打包 APK 并签名,破解成功