天道酬勤,学无止境

LD_PRELOAD can not intercept syscalls, but only libcalls?

My code works well with malloc, but not with mmap. The code is below:

main.c

#include <stdio.h>
#include <stdlib.h>

int main(){
  int * p = (int*) malloc(sizeof(int));
  printf("in main(): value p = %d\n", *p);
  free(p);
}

preload.c

#define _GNU_SOURCE
#include <time.h>
#include <dlfcn.h>
#include <stdio.h>
#include <sys/types.h>

void *(*orig_malloc)(size_t size);
void *malloc(size_t size){
  printf("  Hooked(preload)! malloc:size:%lu\n", size);
  return orig_malloc(size);
}

void * (*orig_mmap)(void *start, size_t length, int prot, int flags, int fd, off_t offset);
void * mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset){
  printf("  Hooked(preload)! mmap:start:%p, length:%lu, prot:%d, flags:%p, fd:%p, offset:%d\n", start, length, prot, flags, fd, offset);
  return orig_mmap(start, length, prot, flags, fd, offset);
}

void
_init(void)
{
  printf("Loading hack.\n");
  orig_malloc = (void* (*)(size_t)) dlsym(RTLD_NEXT, "malloc");
  orig_mmap = (void* (*)(void*, size_t, int, int, int, off_t)) dlsym(RTLD_NEXT, "mmap");
}

to compile it

gcc -Wall -fPIC -DPIC -c preload.c
ld -shared -o preload.so preload.o -ldl
gcc main.c

to run it with LD_PRELOAD

LD_PRELOAD=./preload.so ./a.out

to run it with strace

strace ./a.out 2>&1 | view -

the printout from LD_PRELOAD does not hook calls to mmap, but only calls to malloc. Meanwhile, when running with strace, the printout does show mmap is called multiple times.

This result baffles me; assuming mmap is indeed called by main.c (I guess through malloc), how come preload.c can not intercept mmap?

PS: My platform is Ubuntu 14.04 with Linux kernel 3.13

PS2: By syscall, I mean the syscall wrapper in libc (not sure if this makes a difference to the question though)..

评论

mmap is a syscall, malloc is not.

Since syscalls are essential for the functioning of a program, they must work before ld.so actually springs into action, they are reside in a section that gets loaded before everything else; it may be linked dynamically, but that mapping (of that particular "virtual" dynamic object) is done by the kernel itself. Looong before ld.so actually gets to work.

The mmap calls printed by strace are glibc-internal. It's impossible to intercept the glibc-internal calls to mmap with LD_PRELOAD:

mmap is not in the .plt-section of /lib64/libc.so.6 but is called directly from glibc and therefore LD_PRELOAD can't intercept glibc's calls to mmap.

$ objdump -j .plt -d /lib64/libc.so.6 

/lib64/libc.so.6:     file format elf64-x86-64


Disassembly of section .plt:

000000000001f400 <*ABS*+0x8e3fb@plt-0x10>:
   1f400:   ff 35 02 ac 39 00       pushq  0x39ac02(%rip)        # 3ba008 <_GLOBAL_OFFSET_TABLE_+0x8>
   1f406:   ff 25 04 ac 39 00       jmpq   *0x39ac04(%rip)        # 3ba010 <_GLOBAL_OFFSET_TABLE_+0x10>
   1f40c:   0f 1f 40 00             nopl   0x0(%rax)

000000000001f410 <*ABS*+0x8e3fb@plt>:
   1f410:   ff 25 02 ac 39 00       jmpq   *0x39ac02(%rip)        # 3ba018 <_GLOBAL_OFFSET_TABLE_+0x18>
   1f416:   68 0b 00 00 00          pushq  $0xb
   1f41b:   e9 e0 ff ff ff          jmpq   1f400 <data.8467+0x1f390>

000000000001f420 <*ABS*+0xb8c10@plt>:
   1f420:   ff 25 fa ab 39 00       jmpq   *0x39abfa(%rip)        # 3ba020 <_GLOBAL_OFFSET_TABLE_+0x20>
   1f426:   68 0a 00 00 00          pushq  $0xa
   1f42b:   e9 d0 ff ff ff          jmpq   1f400 <data.8467+0x1f390>

000000000001f430 <realloc@plt>:
   1f430:   ff 25 f2 ab 39 00       jmpq   *0x39abf2(%rip)        # 3ba028 <_GLOBAL_OFFSET_TABLE_+0x28>
   1f436:   68 00 00 00 00          pushq  $0x0
   1f43b:   e9 c0 ff ff ff          jmpq   1f400 <data.8467+0x1f390>

000000000001f440 <malloc@plt>:
   1f440:   ff 25 ea ab 39 00       jmpq   *0x39abea(%rip)        # 3ba030 <_GLOBAL_OFFSET_TABLE_+0x30>
   1f446:   68 01 00 00 00          pushq  $0x1
   1f44b:   e9 b0 ff ff ff          jmpq   1f400 <data.8467+0x1f390>

000000000001f450 <__tls_get_addr@plt>:
   1f450:   ff 25 e2 ab 39 00       jmpq   *0x39abe2(%rip)        # 3ba038 <_GLOBAL_OFFSET_TABLE_+0x38>
   1f456:   68 02 00 00 00          pushq  $0x2
   1f45b:   e9 a0 ff ff ff          jmpq   1f400 <data.8467+0x1f390>

000000000001f460 <memalign@plt>:
   1f460:   ff 25 da ab 39 00       jmpq   *0x39abda(%rip)        # 3ba040 <_GLOBAL_OFFSET_TABLE_+0x40>
   1f466:   68 03 00 00 00          pushq  $0x3
   1f46b:   e9 90 ff ff ff          jmpq   1f400 <data.8467+0x1f390>

000000000001f470 <*ABS*+0x90f60@plt>:
   1f470:   ff 25 d2 ab 39 00       jmpq   *0x39abd2(%rip)        # 3ba048 <_GLOBAL_OFFSET_TABLE_+0x48>
   1f476:   68 09 00 00 00          pushq  $0x9
   1f47b:   e9 80 ff ff ff          jmpq   1f400 <data.8467+0x1f390>

000000000001f480 <_dl_find_dso_for_object@plt>:
   1f480:   ff 25 ca ab 39 00       jmpq   *0x39abca(%rip)        # 3ba050 <_GLOBAL_OFFSET_TABLE_+0x50>
   1f486:   68 04 00 00 00          pushq  $0x4
   1f48b:   e9 70 ff ff ff          jmpq   1f400 <data.8467+0x1f390>

000000000001f490 <calloc@plt>:
   1f490:   ff 25 c2 ab 39 00       jmpq   *0x39abc2(%rip)        # 3ba058 <_GLOBAL_OFFSET_TABLE_+0x58>
   1f496:   68 05 00 00 00          pushq  $0x5
   1f49b:   e9 60 ff ff ff          jmpq   1f400 <data.8467+0x1f390>

000000000001f4a0 <free@plt>:
   1f4a0:   ff 25 ba ab 39 00       jmpq   *0x39abba(%rip)        # 3ba060 <_GLOBAL_OFFSET_TABLE_+0x60>
   1f4a6:   68 06 00 00 00          pushq  $0x6
   1f4ab:   e9 50 ff ff ff          jmpq   1f400 <data.8467+0x1f390>

000000000001f4b0 <*ABS*+0xb8bc0@plt>:
   1f4b0:   ff 25 b2 ab 39 00       jmpq   *0x39abb2(%rip)        # 3ba068 <_GLOBAL_OFFSET_TABLE_+0x68>
   1f4b6:   68 08 00 00 00          pushq  $0x8
   1f4bb:   e9 40 ff ff ff          jmpq   1f400 <data.8467+0x1f390>

000000000001f4c0 <*ABS*+0x8ec70@plt>:
   1f4c0:   ff 25 aa ab 39 00       jmpq   *0x39abaa(%rip)        # 3ba070 <_GLOBAL_OFFSET_TABLE_+0x70>
   1f4c6:   68 07 00 00 00          pushq  $0x7
   1f4cb:   e9 30 ff ff ff          jmpq   1f400 <data.8467+0x1f390>
[m@localhost ~]$ 

Calls to mmap in glibc don't call it via a .plt entry but directly, it's impossible to intercept these calls:

$ objdump -d /lib64/libc.so.6 | grep mmap
[...]
   81628:   e8 83 ad 07 00          callq  fc3b0 <mmap>
   8177c:   e8 2f ac 07 00          callq  fc3b0 <mmap>
00000000000fc3b0 <mmap>:
   fc3c0:   73 01                   jae    fc3c3 <mmap+0x13>
  13a267:   e8 44 21 fc ff          callq  fc3b0 <mmap>
$ 

00000000000fc3b0 <mmap>:
   fc3b0:   49 89 ca                mov    %rcx,%r10
   fc3b3:   b8 09 00 00 00          mov    $0x9,%eax
   fc3b8:   0f 05                   syscall 
   fc3ba:   48 3d 01 f0 ff ff       cmp    $0xfffffffffffff001,%rax
   fc3c0:   73 01                   jae    fc3c3 <mmap+0x13>
   fc3c2:   c3                      retq   
   fc3c3:   48 8b 0d 96 da 2b 00    mov    0x2bda96(%rip),%rcx        # 3b9e60 <_DYNAMIC+0x2e0>
   fc3ca:   f7 d8                   neg    %eax
   fc3cc:   64 89 01                mov    %eax,%fs:(%rcx)
   fc3cf:   48 83 c8 ff             or     $0xffffffffffffffff,%rax
   fc3d3:   c3                      retq   
   fc3d4:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
   fc3db:   00 00 00 
   fc3de:   66 90                   xchg   %ax,%ax

The title to your question is actually the answer.

assuming mmap is indeed called by main.c (I guess through malloc)

So your main.c doesn't call the library function mmap()? Of course you can't intercept syscalls this way, how would you do it? Some architectures have a syscall CPU instruction, some use a special interrupt ... there are a lot of ways, but it's in any case completely different from C calling conventions. The kernel is not somehow linked to your binary but takes control (with some hardware assistence) when your userspace process does something ... "special".

If you want to know how to intercept syscalls, this is of course very platform specific, but I would advise you to just take a look in the source of the strace utility. You will never see an malloc() in strace, because this is not a syscall, malloc() uses the mmap syscall.

On the other hand, if you preload your lib to a binary that actually calls the libc mmap() function, it will work as expected.

In a nutshell: libc mmap() is a user-friendly wrapper around the mmap syscall and with the following main:

#include <sys/mman.h>

int main()
{
    void *test = mmap(0, 20, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS,
        -1, 0);
    return 0;
}

The result is:

Loading hack.
Hooked(preload)! mmap:start:(nil), length:20, prot:3, flags:0x22,
fd:0xffffffff, offset:0

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • 如何增强Linux内核中的访问控制安全 | 洞见
    背景前段时间,我们的项目组在帮客户解决一些操作系统安全领域的问题,涉及到windows,Linux,macOS三大操作系统平台。无论什么操作系统,本质上都是一个软件,任何软件在一开始设计的时候,都不能百分之百的满足人们的需求,所以操作系统也是一样,为了尽可能的满足人们需求,不得不提供一些供人们定制操作系统的机制。当然除了官方提供的一些机制,也有一些黑魔法,这些黑魔法不被推荐使用,但是有时候面对具体的业务场景,可以作为一个参考的思路。Linux中常见的拦截过滤本文着重介绍Linux平台上常见的拦截:用户态动态库拦截。内核态系统调用拦截。堆栈式文件系统拦截。inline hook拦截。LSM(Linux Security Modules)动态库劫持Linux上的动态库劫持主要是基于LD_PRELOAD环境变量,这个环境变量的主要作用是改变动态库的加载顺序,让用户有选择的载入不同动态库中的相同函数。但是使用不当就会引起严重的安全问题,我们可以通过它在主程序和动态连接库中加载别的动态函数,这就给我们提供了一个机会,向别人的程序注入恶意的代码。假设有以下用户名密码验证的函数:#include <stdio.h>#include <string.h>#include <stdlib.h>int main(int argc, char **argv){char passwd[] =
  • Linux内核中模块的替代功能(Overriding functionality with modules in Linux kernel)
    问题 在不深入了解为什么的细节的情况下,我正在寻找一种干净的(尽可能)的方法来替换可加载模块中的内核函数和系统调用。 我最初的想法是编写一些代码来覆盖某些功能,这些功能将采用原始功能(可能的话,调用该功能),然后添加一些我自己的代码。 关键是我编写的函数必须具有原始函数的名称,因此其他代码在尝试访问它时将改为访问我的函数。 通过将代码放入适当的函数中,我可以很容易地(相对地)直接在内核中执行此操作,但是我想知道是否有人知道一点C魔术,而这不一定是可以实现该目标的可怕的内核(或C)编码实践同样的结果。 我想到了#defines和typedef的想法,但是我不太明白。 简而言之:有人知道一种有效地覆盖Linux内核中的功能(从模块)的方法吗? 编辑:由于被问到,我本质上是想从内核中记录某些功能(创建/删除目录等),但是出于理智的考虑,可加载模块似乎很有意义,而不是必须编写一个大补丁来内核代码,并在每次更改时重新编译。 可以向内核中添加最少的代码是可以的,但是我想将大部分工作卸载到模块上。 回答1 我意识到这个问题已有3年了,但是对于其他试图做这种事情的人来说,内核有一个名为kprobes的接口来执行您需要的操作。 回答2 您可能希望挂接系统调用(PDF链接),这将有效地让您记录调用内核函数的用户进程。 如果您确实想记录内核对内核功能的使用情况,则需要查看内核功能跟踪。 回答3
  • 将共享库注入流程(Inject shared library into a process)
    问题 我刚刚开始学习Linux中的注入技术,并想编写一个简单程序将共享库注入正在运行的进程中。 (该库将只打印一个字符串。)但是,经过几个小时的研究,我找不到任何完整的示例。 好吧,我确实发现我可能需要使用ptrace()来暂停进程并注入内容,但是不确定如何将库加载到目标进程的内存空间中以及C代码中的重定位内容。 有谁知道共享库注入的任何好的资源或可行示例? (当然,我知道可能有一些现有的库,例如hotpatch,我可以使用它们使注入更加容易,但这不是我想要的) 如果有人可以写一些伪代码或给我一个例子,我将不胜感激。 谢谢。 PS:我不是在问LD_PRELOAD技巧。 回答1 在对原始问题的评论中提到的“ LD_PRELOAD技巧”并不是真正的技巧。 这是在动态链接的过程中添加功能(或更常见的是插入现有功能)的标准方法。 它是Linux动态链接器ld.so提供的标准功能。 Linux动态链接器由环境变量(和配置文件)控制。 LD_PRELOAD只是一个环境变量,它提供应针对每个进程链接的动态库的列表。 (您还可以将库添加到/etc/ld.so.preload ,在这种情况下,将为每个二进制文件自动加载该库,而与LD_PRELOAD环境变量无关。) 这是一个例子example.c : #include <unistd.h> #include <errno.h> static void
  • How to intercept file system access inside dlopen()?
    I want to intercept all file system access that occurs inside of dlopen(). At first, it would seem like LD_PRELOAD or -Wl,-wrap, would be viable solutions, but I have had trouble making them work due to some technical reasons: ld.so has already mapped its own symbols by the time LD_PRELOAD is processed. It's not critical for me to intercept the initial loading, but the _dl_* worker functions are resolved at this time, so future calls go through them. I think LD_PRELOAD is too late. Somehow malloc circumvents the issue above because the malloc() inside of ld.so does not have a functional free()
  • Dockerfile HOSTNAME Instruction for docker build like docker run -h
    I am attempting to set the hostname inside a docker container during the build since certain software installs use the discovered randomly generated hostname and permanently bake that hostname into the configuration. While it is possible to set the hostname when you run interactively via run -h, the same functionality is not available using build via the Dockerfile. The only way to work around this is to use LD_PRELOAD hacks so that I can set the hostname to localhost. The LD_PRELOAD hacks have unwanted sideeffects that I am having trouble working around. The software install works without
  • How to use ptrace(2) to change behaviour of syscalls?
    Are there any guides or examples (especially ARM ones) or libraries of using ptrace to affect execution of other process? For example, to make it believe that some data is appeared on file descriptor (i.e. release select/poll with some result and "answer" the following read syscall before the kernel). Expecting something involving PTRACE_SYSEMU. Can it be done in portable way? I want something like libc-overriding LD_PRELOAD trick, but which can be attached at runtime. Can it be done with some gdb commands? Ideal variant would be if there is some library where I can easily and portably hook
  • What are the differences between LD_PRELOAD and strace?
    Both methods are used to gather system calls also parameters and return values of them. When we prefer LD_PRELOAD and why? Maybe we can say that we can only gather syscalls via strace but we can gather library calls with LD_PRELOAD trick. However, there is another tracer for libraries whose name is ltrace.
  • LD_PRELOAD无法按预期工作(LD_PRELOAD does not work as expected)
    问题 考虑以下可以在任何程序执行之前预加载的库: // g++ -std=c++11 -shared -fPIC preload.cpp -o preload.so // LD_PRELOAD=./preload.so <command> #include <iostream> struct Goodbye { Goodbye() {std::cout << "Hello\n";} ~Goodbye() {std::cout << "Goodbye!\n";} } goodbye; 问题是,尽管始终调用全局变量goodbye的构造函数,但对于某些程序(例如ls却没有调用析构函数: $ LD_PRELOAD=./preload.so ls Hello 对于其他一些程序,按预期方式调用析构函数: $ LD_PRELOAD=./preload.so man Hello What manual page do you want? Goodbye! 您能解释一下为什么在第一种情况下不调用析构函数吗? 编辑:上面的问题已得到解答,这是一个程序可能会使用_exit(),abort()退出。 然而: 有没有办法在预加载的程序退出时强制调用给定的函数? 回答1 ls具有atexit (close_stdout); 作为其初始化代码。 完成后,它将关闭stdout(即close(1) )
  • Preloading my library for a few functions while using the original by others using LD_PRELOAD
    I have written a wrapper for the open() syscall and preload it using the LD_PRELOAD environment variable. I want only a few functions of the program to use the modified open() whereas others would use the original. Separating the functions in two programs is not an option as one calls the other. How can it be done?
  • Dockerfile HOSTNAME docker build 指令,如 docker run -h(Dockerfile HOSTNAME Instruction for docker build like docker run -h)
    问题 我试图在构建期间在 docker 容器内设置主机名,因为某些软件安装使用发现的随机生成的主机名并将该主机名永久地烘焙到配置中。 虽然可以在通过 run -h 交互运行时设置主机名,但使用通过 Dockerfile 构建时无法使用相同的功能。 解决此问题的唯一方法是使用 LD_PRELOAD hacks,以便我可以将主机名设置为 localhost。 LD_PRELOAD 黑客有我无法解决的不必要的副作用。 使用“docker run -it -h localhost”时,软件安装工作没有问题。 strace 报告安装程序调用 uname 来确定主机名。 uname({sys="Linux", node="12b0c7c7eacb", ...}) = 0 有谁知道如何解决这个限制? 更新 1 这不是问题如何处理 Dockerfile 中的 -h 选项之类的特定主机名的问题,因为它专门讨论了动态生成该文件引起的“/etc/hosts”问题。 这很容易解决,因为它是一个可写文件。 这是关于尝试从系统调用(例如 uname 和 gethostname)解析主机名的软件安装。 据我所知,这无法解决,因为无法在正在运行的Docker容器中更改主机名。 uname 系统调用可能引用 /proc/sys/kernel/hostname,这是只读的,不能更改。 通常可以运行 hostname
  • Linux内核-通过模块动态添加系统调用(Linux kernel - add system call dynamically through module)
    问题 有什么方法可以添加动态的系统调用,例如通过模块吗? 我找到了一些地方,可以通过仅更改sys_call_table[]数组以覆盖模块的方式覆盖现有系统调用,以在安装模块时获取覆盖的函数而不是本机函数,但是您可以使用新的系统调用和模块? 回答1 否, sys_call_table的大小是固定的: const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = { ... 您可能已经发现的最好的办法是拦截现有的系统调用。 回答2 扎克,是的,有可能:D 尽管sys_call_table具有固定大小,但在某些情况下,表中可能有空闲位置 看这个链接: lxr.free-electrons.com/source/arch/x86/kernel/syscall_32.c lxr.free-electrons.com/source/arch/x86/kernel/syscall_64.c 首先,内核使用指向sys_ni_syscall的指针填充sys_call_table的所有位置编译时,将基于以下表生成文件asm / syscalls_32.h和asm / syscalls_64.h : lxr.free-electrons.com/source/arch/x86/syscalls/syscall_32.tbl lxr.free
  • Arguments in syscall intercept using loadable kernel module seem to be broken
    First post so I apologize for the possibly low quality explanation. I was trying to write a loadable kernel module that does nothing but intercept syscalls to SYS_open, print the arguments to KERN_INFO and then forward the arguments to the real syscall. The forwarding part seems to be working just fine, but I'm having issues with the printing, arguments seem to be broken, from the syscall interceptor function's perspective. Following are the pointer to the real open syscall as well as the interceptor definition. asmlinkage int (*real_open) (const char __user *, int, umode_t); asmlinkage int
  • 使用LD_PRELOAD机制覆盖'malloc'(Overriding 'malloc' using the LD_PRELOAD mechanism)
    问题 我正在尝试编写一个简单的共享库,该共享库会将malloc调用记录到stderr(如果可以的话,可以是一种“ mtrace”)。 但是,这不起作用。 这是我的工作: /* mtrace.c */ #include <dlfcn.h> #include <stdio.h> static void* (*real_malloc)(size_t); void *malloc(size_t size) { void *p = NULL; fprintf(stderr, "malloc(%d) = ", size); p = real_malloc(size); fprintf(stderr, "%p\n", p); return p; } static void __mtrace_init(void) __attribute__((constructor)); static void __mtrace_init(void) { void *handle = NULL; handle = dlopen("libc.so.6", RTLD_LAZY); if (NULL == handle) { fprintf(stderr, "Error in `dlopen`: %s\n", dlerror()); return; } real_malloc = dlsym(handle,
  • 泊坞窗容器中是否可以更改日期?(Is it possible change date in docker container?)
    问题 我在tomcat中有一个带有正在运行的程序的容器。 我只需要在此容器中更改日期并测试我的程序行为。 我有时间敏感的逻辑,有时需要查看几天或几个月后会发生什么。 码头工人有可能吗? 我读到如果更改容器中的日期,则日期将在主机系统上更改。 但这对我来说不是一个好主意。 我需要在一台服务器上有此应用程序的几个实例,并有可能为每个实例设置不同的时间。 但是,当我尝试更改容器内的日期时,出现错误: sudo date 04101812 date: cannot set date: Operation not permitted Fri Apr 10 18:12:00 UTC 2015 回答1 在不影响主机操作系统的情况下,很有可能动态地更改Docker容器中的时间。 解决的办法是伪造它。 该库拦截所有用于检索当前时间和日期的系统调用程序。 实施很容易。 适当地向Dockerfile添加功能: WORKDIR / RUN git clone https://github.com/wolfcw/libfaketime.git WORKDIR /libfaketime/src RUN make install 请记住在运行要应用伪造时间的应用程序之前,先设置环境变量LD_PRELOAD 。 例子: CMD ["/bin/sh", "-c", "LD_PRELOAD=/usr/local
  • How do I reimplement (or wrap) a syscall function on Linux?
    Suppose I want to completely take over the open() system call, maybe to wrap the actual syscall and perform some logging. One way to do this is to use LD_PRELOAD to load a (user-made) shared object library that takes over the open() entry point. The user-made open() routine then obtains the pointer to the glibc function open() by dlsym()ing it, and calling it. The solution proposed above is a dynamic solution, however. Suppose I want to link my own open() wrapper statically. How would I do it? I guess the mechanism is the same, but I also guess there will be a symbol clash between the user
  • 如何在Linux上重新实现(或包装)syscall函数?(How do I reimplement (or wrap) a syscall function on Linux?)
    问题 假设我想完全接管open()系统调用,也许要包装实际的syscall并执行一些日志记录。 一种方法是使用LD_PRELOAD加载一个(用户制作的)共享对象库,该库将接管open()入口点。 然后,用户制作的open()例程通过dlsym()对其进行调用并获取指向glibc函数open()的指针。 但是,以上提出的解决方案是动态解决方案。 假设我想静态地链接自己的open()包装器。 我该怎么办? 我猜想机制是一样的,但是我也猜想用户定义的open()和libc open()之间会有符号冲突。 请分享其他任何技术来达到相同的目标。 回答1 您可以使用ld提供的包装功能。 来自man ld : --wrap symbol使用包装函数。 任何对symbol未定义引用都将解析为__wrap_symbol 。 任何对__real_symbol未定义引用都将解析为symbol 。 所以,你只需要使用前缀__wrap_为您的包装功能和__real_当你想调用的真正功能。 一个简单的例子是: malloc_wrapper.c : #include <stdio.h> void *__real_malloc (size_t); /* This function wraps the real malloc */ void * __wrap_malloc (size_t size) { void
  • Windows 上的中介层(interposers on Windows)
    问题 是否可以使用 LD_PRELOAD 替代系统功能,就像在 Linux 和 Solaris 上一样 例如通过设置环境变量:LD_PRELOAD=/path/to/mymalloc.so 我将替换 malloc 函数,而不是在系统库中已经安装的 C 运行时中。 系统 dll 中的所有其他功能将正常运行。 回答1 Microsoft Research 有一个名为 Detours 的库,它允许您拦截 Win32 API 调用。 Detours 是一个用于在 x86、x64 和 IA64 机器上检测任意 Win32 函数的库。 Detours 通过重写目标函数的内存代码来拦截 Win32 函数。 Detours 包还包含将任意 DLL 和数据段(称为有效负载)附加到任何 Win32 二进制文件的实用程序。 回答2 如果 Detours(如前所述)不是一个选项 - 那么你可以看看 WinAPI Override。 它是主动维护的。 请注意,只有 32 位版本可用。
  • 覆盖C中的函数调用(Override a function call in C)
    问题 为了记录调用,我想覆盖对各种API的某些函数调用,但是我也可能想在将数据发送到实际函数之前对其进行操作。 例如,假设我在源代码中使用了数千次名为getObjectName的函数。 有时我想暂时重写此功能,因为我想更改此功能的行为以查看不同的结果。 我创建一个新的源文件,如下所示: #include <apiheader.h> const char *getObjectName (object *anObject) { if (anObject == NULL) return "(null)"; else return "name should be here"; } 我会像往常一样编译所有其他源代码,但在与API库链接之前,我先将其与该函数链接。 正常工作,除非我显然无法在覆盖函数中调用实函数。 有没有一种更简单的方法来“覆盖”一个函数而不会出现链接/编译错误/警告? 理想情况下,我希望能够仅通过编译和链接一个或两个额外的文件来覆盖该功能,而不是随意使用链接选项或更改程序的实际源代码。 回答1 如果您仅想为您的源捕获/修改调用,最简单的解决方案是将头文件( intercept.h )与以下内容放在一起: #ifdef INTERCEPT #define getObjectName(x) myGetObectName(x) #endif 和实施功能(在如下intercept
  • 如何拦截dll方法调用?(How to intercept dll method calls?)
    问题 如何拦截dll方法调用? 有哪些可用的技术? 只能在C / C ++中完成吗? 如何拦截从所有正在运行的进程到给定dll的方法调用? 如何拦截从给定进程到给定dll的方法调用? 回答1 我可以考虑两种标准方法来执行此操作 DLL导入表挂钩。 为此,您需要解析DLL的PE标头,找到导入表并写入您自己的函数的地址,而不是已在此处写入的函数的地址。 您可以保存原始函数的地址以便以后调用。 Wikipedia文章的外部链接中的引用应为您提供执行此操作所需的所有信息。 直接修改代码。 查找要挂钩的函数的实际代码,然后修改该函数的第一个操作码以跳转到您自己的代码。 您需要保存在那里的操作码,以便它们最终被执行。 这比听起来简单,主要是因为它已经由Detours库的形式已经由Microsoft自己实施了。 这是一件非常整洁的事情。 仅需几行代码,您就可以替换例如对Outlook.exe中对GetSystemMetrics()的所有调用,并观察发生的奇迹。 一种方法的优点是另一种方法的缺点。 第一种方法允许您将手术钩子精确地添加到所需的DLL,而所有其他DLL都将通过钩子脱钩。 第二种方法允许您使用最全局的钩子来拦截所有执行该函数的调用。 回答2 如果您事先了解所有DLL函数,则一种技术是编写自己的包装DLL,它将所有函数调用转发到实际DLL。 不必用C / C ++编写此DLL。
  • glibc不推荐使用的__malloc_hook功能的替代方法(An alternative for the deprecated __malloc_hook functionality of glibc)
    问题 我正在为C写一个内存分析器,为此它通过malloc_hooks拦截对malloc , realloc和free函数的调用。 不幸的是,由于它们在多线程环境中的不良行为而被弃用。 我找不到描述实现同一目标的替代性最佳实践解决方案的文档,有人可以启发我吗? 我已经读过一个简单的#define malloc(s) malloc_hook(s)可以解决问题,但这不适用于我所想到的系统设置,因为它过于侵入原始代码库,不适合使用在概要分析/跟踪工具中使用。 必须手动更改原始应用程序代码,这对于任何体面的分析器来说都是致命的。 理想情况下,仅通过链接到可选的共享库即可启用或禁用我要查找的解决方案。 例如,我当前的设置使用使用__attribute__ ((constructor))声明的函数来安装拦截malloc钩子。 谢谢 回答1 经过尝试一些事情之后,我终于设法弄清楚该如何做。 首先,在glibc , malloc被定义为弱符号,这意味着它可以被应用程序或共享库覆盖。 因此,不一定需要LD_PRELOAD 。 相反,我在共享库中实现了以下功能: void* malloc (size_t size) { [ ... ] } 由应用程序而不是glibc的malloc调用。 现在,等效于__malloc_hook的功能,仍然缺少一些东西。 1.)来电者地址 除了malloc的原始参数外,