免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2754 | 回复: 6
打印 上一主题 下一主题

截获系统调用之改进版 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-06-18 15:25 |只看该作者 |倒序浏览
从网上看到有介绍截获系统调用的文章,于是自己下载下来编译执行了一下。
但是发现这些例子都有缺点:像sys_execve(struct pt_regs regs)这样的系统调用,
截获后若要它仍然完成原有的功能需要调用do_execve()函数,但是这个函数是内核不导
出,很麻烦。另外,即算是截获函数实现了它的功能,错误还是难免出现。

    于是就想怎么样截获后又能不留痕迹地实现原有功能。一个好的想法是直接在截获函数中
直接调用sys_execve().然而想到信号处理时如果中断了系统调用则可以重新执行,那么
能不能通过类似的方法执行系统调用呢?一个想法:替换之前将要替换的系统调用保存在其他
中断向量中,然后在替换函数中调整内核堆栈,让系统重新以另一个调用号调用保存的系统调用。

经过近两天的编码调试,终于如愿以偿。先说明一下关键之处,然后是代码。
关键之处:   
1.将原来的系统调用函数保存在向量__NR_save=17中,这里用17有两个原因
    1) 在文件syscall_table.S中,向量17的函数是sys_ni_syscall,这是一个没有使用的
        调用号。
       .long sys_ni_syscall    /* old break syscall holder */
    2) 一开始我用 319,因为NR_syscalls=318
        #define NR_syscalls 318
        大于NR_syscalls的调用号自然没人用,然而这样做不行。因为在entry.S中
        system_call:
            ...
            cmpl $(nr_syscalls), %eax
            jae syscall_badsys   
            . ...
       若调用号大于NR_syscalls,根本不会执行调用函数
2.    信号处理中重新执行中断的调用是这样做的:
       case -ERESTARTNOINTR:
          regs->eax = regs->orig_eax;
          regs->eip -= 2;
    于是我就想这样做
          pr.eip-=2;
    然而老是出错,后来查看汇编后的代码发现,减2操作是在寄存器中实现的,内核堆栈
    根本没有改变。于是改用汇编:
     __asm__("sub $0x2,%0" : "=m" (pr.eip));
3.  将内核堆栈中regs.eax改为__NR_save,然而这一步不能通过在函数中赋值改变
    因为在entry.S中调用完函数后,内核将寄存器eax值存入regs.eax中
         system_call:
            ...
            call *sys_call_table(,%eax,4)
            movl %eax,EAX(%esp)             # store the return value      
             . ...  
    我们知道寄存器eax存的是函数返回值,于是我将替换函数返回_NR_save,让内核自动
    将其保存在regs.eax。
以下是代码:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <asm/unistd.h>
#include <asm/ptrace.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xunil@bmy and hhj");
MODULE_DESCRIPTION("Different from others, this module automatically locate the entry of sys_call_table !");
unsigned long *sys_call_table=NULL;
//asmlinkage
int (*orig_exec)(struct pt_regs);
int __NR_save=17;
struct _idt
{
unsigned short offset_low,segment_sel;
unsigned char reserved,flags;
unsigned short offset_high;
};

unsigned long *getscTable(void)
{

unsigned char idtr[6],*shell,*sort;
struct _idt *idt;
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));

/* get the address of system_call */
idt=(struct _idt*)(*(unsigned long*)&idtr[2]+8*0x80);
offset_low = idt->offset_low;
offset_high = idt->offset_high;
system_call=(offset_high<<16)|offset_low;

shell=(char *)system_call;
sort="\xff\x14\x85";

/* get the address of sys_call_table */

for(i=0;i<(100-2);i++)
if(shell==sort[0]&&shell[i+1]==sort[1]&&shell[i+2]==sort[2])
{
printk("here ok!!\n");
break;

}
p=&shell;
p+=3;
sct=*(unsigned long*)p;

return (unsigned long*)(sct);
}
//asmlinkage
int hacked_exec(struct pt_regs pr){
    __asm__("sub $0x2,%0" : "=m" (pr.eip));
//    pr.eax=__NR_save;
    printk("PID %d called sys_execve !\n",current->pid);
    return __NR_save;
}


static int __init find_init(void){
sys_call_table = getscTable();
printk("here 1\n");

orig_exec=(int(*)(struct pt_regs))(sys_call_table[__NR_execve]);
printk("here 2,%d\n",__NR_save);
sys_call_table[__NR_execve]=(long)hacked_exec;
sys_call_table[__NR_save]=(long)orig_exec;
return 0;
}

static void __exit find_cleanup(void){
sys_call_table[__NR_execve]=(unsigned long)orig_exec;
}

module_init(find_init);
module_exit(find_cleanup);
有空到我的博客瞧瞧哦 hhj.cublog.cn

[ 本帖最后由 hauto 于 2007-6-18 15:27 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2007-06-18 16:06 |只看该作者
说实话,我觉得符号不导出是一项shit一样的功能,除了给程序员制造障碍以外别无所图。

论坛徽章:
0
3 [报告]
发表于 2007-06-18 16:43 |只看该作者
原帖由 hauto 于 2007-6-18 15:25 发表于 1楼  
从网上看到有介绍截获系统调用的文章,于是自己下载下来编译执行了一下。
但是发现这些例子都有缺点:像sys_execve(struct pt_regs regs)这样的系统调用,
截获后若要它仍然完成原有的功能需要调用do_execve() ...

我有一点疑问是,既然都要改kernel,为什么不改简单一点呢?你可以直接在INT 80 的handler加一个hook,这个hook首先保存上下文(几个寄存器的值),然后执行你要的操作,最后返回时再恢复上下文即可。
另外如果是想在实际运用中截获系统调用,使用ptrace()函数是正道。

[ 本帖最后由 zx_wing 于 2007-6-18 16:47 编辑 ]

论坛徽章:
0
4 [报告]
发表于 2007-06-18 16:54 |只看该作者
原帖由 zx_wing 于 2007-6-18 16:43 发表于 3楼  

我有一点疑问是,既然都要改kernel,为什么不改简单一点呢?你可以直接在INT 80 的handler加一个hook,这个hook首先保存上下文(几个寄存器的值),然后执行你要的操作,最后返回时再恢复上下文即可。
另外如 ...

想法不错!不过,实现起来恐怕没那么简单吧?

论坛徽章:
0
5 [报告]
发表于 2007-06-18 17:12 |只看该作者
关键是保存上下文,其它倒不难

[ 本帖最后由 zx_wing 于 2007-11-12 13:01 编辑 ]

论坛徽章:
0
6 [报告]
发表于 2007-11-12 10:53 |只看该作者
to  zx-wing  ;
     有没有例子啊 , 发上来看看啊。。

论坛徽章:
0
7 [报告]
发表于 2007-12-04 14:33 |只看该作者
楼主,你这儿是不是错了
if(shell==sort[0]&&shell[i+1]==sort[1]&&shell[i+2]==sort[2])
{
printk("here ok!!\n");
break;

}
p=&shell;
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP