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