系统调用篇——0环层面调用过程(上)
写在前面
此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我。
你如果是从中间插过来看的,请仔细阅读 羽夏看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
,如所示:
.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 PreviousMode : 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
函数。
下一篇
本文来自博客园,作者:寂静的羽夏 ,一个热爱计算机技术的菜鸟
转载请注明原文链接:https://www.cnblogs.com/wingsummer/p/15543945.html