抖音数据采集Frida教程,Frida Java Hook 详解:代码及示例(下)

TiToData 2021-01-20 21:48:54
Android Python javascript ordinary


抖音数据采集Frida教程,Frida Java Hook 详解:代码及示例(下)

<br>

短视频、直播数据实时采集接口,请查看文档: TiToData

<br>免责声明:本文档仅供学习与参考,请勿用于非法用途!否则一切后果自负。<br>

1.1 Java层拦截内部类函数

之前我们已经学习过了HOOK普通函数、方法重载、构造函数,现在来更深入的学习HOOKAndroid逆向中,我们也会经常遇到在Java层的内部类。Java内部类函数,使得我们更难以分析代码。我们在这章节中对内部类进行一个基本了解和使用FRIDA对内部类进行钩子拦截处理。什么是内部类?所谓内部类就是在一个类内部进行其他类结构的嵌套操作,它的优点是内部类与外部类可以方便的访问彼此的私有域(包括私有方法、私有属性),所以Android中有很多的地方都会使用到内部类,我们来见一个例子也是最直观的,如下图4-17。<br><br>图4-17 User类中的clz类<br>在图4-17中看到User类中嵌套了一个clz,这样的操作也是屡见不鲜了。在frida中,我们可以使用$符号对起进行处理。首先打开jadxgui软件对代码进行反编译,反编译之后进入User类,下方会有一个smali的按钮,点击smali则会进入smali代码,进入smali代码直接按ctrl+f局部搜索字符串clz,因为clz是内部类的名称,那么就会搜到Lcom/roysue/roysueapplication/User\$clz;,我们将翻译成java代码就是:com.roysue.roysueapplication.User\$clz,去掉第一个字符串的L/以及;就构成了内部类的具体类名了,见下图4-18。<br>image.pngimage.gif<br>图4-18 smali代码<br>经过上面的分析我们已经得知最重要的部分类的路径:com.roysue.roysueapplication.User\$clz,现在来对内部类进行HOOK,现在开始编写js脚本。

1.1.1 拦截内部类函数代码示例

function hook_overload_3() {
if(Java.available) {
Java.perform(function () {
console.log("start hook");
//注意此处类的路径填写更改所分析的路径
var clz = Java.use('com.roysue.roysueapplication.User$clz');
if(clz != undefined) {
//这边也是像正常的函数来hook即可
clz.toString.implementation = function (){
console.log("成功hook clz类");
return this.toString();
}
} else {
console.log("clz: undefined");
}
console.log("start end");
});
}
}

执行脚本之后,我们可以看到控制也已经成功附加并且打印了成功hook clz类,这样我们也能够对Java层的内部类进行处理了。

[Google Pixel::com.roysue.roysueapplication]-> 成功hook clz类
成功hook clz类

1.2 Java层枚举所有的类并定位类

在前面我们学会了如何在java层的各种函数的HOOK操作了,现在开始学习枚举所有的类并定位类的骚套路了~,学习之前我们要了解API中的enumerateLoadedClasses方法,它是属于Java对象中的一个方法。能够枚举现在加载的所有类,enumerateLoadedClasses存在2个回调函数,分别是onMatch:function(ClassName):为每个加载的具有className的类调用,每个ClassName返回来的都是一个类名;和onComplete:function():在枚举所有类枚举完之后回调一次。

1.2.1 枚举所有的类并定位类代码示例


setTimeout(function (){
Java.perform(function (){
console.log("n[*] enumerating classes...");
//Java对象的API enumerateLoadedClasses
Java.enumerateLoadedClasses({
//该回调函数中的_className参数就是类的名称,每次回调时都会返回一个类的名称
onMatch: function(_className){
//在这里将其输出
console.log("[*] found instance of '"+_className+"'");
//如果只需要打印出com.roysue包下所有类把这段注释即可,想打印其他的替换掉indexOf中参数即可定位到~
//if(_className.toString().indexOf("com.roysue")!=-1)
//{
// console.log("[*] found instance of '"+_className+"'");
//}
},
onComplete: function(){
//会在枚举类结束之后回调一次此函数
console.log("[*] class enuemration complete");
}
});
});
});

当我们执行该脚本时,注入目标进程之后会开始调用onMatch函数,每次调用都会打印一次类的名称,当onMatch函数回调完成之后会调用一次onComplete函数,最后会打印出class enuemration complete,见下图。<br><br>图4-19 枚举所有类

1.3 Java层枚举类的所有方法并定位方法

上文已经将类以及实例枚举出来,接下来我们来枚举所有方法,打印指定类或者所有的类的内部方法名称,主要核心功能是通过类的反射方法中的getDeclaredMethods(),该api属于JAVAJDK中自带的API,属于java.lang.Class包中定义的函数。该方法获取到类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法。在Java中它是这样定义的:public Method[] getDeclaredMethods();其返回值是一个Method数组,Method实际上就是一个方法名称字符串,当然也是一个对象数组,然后我们将它打印出来。

1.3.1 枚举类的所有方法并定位方法代码示例

function enumMethods(targetClass)
{
var hook = Java.use(targetClass);
var ownMethods = hook.class.getDeclaredMethods();
hook.$dispose;
return ownMethods;
}
function hook_overload_5() {
if(Java.available) {
Java.perform(function () {
var a = enumMethods("com.roysue.roysueapplication.User$clz")
a.forEach(function(s) {
console.log(s);
});
});
}
}

我们先定义了一个enumMethods方法,其参数targetClass是类的路径名称,用于Java.use获取类对象本身,获取类对象之后再通过其.class.getDeclaredMethods()方法获取目标类的所有方法名称数组,当调用完了getDeclaredMethods()方法之后再调用$dispose方法释放目标类对象,返回目标类所有的方法名称、返回类型以及函数的权限,这是实现获取方法名称的核心方法,下面一个方法主要用于注入到目标进程中去执行逻辑代码,在hook_overload_5方法中先是使用了Java.perform方法,再在内部调用enumMethods方法获取目标类的所有方法名称、返回类型以及函数的权限,返回的是一个Method数组,通过forEach迭代器循环输出数组中的每一个值,因为其本身实际就是一个字符串所以直接输出就可以得到方法名称,脚本执行效果如下图4-20。<br>image.gifimage.png<br>图4-20 脚本执行后效果在图4-17中clz只有一个toString方法,我们填入参数为com.roysue.roysueapplication.User$clz,就能够定位到该类中所有的方法。

1.4 Java层拦截方法的所有方法重载

我们学会了枚举所有的类以及类的有方法之后,那我们还想知道如何获取所有的方法重载函数,毕竟在Android反编译的源码中方法重载不在少数,对此,一次性hook所有的方法重载是非常有必要的学习。我们已经知道在hook重载方法时需要写overload('x'),也就是说我们需要构造一个重载的数组,并把每一个重载都打印出来。

1.4.1 拦截方法的所有方法重载代码示例

function hook_overload_8() {
if(Java.available) {
Java.perform(function () {
console.log("start hook");
var targetMethod = 'add';
var targetClass = 'com.roysue.roysueapplication.Ordinary_Class';
var targetClassMethod = targetClass + '.' + targetMethod;
//目标类
var hook = Java.use(targetClass);
//重载次数
var overloadCount = hook[targetMethod].overloads.length;
//打印日志:追踪的方法有多少个重载
console.log("Tracing " + targetClassMethod + " [" + overloadCount + " overload(s)]");
//每个重载都进入一次
for (var i = 0; i < overloadCount; i++) {
//hook每一个重载
hook[targetMethod].overloads[i].implementation = function() {
console.warn("n*** entered " + targetClassMethod);
//可以打印每个重载的调用栈,对调试有巨大的帮助,当然,信息也很多,尽量不要打印,除非分析陷入僵局
Java.perform(function() {
var bt = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
console.log("nBacktrace:n" + bt);
});
// 打印参数
if (arguments.length) console.log();
for (var j = 0; j < arguments.length; j++) {
console.log("arg[" + j + "]: " + arguments[j]);
}
//打印返回值
var retval = this[targetMethod].apply(this, arguments); // rare crash (Frida bug?)
console.log("nretval: " + retval);
console.warn("n*** exiting " + targetClassMethod);
return retval;
}
}
console.log("hook end");
});
}
}

1.4.2 拦截方法的所有方法重载代码示例详解

上面这段代码可以打印出com.roysue.roysueapplication.Ordinary_Class类中add方法重载的个数以及hook该类中所有的方法重载函数,现在来剖析上面的代码为什么可以对一个类中的所有的方法重载HOOK挂上钩子。首先我们定义了三个变量分别是targetMethod、targetClass、targetClassMethod,这三个变量主要于定义方法的名称、类名、以及类名+方法名的赋值,首先使用了Java.use获取了目标类对象,再获取重载的次数。这里详细说一下如何获取的:var method_overload = cls[<func_name>].overloads[index];这句代码可以看出通过cls索引func_name到类中的方法,而后面写到overloads[index]是指方法重载的第index个函数,大致意思就是返回了一个method对象的第index位置的函数。而在代码中写道:var overloadCount = hook[targetMethod].overloads.length;,采取的方法是先获取类中某个函数所有的方法重载个数。继续往下走,开始循环方法重载的函数,刚刚开始循环时hook[targetMethod].overloads[i].implementation这句对每一个重载的函数进行HOOK。这里也说一下Arguments:Argumentsjs中的一个对象,js内的每个函数都会内置一个Arguments对象实例arguments,它引用着方法实参,调用其实例对象可以通过arguments[]下标的来引用实际元素,arguments.length为函数实参个数,arguments.callee引用函数自身。这就是为什么在该段代码中并看不到arguments的定义却能够直接调用的原因,因为它是内置的一个对象。好了,讲完了arguments咱们接着说,打印参数通过arguments.length来循环以及arguments[j]来获取实际参数的元素。那现在来看applyapplyjs中是怎么样的存在,apply的含义是:应用某一对象的一个方法,用另一个对象替换当前对象,this[targetMethod].apply(this, arguments);这句代码简言之就是执行了当前的overload方法。执行完当前的overload方法并且打印以及返回给真实调用的函数,这样不会使程序错误。那么最终执行效果见下图4-21:<br>image.pngimage.gif<br>图4-21 终端显示<br>可以看到成功打印了add函数的方法重载的数量以及hook打印出来的参数值、返回值!

1.5 Java层拦截类的所有方法

学会了如何HOOK所有方法重载函数后,我们可以把之前学习的整合到一起,来hook指定类中的所有方法,也包括方法重载的函数。下面js中核心代码是利用重载函数的特点来HOOK全部的方法,普通的方法也是一个特殊方法重载,只是它只是一个方法而已,直接把它当作方法重载来HOOK就好了,打个比方正方形是特殊的长方形,而长方形是不是特殊的正方形。这个正方形是普通函数,而长方形是重载方法这样大家应该很好理解了~在上一章节中已经知道了如何hook方法重载,只是方法名称和类名是写死的,只需要把成员的targetClass、targetMethod定义方法中的参数即可,在该例子中拿到指定类所有的所有方法名称,更加灵活使用了,代码如下。

1.5.1 拦截类的所有方法代码示例

function traceClass(targetClass)
{
//Java.use是新建一个对象哈,大家还记得么?
var hook = Java.use(targetClass);
//利用反射的方式,拿到当前类的所有方法
var methods = hook.class.getDeclaredMethods();
//建完对象之后记得将对象释放掉哈
hook.$dispose;
//将方法名保存到数组中
var parsedMethods = [];
methods.forEach(function(method) {
//通过getName()方法获取函数名称
parsedMethods.push(method.getName());
});
//去掉一些重复的值
var targets = uniqBy(parsedMethods, JSON.stringify);
//对数组中所有的方法进行hook
targets.forEach(function(targetMethod) {
traceMethod(targetClass + "." + targetMethod);
});
}
function hook_overload_9() {
if(Java.available) {
Java.perform(function () {
console.log("start hook");
traceClass("com.roysue.roysueapplication.Ordinary_Class");
console.log("hook end");
});
}
}
s1etImmediate(hook_overload_9);

执行脚本效果可以看到,hook到了com.roysue.roysueapplication.Ordinary_Class类中所有的函数,在执行其被hook拦截的方法时候,也打印出了每个方法相应的的参数以及返回值,见下图4-22。<br><br>图4-22 终端运行显示效果

1.6 Java层拦截类的所有子类

这里的核心功能也用到了上一小章节中定义的traceClass函数,该函数只需要传入一个class路径即可对class中的函数完成注入hook。那么在本小章节来hook掉所有类的子类,使我们的脚本更加的灵活方便。通过之前的学习我们已经知道enumerateLoadedClasses这个api可以枚举所有的类,用它来获取所有的类然后再调用traceClass函数就可以对所有类的子进行全面的hook。但是一般不会hook所有的函数,因为AndroidAPI函数实在太多了,在这里我们需要匹配自己需要hook的类即可,代码如下。

//枚举所有已经加载的类
Java.enumerateLoadedClasses({
onMatch: function(aClass) {
//迭代和判断
if (aClass.match(pattern)) {
//做一些更多的判断,适配更多的pattern
var className = aClass.match(/[L]?(.*);?/)[1].replace(///g, ".");
//进入到traceClass里去
traceClass(className);
}
},
onComplete: function() {}
});

1.7 RPC远程调用Java层函数

FRIDA中,不但提供很完善的HOOK机制,并且还提供rpc接口。可以导出某一个指定的函数,实现在python层对其随意的调用,而且是随时随地想调用就调用,极其方便,因为是在供给外部的python,这使得rpc提供的接口可以与python完成一些很奇妙的操作,这些导出的函数可以是任意的java内部的类的方法,调用我们自己想要的对象和特定的方法。那我们开始动手吧,现在我们来通过RPC的导出功能将图4-9中的add方法供给外部调用,开始编写rpc_demo.py文件,这次是python文件了哦~不是js文件了

1.7.1 rpc导出Java层函数代码示例

import codecs
import frida
from time import sleep
# 附加进程名称为:com.roysue.roysueapplication
session = frida.get_remote_device().attach('com.roysue.roysueapplication')
# 这是需要执行的js脚本,rpc需要在js中定义
source = """
//定义RPC
rpc.exports = {
//这里定义了一个给外部调用的方法:sms
sms: function () {
var result = "";
//嵌入HOOK代码
Java.perform(function () {
//拿到class类
var Ordinary_Class = Java.use("com.roysue.roysueapplication.Ordinary_Class");
//最终rpc的sms方法会返回add(1,3)的结果!
result = Ordinary_Class.add(1,3);
});
return result;
},
};
"""
# 创建js脚本
script = session.create_script(source)
script.load()
# 这里可以直接调用java中的函数
rpc = script.exports
# 在这里也就是python下直接通过rpc调用sms()方法
print(rpc.sms())
sleep(1)
session.detach()

当我们执行python rpc_demo.py时先会创建脚本并且注入到目标进程,在上面的source实际上就是js逻辑代码了。在js代码内我们定义了rpc可以给python调用的sms函数,而sms函数内部嵌套调用Java.perform再对需要拿到的函数的类进行主动调用,把最终的结果返回作为sms的返回值,当我们在python层时候可以任意调用sms中的原型add方法~

1.8 综合案例一:在安卓8.1上dump蓝牙接口和实例

一个比较好的综合案例 :dump蓝牙信息的“加强版”——BlueCrawl

VERSION="1.0.0"
setTimeout(function(){
Java.perform(function(){
Java.enumerateLoadedClasses({
onMatch: function(instance){
if (instance.split(".")[1] == "bluetooth"){
console.log("[->]t"+lightBlueCursor()+instance+closeCursor());
}
},
onComplete: function() {}
});
Java.choose("android.bluetooth.BluetoothGattServer",{
onMatch: function (instance){
...
onComplete: function() { console.log("[*] -----");}
});
Java.choose("android.bluetooth.BluetoothGattService",{
onMatch: function (instance){
...
onComplete: function() { console.log("[*] -----");}
});
Java.choose("android.bluetooth.BluetoothSocket",{
onMatch: function (instance){
...
onComplete: function() { console.log("[*] -----");}
});
Java.choose("android.bluetooth.BluetoothServerSocket",{
onMatch: function (instance){
...
onComplete: function() { console.log("[*] -----");}
});
Java.choose("android.bluetooth.BluetoothDevice",{
onMatch: function (instance){
...
onComplete: function() { console.log("[*] -----");}
});
});
},0);

该脚本首先枚举了很多蓝牙相关的类,然后choose了很多类,包括蓝牙接口信息以及蓝牙服务接口对象等,还加载了内存中已经分配好的蓝牙设备对象,也就是上文我们已经演示的信息。我们可以用这个脚本来“查看”App加载了哪些蓝牙的接口,App是否正在查找蓝牙设备、或者是否窃取蓝牙设备信息等。在电脑上运行命令:$ frida -U -l bluecrawl-1.0.0.js com.android.bluetooth执行该脚本时会详细打印所有蓝牙接口信息以及服务接口对象~~

1.9 综合案例二:动静态结合逆向WhatsApp

我们来试下它的几个主要的功能,首先是本地库的导出函数。


setTimeout(function() {
Java.perform(function() {
trace("exports:*!open*");
//trace("exports:*!write*");
//trace("exports:*!malloc*");
//trace("exports:*!free*");
});
}, 0);

我们hook的是open()函数,跑起来看下效果:<br>

$ frida -U -f com.whatsapp -l raptor_frida_android_trace_fixed.js --no-pause

如图所示*!open*根据正则匹配到了openlogopen64等导出函数,并hook了所有这些函数,打印出了其参数以及返回值。接下来想要看哪个部分,只要扔到jadx里,静态“分析”一番,自己随便翻翻,或者根据字符串搜一搜。比如说我们想要看上图中的com.whatsapp.app.protocol包里的内容,就可以设置trace("com.whatsapp.app.protocol")。可以看到包内的函数、方法、包括重载、参数以及返回值全都打印了出来。这就是frida脚本的魅力。当然,脚本终归只是一个工具,你对Java、安卓App的理解,和你的创意才是至关重要的。接下来可以搭配Xposed module看看别人都给whatsapp做了哪些模块,hook的哪些函数,实现了哪些功能,学习自己写一写。

短视频、直播数据实时采集接口,请查看文档: TiToData

<br>免责声明:本文档仅供学习与参考,请勿用于非法用途!否则一切后果自负。

版权声明
本文为[TiToData]所创,转载请带上原文链接,感谢
https://my.oschina.net/u/2293598/blog/4916853

  1. 【计算机网络 12(1),尚学堂马士兵Java视频教程
  2. 【程序猿历程,史上最全的Java面试题集锦在这里
  3. 【程序猿历程(1),Javaweb视频教程百度云
  4. Notes on MySQL 45 lectures (1-7)
  5. [computer network 12 (1), Shang Xuetang Ma soldier java video tutorial
  6. The most complete collection of Java interview questions in history is here
  7. [process of program ape (1), JavaWeb video tutorial, baidu cloud
  8. Notes on MySQL 45 lectures (1-7)
  9. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  10. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  11. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件
  12. Refined spring boot 03: spring boot configuration files and configuration management, and reading configuration files in three ways
  13. 【递归,Java传智播客笔记
  14. [recursion, Java intelligence podcast notes
  15. [adhere to painting for 386 days] the beginning of spring of 24 solar terms
  16. K8S系列第八篇(Service、EndPoints以及高可用kubeadm部署)
  17. K8s Series Part 8 (service, endpoints and high availability kubeadm deployment)
  18. 【重识 HTML (3),350道Java面试真题分享
  19. 【重识 HTML (2),Java并发编程必会的多线程你竟然还不会
  20. 【重识 HTML (1),二本Java小菜鸟4面字节跳动被秒成渣渣
  21. [re recognize HTML (3) and share 350 real Java interview questions
  22. [re recognize HTML (2). Multithreading is a must for Java Concurrent Programming. How dare you not
  23. [re recognize HTML (1), two Java rookies' 4-sided bytes beat and become slag in seconds
  24. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  25. RPC 1: how to develop RPC framework from scratch
  26. 造轮子系列之RPC 1:如何从零开始开发RPC框架
  27. RPC 1: how to develop RPC framework from scratch
  28. 一次性捋清楚吧,对乱糟糟的,Spring事务扩展机制
  29. 一文彻底弄懂如何选择抽象类还是接口,连续四年百度Java岗必问面试题
  30. Redis常用命令
  31. 一双拖鞋引发的血案,狂神说Java系列笔记
  32. 一、mysql基础安装
  33. 一位程序员的独白:尽管我一生坎坷,Java框架面试基础
  34. Clear it all at once. For the messy, spring transaction extension mechanism
  35. A thorough understanding of how to choose abstract classes or interfaces, baidu Java post must ask interview questions for four consecutive years
  36. Redis common commands
  37. A pair of slippers triggered the murder, crazy God said java series notes
  38. 1、 MySQL basic installation
  39. Monologue of a programmer: despite my ups and downs in my life, Java framework is the foundation of interview
  40. 【大厂面试】三面三问Spring循环依赖,请一定要把这篇看完(建议收藏)
  41. 一线互联网企业中,springboot入门项目
  42. 一篇文带你入门SSM框架Spring开发,帮你快速拿Offer
  43. 【面试资料】Java全集、微服务、大数据、数据结构与算法、机器学习知识最全总结,283页pdf
  44. 【leetcode刷题】24.数组中重复的数字——Java版
  45. 【leetcode刷题】23.对称二叉树——Java版
  46. 【leetcode刷题】22.二叉树的中序遍历——Java版
  47. 【leetcode刷题】21.三数之和——Java版
  48. 【leetcode刷题】20.最长回文子串——Java版
  49. 【leetcode刷题】19.回文链表——Java版
  50. 【leetcode刷题】18.反转链表——Java版
  51. 【leetcode刷题】17.相交链表——Java&python版
  52. 【leetcode刷题】16.环形链表——Java版
  53. 【leetcode刷题】15.汉明距离——Java版
  54. 【leetcode刷题】14.找到所有数组中消失的数字——Java版
  55. 【leetcode刷题】13.比特位计数——Java版
  56. oracle控制用户权限命令
  57. 三年Java开发,继阿里,鲁班二期Java架构师
  58. Oracle必须要启动的服务
  59. 万字长文!深入剖析HashMap,Java基础笔试题大全带答案
  60. 一问Kafka就心慌?我却凭着这份,图灵学院vip课程百度云