Magisk/native/jni/external/libxhook/README.zh-CN.md
LoveSy 183e5f2ecc
Fix xhook cannot hook app_process
Co-authored-by: canyie <31466456+canyie@users.noreply.github.com>
Co-authored-by: John Wu <topjohnwu@gmail.com>
2022-03-21 15:52:38 -07:00

7.5 KiB
Vendored
Raw Blame History

xhook

xHook

README English Version

Android PLT hook 概述 中文版

xHook 是一个针对 Android 平台 ELF (可执行文件和动态库) 的 PLT (Procedure Linkage Table) hook 库。

xHook 一直在稳定性和兼容性方面做着持续的优化。

特征

  • 支持 Android 4.0 - 10API level 14 - 29
  • 支持 armeabiarmeabi-v7aarm64-v8ax86 和 x86_64。
  • 支持 ELF HASHGNU HASH 索引的符号。
  • 支持 SLEB128 编码的重定位信息。
  • 支持通过正则表达式批量设置 hook 信息。
  • 不需要 root 权限或任何系统权限。
  • 不依赖于任何的第三方动态库。

编译

  • 下载 Android NDK r16b,设置 PATH 环境变量。(对 armeabi 的支持,从 r17 版本开始被移除了)

  • 编译和安装 native 库。

./build_libs.sh
./install_libs.sh

Demo

cd ./xhookwrapper/
./gradlew assembleDebug
adb install ./app/build/outputs/apk/debug/app-debug.apk

API

外部 API 头文件: libxhook/jni/xhook.h

1. 注册 hook 信息

int xhook_register(const char  *pathname_regex_str,  
                   const char  *symbol,  
                   void        *new_func,  
                   void       **old_func);

在当前进程的内存空间中,在每一个符合正则表达式 pathname_regex_str 的已加载ELF中每一个调用 symbol 的 PLT 入口点的地址值都将给替换成 new_func。之前的 PLT 入口点的地址值将被保存在 old_func 中。

new_func 必须具有和原函数同样的函数声明。

成功返回 0失败返回 非0。

pathname_regex_str 只支持 POSIX BRE (Basic Regular Expression) 定义的正则表达式语法。

2. 忽略部分 hook 信息

int xhook_ignore(const char *pathname_regex_str,  
                 const char *symbol);

根据 pathname_regex_strsymbol,从已经通过 xhook_register 注册的 hook 信息中,忽略一部分 hook 信息。如果 symbolNULLxhook 将忽略所有路径名符合正则表达式 pathname_regex_str 的 ELF。

成功返回 0失败返回 非0。

pathname_regex_str 只支持 POSIX BRE 定义的正则表达式语法。

3. 执行 hook

int xhook_refresh(int async);

根据前面注册的 hook 信息,执行真正的 hook 操作。

async 参数传 1 表示执行异步的 hook 操作,传 0 表示执行同步的 hook 操作。

成功返回 0失败返回 非0。

xhook 在内部维护了一个全局的缓存,用于保存最后一次从 /proc/self/maps 读取到的 ELF 加载信息。每次一调用 xhook_refresh 函数这个缓存都将被更新。xhook 使用这个缓存来判断哪些 ELF 是这次新被加载到内存中的。我们每次只需要针对这些新加载的 ELF 做 hook 就可以了。

4. 清除缓存

void xhook_clear();

清除 xhook 的缓存,重置所有的全局标示。

如果你确定你需要的所有 PLT 入口点都已经被替换了,你可以调用这个函数来释放和节省一些内存空间。

5. 启用/禁用 调试信息

void xhook_enable_debug(int flag);

flag 参数传 1 表示启用调试信息,传 0 表示禁用调试信息。 (默认为:禁用)

调试信息将被输出到 logcat对应的 TAG 为:xhook

6. 启用/禁用 SFP (段错误保护)

void xhook_enable_sigsegv_protection(int flag);

flag 参数传 1 表示启用 SFP0 表示禁用 SFP。 (默认为:启用)

xhook 并不是一个常规的业务层的动态库。在 xhook 中我们不得不直接计算一些内存指针的值。在一些极端的情况和环境下读或者写这些指针指向的内存会发生段错误。根据我们的测试xhook 的行为将导致 APP 崩溃率增加 “一千万分之一” (0.0000001)。(具体崩溃率可能会增加多少,也和你想要 hook 的库和符号有关。最终我们不得不使用某些方法来防止这些无害的崩溃。我们叫它SFP (段错误保护),它是由这些调用和值组成的:sigaction() SIGSEGV siglongjmp()sigsetjmp()

在 release 版本的 APP 中,你应该始终启用 SFP这能防止你的 APP 因为 xhook 而崩溃。在 debug 版本的 APP 中,你应该始终禁用 SFP这样你就不会丢失那些一般性的编码失误导致的段错误这些段错误是应该被修复的。

例子

//监测内存泄露
xhook_register(".*\\.so$", "malloc",  my_malloc,  NULL);
xhook_register(".*\\.so$", "calloc",  my_calloc,  NULL);
xhook_register(".*\\.so$", "realloc", my_realloc, NULL);
xhook_register(".*\\.so$", "free",    my_free,    NULL);

//监控 sockets 生命周期
xhook_register(".*\\.so$", "getaddrinfo", my_getaddrinfo, NULL);
xhook_register(".*\\.so$", "socket",      my_socket,      NULL);
xhook_register(".*\\.so$", "setsockopt"   my_setsockopt,  NULL);
xhook_register(".*\\.so$", "bind",        my_bind,        NULL);
xhook_register(".*\\.so$", "listen",      my_listen,      NULL);
xhook_register(".*\\.so$", "connect",     my_connect,     NULL);
xhook_register(".*\\.so$", "shutdown",    my_shutdown,    NULL);
xhook_register(".*\\.so$", "close",       my_close,       NULL);

//过滤出和保存部分安卓 log 到本地文件
xhook_register(".*\\.so$", "__android_log_write",  my_log_write,  NULL);
xhook_register(".*\\.so$", "__android_log_print",  my_log_print,  NULL);
xhook_register(".*\\.so$", "__android_log_vprint", my_log_vprint, NULL);
xhook_register(".*\\.so$", "__android_log_assert", my_log_assert, NULL);

//追踪某些调用 (忽略 linker 和 linker64)
xhook_register("^/system/.*$", "mmap",   my_mmap,   NULL);
xhook_register("^/vendor/.*$", "munmap", my_munmap, NULL);
xhook_ignore  (".*/linker$",   "mmap");
xhook_ignore  (".*/linker$",   "munmap");
xhook_ignore  (".*/linker64$", "mmap");
xhook_ignore  (".*/linker64$", "munmap");

//防御某些注入攻击
xhook_register(".*com\\.hacker.*\\.so$", "malloc",  my_malloc_always_return_NULL, NULL);
xhook_register(".*/libhacker\\.so$",     "connect", my_connect_with_recorder,     NULL);

//修复某些系统 bug
xhook_register(".*some_vendor.*/libvictim\\.so$", "bad_func", my_nice_func, NULL);

//忽略 libwebviewchromium.so 的所有 hook 信息
xhook_ignore(".*/libwebviewchromium.so$", NULL);

//现在执行 hook!
xhook_refresh(1);

技术支持

  1. 查看 xhook-sample
  2. GitHub issues 交流。
  3. 邮件: caikelun@gmail.com
  4. QQ 群: 603635869。二维码:

qq group

贡献

请阅读 xHook Contributing Guide

许可证

xHook 使用 MIT 许可证

xHook 的文档使用 Creative Commons 许可证