目录
一、前言
在上一篇中已经将3环进入0环的流程分析完成完成了,以及提到了使用sysenter指令如何进行权限提升,在本章中我们将从msr查找0环处理函数到查找ssdt表项前的准备工作进行分析。
二、环境
调试机器:windows10
调试工具:windbg、x32dbg、ida8.3
被调试机器:windows 7 sp1 x86
三、词汇说明
ssdt:系统服务描述表
四、调用流程
4.1 查找入口函数
在上一篇中提到使用sysenter进入0环是将msr寄存器的174项中的内容设置到了eip寄存器中,说明了0环的入口函数就为msr寄存器的174项内容的这个地址。
使用windbg可以查询到msr寄存的176存的内容是00000000`83e5d760这个地址,通过 u 指令查看这个地址上可以发现这个地址上是一个KiFastEntry函数。
4.2 分析准备工作
在正式分析之前需要先了解几个结构体才能更好的分析工作。
4.2.1 KPCR
该结构全称叫处理器控制区域,用于记录当前核心执行状态,在进入0环后fs指向的就该结构,在当前分析中我们需要使用该结构中的TSS、PrcbData这两个字段。一定切记0环中fs指向这个结构
//0x3748 bytes (sizeof)
struct _KPCR
{
union
{
struct _NT_TIB NtTib; //0x0
struct
{
struct _EXCEPTION_REGISTRATION_RECORD* Used_ExceptionList; //0x0
VOID* Used_StackBase; //0x4
VOID* Spare2; //0x8
VOID* TssCopy; //0xc
ULONG ContextSwitches; //0x10
ULONG SetMemberCopy; //0x14
VOID* Used_Self; //0x18
};
};
struct _KPCR* SelfPcr; //0x1c
struct _KPRCB* Prcb; //0x20
UCHAR Irql; //0x24
ULONG IRR; //0x28
ULONG IrrActive; //0x2c
ULONG IDR; //0x30
VOID* KdVersionBlock; //0x34
struct _KIDTENTRY* IDT; //0x38
struct _KGDTENTRY* GDT; //0x3c
struct _KTSS* TSS; //0x40
USHORT MajorVersion; //0x44
USHORT MinorVersion; //0x46
ULONG SetMember; //0x48
ULONG StallScaleFactor; //0x4c
UCHAR SpareUnused; //0x50
UCHAR Number; //0x51
UCHAR Spare0; //0x52
UCHAR SecondLevelCacheAssociativity; //0x53
ULONG VdmAlert; //0x54
ULONG KernelReserved[14]; //0x58
ULONG SecondLevelCacheSize; //0x90
ULONG HalReserved[16]; //0x94
ULONG InterruptMode; //0xd4
UCHAR Spare1; //0xd8
ULONG KernelReserved2[17]; //0xdc
struct _KPRCB PrcbData; //0x120
};
4.2.1 KPRCB
该结构是kpcr的扩展属性,在分析中需要使用到CurrentThread属性,该属性就是当前执行线程的结构。
//0x3628 bytes (sizeof)
struct _KPRCB
{
USHORT MinorVersion; //0x0
USHORT MajorVersion; //0x2
struct _KTHREAD* CurrentThread; //0x4
struct _KTHREAD* NextThread; //0x8
struct _KTHREAD* IdleThread; //0xc
UCHAR LegacyNumber; //0x10
UCHAR NestingLevel; //0x11
USHORT BuildType; //0x12
CHAR CpuType; //0x14
CHAR CpuID; //0x15
union
{
USHORT CpuStep; //0x16
struct
{
UCHAR CpuStepping; //0x16
UCHAR CpuModel; //0x17
};
};
struct _KPROCESSOR_STATE ProcessorState; //0x18
ULONG KernelReserved[16]; //0x338
ULONG HalReserved[16]; //0x378
ULONG CFlushSize; //0x3b8
UCHAR CoresPerPhysicalProcessor; //0x3bc
UCHAR LogicalProcessorsPerCore; //0x3bd
UCHAR PrcbPad0[2]; //0x3be
ULONG MHz; //0x3c0
UCHAR CpuVendor; //0x3c4
UCHAR GroupIndex; //0x3c5
USHORT Group; //0x3c6
ULONG GroupSetMember; //0x3c8
ULONG Number; //0x3cc
UCHAR PrcbPad1[72]; //0x3d0
struct _KSPIN_LOCK_QUEUE LockQueue[17]; //0x418
struct _KTHREAD* NpxThread; //0x4a0
ULONG InterruptCount; //0x4a4
ULONG KernelTime; //0x4a8
ULONG UserTime; //0x4ac
ULONG DpcTime; //0x4b0
ULONG DpcTimeCount; //0x4b4
ULONG InterruptTime; //0x4b8
ULONG AdjustDpcThreshold; //0x4bc
ULONG PageColor; //0x4c0
UCHAR DebuggerSavedIRQL; //0x4c4
UCHAR NodeColor; //0x4c5
UCHAR PrcbPad20[2]; //0x4c6
ULONG NodeShiftedColor; //0x4c8
struct _KNODE* ParentNode; //0x4cc
ULONG SecondaryColorMask; //0x4d0
ULONG DpcTimeLimit; //0x4d4
ULONG PrcbPad21[2]; //0x4d8
ULONG CcFastReadNoWait; //0x4e0
ULONG CcFastReadWait; //0x4e4
ULONG CcFastReadNotPossible; //0x4e8
ULONG CcCopyReadNoWait; //0x4ec
ULONG CcCopyReadWait; //0x4f0
ULONG CcCopyReadNoWaitMiss; //0x4f4
volatile LONG MmSpinLockOrdering; //0x4f8
volatile LONG IoReadOperationCount; //0x4fc
volatile LONG IoWriteOperationCount; //0x500
volatile LONG IoOtherOperationCount; //0x504
union _LARGE_INTEGER IoReadTransferCount; //0x508
union _LARGE_INTEGER IoWriteTransferCount; //0x510
union _LARGE_INTEGER IoOtherTransferCount; //0x518
ULONG CcFastMdlReadNoWait; //0x520
ULONG CcFastMdlReadWait; //0x524
ULONG CcFastMdlReadNotPossible; //0x528
ULONG CcMapDataNoWait; //0x52c
ULONG CcMapDataWait; //0x530
ULONG CcPinMappedDataCount; //0x534
ULONG CcPinReadNoWait; //0x538
ULONG CcPinReadWait; //0x53c
ULONG CcMdlReadNoWait; //0x540
ULONG CcMdlReadWait; //0x544
ULONG CcLazyWriteHotSpots; //0x548
ULONG CcLazyWriteIos; //0x54c
ULONG CcLazyWritePages; //0x550
ULONG CcDataFlushes; //0x554
ULONG CcDataPages; //0x558
ULONG CcLostDelayedWrites; //0x55c
ULONG CcFastReadResourceMiss; //0x560
ULONG CcCopyReadWaitMiss; //0x564
ULONG CcFastMdlReadResourceMiss; //0x568
ULONG CcMapDataNoWaitMiss; //0x56c
ULONG CcMapDataWaitMiss; //0x570
ULONG CcPinReadNoWaitMiss; //0x574
ULONG CcPinReadWaitMiss; //0x578
ULONG CcMdlReadNoWaitMiss; //0x57c
ULONG CcMdlReadWaitMiss; //0x580
ULONG CcReadAheadIos; //0x584
ULONG KeAlignmentFixupCount; //0x588
ULONG KeExceptionDispatchCount; //0x58c
ULONG KeSystemCalls; //0x590
ULONG AvailableTime; //0x594
ULONG PrcbPad22[2]; //0x598
struct _PP_LOOKASIDE_LIST PPLookasideList[16]; //0x5a0
struct _GENERAL_LOOKASIDE_POOL PPNPagedLookasideList[32]; //0x620
struct _GENERAL_LOOKASIDE_POOL PPPagedLookasideList[32]; //0xf20
volatile ULONG PacketBarrier; //0x1820
volatile LONG ReverseStall; //0x1824
VOID* IpiFrame; //0x1828
UCHAR PrcbPad3[52]; //0x182c
VOID* volatile CurrentPacket[3]; //0x1860
volatile ULONG TargetSet; //0x186c
VOID (* volatileWorkerRoutine)(VOID* arg1, VOID* arg2, VOID* arg3, VOID* arg4); //0x1870
volatile ULONG IpiFrozen; //0x1874
UCHAR PrcbPad4[40]; //0x1878
volatile ULONG RequestSummary; //0x18a0
struct _KPRCB* volatile SignalDone; //0x18a4
UCHAR PrcbPad50[56]; //0x18a8
struct _KDPC_DATA DpcData[2]; //0x18e0
VOID* DpcStack; //0x1908
LONG MaximumDpcQueueDepth; //0x190c
ULONG DpcRequestRate; //0x1910
ULONG MinimumDpcRate; //0x1914
ULONG DpcLastCount; //0x1918
ULONG PrcbLock; //0x191c
struct _KGATE DpcGate; //0x1920
UCHAR ThreadDpcEnable; //0x1930
volatile UCHAR QuantumEnd; //0x1931
volatile UCHAR DpcRoutineActive; //0x1932
volatile UCHAR IdleSchedule; //0x1933
union
{
volatile LONG DpcRequestSummary; //0x1934
SHORT DpcRequestSlot[2]; //0x1934
struct
{
SHORT NormalDpcState; //0x1934
union
{
volatile USHORT DpcThreadActive:1; //0x1936
SHORT ThreadDpcState; //0x1936
};
};
};
volatile ULONG TimerHand; //0x1938
ULONG LastTick; //0x193c
LONG MasterOffset; //0x1940
ULONG PrcbPad41[2]; //0x1944
ULONG PeriodicCount; //0x194c
ULONG PeriodicBias; //0x1950
ULONGLONG TickOffset; //0x1958
struct _KTIMER_TABLE TimerTable; //0x1960
struct _KDPC CallDpc; //0x31a0
LONG ClockKeepAlive; //0x31c0
UCHAR ClockCheckSlot; //0x31c4
UCHAR ClockPollCycle; //0x31c5
UCHAR PrcbPad6[2]; //0x31c6
LONG DpcWatchdogPeriod; //0x31c8
LONG DpcWatchdogCount; //0x31cc
LONG ThreadWatchdogPeriod; //0x31d0
LONG ThreadWatchdogCount; //0x31d4
volatile LONG KeSpinLockOrdering; //0x31d8
ULONG PrcbPad70[1]; //0x31dc
struct _LIST_ENTRY WaitListHead; //0x31e0
ULONG WaitLock; //0x31e8
ULONG ReadySummary; //0x31ec
ULONG QueueIndex; //0x31f0
struct _SINGLE_LIST_ENTRY DeferredReadyListHead; //0x31f4
ULONGLONG StartCycles; //0x31f8
volatile ULONGLONG CycleTime; //0x3200
volatile ULONG HighCycleTime; //0x3208
ULONG PrcbPad71; //0x320c
ULONGLONG PrcbPad72[2]; //0x3210
struct _LIST_ENTRY DispatcherReadyListHead[32]; //0x3220
VOID* ChainedInterruptList; //0x3320
LONG LookasideIrpFloat; //0x3324
volatile LONG MmPageFaultCount; //0x3328
volatile LONG MmCopyOnWriteCount; //0x332c
volatile LONG MmTransitionCount; //0x3330
volatile LONG MmCacheTransitionCount; //0x3334
volatile LONG MmDemandZeroCount; //0x3338
volatile LONG MmPageReadCount; //0x333c
volatile LONG MmPageReadIoCount; //0x3340
volatile LONG MmCacheReadCount; //0x3344
volatile LONG MmCacheIoCount; //0x3348
volatile LONG MmDirtyPagesWriteCount; //0x334c
volatile LONG MmDirtyWriteIoCount; //0x3350
volatile LONG MmMappedPagesWriteCount; //0x3354
volatile LONG MmMappedWriteIoCount; //0x3358
volatile ULONG CachedCommit; //0x335c
volatile ULONG CachedResidentAvailable; //0x3360
VOID* HyperPte; //0x3364
UCHAR PrcbPad8[4]; //0x3368
UCHAR VendorString[13]; //0x336c
UCHAR InitialApicId; //0x3379
UCHAR LogicalProcessorsPerPhysicalProcessor; //0x337a
UCHAR PrcbPad9[5]; //0x337b
ULONG FeatureBits; //0x3380
union _LARGE_INTEGER UpdateSignature; //0x3388
volatile ULONGLONG IsrTime; //0x3390
ULONGLONG RuntimeAccumulation; //0x3398
struct _PROCESSOR_POWER_STATE PowerState; //0x33a0
struct _KDPC DpcWatchdogDpc; //0x3468
struct _KTIMER DpcWatchdogTimer; //0x3488
VOID* WheaInfo; //0x34b0
VOID* EtwSupport; //0x34b4
union _SLIST_HEADER InterruptObjectPool; //0x34b8
union _SLIST_HEADER HypercallPageList; //0x34c0
VOID* HypercallPageVirtual; //0x34c8
VOID* VirtualApicAssist; //0x34cc
ULONGLONG* StatisticsPage; //0x34d0
VOID* RateControl; //0x34d4
struct _CACHE_DESCRIPTOR Cache[5]; //0x34d8
ULONG CacheCount; //0x3514
ULONG CacheProcessorMask[5]; //0x3518
struct _KAFFINITY_EX PackageProcessorSet; //0x352c
ULONG PrcbPad91[1]; //0x3538
ULONG CoreProcessorSet; //0x353c
struct _KDPC TimerExpirationDpc; //0x3540
ULONG SpinLockAcquireCount; //0x3560
ULONG SpinLockContentionCount; //0x3564
ULONG SpinLockSpinCount; //0x3568
ULONG IpiSendRequestBroadcastCount; //0x356c
ULONG IpiSendRequestRoutineCount; //0x3570
ULONG IpiSendSoftwareInterruptCount; //0x3574
ULONG ExInitializeResourceCount; //0x3578
ULONG ExReInitializeResourceCount; //0x357c
ULONG ExDeleteResourceCount; //0x3580
ULONG ExecutiveResourceAcquiresCount; //0x3584
ULONG ExecutiveResourceContentionsCount; //0x3588
ULONG ExecutiveResourceReleaseExclusiveCount; //0x358c
ULONG ExecutiveResourceReleaseSharedCount; //0x3590
ULONG ExecutiveResourceConvertsCount; //0x3594
ULONG ExAcqResExclusiveAttempts; //0x3598
ULONG ExAcqResExclusiveAcquiresExclusive; //0x359c
ULONG ExAcqResExclusiveAcquiresExclusiveRecursive; //0x35a0
ULONG ExAcqResExclusiveWaits; //0x35a4
ULONG ExAcqResExclusiveNotAcquires; //0x35a8
ULONG ExAcqResSharedAttempts; //0x35ac
ULONG ExAcqResSharedAcquiresExclusive; //0x35b0
ULONG ExAcqResSharedAcquiresShared; //0x35b4
ULONG ExAcqResSharedAcquiresSharedRecursive; //0x35b8
ULONG ExAcqResSharedWaits; //0x35bc
ULONG ExAcqResSharedNotAcquires; //0x35c0
ULONG ExAcqResSharedStarveExclusiveAttempts; //0x35c4
ULONG ExAcqResSharedStarveExclusiveAcquiresExclusive; //0x35c8
ULONG ExAcqResSharedStarveExclusiveAcquiresShared; //0x35cc
ULONG ExAcqResSharedStarveExclusiveAcquiresSharedRecursive; //0x35d0
ULONG ExAcqResSharedStarveExclusiveWaits; //0x35d4
ULONG ExAcqResSharedStarveExclusiveNotAcquires; //0x35d8
ULONG ExAcqResSharedWaitForExclusiveAttempts; //0x35dc
ULONG ExAcqResSharedWaitForExclusiveAcquiresExclusive; //0x35e0
ULONG ExAcqResSharedWaitForExclusiveAcquiresShared; //0x35e4
ULONG ExAcqResSharedWaitForExclusiveAcquiresSharedRecursive; //0x35e8
ULONG ExAcqResSharedWaitForExclusiveWaits; //0x35ec
ULONG ExAcqResSharedWaitForExclusiveNotAcquires; //0x35f0
ULONG ExSetResOwnerPointerExclusive; //0x35f4
ULONG ExSetResOwnerPointerSharedNew; //0x35f8
ULONG ExSetResOwnerPointerSharedOld; //0x35fc
ULONG ExTryToAcqExclusiveAttempts; //0x3600
ULONG ExTryToAcqExclusiveAcquires; //0x3604
ULONG ExBoostExclusiveOwner; //0x3608
ULONG ExBoostSharedOwners; //0x360c
ULONG ExEtwSynchTrackingNotificationsCount; //0x3610
ULONG ExEtwSynchTrackingNotificationsAccountedCount; //0x3614
struct _CONTEXT* Context; //0x3618
ULONG ContextFlags; //0x361c
struct _XSAVE_AREA* ExtendedState; //0x3620
};
4.2.3 KTRAP_FRAME
该结构是保存3环的环境,在前切换进行0环后esp指向该结构中的HardwareSegSs属性,注意这个esp不是从msr的175位获取的,而是从TSS中获取的(当前不明白没关系,等分析了入口函数就清除了)。
//0x8c bytes (sizeof)
struct _KTRAP_FRAME
{
ULONG DbgEbp; //0x0
ULONG DbgEip; //0x4
ULONG DbgArgMark; //0x8
ULONG DbgArgPointer; //0xc
USHORT TempSegCs; //0x10
UCHAR Logging; //0x12
UCHAR Reserved; //0x13
ULONG TempEsp; //0x14
ULONG Dr0; //0x18
ULONG Dr1; //0x1c
ULONG Dr2; //0x20
ULONG Dr3; //0x24
ULONG Dr6; //0x28
ULONG Dr7; //0x2c
ULONG SegGs; //0x30
ULONG SegEs; //0x34
ULONG SegDs; //0x38
ULONG Edx; //0x3c
ULONG Ecx; //0x40
ULONG Eax; //0x44
ULONG PreviousPreviousMode; //0x48
struct _EXCEPTION_REGISTRATION_RECORD* ExceptionList; //0x4c
ULONG SegFs; //0x50
ULONG Edi; //0x54
ULONG Esi; //0x58
ULONG Ebx; //0x5c
ULONG Ebp; //0x60
ULONG ErrCode; //0x64
ULONG Eip; //0x68
ULONG SegCs; //0x6c
ULONG EFlags; //0x70
ULONG HardwareEsp; //0x74
ULONG HardwareSegSs; //0x78
ULONG V86Es; //0x7c
ULONG V86Ds; //0x80
ULONG V86Fs; //0x84
ULONG V86Gs; //0x88
};
4.3 分析KiFastEntry函数
IDA打开ntoskrnl.exe查找KiFastEntry函数。
mov ecx, 23h ; '#'
push 30h ; '0'
pop fs ; 将fs寄存器设置为内核态的30
mov ds, ecx ; ds设置为23
mov es, ecx ; es设置为23
mov ecx, large fs:_KPCR.TSS ; 获取TSS
mov esp, [ecx+_KTSS.Esp0] ; 修改esp,将esp指向_KTRAP_FRAME结构的HardwareSegSs位置
push 23h ; '#' ; 保存三环的ss
push edx ; 保存三环ESP
pushf ; 保存标志寄存器
_KiFastCallEntry endp
; =============== S U B R O U T I N E =======================================
KiFastCallEntryCommon proc near ; CODE XREF: _KiFastCallEntry2+23↑j
; FUNCTION CHUNK AT .text:004351D9 SIZE 00000024 BYTES
push 2 ; 将2压入栈中,用于修改eflags
add edx, 8
popf ; 将eflags存器设置为2
or byte ptr [esp+1], 2 ; 关闭中断
push 1Bh ; 保护3环CS
push dword ptr ds:0FFDF0304h ; 将返回地址压入栈中,ds后的地址是_KUSER_SHARED_DATA结构的SystemCallReturn属性
push 0 ; 压入错误代码
push ebp ; 保存三环ebp
push ebx ; 保存三环ebx
push esi ; 保存三环esi
push edi ; 保存三环edi
mov ebx, large fs:_KPCR.SelfPcr ; ebx设置为kpcr结构指针
push 3Bh ; ';' ; 保存三环fs
mov esi, [ebx+_KPCR.PrcbData.CurrentThread] ; 将当前线程结构指针存入esi中
push dword ptr [ebx] ; 保存用户的Used_ExceptionList
mov dword ptr [ebx], 0FFFFFFFFh ; 填充用户异常链表
mov ebp, [esi+_KTHREAD.InitialStack] ; 设置栈底
push 1
sub esp, 48h ; 将esp进行提升,当前esp指向_KTRAP_FRAME头部
sub ebp, 29Ch ; 提升栈底,提升后栈底也指向_KTRAP_FRAME头部
mov [esi+_KTHREAD.PreviousMode], 1 ; 设置线程模式
cmp ebp, esp ; 判断堆栈是否正确
jnz loc_4351D9 ; 不正确就进行跳转,跳转后直接蓝屏
test byte ptr [ebp+_KTRAP_FRAME.SegCs], 1 ; 验证原CS为三环的CS
jnz short loc_43528C ; 如果原CS是三环的就进行跳转
在上面代码代码中做以下几步操作:
- 修改fs寄存器 ,将fs寄存器修改成0环使用的30;
- 从TSS中获取0环需要使用的esp(在这里我们就可以知道windows其实没有msr中的esp值).
- 关闭中断
- 向KTRAP_FRAME中保存3环的环境
- 从线程结构获取0环的栈底赋值给esp
- 修改线程执行模式
loc_43528C: mov [ebp+_KTRAP_FRAME._Eax], eax ; 保存eax
mov [ebp+_KTRAP_FRAME._Edx], edx ; 保存edx
mov ecx, large fs:_KPCR.PrcbData.CurrentThread ; 获取当前线程
mov ecx, [ecx+_KTHREAD.Process] ; 获取当前进程结构
mov eax, dword ptr [ecx+_EPROCESS.SecurityDomain]
mov ecx, dword ptr [ecx+(_EPROCESS.SecurityDomain+4)]
mov large fs:_KPCR.PrcbData.TrappedSecurityDomain, eax
mov large fs:_KPCR.PrcbData.TrappedSecurityDomain+4, ecx
movzx eax, large byte ptr fs:_KPCR.PrcbData.BpbKernelSpecCtrl
cmp large fs:_KPCR.PrcbData.BpbCurrentSpecCtrl, al
jz short loc_4352D8 ; 如果BpbCurrentSpecCtrl一样就进行跳转
mov large fs:_KPCR.PrcbData.BpbCurrentSpecCtrl, al ; 如果BpbCurrentSpecCtrl不一样就设置为al的值
mov ecx, 48h ; 'H'
xor edx, edx
wrmsr ; 将eax中的值存入到msr寄存器的48位置
loc_4352D8: ; CODE XREF: KiFastCallEntryCommon+AC↑j
movzx edx, large byte ptr fs:_KPCR.PrcbData.___u102
test edx, 8
jz short loc_4352FB
mov eax, 1
xor edx, edx
mov ecx, 49h ; 'I'
wrmsr ; 将msr寄存器的0x49位置设置为1
jmp loc_435418
以上指令主要也是保存一些值,最重要的将eax、edx保存到了KTRAP_FRAME结构中,这两个寄存器中分别保存了调用号和3环的esp指针。
mov eax, [ebp+_KTRAP_FRAME._Eax]
mov edx, [ebp+_KTRAP_FRAME._Edx]
loc_43541E: ; CODE XREF: KiFastCallEntryCommon+56↑j
and [ebp+_KTRAP_FRAME.Dr7], 0 ; 将TRAP_FRAME的dr7设置为0
test [esi+_KTHREAD.Header.___u0.__s3.DebugActive], 0DFh ; 判断是否在调试模式
mov [esi+_KTHREAD.TrapFrame], ebp ; 设置KTHREAD结构中TrapFrame
jnz Dr_FastCallDrSave ; 如果是调试模式就进行跳转
loc_435432: ; CODE XREF: Dr_FastCallDrSave+D↑j
; Dr_FastCallDrSave+79↑j
mov ebx, [ebp+_KTRAP_FRAME._Ebp]
mov edi, [ebp+_KTRAP_FRAME._Eip]
mov [ebp+_KTRAP_FRAME.DbgArgPointer], edx ; 设置3环参数指针
mov [ebp+_KTRAP_FRAME.DbgArgMark], 0BADB0D00h
mov [ebp+_KTRAP_FRAME.DbgEbp], ebx
mov [ebp+_KTRAP_FRAME.DbgEip], edi
sti ; 开启中断
在以上代码中主要流程如下:
- 验证3环是否存在硬件调试环境,如果存在就跳转到“ Dr_FastCallDrSave”这函数里面保存硬件调试使用到的寄存器。
- 将本次使用到的KTRAP_FEAME结构的地址存储到线程结构中
- 开启中断
五、总结
在本章中分析了函数在调用ssdt之前的步骤,这些步骤中主要是保存了3环的环境并且将执行环境切换到0环。