Releases: bytedance/android-inline-hook
shadowhook v1.1.1
New Features
1. Added 4 APIs for registering/unregistering pre/post
callbacks of soinfo::call_constructors
and soinfo::call_destructors
shadowhook_register_dl_init_callback()
,shadowhook_unregister_dl_init_callback()
.shadowhook_register_dl_fini_callback()
,shadowhook_unregister_dl_fini_callback()
.
Bug Fixes
1. Fixed the IT instruction fix bug under Thumb instructions
The fix for the IT instruction under the ne
condition under Thumb has been corrected.
In the previous version, because the incorrect use of memset would make it->firstcond
always 0, only the eq
case could be fixed correctly in the conditional judgment.
2. Fix the memory leak bug caused by the thread re-entering the proxy function
Currently, if the function after pthread_key_clean_all
is hooked, the proxy function execution will be skipped and no memory leak will be caused.
In the previous version: if the proxy function is run after the thread's pthread_key_clean_all
call, if there are more than SH_HUB_THREAD_MAX
cached hub_stack
at this time, new memory will be mmaped out through sh_hub_stack_create
and will not be released.
Improvement
- Upgrade the versions of NDK, CMake, gradle and AGP.
新特性
1. 新增4个API,用于注册 / 反注册 soinfo::call_constructors
和soinfo::call_destructors
的 pre /post
回调
shadowhook_register_dl_init_callback()
,shadowhook_unregister_dl_init_callback()
。shadowhook_register_dl_fini_callback()
,shadowhook_unregister_dl_fini_callback()
。
Bugs 修复
1. 修复了Thumb指令下的IT指令修复bug
针对IT指令在Thumb下的ne
条件下的指令修复进行了修正。
在之前的版本中因为memset的错误使用会使得it->firstcond
一直为0,所以在条件判断中只能正确修复eq
的情况。
2. 修复线程再次进入代理函数引起的内存泄漏bug
目前如果hook了在pthread_key_clean_all
之后的函数将会跳过代理函数执行,不引起内存泄漏。
在之前的版本中:如果代理函数在线程的pthread_key_clean_all
调用之后运行,如果此时已经超过SH_HUB_THREAD_MAX
个缓存的hub_stack
,会通过sh_hub_stack_create
mmap出新的内存并且不会释放。
改进
- 升级 NDK,CMake,gradle 和 AGP 的版本。
shadowhook v1.0.10
Announcements
1. Compatible with Android 15.
Support 16 KB page sizes.
Bugs fixed
1. Fixed the bug when querying the address of indirect function.
When querying the address of indirect function (STT_GNU_IFUNC
), the correct address can now be returned. Previously, the address of the indirect function itself was incorrectly returned.
This will result in the following in previous versions: if you hook the indirect function through shadowhook_hook_sym_name()
or shadowhook_hook_sym_name_callback()
, the target function will not be hooked; if you use shadowhook_dlsym()
or shadowhook_dlsym_dynsym()
to query the indirect function, the wrong function address will be returned.
In most Android systems, memcpy
is an indirect function.
Improvements
1. Improved the judgment logic of the starting character of the symbol name hash suffix.
Previously, it only contained .
, and now $
is added.
In the shadowhook public API, if you need to enter a symbol name (function name) in string form, do not include the hash suffix generated by LLVM, which may change after recompilation. For example:
-
_ZNSt3__110__function6__funcIZN7android10uirenderer12renderthread13DrawFrameTask11postAndWaitEvE3$_0NS_9allocatorIS6_EEFvvEEclEv$c1671e787f244890c877724752face20
:$c1671e787f244890c877724752face20
is a hash suffix, and the starting character of the suffix is $
. When entering the symbol name, you should enter_ZNSt3__110__function6__funcIZN7android10uirenderer12renderthread13DrawFrameTask11postAndWaitEvE3$_0NS_9allocatorIS6_EEFvvEEclEv
. -
_ZNSt3__110__function6__funcIZN7android10uirenderer10RenderNode15handleForceDarkEPNS3_8TreeInfoEE3$_0NS_9allocatorIS7_EEFvPS4_EE7destroyEv.__uniq.10397782060659495822194741288103189803.907f77e9b59bd29450c46d69c21b9e58
:.__uniq.10397782060659495822194741288103189803.907f77e9b59bd29450c46d69c21b9e58
is a hash suffix, and the starting character of the suffix is.
. When entering the symbol name, you should enter_ZNSt3__110__function6__funcIZN7android10uirenderer10RenderNode15handleForceDarkEPNS3_8TreeInfoEE3$_0NS_9allocatorIS7_EEFvPS4_EE7destroyEv
.
2. Improved the execution speed of shadowhook_hook_func_addr()
.
During the execution of shadowhook_hook_func_addr()
, the symbol information is no longer queried, only the program headers information of the corresponding ELF is queried. When hooking ELF files with many symbols, it can greatly shorten the time consumption.
公告
1. 兼容 Android 15。
支持 16 KB page sizes。
Bugs 修复
1. 修正了查询 indirect function 地址时的 bug。
查询 indirect function (STT_GNU_IFUNC
) 地址时,现在能返回正确的地址了,之前错误的返回了 indirect function 本身的地址。
这会导致在之前的版本中:如果通过 shadowhook_hook_sym_name()
或 shadowhook_hook_sym_name_callback()
hook indirect function,不会真正的 hook 目标函数;如果用 shadowhook_dlsym()
或 shadowhook_dlsym_dynsym()
查询 indirect function,返回错误的函数地址。
在大多数 Android 系统中,memcpy
就是一个 indirect function。
改进
1. 改进了符号名 hash 后缀起始符的判断逻辑。
之前只包含了 .
,现在增加了 $
。
在 shadowhook 的 public API 中,如果需要输入字符串形式的符号名(函数名)时,不用包含 LLVM 生成的 hash 后缀,这部分后缀重新编译后可能会改变。例如:
-
_ZNSt3__110__function6__funcIZN7android10uirenderer12renderthread13DrawFrameTask11postAndWaitEvE3$_0NS_9allocatorIS6_EEFvvEEclEv$c1671e787f244890c877724752face20
:$c1671e787f244890c877724752face20
是 hash 后缀,后缀的起始符是$
。输入符号名时应该输入_ZNSt3__110__function6__funcIZN7android10uirenderer12renderthread13DrawFrameTask11postAndWaitEvE3$_0NS_9allocatorIS6_EEFvvEEclEv
。 -
_ZNSt3__110__function6__funcIZN7android10uirenderer10RenderNode15handleForceDarkEPNS3_8TreeInfoEE3$_0NS_9allocatorIS7_EEFvPS4_EE7destroyEv.__uniq.10397782060659495822194741288103189803.907f77e9b59bd29450c46d69c21b9e58
:.__uniq.10397782060659495822194741288103189803.907f77e9b59bd29450c46d69c21b9e58
是 hash 后缀,后缀的起始符是.
。输入符号名时应该输入_ZNSt3__110__function6__funcIZN7android10uirenderer10RenderNode15handleForceDarkEPNS3_8TreeInfoEE3$_0NS_9allocatorIS7_EEFvPS4_EE7destroyEv
。
2. 改进了 shadowhook_hook_func_addr()
的执行速度。
在执行 shadowhook_hook_func_addr()
的过程中,不再查询符号信息,仅查询对应 ELF 的 program headers 信息。在 hook 有很多符号的 ELF 文件时,能极大的缩短耗时。
shadowhook v1.0.9
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_name
和 shadowhook_hook_sym_name_callback
解析符号名可能错误的bug。
当 .symtab
中含有相同前缀的符号名时,这个 bug 可能会导致 hook 错误的函数。
例如:现在要 hook je_mallctlnametomib
,但实际 hook 了 je_mallctl
,一般情况下这会导致必现的崩溃。
此 bug 存在于 1.0.7
和 1.0.8
版本中。
shadowhook v1.0.8
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 之间),所以对信号栈内存空间的额外占用,很容易加剧信号栈溢出的风险。
shadowhook v1.0.7
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 文件的架构,如果架构不匹配,会返回对应的错误码。我们新增了错误码 34
和 35
,分别对应 “用户希望 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。
shadowhook v1.0.6
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"
shadowhook v1.0.5
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.
- related xDL version: v1.2.1
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 需要在初始化时通过 dlopen
和 dlsym
获取 libc.so
中的几个符号地址,这些操作需要持有 linker 的全局 mutex 锁,我们将上述操作移动到了 libshadowhook.so
的 .init_array
中。
shadowhook v1.0.4
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
andshadowhook_hook_sym_name_callback
. - Update version for NDK, CMake, gradle and AGP.
新特性
- 增加了新的 API
shadowhook_hook_func_addr()
,用于通过绝对地址 hook 一个在 ELF 中没有符号信息的函数。
改进
- 改进
shadowhook_hook_sym_name
和shadowhook_hook_sym_name_callback
的执行性能。 - 升级 NDK,CMake,gradle 和 AGP 的版本。
shadowhook v1.0.3
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
inlibc.so
)
Bugs 修复
- 在之前的版本中,在 Android 5.x 中,如果 hook 时仅指定 ELF 文件名(没有指定全路径),而要 hook 的函数又在
.symtab
中,此时会 hook 失败。(比如 hooklibc.so
中的__openat
)
shadowhook v1.0.2
First version.
第一个版本。