sys_call_table 的总结

来源:互联网 发布:怎么把淘宝店铺关闭 编辑:程序博客网 时间:2024/06/05 17:41

        

 在学习代码的过程中,用到了替换sys_call_table替换的问题,自己查了相关资料,在这里总结一下:

首先sys_call_table 是系统内核的一块区间,用来将调用号和服务连接起来,系统调用某一个进程时,就会通过sys_call_table ,来查找到该程序,sys_call_table是一个数组。

         

    1. 首先要获取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


获取到了sys_call_table的基地址之后,我们就可以修改指定offset对应的系统调用了,从而达到劫持系统调用的目的


1.2 获取sys_call_table的其他方法


模拟出一个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

1.3 通过/boot/System.map-2.6.32-358.el6.i686文件查找

cd /bootgrep sys_call_table System.map-2.6.32-358.el6.i686

拦截系统调用来获取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 的汇总资料

系统调用Hook:

http://xiaonieblog.com/?post=121

利用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


                             

                   

原创粉丝点击