[toc]
概述
搞一個包裝,把c庫函包起來,同時順帶加上自己的私貨。
抄的如下鏈接,作者總結(jié)的挺好,不過代碼有編譯錯誤等問題。這里修正一下,所有的代碼都能直接跑
https://blog.csdn.net/liangzc1124/article/details/104974649
編譯時打樁
直接自己寫個庫函數(shù)調(diào)用,加上私貨包起來,再用宏把庫函數(shù)里面的實(shí)現(xiàn),給替換成自己的實(shí)現(xiàn)
建立包裝函數(shù)
建立mymalloc.c文件,定義需要的包裝函數(shù)mymalloc和myfree.
#ifdef COMPILETIME
#include <stdio.h>
#include <malloc.h>
void *mymalloc(size_t size)
{
void *ptr = malloc(size);
printf("malloc(%d)=%p\n", size, ptr);
return ptr;
}
void myfree(void *ptr)
{
free(ptr);
printf("free(%p)\n", ptr);
}
#endif
建立頭文件 malloc.h
該文件向預(yù)處理器指明用mymalloc.c中的包裝函數(shù)替換庫里的目標(biāo)函數(shù),需要在gcc編譯的時候,讓編譯器優(yōu)先去用這個 .h
#define malloc(size) mymalloc(size)
#define free(ptr) myfree(ptr)
void *mymalloc(size_t size);
void myfree(void *ptr);
建立自己的程序文件
建立文件myprog.c,并在其中正常調(diào)用malloc函數(shù)
#include <stdio.h>
#include "mymalloc.h"
int main(void)
{
int *p = malloc(32);
free(p);
return 0;
}
編譯鏈接
gcc -c -DCOMPILETIME mymalloc.c
gcc -I. mymalloc.o -o myprog myprog.c
-I.:指示C預(yù)處理器在搜索通常的系統(tǒng)目錄前,先在當(dāng)前目錄中查找malloc.h
鏈接時打樁
wrap是一種非常常見的打樁方法
-Wl,option:將option傳遞給鏈接器。
option中的每個逗號都要用空格來替換
所以-Wl,--wrap,malloc意味著把--wrap malloc傳遞給鏈接器。
當(dāng)鏈接器看到 --warp ABC (ABC代表任何字符串)選項的時候,會去所有的文件下去尋找 ABC,然后把 ABC 換成 __warp_ABC
之后把所有的 __real_ABC 換成正常的 ABC
包裝函數(shù)
創(chuàng)建mymalloc.c文件,定義包裝函數(shù)
#include <stdio.h>
void *__real_malloc(size_t size);
void __real_free(void *ptr);
//定義malloc 包裝函數(shù)
void *__wrap_malloc(size_t size)
{
void *ptr = __real_malloc(size); //調(diào)用標(biāo)準(zhǔn)庫里的malloc
printf("malloc(%d) = %p\n", (int)size, ptr);
return ptr;
}
//定義free 包裝函數(shù)
void __wrap_free(void *ptr)
{
__real_free(ptr); //調(diào)用標(biāo)準(zhǔn)庫里的free
printf("free(%p) = %p\n", ptr);
}
建立程序
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *p = malloc(32);
free(p);
return 0;
}
編譯鏈接
gcc -c mymalloc.c
gcc -c myprog.c
gcc -Wl,--wrap,malloc -Wl,--wrap,free -o myprog myprog.o mymalloc.o
或者直接
gcc -Wl,--wrap,malloc -Wl,--wrap,free -o myprog myprog.c mymalloc.c
運(yùn)行時打樁
編譯時打樁需要訪問程序的源代碼,而鏈接時需要訪問可重定位目標(biāo)文件。那有沒有一種辦法讓僅僅訪問可執(zhí)行目標(biāo)就能達(dá)到同樣的目的呢?我們可以利用基于動態(tài)鏈接器的LD_PRELOAD環(huán)境變量來實(shí)現(xiàn)。
當(dāng)你將LD_PRELOAD環(huán)境變量設(shè)置為一個共享路徑名的列表(以空格或分號分開),那么在運(yùn)行一個程序時,動態(tài)鏈接器(LD-LINUX.SO)會先搜索列表里的庫,然后才搜素系統(tǒng)其它庫。
利用這個原理,你可以對任何共享庫中的任何函數(shù)打樁,包括libc.so。
建立包裝函數(shù)文件
下面建立mymalloc.c文件,其中定義了malloc和free的包裝函數(shù)。每個包裝函數(shù)中,利用dlsym調(diào)用libc中的標(biāo)準(zhǔn)函數(shù)。
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
//定義malloc 包裝函數(shù)
void *malloc(size_t size)
{
void *(*mallocp)(size_t size);
char *error;
//獲得libc中malloc函數(shù)的地址
mallocp = dlsym(RTLD_NEXT, "malloc");
if((error = dlerror()) != NULL){
fputs(error, stderr);
exit(1);
}
//利用函數(shù)指針間接調(diào)用libc中的malloc函數(shù)
char *ptr = mallocp(size);
printf("malloc()");
return ptr;
}
//定義free 包裝函數(shù)
void free(void *ptr)
{
void (*freep)(void *) = NULL;
char *error;
if(!ptr) {
return;
}
//獲得libc中free函數(shù)的地址
freep = dlsym(RTLD_NEXT, "free");
if((error = dlerror()) != NULL){
fputs(error, stderr);
exit(1);
}
freep(ptr); //利用函數(shù)指針間接調(diào)用libc中的free函數(shù)
printf("free(%p)\n", ptr);
}
建立自己的程序文件
建立文件myprog.c,并在其中正常調(diào)用malloc函數(shù).
#include <stdio.h>
#include <malloc.h>
int main(void)
{
int *p = malloc(32);
free(p);
return 0;
}
編譯包含包裝函數(shù)的共享庫
gcc -shared -fpic -o mymalloc.so mymalloc.c -ldl
-fpic:(position independent code)指示生成位置無關(guān)的目標(biāo)文件;編譯共享庫時該參數(shù)為必選!
-shared:指示編譯器生成一個共享目標(biāo)文件。
-ldl:指示鏈接器鏈接到 libdl.so 共享庫。
編譯與運(yùn)行主程序
gcc -o myprog myprog.c
編譯時打樁,必須要能訪問程序的源代碼,然后重新編譯
鏈接時打樁,必須要能訪問程序的可重定位文件.o,然后重新鏈接。
基于動態(tài)鏈接器的 LD_PRELOAD 環(huán)境變量,我們能用這個機(jī)制,只訪問可執(zhí)行文件.out就可以庫打樁
如果 LD_PRELOAD 環(huán)境變量被設(shè)置為一個共享庫路徑名的列表(空格或者分號分隔),那么當(dāng)你加載和執(zhí)行一個程序,需要解析未定義的引用時,動態(tài)鏈接器會先搜索 LD_PRELOAD庫,然后才搜索任何其他的庫。
通過這個機(jī)制,當(dāng)我們加載和執(zhí)行可執(zhí)行文件時,可以對任何共享庫中的任何函數(shù)打樁,包括libc.so
LD_PRELOAD環(huán)境變量并且運(yùn)行
LD_PRELOAD="./mymalloc.so" ./myprog
以為這就完事了么,如果按照上面的代碼來操作,最后一步一定會coredump
而且會正好掛在 malloc 的 printf("malloc")那一行
原因:printf 會調(diào)用 printf malloc,當(dāng)場進(jìn)行一個死循環(huán)
另外提個問題:為什么wrap鏈接的時候,我的程序就能正常運(yùn)行,而不是coredump,但是動態(tài)鏈接的時候就會coredump呢?
因?yàn)殒溄拥臅r候,wrap替換是看不到printf內(nèi)部的,這個時候libc參與鏈接,只是把printf這個符號扔給了程序,想看到內(nèi)部,必須等到程序運(yùn)行的時候,程序自己看到了printf,主動找libc.so獲取內(nèi)容的時候才行。所以wrap并不會去替換libc.so里面的東西。
但動態(tài)鏈接不一樣,LD_PRELOAD="./mymalloc.so" ./myprog 這句話的意思是,先不要看別的so,優(yōu)先去看 mymalloc.so,從這里面去找函數(shù)的實(shí)現(xiàn)。然后這個so還會去 libc.so 里面去找printf的實(shí)現(xiàn),這樣就直接套娃了,printf去找malloc,malloc又是優(yōu)先被 mymalloc.so定義的,無限循環(huán)。
情景訓(xùn)練場景一
已有一個 libadd.so,內(nèi)有符號add(int size),同時被main和libadd.so內(nèi)的其他符號引用,不重新編譯liba.so的情況下對add進(jìn)行打樁
搞4個文件:
兩個add_test文件做成libadd.so
一個myprog文件放main函數(shù)
一個my_add文件放wrap
上代碼
libadd_test1.c
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
libadd_test2.c
#include <stdio.h>
int add(int a, int b);
int Calculation()
{
int a = 10;
int b = 10;
int ret = add(a , b);
}
myprog.c
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <dlfcn.h>
int add(int a, int b);
int main()
{
int a = 1;
int b = 1;
int ret = add(a, b);
return 0;
}
my_add.c
#include <stdio.h>
int __real_add(int a, int b);
int __wrap_add(int a, int b)
{
int ret = __real_add(a, b);
printf("add result=%d\n", ret);
return ret;
}
運(yùn)行
生成so
gcc -shared -fpic -o libadd.so libadd_test1.c libadd_test2.c
wrap并鏈接,生成可執(zhí)行程序
gcc -Wl,--wrap,add -o myprog myprog.c my_add.c -ldl -rdynamic -L. -ladd
運(yùn)行時需要指定動態(tài)庫的路徑
LD_LIBRARY_PATH=`pwd` ./myprog
如果你鏈接的時候直接指定就鏈這個 libadd.so,那么可以直接運(yùn)行,但是假設(shè)有多個so,最好還是用目錄的方法
gcc -Wl,--wrap,add -o myprog myprog.c my_add.c -ldl -rdynamic ./libadd.so