r0tracer介绍
项目地址
https://github.com/r0ysue/r0tracer
安卓Java层多功能追踪脚本==>相当于 精简版的Objection+Wallbreaker
功能
1、根据黑白名单批量追踪类的所有方法
hook("javax.crypto.Cipher", "$");
|
2、在命中方法后打印出该类或对象的所有域值(包括参数、返回值、调用栈)
3、极简的文本保存日志机制、易于搜索关键参数
4、针对加壳应用找不到类时可以切换Classloader
使用方法
1、修改r0tracer.js
文件最底部处的代码,开启某一个Hook模式。
2、推荐使用Frida14版本,并且将日志使用-o
参数进行输出保存
$ frida -U -f com.r0ysue.example -l r0tracer.js --no-pause -o saveLog5.txt
|
“-f”为Spawn模式,去掉”-f”为Attach模式
3、Frida版本=<12时,要加上--runtime=v8
选项
$ frida -U com.r0ysue.example -l r0tracer.js --runtime=v8 --no-pause -o saveLog6.txt
|
优势
- 比
objection
增加延时spawn
- 比
objection
增加批量hook
类\方法\构造函数
Wallbreaker
在frida14
上还是一直崩
- 比
Wallbreaker
增加hook
看instance
的fields
inspectObject
函数可以单独拿出去使用
源码解读
main()
修改r0tracer.js文件最底部处的代码,开启某一个Hook模式。
function main() { Java.perform(function () { console.Purple("r0tracer begin ... !")
traceClass("javax.crypto.Cipher") }) }
|
三种Hook方式
traceClass("javax.crypto.Cipher")
|
- hook关键字(黑白名单,前面是需要寻找的关键字,后面是忽略的关键字)
hook("javax.crypto.Cipher", "$")
|
- hook关键字(黑白名单 + 第三个参数 关键类,前面时要寻找的关键字,后面时忽略的关键字)
hook("com.roysue.check"," ","com.roysue.xxx")
|
第三种是因为可能某个类不在当前classloader中,需要遍历所有classloader去寻找
hook&havahook
function hook(white, black, target = null) { if (Java.available) { Java.perform(function () { javahook(white, black, target) }) } else if (ObjC.available) { ioshook(white, black) } else { console.log("please connect to either iOS or Android device ...") }
}
function javahook(white, black, target = null) { console.Red("start") if (!(target === null)) { console.LightGreen("Begin enumerateClassLoaders ...") Java.enumerateClassLoaders({ onMatch: function (loader) { try { if (loader.findClass(target)) { console.Red("Successfully found loader") console.Blue(loader); Java.classFactory.loader = loader; 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); } }, 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 ...") }
|
1、如果填了关键方法, 回去遍历所有的classloader,并将当前使用的classloader设置为包含目标类的classloader
2、遍历这个classloader所有的类,满足黑白名单的情况下,将类名推入targetClasses
traceClass&JavaTraceClass
function traceClass(targetClass) { if (Java.available) { Java.perform(function () { JavaTraceClass(targetClass) }) } else if (ObjC.available) { IosTraceClass(targetClass) } else { console.log("please connect to either iOS or Android device ...") } }
function JavaTraceClass(targetClass) { 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); 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") } Targets.forEach(function (targetMethod) { traceMethod(targetClass + "." + targetMethod); }); for (var p = 0; p < 100; p++) { output = output.concat("+"); } console.Green(output); }
|
这里是找到targetClass
下所有的方法和构造方法,对每个方法都执行traceMethod
traceMethod
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]) { 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") 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") } output = output.concat(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new())); output = output.concat("\nretval: " + retval + " => " + JSON.stringify(retval)); } output = output.concat("\n*** exiting " + targetClassMethod); 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; } } }
|
在这里打印类的成员的值,和追踪方法的入参和出参的值,并打印出调用堆栈