LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1349|回复: 11

各位大虾,想修改INT80中断向量来进行系统调用截获并得到系统调用参数,现代码见附件,编

[复制链接]
发表于 2005-4-18 19:55:41 | 显示全部楼层 |阅读模式
哥们,本人想通过对INT80 中断向量的修改来截获系统调用,并得到系统调用参数,现遇到了困难,望各位帮忙看看指点,不胜感激,
本程序编译能通过,但在加载时死机,不过还好,重启后没见LINUX有不良反应。大哥帮忙了。
希望学习LINUX内核编程的同志欢迎讨论,QQ:53401266  EMAIL  qqrilxk@qzinfo.cn

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
发表于 2005-4-20 10:09:14 | 显示全部楼层
  1. #define MODULE
  2. #define KERNEL                   ------>修改为 #define __KERNEL__
  3. #define GFP_KERNEL 0
  4. #define NULL 0
  5. #include <linux/module.h>
  6. #include <linux/tty.h>

  7. #include <linux/init.h>
  8. #include <linux/slab.h>
复制代码

然后

  1. int init_module(void)
  2. {

  3. printk("start get_addr_idt");
  4. __asm__ volatile ("sidt %0": "=m" (idtr));
  5. printk("the idt address is %x\n",idtr.base);
  6. struct descriptor_idt *idte=(struct descriptor_idt *)(idtr.base+8*0x80);
  7. old_stub=(idte->offset_high<<16|idte->offset_low);
  8. printk("<1>oldstub=%x\n",old_stub);
  9. unsigned long new_addr=(unsigned long)my_stub;
  10. idte->offset_high = (unsigned short) (new_addr >> 16);
  11. idte->offset_low = (unsigned short) (new_addr & 0x0000FFFF);
  12. printk("<1>newstub=%x\n",(idte->offset_high<<16|idte->offset_low));

  13. return ;      ---------------------------------->这里修改为:  return 0;

  14. }
复制代码

除此外,你的代码没有问题。我测试了中断向量表的0号(除零异常),很正常。
但是系统调用的太频繁,加载你的模块后,系统只能不停的打印系统调用的参数了。你可以只截获特定的系统调用,这样系统应该可以正常响应
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-4-20 23:11:43 | 显示全部楼层
大哥,非常感谢你的热情回复!你的肯定对我是个很大的鼓励,只是我只想截获INT 80中断向量,其它的中断向量好弄些,比如用我上面的代码稍微改改能行。但对这该死的INT80来说不那么好对付,望大哥有时间试试,谢谢再次回复。
回复 支持 反对

使用道具 举报

发表于 2005-4-21 15:18:37 | 显示全部楼层
三个问题:
1:kmalloc会自己释放吗?你代码中每次系统调用都会malloc一次!(特别注意80调用1秒可能有成千上万次)

2:printk不可以直接向控制台打印,不然你看到的只是花屏,可改为向/var/log/message打印

3:(最重要)在调用my_handler之前,应该要先保护寄存器,不然有些寄存器的值在my_stub中被修改后,恢复不了!可以这样做
void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
"pushl %%es\n"
"pushl %%ds\n"
"pushl %%eax\n"
"pushl %%ebp\n"
"pushl %%edi\n"
"pushl %%esi\n"
"pushl %%edx\n"
"pushl %%ecx\n"
"pushl %%ebx\n"
"call my_handler \n"
"popl %%ebx\n"
"popl %%ecx\n"
"popl %%edx\n"
"popl %%esi\n"
"popl %%edi\n"
"popl %%ebp\n"
"popl %%eax\n"
"popl %%ds\n"
"popl %%es\n"
"jmp *old_stub"
::
);
}
回复 支持 反对

使用道具 举报

发表于 2005-4-21 21:02:08 | 显示全部楼层
Post by qqrilxk
大哥,非常感谢你的热情回复!你的肯定对我是个很大的鼓励,只是我只想截获INT 80中断向量,其它的中断向量好弄些,比如用我上面的代码稍微改改能行。但对这该死的INT80来说不那么好对付,望大哥有时间试试,谢谢再次回复。


那你可不可以不要每次系统调用都打印出参数?比如一次申请大点的内存,每次系统调用的参数存入这个内存区,然后比如每1000次系统调用后才处理一次。

我上次就试过你的代码,去掉其中打印的部分,每次系统调用让一个变量加一,几秒钟后再看,发现系统调用了近500次。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-4-24 10:03:47 | 显示全部楼层

哥们的回复给了我很大启示, 望有兴趣的哥们按我下面说的再试试

上面有位哥们说截获 INT 0很正常,  俺没试过,  不过俺相信应该能行的但对80 向量就不是这么回事了,  为什么呢?  再仔细看看内核会有所发现:  在对非 80 向量进入真实的处理函数前已经进行了操作save_all,即在从中断向量表中取出中断处理函数地址时已进行了save_all操作,  而在save_all中进行了对ds ,es的设置 ,  而我现在的代码中未对此进行设置,  在entry.s中进入中断处理函数前也进行了此操作 ,,我想我的代码也许就错在这里了,,有兴趣的哥们试试. 当然俺也会试着修改的到时再贴出来了..同时希望能将试过后有所发现在代码贴出来,,,不胜感激...
上面有位哥们说通过设一个变量对系统调用次数计数,  俺试过了, 怎么不行只有黑屏呀 唉.  望这哥们贴出代码 .
回复 支持 反对

使用道具 举报

发表于 2005-4-24 12:03:53 | 显示全部楼层
Post by qqrilxk
上面有位哥们说截获 INT 0很正常,  俺没试过,  不过俺相信应该能行的但对80 向量就不是这么回事了,  为什么呢?  再仔细看看内核会有所发现:  在对非 80 向量进入真实的处理函数前已经进行了操作save_all,即在从中断向量表中取出中断处理函数地址时已进行了save_all操作,

非也,对于int 0,这样的异常,并没有save_all 操作,所以不需要恢复.像你这样改就没有什么真实不真实处理函数的问题,中断(异常)处理函数入口已经被你替换,所以,当中断时(或异常发生时),直接跳转到你给的函数执行.
Post by qqrilxk

而在save_all中进行了对ds ,es的设置 ,  而我现在的代码中未对此进行设置,  在entry.s中进入中断处理函数前也进行了此操作 ,,我想我的代码也许就错在这里了,,

这里算是对了,你确实忘了压栈操作.
回复 支持 反对

使用道具 举报

发表于 2005-4-25 10:00:59 | 显示全部楼层
#define MODULE
#define __KERNEL__
#define NULL 0
#include <linux/module.h>
#include <linux/tty.h>
#include <asm/unistd.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/fs.h>

void my_stub();
static unsigned long old_stub;
static unsigned long stub_eax,stub_ebx,stub_ecx;
struct descriptor_idt
{
unsigned short offset_low,seg_selector;
unsigned char reserved,flag;
unsigned short offset_high;
};
struct {
      unsigned short limit;
     unsigned long base;
     }__attribute__ ((packed)) idtr;

void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
"pushl %%es\n"
"pushl %%ds\n"
"pushl %%eax\n"
"pushl %%ebp\n"
"pushl %%edi\n"
"pushl %%esi\n"
"pushl %%edx\n"
"pushl %%ecx\n"
"pushl %%ebx\n"
"movl %%eax,%0\n"
"movl %%ebx,%1\n"
"movl %%ecx,%2\n"
"call my_handler \n"
"popl %%ebx\n"
"popl %%ecx\n"
"popl %%edx\n"
"popl %%esi\n"
"popl %%edi\n"
"popl %%ebp\n"
"popl %%eax\n"
"popl %%ds\n"
"popl %%es\n"
"jmp *old_stub"
::"m"(stub_eax),"m"(stub_ebx),"m"(stub_ecx)
);
}

void my_handler()
{//假设这里只打印eax,ebx,ecx,当然你也可以做其他事情!
//最好,不要什么系统调用都打印!你可以这样
// if(stub_eax == 512) //只截获512号系统调用,该系统调用不存在,用我给的程序测试!
                        //我的系统是rh9,2.4内核!你可以检查/var/log/message
                        //看看是不是有输出了
//  printk(KERN_INFO"0x%x, 0x%x, 0x%x\n",stub_eax,stub_ebx,stub_ecx);
  
  //下面的什么系统调用都被截获,同样可以检查/var/log/message看看是不是系统调用已经源源不断
  //的被输出了
    printk(KERN_INFO"eax=0x%x, ebx=0x%x, ecx=0x%x\n",stub_eax,stub_ebx,stub_ecx);

}

int init_module(void)
{
printk("start get_addr_idt");
__asm__ volatile ("sidt %0": "=m" (idtr));
printk("the idt address is %x\n",idtr.base);
struct descriptor_idt *idte=(struct descriptor_idt *)(idtr.base+8*0x80);
old_stub=(idte->offset_high<<16|idte->offset_low);
printk("<1>oldstub=%x\n",old_stub);
unsigned long new_addr=(unsigned long)my_stub;
idte->offset_high = (unsigned short) (new_addr >> 16);
idte->offset_low = (unsigned short) (new_addr & 0x0000FFFF);
printk("<1>newstub=%x\n",(idte->offset_high<<16|idte->offset_low));
return 0;
}

void cleanup_module()
{
printk("destroy...b..\n");
__asm__ volatile ("sidt %0": "=m" (idtr));
struct descriptor_idt *idte=(struct descriptor_idt *)(idtr.base+8*0x80);
idte->offset_high = (unsigned short) (old_stub >> 16);
idte->offset_low = (unsigned short) (old_stub & 0x0000FFFF);
printk("<1>restore the stub=%x\n",(idte->offset_high<<16|idte->offset_low));
}

MODULE_LICENSE("GPL");
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
以上模块的编译方法:
假设你的linux source在/usr/src/linux路径下
gcc -c -I/usr/src/linux/include ok-test22.c
编译后得到ok-test22.o
insmod ok-test22.o
不就OK了
回复 支持 反对

使用道具 举报

发表于 2005-4-25 10:19:51 | 显示全部楼层
#include <stdio.h>
#include <unistd.h>
int
main()
{
unsigned long sys_num = 512;//512号系统调用(不存在,用来测试上面的模块而已)
unsigned long value = 0;
__asm__("int $0x80":"=a"(value):"0"((long)(sys_num)));
printf("The value is %ld.\n",value);
return value;
}
////////////////////////
以上程序编译方法为:令文件名是:test.c
gcc -o test test.c
编译后得到
test
执行后看看/var/log/message是不是有输出了!

BTW:
说实话,本来这个贴子我是不想回答的!因为个人感觉楼主有点瞧不起人!
从楼主说话的语气来看,好象牛的不得了!请不要忘了一山更比一山高!三人行必有我师!
做技术的切记!
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-4-25 15:58:08 | 显示全部楼层
非常感谢楼上哥们的回复,哥们确实给了我很大的帮助。今天打开这贴子时见到这么多的回复,真让我兴奋。可慢慢往下看时,真有点脸红了。我一阵慌乱,gotop2004大哥在说我吧?再看了看全贴,我知道,我肯定闯祸了。gotop2004大哥,小弟在此向你表示诚挚的歉意,说声对不起了。我的未经思考的随意用词让你以及(肯定有)其他哥们受了委屈。小弟在此小心地解释一下,本人所说的那句“哥们按我下面说的再试试”是起因本人编程较弱(这是事实,几乎没写过什么代码,对自己没太大信心),想借哥们力量帮小弟一把。当然小弟也不是什么。。。。,唉,怎么说呢?只是,其实我只是。。。想相互交流一下并能动手实践,我希望能多有个哥们帮助,我确实没别的意思(我自己的底自己最清楚,丝毫不敢卖弄),可能小弟说话确实不太注意别人感受了,唉,哥们,我只能说声对不起了。希望哥们不要计较小弟的一时失言,谢谢了。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表