PUBLIC _KiFastCallEntry
_KiFastCallEntry proc
;此时:
;eax指向服务编号
;edx指向当前用户栈,edx+8为参数列表
;
; Sanitize the segment registers
;
mov ecx, KGDT_R3_DATA OR RPL_MASK
push KGDT_R0_PCR
pop fs ;fs指向pcr
mov ds, ecx ;ds指向用户数据段
mov es, ecx ;ex指向用户数据段
;
; When we trap into the kernel via fast system call we start on the DPC stack. We need
; shift to the threads stack before enabling interrupts.
;
;其实sysenter操作SYSENTRY_ESP_MSR这个寄存器把它的值存入esp没什么用(SYSENTRY_ESP_MSR在初始化时指向
;prcb的DcpStack
mov ecx, PCR[PcTss] ;从pcr中取得tts
mov esp, [ecx]+TssEsp ;从tts中获得当前线程堆栈的esp
push KGDT_R3_DATA OR RPL_MASK ; Push user SS(压入用户态线程的堆栈段寄存器ss)
push edx ; Push ESP(压入esp)
pushfd ;压入标志寄存器(eflags)
Kfsc10:
push 2 ; Sanitize eflags, clear direction, NT etc;设置eflags为2,,表示所有标志位都是0(中断被关闭)
add edx, 8 ; (edx) -> arguments(移动栈顶指针到参数)
popfd ;
.errnz(EFLAGS_INTERRUPT_MASK AND 0FFFF00FFh)
or byte ptr [esp+1], EFLAGS_INTERRUPT_MASK/0100h ; Enable interrupts in eflags(打开用户态eflags的中断)
push KGDT_R3_CODE OR RPL_MASK ; Push user CS
push dword ptr ds:[USER_SHARED_DATA+UsSystemCallReturn] ; push return address(使其返回时指向用户空间
;的KiFastSystemCallRet
push 0 ; put pad dword for error on stack
push ebp ; save the non-volatile registers
push ebx ;
push esi ;
push edi ;保存现场
mov ebx, PCR[PcSelfPcr] ; Get PRCB address(获得prcb地址)
push KGDT_R3_TEB OR RPL_MASK ; Push user mode FS
mov esi, [ebx].PcPrcbData+PbCurrentThread ; get current thread address(使esi指向当前线程的KTHREAD)
;
; Save the old exception list in trap frame and initialize a new empty
; exception list.
;
push [ebx].PcExceptionList ; save old exception list(保存老的ExceptionList)
mov [ebx].PcExceptionList, EXCEPTION_CHAIN_END ; set new empty list(新的ExceptionList为空白)
mov ebp, [esi].ThInitialStack ;使ebp指向当前线程堆栈的栈顶
;
; Save the old previous mode in trap frame, allocate remainder of trap frame,
; and set the new previous mode.
;
push MODE_MASK ; Save previous mode as user(保存先前模式)
sub esp,TsPreviousPreviousMode ; allocate remainder of trap frame(分配了48h字节, 也就是DbgEbp到Eax所占据的空间
sub ebp, NPX_FRAME_LENGTH + KTRAP_FRAME_LENGTH
mov byte ptr [esi].ThPreviousMode,MODE_MASK ; set new previous mode of user;设置ETHREAD
;中的PREVIOUSMode为UserMode
;
; Now the full trap frame is build.
; Calculate initial stack pointer from thread initial stack to contain NPX and trap.
; If this isn't the same as esp then we are a VX86 thread and we are rejected
;
cmp ebp, esp ;判断堆栈内压入的值是否正确(这些堆栈内压入的东西,就好比一个trap框架)
jne short Kfsc91 ;不正确跳转
;
; Set the new trap frame address.
;
and dword ptr [ebp].TsDr7, 0
test byte ptr [esi].ThDebugActive, 0ffh ; See if we need to save debug registers
mov [esi].ThTrapFrame, ebp ; set new trap frame address(在ETHREAD中设置自陷框架地址)
jnz Dr_FastCallDrSave ; if nz, debugging is active on thread
Dr_FastCallDrReturn: ;
SET_DEBUG_DATA ; Note this destroys edi
sti ; enable interrupts(开中断)
?FpoValue = 0
;
;到这里TRAP_FRAME,开始调用系统服务的准备工作
; (eax) = Service number ;系统服务序号
; (edx) = Callers stack pointer ;参数指针
; (esi) = Current thread address ;线程ETHREAD
;
; All other registers have been saved and are free.
;
; Check if the service number within valid range
;判断系统服务号是否合法
;
_KiSystemServiceRepeat:
mov edi, eax ; copy system service number
shr edi, SERVICE_TABLE_SHIFT ; isolate service table number
and edi, SERVICE_TABLE_MASK ;
mov ecx, edi ; save service table number
add edi, [esi]+ThServiceTable ; compute service descriptor address
mov ebx, eax ; save system service number
and eax, SERVICE_NUMBER_MASK ; isolate service table offset
;
; If the specified system service number is not within range, then attempt
; to convert the thread to a GUI thread and retry the service dispatch.
;如果系统服务号不合法,就调用GUI(win32k.sys,sssdt)
;
cmp eax, [edi]+SdLimit ; check if valid service
jae Kss_ErrorHandler ; if ae, try to convert to GUI thread
;
; If the service is a GUI service and the GDI user batch queue is not empty,
; then call the appropriate service to flush the user batch.
;
cmp ecx, SERVICE_TABLE_TEST ; test if GUI service
jne short Kss40 ; if ne, not GUI service(如果不是GUI跳走)
mov ecx, PCR[PcTeb] ; get current thread TEB address(获得TEB)
xor ebx, ebx ; get number of batched GDI calls
KiSystemServiceAccessTeb:
or ebx, [ecx]+TbGdiBatchCount ; may cause an inpage exception(是否有异常)
jz short Kss40 ; if z, no batched calls(有,跳走)
push edx ; save address of user arguments
push eax ; save service number
call [_KeGdiFlushUserBatch] ; flush GDI user batch
pop eax ; restore service number
pop edx ; restore address of user arguments
;
; The arguments are passed on the stack. Therefore they always need to get
; copied since additional space has been allocated on the stack for the
; machine state frame. Note that we don't check for the zero argument case -
; copy is always done regardless of the number of arguments because the
; zero argument case is very rare.
;
;复制R3的参数到R0的堆栈上
Kss40: inc dword ptr PCR[PcPrcbData+PbSystemCalls] ; system calls
if DBG
mov ecx, [edi]+SdCount ; get count table address
jecxz short @f ; if zero, table not specified
inc dword ptr [ecx+eax*4] ; increment service count
@@: ;
endif
FPOFRAME ?FpoValue, 0
mov esi, edx ; (esi)->User arguments
mov ebx, [edi]+SdNumber ; get argument table address
xor ecx, ecx
mov cl, byte ptr [ebx+eax] ; (ecx) = argument size
mov edi, [edi]+SdBase ; get service table address
mov ebx, [edi+eax*4] ; (ebx)-> service routine
sub esp, ecx ; allocate space for arguments
shr ecx, 2 ; (ecx) = number of argument DWORDs
mov edi, esp ; (edi)->location to receive 1st arg
cmp esi, _MmUserProbeAddress ; check if user address
jae kss80 ; if ae, then not user address
KiSystemServiceCopyArguments: ;向ring0堆栈中拷贝参数起始于标号
rep movsd ; copy the arguments to top of stack.
; Since we usually copy more than 3
; arguments. rep movsd is faster than
; mov instructions.
;
; Check if low resource usage should be simulated.
;
if DBG
test _MmInjectUserInpageErrors, 2
jz short @f
stdCall _MmTrimProcessMemory, <0>
jmp short kssdoit
@@:
mov eax,PCR[PcPrcbData+PbCurrentThread]
mov eax,[eax]+ThApcState+AsProcess
test dword ptr [eax]+PrFlags,0100000h ; is this a inpage-err process?
je short @f
stdCall _MmTrimProcessMemory, <0>
@@:
endif
;
; Make actual call to system service
;
kssdoit:
call ebx ; call system service
kss60:
;
; Check for return to user mode at elevated IRQL.
;
if DBG
test byte ptr [ebp]+TsSegCs,MODE_MASK ; test if previous mode user
jz short kss50b ; if z, previous mode not user
mov esi,eax ; save return status
CurrentIrql ; get current IRQL
or al,al ; check if IRQL is passive level
jnz kss100 ; if nz, IRQL not passive level
mov eax,esi ; restore return status
;
; Check if kernel APCs are disabled or a process is attached.
;
mov ecx,PCR[PcPrcbData+PbCurrentThread] ; get current thread address
mov dl,[ecx]+ThApcStateIndex ; get APC state index
or dl,dl ; check if process attached
jne kss120 ; if ne, process is attached
mov edx,[ecx]+ThCombinedApcDisable ; get kernel APC disable
or edx,edx ; check if kernel APCs disabled
jne kss120 ; if ne, kernel APCs disabled.
kss50b: ;
endif
kss61:
;
; Upon return, (eax)= status code. This code may also be entered from a failed
; KiCallbackReturn call.
;
mov esp, ebp ; deallocate stack space for arguments
;
; Restore old trap frame address from the current trap frame.
;
kss70: mov ecx, PCR[PcPrcbData+PbCurrentThread] ; get current thread address
mov edx, [ebp].TsEdx ; restore previous trap frame address
mov [ecx].ThTrapFrame, edx ;