提问人:bobeff 提问时间:5/18/2023 最后编辑:bobeff 更新时间:5/18/2023 访问量:177
如何在 Linux 上挂钩 malloc 函数?
How to hook malloc function on Linux?
问:
我正在阅读 Fabien Sanglard 的 Driving Compilers 系列文章。在第 3 部分关于编译器的章节中,有一个挂钩 malloc 函数的示例。首先,显示了一个属于无限递归的错误解:
void* malloc(size_t sz) {
void *(*libc_malloc)(size_t) = dlsym(RTLD_NEXT, "malloc");
printf("malloced %zu bytes\n", sz);
return libc_malloc(sz);
}
递归的原因是 dlsym 函数在内部调用 malloc。
之后,提供了一个假定的固定解决方案:
#include <stdio.h>
#include <dlfcn.h>
static void* (*real_malloc)(size_t) = nullptr;
void *malloc(size_t size) {
if(!real_malloc) {
real_malloc = dlsym(RTLD_NEXT, "malloc");
}
printf("malloc(%d) = ", size);
return real_malloc(size);
}
除了解决方案并没有真正解决,因为它遇到了同样的问题。例如,如果我将函数从 malloc 重命名为 那么它将起作用,但它不再是钩子,因为其他软件使用 而不是 .有什么解决方案可以解决这个问题吗?my_malloc
malloc
my_malloc
答:
更多的调查表明,真正调用并导致无限递归的是函数调用,而不是文章中所写的函数调用。这将问题减少到如何以不会发生这种情况的方式进行打印。我想出了以下解决方案:malloc
printf
dlsym
#include <stdio.h>
#include <dlfcn.h>
static void* (*real_malloc)(size_t) = NULL;
void* malloc(size_t size) {
if(!real_malloc) {
real_malloc = dlsym(RTLD_NEXT, "malloc");
}
static char isPrintF = 0;
if (isPrintF) {
return real_malloc(size);
}
char* p = real_malloc(size);
isPrintF = 1;
printf("malloc(%zu) = %p\n", size, p);
isPrintF = 0;
return p;
}
评论
isPrintF
snprintf()
malloc()
s[n]printf()
s[n]printf()
malloc()
首先,根据 7.1.4 库函数的使用,(草案)C11 标准第 4 段:
标准库中的函数不保证是可重入的,并且可能会修改具有静态或线程存储持续时间的对象。
因此,您不能安全地使用 C 标准中的任何功能并保证安全。您需要依赖特定于平台的解决方案。
首先,您可以找到您的平台允许从信号处理程序中调用哪些函数 - 这些函数几乎必须是可重入的。
对于基于 POSIX 的系统,我假设您正在使用,因为您使用了 POSIX 函数 dlsym(),
您可以从 2.4 Signal Concepts 开始,它有一个广泛的列表。
请注意,它位于异步信号安全函数列表中,但未列。write()
printf()
所以你的代码
static void* (*real_malloc)(size_t) = nullptr;
void *malloc(size_t size) {
if(!real_malloc) {
real_malloc = dlsym(RTLD_NEXT, "malloc");
}
printf("malloc(%d) = ", size);
return real_malloc(size);
}
可以替换为
static void* (*real_malloc)(size_t) = nullptr;
void *malloc(size_t size) {
if(!real_malloc) {
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock( &mutex );
if(!real_malloc) {
real_malloc = dlsym(RTLD_NEXT, "malloc");
}
pthread_mutex_unlock( &mutex );
}
write( STDOUT_FILENO, "malloc()", strlen( "malloc()" );
return real_malloc(size);
}
请注意,我省略了地址 - 需要将其转换为字符串。这并不难做到,并且可以很容易地找到有关如何做到这一点的示例,例如在如何将 int 转换为 C 中的字符串?的答案中。请注意,您不能使用任何使用非异步信号安全标准库函数的答案。
如果你在 Solaris 上运行,实际上在那里是异步信号安全的。s[n]printf()
我还为多线程使用添加了一些保护 - 在获取应该保护的实际值时存在竞争条件,如果只是因为如果指针值损坏,您可能永远无法重现导致的任何错误。malloc()
编辑
根据 @ChrisDodd 的评论,已修复以解决对安全性的担忧:dlsym()
static void* (*real_malloc)(size_t) = NULL;
__attribute__((constructor))
static void initValues(void) {
real_malloc = dlsym(RTLD_NEXT, "malloc");
}
void *malloc(size_t size) {
write( STDOUT_FILENO, "malloc()", strlen( "malloc()" );
return real_malloc(size);
}
请注意,替换中的代码现在要简单得多 - 不可能有竞争条件。malloc()
评论
malloc()
malloc()
dlsym()
malloc()
dlsym()
评论
printf("malloc(%zu) = \n", size);
试看跌期权
,不知道这是否在内部调用,我猜不会。如果你也想看看尺寸,可能必须发挥创意..打印到本地 char 数组,然后,或者一次打印一个字节的大小?malloc
puts
putchar
printf()
malloc()
printf()