您的位置:首页 > 编程语言

UNIX 编程技巧——如何 hook 一个共享库

2014-01-10 16:05 176 查看
有时程序员需要完成这类任务:

假如你有一个二进制版的系统,例如现在流行的android,你需要为这个系统开发一个软件。

这个软件牵涉到系统行为,因此需要对系统做修改。然而你并没有这个系统的所有源码

( Nexus S的源码不一定与 android 官方版本一模一样),或者是你只有这个系统的头文件。

当你需要修改部分系统行为的时候,你不可能用源码重新编译一个共享库来替换系统文件。

此时,就需要利用 dlfcn.h 运行时动态链接库来实现我们的目标。利用 dlopen(),dlsym(),dlerror(),dlclose()这

四个函数,可以实现类似 Windows 平台 Api Hook 机制的效果。

步骤如下:

1、首先得先将原共享库改名。假如系统中有一个共享库libwuxiao.so,那么就需要

把它改成其他名字,比如,改为 libwuxiao_orig.so。

假如这个共享库的源码如下:

#include <stdio.h>

void func_a(int a, int b)

{

printf("func_a called a=%d b=%d\n",a,b);

}

void func_b(char* string)

{

printf("func_b called string=%s\n",string);

}

int func_c()

{

return 123;

}

编译一下, gcc -shared -fPIC -o libwuxiao.so wuxiao.c

可以写一个可执行程序来测试一下这个共享库。之后,你可以看到

我们完全不修改这个可执行文件,让这个可执行文件执行我们增加的代码。

#include <stdio.h>

extern void func_a(int a, int b);

extern void func_b(char* string);

extern int func_c();

int main(int argc, char* argv)

{

func_a(55,66);

func_b("hello,wuxiao");

int b = func_c();

printf("after func(), b=%d\n",b);

}

gcc -o wuxiao_test main.c -lwuxiao -L/path/to/libwuxiao.so

2、然后有两类编程方法实现wrapper:

a) 先调用dlopen(),返回libwuxiao_orig.so的句柄,再从句柄中查找想要的函数

#include <stdio.h>

#include <dlfcn.h>

static void* libwuxiao_handle = NULL;

__attribute__((constructor)) _init_wuxiao()

{

libwu_handle = dlopen("libwuxiao_orig.so",RTLD_NOW); // 注意:已经改名字了

}

__attribute__((destructor)) void _fini_wuxiao()

{

dlclose(libwu_handle);

}

int func_c()

{

int (*func_c_ptr)();

func_c_ptr = dlsym(libwuxiao_handle,"func_c");

if ( func_c_ptr != NULL )

{

printf(" add your hook code here\n");

return func_c_ptr();

}

return -1;

}

链接方法: gcc -shared -fPIC -o libwuxiao.so -ldl -lwuxiao_orig -L/path/to/libwuxiao_orig.so

(编译完上面的伪装共享库之后,执行一下 main_test,看看增加的代码有没有被执行。)

上面代码中,原共享库的 func_c 就是我们想要修改的函数。

这里写了一个相同的 func_c,它查找到原函数的地址,先执行 hook 代码,然后执行原 func_c 函数,

因而不会影响原函数的执行,只不过多执行了我们的代码。

随后 “伪装”成为libwuxiao.so,系统在执行程序的时候,就会查找伪造的 libwuxiao.so,

而伪造的libwuxiao.so又会去加载真正的 libwuxiao_orig.so。

这样达到了我们的目标:

1、保证原libwuxiao.so中的所有函数不会丢失

2、在调用原libwuxiao.so中的函数的时候能够执行我自己的代码。

b) 这里的方法更简单,原理与上面的相同,都是通过在伪造的libwuxiao.so中链接原libwuxiao_orig.so,

然后再定义同型函数,查找原函数地址,然后调用原函数的方法。这里的方法仅对GNU版本的系统有效。

#include <stdio.h>

#include <sys/types.h>

#include <dlfcn.h>

int func_c()

{

int (*func_c_ptr)();

// find func_c() in another shared object(with the same name)

dlerror();

func_c_ptr = dlsym(RTLD_DEFAULT,"func_c");

while ( func_c_ptr == func_c )

{

if ( func_c_ptr == NULL ) {

printf("Error(wrapper-2): cannot find func_c()\n");

break;

}

dlerror();

func_c_ptr = dlsym(RTLD_NEXT,"func_c");

}

if ( func_c_ptr != NULL )

{

printf("wuxiao is very powerful the second time\n");

return func_c_ptr();

}

return -1;

}

编译: gcc -shared -fPIC -o libwuxiao.so -ldl -lwuxiao_orig -L. -D_GNU_SOURCE

这种方法虽然更简单,但只对GNU有效,如果你的代码要放在非GNU的系统上执行,那还是用a)中的方法吧。

总结:

1、这里实现的wrapper的局限性就在于,你必须知道原来的共享库中的函数原型,但是你不必知道所有函数的原型。

你只需要知道其中你需要的函数原型即可。

2、如果你遇到一个共享库,里面的符号是强符号(不能再定义一个同名函数了),则这里的方法不再有用了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐