如何覆盖C系统调用? [英] How to Override A C System Call?
问题描述
所以问题出在下面.项目需要拦截所有文件IO
操作,例如open()
和close()
.我正在尝试在调用相应的open()
或close()
之前添加printf()
.我不应该通过将open()
或close()
更改为myOpen()
或myClose()
来重写源代码.我一直在尝试使用LD_PRELOAD
环境变量.但是出现了无限循环问题.我的问题是这样的一个. /p>
So the problem is the following. The project needs to intercept all file IO
operations, like open()
and close()
. I am trying to add printf()
before calling the corresponding open()
or close()
. I am not supposed to rewrite the source code by changing open()
or close()
to myOpen()
or myClose()
for example. I have been trying to use LD_PRELOAD
environment variable. But the indefinite loop problem came up. My problem is like this 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
以使用该共享库
You need to create a shared library (.so
) that has code for all functions that you want to intercept. And, you want to set LD_PRELOAD
to use that shared library
这是open
函数的一些示例代码.您需要对要拦截的每个函数执行类似的操作:
Here is some sample code for the open
function. You'll need to do something similar for each function you want to intercept:
#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
I believe RTLD_NEXT
is easiest and may be sufficient. Otherwise, you could add a constructor that does dlopen
once on libc
更新:
我不熟悉C,并且gcc遇到以下问题. 错误:未声明'NULL'(此功能首次使用)",
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
,则需要此.
This is defined by several #include
files, so try #include <stdio.h>
. You'll need that if you want to call printf
.
错误:未声明'RTLD_NEXT'(此功能首次使用)",
"error: 'RTLD_NEXT' undeclared (first use in this function)",
这是通过执行#include <dlfcn.h>
来定义的(如我的示例所示)
That is defined by doing #include <dlfcn.h>
[as shown in my example]
和符号查找错误:./hack_stackoverflow.so:未定义的符号:dlsym".
and "symbol lookup error: ./hack_stackoverflow.so: undefined symbol: dlsym".
在man dlsym
中表示:与-ldl
链接,因此,将-ldl
添加到构建.so
的行中.
From man dlsym
, it says: Link with -ldl
So, add -ldl
to the line that builds your .so
.
此外,如果特殊内容"所做的事情在您的拦截函数上循环返回,则必须小心以防止无限递归.
Also, you have to be careful to prevent infinite recursion if the "special stuff" does something that loops back on your intercept function.
值得注意的是,您想致电printf
.如果您拦截write
系统调用,则可能会发生不良情况.
Notably, you want to call printf
. If you intercept the write
syscall, bad things may happen.
因此,您需要跟踪何时已进入某个拦截函数中,并且不(如果已存在)进行一些特殊操作.请参见in_self
变量.
So, you need to keep track of when you're already in one of your intercept functions and not do anything special if already there. See the in_self
variable.
#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;
}
上面的方法对于单线程程序/环境还可以,但是如果您拦截任意一个,则 it 可能是 multithreaded .
The above works okay for single threaded programs/environments, but if you're intercepting an arbitrary one, it could be multithreaded.
因此,我们必须在构造函数中初始化所有real_*
指针.这是一个具有特殊属性的函数,它告诉动态加载程序自动调用该函数.
So, we'd have to initialize all the real_*
pointers in a constructor. This is a function with a special attribute that tells the dynamic loader to call the function ASAP automatically.
而且,我们必须将in_self
放入线程本地存储.为此,我们添加了__thread
属性.
And, we have to put in_self
into thread local storage. We do this by adding the __thread
attribute.
对于多线程版本,您可能需要链接-lpthread
和-ldl
.
You may need to link with -lpthread
as well as -ldl
for the multithreaded version.
编辑:我们还必须保留正确的errno
值
We also have to preserve the correct errno
value
将它们放在一起:
#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;
}
这篇关于如何覆盖C系统调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!