一、代码注入法--让程序自己吐出注册码
利用android SDK的android.util.Log类输出调试信息,有Log.v(),Log.i(),Log.d(),Log.w(),Log.e()五个调试信息的输出方法,v表示verbose类型信息、d表示debug类信息、i表示info类信息、w表示warn类型信息、e表示error信息。
DDMS提供了LogCat窗口来显示Log类的输出信息,可通过命令:adb logcat -s NS:V 其中NS是调试信息的Tag标志
例子:
这个例子为输入正确的用户名密码可以登陆成功,静态反编译是看不到数据的,除了动态调试外还可以用代码注入法让程序自己输出用户名密码。
这时候就用到了Log.v()这个日志输出函数了,反编译apk得到smail文件修改关键地方(我们想要看的寄存器的值),在关键比较函数之前,在获得我们想要的数据之后插入Log.v()函数获得我们想要的寄存器的值。
看反编译结果,找到关键函数,确定有我们想要数据的寄存器,插入代码获取数据:
通过在关键地方注入一段输出Log的代码,输出我们想要的寄存器的数据。因为用的是新的局部变量v2、v3所以将寄存器数量.locals 2 改为.locals 4,不然程序会崩溃。
修改完代码后保存,重打包,签名apk,再次运行,输入任意用户名密码,虽然输出的仍然是登陆失败,但是Log.v()方法已经将正确的用户名密码输出了。
在命令行执行 adb logcat –s SN:v 输出信息如下:
二、栈跟踪法
通过代码注入法屡试不爽,但是需要人工阅读大量的反汇编代码,可能需要多次代码注入,对于大型程序可谓是一件苦差事,栈跟踪方法是向反汇编后的代码中插入栈跟踪信息输出的代码,想要知道某个函数或某个组件(Toast)何时被调用,只需要在其smail代码后面加入栈跟踪输出函数代码:
new Exception("print trace").printStackTrace();
转换为smail代码:
new-instance v0, Ljava/lang/Exception;
const-string v1, "print trace"
invoke-direct {v0, v1}, Ljava/lang/Exception;-><init>(Ljava/lang/String;)V
invoke-virtual {v0}, Ljava/lang/Exception;->printStackTrace()V
此代码可以用工具生成:java2smail
链接: https://pan.baidu.com/s/103-gsuXOq5Xpqmk7TBIt7A 提取码: nwvk
如原来:
修改后:
保存打包后安装运行,用logcat查看信息:
adb logcat -s System.err:V*:W
如图:调用顺序从程序启动到代码注入的地方,从下到上
看到调用顺序是MainActivity调用的。对于调用复杂的也可以显示。
其他动态调试方法Method Profiling等,持续更新中。。。。。。