Snowdream

I'm awake but my world is half asleep
posts - 403, comments - 310, trackbacks - 0, articles - 7
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

读核笔记(3) - 系统调用

Posted on 2008-02-18 14:35 ZelluX 阅读(403) 评论(4)  编辑  收藏 所属分类: LinuxSystem
_syscall 宏:
最简单的没有参数的系统调用的实现:
/* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */
#define _syscall0(type,name) \
type name(
void) \
{ \
long __res; \
__asm__ 
volatile ("int $0x80" \
    : 
"=a" (__res) \
    : 
"0" (__NR_##name)); \
__syscall_return(type,__res); \
}

以getuid()为例,_syscall0(int, getuid)展开后就变成
int getuid(void)
{
    
long __res;
    __asm__ 
volatile("int $0x80"
                    :
"=a" (__res)
                    :
"0"  (__NR_getuid));
    __syscall_return(
int, __res);
}

程序把系统调用号__NR_getuid放入eax寄存器,然后调用软中断。
include/asm-i386/hw_irq.h中的定义:
00025   #define SYSCALL_VECTOR  0x80;

arch/i386/kernel/traps.c中把该中断号绑定到system_call函数:
00944   set_system_gate(SYSCALL_VECTOR,&system_call);

system_call函数在arch/i386/kernel/entry.S中:
ENTRY(system_call)
    pushl %eax            # save orig_eax
    SAVE_ALL
    GET_CURRENT(%ebx)
    testb $0x02
,tsk_ptrace(%ebx)    # PT_TRACESYS
    jne tracesys
    cmpl $(NR_syscalls)
,%eax
    jae badsys
    call *SYMBOL_NAME(sys_call_table)(
,%eax,4)
    movl %eax
,EAX(%esp)        # save the return value
ENTRY(ret_from_sys_call)
    cli                # need_resched and signals atomic test
    cmpl $
0,need_resched(%ebx)
    jne reschedule
    cmpl $
0,sigpending(%ebx)
    jne signal_return
restore_all:
    RESTORE_ALL
主要步骤:
1. 保留一份系统调用号的最初拷贝
2. 保存堆栈环境(SAVE_ALL)
3. 得到当前task_struct的地址,保存到ebx中
4. 检查系统调用号
5. 根据%eax调用号计算地址,调用相应函数
6. 在entry.S后面可以看到,
.data
ENTRY(sys_call_table)
    .long SYMBOL_NAME(sys_ni_syscall)    /* 
0  -  old "setup()" system call*/
        
    .long SYMBOL_NAME(sys_getuid16)
    .long SYMBOL_NAME(sys_stime)        /* 
25 */
    .long SYMBOL_NAME(sys_ptrace)
        
sys_call_table + %eax * 4是sys_getuid16地址,kernel/uid16.c中:
asmlinkage long sys_getuid16(void)
{
    
return high2lowuid(current->uid);
}
很简单的处理代码,返回当前进程的uid。这里asmlinkage修饰符表示函数必须从堆栈中,而不是从寄存器中拿参数(防止gcc用寄存器传参优化)。
7. 保存返回值eax到堆栈中的eax
8. RESTORE_ALL

另外这里再提一下GET_CURRENT的实现
#define GET_CURRENT(reg) \
    movl $-
8192, reg; \
    andl %esp, reg
很巧妙的实现,把栈指针与掩码-8192做与操作,末尾13位清零,就是当前的进程的task_struct地址了。


接下来是利用内核模块动态添加一个系统调用的例程,由于2.4.20以后sys_call_table符号不再被导出了,要获得这个地址得手动hack。尚未成功,下次回过头来看看。

评论

# re: 读核笔记(3) - 系统调用  回复  更多评论   

2008-02-18 16:07 by overboming
版主,代码高亮怎么做方便来着?

# re: 读核笔记(3) - 系统调用  回复  更多评论   

2008-02-18 16:09 by ZelluX
@overboming
这个博客网站的编辑器有相关的功能

# re: 读核笔记(3) - 系统调用  回复  更多评论   

2008-02-26 16:03 by luohandsome
水木的kerneltech板5507讲系统调用劫持的:
LKM Rootkits on Linux x86 v2.6

# re: 读核笔记(3) - 系统调用  回复  更多评论   

2008-02-26 16:18 by ZelluX
@luohandsome
恩,多谢啦~~

只有注册用户登录后才能发表评论。


网站导航: