eumnq8 发表于 2021-4-27 13:06:13

frida android 常用脚本备忘

1、//基础数据类型char
var char= Java.use("java.lang.Class").getPrimitiveClass("char")

2、挂钩dlopen函数
//第一种方式(针对较老的系统版本)
var dlopen = Module.findExportByName(null, "dlopen");
console.log(dlopen);
if(dlopen != null){
    Interceptor.attach(dlopen,{
      onEnter: function(args){
            var soName = args.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.readCString();
            console.log(soName);
            if(soName.indexOf("libc.so") != -1){
                this.hook = true;
            }
      },
      onLeave: function(retval){
            if(this.hook) {
                dlopentodo();
            };
      }
    });
}
function dlopentodo(){
    //todo ...
}

3、打印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();
      }
    });
}

4、打印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){
            
      }
    });
}

5、挂钩Java中的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);
            }
      };
    });
}

6、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;
}

7、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;
}

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

9、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));
}

10、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;
}

11、主线程调用
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:,
            //创建类成员变量
            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())
      })
    })
}

12、批量断点定位调用
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+"----- 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.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())
}

13、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']));
    };

14、日志打印
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')
}

15、内存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(":", libso.name);
      console.warn(":", libso.base);
      console.warn(":", libso.size);
      console.warn(":", 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(":", file_path);
      }
    });
}

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

17、计划任务
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)
    })
}

18、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)
                console.warn("class\t--->\t"+args.readCString())
            },
            onLeave:function(retval){}
      })

      Interceptor.attach(getNativeAddress(GetStaticMethodID),{
            onEnter:function(args){
                console.error("\n-------------GetStaticMethodID-------------")
                console.warn(args)
                console.warn(args)
                console.warn(args.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))
                for (var i = 0,nMethods = parseInt(args); i < nMethods; i++) {
                  var structSize = pSize * 3; // = sizeof(JNINativeMethod)
                  var methodsPtr = ptr(args);
                  var signature = methodsPtr.add(i * structSize + pSize).readPointer();
                  var fnPtr = methodsPtr.add(i * structSize + (pSize * 2)).readPointer(); // void* fnPtr
                  var jClass = jclassAddress2NameMap].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,
                        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');
                }
            }
      });
    })
}

19、Native Log日志Hook
#int __android_log_print(int prio, const char* tag, const char* fmt, ...)
function hookLog() {
    Interceptor.attach(Module.findExportByName("liblog.so","__android_log_print"), {
      onEnter: function (args) {
            // var level = ""
            // var level_i = parseInt(args)
            // if(level_i == 0){
            //   level = "ANDROID_LOG_UNKNOWN"
            // }else if(level_i == 1){
            //   level = "ANDROID_LOG_DEFAULT"
            // }else if(level_i == 2){
            //   level = "ANDROID_LOG_VERBOSE"
            // }else if(level_i == 3){
            //   level = "ANDROID_LOG_DEBUG"
            // }else if(level_i == 4){
            //   level = "ANDROID_LOG_INFO"
            // }else if(level_i == 5){
            //   level = "ANDROID_LOG_WARN"
            // }else if(level_i == 6){
            //   level = "ANDROID_LOG_ERROR"
            // }else if(level_i == 7){
            //   level = "ANDROID_LOG_FATAL"
            // }else if(level_i == 8){
            //   level = "ANDROID_LOG_SILENT"
            // }
            // if(level_i==5){
            //   console.warn(level+"\t"+args.readCString()+"\t"+args.readCString())
            // }else if(level_i > 5){
            //   console.error(level+"\t"+args.readCString()+"\t"+args.readCString())
            // }else{
            //   console.log(level+"\t"+args.readCString()+"\t"+args.readCString())
            // }
            console.log(args.readCString()+"\t"+args.readCString())
      }
    });
}

20、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)
      }
    })

21、获取参数类型
function getParamType(obj) {
    return obj == null ? String(obj) : Object.prototype.toString.call(obj).replace(/\/i, "$1") || "object";
}

22、hook 所有重载函数
function hookAllOverloads(targetClass, targetMethod) {
    Java.perform(function () {
         var targetClassMethod = targetClass + '.' + targetMethod;
         var hook = Java.use(targetClass);
         var overloadCount = hook.overloads.length;
         for (var i = 0; i < overloadCount; i++) {
                hook.overloads.implementation = function() {
                     var retval = this.apply(this, arguments);
                     //这里可以打印结果和参数
                     return retval;
               }
            }
   });
}

页: [1]
查看完整版本: frida android 常用脚本备忘