Inline Hook 原理
修改原函数开头的汇编指令,跳转到自定义的函数进行hook,hook结束后,恢复原函数汇编指令,执行原流程
对抗
保护内存不可读写,因为inlinehook需要在内存中拿到函数地址
轮询crc校验内存是否被修改,被改了再改回来阻止hook
Plt/Got Hook Plt & Got
原理
利用linux的延迟绑定机制,也就是找函数的时候 plt–>got –> 实际地址
Got –> 实际地址 的映射只会被初始化映射一次,后面就直接按映射地址去找了
因此可以通过修改 Got –> 实际地址 的映射【也就是修改GOT表的内容】来实现执行自定义的函数
只要符号表没损坏,函数是动态注册还是非导出native方法都可以用got/plt hook
特点
可以hook 大量系统API,但难以精准hook某次函数调用,因此更适合oem厂商
got/plt表中的函数不全
需要在自定义函数的结尾调用原函数恢复程序执行
Exception Hook
对想要hook的地址设置非法指令,触发SIGILL异常,利用异常去实现hook
Objection Use 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 jobs list jobs kill <job_id> memory list modules memory list exports <name.so> [--json name.json] memory dump all <filename> memory dump from_base <startaddress> <字节数> <文件> android hooking list classes <class name > android hooking list class_methods android hooking list class_loaders android hooking list activities android hooking list receives android hooking list services android heap search instances <类名> android hooking search methods <keyword> android hooking search classes <keyword> android intent launch_activity <活动名> android hooking generate simple <类名> android hooking watch class <类名> [--dump-args --dump-backtrace --dump-return ] android hooking watch class_method <方法名> [--dump-args --dump-backtrace --dump-return ] android hooking watch class_method <方法名> "参数1, 参数2" --dump-args --dump-backtrace --dump-return
Frida 基本使用
加强版:
1 2 3 4 5 6 7 8 9 10 11 12 frida-ps -Ua frida -U -f 包名 -l frida.js frida -U 包名 -l frida.js fs -l 0.0 .0 .0 :1234 adb forward tcp:1234 tcp:1234 frida -H 127.0 .0 .1 :1234 包名 -l hook.js netstat -an | grep LISTEN
问题 1. hook失败一次后打开APK一直提示wait for debugger
开发者选项–>等待调试程序–>关闭
2. 端口占用 1 2 3 ps -ef | grep frida ps -ef | grep hluda kill -9 PID
Java层hook 常规调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 function main ( ){ Java .perform (function ( ){ var hook = Java .use ('com.example.androiddemo.Activity.FridaActivity1' ); hook.a .implementation = function (str ){ return false ; } }) } setImmediate (main)function main ( ){ Java .perform (function ( ){ let Login = Java .use ("com.example.androiddemo.Activity.LoginActivity" ); Login [functionName].overload ('java.lang.String' ,'java.lang.String' ).implementation = function (str,str2 ){ return passwd; } }) } setImmediate (main)> 在重名的变量前加一个_下划线 > 指定包名加上内部类名: > com.example .androiddemo .Activity .FridaActivity4$InnerClasses Java .enumerateClassLoaders ({ onMatch : function (loader ) { try { if (loader.findClass ("com.example.androiddemo.Dynamic.DynamicCheck" )){ console .log ("Successfully found loader" ) console .log (loader); Java .classFactory .loader = loader; } } catch (error){ console .log ("find error:" + error) } }, onComplete : function ( ) { console .log ("end1" ) } })
主动调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 private static boolean static_bool_var = false ; private boolean bool_var = false ; private static void setStatic_bool_var ( ) { static_bool_var = true ; } private void setBool_var ( ) { this .bool_var = true ; } function main ( ){ Java .perform (function ( ){ var MainActivity = Java .use ('com.example.retest.MainActivity' ) MainActivity .staticsecret (); MainActivity .static_bool_var .value = true ; Java .choose ('com.example.retest.MainActicity' ,{ onMatch :function (ins ){ ins.secret () ins.bool_var .value = true ; }, onComplete : function ( ){ console .log ('search Complete' ); } }) }) } setImmediate (main)
Log类Hook 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Java .perform (function ( ) { function hook_log ( ) { console .log ("hook log ----------------:" ); var Log = Java .use ('android.util.Log' ); Log .v .overload ('java.lang.String' , 'java.lang.String' ).implementation = function (tag, content ) { console .log (tag + " v" , content); }; Log .d .overload ('java.lang.String' , 'java.lang.String' ).implementation = function (tag, content ) { console .log (tag + " d" , content); }; Log .w .overload ('java.lang.String' , 'java.lang.String' ).implementation = function (tag, content ) { console .log (tag + " w" , content); }; Log .i .overload ('java.lang.String' , 'java.lang.String' ).implementation = function (tag, content ) { console .log (tag + " i" , content); }; Log .e .overload ('java.lang.String' , 'java.lang.String' ).implementation = function (tag, content ) { console .log (tag + " e" , content); }; } hook_log () })
StackHook 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function printStack (name ) { Java .perform (function ( ) { var Exception = Java .use ("java.lang.Exception" ); var ins = Exception .$new("Exception" ); var straces = ins.getStackTrace (); if (straces != undefined && straces != null ) { var strace = straces.toString (); var replaceStr = strace.replace (/,/g , "\\n" ); console .log ("=============================" + name + " Stack strat=======================" ); console .log (replaceStr); console .log ("=============================" + name + " Stack end=======================\r\n" ); Exception .$dispose(); } }); } function showStacks ( ) { Java .perform (function ( ) { console .log ( Java .use ("android.util.Log" ) .getStackTraceString ( Java .use ("java.lang.Throwable" ).$new() ) ); }) }
OnClickListener 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 var jclazz = null ;var jobj = null ;function getObjClassName (obj ) { if (!jclazz) { var jclazz = Java .use ("java.lang.Class" ); } if (!jobj) { var jobj = Java .use ("java.lang.Object" ); } return jclazz.getName .call (jobj.getClass .call (obj)); } function watch (obj, mtdName ) { var listener_name = getObjClassName (obj); var target = Java .use (listener_name); if (!target || !mtdName in target) { return ; } target[mtdName].overloads .forEach (function (overload ) { overload.implementation = function ( ) { console .log ("[WatchEvent] " + mtdName + ": " + getObjClassName (this )) return this [mtdName].apply (this , arguments ); }; }) } function OnClickListener ( ) { Java .perform (function ( ) { Java .use ("android.view.View" ).setOnClickListener .implementation = function (listener ) { if (listener != null ) { watch (listener, 'onClick' ); } return this .setOnClickListener (listener); }; Java .choose ("android.view.View$ListenerInfo" , { onMatch : function (instance ) { instance = instance.mOnClickListener .value ; if (instance) { console .log ("mOnClickListener name is :" + getObjClassName (instance)); watch (instance, 'onClick' ); } }, onComplete : function ( ) { } }) }) } setImmediate (OnClickListener );
VpnHook 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function main ( ){ Java .perform (function ( ){ Java .use ("android.net.NetworkInfo" ).isConnected .implementation = function ( ){ console .log ("first called!" ) return false } Java .use ("java.net.NetworkInterface" ).getName .implementation = function ( ){ console .log ("second called!" ) return "" } Java .use ("android.net.NetworkCapabilities" ).hasTransport .implementation =function ( ){ console .log ("third called!" ) return false } }) } setImmediate (main)
SSLHook 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function hook_ssl ( ) { Java .perform (function ( ) { var ClassName = "com.android.org.conscrypt.Platform" ; var Platform = Java .use (ClassName ); var targetMethod = "checkServerTrusted" ; var len = Platform [targetMethod].overloads .length ; console .log (len); for (var i = 0 ; i < len; ++i) { Platform [targetMethod].overloads [i].implementation = function ( ) { console .log ("class:" , ClassName , "target:" , targetMethod, " i:" , i, arguments ); }; } }); } setTimeout (hook_ssl, 100 );
SockerHook 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 function pass_ssl ( ){ var ClassName = "com.android.org.conscrypt.Platform" ; var Platform = Java .use (ClassName ); var targetMethod = "checkServerTrusted" ; var len = Platform [targetMethod].overloads .length ; console .log (len); for (var i = 0 ; i < len; ++i) { Platform [targetMethod].overloads [i].implementation = function ( ) { console .log ("class:" , ClassName , "target:" , targetMethod, " i:" , i, arguments ); }; } } function hook_update ( ){ var appUpdateActivity = Java .use ("com.huawei.updatesdk.service.otaupdate.AppUpdateActivity" ); appUpdateActivity.onCreate .implementation = function (aaa ){ console .log ("--------hook update--------" ) showStacks () this .finish (); }; } function main ( ){ Java .perform (function ( ){ var SwitchConfig = Java .use ('mtopsdk.mtop.global.SwitchConfig' ) SwitchConfig .isGlobalSpdySwitchOpen .implementation = function ( ){ console .log ('SwitchConfig' ) return false } pass_ssl (); hook_update (); }) } setTimeout (main, 100 );
绕过FLAG_SECURE或防止截屏 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Java.perform(function () { Java.use('android.view.SurfaceView' ).setSecure.overload('boolean' ).implementation = function (flag) { send('[1] flag:' + flag); this.call(false ); }; var LayoutParams = Java.use('android.view.WindowManager$LayoutParams' ); Java.use('android.view.Window' ).setFlags.overload('int' , 'int' ).implementation = function (flags, mask) { send('flag secure: ' + LayoutParams.FLAG_SECURE.value); send('before:' + flags); flags = (flags.value & ~LayoutParams.FLAG_SECURE.value); send('after:' + flags); this.call(this, flags, mask); }; });
spdy 1 2 3 4 5 6 7 8 Java .perform (function ( ) { var SwitchConfig = Java .use ('mtopsdk.mtop.global.SwitchConfig' ); SwitchConfig .isGlobalSpdySwitchOpen .overload ().implementation = function ( ){ var ret = this .isGlobalSpdySwitchOpen .apply (this , arguments ); console .log ("isGlobalSpdySwitchOpenl " +ret) return false } })
Native层Hook
hook jni_onload
如果想追踪jnionload在apk启动时的调用,需要先hook全局的dlopen函数,然后再通过dlopen函数去hook目标so中JniOnload的调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 function hook_dlopen (soName = '' ) { Interceptor .attach (Module .findExportByName (null , "android_dlopen_ext" ), { onEnter : function (args ) { var pathptr = args[0 ]; if (pathptr !== undefined && pathptr != null ) { var path = ptr (pathptr).readCString (); if (path.indexOf (soName) >= 0 ) { this .is_can_hook = true ; } } }, onLeave : function (retval ) { if (this .is_can_hook ) { hook_JNI_OnLoad () } } } ); } function hook_JNI_OnLoad ( ){ let exportAddress = Module .findExportByName ("soName" , "JNI_OnLoad" ); var baseAddress = Module .findBaseAddress ("soName" ); var offset = 0x17D19C ; var instructionAddress = baseAddress.add (offset); console .log ('Instruction Address: ' + instructionAddress); Interceptor .attach (instructionAddress, { onEnter : function (args ) { console .log ("init called : " + instructionAddress); } }); Interceptor .attach (exportAddress, { onEnter : function (args ) { console .log ("JNI_OnLoad called : " + exportAddress); } }); } setImmediate (hook_dlopen, "soName" )
枚举内存中加载的so 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 var modules = Process .enumerateModules ();for (var i in modules){ var module = modules[i]; console .log (module .name ); } hook函数 var memcmp_base = Module .findExportByName ("libtiny.so" ,'memcmp' );console .log (memcmp_base);Interceptor .attach (memcmp_base, { onEnter : function (args ) { var s1 = args[0 ]; var s2 = args[1 ]; var n = args[2 ].toInt32 (); console .log ("address1:" ,s1); console .log ("address2:" ,s2); var data1 = Memory .readByteArray (s1,n); var data2 = Memory .readByteArray (s2,n); console .log ("data1:" ,data1); console .log ("data2:" ,data2); } }); 基地址+相对偏移定位 var baseAddress = Module .getBaseAddress ('libtiny.so' ); var offset = 0x24B4D8 ; var targetAddress = baseAddress.add (offset);console .log (targetAddress);
hook指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 function two ( ){ var baseAddress = Module .getBaseAddress ('' ); var offset = 0x24B4D8 ; var targetAddress = baseAddress.add (offset); var offset = 0x24BAA4 ; var instructionAddress = baseAddress.add (offset); console .log ('Instruction Address: ' + instructionAddress); var instructionBytes = Memory .readByteArray (instructionAddress, 4 ); var byteArray = new Uint8Array (instructionBytes); var hexString = Array .from (byteArray).map (b => ('0' + b.toString (16 )).slice (-2 )).join (' ' ).toUpperCase (); console .log ('Instruction Bytes: ' + hexString); Interceptor .attach (instructionAddress, { onEnter : function (args ) { console .log ('X1 before MOV: ' + this .context .x1 ); console .log ('X27: ' + this .context .x27 ); var data1 = Memory .readByteArray (this .context .x27 ,0x10 ); console .log ("data1:" ,data1); console .log ('-------------------------------------------------' ) }, onLeave : function (retval ) { } }); }
调用追踪 1 2 3 4 5 6 7 8 9 10 var backtrace = Thread .backtrace (this .context , Backtracer .FUZZY )backtrace.forEach (function (addr ){ console .log ('0x' +addr.toString (16 )); var symbol = DebugSymbol .fromAddress (addr); if (symbol) { console .log (address + ' - ' + symbol.name ); } else { console .log (address + ' - [Unknown]' ); } });
hook_RegisterNatives 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 function find_RegisterNatives (params ) { var symbols = Module .enumerateSymbolsSync ("libart.so" ); var addrRegisterNatives = null ; for (var i = 0 ; i < symbols.length ; i++) { var symbol = symbols[i]; if (symbol.name .indexOf ("art" ) >= 0 && symbol.name .indexOf ("JNI" ) >= 0 && symbol.name .indexOf ("RegisterNatives" ) >= 0 && symbol.name .indexOf ("CheckJNI" ) < 0 ) { addrRegisterNatives = symbol.address ; console .log ("RegisterNatives is at " , symbol.address , symbol.name ); hook_RegisterNatives (addrRegisterNatives) } } } function hook_RegisterNatives (addrRegisterNatives ) { if (addrRegisterNatives != null ) { Interceptor .attach (addrRegisterNatives, { onEnter : function (args ) { console .log ("[RegisterNatives] method_count:" , args[3 ]); var env = args[0 ]; var java_class = args[1 ]; var class_name = Java .vm .tryGetEnv ().getClassName (java_class); var methods_ptr = ptr (args[2 ]); var method_count = parseInt (args[3 ]); for (var i = 0 ; i < method_count; i++) { var name_ptr = Memory .readPointer (methods_ptr.add (i * Process .pointerSize * 3 )); var sig_ptr = Memory .readPointer (methods_ptr.add (i * Process .pointerSize * 3 + Process .pointerSize )); var fnPtr_ptr = Memory .readPointer (methods_ptr.add (i * Process .pointerSize * 3 + Process .pointerSize * 2 )); var name = Memory .readCString (name_ptr); var sig = Memory .readCString (sig_ptr); var find_module = Process .findModuleByAddress (fnPtr_ptr); console .log ("[RegisterNatives] java_class:" , class_name, "name:" , name, "sig:" , sig, "fnPtr:" , fnPtr_ptr, " fnOffset:" , ptr (fnPtr_ptr).sub (find_module.base ), " callee:" , DebugSymbol .fromAddress (this .returnAddress )); } } }); } } setImmediate (find_RegisterNatives);
hook jni NewStringUTF 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 function test(){ var symbols = Module.enumerateSymbolsSync("libart.so"); var addrNewStringUTF = null; for (var i = 0; i < symbols.length; i++) { var symbol = symbols[i]; if (symbol.name.indexOf("NewStringUTF") >= 0 && symbol.name.indexOf("CheckJNI") < 0) { addrNewStringUTF = symbol.address; console.log("NewStringUTF is at ", symbol.address, symbol.name); break } } if (addrNewStringUTF !== null) { Interceptor.attach(addrNewStringUTF, { onEnter: function(args) { var utfString = Memory.readUtf8String(args[0]); //console.log("JNI_NewStringUTF called with string: " + utfString); if (utfString.indexOf("XYAAAAAQAAAAEAAAB") !== -1) { console.log(utfString); // 4.4 读取当前在执行那个so文件,及so文件中的地址 console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n'); // 4.5 打印调用栈 console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new())); } }, onLeave: function(retval) { } }); } else { console.log("NewStringUTF not found."); } } setImmediate(test)
dump so 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var libxx = Process.getModuleByName("xxx.so"); console.log("*****************************************************"); console.log("name: " +libxx.name); console.log("base: " +libxx.base); console.log("size: " +ptr(libxx.size)); var file_path = "/data/data/" + libxx.name + "_" + libxx.base + "_" + ptr(libxx.size) + ".so"; console.log(file_path); var file_handle = new File(file_path, "wb"); if (file_handle && file_handle != null) { Memory.protect(ptr(libxx.base), libxx.size, 'rwx'); var libso_buffer = ptr(libxx.base).readByteArray(libxx.size); file_handle.write(libso_buffer); file_handle.flush(); file_handle.close(); console.log("[dump]:", file_path); }