2005-2-15 眾所周知 nt kernel 是多任務(wù)搶占試方式運行的,在非 SMP 系統(tǒng)上,每個進程分配特定的CPU 時間片來達到執(zhí)行目的,這樣看上去就象多個任務(wù)在同時運行。而 nt kernel又是以線程調(diào)度為核心的,這樣線程切換了也就意味著當前進程的切換。系統(tǒng)不斷重復這個過程,來使每個進程得以運行。在介紹流程前,需要先了解幾個系統(tǒng)內(nèi)部結(jié)構(gòu):KPCR、ETHREAD、EPROCESS、可以說,涉及到進程調(diào)度的函數(shù)基本都是對這幾個重要結(jié)構(gòu)的設(shè)置與填充。關(guān)于各結(jié)構(gòu)的細節(jié),對系統(tǒng)內(nèi)核有所了解的人自然都很熟悉,我就不再多廢話了。這里是結(jié)合進程調(diào)度來說。KPCR 這個結(jié)構(gòu)存放的是當前 CPU 所正在處理的各種信息,其中包括了當前正在運行的線程 ETHREAD 結(jié)構(gòu)。而 ETHREAD 結(jié)構(gòu)又與 EPROCESS 結(jié)構(gòu)是相互關(guān)聯(lián)的。這也就是很多核心函數(shù)通過 KPCR 啟始地址 0xFFDFF000 + 偏移就能夠得到當前正在運行的線程與進程的原因,如 KeGetCurrentThread()、IoGetCurrentProcess() 等。這也表明在非 SMP 系統(tǒng)上,某時間段內(nèi)當前 CPU 處理的進程只可能有一個。當進行進程調(diào)度時系統(tǒng)將根據(jù)當前所存活的進程來選擇讓哪個線程 ETHREAD 結(jié)構(gòu)來替換 KPCR 中的 ETHREAD,使其變?yōu)檎谶\行的狀態(tài)。那么又是如何觸發(fā)進程調(diào)度請求能讓所有進程均得以執(zhí)行的呢?開始已經(jīng)提到過,每個進程分配特定的 CPU 時間片來達到執(zhí)行目的,而系統(tǒng)的 CPU 時鐘中斷確定了每個進程分配的時間片。也就是當系統(tǒng) CPU 時鐘中斷觸發(fā)時,產(chǎn)生的進程調(diào)度請求。在詳細分析調(diào)度流程與各函數(shù)分支前,我們先來看一下大致的流程。首先當 CPU 時鐘中斷觸發(fā)時,系統(tǒng)將調(diào)用 KiDispatchInterrupt(),比較當前進程分配的時間片,如用完后會調(diào)用 KiQuantumEnd() 根據(jù)各線程優(yōu)先級等信息用特定的調(diào)度算法選擇新的線程(ETHREAD),然后將其返回值,一個 ETHREAD 結(jié)構(gòu)作為參數(shù),來調(diào)用 SwapContext() 設(shè)置 ETHREAD,EPROCESS 中的各項參數(shù),并替換 KPCR 中的相應結(jié)構(gòu)完成線程切換,調(diào)度到另一個進程(EPROCESS)的線程(ETHREAD)繼續(xù)執(zhí)行。(當線程等待某一事件(Event)或信號量(Semaphore)時會自動放棄當前時間片) 。通過上面的大致分析,我們可以看出與進程調(diào)度密切相關(guān)的幾個函數(shù),KiDispatchInterrupt() 與 SwapContext(),下面我們將就這兩個關(guān)鍵的調(diào)度函數(shù)進行詳細的分析。 首先來看下 KiDispatchInterrupt() 函數(shù),當調(diào)用此函數(shù)時,首先得到當前 KPCR 自身結(jié)構(gòu)與DCP鏈表頭,并比較當前是否有 DPC 正在處理,如果有 DPC 正在處理,則設(shè)置 DPC 異常煉。如果沒有,則直接跳轉(zhuǎn)到比較 KPCR 中 QuantumEnd 值,QuantumEnd 表示當前 KPCR 中正在處理的線程時間片總數(shù)。此值是是根據(jù)當前運行線程 ETHREAD->Quantum 中的值來填充的,就是說 KPCR 中 QuantumEnd 值不為0并不代表當前線程不允許切換,此時還需要調(diào)用 KiQuantumEnd() 來近一步判斷是否允許切換。所以當前值不為0時則跳轉(zhuǎn)到 KiQuantumEnd() 函數(shù)處做進一步判斷。KiQuantumEnd() 函數(shù)會取當前線程(ETHREAD)結(jié)構(gòu)中的 Quantum 值來進行判斷是否為0,根據(jù)線程優(yōu)先等參數(shù)調(diào)用 KiFindReadyThread() 函數(shù)來選擇一個新的線程填充 KPCR中的 NextThread ,并將 NextThread作為函數(shù)返回值,跳轉(zhuǎn)到相應地址繼續(xù)進行線程切換。如果返回值返為空,則表示無法進行切換,函數(shù)返回。但如果 KPCR 中的 QuantumEnd 本身已為0,則表示當前線程 ETHREAD分配時間片已經(jīng)用完,可以進行線程換,所以繼續(xù)比較 KPCR 中是否有下一個線程(偏移NextThread),如果KPCR 中下一線程(偏移NextThread)偏移為空,則表示沒有就緒線程可切換,直接跳轉(zhuǎn)到返回地址,完成函數(shù)調(diào)用。如果不為空的話,則可繼續(xù)進行線程切換。到此所需的基本參數(shù)都已經(jīng)準備就緒,下面要做的就是把 KPCR 中下一個線程(偏移NextThread),替換成 KPCR 中當前線程(偏移CurrentThread),并將 KPCR 中下一線程(偏移NextThread)清0,調(diào)用 KiReadyThread() 就緒剛剛設(shè)置好的下一線程(偏移NextThread),也就是現(xiàn)在為當前線程 (偏移CurrentThread)。最后調(diào)用 SwapContext() 函數(shù)完成最終的切換。 下面來看一下 SwapContext() 函數(shù)的實現(xiàn),上面提到過 SwapContext() 是在 KiDispatchInterrupt() 函數(shù)中調(diào)用的,用來完成最終的線程切換。函數(shù)首先設(shè)置要切換的新線程狀態(tài)(NextThread->Status) 為運行狀態(tài)。接下來判斷當前是否有 DCP 列程正在運行( KPCR 中的 DpcRoutineActive 是否為 0,不為0則表示當前有 DPC 處理,微軟規(guī)定在進行線程調(diào)度時不允許 DPC 列程運行,否則將系統(tǒng)崩潰,其實這不是必須的,僅僅是微軟的規(guī)定而已) 如果有則跳轉(zhuǎn)到調(diào)用 KeBugCheck() 函數(shù)處,系統(tǒng)崩潰。如果沒有則繼續(xù)取要切換的新線程(NextThread->DebugActive)調(diào)試標志狀態(tài),并賦給 KPCR 中 DebugActive 。保存 ESP 到要被切換的舊線程 (CurrentThread->KernelStack)內(nèi)核堆棧中,并將要切換的新線程(NextThread->InitalStack,NextThread->StackLimit)中的堆棧啟始地址與大小賦給 KPCR 中的相應位置。繼續(xù)取要切換的新線程(NextThread->NpxState)中的 NPX 狀態(tài)與CR0 的 NPX 狀態(tài)進行比較,如不相等跳轉(zhuǎn)到重新設(shè)置 CR0 處進行處理。刷新 CR0 后回跳轉(zhuǎn)回來繼續(xù)下面的運行。(CR0 中的 NPX 位狀態(tài)是 CPU 通過某幾個指令觸發(fā)一個異常后進入一個特殊的狀態(tài)來處理浮點指令,這時是不允許線程切換的,所以不相同的情況下需要重新刷新 CR0 狀態(tài)。 )接下來進行模式判斷,判斷要切換的新線程(NextThread)是否運行在 V86 模式下,如果是繼續(xù)調(diào)整內(nèi)核堆棧空間。如果不是則跳過調(diào)整。從 KPCR 中得到KTSS 地址,并將 NPX 標志位保存到 KTSS 中的 ESP0 處(這樣不論是否為 V86 模式下運行的線程都可以共享)。此時各項標志,結(jié)構(gòu)與參數(shù)都已就緒,下面的工作就是要進行具體的切換過程了。首先取要切換的新線程(NextThread->KernelStack)的內(nèi)核堆棧賦與當前內(nèi)核堆棧指針ESP,并設(shè)置 KPCR 中的用戶堆棧(TEB)為要切換的新線程(NextThread->TEB)的用戶堆棧(TEB)。然后將用戶堆棧(TEB)放入 KPCR中 GDT 的相應結(jié)構(gòu)中。比較要被切換的舊線程(CurrentThread->EPROCESS)中的進程,是否與要切換的新線程(NextThread->EPROCESS)中的進程相等?也就是判斷要切換的進程是否為當前進程,如果是則不刷新當前頁目錄表(CR3)以及其他相關(guān)值,而直接跳轉(zhuǎn)到添加當前進程切換計數(shù)與判斷當前線程是否存在 Pending 的 APC 調(diào)用處,然后退出,完成切換。如果要切換的線程不是當前進程,則從要切換的新線程(NextThread->EPROCESS)中取出當前進程,并從當前進程(EPROCESS->DirectoryTableBase)中得到頁目錄表來更新KPCR 中 KTSS 中的 TSSCR3 的值與 CR3 寄存器中的值,(這也就是為什么 CR3 總指向當前進程頁目錄地址)繼續(xù)將當前進程(EPROCESS->IopmOffset)中 IOPM 值賦與 KPCR 中 KTSS 中的 IOPM。再比較當前進程(EPROCESS->LdtDescriptor)中的 LDT 是否為空,如果不為空則從 KPCR中取得 KGDT 的位置,并從 KGDT 中索引到 LDT,把當前進程(EPROCESS->LdtDescriptor)中的 LDT 賦與 KPCR 中的LDT。再得到 KPCR 中 KIDT 的位置,把當前進程(EPROCESS->Int21Descriptor)中的 INT 21中斷賦與 KPCR 中 KIDT中的相應位置,使當前進程可以調(diào)用 INT 21 。最后調(diào)用 LLDT 使當前所有設(shè)置生效。(按理說 NT 內(nèi)核中 32 位應用程序是不使用 LDT 的但為什么在線程切換中會有設(shè)置LDT的部分呢?這是為了向下兼容 16 位的應用程序,當調(diào)度到一個 16 位的應用程時則會特意為它分配 LDT 并且使 IDT 中的 INT 21 有效,玩過 DOS 的人都知道 INT 21 是 DOS 下的系統(tǒng)調(diào)用,可以試著運行一個 16 位的 DOS 程序,然后觀察下 IDT 表就會發(fā)現(xiàn),原來沒有用到的 INT21 會被設(shè)置成一個 16 位的 TrapGate)否則如果為空則設(shè)置成不使用 LDT,把要切換的新線程(NextThread->ContextSwitches)中的切換次數(shù)與 KPCR 中的切換總和各加一,恢復異常鏈,并比較要切換的新線程(NextThread->KernelApcPending)中的 APC 調(diào)用是否沒有完成 ,如果當前 APC 狀態(tài)沒有完成的話則判斷當前是否可以處理 APC 調(diào)用,如果不能則設(shè)置返回標志為 Pending 完成線程切換的所有工作并返回。否則設(shè)置當前 IRQL 為 APC LEVEL 并調(diào)用 HalRequestSoftwareInterrupt() 函數(shù)來處理 APC Pending狀態(tài),處理完成后清除 Pending 狀態(tài),完成線程切換的所有工作并返回。如果當前 APC狀態(tài)完成的話,則恢復各寄存器和標志寄存器的值并返回,完成線程切換的所有工作。當調(diào)用 KiDispatchInterrupt() 函數(shù)時,:u KiDispatchInterrupt l 1000ntoskrnl!KiDispatchInterrupt0008:80467DD0 MOV EBX,[FFDFF01C]0008:80467DD6 LEA EAX,[EBX+00000800]得到當前 KPCR 自身結(jié)構(gòu)與DCP鏈表頭,EAX=DPC,EBX=KPCR0008:80467DDC CLI0008:80467DDD CMP EAX,[EAX]0008:80467DDF JZ 80467DFE關(guān)中斷,并比較當前 DPC 是否為空,如果為空,直接跳轉(zhuǎn)到比較 KPCR 中QuantumEnd 值和比較是否有下一個線程(ETHREAD)結(jié)構(gòu)0008:80467DE1 PUSH EBP0008:80467DE2 PUSH DWORD PTR [EBX]0008:80467DE4 MOV DWORD PTR [EBX],FFFFFFFF0008:80467DEA MOV EDX,ESP0008:80467DEC MOV ESP,[EBX+0000081C] 注釋: 得到 DPC 堆棧0008:80467DF2 PUSH EDX0008:80467DF3 MOV EBP,EAX0008:80467DF5 CALL 804633E7 注釋: KiRetireDpcList()函數(shù)0008:80467DFA POP ESP0008:80467DFB POP DWORD PTR [EBX]0008:80467DFD POP EBP設(shè)置 DPC 異常鏈0008:80467DFE STI0008:80467DFF CMP DWORD PTR [EBX+00000870],00 注釋: QuantumEnd 線程時間片0008:80467E06 JNZ 80467E5A如果 KPCR 中 QuantumEnd 值不為 0 則表示可能當前線程(ETHREAD)時間片沒有用完,跳轉(zhuǎn)到判斷當前線程(ETHREAD) 是否可以進行切換。0008:80467E08 CMP DWORD PTR [EBX+00000128],00 注釋: NextThread (ETHREAD結(jié)構(gòu))0008:80467E0F JZ 80467E59是否有下一個線程,如果 KPCR 中下一線程 NextThread(ETHREAD)偏移為空,則直接跳轉(zhuǎn)到返回地址,完成函數(shù)調(diào)用。0008:80467E11 MOV EAX,[EBX+00000128] 此時 EAX = NextThread (ETHREAD結(jié)構(gòu))0008:80467E17 SUB ESP,0C0008:80467E1A MOV [ESP+08],ESI0008:80467E1E MOV [ESP+04],EDI0008:80467E22 MOV [ESP],EBP0008:80467E25 MOV ESI,EAX 0008:80467E27 MOV EDI,[EBX+00000124] 注釋: CurrentThread (ETHREAD結(jié)構(gòu))0008:80467E2D MOV DWORD PTR [EBX+00000128],000000000008:80467E37 MOV [EBX+00000124],ESI0008:80467E3D MOV ECX,EDI0008:80467E3F CALL 8042F944 注釋: KiReadyThread函數(shù)把 KPCR 中下一個線程 NextThread (ETHREAD) 結(jié)構(gòu),替換成 KPCR 中當前 CurrentThread(ETHREAD)線程結(jié)構(gòu),并將 KPCR 中下一線程 NextThread (ETHREAD) 偏移清0,調(diào)用 KiReadyThread() 就緒剛剛設(shè)置好的下一線程 NextThread(ETHREAD) 結(jié)構(gòu),現(xiàn)在為當前線程 CurrentThread (ETHREAD) 結(jié)構(gòu)。0008:80467E44 MOV CL,01 設(shè)置 APC IRQL 標志,SwapContext() 調(diào)用會先設(shè)置當前IRQL 為 APC_LEVEL。0008:80467E46 CALL 80467E70 注釋: SwapContext函數(shù)0008:80467E4B MOV EBP,[ESP]0008:80467E4E MOV EDI,[ESP+04]0008:80467E52 MOV ESI,[ESP+08]0008:80467E56 ADD ESP,0C調(diào)用 SwapContext() 完成線程切換。(__fastcall 調(diào)用規(guī)范,平衡堆棧)0008:80467E59 RET 注釋: KiDispatchInterrupt 函數(shù)調(diào)用完畢,返回。0008:80467E5A MOV DWORD PTR [EBX+00000870],00000000 注釋: QuantumEnd 0008:80467E64 CALL 804306D2 注釋: KiQuantumEnd 0008:80467E69 OR EAX,EAX 注釋: EAX = NextThread(ETHREAD)0008:80467E6B JNZ 80467E17 0008:80467E6D RET 注釋: KiDispatchInterrupt 函數(shù)調(diào)用完畢,返回。設(shè)置 KPCR 中 QuantumEnd 值為 0,并調(diào)用 KiQuantumEnd 相應處理,KiQuantumEnd 函數(shù)會取當前線程(ETHREAD)結(jié)構(gòu)中的 Quantum 值來進行判斷是否為0,如果可以進行線程切換,則根據(jù)線程優(yōu)先等參數(shù)調(diào)用 KiFindReadyThread() 函數(shù)來選擇一個新的線程填充 KPCR 中的 NextThread (ETHREAD結(jié)構(gòu)),并將 NextThread (ETHREAD結(jié)構(gòu)) 賦給 EAX,并跳轉(zhuǎn)到線程切換地址進行切換。如果 EAX 返回為空,則表示無法進行切換,函數(shù)返回。_______________________________________________________________________________________ 以上基本把線程調(diào)度各分之走完,可以大概看出一個進程交替運行的流程,下面的 SwapContext 函數(shù)完成實際切換。SwapContext() EBX = KPCRESI = NextThreadEDI = CurrentThread0008:80467E6E MOV EDI,EDI0008:80467E70 OR CL,CL0008:80467E72 MOV BYTE PTR ES:[ESI+2D],02 (ETHREAD->State)設(shè)置要切換的新線程NextThread(ETHREAD)為運行狀態(tài)。0008:80467E77 PUSHFD0008:80467E78 MOV ECX,[EBX]0008:80467E7A CMP DWORD PTR [EBX+0000080C],000008:80467E81 PUSH ECX0008:80467E82 JNZ 80467F7D線程切換時不允許有 DPC 列程產(chǎn)生,所以先比較 KPCR 中的DpcRoutineActive 是否為 0,不為0則表示當前有 DPC 處理,跳轉(zhuǎn)到 KeBugCheck() 函數(shù)處顯示藍屏。0008:80467E88 MOV EBP,CR0 注釋:EBP = CR00008:80467E8B MOV EDX,EBP 注釋:EDX = CR0 0008:80467E8D MOV CL,[ESI+2C]0008:80467E90 MOV [EBX+50],CL取要切換的新線程NextThread(ETHREAD)調(diào)試標志狀態(tài),并賦給 KPCR 中DebugActive 相應標志。0008:80467E93 CLI0008:80467E94 MOV [EDI+28],ESP 將堆棧指針賦給要被切換的舊線程 CurrentThread(ETHREAD)中的 KernelStack。0008:80467E97 MOV EAX,[ESI+18]0008:80467E9A MOV ECX,[ESI+1C]0008:80467E9D SUB EAX,000002100008:80467EA2 MOV [EBX+08],ECX0008:80467EA5 MOV [EBX+04],EAX將要切換的新線程NextThread(ETHREAD)中的初始化堆棧(InitalStack)與堆棧大小(StackLimit)賦給 KPCR 中的相應位置,以便處理。0008:80467EA8 XOR ECX,ECX0008:80467EAA MOV CL,[ESI+31]0008:80467EAD AND EDX,-0F0008:80467EB0 OR ECX,EDX0008:80467EB2 OR ECX,[EAX+0000020C]0008:80467EB8 CMP EBP,ECX0008:80467EBA JNZ 80467F75取要切換的新線程NextThread(ETHREAD)中的 NPX 位與 CR0 的 NPX 位進行比較,如不相等跳轉(zhuǎn)到重新設(shè)置 CR0 處進行處理。0008:80467EC0 TEST DWORD PTR [EAX-1C],000200000008:80467EC7 JNZ 80467ECC判斷當前是否為 V86模式,如果不是直接跳到取得 KTSS 處。0008:80467EC9 SUB EAX,10如果是 V86 模式則繼續(xù)調(diào)整內(nèi)核堆??臻g。0008:80467ECC MOV ECX,[EBX+40] 注釋:ECX = KPCR->KTSS0008:80467ECF MOV [ECX+04],EAX0008:80467ED2 MOV ESP,[ESI+28] 0008:80467ED5 MOV EAX,[ESI+20]0008:80467ED8 MOV [EBX+18],EAX 注釋:EAX = TEB從 KPCR 中得到 KTSS 地址,并將 NPX 位保存到 KTSS 中的 ESP0 處,取要切換的新線程NextThread(ETHREAD)的內(nèi)核堆棧(KernelStack )賦與ESP,并設(shè)置 KPCR 中的用戶堆棧(TEB) 為要切換的新線程NextThread(ETHREAD)的用戶堆棧(TEB)。0008:80467EDB STI0008:80467EDC MOV ECX,[EBX+3C] 注釋:ECX = KPCR->GDT 0008:80467EDF MOV [ECX+3A],AX0008:80467EE3 SHR EAX,100008:80467EE6 MOV [ECX+3C],AL0008:80467EE9 SHR EAX,080008:80467EEC MOV [ECX+3F],AL0008:80467EEF MOV EAX,[EDI+44]0008:80467EF2 CMP EAX,[ESI+44]0008:80467EF5 JZ 80467F20將用戶堆棧(TEB)放入 KPCR 中 GDT 的相應結(jié)構(gòu)中。比較要被切換的舊線程 CurrentThread(ETHREAD)中的 EPROCESS,與要切換的新線程NextThread(ETHREAD)中的 EPROCESS 是否相等?也就是判斷要切換的是否為當前進程,如果是則不設(shè)置當前頁目錄表(CR3)以及其他相關(guān)值,而直接跳轉(zhuǎn)到添加當前進程切換計數(shù)與判斷當前線程是否存在 Pending 的 APC 調(diào)用處,然后退出,完成切換。0008:80467EF7 MOV EDI,[ESI+44] 注釋:EDI = NextThread->EPROCESS0008:80467EFA XOR EAX,EAX0008:80467EFC MOV GS,AX0008:80467EFF MOV EAX,[EDI+18] 注釋:EAX = EPROCESS->DirectoryTableBase0008:80467F02 MOV EBP,[EBX+40] 注釋:EBP = KPCR->KTSS0008:80467F05 MOV ECX,[EDI+30] 注釋:EDI = EPROCESS->IopmOffset0008:80467F08 MOV [EBP+1C],EAX0008:80467F0B MOV CR3,EAX 注釋:CR3 = EPROCESS->DirectoryTableBase0008:80467F0E MOV [EBP+66],CX0008:80467F12 XOR EAX,EAX0008:80467F14 CMP [EDI+20],AX 0008:80467F18 JNZ 80467F470008:80467F1A LLDT AX0008:80467F1D LEA ECX,[ECX+00]如果要切換的線程不是當前進程,則從要切換的新線程NextThread(ETHREAD)中取當前進程(EPROCESS),并從當前進程(EPROCESS)中得到頁目錄表來更新KPCR->KTSS中的TSSCR3的值與 CR3 寄存器中的值,繼續(xù)將當前進程(EPROCESS)中IOPM 值賦與 KPCR-KTSS 中的 IOPM。再比較當前進程(EPROCESS)中的 LDT 是否為空,如果不為空則跳轉(zhuǎn)到設(shè)置 LDT 處執(zhí)行。否則設(shè)置 LDT 為空。0008:80467F20 INC DWORD PTR [ESI+4C]0008:80467F23 INC DWORD PTR [EBX+000005C0]0008:80467F29 POP ECX0008:80467F2A MOV [EBX],ECX0008:80467F2C CMP BYTE PTR [ESI+49],000008:80467F30 JNZ 80467F360008:80467F32 POPFD0008:80467F33 XOR EAX,EAX0008:80467F35 RET 注釋: SwapContext 函數(shù)調(diào)用完畢,返回。把要切換的新線程NextThread(ETHREAD)中的 ContextSwitches 與 KPCR 中的KeContextSwitches 各加一,這兩個值表示進行線程切換的次數(shù)?;謴彤惓f?,并比較要切換的新線程NextThread(ETHREAD)中的 KernelApcPending,如果當前APC 狀態(tài)是 Pending 的話則跳轉(zhuǎn)到處理 APC Pending 地址繼續(xù)。不為 Pending 的話,則恢復各寄存器和標志寄存器的值并返回,完成線程切換的所有工作。0008:80467F36 POPFD0008:80467F37 JNZ 80467F3C0008:80467F39 MOV AL,010008:80467F3B RET判斷當前是否可以處理 APC 調(diào)用,如果不能則設(shè)置返回標志為 Pending 完成線程切換的所有工作并返回。否則跳轉(zhuǎn)到處理軟中斷地址繼續(xù)。0008:80467F3C MOV CL,01 注釋: IRQL = APC LEVEL0008:80467F3E CALL [HAL!HalRequestSoftwareInterrupt]0008:80467F44 XOR EAX,EAX0008:80467F46 RET設(shè)置當前 IRQL 為 APC LEVEL 并調(diào)用 HalRequestSoftwareInterrupt() 函數(shù)來處理APC Pending 狀態(tài),處理完成后清除 Pending 狀態(tài),完成線程切換的所有工作并返回。0008:80467F47 MOV EBP,[EBX+3C] 注釋: EBP = KPCR->KGDT0008:80467F4A MOV EAX,[EDI+20] 注釋: EAX = EPROCESS->LdtDescriptor0008:80467F4D MOV [EBP+48],EAX0008:80467F50 MOV EAX,[EDI+24]0008:80467F53 MOV [EBP+4C],EAX0008:80467F56 MOV EAX,000000480008:80467F5B MOV EBP,[EBX+38] 注釋: EBP = KPCR->KIDT0008:80467F5E MOV ECX,[EDI+28] 注釋: ECX = EPROCESS->Int21Descriptor0008:80467F61 MOV [EBP+00000108],ECX0008:80467F67 MOV ECX,[EDI+2C]0008:80467F6A MOV [EBP+0000010C],ECX0008:80467F70 LLDT AX0008:80467F73 JMP 80467F20從 KPCR 中取得 KGDT 的位置,并從 KGDT 中索引到 LDT,把當前進程(EPROCESS)中的LDT 賦與 KPCR 中的 LDT。再得到 KPCR 中 KIDT 的位置,把當前進程(EPROCESS)中的INT 21中斷賦與 KPCR 中 KIDT 中的相應位置,使當前進程可以調(diào)用 INT 21 。最后調(diào)用LLDT 使當前所有設(shè)置生效后跳轉(zhuǎn)回添加線程切換次數(shù)與判斷當前 APC Pending 處繼續(xù)運行。0008:80467F75 MOV CR0,ECX0008:80467F78 JMP 80467EC0重新設(shè)置 CR0 的 NPX 位,并向上跳轉(zhuǎn)到判斷是否為 V86 模式處繼續(xù)。0008:80467F7D PUSH 000000B80008:80467F82 CALL ntoskrnl!KeBugCheck0008:80467F87 RET調(diào)用藍屏函數(shù),系統(tǒng)崩潰重新啟動。 筆記是今年春節(jié)利用放假時間寫的,當時分析到一半才發(fā)現(xiàn)原來 WIN2K 源代碼中已經(jīng)包含了此部分,無奈已經(jīng)把匯編進行了簡單的注釋,索性就這樣寫下去。錯誤之處再所難免,還望得到您的指正。參考資源: Windows 2000 源代碼感謝 FlashSky,SoBeIt 與我探討。 |
聯(lián)系客服