1. 首先要获取sys_call_table 的内存地址。
在学习代码的过程中,用到了替换sys_call_table替换的问题,自己查了相关资料,在这里总结一下:
首先sys_call_table 是系统内核的一块区间,用来将调用号和服务连接起来,系统调用某一个进程时,就会通过sys_call_table ,来查找到该程序,sys_call_table是一个数组。
1.1利用0x80中断劫持system_call->sys_call_table
因为系统调用都是通过0x80中断来进行的,故可以通过查找0x80中断的处理程序来获得sys_call_table的地址。其基本步骤是
1. 获取中断描述符表(IDT)的地址(使用C ASM汇编)
2. 从中查找0x80中断(系统调用中断)的服务例程(8*0x80偏移)
3. 搜索该例程的内存空间,
4. 从其中获取sys_call_table(保存所有系统调用例程的入口地址)的地址
编程示例
find_sys_call_table.c
#include <linux/module.h>
#include <linux/kernel.h>
// 中断描述符表寄存器结构
struct
{
unsigned short limit;
unsigned int base;
} __attribute__((packed)) idtr;
// 中断描述符表结构
struct
{
unsigned short off1;
unsigned short sel;
unsigned char none, flags;
unsigned short off2;
} __attribute__((packed)) idt;
// 查找sys_call_table的地址
void disp_sys_call_table(void)
{
unsigned int sys_call_off;
unsigned int sys_call_table;
char* p;
int i;
// 获取中断描述符表寄存器的地址
asm("sidt %0":"=m"(idtr));
printk("addr of idtr: %x\n", &idtr);
// 获取0x80中断处理程序的地址
memcpy(&idt, idtr.base+8*0x80, sizeof(idt));
sys_call_off=((idt.off2<<16)|idt.off1);
printk("addr of idt 0x80: %x\n", sys_call_off);
// 从0x80中断服务例程中搜索sys_call_table的地址
p=sys_call_off;
for (i=0; i<100; i++)
{
if (p=='\xff' && p[i+1]=='\x14' && p[i+2]=='\x85')
{
sys_call_table=*(unsigned int*)(p+i+3);
printk("addr of sys_call_table: %x\n", sys_call_table);
return ;
}
}
}
// 模块载入时被调用
static int __init init_get_sys_call_table(void)
{
disp_sys_call_table();
return 0;
}
module_init(init_get_sys_call_table);
// 模块卸载时被调用
static void __exit exit_get_sys_call_table(void)
{
}
module_exit(exit_get_sys_call_table);
// 模块信息
MODULE_LICENSE("GPL2.0");
MODULE_AUTHOR("LittleHann");
Makefile
obj-m := find_sys_call_table.o
编译
make -C /usr/src/kernels/2.6.32-358.el6.i686 M=$(pwd) modules
测试效果
dmesg| tail
1.2 获取sys_call_table的其他方法获取到了sys_call_table的基地址之后,我们就可以修改指定offset对应的系统调用了,从而达到劫持系统调用的目的
1.3 通过/boot/System.map-2.6.32-358.el6.i686文件查找
模拟出一个call *sys_call_table(,%eax,4),然后看其机器码,然后在system_call的附近基于这个特征进行寻找
#include <stdio.h>
void fun1()
{
printf("fun1/n");
}
void fun2()
{
printf("fun2/n");
}
unsigned int sys_call_table[2] = {fun1, fun2};
int main(int argc, char **argv)
{
asm("call *sys_call_table(%eax,4");
}
编译
gcc test.c -o test
objdump进行dump
objdump -D ./test | grep sys_call_table
拦截系统调用来获取sys_call_table
截获各钟系统调用:http://laokaddk.blog.51cto.com/368606/421862
http://laokaddk.blog.51cto.com/368606/926020
2.获取sys_call_table的地址后,对其进行替换:(常用于木马,病毒,一般不建议替换,能编译并加载模块,说明有root权限,有root权限,替换一个系统调用用处不大)
这里就用伪代码了:
int init_hook_syscall(void) //替换sys_call_table
{
printk(KERN_ALERT "sys_call_table: %p\n", sys_call_table);
prov_old_exit = (unsigned long *)(sys_call_table[__NR_exit]); // 保存原来的表,方便之后还原
make_rw((unsigned long)sys_call_table); //sys_call_table 本来只读,改为可以写状
sys_call_table[__NR_exit] = (unsigned long *)prov_exit; //将表替换成prov_exit
make_ro((unsigned long)sys_call_table); //改为写保护(改回只读)
return 0;
}
asmlinkage long prov_exit(int errno){
printk(KERN_ALERT "Inside new_handler().\n");
return 0;
}
/* make the page writable */
static int make_rw(unsigned long address) //
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);
if (pte->pte & ~_PAGE_RW)
pte->pte |= _PAGE_RW; // |= 按位或
return 0;
}
/* make the page write protected */
static int make_ro(unsigned long address) //
{
unsigned int level;
pte_t *pte = lookup_address(address, &level);
pte->pte &= ~_PAGE_RW;
return 0;
}
目前有的疑问:我目前看到的替换代码有不同的:
比如有:
real_open = (void *)sys_call_table[__NR_open];
sys_call_table[__NR_open] = (unsigned long*)fake_open;
real_unlink = (void *)sys_call_table[__NR_unlink];
sys_call_table[__NR_unlink] = (unsigned long*)fake_unlink;
real_unlinkat = (void *)sys_call_table[__NR_unlinkat];
sys_call_table[__NR_unlinkat] = (unsigned long*)fake_unlinkat;;
有:
prov_old_exit_group = (unsigned long *)(sys_call_table[__NR_exit_group]);
prov_old_exit = (unsigned long *)(sys_call_table[__NR_exit])
也有我像我给出的给出的一句代码,他们之间的区别是什么?
参考目录:
http://blog.csdn.net/tianxuhong/article/details/50974400
http://blog.csdn.net/dog250/article/details/6451762
http://laokaddk.blog.51cto.com/368606/936276/
http://www.freebuf.com/sectool/105713.html
关于hook 的汇总资料
利用LD_PRELOAD进行hook:
http://hbprotoss.github.io/posts/li-yong-ld_preloadjin-xing-hook.html
Unix操作系统LD_PRELOAD简介:
http://os.51cto.com/art/201004/195510.htm
高级Linux Kernel Inline Hook技术分析与实现:
http://old.sebug.net/paper/pst_WebZine/pst_WebZine_0x03/html/[PSTZine%200x03][0x03][高级Linux%20Kernel%20Inline%20Hook技术分析与实现].html
hook:
http://laokaddk.blog.51cto.com/368606/d-26/p-2
利用Kprobe探测内核中的变量:http://alanwu.blog.51cto.com/3652632/1111213