使用方法

修改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 ...")
}
  1. 如果填了关键方法, 回去遍历所有的classloader,并将当前使用的classloader设置为包含目标类的classloader
  2. 遍历这个classloader所有的类,满足黑白名单的情况下,将类名推入targetClasses
  3. 执行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 源码分析的更多相关文章

  1. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  2. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

  3. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  4. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  5. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  6. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

  7. java使用websocket,并且获取HttpSession,源码分析

    转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...

  8. ABP源码分析二:ABP中配置的注册和初始化

    一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...

  9. ABP源码分析三:ABP Module

    Abp是一种基于模块化设计的思想构建的.开发人员可以将自定义的功能以模块(module)的形式集成到ABP中.具体的功能都可以设计成一个单独的Module.Abp底层框架提供便捷的方法集成每个Modu ...

  10. ABP源码分析四:Configuration

    核心模块的配置 Configuration是ABP中设计比较巧妙的地方.其通过AbpStartupConfiguration,Castle的依赖注入,Dictionary对象和扩展方法很巧妙的实现了配 ...

随机推荐

  1. [转帖]PostgreSQL 统计所有数据表各自的总行数

    一般来说,可以使用 count(*) 来获取具体某张表的总行数: SELECT count(0) FROM t_user; 如果想获得所有表的行数信息,可以使用以下 SQL 语句: SELECT re ...

  2. [转帖]Linux之pure-ftpd安装和使用

    一.pure-ftpd简介   PureFTPd是一款专注于程序健壮和软件安全的免费FTP服务器软件(基于BSD License),以安全和配置简单为设计目标,支持虚拟主机,IPV6,PAM等功能.. ...

  3. [转帖]线上一个隐匿 Bug 的复盘

    前言 之前负责的一个项目上线好久了,最近突然爆出一 Bug,最后评估影响范围将 Bug 升级成了故障,只因为影响的数据量有 10000 条左右,对业务方造成了一定的影响. 但因为不涉及到资金损失,Bu ...

  4. [转帖]Kubernetes-17:Kubernets包管理工具—>Helm介绍与使用

    https://www.cnblogs.com/v-fan/p/13949025.html Kubernets包管理工具->Helm 什么是Helm? 我们都知道,Linux系统各发行版都有自己 ...

  5. 国产CPU制造工艺与部分性能总结

    国产CPU制造工艺与部分性能总结 背景 最近一段时间验证了很多国产CPU的性能. 感觉很多地方与之前的理解有一些偏差. 前几天总结了部分架构和指令集相关的差异 今天想着总结一下制造相关的部分. 希望能 ...

  6. Spring Boot日志框架Slf4j+logback

    一.简介 Slf4j Java的简单日志记录外观(Simple Logging Facade for Java )可作为各种日志记录框架(例如java.util.logging,logback,log ...

  7. js解析上传APK文件的详细信息

    前端解析APK版本信息 需要安装这个包,可以使用cnpm或者npm npm 安装 app-info-parser ( 命令:npm install app-info-parser) APKInfo为i ...

  8. go中异常处理流程

    在 Go 语言中,panic.recover 和 defer 是用于处理异常情况的关键字.它们通常一起使用来实现对程序错误的处理和恢复. 1. defer 语句 defer 用于在函数返回之前执行一段 ...

  9. Go中字符串处理:fmt.Sprintf与string.Builder的比较

    在Go语言中,我们通常会遇到两种主要的方式来处理和操作字符串:使用fmt.Sprintf函数和string.Builder类型.尽管两者都可以实现字符串的格式化和连接,但它们在性能和用法上有一些关键区 ...

  10. 解决pycharm编辑超大超大项目时CPU占用100%

    在编辑py文件时,cpu占用100%其实和内存的关系不大,因为这个现象是间歇性的,不是持续的. 我试过给pycharm分配16GB的内存,也是一样没有缓解CPU占用高. 项目和pycharam也都是存 ...