Frida Hook

作者: 约你一起偷西瓜 | 来源:发表于2020-08-05 17:18 被阅读0次

    - Hook Dlopen

    //第一种方式(针对较老的系统版本)
    var dlopen = Module.findExportByName(null, "dlopen");
    console.log(dlopen);
    if(dlopen != null){
        Interceptor.attach(dlopen,{
            onEnter: function(args){
                var soName = args[0].readCString();
                console.log(soName);
                if(soName.indexOf("libc.so") != -1){
                    this.hook = true;
                }
            },
            onLeave: function(retval){
                if(this.hook) { 
                    dlopentodo();
                };
            }
        });
    }
    
    //第二种方式(针对新系统版本)
    var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
    console.log(android_dlopen_ext);
    if(android_dlopen_ext != null){
        Interceptor.attach(android_dlopen_ext,{
            onEnter: function(args){
                var soName = args[0].readCString();
                console.log(soName);
                if(soName.indexOf("libc.so") != -1){
                    this.hook = true;
                }
            },
            onLeave: function(retval){
                if(this.hook) {
                    dlopentodo();
                };
            }
        });
    }
    function dlopentodo(){
        //todo ...
    }
    

    - Java堆栈打印

    function show_java_trace(){
        Java.perform(function(){
            var MessageDigest = Java.use("java.security.MessageDigest");
            MessageDigest.digest.overload().implementation = function(){
                //var stack = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new());
                var stack = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new());
                console.log(stack);
                return this.digest();
            }
        });
    }
    

    - Native堆栈打印

    function show_native_trace(){
        var func = Module.findBaseAddress("libil2cpp.so").add(0x56FCA8);
        Interceptor.attach(func, {
            onEnter: function(args){
                console.log("called from:\n"+
                    Thread.backtrace(this.context,Backtracer.ACCURATE)
                    .map(DebugSymbol.fromAddress).join("\n"));
            },
            onLeave: function(retval){
                
            }
        });
    }
    

    - HookJava中的loadLibrary并打印堆栈

    function hook_library(){
        Java.perform(function() {
            const System = Java.use('java.lang.System');
            const Runtime = Java.use('java.lang.Runtime');
            const VMStack = Java.use('dalvik.system.VMStack');
    
            System.loadLibrary.implementation = function(library) {
                try {
                    console.log('System.loadLibrary("' + library + '")');
                    console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
                    const loaded = Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), library);
                    return loaded;
                } catch(ex) {
                    console.log(ex);
                }
            };
    
            System.load.implementation = function(library) {
                try {
                    console.log('System.load("' + library + '")');
                    console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
                    const loaded = Runtime.getRuntime().load0(VMStack.getCallingClassLoader(), library);
                    return loaded;
                } catch(ex) {
                    console.log(ex);
                }
            };
        });
    }
    

    - String转Byte

    function stringToBytes(str) {  
        var ch, st, re = []; 
        for (var i = 0; i < str.length; i++ ) { 
            ch = str.charCodeAt(i);  
            st = [];                 
            do {  
                st.push( ch & 0xFF );  
                ch = ch >> 8;          
            }    
            while ( ch );  
            re = re.concat( st.reverse() ); 
        }  
        return re;  
    }
    

    - hexToBytes

    function hexToBytes(str) {
        var pos = 0;
        var len = str.length;
        if (len % 2 != 0) {
            return null;
        }
        len /= 2;
        var hexA = new Array();
        for (var i = 0; i < len; i++) {
            var s = str.substr(pos, 2);
            var v = parseInt(s, 16);
            hexA.push(v);
            pos += 2;
        }
        return hexA;
    }
    

    - bytes2Hex

    function bytes2Hex(arr) {
        var str = "[";
        for (var i = 0; i < arr.length; i++) {
            var z = parseInt(arr[i]);
            if (z < 0) z = 255 + z;
            var tmp = z.toString(16);
            if (tmp.length == 1) {
                tmp = "0" + tmp;
            }
            str = str + " " + tmp;
        }
        return (str + " ]").toUpperCase();
    }
    

    - ArrayBuffer 转换

    function ab2Hex(buffer) {
        var arr = Array.prototype.map.call(new Uint8Array(buffer), function (x) {return ('00' + x.toString(16)).slice(-2)}).join(" ").toUpperCase();
        return "[" + arr + "]";
    }
     
    function ab2Str(buffer) {
        return String.fromCharCode.apply(null, new Uint8Array(buffer));
    }
    

    - jstring, jbytearray显示

    function jstring2Str(jstring) {
       var ret;
       Java.perform(function() {
           var String = Java.use("java.lang.String");
           ret = Java.cast(jstring, String);
       });
       return ret;
    }
     
    function jbyteArray2Array(jbyteArray) {
       var ret;
       Java.perform(function() {
           var b = Java.use('[B');
           var buffer = Java.cast(jbyteArray, b);
           ret = Java.array('byte', buffer);
       });
       return ret;
    }
    

    - 主线程调用

    function RunOnMain(){
        Java.perform(function(){
            var cls_main = null
            //获取Context
            Java.choose("com.lzy.ndk.MainActivity",{
                onMatch:function(clazz){
                    cls_main = clazz
                },
                onComplete:function(){}
            })
            //动态注册一个类实现Runnable方法
            var cls_run = Java.registerClass({
                name:"com.lzy.frida.runnable",
                implements:[Java.use("java.lang.Runnable")],
                //创建类成员变量
                fields:{
                    description: 'java.lang.String',
                    limit: 'int'
                },
                //创建方法以及重载方法的用法
                methods:{
                    run:function(){
                        Java.use("android.widget.Toast").makeText(cls_main,Java.use("java.lang.String").$new("this is a test Toast"),1).show()
                    
                    },
                    add:[{
                        returnType:'java.lang.String',
                        argumentTypes:['java.lang.String','java.lang.String'],
                        implementation:function(str1,str2){
                            return str1+"+++"+str2
                        }
                    },
                    {
                        returnType:'java.lang.String',
                        argumentTypes:['java.lang.String'],
                        implementation:function(str1){
                            return str1+"==="
                        }
                    }
                    ]
                }
            })
            //这里的实现主线程调用方法很多,这里举例一种
            //1.随便在MainActivity找一个View,View.post(Runnable)
            cls_main.bt1.value.post(cls_run.$new())
            //2.Activity的方法runOnUiThread()
            cls_main.runOnUiThread(cls_run.$new())
            //3.new Handler(getMainLooper()).post()
            Java.use("android.os.Handler").$new(cls_main.getMainLooper()).post(cls_run.$new())
            //4.Java.scheduleOnMainThread(function(){}) 不推荐,不好用总是出问题
            Java.scheduleOnMainThread(function(){
                console.log(Java.isMainThread())
            })
        })
    }
    

    - 写文件(需要存储卡读写权限)

    function write_file1() {
        //frida 的api来写文件
        var file = new File("/sdcard/a.txt", "w")
        file.write("123123123")
        file.flush()
        file.close()
    }
    
    
    function write_file2() {
        //把C函数定义为NativeFunction来写文件
        var addr_fopen = Module.findExportByName("libc.so", "fopen")
        var addr_fputs = Module.findExportByName("libc.so", "fputs")
        var addr_fclose = Module.findExportByName("libc.so", "fclose")
    
        console.log("addr_fopen:", addr_fopen, "addr_fputs:", addr_fputs, "addr_fclose:", addr_fclose)
        var fopen = new NativeFunction(addr_fopen, "pointer", ["pointer", "pointer"])
        var fputs = new NativeFunction(addr_fputs, "int", ["pointer", "pointer"])
        var fclose = new NativeFunction(addr_fclose, "int", ["pointer"])
    
        var filename = Memory.allocUtf8String("/sdcard/a.txt")
        var open_mode = Memory.allocUtf8String("w+")
        var file = fopen(filename, open_mode)
        console.log("fopen file:", file);
    
        var buffer = Memory.allocUtf8String("123123123")
        var ret = fputs(buffer, file)
        console.log("fputs ret:", ret)
    
        fclose(file)
    }
    

    - 批量断点定位调用

    function add_native_break_points(){
        
    // 结合Il2CppDumper使用,用于批量快速下断点,跟踪native函数调用
    // frida -U -f <PackageName> -l C:\Users\lzy\utils\bpoints.js --no-pause
    
    const soName = "libil2cpp.so"
    
    const arrayAddr =
        ['0x71541c', '0x715b38', '0x715be4', '0x715c61']
    
    const arrayName =
        ['GameManager$$Awake', 'GameManager$$GetParam', 'GameManager$$SaveParam', 'GameManager$$ActivatePrivacyButton']
    
    function breakPoints(){
    
        const soAddr = Module.findBaseAddress(soName);
        console.error('\nsoAddr:' + soAddr + "\n");
    
        Java.perform(function(){
            arrayAddr
                .map(function(temp){return soAddr.add(temp)})
                .forEach(function(value,index,array){
                    console.log("-------------------------");
                    console.log('currentAddr:' + value);
                    try{
                        funcTmp(value,index,arrayName);
                    }catch(e){
                        //Thumb指令集地址要加一
                        funcTmp(value.add(1),soAddr,index,arrayName);
                    }
                console.log("\t\t---->"+index,value+" is prepared ");
            })
            console.log("\n")
        })
    
        function funcTmp(currentAddr,index,arrayName){
            Interceptor.attach(currentAddr, {
                onEnter: function(args){
                    console.log("called : "+arrayName[index]+"  ----- addr : " + currentAddr.sub(soAddr) +"\n");
                    this.temp = currentAddr.sub(soAddr);
                    if(this.temp === 0xef3080) {
                        console.log('CCCryptorCreate called from:\n' +
                            Thread.backtrace(this.context, Backtracer.ACCURATE)
                                .map(DebugSymbol.fromAddress).join('\n') + '\n');
                    }
                },
                onLeave: function(retval){
    
                }
            });
        }
    }
    
    function hook_dlopen() {
        // const dlopen = Module.findExportByName(null, "dlopen");
        const dlopen = Module.findExportByName(null, "android_dlopen_ext");
    
        if (dlopen != null) {
            Interceptor.attach(dlopen, {
                onEnter: function (args) {
                    var l_soName = args[0].readCString()
                    console.log(l_soName)
                    if (l_soName.indexOf(soName) != -1) {
                        this.hook = true
                    }
                },
                onLeave: function (retval) {
                    if (this.hook) {
                        console.warn("\nLoaded "+soName + " add break points")
                        breakPoints()
                    }
                }
            })
        }
    }
    
    setImmediate(hook_dlopen())
    }
    

    配合Il2CppDumper生成的script.json食用更香

    import json
    
    if __name__ == '__main__':
    
        # json地址
        f = open('C:\\Users\\lzy\\Desktop\\Il2CppDumper-v6.2.1\\script.json',encoding='utf-8')
        # 查找的字符串
        searchStr = "Network"
    
        j = json.load(f)
        ScriptMethod = j['ScriptMethod']
        ScriptMetadataMethod = j['ScriptMetadataMethod']
    
        temp_name = []
        temp_addr = []
    
        for temp in ScriptMethod:
            if searchStr in temp["Name"]:
                temp_name.append(temp["Name"])
                temp_addr.append(hex(temp["Address"]))
    
        print('Found : '+str(len(temp_name))+' Function')
        print('--------------------------------')
        print(temp_addr)
        print('--------------------------------')
        print(temp_name)
        print('--------------------------------')
    

    - TracerPid fgets 反调试

        var anti_fgets = function () {
            show_log("anti_fgets");
            var fgetsPtr = Module.findExportByName("libc.so", "fgets");
            var fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer', 'int', 'pointer']);
            Interceptor.replace(fgetsPtr, new NativeCallback(function (buffer, size, fp) {
                var retval = fgets(buffer, size, fp);
                var bufstr = Memory.readUtf8String(buffer);
                if (bufstr.indexOf("TracerPid:") > -1) {
                    Memory.writeUtf8String(buffer, "TracerPid:\t0");
                    // dmLogout("tracerpid replaced: " + Memory.readUtf8String(buffer));
                }
                return retval;
            }, 'pointer', ['pointer', 'int', 'pointer']));
        };
    
    

    - 日志打印

    function log(str){
        var threadid = Process.getCurrentThreadId()
        var date = new Date()
        var month = date.getMonth() + 1
        var strDate = date.getDate()
        var hour = date.getHours()
        var Minutes = date.getMinutes()
        var Seconds = date.getSeconds()
        if (month >= 1 && month <= 9) {
            month = "0" + month
        }
        if (strDate >= 0 && strDate <= 9) {
            strDate = "0" + strDate
        }
        if (hour >= 0 && hour <= 9) {
            hour = "0" + hour
        }
        if (Minutes >= 0 && Minutes <= 9) {
            Minutes = "0" + Minutes
        }
        if (Seconds >= 0 && Seconds <= 9) {
            Second = "0" + Seconds
        }
        var currentDate = date.getFullYear() + "-" + month + "-" + strDate
                + " " + hour + ":" + Minutes + ":" + Seconds
        var log = "["+threadid+"][" + currentDate + "] --- " + str
        console.log('\x1b[3' + '6;01' + 'm', log, '\x1b[39;49;00m')
    }
    
    --------------------------------------------------------
    
    //native日志函数
    var nativeLogF
    var nativeLogPossible
    
    function initNativeLog() {
        try {
            var native_log_function = Module.findExportByName(null, "__android_log_print")
            native_log_function ? (nativeLogPossible = true, nativeLogF = new NativeFunction(native_log_function, "int", ["int", "pointer", "pointer", "...", "pointer"])) : LOG("initNativeLog Cound not find export")
        } catch (e) {
            LOG(e.message)
        }
    }
    
    function NLOG(msg) {
        try {
            if (!nativeLogPossible) return;
            var str_tip = Memory.allocUtf8String("Native"),
                str_formart = Memory.allocUtf8String("%s"),
                str_msg = Memory.allocUtf8String(msg)
            nativeLogF(6, str_tip, str_formart, str_msg)
        } catch (e) {
            console.error(e.message)
        }
    }
    
    function JLOG(msg) {
        try {
            Java.available && Java.perform(function () {
                Java.use("android.util.Log").e("Native", msg)
            })
        } catch (e) { }
    }
    

    - frida api 翻译指定内存位置的代码汇编

    function toAssembler(pointer,length){
        length = arguments[1] ? arguments[1] : 10
        var baseAddr = Module.findBaseAddress(soName)
        console.error("-----------------------------------")
        console.warn("Module Addr = \t" + baseAddr)
        console.error("------------------")
        var target = ptr(pointer)
        for (var t = 0; t < length; t++) {
            var t_addr = target.add(Process.pointerSize * t)
            console.log(t_addr + " -> " + t_addr.sub(baseAddr) + "\t" + Instruction.parse(t_addr))
        }
        console.error("-----------------------------------")
    }
    

    - 内存dump so(脱壳upx壳)

    function dump_so(so_name) {
        Java.perform(function () {
            var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
            var dir = currentApplication.getApplicationContext().getFilesDir().getPath();
            var libso = Process.getModuleByName(so_name);
            console.error("------------------------------");
            console.warn("[name]:", libso.name);
            console.warn("[base]:", libso.base);
            console.warn("[size]:", libso.size);
            console.warn("[path]:", libso.path);
            console.error("------------------------------");
            var file_path = dir + "/" + libso.name + "_" + libso.base + "_" + ptr(libso.size) + ".so";
            var file_handle = new File(file_path, "wb");
            if (file_handle && file_handle != null) {
                Memory.protect(ptr(libso.base), libso.size, 'rwx');
                var libso_buffer = ptr(libso.base).readByteArray(libso.size);
                file_handle.write(libso_buffer);
                file_handle.flush();
                file_handle.close();
                console.log("[dump]:", file_path);
            }
        });
    }
    

    - 动态加载Dex / SO

    function loadDex(){
        //这里只是提一下可以使用Frida提供的Api加载dex,你也可以解包再打包,但是显然这个方便得多
        //手动去加载一些工具类(Gson,AndroidUtilCode,自己写的工具类等等)
        Java.openClassFile("/data/local/tmp/helper.dex").load()
        var gson = Java.use("com.google.gson.Gson").$new()
        ...
    }
    
    const m = Module.load("libnative.so');
    

    - 计划任务

    function ScheduledTask(){
        //用在Spawn启动的时候
        setImmediate(function(){
            console.log("立即执行,只执行一次")
        })
        setTimeout(function(){
            console.log("一秒后执行,只执行一次")
        },1000)
        //Frida Api
        setInterval(function(){
            console.log("每隔一秒执行一次")
        },1000)
        // Java Api
        Java.perform(function(){
            Java.registerClass({
                name:"com.lzy.frida.tsk",
                superClass:Java.use("java.util.TimerTask"),
                methods:{
                    run:function(){
                        console.log("等待两秒后每隔一秒调用一次")
                    }
                }
            })
            Java.use("java.util.Timer").$new().schedule(Java.use("com.lzy.frida.tsk").$new(),2000,1000)
        })
    }
    

    - JNI函数Trace Demo

    function TraceJni(){
        Java.perform(function(){
    
            var pSize = Process.pointerSize
            var env = Java.vm.getEnv()
    
            //JNI函数相对env偏移位置参考:
            //https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#NewStringUTF
            var GetStaticMethodID = 113,findclass = 6,RegisterNatives = 215;
        
            function getNativeAddress(idx) {
                return env.handle.readPointer().add(idx * pSize).readPointer()
            }
    
            Interceptor.attach(getNativeAddress(findclass),{
                onEnter:function(args){
                    console.error("-------------findClass-------------")
                    console.warn("env\t--->\t"+args[0])
                    console.warn("class\t--->\t"+args[1].readCString())
                },
                onLeave:function(retval){}
            })
    
            Interceptor.attach(getNativeAddress(GetStaticMethodID),{
                onEnter:function(args){
                    console.error("\n-------------GetStaticMethodID-------------")
                    console.warn(args[0])
                    console.warn(args[1])
                    console.warn(args[2].readCString())
                },
                onLeave:function(retval){}
            })
    
            //RegisterNative结构体参照:
            //https://android.googlesource.com/platform/libnativehelper/+/master/include_jni/jni.h#129
            Interceptor.attach(getNativeAddress(RegisterNatives), {
                onEnter: function(args) {
                    console.log(parseInt(args[3]))
                    for (var i = 0,nMethods = parseInt(args[3]); i < nMethods; i++) {
                        var structSize = pSize * 3; // = sizeof(JNINativeMethod)
                        var methodsPtr = ptr(args[2]);
                        var signature = methodsPtr.add(i * structSize + pSize).readPointer();
                        var fnPtr = methodsPtr.add(i * structSize + (pSize * 2)).readPointer(); // void* fnPtr
                        var jClass = jclassAddress2NameMap[args[0]].split('/');
                        var methodName = methodsPtr.add(i * structSize).readPointer().readCString();
                        console.log('\x1b[3' + '6;01' + 'm', JSON.stringify({
                            module: DebugSymbol.fromAddress(fnPtr)['moduleName'],
                            // https://www.frida.re/docs/javascript-api/#debugsymbol
                            package: jClass.slice(0, -1).join('.'),
                            class: jClass[jClass.length - 1],
                            method: methodName,
                            // methodsPtr.readPointer().readCString(), // char* name
                            signature: signature.readCString(),
                            // char* signature TODO Java bytecode signature parser { Z: 'boolean', B: 'byte', C: 'char', S: 'short', I: 'int', J: 'long', F: 'float', D: 'double', L: 'fully-qualified-class;', '[': 'array' } https://github.com/skylot/jadx/blob/master/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java
                            address: fnPtr
                        }), '\x1b[39;49;00m');
                    }
                }
            });
        })
    }
    

    - Native函数断点/调用/替换

    //主动调用native函数
        var soAddr = Module.findBaseAddress("libil2cpp.so");
        new NativeFunction(soAddr.add(0x4c33b0),"void",['pointer'])(Java.vm.tryGetEnv())
    
    //替换native函数
    //支持的类型:void,pointer,int,uint,long,ulong,char,uchar,float,double,int8,uint8,int16,uint16,int32,uint32,int64,uint64,bool
        Interceptor.replace(new NativeFunction(soAddr.add(0x58F0F4),'void', ['pointer']), new NativeCallback(function (arg) {
            console.log("called from:\n"+
                    Thread.backtrace(this.context,Backtracer.FUZZY)
                    .map(DebugSymbol.fromAddress).join("\n"));
        }, 'void', ['pointer']));
    
    //拦截native函数
        Interceptor.attach(soAddr.add(0xb7a93c),{
            onEnter:function(arg){
                console.log("called 0xb7a93c")
            },
            onLeave:function(retval){
                console.warn(retval)
            }
        })
    

    - 获取类型

    function getParamType(obj) {
        return obj == null ? String(obj) : Object.prototype.toString.call(obj).replace(/\[object\s+(\w+)\]/i, "$1") || "object";
    }
    

    - hook 所有重载函数

    function hookAllOverloads(targetClass, targetMethod) {
        Java.perform(function () {
             var targetClassMethod = targetClass + '.' + targetMethod;
             var hook = Java.use(targetClass);
             var overloadCount = hook[targetMethod].overloads.length;
             for (var i = 0; i < overloadCount; i++) {
                    hook[targetMethod].overloads[i].implementation = function() {
                         var retval = this[targetMethod].apply(this, arguments);
                         //这里可以打印结果和参数
                         return retval;
                     }
                  }
       });
     }
    
    /**
     * 这里记录一下杠精日常(durk不必这么操作):用java反射来实现frida的函数调用
     * 详情见下代码,归纳一下来说就是:
     * 获取反射方法的时候传递的是基本数据类型,调用反射方法的时候参数用基础类型的封装类型
     */
    function java_invoke(){
    
        /**
         * java code clas ==> com.lzy.dobbytest.test
         * 
         *  public void showLog(int a){
                System.out.println("this is a string showLog! "+a);
            }
    
            public void showLog(String a){
                System.out.println("this is a string showLog! "+a + "String");
            }
    
            public void showLog(char a){
                System.out.println("this is a string showLog! "+a + "Int");
            }
    
            public void showLog(byte a){
                System.out.println("this is a string showLog! "+a + "Byte");
            }
    
            public void showLog(boolean a,int  b){
                System.out.println("this is a string showLog! "+a + b + "boolean int");
            }
        }
        */
    
        Java.perform(function(){
    
            var cls_utils = Java.use("com.lzy.dobbytest.MD5Utils")
            var newCls = cls_utils.$new()
            var str_obj = "java.lang.Object"
    
            //拿到基本数据类型
            var type_int = Java.use("java.lang.Class").getPrimitiveClass("int") 
            var type_boolean = Java.use("java.lang.Class").getPrimitiveClass("boolean")
            var type_byte = Java.use("java.lang.Class").getPrimitiveClass("byte")
    
            //String 参数的方法
            var String = Java.use("java.lang.String");
            var method = cls_utils.class.getDeclaredMethod("showLog",Java.array(str_obj,[String.class]))
            method.invoke(newCls,Java.array(str_obj,["test"]))  //这里有自动的类型转换 String.$new("test") == "test"
    
            //Int 参数的方法
            var method = cls_utils.class.getDeclaredMethod("showLog",Java.array(str_obj,[type_int]))
            method.invoke(newCls,Java.array(str_obj,[Java.use("java.lang.Integer").$new('12')])) //等价于Java.use("java.lang.Integer").valueOf('12')
    
            //Boolean 参数的方法
            var method = cls_utils.class.getDeclaredMethod("showLog",Java.array(str_obj,[type_boolean]))
            method.invoke(newCls,Java.array(str_obj,[Java.use("java.lang.Boolean").$new('true')]))
             
            //Byte 参数的方法
            var method = cls_utils.class.getDeclaredMethod("showLog",Java.array(str_obj,[type_byte]))
            method.invoke(newCls,Java.array(str_obj,[Java.use("java.lang.Byte").$new(String.$new('92'))]))
    
            //Boolean 和 Int 参数的方法
            var method = cls_utils.class.getDeclaredMethod("showLog",Java.array(str_obj,[type_boolean,type_int]))
            method.invoke(newCls,Java.array(str_obj,[Java.use("java.lang.Boolean").$new('true'),Java.use("java.lang.Integer").$new('12')]))
             
        })
    }
    



    pip的源码安装在site-packages目录下

    frida 使用小技巧

    • 免root使用frida → 重打包
      1. 反编译后重写将 libfrida-gadget.so 打包进app
        并在smali代码中加载
        • 注意
          这里去下载的so一定得是和你本地当前的frida版本一致
          使用pip list 查看本地的版本
        const-string v1, "frida-gadget"
        invoke-static {v1}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V
      
      1. 为app添加网络访问权限,以便frida-gadget可以打开套接字
        <uses-permission android:name="android.permission.INTERNET" />
      
      1. 出现日志:Frida: Listening on TCP port 27042,这时候是处于一个等待frida连接状态
      2. frida-ps -U可以查看到当前程序PID 以及默认的名字Gadget
      3. 使用以上的PID或者是默认的名字Gadget连接frida
    • 免root使用frida → ptrace注入

      (这里我是用的是别人对其封装一层方便使用的 脚本原脚本在这里

      1. 首先是在linux(kali)环境下完成
      2. git clone请在linux下完成(坑!别再windows下拷贝过去,可能出现编码问题)
      3. adb shell am start -D -n com.lzy.xxxx/.MainActivity以调试模式启动
        除了命令启动,也可以再手机开发者选项中找到调试应用并开启等待调试器也行
        (这里的话仅再app中debuggable=“true“时候能这么启动)
      4. 再就是参照作者写的,一句话就搞定注入即可
        ./jdwp-lib-injector.sh frida-gadget.so
    • root使用frida → root 身份运行 frida_server

      (这也算是我们使用最频繁的场景)

      1. 首先还是去官网搞一个frida_server
      2. push 到 /data/local/tmp/frida/目录之下
      3. 在上面目录给android_server权限 chmod 777 ./*
      4. ./frida_server 跑起来 或者是 cmd 中使用
        adb shell "su -c ./data/local/tmp/frida/frida_server"
        (后面这条命令不是所有手机都适用)

      https://www.jianshu.com/p/d2d7da75990a

    • 重打包或者root,LIEF改so,实现对frida-gadget.so的加载

      参考原文 原文Demo
      这种操作也是非常的方便实用,前提也是需要重打包apk,
      当然你手机有root的时候直接copy替换原so也是可行的
      (这种操作也有弊端,执行不到jni_onload,只会执行到init_array的函数)

      1. 首先还是拿到我们的工具LIEF
        (不建议使用windows踩坑,心态容易崩,直接上linux,用pip安装上就是)
      2. 然后写一个贼简单的脚本
        import lief
        libnative = lief.parse("libnative-lib.so")    //待修改的so
        libnative.add_library("libfrida-gadget.so")   //在frida官网搞来的和本地版本一致gadget
        libnative.write("libfrida-gadget1.so")        //输出的文件名称
      
      1. 然后用我们新的so改回原名称,替换原so,并同时把gadget.so放回lib重打包即可
    • 使用Xpatch实现免Root的Frida功能

    • root使用frida → 本地化脚本调用
      1. 重打包部分和上文 [免root使用frida → 重打包] 一样
      2. 新增一步操作,创建一个和lib同名但是后缀为conf.so的文本文件
        libfrida-gadget.so → libfrida-gadget.config.so
        libfrida-gadget.config.so的文本内容为以下
      {
        "interaction": {
          "type": "script",
          "path": "/sdcard/script.js",
          "on_change": "reload"
        }
      }
      
      1. 在上述位置放上我们的hook脚本即可
        参考文章:文章 官网教程
    实现frida的注入有以下方法
    • 重打包手动注入frida-gadget.so(使用方法一)
    • 改机重刷系统,实现全局可调式(使用方法二)
    • 使用xposed的插件BDOpener,实现全局可调式(使用方法二)
    • 修改app清单文件重打包(使用方法二)
    • Root身份运行frida_server(使用方法三)

    相关文章

      网友评论

        本文标题:Frida Hook

        本文链接:https://www.haomeiwen.com/subject/voqorktx.html