让我们hook一个库函数
来源:互联网 发布:阿里云空间购买流程 编辑:程序博客网 时间:2024/05/22 12:26
让我们hook一个库函数
原文地址:http://opensourceforu.com/2011/08/lets-hook-a-library-function/
说明:
Hook中文翻译为钩子,可以用来截获调用函数,并改变函数的行为。Windows和Linux都提供了相应的实现机制。这篇文章是针对Linux平台的。也是在学习协程库libco过程中接触到的。
正文:
如果你是一个开发者,并期望去改变一个库函数的行为,那么这篇文章将带你入门——只是用库函数做实验。所有的代码是用C写的,在Linux上面使用GCC编译测试。
根据维基百科,“在计算机程序中,hook包含一系列的技术,通过在软件中截获系统调用、消息或者事件,改变或者增强操作系统、应用程序或者其他软件模块的行为。那些处理这些拦截系统调用,事件或者消息的代码就称为hook。”
截获一个系统调用,或者调用你自己的外壳代码,也称为函数介入。
Hook有两个好处:
•你不需要到像libc(glibc是GNU的C库,libc差不多是glibc的一半大小)一样的库里查找函数的定义,并改变它。严肃地说,这是非常下流的技术(至少对我而言!)
•你不需要重新编译库的源码。
库函数和系统调用
图1和图2使用图表的形式展现了一个库函数hook之后的样子。
图1:库函数
图2:使用hook之后的库函数
现在让我们来hook一个库函数。下面的prog.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]# gcc -o prog1 prog1.c[root@workbenchsvr malloc_hook]# ./prog1calling from main...returning to main...freeing memory...[root@workbenchsvr malloc_hook]#
下面一个程序,prog2.c,是一个简单的malloc()函数的hook:
#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 ./prog1calling from main...inside shared object...malloc(10) = 0x8191008displaying memory leaks...returning from shared object...returning to main...freeing memory...[root@workbenchsvr malloc_hook]#
现在让我们近距离看看第一个hook程序。dlsym()函数接受两个参数:第一个是调用dlopen()返回的句柄。这里我们必须使用RTLD_NEXT。
这告诉动态链接器查找指定函数的下一个版本,而不是调用dlsym()的那个[注:这里指的是查找真正的malloc函数]。第二个参数是一个字符串类型的标识名(本例中就是malloc)。dlsym()返回第二个参数执行标识的地址。编译的时候,使用fPIC产生一个位置无关的对象。
LD_PRELOAD环境变量提供给加载器一系列需要加载的函数,在加载其他东西之前。我们只用它加载libprog2.so,并在prog1程序中动态链接。不要忘记在LD_PRELOAD中为.so提供绝对路径。当然,如果你想使用GNU C库中的某些扩展功能,需要包含_GNU_SOURCE[注:这里如果不包含,则找不到RTLD_NEXT的定义],因为这些扩展在其他非GNU的系统上可能不能用,加入这个#define有利于移植。
我们能使用dlsym()去hook所有函数吗?
如果我们想为dlsym()本身加壳,或者为那些内部调用了dlsym()函数的库函数加壳,上面的hook方法将不可用。所以,有没有干预dlsym()的函数?有的,但你不能使用相同的hook过程———如果你想试试,请看一下输出结果。首先,使用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;}
接下来的dl_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_prog1The value of x is 100[root@workbenchsvr dlsym_hook]#
现在试图hook函数dlsmn(),你将因为循环调用而得到一个段错误(dlsym()将调用自己)。下面的dl_prog2.c将导致dlsym()循环调用自己,结果是内存泄露和段错误:
#define _GNU_SOURCE#include <stdio.h>#include <stdint.h>#include <dlfcn.h> void *dlsym(void *handle, const char *name){ void *(*dlsym_fn)(void *, const char *)=NULL; printf("inside shared object::before dlsym()...\n"); dlsym_fn=dlsym(RTLD_NEXT, "dlsym"); /* this will call itself again and again */ printf("inside shared object::after dlsym()...\n"); return (*dlsym_fn)(handle, name);}
输出:
[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_prog1inside shared object::before dlsym()...…...............................................................[注:这里一直在循环。。。]inside shared object::before dlsym()...Segmentation fault[root@workbenchsvr dlsym_hook]#
下面的dl_prog3.c程序成功地干预了dlsym():
#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;}
输出:
[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_prog1Ha Ha...dlsym() HookedThe value of x is 100[root@workbenchsvr dlsym_hook]#
我们还能做什么?
我已经hook了像getaddrinfo(),open()等函数,所以你能够干预任何函数。但这里有一些限制:
•当心那些自己调用了dlsym()的函数,这时候你需要__libc_dlsym(handle,symbol)技术
•确保SUID位没有设置,否则你不能使用LD_PRELOAD。
•而且,内部库函数调用时再运行时之前确定的—也是就说,如果在libc中的一些函数调用了getaddrinfo()或者malloc(),它将不会在不同的库中调用hook。
参考
•Linux中的函数介入[http://jayconrod.com/posts/23/tutorial-function-interposition-in-linux]
•Linux的man pages:dlopen(), dlsym(), dlerror(), dlclose()
- 让我们hook一个库函数
- Linux 下Hook一个共享库函数
- Linux下Hook一个共享库函数
- Android Hook程序,对库函数进行HOOK
- 一种hook libc库函数的简易方案
- 如何Hook一个函数
- 写一个SSDTShadow Hook
- HOOK一个C函数
- 让我们自己编写一个操作系统(OS)(一)
- 谁让我们无功而返(一个案例分析)
- 让我们致力于一个LLVM超级优化器
- 让我们考虑一个地图引擎系统。。。
- Let’s Hook a Library Function(给库函数设置钩子)
- 一天一个库函数-for C
- HOOK API的一个类
- 一个 Hook Api 的源代码
- 一个简单的hook 新手入门
- netfilter:开发一个hook函数
- matlab
- Mybatis学习笔记(七)【输入映射】
- HDU
- linux 系统下zookeeper的安装和配置
- Mybatis学习笔记(八)【输出映射】
- 让我们hook一个库函数
- Unity Editor编辑器(MenuItem)
- 授权验证,验证码工作方式
- 使用xml作为数据库的配置文件的路径读取问题
- 闭包的应用
- Java中Jackson使用汇总
- Mybatis学习笔记(九)【动态sql】
- 【人工智能】第三章 通过搜索进行问题求解
- pandas apply vs agg vs transform