frida 大佬原博客《安卓逆向这档事》十三、是时候学习一下Frida一把梭了(上) - 『移动安全区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
再次重申一次:本文仅仅是在大佬博客上写一点自己的笔记,建议看大佬的博客进行学习QAQ
1.什么是Frida? Frida 是一款开源的动态插桩工具,可以插入一些代码到原生App的内存空间去动态地监视和修改其行为,支持Windows、Mac、Linux、Android或者iOS,从安卓层面来讲,可以实现Java
层和Native
层Hook
操作。项目地址 官网及使用文档
2.Frida原理及重要组件 frida注入的原理就是找到目标进程,使用ptrace跟踪目标进程获取mmap,dlpoen,dlsym等函数库的偏移获取mmap在目标进程申请一段内存空间将在目标进程中找到存放frida-agent-32/64.so的空间启动执行各种操作由agent去实现
组件名称
功能描述
frida-gum
提供了inline-hook的核心实现,还包含了代码跟踪模块Stalker,用于内存访问监控的MemoryAccessMonitor,以及符号查找、栈回溯实现、内存扫描、动态代码生成和重定位等功能
frida-core
fridahook的核心,具有进程注入、进程间通信、会话管理、脚本生命周期管理等功能,屏蔽部分底层的实现细节并给最终用户提供开箱即用的操作接口。包含了frida-server、frida-gadget、frida-agent、frida-helper、frida-inject等关键模块和组件,以及之间的互相通信底座
frida-gadget
本身是一个动态库,可以通过重打包修改动态库的依赖或者修改smali代码去实现向三方应用注入gadget,从而实现Frida的持久化或免root
frida-server
本质上是一个二进制文件,类似于前面学习到的android_server,需要在目标设备上运行并转发端口,在Frida hook中起到关键作用
3.Frida与Xposed的对比
工具
优点
缺点
Xposed
直接编写Java代码,Java层hook方便,可打包模块持久化hook
环境配置繁琐,兼容性较差,难以Hook底层代码。
Frida
配置简单,免重启hook。支持Java层和Native层的hook操作
持久化hook相对麻烦
4.Frida基础知识 1.基础指令 1.frida-ps -U 查看当前手机运行的进程 2.frida-ps –help 查看help指令
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 使用方式: frida-ps [选项] 选项: -h, -D ID, -U, -R, -H HOST, 与HOST进行TLS通信,期望的CERTIFICATE 设置心跳包间隔(秒),或设置为0 以禁用(默认为-1 ,根据传输方式自动选择) 设置与 添加与 -O FILE, 包含额外命令行选项的文本文件 -a, -i, -j,
2.操作模式:
操作模式
描述
优点
主要用途
CLI(命令行)模式
通过命令行直接将JavaScript脚本注入进程中,对进程进行操作
便于直接注入和操作
在较小规模的操作或者需求比较简单的场景中使用
RPC模式
使用Python进行JavaScript脚本的注入工作,实际对进程进行操作的还是JavaScript脚本,可以通过RPC传输给Python脚本来进行复杂数据的处理
在对复杂数据的处理上可以通过RPC传输给Python脚本来进行,有利于减少被注入进程的性能损耗
在大规模调用中更加普遍,特别是对于复杂数据处理的需求
3.注入模式与启动命令:
注入模式
描述
命令或参数
优点
主要用途
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已经启动,或者我们只关心特定时刻或特定功能的行为时使用
Spawn模式
1 frida -U -f 进程名 -l hook. js
attach模式 :
frida_server自定义端口
1 2 3 4 frida server 默认端口:27042 taimen: / $ sutaimen: / # cd data/local/tmp/ taimen: /data/local/tmp # ./fs1280 -l 0.0.0.0:6666
logcat |grep "D.zj2595"
日志捕获adb connect 127.0.0.1:62001
模拟器端口转发
4.基础语法
API名称
描述
Java.use(className)
获取指定的Java类并使其在JavaScript代码中可用。
Java.perform(callback)
确保回调函数在Java的主线程上执行。
Java.choose(className, callbacks)
枚举指定类的所有实例。
Java.cast(obj, cls)
将一个Java对象转换成另一个Java类的实例。
Java.enumerateLoadedClasses(callbacks)
枚举进程中已经加载的所有Java类。
Java.enumerateClassLoaders(callbacks)
枚举进程中存在的所有Java类加载器。
Java.enumerateMethods(targetClassMethod)
枚举指定类的所有方法。
5.日志输出语法区别
日志方法
描述
区别
console.log()
使用JavaScript直接进行日志打印
多用于在CLI模式中,console.log()
直接输出到命令行界面,使用户可以实时查看。在RPC模式中,console.log()
同样输出在命令行,但可能被Python脚本的输出内容掩盖。
send()
Frida的专有方法,用于发送数据或日志到外部Python脚本
多用于RPC模式中,它允许JavaScript脚本发送数据到Python脚本,Python脚本可以进一步处理或记录这些数据。
6.Hook框架模板 1 2 3 4 5 6 function main ( ){ Java .perform (function ( ){ hookTest1 (); }); } setImmediate (main);
5.Frida常用API 1.Hook普通方法、打印参数和修改返回值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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 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 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 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 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 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 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 var ClassName =Java .use ("com.zj.wuaipojie.Demo" ); ClassName .privateFunc ("传参" );
非静态方法
1 2 3 4 5 6 7 8 9 10 11 12 var ret = null ; Java .perform (function ( ) { Java .choose ("com.zj.wuaipojie.Demo" ,{ onMatch :function (instance ){ ret=instance.privateFunc ("aaaaaaa" ); }, onComplete :function ( ){ } }); })
objection 1.什么是objection objection是基于frida的命令行hook集合工具, 可以让你不写代码, 敲几句命令就可以对java函数的高颗粒度hook, 还支持RPC调用。可以实现诸如内存搜索、类和模块搜索、方法hook打印参数返回值调用栈等常用功能,是一个非常方便的,逆向必备、内存漫游神器。项目地址
2.objection环境配置 已不更新,要和frida的版本匹配
1 2 3 4 python使用的版本建议大于3 .8 ,不然可能会报错,或者你调低frida以及objection的版本 pip install objection==1 .11 .0 pip install frida-tools==9 .2 .4 frida 14 .2 .18
3.objection快速上手 1.help命令注释
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 objection Checking for a newer version of objection... Usage: objection [OPTIONS] COMMAND [ARGS]... _ _ _ _ ___| | _| _| ___ ___| | _| _| ___ ___ | . | . | | - _| _| _| | . | | | ___| ___| | ___| ___| _| | _| ___| _| _| | ___| (object)inject(ion) Runtime Mobile Exploration by : @leonjza from @sensepost 默认情况下,通信将通过USB进行,除非提供了` 选项: - N, - h, - p, - ah, - ap, - g, - S, - d, 命令: api 以无头模式启动objection API服务器。 device- type 获取关于已连接设备的信息。 explore 启动objection探索REPL。 patchapk 使用frida- gadget.so补丁一个APK。 patchipa 使用FridaGadget dylib补丁一个IPA。 run 运行单个objection命令。 signapk 使用objection密钥对APK进行Zipalign和签名。 version 打印当前版本并退出。
2.注入命令
1 2 3 4 5 6 objection -g 包名 explore - help :不知道当前命令的效果是什么,在当前命令前加help 比如:help env ,回车之后会出现当前命令的解释信息 - 按空格:不知道输入什么就按空格,会有提示出来 - jobs :可以进行多项hook - 日志:objection的日志文件生成在 C:\Users\Administrator\.objection
启动前就hook
1 objection -g 进程名 explore
4.objection基础api
memory list modules -查看内存中加载的库
1 2 3 4 5 6 7 8 9 10 11 12 memory list modules Save the output by adding `--json modules.json` to this command Name Base Size Path ---------------------------------------------------------------- ------------ ------------------- ------------------------------------------------------------------------------ app_process64 0x57867c9000 40960 (40.0 KiB) /system/bin/app_process64 linker64 0x72e326a000 229376 (224.0 KiB) /system/bin/linker64 libandroid_runtime.so 0x72e164e000 2113536 (2.0 MiB) /system/lib64/libandroid_runtime.so libbase.so 0x72dfa67000 81920 (80.0 KiB) /system/lib64/libbase.so libbinder.so 0x72dec1c000 643072 (628.0 KiB) /system/lib64/libbinder.so libcutils.so 0x72de269000 86016 (84.0 KiB) /system/lib64/libcutils.so libhidlbase.so 0x72df4cc000 692224 (676.0 KiB) /system/lib64/libhidlbase.so liblog.so 0x72e0be1000 98304 (96.0 KiB) /system/lib64/liblog
memory list exports so名称 - 查看库的导出函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 memory list exports liblog.so Save the output by adding `--json exports.json` to this command Type Name Address -------- ------------------------------------ ------------function android_log_write_int32 0x72e0be77c8 function android_log_write_list_begin 0x72e0be76f0 function __android_log_bswrite 0x72e0be9bd8 function __android_log_security 0x72e0bf2144 function __android_log_bwrite 0x72e0be9a18 function android_log_reset 0x72e0be75ec function android_log_write_string8 0x72e0be7a38 function android_logger_list_free 0x72e0be8c04 function __android_log_print 0x72e0be9728 function __android_logger_property_get_bool 0x72e0bf2248 function android_logger_get_id 0x72e0be8270 function android_logger_set_prune_list 0x72e0be8948
android hooking list activities -查看内存中加载的activity /android hooking list services -查看内存中加载的services
android intent launch_activity 类名 -启动activity
或service
(可以用于一些没有验证的activity,在一些简单的ctf中有时候可以出奇效)
关闭ssl校验 android sslpinning disable
关闭root检测 android root disable
5.objection内存漫游
内存搜刮类实例
1 2 3 4 5 android heap search instances 类名(命令) Class instance enumeration complete for com.zj.wuaipojie.Demo Hashcode Class toString() --------- --------------------- ----------------------------- 215120583 com.zj.wuaipojie.Demo com.zj.wuaipojie.Demo@cd27ac7
调用实例的方法
1 2 3 4 5 android heap execute <handle> getPublicInt(实例的hashcode+方法名) 如果是带参数的方法,则需要进入编辑器环境 android heap evaluate <handle> console.log(clazz.a("吾爱破解" )); 按住esc+enter触发
android hooking list classes -列出内存中所有的类(结果比静态分析的更准确)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 android hooking list classes tw.idv.palatis.xappdebug.MainApplication tw.idv.palatis.xappdebug.xposed.HookMain tw.idv.palatis.xappdebug.xposed.HookMain$a tw.idv.palatis.xappdebug.xposed.HookMain$b tw.idv.palatis.xappdebug.xposed.HookMain$c tw.idv.palatis.xappdebug.xposed.HookMain$d tw.idv.palatis.xappdebug.xposed.HookSelf u v void w xposed.dummy.XResourcesSuperClass xposed.dummy.XTypedArraySuperClass Found 10798 classes
android hooking search classes 关键类名 -在内存中所有已加载的类中搜索包含特定关键词的类
1 2 3 4 5 6 7 8 9 10 11 12 android hooking search classes wuaipojie Note that Java classes are only loaded when they are used, so if the expected class has not been found, it might not have been loaded yet. com .zj.wuaipojie.Democom .zj.wuaipojie.Demo$Animal com .zj.wuaipojie.Demo$Companion com .zj.wuaipojie.Demo$InnerClass com .zj.wuaipojie.Demo$test $1 com .zj.wuaipojie.MainApplicationcom .zj.wuaipojie.databinding.ActivityMainBinding... Found 38 classes
android hooking search methods 关键方法名 -在内存中所有已加载的类的方法中搜索包含特定关键词的方法(一般不建议使用,特别耗时,还可能崩溃)
android hooking list class_methods 类名 -内存漫游类中的所有方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 android hooking list class_methods com.zj.wuaipojie.ui.ChallengeSixth private static final void com.zj.wuaipojie.ui.ChallengeSixth .onCreate$lambda - 0 (com.zj.wuaipojie.ui.ChallengeSixth ,android.view.View )private static final void com.zj.wuaipojie.ui.ChallengeSixth .onCreate$lambda - 1 (com.zj.wuaipojie.ui.ChallengeSixth ,android.view.View )private static final void com.zj.wuaipojie.ui.ChallengeSixth .onCreate$lambda - 2 (com.zj.wuaipojie.ui.ChallengeSixth ,android.view.View )private static final void com.zj.wuaipojie.ui.ChallengeSixth .onCreate$lambda - 3 (com.zj.wuaipojie.ui.ChallengeSixth ,android.view.View )protected void com.zj.wuaipojie.ui.ChallengeSixth .onCreate(android.os.Bundle ) public final java.lang.String com.zj.wuaipojie.ui.ChallengeSixth .hexToString(java.lang.String )public final java.lang.String com.zj.wuaipojie.ui.ChallengeSixth .unicodeToString(java.lang.String )public final void com.zj.wuaipojie.ui.ChallengeSixth .toastPrint(java.lang.String )public static void com.zj.wuaipojie.ui.ChallengeSixth .$r8 $lambda $1 lrkrgiCEFWXZDHzLRibYURG1h8(com.zj.wuaipojie.ui.ChallengeSixth ,android.view.View )public static void com.zj.wuaipojie.ui.ChallengeSixth .$r8 $lambda $IUqwMqbTKaOGiTaeOmvy_GjNBso (com.zj.wuaipojie.ui.ChallengeSixth ,android.view.View )public static void com.zj.wuaipojie.ui.ChallengeSixth .$r8 $lambda $Kc_cRYZjjhjsTl6GYNHbgD - i6sE(com.zj.wuaipojie.ui.ChallengeSixth ,android.view.View )public static void com.zj.wuaipojie.ui.ChallengeSixth .$r8 $lambda $PDKm2AfziZQo6Lv1HEFkJWkUsoE (com.zj.wuaipojie.ui.ChallengeSixth ,android.view.View )Found 12 method(s)
6.objectionHook
hook类的所有方法
1 android hooking watch class 类名
hook方法的参数、返回值和调用栈
1 android hooking watch class_method 类名.方法名 --dump -args --dump -return --dump -backtrace
hook 类的构造方法
1 android hooking watch class_method 类名.$init
hook 方法的所有重载
1 android hooking watch class_method 类名.方法名
trace实战java控制流混淆 样本展示: 项目地址:BlackObfuscator
对抗方法
ZenTracer
项目地址
缺点:无法打印调用栈,无法
构造函数
1 2 3 4 5 因为以长久不更新,故新版frida不兼容,下面是我跑起来的版本 python==3.8.8 firda==14.2.18 frida-tools==9.2.4 还需要安装pyqt5的库
1 2 3 4 5 6 7 8 1 .运行server端2 .点击action3 .点击Match Regex设置过滤标签4 .输入包名(或者方法名等可以过滤的标签),点击add 5 .点击action的start6 .点击应用触发相应的逻辑7 .可左上角fils-Export JSON来导出日志分析
r0tracer项目地址 兼容最新版本
1 2 3 4 5 6 7 8 9 10 11 12 13 hook ("com.zj.wuaipojie2023_1" , "$" ); frida -U -f 【2023 春节】解题领红包之四 -l r0tracer.js -o Log .txt
Process、Module、Memory基础 1.Process Process
对象代表当前被Hook的进程,能获取进程的信息,枚举模块,枚举范围等
API
含义
Process.id
返回附加目标进程的 PID
Process.isDebuggerAttached()
检测当前是否对目标程序已经附加
Process.enumerateModules()
枚举当前加载的模块,返回模块对象的数组
Process.enumerateThreads()
枚举当前所有的线程,返回包含 id
, state
, context
等属性的对象数组
2.Module Module
对象代表一个加载到进程的模块(例如,在 Windows 上的 DLL,或在 Linux/Android 上的 .so 文件),能查询模块的信息,如模块的基址、名称、导入/导出的函数等
API
含义
Module.load()
加载指定so文件,返回一个Module对象
enumerateImports()
枚举所有Import库函数,返回Module数组对象
enumerateExports()
枚举所有Export库函数,返回Module数组对象
enumerateSymbols()
枚举所有Symbol库函数,返回Module数组对象
Module.findExportByName(exportName)、Module.getExportByName(exportName)
寻找指定so中export库中的函数地址
Module.findBaseAddress(name)、Module.getBaseAddress(name)
返回so的基地址
3.Memory Memory
是一个工具对象,提供直接读取和修改进程内存的功能,能够读取特定地址的值、写入数据、分配内存等
方法
功能
Memory.copy()
复制内存
Memory.scan()
搜索内存中特定模式的数据
Memory.scanSync()
同上,但返回多个匹配的数据
Memory.alloc()
在目标进程的堆上申请指定大小的内存,返回一个NativePointer
Memory.writeByteArray()
将字节数组写入一个指定内存
Memory.readByteArray
读取内存
2.枚举导入导出表
导出表(Export Table) :列出了库中可以被其他程序或库访问的所有公开函数和符号的名称。
导入表(Import Table) :列出了库需要从其他库中调用的函数和符号的名称。
简而言之,导出表告诉其他程序:“这些是我提供的功能。”,而导入表则表示:“这些是我需要的功能。”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function hookTest1 ( ){ Java .perform (function ( ){ var imports = Module .enumerateImports ("lib52pojie.so" ); for (var i =0 ; i < imports.length ;i++){ if (imports[i].name == "vip" ){ console .log (JSON .stringify (imports[i])); console .log (imports[i].address ); } } var exports = Module .enumerateExports ("lib52pojie.so" ); for (var i =0 ; i < exports .length ;i++){ console .log (JSON .stringify (exports [i])); } }) }
3.Native函数的基础Hook打印
整数型、布尔值类型、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 function hookTest2 ( ){Java .perform (function ( ){ var helloAddr = Module .findExportByName ("lib52pojie.so" ,"Java_com_zj_wuaipojie_util_SecurityUtil_checkVip" ); console .log (helloAddr); if (helloAddr != null ){ Interceptor .attach (helloAddr,{ 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 ()); } }) } }) }
字符串类型
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 function hookTest2 ( ){ Java .perform (function ( ){ var helloAddr = Module .findExportByName ("lib52pojie.so" ,"Java_com_zj_wuaipojie_util_SecurityUtil_vipLevel" ); if (helloAddr != null ){ Interceptor .attach (helloAddr,{ onEnter : function (args ){ var jString = Java .cast (args[2 ], Java .use ('java.lang.String' )); console .log ("参数:" , jString.toString ()); var JNIEnv = Java .vm .getEnv (); var originalStrPtr = JNIEnv .getStringUtfChars (args[2 ], null ).readCString (); console .log ("参数:" , originalStrPtr); }, onLeave : function (retval ){ var returnedJString = Java .cast (retval, Java .use ('java.lang.String' )); console .log ("返回值:" , returnedJString.toString ()); } }) } }) }
4.Native函数的基础Hook修改
整数型修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function hookTest3 ( ){Java .perform (function ( ){ var helloAddr = Module .findExportByName ("lib52pojie.so" ,"Java_com_zj_wuaipojie_util_SecurityUtil_checkVip" ); console .log (helloAddr); if (helloAddr != null ){ Interceptor .attach (helloAddr,{ onEnter : function (args ){ args[0 ] = ptr (1000 ); console .log (args[0 ]); }, onLeave : function (retval ){ retval.replace (20000 ); console .log ("retval" ,retval.toInt32 ()); } }) } }) }
字符串类型修改
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 function hookTest2 ( ){Java .perform (function ( ){ var helloAddr = Module .findExportByName ("lib52pojie.so" ,"Java_com_zj_wuaipojie_util_SecurityUtil_vipLevel" ); if (helloAddr != null ){ Interceptor .attach (helloAddr,{ onEnter : function (args ){ var JNIEnv = Java .vm .getEnv (); var originalStrPtr = JNIEnv .getStringUtfChars (args[2 ], null ).readCString (); console .log ("参数:" , originalStrPtr); var modifiedContent = "至尊" ; var newJString = JNIEnv .newStringUtf (modifiedContent); args[2 ] = newJString; }, onLeave : function (retval ){ var returnedJString = Java .cast (retval, Java .use ('java.lang.String' )); console .log ("返回值:" , returnedJString.toString ()); var JNIEnv = Java .vm .getEnv (); var modifiedContent = "无敌" ; var newJString = JNIEnv .newStringUtf (modifiedContent); retval.replace (newJString); } }) } }) }
5.SO基址的获取方式 1 2 3 var moduleAddr1 = Process .findModuleByName ("lib52pojie.so" ).base ; var moduleAddr2 = Process .getModuleByName ("lib52pojie.so" ).base ; var moduleAddr3 = Module .findBaseAddress ("lib52pojie.so" );
6.Hook未导出函数与函数地址计算 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function hookTest6 ( ){ Java .perform (function ( ){ var soAddr = Module .findBaseAddress ("lib52pojie.so" ); console .log (soAddr); var funcaddr = soAddr.add (0x1071C ); console .log (funcaddr); if (funcaddr != null ){ Interceptor .attach (funcaddr,{ onEnter : function (args ){ }, onLeave : function (retval ){ console .log (retval.toInt32 ()); } }) } }) }
函数地址计算
安卓里一般32 位的 so 中都是thumb
指令,64 位的 so 中都是arm
指令
通过IDA里的opcode bytes来判断,arm 指令为 4 个字节(options -> general -> Number of opcode bytes (non-graph) 输入4)
thumb 指令,函数地址计算方式: so 基址 + 函数在 so 中的偏移 + 1 arm 指令,函数地址计算方式: so 基址 + 函数在 so 中的偏移
7.Hook_dlopen dlopen源码 android_dlopen_ext源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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 ("lib52pojie.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 ("lib52pojie.so" ) >= 0 ) this .call_hook = true ; }, onLeave : function (retval ) { if (this .call_hook ) hookTest2 (); } }); }
8.借助IDA脚本实现一键式hook IDA&Frida 学习