android使用frida脚本追踪方法调用,方法之间的关系一目了然。
在 android逆向__超级好用,使用frida追踪方法 一文中,介绍了如何使用firda脚本追踪android的方法调用,在实际的使用中,方法被调用,就会在终端打印方法的开始标记、方法的参数和方法的结束标记。但是看起来有些凌乱:
*** entered com.test.flyer.MainActivity$1.onClick
arg[0]: android.support.v7.widget.AppCompatButton{4267c9c8 VFED..C. ...P.... 0,0-264,144 #7f070022 app:id/button}
*** entered com.test.flyer.MainActivity.test
arg[0]: Jack
*** entered com.test.flyer.MainActivity.gainAge
arg[0]: 16
retval: 26
*** exiting com.test.flyer.MainActivity.gainAge
*** entered com.test.flyer.MainActivity.gainEnjoy
arg[0]: 篮球
retval: 我喜欢篮球
*** exiting com.test.flyer.MainActivity.gainEnjoy
retval: OK
*** exiting com.test.flyer.MainActivity.test
retval: undefined
*** exiting com.test.flyer.MainActivity$1.onClick
截图效果:
在这样一堆打印中,我们不能清晰的分辨各个方法的嵌套关系,总之一句话,就是不够醒目。于是乎对 raptor_frida_android_trace.js 脚本做了改进,下面是部分内容:
var logContentArray = new Array();
var singlePrefix = "|----"
// find and trace all methods declared in a Java Class
function traceClass(targetClass)
{
var hook = Java.use(targetClass);
var methods = hook.class.getDeclaredMethods();
hook.$dispose;
var parsedMethods = [];
methods.forEach(function(method) {
parsedMethods.push(method.toString().replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1]);
});
var targets = uniqBy(parsedMethods, JSON.stringify);
targets.forEach(function(targetMethod) {
traceMethod(targetClass + "." + targetMethod);
});
}
// trace a specific Java Method
function traceMethod(targetClassMethod)
{
var delim = targetClassMethod.lastIndexOf(".");
if (delim === -1) return;
// slice() 方法可提取字符串的某个部分,并以新的字符串返回被提取的部分
var targetClass = targetClassMethod.slice(0, delim)
var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length)
var hook = Java.use(targetClass);
var overloadCount = hook[targetMethod].overloads.length;
console.log("Tracing " + targetClassMethod + " [" + overloadCount + " overload(s)]");
for (var i = 0; i < overloadCount; i++) {
// hook方法
hook[targetMethod].overloads[i].implementation = function() {
var logContent_1 = "entered--"+targetClassMethod;
var prefixStr = gainLogPrefix(logContentArray);
logContentArray.push(prefixStr + logContent_1);
console.warn(prefixStr + logContent_1);
// print backtrace, 打印调用堆栈
// Java.perform(function() {
// var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
// console.log(prefixStr +"Backtrace:" + bt);
// });
// print args
// if (arguments.length) console.log();
// 打印参数
for (var j = 0; j < arguments.length; j++)
{
var tmpLogStr = prefixStr + "arg[" + j + "]: " + arguments[j];
console.log(tmpLogStr);
logContentArray.push(tmpLogStr);
}
// print retval
var retval = this[targetMethod].apply(this, arguments); // rare crash (Frida bug?)
// 打印返回值
// console.log("\n"+ targetClassMethod +"--retval: " + retval);
var tmpReturnStr = prefixStr + "retval: " + retval;
logContentArray.push(tmpReturnStr);
console.log(tmpReturnStr);
//结束标志
var logContent_ex = "exiting--" + targetClassMethod;
logContentArray.push(prefixStr + logContent_ex);
console.warn(prefixStr + logContent_ex);
return retval;
}
}
}
// 获取打印前缀
function gainLogPrefix(theArray)
{
var lastIndex = theArray.length - 1;
if (lastIndex<0)
{
return singlePrefix;
}
for (var i = lastIndex; i >= 0; i--)
{
var tmpLogContent = theArray[i];
var cIndex = tmpLogContent.indexOf("entered--");
if ( cIndex == -1)
{
var cIndex2 = tmpLogContent.indexOf("exiting--");
if ( cIndex2 == -1)
{
continue;
}
else
{
//与上个方法平级
var resultStr = tmpLogContent.slice(0,cIndex2);
return resultStr;
}
}
else
{
//在上个方法的内部
var resultStr = singlePrefix + tmpLogContent.slice(0,cIndex);//replace(/entered--/, "");
// console.log("("+tmpLogContent+")前缀是:("+resultStr+")");
return resultStr;
}
}
return "";
}
// usage examples
setTimeout(function() { // avoid java.lang.ClassNotFoundException
Java.perform(function() {
trace("com.test.flyer.MainActivity");
// trace("com.test.flyer.MainActivity.gainAge");
// trace("com.target.utils.CryptoUtils.decrypt");
// trace("com.target.utils.CryptoUtils");
// trace("CryptoUtils");
// trace(/crypto/i);
// trace("exports:*!open*");
});
}, 0);
js文件内,主要新增 gainLogPrefix方法和 gainLogPrefix_Module方法,用于生成"|----"标记。
使用新的js追踪方法,终端打印如下:
|----entered--com.test.flyer.MainActivity$1.onClick
|----arg[0]: android.support.v7.widget.AppCompatButton{426a5f40 VFED..C. ...P.... 0,0-264,144 #7f070022 app:id/button}
|----|----entered--com.test.flyer.MainActivity.test
|----|----arg[0]: Jack
|----|----|----entered--com.test.flyer.MainActivity.gainAge
|----|----|----arg[0]: 16
|----|----|----retval: 26
|----|----|----exiting--com.test.flyer.MainActivity.gainAge
|----|----|----entered--com.test.flyer.MainActivity.gainEnjoy
|----|----|----arg[0]: 篮球
|----|----|----retval: 我喜欢篮球
|----|----|----exiting--com.test.flyer.MainActivity.gainEnjoy
|----|----retval: OK
|----|----exiting--com.test.flyer.MainActivity.test
|----retval: undefined
|----exiting--com.test.flyer.MainActivity$1.onClick
截图效果:
看起来顺眼多了,哈哈哈,希望各位大佬能喜欢。
本次测试项目和js脚本获取方式:
1、关注公众号"逆向APP",回复"frida追踪方法02"
2、github地址,可以帮忙点个 Star