r0tracer分析学习

这个就是个frida脚本,可以从中学习到一些hook思路

1. javahook(white, black, target = null)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
function javahook(white, black, target = null) {
console.Red("start")
if (!(target === null)) { // target非空。
console.LightGreen("Begin enumerateClassLoaders ...")
Java.enumerateClassLoaders({ // 枚举 Java VM 中存在的类加载器
onMatch: function (loader) { // 把找到的目标设置到loader里面
try {
if (loader.findClass(target)) {
console.Red("Successfully found loader")
console.Blue(loader);
Java.classFactory.loader = loader; // Frida中的一个类加载器,它可以在运行时动态地加载Java类。
console.Red("Switch Classloader Successfully ! ")
}
}
catch (error) {
console.Red(" continuing :" + error)
}
},
onComplete: function () {
console.Red("EnumerateClassloader END")
}
})
}

console.Red("Begin Search Class...")
var targetClasses = new Array();
Java.enumerateLoadedClasses({ // 枚举现在加载的类,和上面不同
onMatch: function (className) {
if (className.toString().toLowerCase().indexOf(white.toLowerCase()) >= 0 &&
(black == null || black == '' || className.toString().toLowerCase().indexOf(black.toLowerCase()) < 0)) {
console.Black("Found Class => " + className)
targetClasses.push(className);
traceClass(className); // trace!
} // 每找到一个就加入targetClasses中,然后执行traceClass
}, onComplete: function () {
console.Black("Search Class Completed!")
}
})

var output = "On Total Tracing :" + String(targetClasses.length) + " classes :\r\n";
targetClasses.forEach(function (target) {
output = output.concat(target);
output = output.concat("\r\n")
}) // 输出相关信息
console.Green(output + "Start Tracing ...")
}

进行最初始的追踪类:

先从JVM中开始寻找目标类

然后再扫描全部已经加载的类(这样会节约不少时间,但是坏处是有的时候会hook不上某些方法),通过处理获得类名然后释放(节约内存空间),再将类名加入一个Array中

每加入一个类每个类进行进一步的操作:traceClass(className);

2. traceClass(targetClass)

1
2
3
4
5
6
7
8
9
10
11
function traceClass(targetClass) {  // 通用模块,支持安卓和ios
if (Java.available) {
Java.perform(function () { // hook中再hook?
JavaTraceClass(targetClass)
})
} else if (ObjC.available) {
IosTraceClass(targetClass)
} else {
console.log("please connect to either iOS or Android device ...")
}
}

3. JavaTraceClass(targetClass)

我超这作者居然写注释,爱了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function JavaTraceClass(targetClass) {
//Java.use是新建一个对象哈,大家还记得么?
var hook = Java.use(targetClass);
//利用反射的方式,拿到当前类的所有方法
var methods = hook.class.getDeclaredMethods();
//建完对象之后记得将对象释放掉哈
hook.$dispose;
//将方法名保存到数组中
var parsedMethods = [];
var output = "";
output = output.concat("\tSpec: => \r\n")
methods.forEach(function (method) {
output = output.concat(method.toString())
output = output.concat("\r\n")
parsedMethods.push(method.toString().replace(targetClass + ".", "TOKEN").match(/\sTOKEN(.*)\(/)[1]);
});
//去掉一些重复的值
var Targets = uniqBy(parsedMethods, JSON.stringify);
// targets = [];
var constructors = hook.class.getDeclaredConstructors(); // 构造函数
if (constructors.length > 0) {
constructors.forEach(function (constructor) {
output = output.concat("Tracing ", constructor.toString())
output = output.concat("\r\n")
})
Targets = Targets.concat("$init")
}
//对数组中所有的方法进行hook,
Targets.forEach(function (targetMethod) {
traceMethod(targetClass + "." + targetMethod);
});
//画个横线
for (var p = 0; p < 100; p++) {
output = output.concat("+");
}
console.Green(output);
}

这个函数依次对目标类进行所有方法名字(包括构造方法)记录,然后去重,再依次hook(traceMethod)

4. traceMethod(targetClassMethod)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// trace单个类的所有静态和实例方法包括构造方法 trace a specific Java Method
function traceMethod(targetClassMethod) {
var delim = targetClassMethod.lastIndexOf(".");
if (delim === -1) return;
var targetClass = targetClassMethod.slice(0, delim) // 得到类名
var targetMethod = targetClassMethod.slice(delim + 1, targetClassMethod.length) // 得到方法名
var hook = Java.use(targetClass);
if (!hook[targetMethod]) { // 尝试hook,尝试得知方法
return;
}
var overloadCount = hook[targetMethod].overloads.length; //获得目标方法的数目?
console.Red("Tracing Method : " + targetClassMethod + " [" + overloadCount + " overload(s)]"); // 输出信息
for (var i = 0; i < overloadCount; i++) {
hook[targetMethod].overloads[i].implementation = function () {
//初始化输出
var output = "";
//画个横线
for (var p = 0; p < 100; p++) {
output = output.concat("==");
}
//域值
if (!isLite) { output = inspectObject(this, output); }
//进入函数
output = output.concat("\n*** entered " + targetClassMethod);
output = output.concat("\r\n")
// if (arguments.length) console.Black();
//参数
var retval = this[targetMethod].apply(this, arguments);
if (!isLite) {
for (var j = 0; j < arguments.length; j++) {
output = output.concat("arg[" + j + "]: " + arguments[j] + " => " + JSON.stringify(arguments[j]));
output = output.concat("\r\n")
}
//调用栈
// 通过hook安卓自带的Log来获取堆栈信息
// 创建的一个新的Throwable对象。Throwable是Java中用于表示异常的基类,包含相关异常信息,包括堆栈跟踪。
output = output.concat(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
//返回值
output = output.concat("\nretval: " + retval + " => " + JSON.stringify(retval));
}
// inspectObject(this)
//离开函数
output = output.concat("\n*** exiting " + targetClassMethod);
//最终输出
// console.Black(output);
var r = parseInt((Math.random() * 7).toFixed(0));
var i = r;
var printOutput = null;
switch (i) {
case 1:
printOutput = console.Red;
break;
case 2:
printOutput = console.Yellow;
break;
case 3:
printOutput = console.Green;
break;
case 4:
printOutput = console.Cyan;
break;
case 5:
printOutput = console.Blue;
break;
case 6:
printOutput = console.Gray;
break;
default:
printOutput = console.Purple;
}
printOutput(output);
return retval;
}
}
}

就是hook java层的方法,可惜也只是在java层了。

有意思的是获得调用栈的方式:

  1. 通过hook安卓自带的Log来获取堆栈信息

  2. 创建的一个新的Throwable对象。Throwable是Java中用于表示异常的基类,包含相关异常信息,包括堆栈跟踪。