首页
社区
课程
招聘
[原创]使用 frida 追踪android方法调用_超级醒目
发表于: 2019-6-26 23:35 22819

[原创]使用 frida 追踪android方法调用_超级醒目

2019-6-26 23:35
22819

先看frida追踪方法调用,打印效果,超级醒目:


https://github.com/luoyanbei/android_frida_trace_myTrace

https://github.com/0xdea/frida-scripts




点击"测试"按钮,触发onClick点击事件,调用test方法,test方法内部又分别调用了gainAge和gainEnjoy方法。
方法的调用层级、参数和返回值,都一目了然。使用frida调试是相当happy!!!


本次测试项目包名是 com.example.king.testappsflyer,frida的js脚本主要代码:

var logContentArray = new Array();

var singlePrefix = "|----"


// 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");
	});   
}, 0);

终端内输入命令,启动app时加载js脚本:

frida -U -f com.example.king.testappsflyer --no-pause -l raptor_frida_android_trace.js

开始追踪 MainActivity 内的方法。



点击“测试”按钮,就能在终端内看到打印信息:


|----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地址:

https://github.com/luoyanbei/android_frida_trace_myTrace



本文原始js脚本地址:

https://github.com/0xdea/frida-scripts








点击"测试"按钮,触发onClick点击事件,调用test方法,test方法内部又分别调用了gainAge和gainEnjoy方法。
方法的调用层级、参数和返回值,都一目了然。使用frida调试是相当happy!!!


本次测试项目包名是 com.example.king.testappsflyer,frida的js脚本主要代码:

var logContentArray = new Array();

var singlePrefix = "|----"


// 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");
	});   
}, 0);

var logContentArray = new Array();

var singlePrefix = "|----"


// 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");
	});   
}, 0);

终端内输入命令,启动app时加载js脚本:

frida -U -f com.example.king.testappsflyer --no-pause -l raptor_frida_android_trace.js

开始追踪 MainActivity 内的方法。



点击“测试”按钮,就能在终端内看到打印信息:


|----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地址:

https://github.com/luoyanbei/android_frida_trace_myTrace



frida -U -f com.example.king.testappsflyer --no-pause -l raptor_frida_android_trace.js

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2019-7-5 17:01 被luoyanbei编辑 ,原因:
收藏
免费 7
支持
分享
最新回复 (28)
雪    币: 181
活跃值: (621)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
好评
2019-6-26 23:44
0
雪    币: 1392
活跃值: (5177)
能力值: ( LV13,RANK:240 )
在线值:
发帖
回帖
粉丝
3
不错~
2019-6-27 08:04
0
雪    币: 6580
活跃值: (4536)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
4
收藏备用
2019-6-27 09:44
0
雪    币: 634
活跃值: (1503)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
5
Nice,不过没有处理多线程的情况。
2019-6-27 10:49
0
雪    币: 3032
活跃值: (3366)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
6
跟erlang 中的recon_trace 类似
2019-6-27 12:47
0
雪    币: 916
活跃值: (3434)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
7

这样够直观吗
图片描述

2019-6-28 01:25
0
雪    币: 1696
活跃值: (2297)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
葫芦娃 这样够直观吗 ![图片描述](upload/attach/201906/715510_HJZKBM6FXSD7UAC.png)
求大佬分享
2019-6-28 08:12
0
雪    币: 2686
活跃值: (1020)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
9
葫芦娃 这样够直观吗 ![图片描述](upload/attach/201906/715510_HJZKBM6FXSD7UAC.png)
很清晰直观,求大佬分享
2019-6-28 08:36
0
雪    币: 120
活跃值: (1597)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
感谢分享,简直就是神器!
2019-6-28 09:54
0
雪    币: 916
活跃值: (3434)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
11

最后于 2019-6-28 10:42 被葫芦娃编辑 ,原因:
2019-6-28 10:40
0
雪    币: 4831
活跃值: (479)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
https://www.frida.re/docs/examples/android/  测试这个例子  有点问题
[Xiaomi MI 8::com.example.seccon2015.rock_paper_scissors]-> is--java
is--java--2--com.example.seccon2015.rock_paper_scissors.MainActivity$1
TypeError: cannot read property 1 of null
    at [anon] (duk_hobject_props.c:2384)
    at [anon] (/repl1.js:61)
    at frida/node_modules/frida-java/index.js:181
    at forEach (native)
    at /_java.js:2327
    at frida/node_modules/frida-java/index.js:109
    at trace (/repl1.js:69)
    at [anon] (/repl1.js:340)
    at frida/node_modules/frida-java/lib/vm.js:43
    at M (frida/node_modules/frida-java/index.js:347)
    at frida/node_modules/frida-java/index.js:299
    [...]
2019-6-28 17:51
0
雪    币: 2686
活跃值: (1020)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
13
xiaokangpwn https://www.frida.re/docs/examples/android/ 测试这个例子 有点问题 [Xiaomi MI 8::com.example.seccon2015.rock ...
亲测可以啊,我这里没问题。
 js脚本中追踪的类改一下: 
setTimeout(function() { 
	Java.perform(function() {
		trace("com.example.seccon2015.rock_paper_scissors.MainActivity");
	});   
}, 0);

运行app并加载js脚本:
 frida -U -f com.example.seccon2015.rock_paper_scissors --no-pause -l raptor_frida_android_trace_copy.js 
com.example.seccon2015.rock_paper_scissors 是包名,raptor_frida_android_trace_copy.js  是js脚本文件名。

 终端打印:

app启动后界面:


点击"R"按钮,打印如下:



2019-7-2 11:01
0
雪    币: 2155
活跃值: (4532)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
这是什么问题呢
Spawned `com.example.king.testappsflyer`. Resuming main thread!
[Xiaomi Redmi 5A::com.example.king.testappsflyer]-> is--java
is--java--2--com.test.flyer.MainActivity
TypeError: cannot read property 1 of null
    at [anon] (../../../frida-gum/bindings/gumjs/duktape.c:56648)
    at /repl1.js:61
    at frida/node_modules/frida-java/index.js:182
    at forEach (native)
    at /_java.js:2682
    at frida/node_modules/frida-java/index.js:110
    at trace (/repl1.js:69)
    at /repl1.js:340
    at frida/node_modules/frida-java/lib/vm.js:42
    at E (frida/node_modules/frida-java/index.js:348)
    at frida/node_modules/frida-java/index.js:300
    [...]

2019-7-3 15:59
0
雪    币: 2686
活跃值: (1020)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
15
小黄鸭爱学习 这是什么问题呢Spawned&nbsp;`com.example.king.testappsflyer`.&nbsp;Resuming&nbsp;main&nbsp;t ...
电脑端安装的frida版本和手机内的frida-server版本号一致吗?
2019-7-4 14:18
0
雪    币: 2155
活跃值: (4532)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
luoyanbei 电脑端安装的frida版本和手机内的frida-server版本号一致吗?
都是最新版的 
2019-7-4 19:17
0
雪    币: 6267
活跃值: (1082)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
17
https://github.com/0xdea/frida-scripts/blob/master/raptor_frida_android_trace.js 这个应该才是原创脚本吧?
2019-7-5 12:52
0
雪    币: 774
活跃值: (1036)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
Error: Unable to parse ART internals; please file a bug at https://github.com/frida/frida-java 会报错。
最后于 2019-7-5 15:34 被leehero编辑 ,原因:
2019-7-5 15:13
0
雪    币: 2359
活跃值: (288)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
Error: Unable to parse ART internals; please file a bug at https://github.com/frida/frida-java
2019-7-7 18:07
0
雪    币: 574
活跃值: (405)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
好东西,感谢分享
2019-7-11 10:23
0
雪    币: 233
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21
Error: Unable to parse ART internals; please file a bug at https://github.com/frida/frida-java
    at $e (frida/node_modules/frida-java/lib/android.js:2221)
    at Qe (frida/node_modules/frida-java/lib/android.js:1976)
    at frida/node_modules/frida-java/lib/android.js:1506
    at [anon] (native)
    at frida/runtime/core.js:199
    at Be (frida/node_modules/frida-java/lib/android.js:1510)
    at frida/node_modules/frida-java/lib/android.js:2261
    at ue (frida/node_modules/frida-java/lib/android.js:831)
    at /_java.js:2677
TypeError: cannot write property '_code' of null
    at [anon] (../../../frida-gum/bindings/gumjs/duktape.c:57666)
    at Be (frida/node_modules/frida-java/lib/android.js:1510)
    at frida/node_modules/frida-java/lib/android.js:2261
    at ue (frida/node_modules/frida-java/lib/android.js:831)
    at /_java.js:2677
    at frida/node_modules/frida-java/index.js:110
    at trace (/repl1.js:51)
    at /repl1.js:175
    at frida/node_modules/frida-java/lib/vm.js:42
    at E (frida/node_modules/frida-java/index.js:348)
    at frida/node_modules/frida-java/index.js:334
    [...]
2019-7-15 14:36
1
雪    币: 2270
活跃值: (5537)
能力值: ( LV8,RANK:146 )
在线值:
发帖
回帖
粉丝
22
小黄鸭爱学习 这是什么问题呢Spawned&nbsp;`com.example.king.testappsflyer`.&nbsp;Resuming&nbsp;main&nbsp;t ...
您好,请问你的问题解决了吗  我也出现这个问题了
2019-7-29 18:42
0
雪    币: 92
活跃值: (508)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
TypeError: cannot read property 1 of null
    at [anon] (../../../frida-gum/bindings/gumjs/duktape.c:56648)
    at /repl1.js:61
    at frida/node_modules/frida-java-bridge/index.js:180
    at forEach (native)
    at /_java.js:2934
    at frida/node_modules/frida-java-bridge/index.js:228
    at trace (/repl1.js:69)
    at /repl1.js:338
    at frida/node_modules/frida-java-bridge/lib/vm.js:11
    at E (frida/node_modules/frida-java-bridge/index.js:346)
    at frida/node_modules/frida-java-bridge/index.js:298
    [...]
2019-8-17 17:53
0
雪    币: 1985
活跃值: (1831)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
感谢分享,收藏备用。
2020-3-26 02:58
0
雪    币: 522
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
25
大佬,我私聊不了你,但是我报错了 不知道怎么弄,百度也没有
ReferenceError: identifier 'trace' undefined
    at [anon] (../../../frida-gum/bindings/gumjs/duktape.c:83728)
    at /yiyouhui_trace.js:108
    at frida/node_modules/frida-java-bridge/lib/vm.js:11
    at frida/node_modules/frida-java-bridge/index.js:392
    at frida/node_modules/frida-java-bridge/index.js:378
    at we (frida/node_modules/frida-java-bridge/lib/class-factory.js:602) 
    at frida/node_modules/frida-java-bridge/lib/class-factory.js:585  
报这个错,不知道怎么解决,大佬有空的话能否看看 谢谢
2020-7-3 20:12
0
游客
登录 | 注册 方可回帖
返回
//