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 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);
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是立即执行函数,setTimeout是等待毫秒后延迟执行函数
//二者在attach模式下没有区别
//在spawn模式下,hook系统API时如javax.crypto.Cipher建议使用setImmediate立即执行,不需要延时
//在spawn模式下,hook应用自己的函数或含壳时,建议使用setTimeout并给出适当的延时(500~5000)
*/
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 方式
/*
hook指定方法, 如果有重载会hook所有重载,如果有疑问可以看
--dump-args : 打印参数
--dump-backtrace : 打印调用栈
--dump-return : 打印返回值
*/
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 false
Spawn 方式 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文章来源:https://www.toymoban.com/news/detail-511119.html
这个也可以使用adb查看:文章来源地址https://www.toymoban.com/news/detail-511119.html
(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 包名
到了这里,关于frida工具Jnitrace | Objection | r0tracer的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!