Bytedance Android Inline Hook Versions Save

:fire: ShadowHook is an Android inline hook library which supports thumb, arm32 and arm64.

v1.0.9

3 months ago

Announcements

1. Compatible with Android 14 QPR2 Beta3.

Issue: #50. Thanks: @ScalletaZ, @mskmkt0704 Issue: #53. Thanks: @osm0sis

Bugs fixed

1. Fix the bug where shadowhook_hook_sym_name and shadowhook_hook_sym_name_callback may resolve symbol names incorrectly.

This bug could cause the wrong function to be hooked when .symtab contains symbol names with the same prefix.

For example: Now we want to hook je_mallctlnametomib, but actually hook je_mallctl. Generally, this will lead to a certain crash.

This bug exists in versions 1.0.7 and 1.0.8.

公告

1. 兼容 Android 14 QPR2 Beta3。

Issue: #50. Thanks: @ScalletaZ, @mskmkt0704 Issue: #53. Thanks: @osm0sis

Bugs 修复

1. 修复 shadowhook_hook_sym_nameshadowhook_hook_sym_name_callback 解析符号名可能错误的bug。

.symtab 中含有相同前缀的符号名时,这个 bug 可能会导致 hook 错误的函数。

例如:现在要 hook je_mallctlnametomib,但实际 hook 了 je_mallctl,一般情况下这会导致必现的崩溃。

此 bug 存在于 1.0.71.0.8 版本中。

v1.0.8

7 months ago

Announcements

1. Compatible with Android 14 (API level 34).

Bugs fixed

1. Fixed an occasional crash with SIGILL caused by arm64 BTI.

In the previous version, if the system supported arm64 BTI and the hooked ELF was compiled with BTI instructions, there would be about a 1/1024 probability of a crash. The crash will not occur randomly, it will only occur when the "1st instruction and the 2nd instruction (or the 4th instruction and the 5th instruction)" at the head of the hooked function belong to two different memory pages.

2. Fixed a bug that caused the arm64 MTE mechanism to fail.

The crash protection mechanism of shadowhook registers the signal handler of sigsegv and sigbus, but SA_EXPOSE_TAGBITS was not added in previous versions. Because the signal handler of shadowhook will be executed before the art sigchain, the tag bits in the address will be lost, which will lead to the failure of the MTE mechanism.

Improve

1. Optimized the signal stack memory usage issue of the crash protection mechanism.

In previous versions, shadowhook's signal handler would occupy some signal stack memory (64 bytes for arm32 and 128 bytes for arm64). After optimization, the signal stack memory will not be occupied.

Since the Android signal stack memory space is very limited (depending on the Android version and CPU architecture, each thread is approximately between 8KB and 32KB), the additional occupation of the signal stack memory space can easily aggravate the risk of signal stack overflow.

公告

1. 兼容 Android 14 (API level 34)。

Bugs 修复

1. 修复了一个 arm64 BTI 导致的 SIGILL 偶发崩溃。

在之前的版本中,如果系统支持 arm64 BTI 并且被 hook 的 ELF 编译时也加入了 BTI 指令,那么大约会有 1/1024 的概率发生崩溃。崩溃不会随机发生,只有当被 hook 函数头部的“第1条指令和第2条指令(或者第4条指令和第5条指令)”分别属于两个不同的内存页时才会发生。

2. 修复了一个导致 arm64 MTE 机制失效的 bug。

shadowhook 的崩溃保护机制注册了 sigsegv 和 sigbus 的信号处理函数,但是在之前的版本中没有添加 SA_EXPOSE_TAGBITS。因为 shadowhook 的信号处理函数会比 art sigchain 的先执行,于是导致了地址中 tag bits 丢失,进而导致 MTE 机制失效。

改进

1. 优化了崩溃保护机制的信号栈内存占用问题。

在之前的版本中,shadowhook 的信号处理函数会占用一些信号栈内存(arm32 为 64 字节,arm64 为 128 字节)。优化后不会占用信号栈内存。

由于 Android 信号栈内存空间十分有限(根据 Android 版本和 CPU 架构不同,每个线程大约在 8KB 到 32 KB 之间),所以对信号栈内存空间的额外占用,很容易加剧信号栈溢出的风险。

v1.0.7

1 year ago

Bugs fixed

1. Avoid crashes in x86 Houdini environment.

ShadowHook currently only supports arm and arm64 architectures. When running in the x86 Houdini environment, if you use ShadowHook to hook the system library of the x86 architecture, it will crash.

In particular, when the user tries to hook the library that has not been loaded into the memory through shadowhook_hook_sym_name() or shadowhook_hook_sym_name_callback(), ShadowHook will hook the linker‘s do_dlopen internally in order to automatically complete the hook work when the library is loaded into memory in the future, and the linker of the x86 Houdini environment is also of the x86 architecture, which leads to a crash.

We now check the architecture of the hooked ELF file before hooking. If the architecture does not match, the corresponding error code will be returned. We have added error codes 34 and 35, which correspond to the two cases of "the architecture of the ELF that the user wants to hook does not match" and "the architecture of the linker does not match" respectively.

Improve

1. Avoid compile warnings caused by shadowhook.h header file.

Previously, the BYTEHOOK_STACK_SCOPE macro in the shadowhook.h header file contained a temporary variable starting with a double underscore, which caused a compilation warning (reserved-identifier) in some compiler versions.

2. Ignore the extra suffix generated by LLVM during symbol lookup.

LLVM may add additional suffixes to symbols in ELF .symtab. The format of the suffix is .xxxx.hash, such as _ZN3artL21IsSafeToCallAbortSafeEv.__uniq.55395457626730424248235132913560037531.llvm.1533082929482216501, the _ZN3artL21IsSafeToCallAbortSafeEv is called canonical name, the hash section in the suffix may change after recompilation.

Passing only the canonical name is now supported when passing symbol names in shadowhook_dlsym(), shadowhook_dlsym_symtab(), shadowhook_hook_sym_name() and shadowhook_hook_sym_name_callback().

Bugs 修复

1. 避免在 x86 houdini 环境中发生崩溃。

ShadowHook 目前只支持 arm 和 arm64 架构,在 x86 Houdini 环境中运行时,如果用 ShadowHook 来 hook x86 架构的系统库就会发生崩溃。

尤其是,当用户通过 shadowhook_hook_sym_name()shadowhook_hook_sym_name_callback() 试图 hook 还未加载到内存中的 so 库时,ShadowHook 为了在这个 so 库未来被加载到内存时自动的完成 hook 工作,会在内部 hook linker 的 do_dlopen,而 x86 Houdini 环境的 linker 也是 x86 架构的,这就导致了崩溃。

我们现在在 hook 之前先检测被 hook ELF 文件的架构,如果架构不匹配,会返回对应的错误码。我们新增了错误码 3435,分别对应 “用户希望 hook 的 ELF 的架构不匹配” 和 “linker 的架构不匹配” 这两种情况。

改进

1. 避免 shadowhook.h 头文件引起的编译警告。

之前 shadowhook.h 头文件中 BYTEHOOK_STACK_SCOPE 宏中包含一个以双下划线开头的临时变量,这在某些编译器版本中会引发一个编译警告(reserved-identifier)。

2. 符号查找时忽略 LLVM 产生的额外后缀。

LLVM 可能为 ELF .symtab 中的符号添加额外的后缀,后缀的格式是 .xxxx.hash,比如 _ZN3artL21IsSafeToCallAbortSafeEv.__uniq.55395457626730424248235132913560037531.llvm.1533082929482216501,其中的 _ZN3artL21IsSafeToCallAbortSafeEv 称为 canonical name,后缀中的 hash 重新编译后可能发生变化。

现在,在 shadowhook_dlsym()shadowhook_dlsym_symtab()shadowhook_hook_sym_name()shadowhook_hook_sym_name_callback() 中传递符号名时,支持只传递 canonical name。

v1.0.6

1 year ago

Incompatible changes

1. Operation record is no longer enabled by default.

Added API and initialization parameters to enable and disable operation record.

Writing operation records every time hook and unhook will have some impact on performance. It is recommended to sample or enable operation records according to actual needs.

Improve

1. No more attempt to get timezone via localtime_r() in operation record.

localtime_r() will call getenv() to access the global environ, if there are concurrent setenv() calls at this time, a crash may occur, because in bionic, the access to environ is not protected by a lock.

What we can do currently is try to avoid calling getenv() and setenv().

2. In unique mode, an abort() will be triggered if a proxy function written for shared mode is used.

Doing so allows for clearer and earlier detection of such do-not-use issues.

New features

1. Add API to get ShadowHook version number.

At the same time, in the case of only ELF files, the version number of ShadowHook can also be determined in the following ways:

llvm-strings libshadowhook.so | grep "shadowhook version"

不兼容的变更

1. 默认不再开启操作记录。

增加了 API 和初始化参数用于开启和关闭操作记录。

每次 hook 和 unhook 时都写操作记录对性能会有一些影响,建议根据实际需要采样的或有针对性的开启操作记录。

改进

1. 操作记录中不再尝试通过 localtime_r() 获取时区。

localtime_r() 会调用 getenv() 来访问全局的 environ,如果此时存在并发的 setenv() 调用,则可能会发生崩溃,因为对 environ 的访问在 bionic 中没有锁保护。

我们目前能做的是尽量避免调用 getenv()setenv()

2. 在 unique 模式中,如果使用为 shared 模式编写的 proxy 函数,将触发主动 abort()

这样做可以更明确和更早的发现这类勿用问题。

新特性

1. 增加 API 用于获取 ShadowHook 的版本号。

同时,在仅有 ELF 文件的情况下,也可以通过以下方式确定 ShadowHook 的版本号:

llvm-strings libshadowhook.so | grep "shadowhook version"

v1.0.5

1 year ago

Bugs fixed

1. Fix the bug that some function addresses cannot use ELF gap for relative jump when hooking.

This bug will cause the hook stability of some functions to decrease. The bug occurs when the absolute address of a function is in the following ranges:

arch address ranges
thumb [0, 0x1000000)
thumb (0xFF000001, 0xFFFFFFFF]
arm [0, 0x2000000)
arm (0xFE000003, 0xFFFFFFFF]
arm64 [0, 0x8000000)
arm64 (0xFFFFFFFFF8000003, 0xFFFFFFFFFFFFFFFF]
  • related issue: #25
  • thanks to: @supernghia89

2. Fix the bug that part of ELF cannot be hooked in Android 4.x.

The first LOAD segment of ELF may be read-only (use the linker option --rosegment), and the /proc/self/maps at this time may look like this:

75b8d000-75b9f000 r--p 00000000 b3:1c 89884 /data/app-lib/io.hexhacking.xdl.sample-2/libquick.so
75b9f000-75bde000 r-xp 00012000 b3:1c 89884 /data/app-lib/io.hexhacking.xdl.sample-2/libquick.so
75bde000-75be1000 r--p 00051000 b3:1c 89884 /data/app-lib/io.hexhacking.xdl.sample-2/libquick.so
75be1000-75be2000 rw-p 00054000 b3:1c 89884 /data/app-lib/io.hexhacking.xdl.sample-2/libquick.so

In previous ShadowHook versions, this type of ELF could not be hooked in Android 4.x.

3. Fix the bug that the wrong initialization state may be returned when ShadowHook#init() is called concurrently.

It may actually be still being initialized, but it returns a state that has been initialized.

Improve

1. Avoid additional acquisition of the linker's global mutex lock during initialization.

ShadowHook needs to obtain several symbol addresses in libc.so through dlopen and dlsym during initialization. These operations need to hold the linker's global mutex lock. We moved the above operations to .init_array of libshadowhook.so.

Bugs 修复

1. 修复部分函数地址在 hook 时无法利用 ELF gap 作相对跳转的bug。

这个 bug 会导致部分函数的 hook 稳定性下降。当函数的绝对地址在以下范围内时,会出现这个 bug:

架构 地址范围
thumb [0, 0x1000000)
thumb (0xFF000001, 0xFFFFFFFF]
arm [0, 0x2000000)
arm (0xFE000003, 0xFFFFFFFF]
arm64 [0, 0x8000000)
arm64 (0xFFFFFFFFF8000003, 0xFFFFFFFFFFFFFFFF]
  • 相关的 issue:#25
  • 感谢:@supernghia89

2. 修复 Android 4.x 中无法 hook 部分 ELF 的 bug。

ELF 的第一个 LOAD segment 可能是只读的(用链接器选项 --rosegment),此时的 /proc/self/maps 大概是这样的:

75b8d000-75b9f000 r--p 00000000 b3:1c 89884 /data/app-lib/io.hexhacking.xdl.sample-2/libquick.so
75b9f000-75bde000 r-xp 00012000 b3:1c 89884 /data/app-lib/io.hexhacking.xdl.sample-2/libquick.so
75bde000-75be1000 r--p 00051000 b3:1c 89884 /data/app-lib/io.hexhacking.xdl.sample-2/libquick.so
75be1000-75be2000 rw-p 00054000 b3:1c 89884 /data/app-lib/io.hexhacking.xdl.sample-2/libquick.so

在之前的 ShadowHook 版本中,在 Android 4.x 中这种类型 ELF 无法被 hook。

  • 相关的 xDL 版本:v1.2.1

3. 修复了并发调用 ShadowHook#init() 时可能返回错误的初始化状态的 bug。

可能实际还处在初始化中,但是却返回了已经初始化完成的状态。

改进

1. 避免在初始化期间额外获取 linker 的全局 mutex 锁。

ShadowHook 需要在初始化时通过 dlopendlsym 获取 libc.so 中的几个符号地址,这些操作需要持有 linker 的全局 mutex 锁,我们将上述操作移动到了 libshadowhook.so.init_array 中。

v1.0.4

1 year ago

New features

  • Added new API shadowhook_hook_func_addr() for hooking a function (which has no symbol info in ELF) by absolute address.

Improve

  • Improve the performance of shadowhook_hook_sym_name and shadowhook_hook_sym_name_callback.
  • Update version for NDK, CMake, gradle and AGP.

新特性

  • 增加了新的 API shadowhook_hook_func_addr(),用于通过绝对地址 hook 一个在 ELF 中没有符号信息的函数。

改进

  • 改进 shadowhook_hook_sym_nameshadowhook_hook_sym_name_callback 的执行性能。
  • 升级 NDK,CMake,gradle 和 AGP 的版本。

v1.0.3

2 years ago

Bugs fixed

  • In previous versions, in Android 5.x, if only the ELF file name was specified when hooking (the full path was not specified), and the function to be hooked was in .symtab, the hook would fail. (such as hooking __openat in libc.so)

Bugs 修复

  • 在之前的版本中,在 Android 5.x 中,如果 hook 时仅指定 ELF 文件名(没有指定全路径),而要 hook 的函数又在 .symtab 中,此时会 hook 失败。(比如 hook libc.so 中的 __openat

v1.0.2

2 years ago

First version.

第一个版本。