下面是一段代码可以实现系统调用的替换 linux 内部的系统调用 sys_mkdir :(code.c)
系统调用作为一个数组放在一个系统调用表sys_call_table之中, 由于sys_call_table在不同主机上的地址是不一样的。我们的直接赋值的办法是行不通的。那么我们就需要通过其他方式获取了。
具体的获取思路如下:每一个系统调用都是通过int 0x80中断进入核心,中断描述符表把中断服务程序和中断向量对应起来。对于系统调用来说,
操作系统会调用 system_call中断服务程序。system_call函数在系统调用表中根据系统调用号找到并调用相应的系统调用服务例程。
idtr寄存器指向 中断描述符表 的起始地址,用sidt[asm ("sidt %0" : "=m" (idtr));]指令得到中断描述符表起始地址,从这条指令中得到的指针可以获得int 0x80中断服描述符
所在位置,然后计算出system_call函数的地址反编译一下system_call函数可以看到在system_call函数内,是用call sys_call_table指令来调用系统调用函数的。
因此,只要找到system_call里 的call sys_call_table(,eax,4)指令的机器指令就可以获得系统调用表的入口地址了。
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <asm/unistd.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("gudujain");
MODULE_DESCRIPTION("Different from others, this module automatically locate the entry of sys_call_table !");
unsigned long *sys_call_table=NULL;
typedef asmlinkage int (*type_orig_mkdir)(const char *,int);
type_orig_mkdir orig_mkdir;
typedef struct _idt
{
unsigned short offset_low,segment_sel;
unsigned char reserved,flags;
unsigned short offset_high;
}idt;
unsigned long *getscTable()
{
unsigned char idtr[6],*shell,*sort;
idt *pidt;
unsigned long system_call,sct;
unsigned short offset_low,offset_high;
char *p;
int i;
/* get the interrupt descriptor table */
__asm__("sidt %0" : "=m" (idtr)); //通过sidt指令获取中断描述符表寄存器的地址,这里面包含有idt表的首地址
/* get the address of system_call */
pidt = (idt *)(*(unsigned long*)&idtr[2]+8*0x80);
//通过idtr里面的idt的首地址+偏移获取0x80中断处理程序的地址,
offset_low = pidt->offset_low;
offset_high = pidt->offset_high;
system_call = (offset_high<<16)|offset_low; //通过这项idt找到system_call的函数地址
shell = (char *)system_call; ///执行 int 0x80 之后到这个地址开始执行。
sort = "\xff\x14\x85";
/// shell 指向的地址包含如下指令:
/// 0xc0106bf2 <system_call+42>: jne 0xc0106c48 <tracesys>
/// 0xc0106bf4 <system_call+44>: call *0xc01e0f18(,%eax,4)
/// 0xc0106bfb <system_call+51>: mov %eax,0x18(%esp,1)
/// 0xc0106bff <system_call+55>: nop
/// 其中地址 0xc0106bf4 处指令 <system_call+44>:机器码是: 0x188514ff (little endian)
/* get the address of sys_call_table */
for(i=0;i<(100-2);i++)
if( (shell[i]==sort[0]) && (shell[i+1]==sort[1]) && (shell[i+2]==sort[2]) )
break;
p = &shell[i];
p += 3;
sct = *(unsigned long*)p;
return (unsigned long*)(sct);
}
asmlinkage int hacked_mkdir(const char * pathname, int mode)
{
printk("PID %d called sys_mkdir !\n",current->pid);
return orig_mkdir(pathname,mode);
}
static int __init find_init(void)
{
sys_call_table = getscTable();
printk("the sys_call_table address is:%x\n",sys_call_table);
orig_mkdir =(type_orig_mkdir)sys_call_table[__NR_mkdir];
sys_call_table[__NR_mkdir]=(unsigned long)hacked_mkdir;
return 0;
}
static void __exit find_cleanup(void)
{
sys_call_table[__NR_mkdir]=(unsigned long)orig_mkdir;
}
module_init(find_init);
module_exit(find_cleanup);
加载编译得到的内核模块 code.ko:
#insmod code.ko
测试是否成功: