ReactOs是怎么实现系统调用的。。 老毛文章学习笔记

1. stub函数

__declspec(naked) __stdcall

NtReadFile(

{

       __asm {

              push       ebp

              mov ebp, esp

              mov eax,152           // 系统调用号

              lea   edx, 8[ebp]     // lea  edx, [ebp +8]   ,这样edx 便指向了用户空间堆栈第一个参数的地址

              int    0x2E             //系统自陷指令。。进入内核。。。

              pop ebp               // 恢复堆栈

              ret   9

       }

}

2. 系统初始化 系统调用门的代码:

 

VOID INIT_FUNCTION

KeInitExceptions(VOID)

/*

 * FUNCTION: Initalize CPU exception handling

 */

{

   ……

   set_trap_gate(0, (ULONG)KiTrap0, 0);

   set_trap_gate(1, (ULONG)KiTrap1, 0);

   set_trap_gate(2, (ULONG)KiTrap2, 0);

   set_trap_gate(3, (ULONG)KiTrap3, 3);

   ……

   set_system_call_gate(0x2d,(int)interrupt_handler2d);

   set_system_call_gate(0x2e,(int)KiSystemService);

}

3.所以在cpu自陷到内核中时,便执行KiSystemService.  KiSystemService的工作有两个:

1. 使用edx把参数从用户地址空间中拷贝到内核堆栈中。

2. 根据eax在SSDT或Shadow SSDT中找到系统调用的起始地址..

1) 先判断在系统调用在哪个SSDT中

说明:     

  在windows中有 KeServiceDescriptorTable保存了SSDT的相关内容。。早期windows只有一个SSDT , 我称为MainSSDT , 后来把GDI的东西移到内核win32k.sys后又实用了一个SSDT。。称为Shadow SSDT 所以共有两个SSDT。。Main SSDT保存的系统调用号都小于x01000, 后一个大于0x1000, 而mainSSDT大约有248个系统调用。。。  Shadow SSDT有600多调用....

所以把系统调用右移8位然后与0x10便知道使用哪个SSDT 了。。。。

 

2) 根据系统调用号知道找到函数的地址 。。

分析下KiSystemService的代码:(看到AT&T的汇编 就头大。。。 )

 

 

    /* Construct a trap frame on the stack */

    /* Error code */
    pushl $0    
    pushl %ebp
    pushl %ebx
    pushl %esi
    pushl %edi
    pushl %fs
    /* Load PCR selector into fs */
    movl  $PCR_SELECTOR, %ebx
    movl  %ebx, %fs

    /* Save the old exception list */
    movl         %fs:KPCR_EXCEPTION_LIST, %ebx
    pushl %ebx
    /* Put the exception handler chain terminator */
    movl         $0xffffffff, %fs:KPCR_EXCEPTION_LIST
    /* Get a pointer to the current thread */
    movl         %fs:KPCR_CURRENT_THREAD, %esi
    /* Save the old previous mode */
    movl         $0, %ebx
    movb         %ss:KTHREAD_PREVIOUS_MODE(%esi), %bl
    pushl        %ebx
           /* Set the new previous mode based on the saved CS selector */
    movl         0x24(%esp), %ebx
    cmpl         $KERNEL_CS, %ebx
    jne          L1
    movb         $KernelMode, %ss:KTHREAD_PREVIOUS_MODE(%esi)
    jmp          L3
L1:
    movb         $UserMode, %ss:KTHREAD_PREVIOUS_MODE(%esi)
L3:

    /* Save other registers */   
    pushl %eax
    pushl %ecx
    pushl %edx
    pushl %ds
    pushl %es
    pushl %gs
    pushl $0     /* DR7 */
    pushl $0     /* DR6 */
    pushl $0     /* DR3 */
    pushl $0     /* DR2 */
    pushl $0     /* DR1 */
    pushl $0     /* DR0 */
    pushl $0     /* XXX: TempESP */
    pushl $0     /* XXX: TempCS */
    pushl $0     /* XXX: DebugPointer */
    pushl $0     /* XXX: DebugArgMark */
    pushl $0     /* XXX: DebugEIP */
    pushl $0     /* XXX: DebugEBP */

           /* Load the segment registers */
    movl  $KERNEL_DS, %ebx
    movl  %ebx, %ds
    movl  %ebx, %es
    movl  %ebx, %gs

           /* Save the old trap frame pointer (over the EDX register??) */
           movl KTHREAD_TRAP_FRAME(%esi), %ebx
    movl %ebx, 0x3C(%esp)
 
    /* Save a pointer to the trap frame in the TCB */
    movl %esp, KTHREAD_TRAP_FRAME(%esi)
 
           /*  Set ES to kernel segment  */
           movw $KERNEL_DS,%bx
           movw %bx,%es

           /*  Allocate new Kernel stack frame  */
           movl %esp,%ebp

           /*  Users's current stack frame pointer is source  */
           movl %edx,%esi

           /*  Determine system service table to use  */            //决定使用哪个SSDT
           cmpl  $0x0fff, %eax                                               //判断系统调用号是否大约0x1000
           ja    new_useShadowTable

           /*  Check to see if EAX is valid/inrange  */
           cmpl  %es:_KeServiceDescriptorTable + 8, %eax
           jbe   new_serviceInRange
           movl  $STATUS_INVALID_SYSTEM_SERVICE, %eax
           jmp   new_done

new_serviceInRange:

           /*  Allocate room for argument list from kernel stack  */
           movl  %es:_KeServiceDescriptorTable + 12, %ecx
           movl  %es:(%ecx, %eax, 4), %ecx
           subl  %ecx, %esp

           /*  Copy the arguments from the user stack to the kernel stack  */
           movl %esp,%edi                               //堆栈拷贝
           rep  movsb

           /*  DS is now also kernel segment  */
           movw %bx, %ds
   
    /* Call system call hook */
    pushl %eax
    call _KiSystemCallHook
    popl %eax

           /*  Make the system service call  */
           movl  %es:_KeServiceDescriptorTable, %ecx
           movl  %es:(%ecx, %eax, 4), %eax                         //系统调用的函数的起始地址    系统调用号* 4
           call  *%eax

#if CHECKED
           /*  Bump Service Counter  */
#endif

           /*  Deallocate the kernel stack frame  */
           movl %ebp,%esp

    /* Call the post system call hook and deliver any pending APCs */
    pushl %ebp
    pushl %eax
    call _KiAfterSystemCallHook
    addl $8,%esp

           jmp  new_done

new_useShadowTable:

           subl  $0x1000, %eax

           /*  Check to see if EAX is valid/inrange  */
           cmpl  %es:_KeServiceDescriptorTableShadow + 24, %eax
           jbe   new_shadowServiceInRange
           movl  $STATUS_INVALID_SYSTEM_SERVICE, %eax
           jmp   new_done

new_shadowServiceInRange:

           /*  Allocate room for argument list from kernel stack  */
           movl  %es:_KeServiceDescriptorTableShadow + 28, %ecx
           movl  %es:(%ecx, %eax, 4), %ecx
           subl  %ecx, %esp

           /*  Copy the arguments from the user stack to the kernel stack  */
           movl %esp,%edi
           rep movsb

           /*  DS is now also kernel segment  */
           movw %bx,%ds

    /* Call system call hook */
    pushl %eax
    call _KiSystemCallHook
           popl %eax
 
           /*  Make the system service call  */
           movl  %es:_KeServiceDescriptorTableShadow + 16, %ecx
           movl  %es:(%ecx, %eax, 4), %eax
           call  *%eax

#if CHECKED
           /*  Bump Service Counter  */
#endif

           /*  Deallocate the kernel stack frame  */
           movl %ebp,%esp

    /* Call the post system call hook and deliver any pending APCs */
    pushl %esp
    pushl %eax
    call _KiAfterSystemCallHook
    addl $8,%esp

new_done:

           /* Restore the user context */
    /* Get a pointer to the current thread */
           movl %fs:0x124, %esi
 
           /* Restore the old trap frame pointer */
           movl 0x3c(%esp), %ebx
    movl %ebx, KTHREAD_TRAP_FRAME(%esi)
 
    /* Skip debug information and unsaved registers */
    addl $0x30, %esp
    popl %gs
    popl %es
    popl %ds
    popl %edx
    popl %ecx
    addl $0x4, %esp   /* Don't restore eax */

    /* Restore the old previous mode */
    popl %ebx
    movb %bl, %ss:KTHREAD_PREVIOUS_MODE(%esi)

    /* Restore the old exception handler list */
    popl %ebx
    movl %ebx, %fs:KPCR_EXCEPTION_LIST
 
    popl %fs
    popl %edi
    popl %esi
    popl %ebx
    popl %ebp
    addl $0x4, %esp  /* Ignore error code */
  
           iret

 

 

 

 

 

 

 

 

 

posted @ 2009-07-04 20:02  甜甜嘟嘟  阅读(464)  评论(0编辑  收藏  举报