在 dlopen-ed 库中覆盖 malloc/free

overwrite malloc/free in dlopen-ed library

我有一个共享库 - plugin.so,它由带有标志 RTLD_LOCAL 的主机程序 dlopen 编辑,我在该库中定义了自己的内存操作函数:

void *plugin_malloc(size_t size) { /* ... */ }
void plugin_free(void *ptr) { /* ... */ }

我需要用我自己的 plugin_malloc/plugin_free 替换 plugin.so 中的所有 malloc/free 调用,我尝试使用 GCC 的别名属性扩展:

void *malloc(size_t) __attribute__((alias("plugin_malloc"), used))
void free(void*) __attribute__((alias("plugin_free"), used))

然而,这仅在库链接到宿主程序时有效,但不适用于 dlopen 方式。

我在Linux编译器GCC-4.8.5,我有plugin.so的源代码,可以随意修改,但我不能修改宿主程序,不仅plugin.so而且整个程序都替换malloc/free也是可以的

那么,有什么解决办法吗?谢谢。


编辑:我也无权修改主机程序的启动参数、环境变量,我能做的只是向拥有的人提供plugin.so主机程序,他们 运行 主机程序和 dlopen 我的 plugin.so.

考虑到您在使用 -fPIC 编译的共享库中提供了一对名为 malloc 和 free 的函数(例如 plugin_malloc),那么您只需要 LD_PRELOAD调用您的客户端应用程序时:

LD_PRELOAD=/path/mymalloc.so executable

或在致电您的客户之前将其导出:

export LD_PRELOAD=/path/mymalloc.so
executable

更多详情:

[更新]

考虑到你不能改变环境,只能更换动态库,没有别的,那么你可以:

  • 找到 malloc/free 在内存中的位置
  • 将 jmp 的原始函数入口代码替换为您自己的函数

您的库需要一个初始化函数来完成这项肮脏的工作。检查构造函数属性 here。但是,可能不允许替换代码。

另一种探索的可能性(oit?):如果代码使用 glibc,您可以尝试在您的库中提供 __malloc_hook

还有一个:在您的库初始化函数中获取应用程序控制,永远不会从中返回,然后使用您的自定义设置再次执行该应用程序。

目前,考虑到您的限制,我无法得出任何其他可能性。

What I need is to replace ALL malloc/free calls in plugin.so with my own plugin_malloc/plugin_free,

这很容易做到。

假设您有 foo.oplugin_malloc.o 链接到 plugin.sofoo.o 使用 mallocfree,而 plugin.o 定义 plugin_mallocplugin_free.

然后:

objcopy --redefine-sym malloc=plugin_malloc --redefine-sym free=plugin_free foo.o foo2.o
gcc -shared -fPIC plugin.o foo2.o -o plugin.so

瞧:foo.o 中对 mallocfree 的所有引用都已被替换。享受。

更新:

if I call a glibc function which allocates memory and needs to be freed in my code, the program crashes. e.g. char *s = strdup("hello"); free(s); because strdup calls glibc's malloc, but the later free is my plugin_free

有几种方法,其中只有一些满足您的其他限制条件:

  1. 您必须将 所有 mallocs 和 frees 替换为您自己的 entire 程序 (例如,通过 LD_PRELOAD,或通过将 malloc 实现静态链接到主可执行文件中),或
  2. 您必须确保您不会调用任何您希望稍后free通过malloc分配内存的函数,或者
  3. 对于 stdupasprintf 等的任何此类调用,当您想要释放此内存时,您必须调用 __libc_free 而不是 plugin_free,或者
  4. plugin_malloc中,用16个额外的字节填充你分配的所有内存header(用于对齐)将幻数写入块的开头,并且return一个指针过去header 给来电者。在 plugin_free 中检查对齐是否正确,然后检查 header 中的幻数。如果它在那里,从指针中减去 16 并使用 plugin_free 的其余部分来释放内存。如果幻数不存在,假设指针不是来自 plugin_malloc,而是调用 __libc_free
  5. plugin_malloc 中跟踪您曾经分配的所有块。在 plugin_free 中检查该列表,如果指针不在列表中则使用 __libc_free

由于您拥有插件的所有资源,因此解决方案 2 或 3 应该可行,但显然需要您审核对 free 的每次调用以查看内存来自何处。

变体 4 和 5 不需要这样的审计,但没有将插件分配的内存与主程序分配的内存完全分开。

不要忘记其他分配内存的方法:reallocmemalignposix_memalign