Frida Hook

- 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 writeFile() {
    //frida 的api来写文件
    var file = new File("/sdcard/a.txt", "w")
    file.write("123123123")
    file.flush()
    file.close()
}

/**
 * 从内存指定位置读取指定长度并写入指定文件目录
 * PS:需要APP有文件读写权限
 * @param {String}  path    存放路径
 * @param {String}  name    文件名
 * @param {Number}  mPtr    内存起始位置
 * @param {Number}  length  内存区域的长度
 */
function writeFile(path,name,mPtr,length) {
    var fopen = new NativeFunction(Module.findExportByName("libc.so", "fopen"), "pointer", ["pointer", "pointer"])
    var fwrite = new NativeFunction(Module.findExportByName("libc.so", "fwrite"), "int", ["pointer",'int','int', "pointer"])
    var fclose = new NativeFunction(Module.findExportByName("libc.so", "fclose"), "int", ["pointer"])
    var system=new NativeFunction(Module.findExportByName("libc.so","system"),"pointer",["pointer"])
    system(Memory.allocUtf8String("mkdir -p "+path))
    var filename = Memory.allocUtf8String(path+"/"+name+".dll")
    var open_mode = Memory.allocUtf8String("a")
    var file = fopen(filename, open_mode)
    fwrite(mPtr,1,length,file)
    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也是可行的
    (_init,constructor,jni_onload()这里的jni_onload并非立即执行,实际demo中感觉有一点点的延迟,欢迎大佬科普为什么延迟)

    1. 首先还是拿到我们的工具LIEF
    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(使用方法三)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266