另外提供了使用失败时适配一加系列手机的一些解决方法
操作模式:
操作模式
描述
优点
主要用途
CLI(命令行)模式
通过命令行直接将JavaScript脚本注入进程中,对进程进行操作
便于直接注入和操作
在较小规模的操作或者需求比较简单的场景中使用
RPC模式
使用Python进行JavaScript脚本的注入工作,实际对进程进行操作的还是JavaScript脚本,可以通过RPC传输给Python脚本来进行复杂数据的处理
在对复杂数据的处理上可以通过RPC传输给Python脚本来进行,有利于减少被注入进程的性能损耗
在大规模调用中更加普遍,特别是对于复杂数据处理的需求
注入模式与启动命令:
注入模式
描述
命令或参数
优点
主要用途
Spawn模式
将启动App的权利交由Frida来控制,即使目标App已经启动,在使用Frida注入程序时还是会重新启动App
在CLI模式中,Frida通过加上 -f 参数指定包名以spawn模式操作App
适合于需要在App启动时即进行注入的场景,可以在App启动时即捕获其行为
当需要监控App从启动开始的所有行为时使用
Attach模式
在目标App已经启动的情况下,Frida通过ptrace注入程序从而执行Hook的操作
在CLI模式中,如果不添加 -f 参数,则默认会通过attach模式注入App
适合于已经运行的App,不会重新启动App,对用户体验影响较小
在App已经启动,或者我们只关心特定时刻或特定功能的行为时使用
1 frida -U -f 进程名 -l hook.js
attach模式 :
如何开始? 1 2 3 4 5 6 7 8 9 10 11 12 13 adb shell su cd /data/local/tmp/./frida-server 另开终端 frida-ps -Uai frida -U -f <package_name>
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 记个小坑,使用frida spawn模式时会time out,提示PID 1442进程超时, 使用frida-ps查询发现,该进程是zygote,遂想到一加12不支持32位应用, 于是使用***adb shell su -c stop zygote_secondary***结束该进程,再次spawn,运行成功。 --------------------------------------------------------------------------------------- 对于运行 ColorOS 15 的一加用户,请使用以下命令终止 zygote32 进程。 否则,Frida 将等待来自 zygote32 进程的信号响应(显然已超时),而不是预期的 zygote64 进程。 ***setprop persist.sys.tango_zygote32.start 0*** 在此之后,将停止。zygote_tango OP5CFBL1:/data/local/tmp [init.svc.zygote]: [running] [init.svc.zygote_ocomp]: [running] [init.svc.zygote_tango]: [stopped] [init.svc_debug_pid.zygote]: [26502] [init.svc_debug_pid.zygote_ocomp]: [1611] [init.svc_debug_pid.zygote_tango]: [] [persist.oplus.zygote_ocomp]: [true ] [persist.sys.system.abnormalboot]: [2025-01-15 07:44:49:zygote reboot by netd] [persist.sys.tango_zygote32.start]: [0] [ro.boottime.zygote]: [6197081507] [ro.boottime.zygote_ocomp]: [6380248329] [ro.boottime.zygote_tango]: [6382967496] [ro.zygote]: [zygote64]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 其他:可考虑禁用magisk中Zygisk SElinux状态保持宽容:***setenforce 0*** 禁用usap:***setprop persist.device_config.runtime_native.usap_pool_enabled false *** 您可以尝试以下作: getprop|grep usap 如果 getprop xxx.usap 返回 true setprop xxxx.usap false 然后 getprop xxxx.usap 将返回 false 以自己的方式使用 spawn olives:/ [persist.device_config.runtime_native.usap_pool_enabled]: [false ] [persist.device_config.runtime_native.usap_pool_refill_delay_ms]: [3000] [persist.device_config.runtime_native.usap_pool_size_max]: [10] [persist.device_config.runtime_native.usap_pool_size_min]: [1] [persist.device_config.runtime_native.usap_refill_threshold]: [5] olives:/
Frida Java层 hook 1.Hook普通方法、打印参数和修改返回值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function hookTest1 ( ){ var utils = Java .use ("类名" ); utils.method .implementation = function (a, b ){ a = 123 ; b = 456 ; var retval = this .method (a, b); console .log (a, b, retval); return retval; } }
2.Hook重载参数 1 2 3 4 5 6 7 8 9 10 11 12 13 function hookTest2 ( ){ var utils = Java .use ("com.zj.wuaipojie.Demo" ); utils.Inner .overload ('com.zj.wuaipojie.Demo$Animal' ,'java.lang.String' ).implementation = function (a,b ){ b = "aaaaaaaaaa" ; this .Inner (a,b); console .log (b); } }
3.Hook构造函数 1 2 3 4 5 6 7 8 9 10 function hookTest3 ( ){ var utils = Java .use ("com.zj.wuaipojie.Demo" ); utils.$init .overload ('java.lang.String' ).implementation = function (str ){ console .log (str); str = "52" ; this .$init(str); } }
4.Hook字段 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function hookTest5 ( ){ Java .perform (function ( ){ var utils = Java .use ("com.zj.wuaipojie.Demo" ); utils.staticField .value = "我是被修改的静态变量" ; console .log (utils.staticField .value ); Java .choose ("com.zj.wuaipojie.Demo" , { onMatch : function (obj ){ obj._privateInt .value = "123456" ; obj.privateInt .value = 9999 ; }, onComplete : function ( ){ } }); }); }
5.Hook内部类 1 2 3 4 5 6 7 8 9 10 11 12 function hookTest6 ( ){ Java .perform (function ( ){ var innerClass = Java .use ("com.zj.wuaipojie.Demo$innerClass" ); console .log (innerClass); innerClass.$init .implementation = function ( ){ console .log ("eeeeeeee" ); } }); }
6.枚举所有的类与类的所有方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function hookTest7 ( ){ Java .perform (function ( ){ Java .enumerateLoadedClasses ({ onMatch : function (name,handle ){ if (name.indexOf ("com.zj.wuaipojie.Demo" ) !=-1 ){ console .log (name); var clazz =Java .use (name); console .log (clazz); var methods = clazz.class .getDeclaredMethods (); console .log (methods); } }, onComplete : function ( ){} }) }) }
7.枚举所有方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function hookTest8 ( ){ Java .perform (function ( ){ var Demo = Java .use ("com.zj.wuaipojie.Demo" ); var methods =Demo .class .getDeclaredMethods (); for (var j=0 ; j < methods.length ; j++){ var methodName = methods[j].getName (); console .log (methodName); for (var k=0 ; k<Demo [methodName].overloads .length ;k++){ Demo [methodName].overloads [k].implementation = function ( ){ for (var i=0 ;i<arguments .length ;i++){ console .log (arguments [i]); } return this [methodName].apply (this ,arguments ); } } } }) }
8.主动调用 静态方法
1 2 3 var ClassName =Java .use ("com.zj.wuaipojie.Demo" );ClassName .privateFunc ();
非静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 var ret = null ;Java .perform (function ( ) { Java .choose ("com.zj.wuaipojie.Demo" ,{ onMatch :function (instance ){ ret=instance.privateFunc ("aaaaaaa" ); }, onComplete :function ( ){ } }); })
Frida native 层 hook 1.Process
API
说明
Process.id
返回附加目标进程的 id
Process.isDebuggerAttached
检查当前是否对目标程序已附加
Process.enumerateModules
枚举当前进程的所有模块,返回模块对象的数组
Process.enumerateThreads
枚举当前进程的所有线程,返回包含 id
、state
、context
等属性的对象数组
2.Module
API
说明
Module.load
加载指定 so 文件,返回一个 Module 对象
enumerateImports()
枚举所有 Import 库函数,返回 Module 数组对象
enumerateExports()
枚举所有 Export 库函数,返回 Module 数组对象
enumerateSymbols()
枚举所有 Symbol 库函数,返回 Module 数组对象
findExportByName()findBaseAddress()
寻找指定 so 库中 export 库中的函数地址
Module.findBaseAddress(name)Module.getBaseAddress(name)
返回 so 的基地址
3.Memory
API
说明
Memory.copy()
复制内存
Memory.scan()
搜索内存中特定模式的数据
Memory.scanSync()
同步搜索内存中特定模式的数据,返回多个匹配的数据
Memory.alloc()
在目标进程的堆上申请指定大小的内存,返回一个 NativePointer
Memory.writeByteArray()
将字节数组写入指定内存地址
Memory.readByteArray()
从指定内存地址读取字节数组
4. hook 打印 (int 型、bool 型、char 型) 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 function hookTest ( ) { Java .perform (function ( ) { var addr = Module .findExportByName ("<so_name>" , "<function_name>" ); console .log (addr); if (addr != null ) { Interceptor .attach (addr, { onEnter : function (args ) { console .log (args[0 ]); console .log (this .context .x1 ); console .log (args[1 ].toInt32 ()); console .log (args[2 ].readCString ()); console .log (hexdump (args[2 ])); }, onLeave : function (retval ) { console .log (retval); console .log ("retval" , retval.toInt32 ()); } }) } }) }
(string 型) 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 hookTest ( ) { Java .perform (function ( ) { var addr = Module .findExportByName ("<so_name>" , "<function_name>" ); console .log (addr); if (addr != null ) { Interceptor .attach (addr, { onEnter : function (args ) { var jString = Java .cast (args[1 ].Jave .use ("Java.lang.String" )); console .log ("args:" , jString.toString ()); var JNIEnv = Java .vm .getEnv (); var originalStrPtr = Java .getStringUtfChars (args[1 ], null ).readCString (); console .log (originalStrPtr); var modifiedContent = "str1" ; var newJSring = JNIEnv .newSringUtf (modifiedContent); args[1 ] = newJString; }, onLeave : function (retval ) { var jString = Java .cast (retval.Jave .use ("Java.lang.String" )); console .log ("retval:" , jString.toString ()); var JNIEnv = Java .vm .getEnv (); var originalStrPtr = Java .getStringUtfChars (retval, null ).readCString (); console .log (originalStrPtr); var modifiedContent = "str1" ; var newJSring = JNIEnv .newSringUtf (modifiedContent); retval.replace (newJString); } }) } }) }
5. 枚举导入导出表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function hookTest1 ( ) { Java .perform (function ( ) { var imports = Module .enumerateImports ("libxxx.so" ); for (var i = 0 ; i < imports.length ; i++) { if (imports[i].name == "some_name" ) { console .log (JSON .stringify (imports[i])); console .log (imports[i].address ); } } var exports = Module .ennumerateExports ("libxxx.so" ); for (var i = 0 ; i < exports .length ; i++) { console .log (JSON .stringify (exports [i])); } }) }
6.获取 so 基地址 1 2 3 4 5 6 7 8 9 10 11 function get_to_so_addr ( ) { Java .perform (function ( ) { var moduleAddr1 = Process .findModuleByName ("libxxx.so" ).base ; var moduleAddr2 = Process .getModuleByName ("libxxx.so" ).base ; var moduleAddr3 = Module .findBaseAddress ("libxxx.so" ); console .log (moduleAddr1); console .log (moduleAddr2); console .log (moduleAddr3); }) }
7.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 function hookTest5 ( ){ Java .perform (function ( ){ var soAddr= findBaseAddress ("libxxx.so" ); console .log (aoAddr); var funcAddr=soAddr.add (0x1234 ); console .log (funcAddr); if (funcAddr!=null ){ Interceptor .attach (funcAddr,{ onEnter :function (args ){ }, onLeave :function (retval ){ console .log (retval.toInt2 ()); } }) } }) }
8.hook dlopen 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function hook_dlopen ( ){ var dlopen=Module .findExportByName (null ,"dlopen" ); Interceptor .attach (dlopen,{ onEnter :function (args ){ var so_name=args[0 ].readCString (); if (so_name.indexOf ("libxxx.so" )>=0 ) this .call_hook =true ; }, onLeave :function (retval ){ if (this ,call_hook) hookTest2 (); } }); var android_dlopen_ext=Module .findExportByName (null ,"android_dlopen_ext" ); Interceptor .attach (android_dlopen_ext,{ onEnter :function (args ){ var so_name=args[0 ].readCString (); if (so_name.indexOf ("libxxx.so" )>=0 ) this .call_hook =true ; },onLeave :function (retval ){ if (this .call_hook ) hookTest2 (); } }); }
9.写数据 1 2 3 4 5 6 7 8 9 10 var file_path="data/user/0/<package_name>/xxx.txt" ;var file_handle= new File (file_path,"wb" );if (file_handle&&file_handle!=null ){ file_handle.write (data); console .log ("write data success!" ); file_handle.flush (); file_handle.close (); }
10.内联Hook 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 function inline_hook ( ){ var soAddr=Module .findBaseAddress ("libxxx.so" ); if (soAddr){ var funcAddr=soAddr.add (0x1234 ); Java .perform (function ( ){ Interceptor .attach (funcAddr,{ onEnter :function (args ){ console .log (this .context ); console .log (this .context .x1 ); this .context .x1 =ptr (1 ); console .log (this .context .x1 ); } }) }) } } var soAddr=Module .findBaseAddress ("libxxx.so" );var codeAddr=Instruction .parse (soAddr.add (0x1234 ));console .log (codeAddr.toString ());
11. 普通函数与 JNI 函数的主动调用 1 2 3 4 5 6 7 var funcAddr=Module .findBaseAddress ("libxxx.so" ).add (0x1234 );var aesAddr= new NativeFunction (funcAddr,'pointer' ,['pointer' ,'pointer' ]);var encry_test=Memory .allocUtf8String ("cyphertext" );var key=Memory .allocUtf8String ('key' );console .log (aesAddr (encry_test,key).readCString ());
常见模板? Frida 模板 1 2 3 4 5 6 7 8 9 10 11 12 Java .perform (function ( ) { var <class_reference> = Java .use ("<package_name>.<class>" ); <class_reference>.<method_to_hook>.implementation = function (<args> ) { } })
Java.perform
是 Frida 中的一个函数,用于为脚本创建特殊上下文,以便与 Android 应用程序中的 Java 代码进行交互。这就像打开一扇门来访问和作应用程序内部运行的 Java 代码。进入此上下文后,您可以执行挂钩方法或访问 Java 类等作,以控制或观察应用程序的行为。
var <class_reference> = Java.use("<package_name>.<class>");
在这里,您声明了一个变量来表示目标 Android 应用程序中的 Java 类。您指定要与函数一起使用的类,该函数将类名作为参数。表示 Android 应用程序的包名称,并表示要与之交互的类。<class_reference>Java.use<package_name><class>
<class_reference>.<method_to_hook>.implementation = function(<args>) {}
在选定的类中,通过使用表示法访问它来指定要挂接的方法。在这里,您可以定义自己的逻辑,以便在调用挂接的方法时执行。<class_reference>.<method_to_hook><args>
调用静态方法 1 2 3 4 5 6 7 Java .perform (function ( ) { var <class_reference> = Java .use ("<package_name>.<class>" ); <class_reference>.<static_method>(); })
such as…
1 2 3 4 5 6 7 Java .perform (function ( ) { var a = Java .use ("com.ad2001.frida0x2.MainActivity" ); a.get_flag (4919 ); })
使用 Frida 更改变量的值 1 2 3 4 5 6 Java .perform (function ( ){var <class_reference> = Java .use ("<package_name>.<class>" );<class_reference>.<variable>.value = <value>; })
such as…
1 2 3 4 5 6 7 Java .perform (function ( ){ var a = Java .use ("com.ad2001.frida0x3.Checker" ); a.code .value = 512 ; })
调用非静态方法 在 Frida 中,要创建 Java 类的实例,可以使用$new()
方法。
1 2 3 4 5 6 7 Java .perform (function ( ) { var <class_reference> = Java .use ("<package_name>.<class>" ); var <class_instance> = <class_reference>.$new(); <class_instance>.<method>(); })
such as…
1 2 3 4 5 6 7 8 Java .perform (function ( ) { var check = Java .use ("com.ad2001.frida0x4.Check" ); var check_obj = check.$new(); var res = check_obj.get_flag (1337 ); console .log ("FLAG " + res); })
调用现有实例的方法 因为 frida 可以很容易地在现有实例上调用方法。为此,我们将使用两个 API。
Java.performNow
:用于在 Java 运行时的上下文中执行代码的函数。
Java.choose
:在运行时枚举指定 Java 类(作为第一个参数提供)的实例。
1 2 3 4 5 6 7 8 Java .performNow (function ( ) { Java .choose ('<Package>.<class_Name>' , { onMatch : function (instance ) { }, onComplete : function ( ) {} }); });
这里有两个回调:
onMatch 上
将针对在作期间找到的指定类的每个实例执行回调函数。onMatchJava.choose
该回调函数接收当前实例作为其参数。
您可以在回调中定义要对每个实例执行的自定义作。onMatch
function(instance) {}instance
,该参数表示目标类的每个匹配实例。您可以使用所需的任何其他名称。
onComplete
回调在作完成后执行作或清理任务。此数据块是可选的,如果您在搜索完成后不需要执行任何特定作,则可以选择将其留空。onCompleteJava.choose
such as…
1 2 3 4 5 6 7 8 9 Java .performNow (function ( ) { Java .choose ('com.ad2001.frida0x5.MainActivity' , { onMatch : function (instance ) { console .log ("Instance found" ); instance.flag (1337 ); }, onComplete : function ( ) {} }); });