2021-01-20

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

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


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


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

1.1 Java层拦截内部类函数

之前我们已经学习过了HOOK普通函数、方法重载、构造函数,现在来更深入的学习HOOKAndroid逆向中,我们也会经常遇到在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。
image.pngimage.gif
图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。
image.gifimage.png
图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:
image.pngimage.gif
图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*根据正则匹配到了openlogopen64等导出函数,并hook了所有这些函数,打印出了其参数以及返回值。接下来想要看哪个部分,只要扔到jadx里,静态"分析"一番,自己随便翻翻,或者根据字符串搜一搜。比如说我们想要看上图中的com.whatsapp.app.protocol包里的内容,就可以设置trace("com.whatsapp.app.protocol")。可以看到包内的函数、方法、包括重载、参数以及返回值全都打印了出来。这就是frida脚本的魅力。当然,脚本终归只是一个工具,你对Java、安卓App的理解,和你的创意才是至关重要的。接下来可以搭配Xposed module看看别人都给whatsapp做了哪些模块,hook的哪些函数,实现了哪些功能,学习自己写一写。

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


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









原文转载:http://www.shaoqun.com/a/512395.html

跨境电商:https://www.ikjzd.com/

塔图:https://www.ikjzd.com/w/2274

刘小东:https://www.ikjzd.com/w/1853


抖音数据采集Frida教程,FridaJavaHook详解:代码及示例(下)短视频、直播数据实时采集接口,请查看文档:TiToData免责声明:本文档仅供学习与参考,请勿用于非法用途!否则一切后果自负。1.1Java层拦截内部类函数之前我们已经学习过了HOOK普通函数、方法重载、构造函数,现在来更深入的学习HOOK在Android逆向中,我们也会经常遇到在Java层的内部类。Java内部类函数,使
笨鸟转运:笨鸟转运
stylenanda官网:stylenanda官网
螺髻山 赏峨眉"姊妹山"的六大奇观:螺髻山 赏峨眉"姊妹山"的六大奇观
广州麓湖儿童乐园有大人玩的吗?麓湖儿童乐园成人能玩吗?:广州麓湖儿童乐园有大人玩的吗?麓湖儿童乐园成人能玩吗?
八里沟里面有住宿的没?辉县八里沟住宿餐饮怎么样?:八里沟里面有住宿的没?辉县八里沟住宿餐饮怎么样?

No comments:

Post a Comment