抖音数据采集Frida教程,Frida Java Hook 详解:代码及示例(下)
短视频、直播数据实时采集接口,请查看文档: TiToData
免责申明:本文档仅供学习与参考,请勿用于非法用处!否则所有后果自负。
1.1 Java层拦挡外部类函数
之前咱们曾经学习过了HOOK
一般函数、办法重载、构造函数,当初来更深刻的学习HOOK
在Android
逆向中,咱们也会常常遇到在Java
层的外部类。Java
外部类函数,使得咱们更难以剖析代码。咱们在这章节中对内部类进行一个根本理解和应用FRIDA
对内部类进行钩子拦挡解决。什么是外部类?所谓外部类就是在一个类外部进行其余类构造的嵌套操作,它的长处是外部类与外部类能够不便的拜访彼此的公有域(包含公有办法、公有属性),所以Android
中有很多的中央都会应用到外部类,咱们来见一个例子也是最直观的,如下图4-17。
图4-17 User类中的clz类
在图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。
图4-18 smali代码
通过下面的剖析咱们曾经得悉最重要的局部类的门路: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
,见下图。
图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。
图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:Arguments
是js
中的一个对象,js
内的每个函数都会内置一个Arguments
对象实例arguments
,它援用着办法实参,调用其实例对象能够通过arguments[]
下标的来援用理论元素,arguments.length
为函数实参个数,arguments.callee
援用函数本身。这就是为什么在该段代码中并看不到arguments
的定义却可能间接调用的起因,因为它是内置的一个对象。好了,讲完了arguments
咱们接着说,打印参数通过arguments.length
来循环以及arguments[j]
来获取理论参数的元素。那当初来看apply
,apply
在js
中是怎么样的存在,apply
的含意是:利用某一对象的一个办法,用另一个对象替换以后对象,this[targetMethod].apply(this, arguments);
这句代码简言之就是执行了以后的overload
办法。执行完以后的overload
办法并且打印以及返回给实在调用的函数,这样不会使程序谬误。那么最终执行成果见下图4-21:
图4-21 终端显示
能够看到胜利打印了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。
图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 codecsimport fridafrom time import sleep# 附加过程名称为:com.roysue.roysueapplicationsession = 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()
函数,跑起来看下成果:
$ frida -U -f com.whatsapp -l raptor_frida_android_trace_fixed.js --no-pause
如图所示*!open*
依据正则匹配到了openlog
、open64
等导出函数,并hook
了所有这些函数,打印出了其参数以及返回值。接下来想要看哪个局部,只有扔到jadx里,动态“剖析”一番,本人轻易翻翻,或者依据字符串搜一搜。比如说咱们想要看上图中的com.whatsapp.app.protocol
包里的内容,就能够设置trace("com.whatsapp.app.protocol")
。能够看到包内的函数、办法、包含重载、参数以及返回值全都打印了进去。这就是frida
脚本的魅力。当然,脚本终归只是一个工具,你对Java
、安卓App
的了解,和你的创意才是至关重要的。接下来能够搭配Xposed module
看看他人都给whatsapp
做了哪些模块,hook
的哪些函数,实现了哪些性能,学习本人写一写。
短视频、直播数据实时采集接口,请查看文档: TiToData
免责申明:本文档仅供学习与参考,请勿用于非法用处!否则所有后果自负。