Windows内核(三)——系统调用

[toc]

本文是windows内核系列的第三部分,本来应该是放到第二部分后面的,但是第二部分会用到这部分的相关内容 ,就先放第三部分了。

001.API函数的调用过程(3环部分)

主要是存放在 C:\WINDOWS\system32 下面所有的dll

几个重要的DLL

  • Kernel32.dll:最核心的功能模块,比如管理内存、进程和线程相关的函数等.
  • User32.dll:是Windows用户界面相关应用程序接口,如创建窗口和发送消息等.
  • GDI32.dll:全称是Graphical Device Interface(图形设备接口),包含用于画图和显示文本的函数.比如要显示一个程序窗口,就调用了其中的函数来画这个窗口.
  • Ntdll.dll:大多数API都会通过这个DLL进入内核(0环).

alt+t直接搜就行,这里使用ReadProcesMemory来举例

首先是kernel32这个dll

可以看到是call了一个函数,之后巴拉巴拉,

image-20230106134646722

调用的这个函数在ntdll这个dll里面,在import可以看到,他做了这些事:其中的0BA是一个编号,7FFE0300决定以什么方式进0环

image-20230106134533807

kernel32.dll(ReadProcessMemory) =>

ntdll.dll(NtReadVirtualMemory)

002.API函数的调用过程(3环进0环 上)

下面说下这部分:

image-20230106134533807

首先是7FFE0300这个地址

_KUSER_SHARED_DATA

  1. 在 User 层和 Kernel 层分别定义了一个 _KUSER_SHARED_DATA 结构区域,用于 User 层和 Kernel 层共享某些数据
  2. 它们使用固定的地址值映射,_KUSER_SHARED_DATA 结构区域在 User 和 Kernel 层地址分别为:
    • User 层地址为:0x7ffe0000
    • Kernnel 层地址为:0xffdf0000

特别说明:

虽然指向的是同一个物理页,但在User 层是只读的,在Kernnel层是可写的.

可以看到两者都是一样的:

image-20230106140227852

可以看到300的位置是系统调用:

image-20230106140509018

CPU是否支持快速调用

实验:是否支持快速调用

当通过eax=1来执行cpuid指令时,处理器的特征信息被放在ecxedx寄存器中,其中edx包含了一个SEP位(11位),该位指明了当前处理器知否支持sysenter/sysexit指令

  • 支持:ntdll.dll!KiFastSystemCall()
  • 不支持:ntdll.dll!KiIntSystemCall()

eax置1后执行,发现是0xBFF,第11位是1,表示支持

image-20230106141108580

当系统启动的时候,上述执行,之后将相应的函数放到偏移0x300的位置。

进R0需要更改哪些寄存器

  1. CS的权限由3变为0,意味着需要新的CS

  2. SS与CS的权限永远一致,需要新的SS

  3. 权限发生切换的时候,堆栈也一定会切换,需要新的ESP

  4. 进0环后代码的位置,需要EIP


不支持的时候使用的是ntdll.dll!KiIntSystemCall()

将eax中的编号和edx(参数指针),之后中断0x2E进入内核

image-20230106142000444

kd>  dq 8003f400+170
ReadVirtual: 8003f570 not properly sign extended
8003f570  8054ee00`00082611 80548e00`0008590c
8003f580  80548e00`00081cd0 80548e00`00081cda
8003f590  80548e00`00081ce4 80548e00`00081cee
8003f5a0  80548e00`00081cf8 80548e00`00081d02
8003f5b0  80548e00`00081d0c 806e8e00`00087864
8003f5c0  80548e00`00081d20 80548e00`00081d2a
8003f5d0  80548e00`00081d34 80548e00`00081d3e
8003f5e0  80548e00`00081d48 806e8e00`00088e2c
kd> u 80542611
ReadVirtual: 80542611 not properly sign extended
80542611 6a00            push    0
80542613 55              push    ebp
80542614 53              push    ebx
80542615 56              push    esi
80542616 57              push    edi
80542617 0fa0            push    fs
80542619 bb30000000      mov     ebx,30h
8054261e 668ee3          mov     fs,bx

支持快速调用的时候使用ntdll.dll!KiFastSystemCall()

也就2行代码:

image-20230106143004394

中断门进0环,需要的CS、EIP在IDT表中,需要查内存(SS与ESP由TSS提供)而CPU如果支持sysenter指令时,操作系统会提前将CS/SS/ESP/EIP的值存储在MSR寄存器中,sysenter指令执行时,CPU会将MSR寄存器中的值直接写入相关寄存器,没有读内存的过程,所以叫快速调用,本质是一样的!

003.API函数的调用过程(3环进0环 下)

不支持快速调用的话就中断进0环,支持的话就sysenter,需要的寄存器信息(CS,SS,ESP,EIP)在MSR寄存器中可以找到:(ss在cs+8的位置)

MSR 地址
IA32_SYSENTER_CS 174H
IA32_SYSENTER_ESP 175H
IA32_SYSENTER_EIP 176H

可以通过RDMSR/WRMST来进行读写(操作系统使用WRMST写该寄存器):

kd> rdmsr 174   //查看CS
kd> rdmsr 175   //查看ESP
kd> rdmsr 176   //查看EIP
> 参考:Intel白皮书第二卷(搜索sysenter)
kd> rdmsr 174
msr[174] = 00000000`00000008
kd> rdmsr 175
msr[175] = 00000000`f78af000
kd> rdmsr 176
msr[176] = 00000000`805426e0
kd> u 805426e0
ReadVirtual: 805426e0 not properly sign extended
805426e0 b923000000      mov     ecx,23h
805426e5 6a30            push    30h
805426e7 0fa1            pop     fs
805426e9 8ed9            mov     ds,cx
805426eb 8ec1            mov     es,cx
805426ed 648b0d40000000  mov     ecx,dword ptr fs:[40h]
805426f4 8b6104          mov     esp,dword ptr [ecx+4]
805426f7 6a23            push    23h

总结

API通过中断门进0环:

1)  固定中断号为0x2E
2)  CS/EIP由门描述符提供   ESP/SS由TSS提供
3)  进入0环后执行的内核函数:NT!KiSystemService

API通过sysenter指令进0环:

1)  CS/ESP/EIP由MSR寄存器提供(SS是算出来的)
2)  进入0环后执行的内核函数:NT!KiFastCallEntry

内核模块:ntoskrnl.exe/ntkrnlpa.exe

004.API函数的调用过程(保存现场)

这里使用KiSystemService举例。

首先接收三个结构体:

_Trap_Frame结构体

这个结构体在0环,由操作系统进行维护

无论是快速调用还是中断进0环都会将寄存器写到这个结构体里

kd> dt _ktrap_Frame

image-20230107134858939.png

在中断进入0环的时候,会压入0x068~0x078的5个值,快速调用的 时候,这5个值是没有的。

_ETHREAD线程相关的结构体

kd> dt _ETHREAD
ntdll!_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

里面有个_KTHREAD

kd> dt _KTHREAD
ntdll!_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

_KPCR

KPCR 叫CPU控制区(Processor Control Region)

CPU也有自己的控制块,每一个CPU有一个,叫KPCR

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

其中第一个是一个结构体_NT_TIB

kd> dt _NT_TIB
ntdll!_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//结构体指针 指向自己

_KPRC的最后一个也是一个结构体_KPRCB

kd> dt _KPRCB
ntdll!_KPRCB
   +0x000 MinorVersion     : Uint2B
   +0x002 MajorVersion     : Uint2B
   +0x004 CurrentThread    : Ptr32 _KTHREAD//当前CPU所执行线程的_ETHREAD
   +0x008 NextThread       : Ptr32 _KTHREAD//下一个_ETHREAD
   +0x00c IdleThread       : Ptr32 _KTHREAD//当所以线程都执行完了CPU就可以执行这个
   +0x010 Number           : Char//CPU编号
   +0x011 Reserved         : Char
   +0x012 BuildType        : Uint2B
   +0x014 SetMember        : Uint4B
   +0x018 CpuType          : Char
   +0x019 CpuID            : Char
   +0x01a CpuStep          : Uint2B//CPU子版本号
   +0x01c ProcessorState   : _KPROCESSOR_STATE//CPU状态
   +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//Npx浮点处理器  最后一次用过浮点的线程
   +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

逆向分析 KiSystemService

KiSystemService这个函数也就是中断的时候,0x2E的内容

.text:004067D1 _KiSystemService proc near              ; CODE XREF: ZwAcceptConnectPort(x,x,x,x,x,x)+C↓p
.text:004067D1                                         ; ZwAccessCheck(x,x,x,x,x,x,x,x)+C↓p ...
.text:004067D1
.text:004067D1 arg_0           = dword ptr  4
.text:004067D1
.text:004067D1                 push    0
.text:004067D3                 push    ebp             ; push的0是压入0x064 ErrCode的位置
.text:004067D4                 push    ebx             ; +0x05c Ebx
.text:004067D5                 push    esi             ; +0x058 Esi
.text:004067D6                 push    edi             ; +0x054 Edi
.text:004067D7                 push    fs              ; +0x050 SegFs
.text:004067D9                 mov     ebx, 30h ; '0'  ; 为FS寄存器赋值,指向KPCR结构体
.text:004067DE                 mov     fs, ebx         ; 将段选择子0x30查询GDT这张表,找到对应的段描述符,把段描述符加载到FS寄存器
.text:004067DE                                         ;
.text:004067DE                                         ; 0x30: // 0011 0000
.text:004067DE                                         ; 索引为6的段描述符,查GDT表
.text:004067DE                                         ; 本机中idx为6的值:ffc093df`f0000001
.text:004067DE                                         ; fs.base = ffdff000,指向当前CPU的KPCR结构
.text:004067E0                 push    large dword ptr fs:0 ; 保存旧的 ExceptionList,然后把新的清成-1
.text:004067E7                 mov     large dword ptr fs:0, 0FFFFFFFFh
.text:004067F2                 mov     esi, large fs:124h ; esi 指向 CurrentThread,当前CPU所执行线程的_ETHREAD
.text:004067F9                 push    dword ptr [esi+140h] ; 保存 CurrentThread.PreviousMode(先前模式)
.text:004067F9                                         ; PreviousMode = 0 表示从0环调用过来
.text:004067F9                                         ; PreviousMode = 1 表示从3环调用过来
.text:004067FF                 sub     esp, 48h        ; esp 指向 _KTRAP_FRAME
.text:00406802                 mov     ebx, [esp+68h+arg_0] ; 取出esp+0x6C,也就是3环压入的参数CS
.text:00406806                 and     ebx, 1          ; 0环最低位为0,3环最低位为1
.text:00406809                 mov     [esi+140h], bl  ; 填写新的“先前模式”
.text:0040680F                 mov     ebp, esp        ; ESP == EBP ==_KTRAP_FRAME
.text:00406811                 mov     ebx, [esi+134h] ; EBX现在是_KTRAP_FRAME
.text:00406817                 mov     [ebp+3Ch], ebx  ; 将_KTRAP_FRAME中的TrapFrame暂时存到这里,之后
.text:00406817                                         ; mov     edx, [ebp+3Ch]
.text:00406817                                         ; 会将这个值取出来重新赋给_KTRAP_FRAME的TrapFrame
.text:0040681A                 mov     [esi+134h], ebp ; CurrentThread.TrapFrame 指向当前 _KTRAP_FRAME
.text:00406820                 cld                     ; 将标志寄存器Flag的方向标志位DF清零
.text:00406821                 mov     ebx, [ebp+60h]  ;  3环ebp
.text:00406824                 mov     edi, [ebp+68h]  ; 3环eip
.text:00406827                 mov     [ebp+0Ch], edx  ; _KTRAP_FRAME.DbgArgPointer = edx
.text:00406827                                         ; 这一步是保存3环API参数指针
.text:0040682A                 mov     dword ptr [ebp+8], 0BADB0D00h
.text:00406831                 mov     [ebp+0], ebx    ; _KTRAP_FRAME.DbgEbp = _KTRAP_FRAME.Ebp
.text:00406834                 mov     [ebp+4], edi    ; _KTRAP_FRAME.DbgEip = _KTRAP_FRAME.Eip
.text:00406837                 test    byte ptr [esi+2Ch], 0FFh
.text:0040683B                 jnz     Dr_kss_a        ; 测试 CurrentThread.DebugActive
.text:0040683B                                         ; 如果正被调试,保存调试相关的寄存器到 _KTRAP_FRAME
.text:00406841
.text:00406841 loc_406841:                             ; CODE XREF: Dr_kss_a+10↑j
.text:00406841                                         ; Dr_kss_a+7C↑j
.text:00406841                 sti                     ; 允许中断
.text:00406842                 jmp     loc_406932
.text:00406842 _KiSystemService endp

005.API函数的调用过程(系统服务表)

上节内容:进0环后,3环的各种寄存器都会保留到_Trap_Frame结构体中

本节内容:

  • 如何根据系统服务号(eax中存储)找到要执行的内核函数?
  • 调用时参数是存储到3环的堆栈,如何传递给内核函数?

SystemServiceTable 系统服务表

image-20230107230553238

这个表在_KTHREAD的偏移为0xE0的位置。

如何判断是上面还是下面那张表:

image-20230107230710291

这个是_KiFastCallEntry,KiSystemService的最后直接到了0x00406932的位置

.text:0040689F _KiFastCallEntry proc near              ; DATA XREF: _KiTrap01+71↓o
.text:0040689F                                         ; KiLoadFastSyscallMachineSpecificRegisters(x)+24↓o
.text:0040689F
.text:0040689F var_B           = byte ptr -0Bh
.text:0040689F
.text:0040689F ; FUNCTION CHUNK AT .text:0040686C SIZE 00000024 BYTES
.text:0040689F
.text:0040689F                 mov     ecx, 23h ; '#'
.text:004068A4                 push    30h ; '0'
.text:004068A6                 pop     fs
.text:004068A8                 mov     ds, ecx
.text:004068AA                 mov     es, ecx
.text:004068AC                 mov     ecx, large fs:40h
.text:004068B3                 mov     esp, [ecx+4]
.text:004068B6                 push    23h ; '#'
.text:004068B8                 push    edx
.text:004068B9                 pushf
.text:004068BA
.text:004068BA loc_4068BA:                             ; CODE XREF: _KiFastCallEntry2+23↑j
.text:004068BA                 push    2
.text:004068BC                 add     edx, 8
.text:004068BF                 popf
.text:004068C0                 or      [esp+0Ch+var_B], 2
.text:004068C5                 push    1Bh
.text:004068C7                 push    dword ptr ds:0FFDF0304h
.text:004068CD                 push    0
.text:004068CF                 push    ebp
.text:004068D0                 push    ebx
.text:004068D1                 push    esi
.text:004068D2                 push    edi
.text:004068D3                 mov     ebx, large fs:1Ch
.text:004068DA                 push    3Bh ; ';'
.text:004068DC                 mov     esi, [ebx+124h]
.text:004068E2                 push    dword ptr [ebx]
.text:004068E4                 mov     dword ptr [ebx], 0FFFFFFFFh
.text:004068EA                 mov     ebp, [esi+18h]
.text:004068ED                 push    1
.text:004068EF                 sub     esp, 48h
.text:004068F2                 sub     ebp, 29Ch
.text:004068F8                 mov     byte ptr [esi+140h], 1
.text:004068FF                 cmp     ebp, esp
.text:00406901                 jnz     loc_40686C
.text:00406907                 and     dword ptr [ebp+2Ch], 0
.text:0040690B                 test    byte ptr [esi+2Ch], 0FFh
.text:0040690F                 mov     [esi+134h], ebp
.text:00406915                 jnz     Dr_FastCallDrSave
.text:0040691B
.text:0040691B loc_40691B:                             ; CODE XREF: Dr_FastCallDrSave+10↑j
.text:0040691B                                         ; Dr_FastCallDrSave+7C↑j
.text:0040691B                 mov     ebx, [ebp+60h]
.text:0040691E                 mov     edi, [ebp+68h]
.text:00406921                 mov     [ebp+0Ch], edx
.text:00406924                 mov     dword ptr [ebp+8], 0BADB0D00h
.text:0040692B                 mov     [ebp+0], ebx
.text:0040692E                 mov     [ebp+4], edi
.text:00406931                 sti
.text:00406932
.text:00406932 loc_406932:                             ; CODE XREF: _KiBBTUnexpectedRange+18↑j
.text:00406932                                         ; _KiSystemService+71↑j
.text:00406932                 mov     edi, eax        ; 取出3环传进来的系统调用号
.text:00406934                 shr     edi, 8          ; 右移8位
.text:00406937                 and     edi, 30h        ; 检测系统调用号12位
.text:00406937                                         ; 如果等于1,那么 edi == 0x10
.text:00406937                                         ; 如果等于0,那么 edi == 0x00
.text:0040693A                 mov     ecx, edi
.text:0040693C                 add     edi, [esi+0E0h] ; edi += CurrentThread.ServiceTable
.text:0040693C                                         ; 此时 edi 指向了API对应的系统服务表
.text:0040693C                                         ;
.text:0040693C                                         ; 0x10 刚好是系统服务表的大小
.text:0040693C                                         ; 系统服务表有 ServiceTable, Count, ServiceLimit 和 ArgmentTable
.text:0040693C                                         ;  4项共0x10字节,所以通过这里的代码也可以推断,内核和win32k.sys的系统服务表是连续的
.text:0040693C                                         ;  第一张是内核的,第二张是win32k.sys的
.text:00406942                 mov     ebx, eax        ; ebx = 系统调用号
.text:00406944                 and     eax, 0FFFh      ; eax = 系统服务表下标
.text:00406949                 cmp     eax, [edi+8]
.text:0040694C                 jnb     _KiBBTUnexpectedRange ; 检查系统调用号是否超过系统服务表的范围,超过就跳到异常处理
.text:00406952                 cmp     ecx, 10h
.text:00406955                 jnz     short loc_406972 ; 跳转条件:系统服务(ntdll.dll 的API)
.text:00406955                                         ; 不跳转条件:图形及用户界面(gdi.dll 的API)
.text:00406957                 mov     ecx, large fs:18h
.text:0040695E                 xor     ebx, ebx
.text:0040695E _KiFastCallEntry endp ; sp-analysis failed
.text:0040695E
.text:00406960
.text:00406960 ; =============== S U B R O U T I N E =======================================
.text:00406960
.text:00406960
.text:00406960 ; _DWORD __stdcall KiSystemServiceAccessTeb()
.text:00406960 _KiSystemServiceAccessTeb@0 proc near   ; DATA XREF: KiPreprocessAccessViolation(x,x,x)+3D↓o
.text:00406960
.text:00406960 ; FUNCTION CHUNK AT .text:00406B4F SIZE 0000000A BYTES
.text:00406960
.text:00406960                 or      ebx, [ecx+0F70h]
.text:00406966                 jz      short loc_406972 ; _KCPR.KPRCB.KeSystemCalls += 1, 系统调用计数加1
.text:00406968                 push    edx
.text:00406969                 push    eax
.text:0040696A                 call    ds:_KeGdiFlushUserBatch
.text:00406970                 pop     eax
.text:00406971                 pop     edx
.text:00406972
.text:00406972 loc_406972:                             ; CODE XREF: _KiFastCallEntry+B6↑j
.text:00406972                                         ; KiSystemServiceAccessTeb()+6↑j
.text:00406972                 inc     large dword ptr fs:638h ; _KCPR.KPRCB.KeSystemCalls += 1, 系统调用计数加1
.text:00406979                 mov     esi, edx        ; esi = edx = 3环参数指针
.text:0040697B                 mov     ebx, [edi+0Ch]  ; ebx 指向函数参数表
.text:0040697B                                         ; eax 是系统调用号
.text:0040697E                 xor     ecx, ecx
.text:00406980                 mov     cl, [eax+ebx]   ; cl = 参数字节数
.text:00406983                 mov     edi, [edi]      ; edi 指向函数地址表
.text:00406985                 mov     ebx, [edi+eax*4] ; ebx 指向0环函数
.text:00406988                 sub     esp, ecx        ; 从这句开始,到call为止,完成了复制3环参数的工作
.text:00406988                                         ; 这句是模拟压栈操作
.text:0040698A                 shr     ecx, 2          ; 参数字节数 / 4,得到参数个数
.text:0040698D                 mov     edi, esp
.text:0040698F                 test    byte ptr [ebp+72h], 2
.text:00406993                 jnz     short loc_40699B ; 越界检查
.text:00406993                                         ; 如果 esi(3环参数指针)大于等于 0x7fff0000,则返回 c0000005 异常
.text:00406995                 test    byte ptr [ebp+6Ch], 1
.text:00406999                 jz      short _KiSystemServiceCopyArguments@0 ; 复制参数:复制 esi 到 edi,每次复制4字节,次数由 ecx 决定
.text:00406999                                         ; 方向由DF决定,DF=0,故每次复制后,edi 和 esi 都加4
.text:0040699B
.text:0040699B loc_40699B:                             ; CODE XREF: KiSystemServiceAccessTeb()+33↑j
.text:0040699B                 cmp     esi, ds:_MmUserProbeAddress ; 越界检查
.text:0040699B                                         ; 如果 esi(3环参数指针)大于等于 0x7fff0000,则返回 c0000005 异常
.text:004069A1                 jnb     loc_406B4F
.text:004069A1 _KiSystemServiceAccessTeb@0 endp
.text:004069A1
.text:004069A7
.text:004069A7 ; =============== S U B R O U T I N E =======================================
.text:004069A7
.text:004069A7 ; 复制参数:复制 esi 到 edi,每次复制4字节,次数由 ecx 决定
.text:004069A7 ; 方向由DF决定,DF=0,故每次复制后,edi 和 esi 都加4
.text:004069A7
.text:004069A7 ; _DWORD __stdcall KiSystemServiceCopyArguments()
.text:004069A7 _KiSystemServiceCopyArguments@0 proc near
.text:004069A7                                         ; CODE XREF: KiSystemServiceAccessTeb()+39↑j
.text:004069A7                                         ; DATA XREF: KiPreprocessAccessViolation(x,x,x):loc_4628DF↓o
.text:004069A7                 rep movsd
.text:004069A9                 call    ebx             ; 复制参数:复制 esi 到 edi,每次复制4字节,次数由 ecx 决定
.text:004069A9 _KiSystemServiceCopyArguments@0 endp    ; 方向由DF决定,DF=0,故每次复制后,edi 和 esi 都加4
.text:004069A9                                         ; 调用内核函数
.text:004069AB

006.API函数的调用过程(SSDT)

本节通过其他方式来访问系统服务表.

SSDT 的全称是 System Services Descriptor Table,系统服务描述符表

kd> dd  KeServiceDescriptorTable(SSDT)
导出的 声明一下就可以使用了

kd> dd  KeServiceDescriptorTableShadow(SSDT Shadow)
未导出 需要用其他的方式来查找

上面的是导出的,下面是未导出的,可以看到导出的里面只有一张表有值,其余三张没有值,如果想看第二张表的话可以看未导出的:

image-20230107234334197

这里使用前面的那个函数进入0环需要的系统服务号(BA),这里要*4,每个是4字节(其实下面的那个函数就是nt!NtReadVirtualMemory这个函数):

image-20230107235014722

所需要的参数的字节数,0x14,也就是20字节:

image-20230107235309282

0%