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、如果你遇到一个共享库,里面的符号是强符号(不能再定义一个同名函数了),则这里的方法不再有用了。
假如你有一个二进制版的系统,例如现在流行的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、如果你遇到一个共享库,里面的符号是强符号(不能再定义一个同名函数了),则这里的方法不再有用了。
相关文章推荐
- UNIX 编程技巧——如何 hook 一个共享库
- [每天一个知识点]24-编程技巧-如何简单计算分页等需要进1的除法
- RCP编程技巧:在程序中如何关闭一个ViewPart?
- 查看: 35330 | 回复: 9 打印 上一主题 下一主题 [学习共享] 转:Shell 编程--本文结合大量实例阐述如何编写一个shell脚本
- UNIX 如果查询一个共享内存已经被IPCRM,程序中如何操作保证安全
- 如何制作一个RSS源,并创建小部件免费使用任何编程技巧
- PHP 小技巧之(3)将一个php源程序存入一个字符串变量以后,如何将其中的尾部空格,换行符以及tab,去掉呢?
- 如何在VC6.0中得到一个程序的运行时间,也就是这个程序耗费的时钟周期数// C和C++的时间编程
- UNIX 环境高级编程(二)—— linux共享库,/etc/ld.so.conf 及 ld.so.conf.d/libc.conf
- 如何有一个好的编程生涯开端
- UNIX环境高级编程学习——列出一个目录中的所有文件
- Windows核心编程-如何以管理员的身份启动一个程序
- Linux多线程编程时如何查看一个进程中的某个线程是否存活
- 第四题:如果一个字符数组中有重复的字符 如"abbcd" 那么如何编程打印其全排列
- 多个分布式系统如何共享使用一个固定公网IP
- 看我如何假装是一个编程极客
- Java编程中如何在一个数值范围内生成一个伪随机数
- 关于hook设置函数如何支持多核CPU的一个说明
- 两台电脑如何共享一个3G无线上网卡?
- 技巧和诀窍: 如何上传一个.SQL文件到远程主机并且执行它来部署一个SQL数据库