andorid jar/库源码解析之frida体验

Posted Supper litt

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了andorid jar/库源码解析之frida体验相关的知识,希望对你有一定的参考价值。

目录:andorid jar/库源码解析 

Frida体验:

  作用:

    android手机上可以对,java和so层代码,进行hook.监控数据和处理内存数据。

    官译:面向开发人员、逆向工程师和安全研究人员的动态工具工具包。

  栗子:

    运行步骤:

    1、https://github.com/frida/frida/releases 下载适合需要运行环境的可执行程序。我这里是arm64,所以下载了一个最新版本的frida64位。

    2、PC端,安装python环境,直接从官网下载一个python安装包安装即可,我这里安装的是python3, 安装包会自己配置环境变量,cmd运行 python,看看有没有,有就是安装上了环境变量了。

    3、使用 pip install frida 安装 frida 环境,基于 python的。  再安装  pip install frida-tools ,我看大部分都是按照了。tools的。

    4、一切ok。开始跑了,,,把步骤1中得到的,可执行文件,拷贝到手机上面  adb push D:\\xxx\\xxx\\frixxxx   /data/local/tmp/

    5、因为push上去的可执行文件默认没有权限,下一步授权。。adb shell 进入手机  su,得到root权限。。cd /data/local 进入 local文件夹  chmod 777 -R *  授权可执行。。 再  cd tmp  进入 tmp  执行 :./frXXX回车运行exe

效果如下图:

    

    6、端口转发,因为要把手机的数据和本地电脑互通,需要设置端口转发,两个端口:执行两条命令

    

adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043

    7、接着就可以允许python了。

    8、代码示例:

import frida, sys

jsCode = """

Java.perform(function () {
    var impl = Java.use("com.android.test.TestUtil");
    impl.test1.implementation = function () {
        send("test1 called!");
        this.test1();
    };

    impl.test2.implementation = function () {
        send("test2 called!");
    };

    impl.test3.implementation = function (a) {
        send("test3 called!");
    };

    impl.test4.implementation = function (a,b,c) {
        send("test4 called!");
        return this.test4(a,b,c);
    };

    impl.test5.overload("int").implementation = function (a) {
        send("test5 1 called!");
    };

    impl.test5.overload("int", "java.lang.String").implementation = function (a,b) {
        send("test5 2 called!");
    };

});

"""
def message(message, data):
    if message["type"] == \'send\':
        print(u"[*] {0}".format(message[\'payload\']))
    else:
        print(message)

process = frida.get_remote_device().attach("com.android.test")
script= process.create_script(jsCode)
script.on("message", message)
script.load()
sys.stdin.read()

  代码中,把js写到了一个string变量中。多行都是用这个固定格式。

  对test1方法进行了hook同时,在后面也对 test1方法进行了调用,如果不调用,方法就不会执行了。参考 test2

  test3针对单个参数的情况,test4针对多个参数。test5,针对不同参数进行了重载,需要指明参数类型,上面做了处理。

  执行js的send方法,内容回呗,发往,scritp的绑定方法 。def message(message, data)在里面,进行输出,你也可以在js中使用 console.log进行输出参数。

  针对是复杂类型的情况,可以直接a.XXXX拿到复杂类型的变量。

  8.2、其他js

// 遍历所有js.指定名称,过滤
Java.perform(function(){
    Java.enumerateLoadedClasses({
        onMatch: function(className) {
            if(className.toString().indexOf("down") >= 0){
                send(className+\'\\"));\');
            }
        },
        onComplete:function(){
            send("done");
        }
    });
});

  9:so,hook处理。

  

import frida, sys

jsCode3 = """
Java.perform(function(){
    
    var exports = Module.enumerateExportsSync("xxx.so");
    for(var i = 0; i < exports.length; i++) {
        if(exports[i] && exports[i].name != undefined){
            if(exports[i].name.indexOf("rapidjson")>=0){
                var nativePointer = new NativePointer(exports[i].address);
                send("exports " + exports[i].name + " nativePointer " + nativePointer);
                Interceptor.attach(nativePointer, {
                    onEnter: function(args) {
                        send(this.context.pc + " called! ");
                        send("context " + JSON.stringify(this.context));
                        
                        // 保存长度,在返回方法进行调用
                        this.fd = args[1];
                        // send(nativePointer + " arg0 " +args[0] + " arg1 " +args[1] + " arg2 " +args[2]);
                    },
                    onLeave:function(retval){
                        send(this.context.pc + " retval " +retval);
                        
                        // dump出内容
                        var size = parseInt(this.fd);
                        if(size > 0){
                            const r = Memory.alloc();
                                
                            // 复制以module.base地址开始的10个字节 那肯定会是7F 45 4C 46...因为一个ELF文件的Magic属性如此。
                            Memory.copy(r,ptr(retval),size);
                            console.log(hexdump(r, {
                                offset: 0,
                                length: size,
                                header: true,
                                ansi: false
                            }));
                        }
                    }
                });
            }
        }
    }
});

""";

jsCode2 = """
Java.perform(function(){
    // 遍历所有模块
    Process.enumerateModules({
        onMatch: function(exp){
            //先获取so的module对象
            var module = Process.findModuleByName(exp.name); 
            
            //??是通配符
            var pattern = "11 22 33 44 55 66"; //
            //基址
            // console.log("base:" + module.base)
            //从so的基址开始搜索,搜索大小为so文件的大小,搜指定条件03 49 ?? 50 20 44的数据
            var res = Memory.scan(module.base, module.size, pattern, {
                onMatch: function(address, size){
                    //搜索成功
                    console.log(\'搜索到 \' + pattern +" 地址是:"+ address.toString());  
                }, 
                onError: function(reason){
                    //搜索失败
                    // console.log(\'搜索失败 \' + reason);
                },
                onComplete: function(){
                    //搜索完毕
                    // console.log("搜索完毕")
                }
            });
        },
        onComplete: function(){
            send(\'stop\');
        }
    });

    // 导入函数
    var imports = Module.enumerateImportsSync("xxx.so");
    for(var i = 0; i < imports.length; i++) {
        if(imports[i].name == \'strncat\'){
            send(imports[i].name + ": " + imports[i].address);
            break;
        }
    }
    
    // 导出函数
    var exports = Module.enumerateExportsSync("xxx.so");
    for(var i = 0; i < exports.length; i++) {
        if(exports[i].name.indexOf(\'add\') != -1){
            send(exports[i].name + ": " + exports[i].address);
            break;
        }
    }
});

""";
    
jsCode = """

var module = Process.findModuleByName("xxx.so"); 
var nativePointer = new NativePointer(module.base.add(0x1740));
send("nativePointer " + nativePointer);
Interceptor.attach(nativePointer, {
    onEnter: function(args) {
        send("mou sub_method called!");
    },
    onLeave:function(retval){
    
    }
});

var nativePointer = new NativePointer(ptr(0x00001122));
send("nativePointer " + nativePointer);
Interceptor.attach(nativePointer, {
    onEnter: function(args) {
        send("mou sub_method 2 called!");
    },
    onLeave:function(retval){
    
    }
});

"""
def message(message, data):
    if message["type"] == \'send\':
        print(u"[*] {0}".format(message[\'payload\']))
    else:
        print(message)

process = frida.get_remote_device().attach("com.android.test")
script= process.create_script(jsCode2)
script.on("message", message)
script.load()
sys.stdin.read()

  提供了多种方法:

  a>遍历所有so模块,查找指定关键字。

  b>遍历某个模块的所有导出函数,名称包含

  c>遍历某个模块的所有导入函数,名称包含。

  d>直接通过,模块得到内存地址和偏移,进行hook.

  e>通过利用 this.的成员变量,在结果中,dump出,入参的参数的某个值进行dump内容。

  f>输出 this.context对象。

  源码解读:

  待补充

  参考:

        https://www.anquanke.com/post/id/195215

  https://www.anquanke.com/member/131652

  源码:https://frida.re/

  引入:

 

以上是关于andorid jar/库源码解析之frida体验的主要内容,如果未能解决你的问题,请参考以下文章

andorid jar/库源码解析之apktool.jar

andorid jar/库源码解析之apktool.jar

andorid jar/库源码解析之Bolts

andorid jar/库源码解析之RxJava2

andorid jar/库源码解析 EventBus

andorid jar/库源码解析