这次分析是参考大神的步骤来的,主要是为了熟练下IDA的使用。
apk下载地址:http://pan.baidu.com/s/1dD357XZ
一、安装apk文件,打开软件如下:
而我们破解的目的就是得到真正的密码。
二、首先使用apktool反编译apk,找到按钮的监听函数,在里面可以看到调用一个check函数来对用户输入进行验证,然后返回结果。
而我们看到这个函数申明为native,所以这个函数的处理逻辑是在so中。
当然这里的破解不是让你直接改smali的判断条件然后反编译这么简单,我们要把真正的密码找出来。
三、使用IDA打开libcrackme.so,找到securityCheck函数的申明:
这里,把v3 按 y 重命名类型为JNIEnv* ,就可以看到调用了GetStringUTFChars把用户输入的字符串转成char,然后和v6进行比较,相等返回1,否则返回0.那么我们看看v6的值是什么?
赶紧把这个密码输进去,,,,然后提示错误。。。
所以并没有这么简单,肯定是在加载so的时候对这个字符串进行了改变,运行的时候这个地方已经变了,所以才不对。
那程序运行后,再附加上去看看这个地址的字符串变成什么了,但是发现一旦附加程序就会退出,所以程序有调试检测的代码。
所以得对.init_array和JNI_OnLoad这两个地方下断点。
这里按SHIFT+F7就可以显示段。
四、还记得上次http://www.alonemonkey.com/linker-load-so.html这篇文章里面说的,如何在.init_array下断点吧。
首先在.init_array断下来,但是发现执行完.init_array段后,程序并没有退出,继续对JNI_OnLoad下断点,发现每次执行到如下位置后程序就会退出。
跟进去看看,原来这里调用了pthread_create函数,因为是在这里创建了线程不断检测调试状态,所以程序一附加就会退出。既然有调试检测,那么肯定会调用fopen,fgets打开进程,并获取进程状态,判断TracerPId的值是不是为0,不为0就是正在调试状态。
接着在fopen的地方下断点,断下来后,查看R0的值:
也就是程序开始读取进程的状态了。
然后找到LR,看看是程序的什么地方调用的。
这里并看不出汇编代码,只知道地址是:AB672420,减去模块加载地址:AB671000 得到文件偏移:1420
也就是这里调用的fopen,查询进程状态的。返回到libcrackme.so继续往下执行,程序接着调用了fgets以及strstr来查找TracerPid的值,虽然我们可以在每次调用的时候去修改TracerPid的值,但是线程在不停的跑,这样是不现实的。
但是发现调用fopen函数的位置在sub_130C这个方法里面,而sub_130C是在这里调用的:
直接把这个函数去掉不就行了,找到这个函数的地址000016B8,然后用winhex打开找到二进制代码,把原来的13 FF FF EB改成00 00 A0 E1,因为ARM是没有单独的NOP指令的。于是我们采用movs r0,r0作为NOP。对应的机器码为”00 00 A0 E1”。再来看已经 没有了:
改完之后重新保存,回编译,再次签名运行,再附加上去已经没有调试检测了。
五、再次找到原来v6指向的字符串。
输入aiyou,bucuoo 验证成功!
六、kill大法。
该技巧是QEver 在《MSC的伪解题报告》中提到的。利用kill我们可以让程序挂起,然后用ida挂载上去,获取有用的信息,然后可以再用kill将程序恢复运行。我们还是拿自毁程序密码这个应用举例,具体实行方法如下:
1 首先用ps获取运行的app的pid。
2 然后用kill -19 [pid] 就可以将这个app挂起了。
3 随后我们用ida attach上这个app。因为整个进程都挂起了,所以这次ida挂载后app并没有闪退。然后就可以在内存中找到答案了。
4 如果想要恢复app的运行,需要将ida退出,然后再使用kill -18 [pid]即可。
忙活了这么久一个kill搞定, 膜拜大牛!
还有一种比较简单的方法就是,插桩,把_android_log_print函数移到下面,打印v6的值。