如何在二进制执行期间挂钩所有linux系统调用
我试图修改
linux系统调用的默认行为.目前我正试图在实际调用它们之前挂钩并添加一个简单的print语句.我知道GCC链接器的标准’wrap’选项以及它如何用于挂钩包装器
Link to GCC Linker options.这完全适用于open(),fstat(),fwrite()等(我实际上挂钩了libc包装器).
更新: 限制是并非所有系统调用都与此方法相关联. > strace ./sample execve("./sample",["./sample"],[/* 72 vars */]) = 0 uname({sys="Linux",node="kumar",...}) = 0 brk(0) = 0x71f000 brk(0x7201c0) = 0x7201c0 arch_prctl(ARCH_SET_FS,0x71f880) = 0 readlink("/proc/self/exe","/home/admin/sample"...,4096) = 41 brk(0x7411c0) = 0x7411c0 brk(0x742000) = 0x742000 access("/etc/ld.so.nohwcap",F_OK) = -1 ENOENT (No such file or directory) fstat(1,{st_mode=S_IFCHR|0620,st_rdev=makedev(136,4),...}) = 0 mmap(NULL,4096,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS,-1,0) = 0x7fbcc54d1000 write(1,"Hello from the wrapped readlink "...,36Hello from the wrapped readlink :з ) = 36 readlink("/usr/bin/gnome-www-browser","/etc/alternatives/gnome-www-brow"...,255) = 35 write(1,36/etc/alternatives/gnome-www-browser ) = 36 exit_group(36) = ? +++ exited with 36 +++ 如果我们仔细地注意到二进制文件,则第一个“未拦截的”调用readlink()(系统调用89,即0x59)来自这些行 – 一些链接器相关的代码部分(即_dl_get_origin)为其功能执行readlink().这些隐式的系统调用(虽然存在于二进制代码中)永远不会被我们的“包装”方法所吸引. 000000000051875c <_dl_get_origin>: 51875c: b8 59 00 00 00 mov $0x59,%eax 518761: 55 push %rbp 518762: 53 push %rbx 518763: 48 81 ec 00 10 00 00 sub $0x1000,%rsp 51876a: 48 89 e6 mov %rsp,%rsi 51876d: 0f 05 syscall 如何将包装思想扩展到readlink()等系统调用(包括所有被调用的隐式调用)? 解决方法
ld有一个包装选项,引用
from manual:
它也适用于系统调用.这是readlink的一个例子: #include <stdio.h> #include <string.h> #include <unistd.h> ssize_t __real_readlink(const char *path,char *buf,size_t bufsiz); ssize_t __wrap_readlink(const char *path,size_t bufsiz) { puts("Hello from the wrapped readlink :з"); __real_readlink(path,buf,bufsiz); } int main(void) { const char testLink[] = "/usr/bin/gnome-www-browser"; char buf[256]; memset(buf,sizeof(buf)); readlink(testLink,sizeof(buf)-1); puts(buf); } 要从编译器将选项传递给链接器,请使用-Wl选项: $gcc test.c -o a -Wl,--wrap=readlink $./a Hello from the wrapped readlink :з /etc/alternatives/gnome-www-browser 这个想法是__wrap_func是你的函数包装器. __real_func链接器将链接到实际函数func.并且每次对代码中的func的调用都将替换为__wrap_func. UPD:有人可能会注意到,静态编译的二进制文件会调用另一个未被截获的readlink.要理解原因,只需做一点实验 – 将代码编译到目标文件,并列出符号,如: $gcc test.c -c -o a.o -Wl,--wrap=readlink $nm a.o 0000000000000037 T main U memset U puts U readlink U __real_readlink U __stack_chk_fail 0000000000000000 T __wrap_readlink 这里有趣的是,你不会看到在进入main函数之前对strace一起看到的一堆函数的引用 – 例如uname(),brk(),access()等等.这是因为main函数不是二进制文件中调用的第一个代码. bit of research with 现在,让我们再做一个例子 – 覆盖_start函数: $cat test2.c #include <stdio.h> #include <unistd.h> void _start() { puts("Hello"); _exit(0); } $gcc test2.c -o a -nostartfiles $strace ./a execve("./a",["./a"],[/* 69 vars */]) = 0 brk(0) = 0x150c000 access("/etc/ld.so.nohwcap",F_OK) = -1 ENOENT (No such file or directory) mmap(NULL,8192,0) = 0x7f3ece55d000 access("/etc/ld.so.preload",R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache",O_RDONLY|O_CLOEXEC) = 3 fstat(3,{st_mode=S_IFREG|0644,st_size=177964,177964,PROT_READ,MAP_PRIVATE,3,0) = 0x7f3ece531000 close(3) = 0 access("/etc/ld.so.nohwcap",F_OK) = -1 ENOENT (No such file or directory) open("/lib/x86_64-linux-gnu/libc.so.6",O_RDONLY|O_CLOEXEC) = 3 read(3,"177ELF211 3 > |