深入理解bytedance/bhook项目:Native Hook技术手册
项目概述
bytedance/bhook是一个强大的Android Native Hook框架,它提供了灵活可靠的函数拦截能力,可以帮助开发者监控和修改Native层函数调用行为。本文将全面解析该项目的Native API使用方法和核心概念。
核心功能与初始化
初始化配置
bytedance/bhook支持两种初始化模式:
#define BYTEHOOK_MODE_AUTOMATIC 0 // 自动模式
#define BYTEHOOK_MODE_MANUAL 1 // 手动模式
int bytehook_init(int mode, bool debug);
使用建议:
- 大多数情况下无需手动调用
bytehook_init(),推荐通过Java层初始化 - 纯Native进程场景才需要显式调用此函数
调试与日志控制
框架提供了灵活的调试控制接口:
bool bytehook_get_debug(void);
void bytehook_set_debug(bool debug);
这些接口与Java层的getDebug()和setDebug()功能一致,便于开发者根据需要开启或关闭调试日志。
Hook核心机制
Hook存根与回调
Hook操作返回的存根(stub)定义如下:
typedef void* bytehook_stub_t;
Hook完成后的回调函数原型:
typedef void (*bytehook_hooked_t)(
bytehook_stub_t task_stub, // 唯一存根,用于unhook
int status_code, // 执行状态码
const char *caller_path_name, // 调用者路径或名称
const char *sym_name, // 函数符号名
void *new_func, // 新函数地址
void *prev_func, // 原函数地址
void *arg); // 自定义参数
Hook操作类型
bytedance/bhook提供了三种粒度的Hook方式:
- 单调用者Hook - 精确控制特定调用者
bytehook_stub_t bytehook_hook_single(
const char *caller_path_name,
const char *callee_path_name,
const char *sym_name,
void *new_func,
bytehook_hooked_t hooked,
void *hooked_arg);
- 部分调用者Hook - 通过过滤器选择调用者
bytehook_stub_t bytehook_hook_partial(
bytehook_caller_allow_filter_t caller_allow_filter,
void *caller_allow_filter_arg,
const char *callee_path_name,
const char *sym_name,
void *new_func,
bytehook_hooked_t hooked,
void *hooked_arg);
- 全调用者Hook - 拦截所有调用者
bytehook_stub_t bytehook_hook_all(
const char *callee_path_name,
const char *sym_name,
void *new_func,
bytehook_hooked_t hooked,
void *hooked_arg);
Hook任务管理
bytedance/bhook以task为单位管理Hook操作:
- 每个task对应一个目标函数符号
- 支持添加回调通知Hook执行结果
- 三种调用者维度控制粒度
- 同步执行,不创建额外线程
自动完成机制:
- 对于单调用者Hook:立即查找并执行,未找到则等待后续加载
- 对于部分/全调用者Hook:持续监控新加载的ELF并执行Hook
路径名称处理
bytedance/bhook支持两种名称格式:
- pathname:完整路径(如
/system/lib/libc.so) - basename:文件名(如
libc.so)
兼容性注意:
- Android 5.x/6.x设备可能只返回basename
- 在过滤器和回调中需要自行处理兼容性
- 建议唯一性库使用basename,可能冲突的使用pathname
处理Linker Namespace
Android 8.0+引入了linker namespace机制,导致同basename的多个库被加载。bytedance/bhook通过callee_path_name参数精确指定目标库:
// 只Hook特定路径的库函数
bytehook_hook_all(
"/system/lib64/libcutils.so", // 精确指定被调用者
"atrace_begin_body",
my_new_func,
my_hooked_callback,
my_hooked_callback_arg);
Unhook操作
保存Hook返回的存根,用于后续解除Hook:
int bytehook_unhook(bytehook_stub_t stub);
使用规范:
- 同步执行,返回0表示成功
- 操作后应将stub置为NULL避免误用
Proxy函数编写指南
调用原函数
使用BYTEHOOK_CALL_PREV宏安全调用原函数:
C++示例:
size_t my_strlen(const char* const str) {
BYTEHOOK_STACK_SCOPE();
return BYTEHOOK_CALL_PREV(my_strlen, str);
}
C示例:
typedef size_t (*strlen_t)(const char* const);
size_t my_strlen(const char* const str) {
size_t result = BYTEHOOK_CALL_PREV(my_strlen, strlen_t, str);
BYTEHOOK_POP_STACK();
return result;
}
栈清理
必须确保执行栈清理:
- C++:使用
BYTEHOOK_STACK_SCOPE(推荐RAII方式) - C:在每个返回点调用
BYTEHOOK_POP_STACK
获取返回地址
使用专用宏获取真实返回地址:
void* lr = BYTEHOOK_RETURN_ADDRESS();
特殊场景:Hook dlopen系列函数
bytedance/bhook提供了专用API监控so加载:
void bytehook_add_dlopen_callback(
bytehook_pre_dlopen_t pre,
bytehook_post_dlopen_t post,
void *data);
特性:
- 不区分Android版本和具体函数(dlopen/android_dlopen_ext)
- 提供加载前后回调点
- 自动处理兼容性问题
最佳实践建议
-
路径处理:
- 唯一性库使用basename
- 可能冲突的使用完整pathname
- 自行处理Android 5.x/6.x兼容性
-
Proxy函数:
- 必须进行栈清理
- 使用专用宏调用原函数
- 多SDK场景协商hook顺序
-
性能考虑:
- 合理使用callee_path_name加速符号查找
- 避免不必要的全量Hook
-
错误处理:
- 检查Hook/Unhook返回值
- 正确处理回调中的状态码
bytedance/bhook框架通过精细的设计,在Android各版本上提供了稳定可靠的Native Hook能力。理解其核心机制和最佳实践,可以帮助开发者构建更强大的Native层监控和修改功能。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



