关于如何获取sys_call_table的地址
时间:2007-06-15 来源:lonelyair
linux内核在2.4.18以后为了安全起见不再导出sys_call_table符号,从而无法直接获得系统调用表的地址,那么就必须找到其他的办法来得到这个地址。在背景知识中提到了/dev/kmem是系统主存的映像,可以通过查询该文件来找到sys_call_table的地址,并对其进行修改,来使用新的系统调用。那么如何在系统映像中找到sys_call_table的地址呢?让我们先看看system_call的源代码是如何来实现系统调用的(代码见/arch/i386/kernel/entry.S):
ENTRY(system_call)
pushl %eax # save orig_eax
SAVE_ALL
GET_CURRENT(%ebx)
cmpl $(NR_syscalls),%eax
jae badsys
testb $0x02,tsk_ptrace(%ebx) # PT_TRACESYS
jne tracesys
call *SYMBOL_NAME(sys_call_table)(,%eax,4)
movl %eax,EAX(%esp) # save the return value
ENTRY(ret_from_sys_call)
这段源代码首先保存相应的寄存器的值,然后判断系统调用号(在eax寄存器中)是否合法,继而对设置调试的情况进行处理,在所有这些进行完后,利用call *SYMBOL_NAME(sys_call_table)(,%eax,4) 来转入相应的系统调用进行处理,其中的SYMBOL_NAME(sys_call_table)得出的就是sys_call_table的地址。从上面的分析可以看出,当找到system_call函数之后,利用字符匹配来寻找相应call语句就可以确定sys_call_table的位置,因为call something(,%eax,4)的机器指令码是0xff 0x14 0x85。所以匹配这个指令码就行了。 如何确定system_call的地址在背景知识中已经介绍了,下面给出相应的伪代码:
struct{ //各字段含义可以参考背景知识中关于IDTR寄存器的介绍
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;
int kmem;
/ *下面函数用于从kemem对应的文件中偏移量为off处读取sz个字节至内存m处*/
void readkmem(void *m,unsigned off,int sz) {………}
/*下面函数用于从src读取count个字节至dest处*/
void weitekmem(void *src,void *dest,unsigned int count) {………..}
unsigned sct; //用来存放sys_call_table地址
char buff[100]; //用于存放system_call函数的前100个字节。
char *p;
if((kmem=open(“/dev/kmem”,O_RDONLY))<0)
return 1;
asm(“sidt %0” “:=m” (idtr)); //读取idtr寄存器的值至idtr结构中
readkmem(&idt,idtr.base+8*0x80,sizeof(idt)) //将0x80描述符读至idt结构中
sys_ call_off=(idt.off2<<16)|idt.off1; //得到system_call函数的地址。
readkmem(buff,sys_call_off,100) //读取system_call函数的前100字节至buff
p=(char *)memmem(buff,100,”xffx14x85”,3); //得到call语句对应机器码的地址
sct=(unsigned *)(p+3) //得到sys_call_table的地址。
ENTRY(system_call)
pushl %eax # save orig_eax
SAVE_ALL
GET_CURRENT(%ebx)
cmpl $(NR_syscalls),%eax
jae badsys
testb $0x02,tsk_ptrace(%ebx) # PT_TRACESYS
jne tracesys
call *SYMBOL_NAME(sys_call_table)(,%eax,4)
movl %eax,EAX(%esp) # save the return value
ENTRY(ret_from_sys_call)
这段源代码首先保存相应的寄存器的值,然后判断系统调用号(在eax寄存器中)是否合法,继而对设置调试的情况进行处理,在所有这些进行完后,利用call *SYMBOL_NAME(sys_call_table)(,%eax,4) 来转入相应的系统调用进行处理,其中的SYMBOL_NAME(sys_call_table)得出的就是sys_call_table的地址。从上面的分析可以看出,当找到system_call函数之后,利用字符匹配来寻找相应call语句就可以确定sys_call_table的位置,因为call something(,%eax,4)的机器指令码是0xff 0x14 0x85。所以匹配这个指令码就行了。 如何确定system_call的地址在背景知识中已经介绍了,下面给出相应的伪代码:
struct{ //各字段含义可以参考背景知识中关于IDTR寄存器的介绍
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;
int kmem;
/ *下面函数用于从kemem对应的文件中偏移量为off处读取sz个字节至内存m处*/
void readkmem(void *m,unsigned off,int sz) {………}
/*下面函数用于从src读取count个字节至dest处*/
void weitekmem(void *src,void *dest,unsigned int count) {………..}
unsigned sct; //用来存放sys_call_table地址
char buff[100]; //用于存放system_call函数的前100个字节。
char *p;
if((kmem=open(“/dev/kmem”,O_RDONLY))<0)
return 1;
asm(“sidt %0” “:=m” (idtr)); //读取idtr寄存器的值至idtr结构中
readkmem(&idt,idtr.base+8*0x80,sizeof(idt)) //将0x80描述符读至idt结构中
sys_ call_off=(idt.off2<<16)|idt.off1; //得到system_call函数的地址。
readkmem(buff,sys_call_off,100) //读取system_call函数的前100字节至buff
p=(char *)memmem(buff,100,”xffx14x85”,3); //得到call语句对应机器码的地址
sct=(unsigned *)(p+3) //得到sys_call_table的地址。
相关阅读 更多 +