Linux C 库打桩技术

文章目录

  • 1.前言
  • 2. 测试环境
  • 3.编译时打桩
    • 3.1.相关文件
      • 3.1.1.main.c
      • 3.1.2.newcalloc.h
      • 3.1.3.newcalloc.c
    • 3.2.编译命令
    • 3.3.运行情况和运行命令
    • 3.4.说明
  • 4.编译时打桩
    • 4.1.相关文件
      • 4.1.1.main.c
      • 4.1.2.newcalloc.c
    • 4.2.编译命令和运行命令
    • 4.3.运行情况
    • 4.4.说明
  • 5. 运行时打桩
    • 5.1.相关文件
      • 5.1.1.main.c
      • 5.1.2.newcalloc.c
    • 5.2.编译命令和运行命令
    • 5.3.运行情况
    • 5.4.说明
  • 6. 参考资料

1.前言

库打桩技术,可以截获对共享库函数的调用。应用上可以如可以控制函数调用的输入输出值,以自己的逻辑替换函数调用等;
基本思想:创建一个与目标函数相同原型的包装函数,通过编译时函数实现的搜索机制、或链接时函数符号解析搜索的机制、或运行时动态链接库的加载机制,将自定义的包装函数替换目标函数。

2. 测试环境

系统环境:Ubuntu14.04
Gcc版本:gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.4)

3.编译时打桩

3.1.相关文件

3.1.1.main.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;
}

3.1.2.newcalloc.h

#define  calloc(nmemb,size) newcalloc(nmemb,size)
#define free(ptr)   newfree(ptr)

void *newcalloc(size_t nmemb, size_t size);
void newfree(void *ptr);

3.1.3.newcalloc.c

#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);
 } 

3.2.编译命令

gcc -m32 -g -c newcalloc.c;
gcc -m32 -g -I. -o main main.c newcalloc.o;
./main
备注 :从测试情况看,这里不指定-I编译选项,也能按期望运行,即main.c中即使以尖括号方式include了,也是从当前目录先搜索。

3.3.运行情况和运行命令

calloc ptr(1,10) ,call times=1
calloc return=0x8734008
msg:aloha
free(0x8734008),call times =1
在这里插入图片描述

3.4.说明

1、编译时打桩,需要能访问源代码,本质上就是先定义自己的包装函数,在包装函数中进行调用标准的目标方法,并生成可重定位的文件。再定义一个通过宏定义方式转换的头文件(名称同需要打桩的函数所在的头文件),gcc编译时优先从本地搜索该同名头文件而调用了包装函数;
2、这里的一个注意点,对于包装文件newcalloc.c的编译需要单独编译,不能与宏定义转换的头文件放在同一个目录下进行编译,否则会变成循环嵌套递归调用,最终导致程序异常。

4.编译时打桩

4.1.相关文件

4.1.1.main.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;
}

4.1.2.newcalloc.c

#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);
 } 

4.2.编译命令和运行命令

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

4.3.运行情况

calloc ptr(1,10) ,call times=1
calloc return=0x840b008
msg:aloha
free(0x840b008),call times =1
在这里插入图片描述

4.4.说明

1、编译时打桩,需要能访问可重定位的目标文件;
2、静态链接器支持使用–wrap f选项进行打桩,即把对符号f的引用解析成__wrap_f,并对符号__real_f的引用解析成f。

5. 运行时打桩

5.1.相关文件

5.1.1.main.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;
}

5.1.2.newcalloc.c

#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);
 } 

5.2.编译命令和运行命令

gcc -m32 -g -shared -fpic -o newcalloc.so newcalloc.c -ldl;
gcc -m32 -g -o main main.c;
LD_PRELOAD="./newcalloc.so" ./main

5.3.运行情况

calloc ptr(1,10) ,call times=1
calloc return=0x8fb5008
msg:aloha
free(0x8fb5008),call times =1
在这里插入图片描述

5.4.说明

1、此机制基于动态链接器的LD_RELOAD环境变量。当加载和执行一个程序,需要解析未定义的引用时,动态链接器(LD-LINUX.SO)会先搜索LD_PRELOAD库,如果没有搜索到 ,则再搜索其他库。
2、中宏定义_GNU_SOURCE不能工作,查看dlfcn.h文件,需要使用__USE_GNU(#include 前面定义)
Linux C 库打桩技术_第1张图片

6. 参考资料

《深入理解计算机系统》中文版,[美]Randal E.Bryant等著,2019.3,机械工业出版社

你可能感兴趣的:(linux,C/C++)