关于大数据:抖音数据采集Frida教程Frida-Java-Hook-详解代码及示例下

8次阅读

共计 10552 个字符,预计需要花费 27 分钟才能阅读完成。

抖音数据采集 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。

图 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: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:

图 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 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() 函数,跑起来看下成果:

$ 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

免责申明:本文档仅供学习与参考,请勿用于非法用处!否则所有后果自负。

正文完
 0