原英文地址:http://opensourceforu.com/2011/08/lets-hook-a-library-function/
如果你是一个开发者,并且希望能够改变库函数工作的方式,这篇文章将给你一个基本的方法教你如何使用有限的知识来对你的库函数进行实验。这边所有的都是C代码,并且使用Linux下的GCC进行测试。Wikipedia说过:"在电脑编程上,给函数加钩子这个操作包含了大量技术,这些技术是通过拦截软件之间的函数调用、传递的消息或者是事件的方式来修改或者增强一个操作系统的行为,应用或者其他的软件成分。用来完成这些操作的代码叫做”钩子“;
阻断一个库函数调用并调用你自己写的代码,这也叫做函数中断。
钩子有两个好处:
1.你不需要在库中查找这个函数的定义,就像libc库(glibc 是GNU C库,并且libc差不多有半个glibc这么大)。严肃的说,这是一个让人讨厌的工作(至少对于我来说收这样!).
2.你不需要重新编译这个库函数的源代码。
库函数和系统调用
请看以下两张图片说明当一个函数加上钩子后会怎么样。
现在让我们来看看库函数。下面这个简单的prog1.c 作用是从堆分配了10个字节的内存并将它释放。
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
int main(void)
{
int *p;
printf("calling from main...\n");
p=(int *)malloc(10);
if(!p)
{
printf("Got allocation error...\n");
exit(1);
}
printf("returning to main...\n");
free(p); /* freeing memory from heap */
printf("freeing memory...\n");
return 0;
}
[root@workbenchsvr malloc_hook]# ./prog1
calling from main...
returning to main...
freeing memory...
[root@workbenchsvr malloc_hook]#
The next program, called prog2.c, is a simple hook for the malloc() function:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h> /* header required for dlsym() */
/* lcheck() is for memory leak check; its code is not shown
here */
void lcheck(void);
void* malloc(size_t size)
{
static void* (*my_malloc)(size_t) = NULL;
printf("inside shared object...\n");
if (!my_malloc)
my_malloc = dlsym(RTLD_NEXT, "malloc"); /* returns the object reference for malloc */
void *p = my_malloc(size); /* call malloc() using function pointer my_malloc */
printf("malloc(%d) = %p\n", size, p);
lcheck(); /* calling do_your_stuff function */
printf("returning from shared object...\n");
return p;
}
void lcheck(void)
{
printf("displaying memory leaks...\n");
/* do required stuff here */
}
|
|
编译并且运行,结果如下:
[root@workbenchsvr malloc_hook]# gcc -shared -ldl -fPIC prog2.c -o libprog2.so
[root@workbenchsvr malloc_hook]# LD_PRELOAD=/home/dibyendu/malloc_hook/libprog2.so ./prog1
calling from main...
inside shared object...
malloc(10) = 0x8191008
displaying memory leaks...
returning from shared object...
returning to main...
freeing memory...
[root@workbenchsvr malloc_hook]
所以让我们仔细看一下这个钩子函数。dlysm()函数有两个参数,第一个是一个句柄由dlopen()函数返回。这里我们必须使用RTLD_NEXT来进行函数终端。
这告诉动态编译器找到这个特别函数的声明,不是那个叫做dlysm(). 第二个参数是一个标志名(这边是叫做malloc),作为一个string的对象。dlysm()返回这个标志的地址作为函数的第二个参数,进行编译的时候,FPIC来创建一个位置无关的对象。
LD_PRELOAD 可变的环境开始给loader一系列的库来装载。我们对它的使用将会把libprog2.so一起装载并且和prog1一起动态链接.不要忘记在LD_PRELOAD添加绝对路径。并且当你在用GNU C库时候想使用某个扩展名,因为不一样的GNU C环境下的扩展名可能不一样,最好添加#define来增加可行性。
Can we hook every function with dlsym()?
当我们如果试图重写 dlysm()这个函数本身的话会怎么办,或者重写任何一个函数当中调用了dlsym().所以,有没有什么方法来阻断dlsym().? ,有,但是你不能使用相同的钩子步骤,看看下面的程序结果吧。首先,从file1.c 和file2.c 创建 一个共享的文件libfile.so 。然后通过 gcc -rdynamic -o dl_prog1 dl_prog1.c -ldl.方式编译。结果很明显。
/* file1.c */
void
file1(
int
*i)
{
*i=100;
}
/* file2.c */
void
file2(
int
*i)
{
*i=200;
}
|
接下来的d1_prog1.c是一个简单的项目来显示dlopen()和dlsym()函数的作用。函数file1()和函数file2()在file1.c和file2.c中被定义。
#include<stdio.h>
#include<dlfcn.h>
#include<stdlib.h>
void file1(int *i);
void file2(int *i);
int main(void)
{
void *handler;
int (*fn) (int *);
int x;
char *error;
handler = dlopen("/home/dibyendu/dlsym_hook/libfile.so", RTLD_LAZY);
if (!handler)
{
fprintf(stderr,"%s\n", dlerror());
exit(1);
}
fn = dlsym(handler,"file1"); /* getting the handle of file1 through dlsym() */
if ((error = dlerror()) != NULL) /* checking error through dlerror() */
{
fprintf(stderr,"%s\n", error);
exit(1);
}
(*fn)(&x); /* Calling file1() to resolve x */
printf("The value of x is %d\n", x);
dlclose(handler); /* closing the file handle */
return 0;
}
[root@workbenchsvr dlsym_hook]# gcc -shared -ldl -fPIC file1.c file2.c -o libfile.so
[root@workbenchsvr dlsym_hook]# gcc -rdynamic -o dl_prog1 dl_prog1.c -ldl
[root@workbenchsvr dlsym_hook]# ./dl_prog1
The value of x is 100
[root@workbenchsvr dlsym_hook]#
现在我们尝试给dlsym()函数添加钩子,并且你会有段错误,因为递归调用了dlysm()函数本身,接下来的d1_prog2.c将会是一个递归调用dlysm()本身,它会引起栈溢出和段错误。
|
|
Output:
[root@workbenchsvr dlsym_hook]# gcc -shared -ldl -fPIC dl_prog2.c -o libdl_prog2.so
[root@workbenchsvr dlsym_hook]# LD_PRELOAD=/home/dibyendu/dlsym_hook/libdl_prog2.so ./dl_prog1
inside shared object::before dlsym()...
…...............................................................
inside shared object::before dlsym()...
Segmentation fault
[root@workbenchsvr dlsym_hook]#
#define __USE_GNU
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
extern void *__libc_dlsym (void *, const char *);
void *dlsym(void *handle, const char *symbol)
{
printf("Ha Ha...dlsym() Hooked\n");
void* result = __libc_dlsym(handle, symbol); /* now, this will call dlsym() library function */
return result;
}
Output:
[root@workbenchsvr dlsym_hook]# gcc -shared -ldl -fPIC dl_prog3.c -o libdl_prog3.so
[root@workbenchsvr dlsym_hook]# LD_PRELOAD=/home/dibyendu/dlsym_hook/libdl_prog3.so ./dl_prog1
Ha Ha...dlsym() Hooked
The value of x is 100
[root@workbenchsvr dlsym_hook]#