如何覆盖 A C 系统调用?
How to Override A C System Call?
所以问题如下。项目需要拦截所有文件IO
操作,例如 open()
和 close()
。我试图在调用相应的 open()
或 close()
之前添加 printf()
。例如,我不应该通过将 open()
或 close()
更改为 myOpen()
或 myClose()
来重写源代码。我一直在尝试使用 LD_PRELOAD
环境变量。但是无限循环的问题出现了。我的问题是这样的 one.
int open(char * path,int flags,int mode)
{
// print file name
printf("open :%s\n",path);
return __open(path,flags,mode);
}
是的,你想要LD_PRELOAD
。
您需要创建一个共享库 (.so
),其中包含您要拦截的所有函数的代码。并且,您想设置 LD_PRELOAD
以使用该共享库
这里是 open
函数的一些示例代码。您需要为每个要拦截的函数做类似的事情:
#define _GNU_SOURCE
#include <dlfcn.h>
int
open(const char *file,int flags,int mode)
{
static int (*real_open)(const char *file,int flags,int mode) = NULL;
int fd;
if (real_open == NULL)
real_open = dlsym(RTLD_NEXT,"open");
// do whatever special stuff ...
fd = real_open(file,flags,mode);
// do whatever special stuff ...
return fd;
}
我认为 RTLD_NEXT
是最简单的并且可能就足够了。否则,您可以添加一个在 libc
上执行 dlopen
once 的构造函数
更新:
I am not familiar with C and I got the following problems with gcc. "error: 'NULL' undeclared (first use in this function)",
这是由几个 #include
文件定义的,所以尝试 #include <stdio.h>
。如果你想打电话给 printf
.
,你将需要它
"error: 'RTLD_NEXT' undeclared (first use in this function)",
这是通过 #include <dlfcn.h>
[如我的示例所示]
定义的
and "symbol lookup error: ./hack_Whosebug.so: undefined symbol: dlsym".
来自 man dlsym
,它说:Link 和 -ldl
所以,将 -ldl
添加到构建您的行.so
.
另外,如果 "special stuff" 做了一些在你的拦截函数上循环的事情,你必须小心防止无限递归。
值得注意的是,您想打电话给 printf
。如果拦截 write
系统调用,可能会发生不好的事情。
因此,您需要跟踪您何时已经处于拦截功能之一,如果已经处于其中,不要做任何特殊的事情。请参阅 in_self
变量。
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
ssize_t
write(int fd,const void *buf,size_t len)
{
static ssize_t (*real_write)(int fd,const void *buf,size_t len) = NULL;
static int in_self = 0;
ssize_t err;
if (real_write == NULL)
real_write = dlsym(RTLD_NEXT,"write");
++in_self;
if (in_self == 1)
printf("mywrite: fd=%d buf=%p len=%ld\n",fd,buf,len);
err = real_write(fd,buf,len);
if (in_self == 1)
printf("mywrite: fd=%d buf=%p err=%ld\n",fd,buf,err);
--in_self;
return err;
}
以上对于单线程 programs/environments 没问题,但是如果你拦截任意线程,它 可能是 多线程.
因此,我们必须在 构造函数 中初始化所有 real_*
指针。这是一个具有特殊属性的函数,告诉动态加载器尽快自动调用该函数。
而且,我们必须将 in_self
放入 线程本地存储 。我们通过添加 __thread
属性来做到这一点。
对于多线程版本,您可能需要 link 和 -lpthread
以及 -ldl
。
编辑: 我们还必须保留正确的 errno
值
综合起来:
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <errno.h>
static int (*real_open)(const char *file,int flags,int mode) = NULL;
static ssize_t (*real_write)(int fd,const void *buf,size_t len) = NULL;
__attribute__((constructor))
void
my_lib_init(void)
{
real_open = dlsym(RTLD_NEXT,"open");
real_write = dlsym(RTLD_NEXT,"write");
}
int
open(const char *file,int flags,int mode)
{
int fd;
// do whatever special stuff ...
fd = real_open(file,flags,mode);
// do whatever special stuff ...
return fd;
}
ssize_t
write(int fd,const void *buf,size_t len)
{
static int __thread in_self = 0;
int sverr;
ssize_t ret;
++in_self;
if (in_self == 1)
printf("mywrite: fd=%d buf=%p len=%ld\n",fd,buf,len);
ret = real_write(fd,buf,len);
// preserve errno value for actual syscall -- otherwise, errno may
// be set by the following printf and _caller_ will get the _wrong_
// errno value
sverr = errno;
if (in_self == 1)
printf("mywrite: fd=%d buf=%p ret=%ld\n",fd,buf,ret);
--in_self;
// restore correct errno value for write syscall
errno = sverr;
return ret;
}
所以问题如下。项目需要拦截所有文件IO
操作,例如 open()
和 close()
。我试图在调用相应的 open()
或 close()
之前添加 printf()
。例如,我不应该通过将 open()
或 close()
更改为 myOpen()
或 myClose()
来重写源代码。我一直在尝试使用 LD_PRELOAD
环境变量。但是无限循环的问题出现了。我的问题是这样的 one.
int open(char * path,int flags,int mode)
{
// print file name
printf("open :%s\n",path);
return __open(path,flags,mode);
}
是的,你想要LD_PRELOAD
。
您需要创建一个共享库 (.so
),其中包含您要拦截的所有函数的代码。并且,您想设置 LD_PRELOAD
以使用该共享库
这里是 open
函数的一些示例代码。您需要为每个要拦截的函数做类似的事情:
#define _GNU_SOURCE
#include <dlfcn.h>
int
open(const char *file,int flags,int mode)
{
static int (*real_open)(const char *file,int flags,int mode) = NULL;
int fd;
if (real_open == NULL)
real_open = dlsym(RTLD_NEXT,"open");
// do whatever special stuff ...
fd = real_open(file,flags,mode);
// do whatever special stuff ...
return fd;
}
我认为 RTLD_NEXT
是最简单的并且可能就足够了。否则,您可以添加一个在 libc
dlopen
once 的构造函数
更新:
I am not familiar with C and I got the following problems with gcc. "error: 'NULL' undeclared (first use in this function)",
这是由几个 #include
文件定义的,所以尝试 #include <stdio.h>
。如果你想打电话给 printf
.
"error: 'RTLD_NEXT' undeclared (first use in this function)",
这是通过 #include <dlfcn.h>
[如我的示例所示]
and "symbol lookup error: ./hack_Whosebug.so: undefined symbol: dlsym".
来自 man dlsym
,它说:Link 和 -ldl
所以,将 -ldl
添加到构建您的行.so
.
另外,如果 "special stuff" 做了一些在你的拦截函数上循环的事情,你必须小心防止无限递归。
值得注意的是,您想打电话给 printf
。如果拦截 write
系统调用,可能会发生不好的事情。
因此,您需要跟踪您何时已经处于拦截功能之一,如果已经处于其中,不要做任何特殊的事情。请参阅 in_self
变量。
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
ssize_t
write(int fd,const void *buf,size_t len)
{
static ssize_t (*real_write)(int fd,const void *buf,size_t len) = NULL;
static int in_self = 0;
ssize_t err;
if (real_write == NULL)
real_write = dlsym(RTLD_NEXT,"write");
++in_self;
if (in_self == 1)
printf("mywrite: fd=%d buf=%p len=%ld\n",fd,buf,len);
err = real_write(fd,buf,len);
if (in_self == 1)
printf("mywrite: fd=%d buf=%p err=%ld\n",fd,buf,err);
--in_self;
return err;
}
以上对于单线程 programs/environments 没问题,但是如果你拦截任意线程,它 可能是 多线程.
因此,我们必须在 构造函数 中初始化所有 real_*
指针。这是一个具有特殊属性的函数,告诉动态加载器尽快自动调用该函数。
而且,我们必须将 in_self
放入 线程本地存储 。我们通过添加 __thread
属性来做到这一点。
对于多线程版本,您可能需要 link 和 -lpthread
以及 -ldl
。
编辑: 我们还必须保留正确的 errno
值
综合起来:
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <errno.h>
static int (*real_open)(const char *file,int flags,int mode) = NULL;
static ssize_t (*real_write)(int fd,const void *buf,size_t len) = NULL;
__attribute__((constructor))
void
my_lib_init(void)
{
real_open = dlsym(RTLD_NEXT,"open");
real_write = dlsym(RTLD_NEXT,"write");
}
int
open(const char *file,int flags,int mode)
{
int fd;
// do whatever special stuff ...
fd = real_open(file,flags,mode);
// do whatever special stuff ...
return fd;
}
ssize_t
write(int fd,const void *buf,size_t len)
{
static int __thread in_self = 0;
int sverr;
ssize_t ret;
++in_self;
if (in_self == 1)
printf("mywrite: fd=%d buf=%p len=%ld\n",fd,buf,len);
ret = real_write(fd,buf,len);
// preserve errno value for actual syscall -- otherwise, errno may
// be set by the following printf and _caller_ will get the _wrong_
// errno value
sverr = errno;
if (in_self == 1)
printf("mywrite: fd=%d buf=%p ret=%ld\n",fd,buf,ret);
--in_self;
// restore correct errno value for write syscall
errno = sverr;
return ret;
}