windows内核研究(系统调用 二)
WindowsApi函数调用过程(3环进0环)
由于我系统是64位的,在调用32位函数时,会把原本的32位api转为64位内核函数来调用
_KUSER_SHARED_DATA结构体
- 在User层和Kernel层分别定义了一个
_KUSER_SHARED_DATA
结构区域,用于User层和Kernel层共享某些数据 - 它们使用固定的地址值来映射,
_KUSER_SHARED_DATA
结构区域在User和Kernel层地址分别为:- User层地址为:
0x7FFE0000
- Kernel层地址为:
0x7FFDF0000
- User层地址为:
注:虽然指向的是同一个物理页,但在User层是只读的,在Kernel层是可写的
我们来查看一个这个 _KUSER_SHARED_DATA 结构体
1: kd> dt _KUSER_SHARED_DATA
ntdll!_KUSER_SHARED_DATA+0x000 TickCountLowDeprecated : Uint4B+0x004 TickCountMultiplier : Uint4B+0x008 InterruptTime : _KSYSTEM_TIME+0x014 SystemTime : _KSYSTEM_TIME+0x020 TimeZoneBias : _KSYSTEM_TIME+0x02c ImageNumberLow : Uint2B+0x02e ImageNumberHigh : Uint2B+0x030 NtSystemRoot : [260] Wchar+0x238 MaxStackTraceDepth : Uint4B+0x23c CryptoExponent : Uint4B+0x240 TimeZoneId : Uint4B+0x244 LargePageMinimum : Uint4B+0x248 AitSamplingValue : Uint4B+0x24c AppCompatFlag : Uint4B+0x250 RNGSeedVersion : Uint8B+0x258 GlobalValidationRunlevel : Uint4B+0x25c TimeZoneBiasStamp : Int4B+0x260 NtBuildNumber : Uint4B+0x264 NtProductType : _NT_PRODUCT_TYPE+0x268 ProductTypeIsValid : UChar+0x269 Reserved0 : [1] UChar+0x26a NativeProcessorArchitecture : Uint2B+0x26c NtMajorVersion : Uint4B+0x270 NtMinorVersion : Uint4B+0x274 ProcessorFeatures : [64] UChar+0x2b4 Reserved1 : Uint4B+0x2b8 Reserved3 : Uint4B+0x2bc TimeSlip : Uint4B+0x2c0 AlternativeArchitecture : _ALTERNATIVE_ARCHITECTURE_TYPE+0x2c4 BootId : Uint4B+0x2c8 SystemExpirationDate : _LARGE_INTEGER+0x2d0 SuiteMask : Uint4B+0x2d4 KdDebuggerEnabled : UChar+0x2d5 MitigationPolicies : UChar+0x2d5 NXSupportPolicy : Pos 0, 2 Bits+0x2d5 SEHValidationPolicy : Pos 2, 2 Bits+0x2d5 CurDirDevicesSkippedForDlls : Pos 4, 2 Bits+0x2d5 Reserved : Pos 6, 2 Bits+0x2d6 CyclesPerYield : Uint2B+0x2d8 ActiveConsoleId : Uint4B+0x2dc DismountCount : Uint4B+0x2e0 ComPlusPackage : Uint4B+0x2e4 LastSystemRITEventTickCount : Uint4B+0x2e8 NumberOfPhysicalPages : Uint4B+0x2ec SafeBootMode : UChar+0x2ed VirtualizationFlags : UChar+0x2ee Reserved12 : [2] UChar+0x2f0 SharedDataFlags : Uint4B+0x2f0 DbgErrorPortPresent : Pos 0, 1 Bit+0x2f0 DbgElevationEnabled : Pos 1, 1 Bit+0x2f0 DbgVirtEnabled : Pos 2, 1 Bit+0x2f0 DbgInstallerDetectEnabled : Pos 3, 1 Bit+0x2f0 DbgLkgEnabled : Pos 4, 1 Bit+0x2f0 DbgDynProcessorEnabled : Pos 5, 1 Bit+0x2f0 DbgConsoleBrokerEnabled : Pos 6, 1 Bit+0x2f0 DbgSecureBootEnabled : Pos 7, 1 Bit+0x2f0 DbgMultiSessionSku : Pos 8, 1 Bit+0x2f0 DbgMultiUsersInSessionSku : Pos 9, 1 Bit+0x2f0 DbgStateSeparationEnabled : Pos 10, 1 Bit+0x2f0 SpareBits : Pos 11, 21 Bits+0x2f4 DataFlagsPad : [1] Uint4B+0x2f8 TestRetInstruction : Uint8B+0x300 QpcFrequency : Int8B+0x308 SystemCall : Uint4B //这个位置对应的就是系统调用,刚好和我上面获取到的函数地址的偏移一致+0x30c SystemCallPad0 : Uint4B+0x310 SystemCallPad : [2] Uint8B+0x320 TickCount : _KSYSTEM_TIME+0x320 TickCountQuad : Uint8B+0x320 ReservedTickCountOverlay : [3] Uint4B+0x32c TickCountPad : [1] Uint4B+0x330 Cookie : Uint4B+0x334 CookiePad : [1] Uint4B+0x338 ConsoleSessionForegroundProcessId : Int8B+0x340 TimeUpdateLock : Uint8B+0x348 BaselineSystemTimeQpc : Uint8B+0x350 BaselineInterruptTimeQpc : Uint8B+0x358 QpcSystemTimeIncrement : Uint8B+0x360 QpcInterruptTimeIncrement : Uint8B+0x368 QpcSystemTimeIncrementShift : UChar+0x369 QpcInterruptTimeIncrementShift : UChar+0x36a UnparkedProcessorCount : Uint2B+0x36c EnclaveFeatureMask : [4] Uint4B+0x37c TelemetryCoverageRound : Uint4B+0x380 UserModeGlobalLogger : [16] Uint2B+0x3a0 ImageFileExecutionOptions : Uint4B+0x3a4 LangGenerationCount : Uint4B+0x3a8 Reserved4 : Uint8B+0x3b0 InterruptTimeBias : Uint8B+0x3b8 QpcBias : Uint8B+0x3c0 ActiveProcessorCount : Uint4B+0x3c4 ActiveGroupCount : UChar+0x3c5 Reserved9 : UChar+0x3c6 QpcData : Uint2B+0x3c6 QpcBypassEnabled : UChar+0x3c7 QpcShift : UChar+0x3c8 TimeZoneBiasEffectiveStart : _LARGE_INTEGER+0x3d0 TimeZoneBiasEffectiveEnd : _LARGE_INTEGER+0x3d8 XState : _XSTATE_CONFIGURATION
查看CPU是否支持快速调用
通过eax=1来执行CPUID指令,处理器的特征信息被放在ecx和edx当中,其中edx包含了一个SEP位(11位) 该位指明了当前处理器是否支持sysenter/sysexit
指令
支持:ntdll.dll!KiFastSystemCall()(64位下为SYSCALL)
不支持:ntdll.dll!KilntSystemCall()(64位下为SYSCALL)
cpuid 指令 // 可以查询cpu的很多信息,需要参数,通过eax来传递
执行完后rcx和rdx的值
- rcx:0xFED8320B
- rdx:0x178BFBFF–>0001 0111 1000 1011 1111 1011 1111 1111
看rdx中的第11位为1,说明我当前cpu是支持的
在操作系统启动时,就会通过cpuid指令查看看是否支持sysenter/sysexit指令,如果支持就会把KiFastSystemCall()写入到7FFE0308这个地址否则为KilntSystemCall()这个函数
3环进0环需要改哪些寄存器?
- CS寄存器:CS的权限由3变为0,意味着需要新的CS
- SS寄存器:SS与CS的权限永远一致,需要新的SS
- ESP寄存器:权限发生切换的时候,堆栈也一定会切换,需要新的ESP
- EIP寄存器:进0环后代码的位置也会改变,需要新的EIP
x86
不支持时,使用int 2Eh这种中断门来实现3环到0环的跳转
支持时直接使用sysenter来进行跳转
为什么sysenter叫快速调用?
那是因为中断门进0环,需要的CS,EIP在IDT表中,需要查内存(SS与ESP由TSS提供),如果CPU支持sysenter指令,操作系统会提前让CS,SS,ESP,EIP的值存在MSR寄存器中,sysenter指令执行时,CPU会将MSR寄存器中的值直接写入相关寄存器,没有读内存的过程,所以叫快速调用(本质上是一样的)
INT 0x2E进0环
查看idt表2e的位置
拆分:8277(地址高)ee(说明是中断门 1110 1110 )00 0008(CS) 8fb1(地址低)
地址(要执行的代码EIP):82778fb1
u 指令 // 对一个地址进行反汇编
这里就是我们进到内核后执行代码的地方
sysenter进0环
在执行sysenter指令之前,操作系统必须指定0环的CS段,SS段,EIP以及ESP
(详情参考inter手册第二卷 sysenter指令)
MSR寄存器 | 地址 |
---|---|
IA32_SYSENTER_CS | 174H |
IA32_SYSENTER_ESP | 175H |
IA32_SYSENTER_EIP | 176H |
SS(MSR中并没有存储SS) | IA32_SYSENTER_CS+8 |
… | … |
可以通过RDMSR/WRMST来进行读写(操作系统使用WRMST写该寄存器)
反汇编地址:82779280
可以看到如果使用的是sysenter那么这个方法正是我们上面说的KiFastCallEntry
总结:
- API通过中断门进0环:
- 固定中断号为0x2E
- CS/EIP由门描述符提供 ESP/SS由TSS提供
- 进入0环后执行的内核函数:NT!KiSystemService
- API通过sysenter指令进0环
- CS/ESP/EIP由MSR寄存器提供(SS通过CS+8算出来)
- 进入0环后执行的内核函数:NT!KiFastCallEntry