如何 hook calloc

一、如何 hook calloc

dlsym 内部可能使用 calloc 分配内存。如果 hook calloc 的时候,使用 dlsym 可能会导致出现死循环。

如下是一种解决方案:

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
/**
* 对于 hook calloc 的几点说明
* 问题点:dlsym 底层实现调用了 calloc 进行内存分配。因此 hook calloc 时需要注意不能出现死循环
*
* 当前解决方案如下:
* 1. 在全局开辟一块内存空间,用来做为 dlsym 内部需要 calloc 的空间
* 2. 设置 my_init_calloc_hook 函数的属性为 constructor,使其自动调用,且在 main 函数之前调用,实现初始化
* 并且设置 constructor 构造函数的优先级为 1。constructor 的优先级,数值越小,越先调用;destructor 的优先级,数值越大,越先调用
* 这个优先级 [1, 100] 范围是保留的,最小只能使用 101
* 因此 my_init_calloc_hook 函数“尽可能”保证了当前代码是当前进程第一个调用 calloc 的位置
* 当 dlsym 内部调用 calloc 时,此时 real_calloc 为空,则使用全局提前开辟好的空间,8192 字节大小够用
* 3. 当 dlsym 寻找 calloc 符号找不到时,出错情况下,为了保证 calloc 函数的逻辑。
* 使用全局变量 is_gather_calloc_ptr_error 做为判断。使业务调用 calloc 直接返回 null,而不是 calloc_ptr_buffer
* 4. 对于在 my_init_calloc_hook 执行前,有调用 calloc 的行为,我们一般默认返回 nullptr。如何实现呢?
* 使用全局变量 is_init_calloc,只有在进入 my_init_calloc_hook 后才将其置为 true
* 保证了在 my_init_hook 之前调用 calloc 的场景都返回 nullptr
*
* 存在问题:
* 此解决方案在 my_init_calloc_hook 执行后,calloc 才可用
*/
static unsigned char calloc_ptr_buffer[8192] = {0};
static calloc_type real_calloc = nullptr;
static bool is_gather_calloc_ptr_error = false;
static bool is_init_calloc = false;

__attribute__((constructor(101))) static void my_init_calloc_hook(void) {
is_init_calloc = true;
static calloc_type tmp_calloc_ptr = reinterpret_cast<calloc_type>(dlsym(RTLD_NEXT, "calloc"));
if (tmp_calloc_ptr != nullptr) {
real_calloc = tmp_calloc_ptr;
} else {
// dlsym 出错的情况
is_gather_calloc_ptr_error = true;
}
}

void* calloc(size_t nmemb, size_t size) __THROW {
if (real_calloc == nullptr) {
if (is_gather_calloc_ptr_error) return nullptr;
if (is_init_calloc) return calloc_ptr_buffer;
return nullptr;
}
void* point = real_calloc(nmemb, size);
if (point) {
default_malloc_size_func(_msize(point), true);
}
return point;
}