寫在前面
此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。由於系統核心的複雜性,故可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我。
你如果是從中間插過來看的,請仔細閱讀 羽夏看Win系統核心——簡述 ,方便學習本教程。
看此教程之前,問一個問題,你明確學系統呼叫的目的了嗎? 沒有的話就不要繼續了,請重新學習 羽夏看Win系統核心——系統呼叫篇 裡面的內容。
? 華麗的分割線 ?
練習及參考
本次答案均為參考,可以與我的答案不一致,但必須成功通過。
1️⃣ 自己編寫WriteProcessMemory
函式(不使用任何DLL
,直接呼叫0環函式)並在程式碼中使用。
? 點選檢視答案 ?
效果和上篇教程一樣。不過這個函式比起ReadProcessMemory
稍微複雜一點點,這個函式會先使用NtProtectVirtualMemory
判斷是否可寫,然後呼叫NtWriteVirtualMemory
寫。因為我直接提供可寫的變數,故這一步就可以省了,程式碼見摺疊。
? 點選檢視程式碼 ?
#include "stdafx.h"
#include <windows.h>
#include <iostream>
int test=0;
BOOL __declspec(naked) __stdcall WriteProcMem0(DWORD handle,DWORD addr,unsigned char* buffer,DWORD len,DWORD sizewrite)
{
_asm
{
mov eax, 115h ;
mov edx, 7FFE0300h;
call dword ptr [edx];
retn 14h;
}
}
BOOL __declspec(naked) __stdcall WriteProcMem1(DWORD handle,DWORD addr,unsigned char* buffer,DWORD len,DWORD sizewrite)
{
_asm
{
mov eax, 115h;
lea edx, [esp+4];
int 2Eh;
retn 14h;
}
}
int main(int argc, char* argv[])
{
int buffer = 0x1234;
WriteProcMem0((DWORD)GetCurrentProcess(),(DWORD)&test,(unsigned char*)&buffer,4,NULL);
printf("第一次的值為:%x\n",test);
buffer=0x5678;
WriteProcMem1((DWORD)GetCurrentProcess(),(DWORD)&test,(unsigned char*)&buffer,4,NULL);
printf("第二次的值為:%x\n",test);
system("pause");
return 0;
}
詳解 sysenter
上一篇介紹了Windows API
在0環層面呼叫過程,並且只是提了一嘴sysenter
,下面來詳細介紹一下這條彙編指令的功能。
在執行sysenter
指令之前,作業系統必須指定0環的CS
、SS
、EIP
以及ESP
。而它們的值儲存在MSR
暫存器中,它的具體結構未公開。每個MSR
暫存器都會有一個相應的ID
,即MSR Index
。當執行RDMSR
或者WRMSR
指令的時候,只要提供MSR Index
就能讓CPU
知道目標MSR
暫存器。儲存上述暫存器的MSR Index
如下表格所示:
MSR | Index |
---|---|
IA32_SYSENTER_CS | 174H |
IA32_SYSENTER_ESP | 175H |
IA32_SYSENTER_EIP | 176H |
看完表格後,你會發現沒有儲存SS
的MSR
暫存器,它是經過計算得到:CS
的值+8就是SS
的值。我們測試一下它們的讀取:
kd> rdmsr 174
msr[174] = 00000000`00000008
kd> rdmsr 175
msr[175] = 00000000`bacd0000
kd> rdmsr 176
msr[176] = 00000000`8053e540
如果呼叫sysenter
進入核心,前往的地址就是8053e540
,我們看看函式是什麼:
kd> uf 8053e540
Flow analysis was incomplete, some code may be missing
8053e540 b923000000 mov ecx,23h
8053e545 6a30 push 30h
8053e547 0fa1 pop fs
8053e549 8ed9 mov ds,cx
8053e54b 8ec1 mov es,cx
8053e54d 8b0d40f0dfff mov ecx,dword ptr ds:[0FFDFF040h]
8053e553 8b6104 mov esp,dword ptr [ecx+4]
8053e556 6a23 push 23h
8053e558 52 push edx
8053e559 9c pushfd
8053e55a 6a02 push 2
8053e55c 83c208 add edx,8
8053e55f 9d popfd
8053e560 804c240102 or byte ptr [esp+1],2
8053e565 6a1b push 1Bh
8053e567 ff350403dfff push dword ptr ds:[0FFDF0304h]
8053e56d 6a00 push 0
8053e56f 55 push ebp
8053e570 53 push ebx
8053e571 56 push esi
8053e572 57 push edi
8053e573 8b1d1cf0dfff mov ebx,dword ptr ds:[0FFDFF01Ch]
8053e579 6a3b push 3Bh
8053e57b 8bb324010000 mov esi,dword ptr [ebx+124h]
8053e581 ff33 push dword ptr [ebx]
8053e583 c703ffffffff mov dword ptr [ebx],0FFFFFFFFh
8053e589 8b6e18 mov ebp,dword ptr [esi+18h]
8053e58c 6a01 push 1
8053e58e 83ec48 sub esp,48h
8053e591 81ed9c020000 sub ebp,29Ch
8053e597 c6864001000001 mov byte ptr [esi+140h],1
8053e59e 3bec cmp ebp,esp
8053e5a0 759a jne nt!KiFastCallEntry2+0x47 (8053e53c) Branch
雖然沒有顯示出函式是什麼,我們可以通過PE
的知識進行定位,經過工具檢視它的檔案對齊和記憶體對齊是一樣的,那就相當的方便了:
首先,我們得知道核心檔案的地址:
kd> dd PsLoadedModuleList
80554fc0 89e033a0 898dbd70 00000000 00000000
80554fd0 00000000 00000000 00000000 00000000
80554fe0 8055c460 80559680 00000000 00000000
80554ff0 00000000 00000000 00000000 00000000
80555000 00000000 00000000 00000000 00000000
80555010 00000000 00000000 80545500 80544e00
80555020 80544e00 80543c00 80546800 80545500
80555030 00000000 00000011 00000011 89bbfb50
kd> dt _LDR_DATA_TABLE_ENTRY 89e033a0
nt!_LDR_DATA_TABLE_ENTRY
+0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x89e03338 - 0x80554fc0 ]
+0x008 InMemoryOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x010 InInitializationOrderLinks : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x018 DllBase : 0x804d8000 Void
+0x01c EntryPoint : 0x806907e0 Void
+0x020 SizeOfImage : 0x1f8480
+0x024 FullDllName : _UNICODE_STRING "\WINDOWS\system32\ntkrnlpa.exe"
+0x02c BaseDllName : _UNICODE_STRING "ntoskrnl.exe"
+0x034 Flags : 0xc004000
+0x038 LoadCount : 1
+0x03a TlsIndex : 0
+0x03c HashLinks : _LIST_ENTRY [ 0x0 - 0x2050d3 ]
+0x03c SectionPointer : (null)
+0x040 CheckSum : 0x2050d3
+0x044 TimeDateStamp : 0
+0x044 LoadedImports : (null)
+0x048 EntryPointActivationContext : (null)
+0x04c PatchInformation : 0x0074006e Void
這個東西在核心篇的總結與提升講到過,我就不詳細講解了。既然得到了目標地址,模組的首地址也都得到了。經過相減得到0x66540
,由於IDA
預設是以ImageBase
為首地址載入,故為0x466540
,如所示:
核心篇的總結與提升暫未釋出,由於其細節和內容很多,釋出後本段文字將會刪除。這裡透露一下里面將會介紹什麼:核心檔案載入和解除安裝過程;進一步隱藏核心檔案繞過
PCHunter
檢測;使用程式碼實現驅動檔案的載入,同時還會把PCHunter
的驅動載入過程(經過逆向所得)進行介紹;跨程式監控MessageBox
專案的實現。不知這些東西何時能夠完成釋出,敬請期待。
.text:00466540 _KiFastCallEntry proc near ; DATA XREF: KiLoadFastSyscallMachineSpecificRegisters(x)+24↑o
.text:00466540 ; _KiTrap01+72↓o
.text:00466540
.text:00466540 var_B = byte ptr -0Bh
.text:00466540
.text:00466540 ; FUNCTION CHUNK AT .text:00466519 SIZE 00000025 BYTES
.text:00466540 ; FUNCTION CHUNK AT .text:004667DC SIZE 00000014 BYTES
.text:00466540
.text:00466540 mov ecx, 23h ; '#'
.text:00466545 push 30h ; '0'
.text:00466547 pop fs
.text:00466549 mov ds, ecx
.text:0046654B mov es, ecx
.text:0046654D mov ecx, ds:0FFDFF040h
.text:00466553 mov esp, [ecx+4]
.text:00466556 push 23h ; '#'
.text:00466558 push edx
.text:00466559 pushf
然後看看我們之前的uf
的結果,是不是一模一樣?
接下來繼續分析通過中斷門0x2E
的進入核心這道門的過程,先找到對應的中斷門:
kd> dq 8003f400+2e*8
ReadVirtual: 8003f570 not properly sign extended
8003f570 8053ee00`0008e481 80548e00`00081780
8003f580 806d8e00`00087d54 89db8e00`00082784
8003f590 80538e00`0008db54 80538e00`0008db5e
8003f5a0 80538e00`0008db68 80538e00`0008db72
8003f5b0 80538e00`0008db7c 80538e00`0008db86
8003f5c0 806d8e00`00081ef0 89d88e00`0008e41c
8003f5d0 89ab8e00`0008815c 89d88e00`0008add4
8003f5e0 89ab8e00`00088584 80538e00`0008dbc2
我們找到了中斷門對應的地址為8053e481
,如法炮製,我們定位到IDA
:
.text:00466481 _KiSystemService proc near ; CODE XREF: ZwAcceptConnectPort(x,x,x,x,x,x)+C↑p
.text:00466481 ; ZwAccessCheck(x,x,x,x,x,x,x,x)+C↑p ...
.text:00466481
.text:00466481 arg_0 = dword ptr 4
.text:00466481
.text:00466481 push 0
.text:00466483 push ebp
.text:00466484 push ebx
.text:00466485 push esi
.text:00466486 push edi
.text:00466487 push fs
.text:00466489 mov ebx, 30h ; '0'
.text:0046648E mov fs, bx
.text:00466491 assume fs:nothing
.text:00466491 push dword ptr ds:0FFDFF000h
.text:00466497 mov dword ptr ds:0FFDFF000h, 0FFFFFFFFh
.text:004664A1 mov esi, ds:0FFDFF124h
.text:004664A7 push dword ptr [esi+140h]
.text:004664AD sub esp, 48h
.text:004664B0 mov ebx, [esp+68h+arg_0]
.text:004664B4 and ebx, 1
.text:004664B7 mov [esi+140h], bl
.text:004664BD mov ebp, esp
.text:004664BF mov ebx, [esi+134h]
.text:004664C5 mov [ebp+3Ch], ebx
.text:004664C8 mov [esi+134h], ebp
.text:004664CE cld
進入0環的過程我們可以可以總結一下:
通過中斷門進0環時,固定中斷號為0x2E
,它的CS
/EIP
由門描述符提供,ESP
/SS
由TSS
提供;進入0環後執行的核心函式為NT!KiSystemService
。
通過sysenter
指令進0環時,CS
/ESP
/EIP
由MSR
暫存器提供,SS
是計算得到的;進入0環後執行的核心函式為NT!KiFastCallEntry
。
進入0環之後
我們通過int 2E
或者sysenter
進入核心後,我們的流程會怎樣繼續進行,首先我們得知道個結構體,它就是KPCR
和Trap_Frame
。
Trap_Frame
棧幀結構體,用於Windows API
儲存現場。經過提權進入0環的時候,Windows
就會遵守這個結構體儲存一系列的資料,它的結構體如下所示:
kd> dt _KTrap_Frame
nt!_KTRAP_FRAME
+0x000 DbgEbp : Uint4B
+0x004 DbgEip : Uint4B
+0x008 DbgArgMark : Uint4B
+0x00c DbgArgPointer : Uint4B
+0x010 TempSegCs : Uint4B
+0x014 TempEsp : Uint4B
+0x018 Dr0 : Uint4B
+0x01c Dr1 : Uint4B
+0x020 Dr2 : Uint4B
+0x024 Dr3 : Uint4B
+0x028 Dr6 : Uint4B
+0x02c Dr7 : Uint4B
+0x030 SegGs : Uint4B
+0x034 SegEs : Uint4B
+0x038 SegDs : Uint4B
+0x03c Edx : Uint4B
+0x040 Ecx : Uint4B
+0x044 Eax : Uint4B
+0x048 PreviousPreviousMode : Uint4B
+0x04c ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x050 SegFs : Uint4B
+0x054 Edi : Uint4B
+0x058 Esi : Uint4B
+0x05c Ebx : Uint4B
+0x060 Ebp : Uint4B
+0x064 ErrCode : Uint4B
+0x068 Eip : Uint4B
+0x06c SegCs : Uint4B
+0x070 EFlags : Uint4B
+0x074 HardwareEsp : Uint4B
+0x078 HardwareSegSs : Uint4B
+0x07c V86Es : Uint4B
+0x080 V86Ds : Uint4B
+0x084 V86Fs : Uint4B
+0x088 V86Gs : Uint4B
下面幾個成員用於虛擬8086模式下,由於我們們研究是保護模式,不使用,故不贅述:
+0x07c V86Es : Uint4B
+0x080 V86Ds : Uint4B
+0x084 V86Fs : Uint4B
+0x088 V86Gs : Uint4B
中斷髮生時,若發生許可權變換,則要儲存舊堆疊,由CPU
壓入的。如下兩個成員儲存:
+0x074 HardwareEsp : Uint4B
+0x078 HardwareSegSs : Uint4B
中斷髮生時,儲存被中斷的程式碼段和iret
要返回的地址,由CPU
壓入的:
+0x068 Eip : Uint4B
+0x06c SegCs : Uint4B
+0x070 EFlags : Uint4B
Windows
中非易失性暫存器需要在中斷例程中先儲存(ErrCode
有時由作業系統壓入,有時由CPU
壓入,詳情請參考本篇教程的中斷門涉及的中斷號部分):
+0x048 PreviousPreviousMode : Uint4B
+0x04c ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x050 SegFs : Uint4B
+0x054 Edi : Uint4B
+0x058 Esi : Uint4B
+0x05c Ebx : Uint4B
+0x060 Ebp : Uint4B
+0x064 ErrCode : Uint4B
用於除錯以及其他用途:
+0x000 DbgEbp : Uint4B
+0x004 DbgEip : Uint4B
+0x008 DbgArgMark : Uint4B
+0x00c DbgArgPointer : Uint4B
+0x010 TempSegCs : Uint4B
+0x014 TempEsp : Uint4B
+0x018 Dr0 : Uint4B
+0x01c Dr1 : Uint4B
+0x020 Dr2 : Uint4B
+0x024 Dr3 : Uint4B
+0x028 Dr6 : Uint4B
+0x02c Dr7 : Uint4B
+0x030 SegGs : Uint4B
+0x034 SegEs : Uint4B
+0x038 SegDs : Uint4B
+0x03c Edx : Uint4B
+0x040 Ecx : Uint4B
+0x044 Eax : Uint4B
由於裡面涉及的知識點比較廣,故不詳細講解,之後的教程將會逐個體現出來。
KPCR
我們先從WinDbg
檢視一下這個結構體:
kd> dt _KPCR
nt!_KPCR
+0x000 NtTib : _NT_TIB
+0x01c SelfPcr : Ptr32 _KPCR
+0x020 Prcb : Ptr32 _KPRCB
+0x024 Irql : UChar
+0x028 IRR : Uint4B
+0x02c IrrActive : Uint4B
+0x030 IDR : Uint4B
+0x034 KdVersionBlock : Ptr32 Void
+0x038 IDT : Ptr32 _KIDTENTRY
+0x03c GDT : Ptr32 _KGDTENTRY
+0x040 TSS : Ptr32 _KTSS
+0x044 MajorVersion : Uint2B
+0x046 MinorVersion : Uint2B
+0x048 SetMember : Uint4B
+0x04c StallScaleFactor : Uint4B
+0x050 DebugActive : UChar
+0x051 Number : UChar
+0x052 Spare0 : UChar
+0x053 SecondLevelCacheAssociativity : UChar
+0x054 VdmAlert : Uint4B
+0x058 KernelReserved : [14] Uint4B
+0x090 SecondLevelCacheSize : Uint4B
+0x094 HalReserved : [16] Uint4B
+0x0d4 InterruptMode : Uint4B
+0x0d8 Spare1 : UChar
+0x0dc KernelReserved2 : [17] Uint4B
+0x120 PrcbData : _KPRCB
可以看出KPCR
裡面還巢狀著兩個結構體TIB
和KPRCB
,我們dt
一下:
kd> dt _NT_TIB
nt!_NT_TIB
+0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 StackBase : Ptr32 Void
+0x008 StackLimit : Ptr32 Void
+0x00c SubSystemTib : Ptr32 Void
+0x010 FiberData : Ptr32 Void
+0x010 Version : Uint4B
+0x014 ArbitraryUserPointer : Ptr32 Void
+0x018 Self : Ptr32 _NT_TIB
kd> dt _KPRCB
nt!_KPRCB
+0x000 MinorVersion : Uint2B
+0x002 MajorVersion : Uint2B
+0x004 CurrentThread : Ptr32 _KTHREAD
+0x008 NextThread : Ptr32 _KTHREAD
+0x00c IdleThread : Ptr32 _KTHREAD
+0x010 Number : Char
+0x011 Reserved : Char
+0x012 BuildType : Uint2B
+0x014 SetMember : Uint4B
+0x018 CpuType : Char
+0x019 CpuID : Char
+0x01a CpuStep : Uint2B
+0x01c ProcessorState : _KPROCESSOR_STATE
+0x33c KernelReserved : [16] Uint4B
+0x37c HalReserved : [16] Uint4B
+0x3bc PrcbPad0 : [92] UChar
+0x418 LockQueue : [16] _KSPIN_LOCK_QUEUE
+0x498 PrcbPad1 : [8] UChar
+0x4a0 NpxThread : Ptr32 _KTHREAD
+0x4a4 InterruptCount : Uint4B
+0x4a8 KernelTime : Uint4B
+0x4ac UserTime : Uint4B
+0x4b0 DpcTime : Uint4B
+0x4b4 DebugDpcTime : Uint4B
+0x4b8 InterruptTime : Uint4B
+0x4bc AdjustDpcThreshold : Uint4B
+0x4c0 PageColor : Uint4B
+0x4c4 SkipTick : Uint4B
+0x4c8 MultiThreadSetBusy : UChar
+0x4c9 Spare2 : [3] UChar
+0x4cc ParentNode : Ptr32 _KNODE
+0x4d0 MultiThreadProcessorSet : Uint4B
+0x4d4 MultiThreadSetMaster : Ptr32 _KPRCB
+0x4d8 ThreadStartCount : [2] Uint4B
+0x4e0 CcFastReadNoWait : Uint4B
+0x4e4 CcFastReadWait : Uint4B
+0x4e8 CcFastReadNotPossible : Uint4B
+0x4ec CcCopyReadNoWait : Uint4B
+0x4f0 CcCopyReadWait : Uint4B
+0x4f4 CcCopyReadNoWaitMiss : Uint4B
+0x4f8 KeAlignmentFixupCount : Uint4B
+0x4fc KeContextSwitches : Uint4B
+0x500 KeDcacheFlushCount : Uint4B
+0x504 KeExceptionDispatchCount : Uint4B
+0x508 KeFirstLevelTbFills : Uint4B
+0x50c KeFloatingEmulationCount : Uint4B
+0x510 KeIcacheFlushCount : Uint4B
+0x514 KeSecondLevelTbFills : Uint4B
+0x518 KeSystemCalls : Uint4B
+0x51c SpareCounter0 : [1] Uint4B
+0x520 PPLookasideList : [16] _PP_LOOKASIDE_LIST
+0x5a0 PPNPagedLookasideList : [32] _PP_LOOKASIDE_LIST
+0x6a0 PPPagedLookasideList : [32] _PP_LOOKASIDE_LIST
+0x7a0 PacketBarrier : Uint4B
+0x7a4 ReverseStall : Uint4B
+0x7a8 IpiFrame : Ptr32 Void
+0x7ac PrcbPad2 : [52] UChar
+0x7e0 CurrentPacket : [3] Ptr32 Void
+0x7ec TargetSet : Uint4B
+0x7f0 WorkerRoutine : Ptr32 void
+0x7f4 IpiFrozen : Uint4B
+0x7f8 PrcbPad3 : [40] UChar
+0x820 RequestSummary : Uint4B
+0x824 SignalDone : Ptr32 _KPRCB
+0x828 PrcbPad4 : [56] UChar
+0x860 DpcListHead : _LIST_ENTRY
+0x868 DpcStack : Ptr32 Void
+0x86c DpcCount : Uint4B
+0x870 DpcQueueDepth : Uint4B
+0x874 DpcRoutineActive : Uint4B
+0x878 DpcInterruptRequested : Uint4B
+0x87c DpcLastCount : Uint4B
+0x880 DpcRequestRate : Uint4B
+0x884 MaximumDpcQueueDepth : Uint4B
+0x888 MinimumDpcRate : Uint4B
+0x88c QuantumEnd : Uint4B
+0x890 PrcbPad5 : [16] UChar
+0x8a0 DpcLock : Uint4B
+0x8a4 PrcbPad6 : [28] UChar
+0x8c0 CallDpc : _KDPC
+0x8e0 ChainedInterruptList : Ptr32 Void
+0x8e4 LookasideIrpFloat : Int4B
+0x8e8 SpareFields0 : [6] Uint4B
+0x900 VendorString : [13] UChar
+0x90d InitialApicId : UChar
+0x90e LogicalProcessorsPerPhysicalProcessor : UChar
+0x910 MHz : Uint4B
+0x914 FeatureBits : Uint4B
+0x918 UpdateSignature : _LARGE_INTEGER
+0x920 NpxSaveArea : _FX_SAVE_AREA
+0xb30 PowerState : _PROCESSOR_POWER_STATE
KPCR
叫做CPU
控制區,英文全稱Processor Control Region
,每一個CPU
都有一個這樣的結構體,我們如何知道我們有幾個CPU
,也就是幾個核呢?我們可以用下面的指令得到:
kd> dd KeNumberProcessors
8054d4e0 00000001 00000006 00009e0a 20013fff
8054d4f0 806bb7c0 00000000 00000000 00000061
8054d500 8003f0e0 00000000 00000000 00000000
8054d510 00000001 00000000 00000001 00000000
8054d520 00000000 00000000 00000000 00000000
8054d530 00000000 00000000 00000000 00000000
8054d540 00000000 00000000 00000000 00000000
8054d550 00000000 00000000 00000000 00000000
可以看出我們只有一個CPU
,那麼如何檢視KPCR
呢?我們可以用下面的指令得到:
kd> dd KiProcessorBlock
80553e40 ffdff120 00000000 00000000 00000000
80553e50 00000000 00000000 00000000 00000000
80553e60 00000000 00000000 00000000 00000000
80553e70 00000000 00000000 00000000 00000000
80553e80 00000000 00000000 00000000 00000000
80553e90 00000000 00000000 00000000 00000000
80553ea0 00000000 00000000 00000000 00000000
80553eb0 00000000 00000000 00000000 00000000
KiProcessorBlock
是一個陣列,它儲存著KPRCB
的地址。既然我知道KPRCB
的地址,通過這個減去偏移我們就可以找到KPCR
這個結構:
kd> dt _KPCR ffdff000
nt!_KPCR
+0x000 NtTib : _NT_TIB
+0x01c SelfPcr : 0xffdff000 _KPCR
+0x020 Prcb : 0xffdff120 _KPRCB
+0x024 Irql : 0x1c ''
+0x028 IRR : 4
+0x02c IrrActive : 0
+0x030 IDR : 0xffff20f8
+0x034 KdVersionBlock : 0x80546ab8 Void
+0x038 IDT : 0x8003f400 _KIDTENTRY
+0x03c GDT : 0x8003f000 _KGDTENTRY
+0x040 TSS : 0x80042000 _KTSS
+0x044 MajorVersion : 1
+0x046 MinorVersion : 1
+0x048 SetMember : 1
+0x04c StallScaleFactor : 0x64
+0x050 DebugActive : 0 ''
+0x051 Number : 0 ''
+0x052 Spare0 : 0 ''
+0x053 SecondLevelCacheAssociativity : 0 ''
+0x054 VdmAlert : 0
+0x058 KernelReserved : [14] 0
+0x090 SecondLevelCacheSize : 0
+0x094 HalReserved : [16] 0
+0x0d4 InterruptMode : 0
+0x0d8 Spare1 : 0 ''
+0x0dc KernelReserved2 : [17] 0
+0x120 PrcbData : _KPRCB
接下來看看比較重要的成員:
ExceptionList
錯誤連結串列,指向EXCEPTION_REGISTRATION_RECORD
的列表,用於SEH
,即為結構化異常處理,裡面記錄了異常處理函式。有人會稱它為SEH
鏈入口。
CurrentThread
當前CPU
所執行執行緒的ETHREAD
結構體。
NextThread
下一個CPU
所執行執行緒的ETHREAD
結構體。
IdleThread
當所有的執行緒都執行完了CPU
就執行這個執行緒。
Number
CPU
編號。
CpuStep
CPU
子版本號。
ProcessorState
CPU
狀態,是_KPROCESSOR_STATE
結構體。
NpxThread
Npx
浮點處理器,最後一次用過浮點的執行緒。
InterruptCount
中斷計數,統計資訊,沒什麼實際意義。
KernelTime
核心時間,統計資訊,沒什麼實際意義。
UserTime
使用者層時間,,統計資訊,沒什麼實際意義。
LogicalProcessorsPerPhysicalProcessor
指明每個物理處理器有幾個邏輯處理器。
MHz
CPU
的頻率。
ETHREAD
介紹KPCR
涉及了KTHREAD
結構體,故把這個ETHREAD
結構體進行介紹一下:
kd> dt _ETHREAD
nt!_ETHREAD
+0x000 Tcb : _KTHREAD
+0x1c0 CreateTime : _LARGE_INTEGER
+0x1c0 NestedFaultCount : Pos 0, 2 Bits
+0x1c0 ApcNeeded : Pos 2, 1 Bit
+0x1c8 ExitTime : _LARGE_INTEGER
+0x1c8 LpcReplyChain : _LIST_ENTRY
+0x1c8 KeyedWaitChain : _LIST_ENTRY
+0x1d0 ExitStatus : Int4B
+0x1d0 OfsChain : Ptr32 Void
+0x1d4 PostBlockList : _LIST_ENTRY
+0x1dc TerminationPort : Ptr32 _TERMINATION_PORT
+0x1dc ReaperLink : Ptr32 _ETHREAD
+0x1dc KeyedWaitValue : Ptr32 Void
+0x1e0 ActiveTimerListLock : Uint4B
+0x1e4 ActiveTimerListHead : _LIST_ENTRY
+0x1ec Cid : _CLIENT_ID
+0x1f4 LpcReplySemaphore : _KSEMAPHORE
+0x1f4 KeyedWaitSemaphore : _KSEMAPHORE
+0x208 LpcReplyMessage : Ptr32 Void
+0x208 LpcWaitingOnPort : Ptr32 Void
+0x20c ImpersonationInfo : Ptr32 _PS_IMPERSONATION_INFORMATION
+0x210 IrpList : _LIST_ENTRY
+0x218 TopLevelIrp : Uint4B
+0x21c DeviceToVerify : Ptr32 _DEVICE_OBJECT
+0x220 ThreadsProcess : Ptr32 _EPROCESS
+0x224 StartAddress : Ptr32 Void
+0x228 Win32StartAddress : Ptr32 Void
+0x228 LpcReceivedMessageId : Uint4B
+0x22c ThreadListEntry : _LIST_ENTRY
+0x234 RundownProtect : _EX_RUNDOWN_REF
+0x238 ThreadLock : _EX_PUSH_LOCK
+0x23c LpcReplyMessageId : Uint4B
+0x240 ReadClusterSize : Uint4B
+0x244 GrantedAccess : Uint4B
+0x248 CrossThreadFlags : Uint4B
+0x248 Terminated : Pos 0, 1 Bit
+0x248 DeadThread : Pos 1, 1 Bit
+0x248 HideFromDebugger : Pos 2, 1 Bit
+0x248 ActiveImpersonationInfo : Pos 3, 1 Bit
+0x248 SystemThread : Pos 4, 1 Bit
+0x248 HardErrorsAreDisabled : Pos 5, 1 Bit
+0x248 BreakOnTermination : Pos 6, 1 Bit
+0x248 SkipCreationMsg : Pos 7, 1 Bit
+0x248 SkipTerminationMsg : Pos 8, 1 Bit
+0x24c SameThreadPassiveFlags : Uint4B
+0x24c ActiveExWorker : Pos 0, 1 Bit
+0x24c ExWorkerCanWaitUser : Pos 1, 1 Bit
+0x24c MemoryMaker : Pos 2, 1 Bit
+0x250 SameThreadApcFlags : Uint4B
+0x250 LpcReceivedMsgIdValid : Pos 0, 1 Bit
+0x250 LpcExitThreadCalled : Pos 1, 1 Bit
+0x250 AddressSpaceOwner : Pos 2, 1 Bit
+0x254 ForwardClusterOnly : UChar
+0x255 DisablePageFaultClustering : UChar
ETHREAD
是執行緒結構體,接觸過PEB
和TEB
可能有所瞭解。但PEB
和TEB
是3環的結構體,ETHREAD
是0環的結構體。由於不瞭解這東西可能對後面的知識瞭解不了,這東西將會在程式執行緒篇進行詳細講解,故在此進行了解性的學習。
ETHREAD
裡面巢狀了一個結構體KTHREAD
,我們來看看它的結構體:
kd> dt _KTHREAD
nt!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x010 MutantListHead : _LIST_ENTRY
+0x018 InitialStack : Ptr32 Void
+0x01c StackLimit : Ptr32 Void
+0x020 Teb : Ptr32 Void
+0x024 TlsArray : Ptr32 Void
+0x028 KernelStack : Ptr32 Void
+0x02c DebugActive : UChar
+0x02d State : UChar
+0x02e Alerted : [2] UChar
+0x030 Iopl : UChar
+0x031 NpxState : UChar
+0x032 Saturation : Char
+0x033 Priority : Char
+0x034 ApcState : _KAPC_STATE
+0x04c ContextSwitches : Uint4B
+0x050 IdleSwapBlock : UChar
+0x051 Spare0 : [3] UChar
+0x054 WaitStatus : Int4B
+0x058 WaitIrql : UChar
+0x059 WaitMode : Char
+0x05a WaitNext : UChar
+0x05b WaitReason : UChar
+0x05c WaitBlockList : Ptr32 _KWAIT_BLOCK
+0x060 WaitListEntry : _LIST_ENTRY
+0x060 SwapListEntry : _SINGLE_LIST_ENTRY
+0x068 WaitTime : Uint4B
+0x06c BasePriority : Char
+0x06d DecrementCount : UChar
+0x06e PriorityDecrement : Char
+0x06f Quantum : Char
+0x070 WaitBlock : [4] _KWAIT_BLOCK
+0x0d0 LegoData : Ptr32 Void
+0x0d4 KernelApcDisable : Uint4B
+0x0d8 UserAffinity : Uint4B
+0x0dc SystemAffinityActive : UChar
+0x0dd PowerState : UChar
+0x0de NpxIrql : UChar
+0x0df InitialNode : UChar
+0x0e0 ServiceTable : Ptr32 Void
+0x0e4 Queue : Ptr32 _KQUEUE
+0x0e8 ApcQueueLock : Uint4B
+0x0f0 Timer : _KTIMER
+0x118 QueueListEntry : _LIST_ENTRY
+0x120 SoftAffinity : Uint4B
+0x124 Affinity : Uint4B
+0x128 Preempted : UChar
+0x129 ProcessReadyQueue : UChar
+0x12a KernelStackResident : UChar
+0x12b NextProcessor : UChar
+0x12c CallbackStack : Ptr32 Void
+0x130 Win32Thread : Ptr32 Void
+0x134 TrapFrame : Ptr32 _KTRAP_FRAME
+0x138 ApcStatePointer : [2] Ptr32 _KAPC_STATE
+0x140 PreviousMode : Char
+0x141 EnableStackSwap : UChar
+0x142 LargeStack : UChar
+0x143 ResourceIndex : UChar
+0x144 KernelTime : Uint4B
+0x148 UserTime : Uint4B
+0x14c SavedApcState : _KAPC_STATE
+0x164 Alertable : UChar
+0x165 ApcStateIndex : UChar
+0x166 ApcQueueable : UChar
+0x167 AutoAlignment : UChar
+0x168 StackBase : Ptr32 Void
+0x16c SuspendApc : _KAPC
+0x19c SuspendSemaphore : _KSEMAPHORE
+0x1b0 ThreadListEntry : _LIST_ENTRY
+0x1b8 FreezeCount : Char
+0x1b9 SuspendCount : Char
+0x1ba IdealProcessor : UChar
+0x1bb DisableBoost : UChar
可以看到KTHREAD
裡面有一個你眼熟的TEB
結構體,這個暫且不說,我們看看幾個必要的成員:
PreviousMode
:先前模式,如果0環呼叫的,值為0
。如果為3環呼叫的,值為1
。
DebugActive
:除錯活動狀態,指示當前執行緒是否處於除錯狀態。如果這個成員被設成0,則會影響硬體斷點無法斷下。其他的知識點和細節將會在程式執行緒篇進行介紹。
TrapFrame
:棧幀,熟不熟悉?這就是我們前面講解的Trap_Frame
結構體。一個執行緒一個棧幀結構體。
本節練習
本節的答案將會在下一節的正文給出,務必把本節練習做完後看下一個講解內容。不要偷懶,實驗是學習本教程的捷徑。
俗話說得好,光說不練假把式,如下是本節相關的練習。如果練習沒做好,就不要看下一節教程了,越到後面,不做練習的話容易夾生了,開始還明白,後來就真的一點都不明白了。本節練習不多,但難度較大,請保質保量的完成。
1️⃣ 逆向分析KiSystemService
和KiFastCallEntry
函式。
下一篇
系統呼叫篇——0環層面呼叫過程(下)