frida工具Jnitrace | Objection | r0tracer

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

这个也可以使用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 包名
  • 75
    点赞
  • 114
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Codeooo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值