Linux下HOOK动态链接库中API的方法
2012年,我写了一篇介绍Windows系统下Ring3层API的hook方案——《一种注册表沙箱的思路、实现——Hook Nt函数》,其在底层使用了微软的Detours库。5年后,我又遇到这么一个问题,但是系统变成了Linux。我最开始的想法是找一个Linux下的Detours库,于是找到了subhook。其原理是:修改被Hook函数起始地址处的汇编代码,让执行流程跳到我们定义的函数中。但是在实际使用中,我发现通过该库调用原始函数有错误——地址违例,导致进程崩溃,所以最终放弃了subhook的方案。(转载请指明出于breaksoftware的csdn博客)
后来发现,Linux用户层Hook非常简单。我们只要定义一个和被Hook的API相同名称、参数、返回值的函数即可。比如我们需要Hook获取用户UID的函数getuid(原来是在libc.so中实现的),则需要定义如下函数:
uid_t getuid(void) {
return 800;
}
我们在main函数中调用之
int main() {
printf("get_uid:%dn", getuid());
}
函数返回
我使用work账户登录的,其真实uid是502。而我们重写了程序中的getuid,则返回的是我们“指定”的800。
如果我们希望在被hook中的函数中调用原始函数,怎么做呢?这儿有个比较尴尬的问题,那就是我们定义的getuid地址将对应于符号getuid,那么原始的getuid(以后称libc中的getuid)地址将对应什么符号?我们怎么找到它?
可以想象libc中的getuid对应的符号不会因为我们的程序而被改变,那么就意味着程序运行中,将有两个getuid。事实也的确如此。
第一个getuid就是我们重定义的hook的函数体,第二个是动态链接库libc.so中的。于是我们在重定义的函数体中,使用
dlsym(RTLD_NEXT, "getuid")
就可以获得原始的函数地址。
所以这种方案的精髓就是RTLD_NEXT参数。我们看下dlsym函数参数的说明:
There are two special pseudo-handles, RTLD_DEFAULT and RTLD_NEXT. The former will find the first occurrence of the desired symbol using the default library search order. The latter will find the next occurrence of a function in the search order after the current library. This allows one to provide a wrapper around a function in another shared library. 这段文字意思是:在默认的库查找顺序下,RTLD_DEFAULT是用于查找第一个符号匹配的函数地址,RTLD_NEXT是用于查找第二个符号匹配的函数地址。这种方式就提供了一种针对动态链接库中函数替换的功能。
以我们例子,RTLD_DEFAULT将找到我们自己定义的getuid,而RTLD_NEXT将找到libc.so中的。 为了方便使用这种方式,我封装了相关调用
#ifndef HOOK_BASE
#define HOOK_BASE
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <dlfcn.h>
#define HOOK_FUNC_TEMPLATE(function_name) function_name##_func_t
#define HOOK_FUNC_ORI_NAME(function_name) function_name##_ori
#define HOOK_FUNC_INIT(function_name) static HOOK_FUNC_TEMPLATE(function_name) HOOK_FUNC_ORI_NAME(function_name);
#define HOOK_FUNC(function_name)
if (!HOOK_FUNC_ORI_NAME(function_name)) {
HOOK_FUNC_ORI_NAME(function_name) = (HOOK_FUNC_TEMPLATE(function_name)) dlsym(RTLD_NEXT, #function_name);
}
#define ORIGINAL_FUNC(function_name) ((HOOK_FUNC_TEMPLATE(function_name)) HOOK_FUNC_ORI_NAME(function_name))
#endif
我们只要关注HOOK_FUNC_INIT、HOOK_FUNC和ORIGINAL_FUNC三个宏。HOOK_FUNC_INIT方法声明了一个全局函数指针变量,其在HOOK_FUNC宏中被指定为被HOOK函数的原始地址。ORIGINAL_FUNC则是将这个指针进行类型转换,从而方便调用。
下一步我们要定义被HOOK的函数的类型
#ifndef HOOK_DEF
#define HOOK_DEF
#include "hook_base.h"
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
typedef uid_t (*HOOK_FUNC_TEMPLATE(getuid))(void);
#endif
然后重定义我们要HOOK的函数
#include "hook_def.h"
#include <stdio.h>
#include <sys/types.h>
HOOK_FUNC_INIT(getuid);
uid_t getuid(void) {
HOOK_FUNC(getuid);
int uid = ORIGINAL_FUNC(getuid)();
printf("getuid original:%dn", uid);
return 800;
}
这段代码,我们先调用原始的getuid函数,并打印出它的值。最后才返回一个我们定义的值——800。
在main函数中,我们只调用getuid。并使用 gcc src/*.c -ldl -o main 编译
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include "hook_def.h"
int main() {
printf("get_uid:%dn", getuid());
return 0;
}
其返回结果如下
- Gnuboard 漏洞分析
- HTTPS 原理与证书实践
- 企业防火墙之iptables
- MongoDB的备份与恢复
- 浅析XSS的几种测试方法
- MongoDB 分片集群技术
- hadoop任务测试
- Android查缺补漏(线程篇)-- AsyncTask的使用及原理详细分析
- Android查缺补漏(IPC篇)-- 进程间通讯基础知识热身
- Android查缺补漏(IPC篇)-- 进程间通讯之AIDL详解
- Android查缺补漏(IPC篇)-- 进程间通讯之Socket简介及示例
- Android查缺补漏(IPC篇)-- Bundle、文件共享、ContentProvider、Messenger四种进程间通讯介绍
- Android查缺补漏(View篇)--布局文件中的“@+id”和“@id”有什么区别?
- Name node is in safe mode.
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法