零、概述
-
Ring3中劫持
- 基于环境变量 LD_PRELOAD 的动态库劫持。
-
Ring0中劫持
-
Kernel Inline Hook
-
syscall table 修改
-
内核调试机制 Kprobe
-
一、Ring3 函数调用劫持
在 Linux 中,动态库加载的时候,会按照以下顺序进行搜索:
LD_PRELOAD > LD_LIBRARY_PATH > /etc/ld.so.cache > /lib > /usr/lib
方法原理:通过 LD_PERELOAD 定义在程序运行前优先加载的动态链接库为自己编写的动态库。
例子:劫持 gets() 函数
#include<stdio.h>
#include<dlfcn.h> //用于搜索原函数
/* 要求:函数的形式必须和原函数一样(返回类型,函数名,函数参数)*/
char* gets(char* str){
/* 自定义的操作区域 */
printf("hook gets! str: %s\n ",str);
/* 调用原函数*/
typeof(gets) *func;//函数指针
func=dlsym(RTLD_NEXT,"gets");//查找 gets 函数位置 dlsym:在打开的动态库里找一个函数
return (*func)(str); //调用原函数执行
}
gcc hook.c -fPIC -shared -ldl -D_GNU_SOURCE -o hook.so
设置 LD_PRELOAD
通过设置环境变量的方法
- 临时设置 export LD_PRELOAD=$PWD/hook.so
- 永久设置
- 修改 profile 文件 加入 export LD_PRELOAD=${YOUR PATH}/hook.so
- 修改 .bashrc 文件 加入 export LD_PRELOAD=${YOUR PATH}/hook.so
编写一个测试程序 test.c
#include <stdio.h>
int main(){
char str[20]="\0";
printf("请输入\n");
gets(str);
return 0;
}
函数调用劫持效果
二、Ring0 系统调用劫持
2.1、Kernel Inline Hook
原理:一个系统调用会调用子函数的,这是通过段内偏移的方式完成的,我们可以通过设置这个偏移指向我们 Hook 的原函数
例子:劫持 sys_read 系统调用
2.2、sys_call_table 修改方法进行系统调用劫持
原理:将系统调用表中对应的服务例程修改为自己 Hook 函数的地址
例子:劫持 fork()
查找 syscall_table 位置的方法
- 代码模拟出 call *sys_call_table(,%eax,4),然后查看机器码查找。
- 通过 /boot/System.map-2.6.32-358.el6.i686 文件查找。
- 通过 /proc/kallsyms 进行搜索。
2.3、内核调试机制 kprobe 进行系统调用劫持
(1)Kprobe介绍
轻量级内核调试机制
(2)Kprobe两种使用方法
模块加载
debugfs接口
(3)Kprobe三种探测手段
kprobe 基本的探测手段 基础 可以在函数内任意位置放置探测点
jprobe 探测在函数的入口,可以方便的获得函数参数,但是每个函数只能有一个探针
Kretprobe 探测在函数的返回值
例子:劫持 execve()
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
int jsys_execve(const char __user *filename,
const char __user *const __user *__argv,
const char __user *const __user *__envp)
{
pr_info("jprobe: execve: %s\n", filename);
/* Always end with a call to jprobe_return(). */
jprobe_return();
return 0;
}
static struct jprobe jprobe_execve = {
.entry= jsys_execve,
.kp = {
.symbol_name = "sys_execve",
},
};
static int __init mymodule_init(void){
int ret;
/* 挂载 hook */
ret = register_jprobe(&jprobe_execve);
if (ret < 0) {
pr_info("register_jprobe failed, returned %d\n", ret);
return -1;
}
pr_info("Planted jprobe execve at %p, handler addr %p\n",
jprobe_execve.kp.addr, jprobe_execve.entry);
return 0;
}
static void __exit mymodule_exit(void)
{
unregister_jprobe(&jprobe_execve);
}
module_init(mymodule_init)
module_exit(mymodule_exit)
MODULE_LICENSE("GPL");
(SAW:Game Over!)