库打桩技术,可以截获对共享库函数的调用。应用上可以如可以控制函数调用的输入输出值,以自己的逻辑替换函数调用等;
基本思想:创建一个与目标函数相同原型的包装函数,通过编译时函数实现的搜索机制、或链接时函数符号解析搜索的机制、或运行时动态链接库的加载机制,将自定义的包装函数替换目标函数。
系统环境:Ubuntu14.04
Gcc版本:gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.4)
#include
#include
int main()
{
size_t len = 10;
char * msg = calloc(1,len);
snprintf( msg,len,"%s","aloha");
printf("msg:%s\n",msg);
free(msg);
msg = 0;
return 0;
}
#define calloc(nmemb,size) newcalloc(nmemb,size)
#define free(ptr) newfree(ptr)
void *newcalloc(size_t nmemb, size_t size);
void newfree(void *ptr);
#include
#include
void *newcalloc(size_t nmemb, size_t size)
{
static int _newcalloc_calltime = 0;
++_newcalloc_calltime;
printf("calloc ptr(%d,%d) ,call times=%d\n",nmemb,size,_newcalloc_calltime);
void * ptr = calloc( nmemb,size);
printf("calloc return=%p\n",ptr);
return ptr;
}
void newfree(void *ptr)
{
static int _newfree_calltime = 0;
++_newfree_calltime;
free(ptr);
printf("free(%p),call times =%d\n",ptr,_newfree_calltime);
}
gcc -m32 -g -c newcalloc.c;
gcc -m32 -g -I. -o main main.c newcalloc.o;
./main
备注 :从测试情况看,这里不指定-I编译选项,也能按期望运行,即main.c中即使以尖括号方式include了,也是从当前目录先搜索。
calloc ptr(1,10) ,call times=1
calloc return=0x8734008
msg:aloha
free(0x8734008),call times =1
1、编译时打桩,需要能访问源代码,本质上就是先定义自己的包装函数,在包装函数中进行调用标准的目标方法,并生成可重定位的文件。再定义一个通过宏定义方式转换的头文件(名称同需要打桩的函数所在的头文件),gcc编译时优先从本地搜索该同名头文件而调用了包装函数;
2、这里的一个注意点,对于包装文件newcalloc.c的编译需要单独编译,不能与宏定义转换的头文件放在同一个目录下进行编译,否则会变成循环嵌套递归调用,最终导致程序异常。
#include
#include
int main()
{
size_t len = 10;
char * msg = calloc(1,len);
snprintf( msg,len,"%s","aloha");
printf("msg:%s\n",msg);
free(msg);
msg = 0;
return 0;
}
#include
//#include
void * __real_calloc(size_t nmemb, size_t size);
void __real_free(void *ptr);
void * __wrap_calloc(size_t nmemb, size_t size)
{
static int _newcalloc_calltime = 0;
++_newcalloc_calltime;
printf("calloc ptr(%d,%d) ,call times=%d\n",nmemb,size,_newcalloc_calltime);
void * ptr = __real_calloc( nmemb,size);
printf("calloc return=%p\n",ptr);
return ptr;
}
void __wrap_free(void *ptr)
{
static int _newfree_calltime = 0;
++_newfree_calltime;
__real_free(ptr);
printf("free(%p),call times =%d\n",ptr,_newfree_calltime);
}
gcc -m32 -g -c newcalloc.c;
gcc -m32 -g -c main.c;
gcc -m32 -Wl,–wrap,calloc -Wl,–wrap,free -o main main.o newcalloc.o;
./main
calloc ptr(1,10) ,call times=1
calloc return=0x840b008
msg:aloha
free(0x840b008),call times =1
1、编译时打桩,需要能访问可重定位的目标文件;
2、静态链接器支持使用–wrap f选项进行打桩,即把对符号f的引用解析成__wrap_f,并对符号__real_f的引用解析成f。
#include
#include
int main()
{
size_t len = 10;
char * msg = calloc(1,len);
snprintf( msg,len,"%s","aloha");
printf("msg:%s\n",msg);
free(msg);
msg = 0;
return 0;
}
#include
#include
#define __USE_GNU
#include
//#define _GNU_SOURCE
void * calloc(size_t nmemb, size_t size)
{
void * (*callocp)(size_t nmemb, size_t size);
char * error;
callocp = dlsym(RTLD_NEXT,"calloc");
if((error=dlerror())!=NULL)
{
printf("dlsym calloc error,msg:%s\n",error);
exit(1);
}
static int _newcalloc_calltime = 0;
++_newcalloc_calltime;
printf("calloc ptr(%d,%d) ,call times=%d\n",nmemb,size,_newcalloc_calltime);
void * ptr = callocp( nmemb,size);
printf("calloc return=%p\n",ptr);
return ptr;
}
void free(void *ptr)
{
void (*freep)(void *ptr);
char * error;
freep = dlsym(RTLD_NEXT,"free");
static int _newfree_calltime = 0;
++_newfree_calltime;
freep(ptr);
printf("free(%p),call times =%d\n",ptr,_newfree_calltime);
}
gcc -m32 -g -shared -fpic -o newcalloc.so newcalloc.c -ldl;
gcc -m32 -g -o main main.c;
LD_PRELOAD="./newcalloc.so" ./main
calloc ptr(1,10) ,call times=1
calloc return=0x8fb5008
msg:aloha
free(0x8fb5008),call times =1
1、此机制基于动态链接器的LD_RELOAD环境变量。当加载和执行一个程序,需要解析未定义的引用时,动态链接器(LD-LINUX.SO)会先搜索LD_PRELOAD库,如果没有搜索到 ,则再搜索其他库。
2、中宏定义_GNU_SOURCE不能工作,查看dlfcn.h文件,需要使用__USE_GNU(#include
《深入理解计算机系统》中文版,[美]Randal E.Bryant等著,2019.3,机械工业出版社