pip install objection
pip install jnitrace
要求是frida版本大于14点多,目前推荐使用 14.2.18
Frida Version : pip install frida==14.2.18
Jnitrace:
JNItrace是一个基于Frida框架的Hook jni方法的库。https://github.com/chame1eon/jnitrace
直接按照上面的pip安装。
jnitrace -l xxx.so 包名 --ignore-vm
注意:
如果出现这种错误就是用了面具的隐藏 hide ;
Spawn模式及 jnitrace 都是需要关闭面具隐藏( Magisk Hide )
不得不说 这真的是个神器。
另外博主有一些笔记,记录如下,方便后续观看:
加密函数定位:
jni| unicorn | androidemu | frida_hook
https://codeooo.blog.csdn.net/article/details/127105204
https://github.com/lasting-yang/frida_hook_libart.git
dump 脚本修复加密so
https://github.com/lasting-yang/frida_dump
frida hook模板:
so :https://codeooo.blog.csdn.net/article/details/122124012
java:
https://codeooo.blog.csdn.net/article/details/120025814
遍历so方法:
https://codeooo.blog.csdn.net/article/details/120033269
hook 常见算法
https://codeooo.blog.csdn.net/article/details/120025814
r0tracer:
安卓Java层多功能追踪脚本
AKA:精简版 objection + Wallbreaker
比objection增加延时spawn
比objection增加批量hook类\方法\构造函数
Wallbreaker在frida14上还是一直崩
比Wallbreaker增加hook看instance的fields
inspectObject函数可以单独拿出去使用
使用方法:
修改r0tracer.js文件最底部处的代码,开启某一个Hook模式。
var isLite = false;var ByPassTracerPid = function () { var fgetsPtr = Module.findExportByName("libc.so", "fgets"); var fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer', 'int', 'pointer']); Interceptor.replace(fgetsPtr, new NativeCallback(function (buffer, size, fp) { var retval = fgets(buffer, size, fp); var bufstr = Memory.readUtf8String(buffer); if (bufstr.indexOf("TracerPid:") > -1) { Memory.writeUtf8String(buffer, "TracerPid:\t0"); console.log("tracerpid replaced: " + Memory.readUtf8String(buffer)); } return retval; }, 'pointer', ['pointer', 'int', 'pointer']));};setImmediate(ByPassTracerPid);(function(){ let Color = {RESET: "\x1b[39;49;00m", Black: "0;01", Blue: "4;01", Cyan: "6;01", Gray: "7;11", "Green": "2;01", Purple: "5;01", Red: "1;01", Yellow: "3;01"}; let LightColor = {RESET: "\x1b[39;49;00m", Black: "0;11", Blue: "4;11", Cyan: "6;11", Gray: "7;01", "Green": "2;11", Purple: "5;11", Red: "1;11", Yellow: "3;11"}; var colorPrefix = '\x1b[3', colorSuffix = 'm' for (let c in Color){ if (c == "RESET") continue; console[c] = function(message){ console.log(colorPrefix + Color[c] + colorSuffix + message + Color.RESET); } console["Light" + c] = function(message){ console.log(colorPrefix + LightColor[c] + colorSuffix + message + Color.RESET); } }})();function uniqBy(array, key) { var seen = {}; return array.filter(function (item) { var k = key(item); return seen.hasOwnProperty(k) ? false : (seen[k] = true); });}function hasOwnProperty(obj, name) { try { return obj.hasOwnProperty(name) || name in obj; } catch (e) { return obj.hasOwnProperty(name); }}function getHandle(object) { if (hasOwnProperty(object, '$handle')) { if (object.$handle != undefined) { return object.$handle; } } if (hasOwnProperty(object, '$h')) { if (object.$h != undefined) { return object.$h; } } return null;}//查看域值function inspectObject(obj, input) { var isInstance = false; var obj_class = null; if (getHandle(obj) === null) { obj_class = obj.class; } else { var Class = Java.use("java.lang.Class"); obj_class = Java.cast(obj.getClass(), Class); isInstance = true; } input = input.concat("Inspecting Fields: => ", isInstance, " => ", obj_class.toString()); input = input.concat("\r\n") var fields = obj_class.getDeclaredFields(); for (var i in fields) { if (isInstance || Boolean(fields[i].toString().indexOf("static ") >= 0)) { // output = output.concat("\t\t static static static " + fields[i].toString()); var className = obj_class.toString().trim().split(" ")[1]; // console.Red("className is => ",className); var fieldName = fields[i].toString().split(className.concat(".")).pop(); var fieldType = fields[i].toString().split(" ").slice(-2)[0]; var fieldValue = undefined; if (!(obj[fieldName] === undefined)) fieldValue = obj[fieldName].value; input = input.concat(fieldType + " \t" + fieldName + " => ", fieldValue + " => ", JSON.stringify(fieldValue)); input = input.concat("\r\n") } } return input;}// trace单个类的所有静态和实例方法包括构造方法 trace a specific Java Methodfunction 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); 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") } //调用栈 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; } }}function traceClass(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);}function hook(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 ...")}function main() { Java.perform(function () { console.Purple("r0tracer begin ... !") //0. 增加精简模式,就是以彩虹色只显示进出函数。默认是关闭的,注释此行打开精简模式。 //isLite = true; //A. 简易trace单个函数 traceClass("javax.crypto.Cipher") //B. 黑白名单trace多个函数,第一个参数是白名单(包含关键字),第二个参数是黑名单(不包含的关键字) // hook("javax.crypto.Cipher", "$"); //C. 报某个类找不到时,将某个类名填写到第三个参数,比如找不到com.roysue.check类。(前两个参数依旧是黑白名单) // hook("com.roysue.check"," ","com.roysue.check"); })}setImmediate(main)//// setTimeout(main, 2000);// 玄之又玄,众妙之门// Frida的崩溃有时候真的是玄学,大项目一崩溃根本不知道是哪里出的问题,这也是小而专的项目也有一丝机会的原因// Frida自身即会经常崩溃,建议多更换Frida(客/服要配套)版本/安卓版本,我自己常用的组合是两部手机,Frida12.8.0全家桶+安卓8.1.0,和Frida14.2.2全家桶+安卓10
肉佬:https://github.com/r0ysue/r0tracer
推荐使用Frida14版本,并且将日志使用-o参数进行输出保存
frida -U -f com.r0ysue.example -l r0tracer.js --no-pause -o saveLog5.txt
“-f” 为 Spawn模式,去掉"-f" 为Attach模式
Frida版本=<12时,要加上–runtime=v8选项
frida -U com.r0ysue.example -l r0tracer.js --runtime=v8 --no-pause -o
saveLog6.txt
objection:
objection 启动界面:
使用objection需要在手机上启动frida-sesver,再使用objection注入需要hook的应用
objection -g <包名> explore
下面指令中,有hook类,hook方法,前进程模块,导出函数等
Memory 指令 memory list modules //枚举当前进程模块 memory list exports [lib_name] //查看指定模块的导出函数 memory list exports libart.so --json /root/libart.json //将结果保存到json文件中 memory search --string --offsets-only //搜索内存android heap 指令 //堆内存中搜索指定类的实例, 可以获取该类的实例id search instances search instances com.xx.xx.class //直接调用指定实例下的方法 android heap execute [ins_id] [func_name] //自定义frida脚本, 执行实例的方法 android heap execute [ins_id]android 指令 android root disable //尝试关闭app的root检测 android root simulate //尝试模拟root环境 android ui screenshot [image.png] //截图 android ui FLAG_SECURE false //设置FLAG_SECURE权限内存漫游 android hooking list classes //列出内存中所有的类 //在内存中所有已加载的类中搜索包含特定关键词的类 android hooking search classes [search_name] //在内存中所有已加载的方法中搜索包含特定关键词的方法 android hooking search methods [search_name] //直接生成hook代码 android hooking generate simple [class_name]hook 方式 android hooking watch class_method com.xxx.xxx.methodName --dump-args --dump-backtrace --dump-return //hook指定类, 会打印该类下的所有调用 android hooking watch class com.xxx.xxx //设置返回值(只支持bool类型) android hooking set return_value com.xxx.xxx.methodName falseSpawn 方式 Hook objection -g packageName explore --startup-command '[obejection_command]'activity 和 service 操作 android hooking list activities //枚举activity android intent launch_activity [activity_class] //启动activity android hooking list services //枚举services android intent launch_service [services_class] //启动services任务管理器 jobs list // 查看任务列表 jobs kill [task_id] // 关闭任务关闭 app 的 ssl 校验 android sslpinning disable监控系统剪贴板 // 获取Android剪贴板服务上的句柄并每5秒轮询一次用于数据。 // 如果发现新数据,与之前的调查不同,则该数据将被转储到屏幕上。 help android clipboard执行命令行 help android shell_exec [command]
查看当前可用的activity或者service:
android hooking list activities/services
这个也可以使用adb查看:
(1)查看当前Activity :adb shell "dumpsys window w | grep name="(2)查看当前栈顶的Activity :adb shell "dumpsys activity | grep mFocusedActivity"或者:adb shell dumpsys activity activities | grep mResumedActivity(3)查看当前栈顶的Activity的Fragment :adb shell dumpsys activity 包名
来源地址:https://blog.csdn.net/weixin_38927522/article/details/127120012