• 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 包名
    
  • 相关阅读:
    zsh: no such file or directory: /usr/local/mysql/bin/mysql
    Java SE 10 新增特性
    强大的数据分析工具——Pandas操作、易错点、知识点三万字详解
    第三篇 《随机点名答题系统》——人员管理详解(类抽奖系统、在线答题系统、线上答题系统、在线点名系统、线上点名系统、在线考试系统、线上考试系统)
    全域数据连接器解决运营痛点问题,助力海尔节省200万+
    Vue2 06 插槽 slot
    GreenPlum在线扩容工具GPExpan实战
    juc之常用4大并发工具类 (四)
    【Rust日报】2023-09-25 Rust + Wasm 是 AGI 的语言吗
    python << 和 >>
  • 原文地址:https://blog.csdn.net/weixin_38927522/article/details/127120012