准备花一周时间把攻防世界上的mobile方向的题做一做,大部分都是套了个安卓壳子的逆向签到题,这一道比较值得记录。

静态分析

jadx里看到两个activity,进应用入口看一眼

检查myapp类下的m字段的值来决定直接进work还是先注册。估计m是个静态变量,进myapp看看

果然如此,估计initSN在动态加载了so实现,处理的赋值。用ida分析一下链接模块

如果是一般的链接题进ida之后就能在函数栏里看到动态链接的方法,例如com_gdufs_xman_MyApp_initSN,但是这个链接库找不到。JNI_OnLoad是.so文件加载的入口点,趁此看看它做了哪些事:
1 | if ( !(*vm)->GetEnv(vm, (void **)&g_env, 65542) ) |
- 尝试获取JNI环境,65542对应JNI_VERSION_1_6
- 成功获取环境后存储在变量g_env中
1 | j___android_log_print(2, "com.gdufs.xman", "JNI_OnLoad()"); |
- 通过Android日志系统输出调试信息,标签为com.gdufs.xman,级别为2
这时候肯定要问,这个安卓日志系统怎么看?因为这些调试信息我们从来没在使用应用或者frida的CLI里看到过
如果做过正向开发,就知道可以直接在AS的Logcat视图里看到,我们也可以在adb里通过命令行看
1 | adb logcat <log_level>:<tag> |
这个之后再试试。
1 | native_class = (*(int (__fastcall **)(int, const char *))(*(_DWORD *)g_env + 24))(g_env, "com/gdufs/xman/MyApp"); |
通过JNI函数FindClass(g_env+24)查找java类com/gdufs/xman/MyApp
1 | *(int (__fastcall **)(int, int, char **, int))(*(_DWORD *)g_env + 860))( |
通过JNI函数RegisterNatvies(g_env+860)注册本地方法,其中第三个参数off_5004代表方法名数组,第四个参数3代表注册的方法数量。
j___android_log_print就是输出日志。
至此,我们就知道实际上链接加载的方法实现就在off_5004中

.data段,DCD相当于x86中的dd代表一字,相似的还有dcb,dcw,dcq,实际数据长度和db,dw,dq相同——在arm汇编中一字32位,但是表示数据长度的伪指令还是和一字16位的x86相同,真令人迷惑。
很容易看出这其实是三个方法结构体,第一个字段代表返回值,()V就是void,(Ljava/lang/String;)V就是字符串;第二个字段相当于native层中对应的C/C++函数。第三个就是函数地址的修正。所以我们就可以看出:实际上动态链接的三个函数就是链接模块中的n1 n2 n3
我们关注的是initSN,进入n1看看:

原先调用initSN的时候本没有传入参数,但是静态分析很容易就能看出来,a1就是m。函数流程就是读取/sdcard/reg.dat中的内容,与EoPAoY62@ElRD相同则将m赋1。寻找一下关于写入/sdcard/reg.dat的代码,在n2 saveSN中有提及——回头去看MainActivity中的逻辑,会发现saveSN在第一次注册的时候被调用,传入的参数是用户输入的字符串。

看来s就是我们传入的字符串,这个函数会对s挎挎一顿处理,然后将处理后的s写入/sdcard/.reg.dat
鉴于这个处理也不难分析,我通过算法逆向来解题
解题
算法逆向
将原字符串三个三个一组,依次与特定字符异或
简单复制粘贴一下,异或的字符都是固定的:
1 |
|
输出结果:

就是w _ a的循环。
1 | target = 'EoPAoY62@ElRD' |
最终的结果就是201608Am!2333