以前虽然也知道应该怎么把程序中用到的函数替换掉,但还从来没有实际做过,最近因工作需要才动手做了一遍。
写一个最简单的hello world程序
// main.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
puts("Hello World!");
return 0;
}
// end of main.c
# gcc main.c -o 1
# ./1
Hello World!
1. 静态编译
使用ld的命令行参数--wrap,a,表示对a的调用改为对__wrap_a的调用,而对__real_a的调用改为对原来的a的调用。
// stub.c
int __wrap_puts(const char *s)
{
int result;
__real_puts("========before real call========");
result = __real_puts(s);
__real_puts("======== after real call========");
return result;
}
// end of stub.c
# gcc -Wl,--wrap,puts main.c stub.c -o 2
# ./2
========before real call========
Hello World!
======== after real call========
2. 动态编译
使用LD_PRELOAD,然后用dlsym取原来的符号地址,缺点是由于安全原因对有suid或sgid的程序没法用,据说有的系统调用也没法这么拦截,需要用ptrace。
// dynstub.c
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
int puts(const char *s)
{
int result;
static typeof(puts) *orig_puts = NULL;
if (orig_puts == NULL)
orig_puts = dlsym(RTLD_NEXT, "puts");
(*orig_puts)("========before real call========");
result = (*orig_puts)(s);
(*orig_puts)("======== after real call========");
return result;
}
// end of dynstub.c
# gcc -fpic -shared -ldl -o dynstub.so dynstub.c
# LD_PRELOAD=/root/tmp/dynstub.so ./1
========before real call========
Hello World!
======== after real call========
在每个函数里用dlsym看起来也不太美观,可能__attribute__ ((constructor))更好点,但这就是大量使用的时候才需要考虑的问题了。
// dynstub2.c
#define _GNU_SOURCE
#include <assert.h>
#include <stdio.h>
#include <dlfcn.h>
static typeof(puts) *orig_puts = NULL;
__attribute__ ((constructor)) void loadsym()
{
orig_puts = dlsym(RTLD_NEXT, "puts");
assert(orig_puts != NULL);
}
int puts(const char *s)
{
int result;
(*orig_puts)("========before real call========");
result = (*orig_puts)(s);
(*orig_puts)("======== after real call========");
return result;
}
// end of dynstub2.c
没有评论:
发表评论