libart.so
: 在 Android 5.0(Lollipop)及更高版本中,libart.so
是 Android 运行时(ART,Android Runtime)的核心组件,它取代了之前的 Dalvik 虚拟机。可以在 libart.so
里找到 JNI 相关的实现。
PS:在高于安卓10的系统里,so的路径是/apex/com.android.runtime/lib64/libart.so,低于10的则在system/lib64/libart.so
函数名称
参数
描述
返回值
RegisterNatives
JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods
反注册类的本地方法。类将返回到链接或注册了本地方法函数前的状态。该函数不应在本地代码中使用。相反,它可以为某些程序提供一种重新加载和重新链接本地库的途径。
成功时返回0;失败时返回负数
GetStringUTFChars
JNIEnv*env, jstring string, jboolean *isCopy
通过JNIEnv接口指针调用,它将一个代表着Java虚拟机中的字符串jstring引用,转换成为一个UTF-8形式的C字符串
-
NewStringUTF
JNIEnv *env, const char *bytes
以字节为单位返回字符串的 UTF-8 长度
返回字符串的长度
FindClass
JNIEnv *env, const char *name
通过对象获取这个类。该函数比较简单,唯一注意的是对象不能为NULL,否则获取的class肯定返回也为NULL。
-
GetMethodID
JNIEnv *env, jclass clazz, const char *name, const char *sig
返回类或接口实例(非静态)方法的方法 ID。方法可在某个 clazz 的超类中定义,也可从 clazz 继承。GetMethodID() 可使未初始化的类初始化。
方法ID,如果找不到指定的方法,则为NULL
GetStaticMethodID
JNIEnv *env, jclass clazz, const char *name, const char *sig
获取类对象的静态方法ID
属性ID对象。如果操作失败,则返回NULL
GetFieldID
JNIEnv *env, jclass clazz, const char *name, const char *sig
回Java类(非静态)域的属性ID。该域由其名称及签名指定。访问器函数的Get<type>Field
及 Set<type>Field
系列使用域 ID 检索对象域。GetFieldID() 不能用于获取数组的长度域。应使用GetArrayLength()。
-
GetStaticFieldID
JNIEnv *env,jclass clazz, const char *name, const char *sig
获取类的静态域ID方法
-
Call<type>Method
, Call<type>MethodA
, Call<type>MethodV
JNIEnv *env, jobject obj, jmethodID methodID, .../jvalue *args/va_list args
这三个操作的方法用于从本地方法调用Java 实例方法。它们的差别仅在于向其所调用的方法传递参数时所用的机制。
NativeType,具体的返回值取决于调用的类型
主要用得多的是hook_RegisterNatives.js:hook打印动态注册的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ~ frida -U -f com.zj.wuaipojie -l .\hook_RegisterNatives.js ____ / _ | Frida 16.2.1 - A world-class dynamic instrumentation toolkit | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at https://frida.re/docs/home/ . . . . . . . . Connected to ONEPLUS A3000 (id=1c7dbd17) Spawning `com.zj.wuaipojie`... RegisterNatives is at 0x74e977a018 _ZN3art3JNIILb0EE15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi RegisterNatives is at 0x74e97e39f0 _ZN3art3JNIILb1EE15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi Spawned `com.zj.wuaipojie`. Resuming main thread! [ONEPLUS A3000::com.zj.wuaipojie ]-> [RegisterNatives] method_count: 0x1 [RegisterNatives] java_class: android.util.StatsLog name: writeImpl sig: ([BII)V fnPtr: 0x7475276214 fnOffset: 0x7475276214 libstats_jni.so!0x1214 callee: 0x747527618c libstats_jni.so!JNI_OnLoad+0xa8 [RegisterNatives] method_count: 0x1 [RegisterNatives] java_class: com.zj.wuaipojie.util.SecurityUtil name: check sig: (Ljava/lang/String;)Z fnPtr: 0x74dcd9a3b4 fnOffset: 0x74dcd9a3b4 lib52pojie.so!_Z5checkP7_JNIEnvP7_jclassP8_jstring callee: 0x74dcd9a514 lib52pojie.so!JNI_OnLoad+0xac
hook_GetStringUTFChars这个的坑点在于:有两个GetStringUTFChars地址,选择第一个就好(默认就变成hook第二个了),不行就通过上面的name.indexOf(“b1EE”) == -1过滤,或者下面添加break退出循环
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 function hook_GetStringUTFChars() { var GetStringUTFChars_addr = null; // jni 系统函数都在 libart.so 中 var module_libart = Process.findModuleByName("libart.so"); var symbols = module_libart.enumerateSymbols(); for (var i = 0; i < symbols.length; i++) { var name = symbols[i].name; if ((name.indexOf("JNI") >= 0) // && (name.indexOf("CheckJNI") == -1 && (name.indexOf("b1EE") == -1)) && (name.indexOf("CheckJNI") == -1) && (name.indexOf("art") >= 0)) { if (name.indexOf("GetStringUTFChars") >= 0) { // 获取到指定 jni 方法地址 GetStringUTFChars_addr = symbols[i].address; console.log("name: ", name);// 有两个地址,选择第一个就好,不行就通过上面的name.indexOf("b1EE") == -1过滤,或者下面添加break退出循环 console.log("GetStringUTFChars addr: ", GetStringUTFChars_addr); break; } } } Java.perform(function(){ console.log("实际的GetStringUTFChars addr: ", GetStringUTFChars_addr); Interceptor.attach(GetStringUTFChars_addr, { onEnter: function(args){ }, onLeave: function(retval){ // retval const char* console.log("GetStringUTFChars onLeave : ", ptr(retval).readCString()); if(ptr(retval).readCString().indexOf("普通") >=0){ console.log("GetStringUTFChars onLeave : ", ptr(retval).readCString()); console.log(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n') + '\n'); } } }) }) } function main(){ Java.perform(function(){ hook_GetStringUTFChars(); }); } setImmediate(main);
运行实例:
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 -U wuaipojie -l .\hook_GetStringUTFChars.js ____ / _ | Frida 16.2.1 - A world-class dynamic instrumentation toolkit | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at https://frida.re/docs/home/ . . . . . . . . Connected to ONEPLUS A3000 (id=1c7dbd17) Attaching... name: _ZN3art3JNIILb0EE17GetStringUTFCharsEP7_JNIEnvP8_jstringPh GetStringUTFChars addr: 0x74e9764548 实际的GetStringUTFChars addr: 0x74e9764548 [ONEPLUS A3000::wuaipojie ]-> GetStringUTFChars onLeave : /data/app/~~mcnLRJYrvfwmPs1SzDLDLw==/com.zj.wuaipojie-qBIKQBAAXl2V9AjNVNrtkw==/lib/arm64/lib52pojie.so GetStringUTFChars onLeave : /data/app/~~mcnLRJYrvfwmPs1SzDLDLw==/com.zj.wuaipojie-qBIKQBAAXl2V9AjNVNrtkw==/lib/arm64/lib52pojie.so GetStringUTFChars onLeave : com.zj.wuaipojie GetStringUTFChars onLeave : android.widget.TextView GetStringUTFChars onLeave : 普通 GetStringUTFChars onLeave : 普通 0x74d6d90588 lib52pojie.so!Java_com_zj_wuaipojie_util_SecurityUtil_vipLevel+0x3c 0x74782e03f0 base.odex!0x3f3f0 0x74782e03f0 base.odex!0x3f3f0
libc.so
: 这是一个标准的 C 语言库,用于提供基本的系统调用和功能,如文件操作、字符串处理、内存分配等。在Android系统中,libc
是最基础的库之一。
hook_kill1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function replaceKILL() { // 查找libc.so库中kill函数的地址 var kill_addr = Module.findExportByName("libc.so", "kill"); // 使用Interceptor.replace来替换kill函数 Interceptor.replace(kill_addr, new NativeCallback(function (arg0, arg1) { // 当kill函数被调用时,打印第一个参数(通常是进程ID) console.log("arg0=> ", arg0); // 打印第二个参数(通常是发送的信号) console.log("arg1=> ", arg1); // 打印调用kill函数的堆栈跟踪信息 console.log('libc.so!kill called from:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE) .map(DebugSymbol.fromAddress).join('\n') + '\n'); }, "int", ["int", "int"])) } function main(){ Java.perform(function(){ replaceKILL(); }); } setImmediate(main);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ~ frida -U wuaipojie -l .\hook_kill.js ____ / _ | Frida 16.2.1 - A world-class dynamic instrumentation toolkit | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at https://frida.re/docs/home/ . . . . . . . . Connected to ONEPLUS A3000 (id=1c7dbd17) [ONEPLUS A3000::wuaipojie ]-> arg0=> 29458 arg1=> 9 libc.so!kill called from: 0x74e94beed8 libart.so!art_quick_generic_jni_trampoline+0x98 0x74e94beed8 libart.so!art_quick_generic_jni_trampoline+0x98 Error: expected an integer
hook_pthread_create下面反调试,应该是反调试的代码在pthread_create新建的线程中实现
下面只是进行输出,没有进行一些修改操作
1 2 3 4 5 6 7 8 9 10 11 12 function hook_pthread_create(){ //hook反调试 var pthread_create_addr = Module.findExportByName("libc.so", "pthread_create"); console.log("pthread_create_addr: ", pthread_create_addr); Interceptor.attach(pthread_create_addr,{ onEnter:function(args){ console.log(args[0], args[1], args[2], args[4]); },onLeave:function(retval){ console.log("retval is =>",retval) } }) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ~ frida -U wuaipojie -l .\hook_libc.js ____ / _ | Frida 16.2.1 - A world-class dynamic instrumentation toolkit | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at https://frida.re/docs/home/ . . . . . . . . Connected to ONEPLUS A3000 (id=1c7dbd17) Attaching... pthread_create_addr: 0x777d7ffc88 [ONEPLUS A3000::wuaipojie ]-> 0x74db4a9ab8 0x0 0x777b8cdd74 0x0 retval is => 0x0 0x7fc751b7e8 0x7fc751b820 0x74e9930358 0x0 retval is => 0x0
hook_str_cmp1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function hook_strcmp() { var pt_strcmp = Module.findExportByName("libc.so", 'strcmp'); Interceptor.attach(pt_strcmp, { onEnter: function (args) { var str1 = args[0].readCString(); var str2 = args[1].readCString(); if (str1.indexOf("test") !== -1 || str2.indexOf("test") !== -1) { // test是我们输入的字符 console.log("strcmp-->", str1, str2); this.printStack = true; } }, onLeave: function (retval) { if (this.printStack) { var stack = Thread.backtrace(this.context, Backtracer.ACCURATE) .map(DebugSymbol.fromAddress).join("\n"); console.log("Stack trace:\n" + stack); } } }) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ~ frida -U wuaipojie -l .\hook_libc.js ____ / _ | Frida 16.2.1 - A world-class dynamic instrumentation toolkit | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at https://frida.re/docs/home/ . . . . . . . . Connected to ONEPLUS A3000 (id=1c7dbd17) [ONEPLUS A3000::wuaipojie ]-> strcmp--> wuaipojie2023 test Stack trace: 0x74866d04d0 lib52pojie.so!_Z5checkP7_JNIEnvP7_jclassP8_jstring+0x4c 0x74866d04d0 lib52pojie.so!_Z5checkP7_JNIEnvP7_jclassP8_jstring+0x4c 0x74866d04d0 lib52pojie.so!_Z5checkP7_JNIEnvP7_jclassP8_jstring+0x4c
libdl.so
是一个处理动态链接和加载的标准库,它提供了dlopen
、dlclose
、dlsym
等函数,用于在运行时动态地加载和使用共享库
别
函数名称
参数
描述
动态链接库操作
dlopen
const char *filename, int flag
打开动态链接库文件
dlsym
void *handle, const char *symbol
从动态链接库中获取符号地址
Hook_dlsym
获取jni静态注册方法地址
1 2 3 4 5 6 7 8 9 10 11 12 13 function hook_dlsym() { var dlsymAddr = Module.findExportByName("libdl.so", "dlsym"); Interceptor.attach(dlsymAddr, { onEnter: function(args) { this.args1 = args[1]; }, onLeave: function(retval) { var module = Process.findModuleByAddress(retval); if (module === null) return; console.log(this.args1.readCString(), module.name, retval, retval.sub(module.base)); } }); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ~ frida -U wuaipojie -l .\hook_libdl.js ____ / _ | Frida 16.2.1 - A world-class dynamic instrumentation toolkit | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at https://frida.re/docs/home/ . . . . . . . . Connected to ONEPLUS A3000 (id=1c7dbd17) [ONEPLUS A3000::wuaipojie ]-> Java_com_zj_wuaipojie_util_SecurityUtil_checkVip lib52pojie.so 0x74866d0614 0x10614 Java_com_zj_wuaipojie_util_SecurityUtil_vipLevel lib52pojie.so 0x74866d061c 0x1061c Java_com_zj_wuaipojie_util_SecurityUtil_diamondNum lib52pojie.so 0x74866d07ec 0x107ec
Linker是Android系统动态库so的加载器/链接器,通过android源码分析 init 和 init_array 是在 callConstructor 中被调用的
hookInit和hookInitArrayfrida的CModule是一个极其强大的模块,本文将使用CModule来完成init_array的信息输出
简单来说,就是借助CModule对soinfo结构体进行解析
经过对比分析历代soinfo结构体的定义,可以确定从Android 8 ~ 14,结构体中init_array的位置都很稳定
于是通过下面的头文件中提取必要的内容,在CModule中定义一个soinfo结构体,这样frida就能自动完成相关偏移的处理
http://aosp.app/android-14.0.0_r1/xref/bionic/libc/include/link.h http://aosp.app/android-14.0.0_r1/xref/bionic/libc/kernel/uapi/asm-generic/int-ll64.h http://aosp.app/android-14.0.0_r1/xref/bionic/libc/kernel/uapi/linux/elf.h http://aosp.app/android-14.0.0_r1/xref/bionic/linker/linker_soinfo.h 接着定义一个函数,接受一个soinfo指针参数和一个callback函数,优雅地输出init_array信息
核心代码:
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 function hook_call_constructors() { // 初始化变量 let get_soname = null; let call_constructors_addr = null; let hook_call_constructors_addr = true; // 根据进程的指针大小找到对应的linker模块 let linker = null; if (Process.pointerSize == 4) { linker = Process.findModuleByName("linker"); } else { linker = Process.findModuleByName("linker64"); } // 枚举linker模块中的所有符号 let symbols = linker.enumerateSymbols(); for (let index = 0; index < symbols.length; index++) { let symbol = symbols[index]; // 查找名为"__dl__ZN6soinfo17call_constructorsEv"的符号地址 if (symbol.name == "__dl__ZN6soinfo17call_constructorsEv") { call_constructors_addr = symbol.address; // 查找名为"__dl__ZNK6soinfo10get_sonameEv"的符号地址,获取soname } else if (symbol.name == "__dl__ZNK6soinfo10get_sonameEv") { get_soname = new NativeFunction(symbol.address, "pointer", ["pointer"]); } } // 如果找到了所有需要的地址和函数 if (hook_call_constructors_addr && call_constructors_addr && get_soname) { // 挂钩call_constructors函数 Interceptor.attach(call_constructors_addr,{ onEnter: function(args){ // 从参数获取soinfo对象 let soinfo = args[0]; // 使用get_soname函数获取模块名称 let soname = get_soname(soinfo).readCString(); // 调用tell_init_info函数并传递一个回调,用于记录构造函数的调用信息 tell_init_info(soinfo, new NativeCallback((count, init_array_ptr, init_func) => { console.log(`[call_constructors] ${soname} count:${count}`); console.log(`[call_constructors] init_array_ptr:${init_array_ptr}`); console.log(`[call_constructors] init_func:${init_func} -> ${get_addr_info(init_func)}`); // 遍历所有初始化函数,并打印它们的信息 for (let index = 0; index < count; index++) { let init_array_func = init_array_ptr.add(Process.pointerSize * index).readPointer(); let func_info = get_addr_info(init_array_func); console.log(`[call_constructors] init_array:${index} ${init_array_func} -> ${func_info}`); } }, "void", ["int", "pointer", "pointer"])); } }); } }
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 ~ frida -U -f com.zj.wuaipojie -l .\hook_init.js ____ / _ | Frida 16.2.1 - A world-class dynamic instrumentation toolkit | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at https://frida.re/docs/home/ . . . . . . . . Connected to ONEPLUS A3000 (id=1c7dbd17) Spawned `com.zj.wuaipojie`. Resuming main thread! [ONEPLUS A3000::com.zj.wuaipojie ]-> [call_constructors] gralloc.msm8996.so count:0 [call_constructors] init_array_ptr:0x0 [call_constructors] init_func:0x0 -> null [call_constructors] liblog.so count:0 [call_constructors] init_array_ptr:0x0 [call_constructors] init_func:0x0 -> null [call_constructors] libutils.so count:2 [call_constructors] init_array_ptr:0x777e675880 [call_constructors] init_func:0x0 -> null [call_constructors] init_array:0 0x777e66d7cc -> [libutils.so + 0x147cc] [call_constructors] init_array:1 0x777e674844 -> [libutils.so + 0x1b844] [call_constructors] libcutils.so count:0 [call_constructors] init_array_ptr:0x0 [call_constructors] init_func:0x0 -> null [call_constructors] libhardware.so count:0 [call_constructors] init_array_ptr:0x0 [call_constructors] init_func:0x0 -> null [call_constructors] libqdMetaData.so count:0 [call_constructors] init_array_ptr:0x0 [call_constructors] init_func:0x0 -> null [call_constructors] liblog.so count:0 [call_constructors] init_array_ptr:0x0 [call_constructors] init_func:0x0 -> null [call_constructors] libcutils.so count:0 [call_constructors] init_array_ptr:0x0 [call_constructors] init_func:0x0 -> null [call_constructors] libutils.so count:2 [call_constructors] init_array_ptr:0x777e675880 [call_constructors] init_func:0x0 -> null
在 Frida 中,RPC(Remote Procedure Calls)是一种机制,允许你在 Frida 脚本和应用程序之间进行远程调用。通过 RPC,你可以在 Frida 脚本中定义函数,并在目标应用程序中调用这些函数,实现脚本和目标应用程序之间的交互。
下面是获取文字和图片url的js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function get_url(){ let ChallengeNinth = Java.use("com.zj.wuaipojie.ui.ChallengeNinth"); ChallengeNinth["updateUI"].implementation = function (list) { // console.log(`ChallengeNinth.updateUI is called: list=${list}`); var ret= this["updateUI"](list); var size = list.size(); for (var i = 0; i < size; i++) { var ImageEntity = Java.cast(list.get(i), Java.use("com.zj.wuaipojie.entity.ImageEntity")); console.log(ImageEntity.name.value + " , " + ImageEntity.cover.value); } return ret; }; } function main(){ Java.perform(function(){ get_url(); }); } setImmediate(main);
先安装下面的库,frida-tools装过就不用装了,最好里面的frida版本跟server要对应上
1 pip install frida-tools uvicorn fastapi requests
uvicorn :uvicorn 是一个基于 ASGI(Asynchronous Server Gateway Interface)的 Web 服务器,用于运行基于 Python 的异步 Web 应用程序。它是一个轻量级、高性能的服务器,常用于部署和运行基于 ASGI 的 Web 框架,如 FastAPI。
fastapi :FastAPI 是一个现代、快速(高性能)、易于使用的 Python Web 框架,用于构建 Web API。它基于 ASGI 并利用 Python 类型提示(Type Hints)和异步特性,提供了强大的自动化文档生成、输入验证、序列化、依赖注入等功能。FastAPI 具有出色的性能和开发效率,被广泛应用于构建高性能的 Web 后端服务。
requests :requests 是一个流行的 Python HTTP 库,用于发送 HTTP 请求和处理响应。它简化了与 Web 服务进行交互的过程,提供了简洁的 API,支持常见的 HTTP 方法(GET、POST、PUT 等),并且可以方便地处理请求头、请求参数、Cookies 等内容。requests 是一个非常实用的工具,适用于各种 Web 开发场景。
假如你看不到评论,可能是你访问Disqus被墙了,请使用代理访问
文章来源: https://www.giantbranch.cn/2024/03/23/%E5%AE%89%E5%8D%93Frida%20Hook%E8%BF%9B%E9%98%B62/ 如有侵权请联系:admin#unsafe.sh