r0tracer 源码分析
使用方法
修改r0tracer.js文件最底部处的代码,开启某一个Hook模式。
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");
    })
}
三种hook方式
一 hook 已知的类
traceClass("javax.crypto.Cipher")
二 hook 关键字(黑白名单,前面时要寻找的关键字,后面时忽略的关键字)
hook("javax.crypto.Cipher", "$")
三 hook 关键字(黑白名单 + 第三个参数 关键类,前面时要寻找的关键字,后面时忽略的关键字)
报某个类找不到时,将某个类名填写到第三个参数
hook("com.roysue.check"," ","com.roysue.xxx")
第三种是因为可能某个类不在当前classloader中,需要遍历所有classloader去寻找
执行命令
frida -U -f com.xxx.xxx -l r0tracer.js  --no-pause -o saveLog5.txt
源码解读
核心方法
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 ...")
}
- 如果填了关键方法, 回去遍历所有的classloader,并将当前使用的classloader设置为包含目标类的classloader
- 遍历这个classloader所有的类,满足黑白名单的情况下,将类名推入targetClasses
- 执行traceClass
function traceClass(targetClass) {
    //Java.use是新建一个对象哈,大家还记得么?
    var hook = Java.use(targetClass);
    //利用反射的方式,拿到当前类的所有方法
    var methods = hook.class.getDeclaredMethods();
    //建完对象之后记得将对象释放掉哈
    hook.$dispose;
    //将方法名保存到数组中
    var parsedMethods = [];
......
    methods.forEach(function (method) {
        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);
    });
......
}
这里是找到targetClass下所有的方法和构造方法,对每个方法都执行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")
            // 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;
        }
    }
}
在这里打印类的成员的值,和追踪方法的入参和出参的值,并打印出调用堆栈
开源项目地址:
https://github.com/r0ysue/r0tracer
r0tracer 源码分析的更多相关文章
- ABP源码分析一:整体项目结构及目录
		ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ... 
- HashMap与TreeMap源码分析
		1. 引言 在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ... 
- nginx源码分析之网络初始化
		nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ... 
- zookeeper源码分析之五服务端(集群leader)处理请求流程
		leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ... 
- zookeeper源码分析之四服务端(单机)处理请求流程
		上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ... 
- zookeeper源码分析之三客户端发送请求流程
		znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ... 
- java使用websocket,并且获取HttpSession,源码分析
		转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ... 
- ABP源码分析二:ABP中配置的注册和初始化
		一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ... 
- ABP源码分析三:ABP Module
		Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ... 
- ABP源码分析四:Configuration
		核心模块的配置 Configuration是ABP中设计比较巧妙的地方.其通过AbpStartupConfiguration,Castle的依赖注入,Dictionary对象和扩展方法很巧妙的实现了配 ... 
随机推荐
- [转帖]Harbor:修改默认的172网段
			背景: harbor 默认启动会随机创建 172 网段的ip地址,跟集群规划的网段冲突 Harbor 网段修改步骤 0. 原来Harbor占用的网段 # 网桥名:harbor_harbor [root ... 
- [转帖]jmeter之foreach循环控制器-03篇
			上篇我们通过正则表达式获取到了一组数据,那么怎么来用呢?下面就用foreach控制器来使用结果,如下图所示 然后再foreach控制器里添加要循环的请求,我们模拟百度搜索,value填入${id} 然 ... 
- [转帖]jmap执行失败了,怎么获取heapdump?
			https://www.jianshu.com/p/f4bfd169b4ca 在之前的OOM问题复盘中,我们添加了jmap脚本来自动dump内存现场,方便排查OOM问题. 但当我反复模拟OOM场景 ... 
- 一次w3wp出现crash的简单解决方案
			1. 前几天同事求助, 说一台服务器iis出现多次崩溃的现象,重启iis就可以了. 具体原因不明. 之前遇到过类似的问题 感觉最彻底的解决方案是 抓dump然后使用windbg 进行分析. 但是自 ... 
- 【实践篇】最全的【DDD领域建模】小白学习手册(文末附资料)
			导读 DDD领域建模被各个大小厂商提起并应用,而每个人都有自己的理解,本文就是针对小白,系统地讲解DDD到底是什么,解决了什么问题,及一些建议和实践.本文主要是思想的一种碰撞和分享,希望能对朋友们有所 ... 
- Qt中qreal的坑
			今天在写Qt的时候遇到了一个bug:同样一个方程在PC机上算的结果是11,但在arm-Linux设备上算出来的结果是12,我自己用计算器按出来的结果也是12. 该段程序是这样的: maxnumbar ... 
- 期盼已久全平台支持-开源IM项目OpenIM之uniapp更新
			国内uniapp使用广泛,OpenIM的uniapp sdk以及文档和demo (https://github.com/OpenIMSDK/Open-IM-Uniapp-Demo)都已更新,本文主要展 ... 
- 3.2 IDAPro脚本IDC常用函数
			IDA Pro内置的IDC脚本语言是一种灵活的.C语言风格的脚本语言,旨在帮助逆向工程师更轻松地进行反汇编和静态分析.IDC脚本语言支持变量.表达式.循环.分支.函数等C语言中的常见语法结构,并且还提 ... 
- CE修改器入门:代码替换功能
			某些游戏重新开始时,数据会存储在与上次不同的地方, 甚至游戏的过程中数据的存储位置也会变动.在这种情况下,你还是可以简单几步搞定它.这次我将尽量阐述如何运用"代码替换"功能,第五关 ... 
- Linux 多种方式实现文件共享
			文件共享服务在Linux系统上有多种方式,最常用的有Samba,vsftp,iSCSI,NFS这四种方式,如下将分别配置四种不同的文件共享服务. VSFTP 文件传输 FTP是文件传输协议.用于Int ... 
