友链
内存布局
用户内存空间和内核内存空间之间的gap是为了避免不经意的越界而导致安全问题
CPU的初始化
系统启动期间,会对所有的处理器进行初始化操作
大部分初始化操作我们都不必了解,因为你了不了解都不影响你的逆向,毕竟你又不是开发操作系统的
但是艺多不压身,多知道点东西总没有坏处
处理器控制区PCR processor control region是一个结构体,每一个处理器都有一个对应的PCR结构体实例,它存储了CPU的重要信息
在x86中,它包含了IDT的基地址和当前的IRQL(interrupt request level——中断请求等级)
在PCR结构体中还有另一个结构体PRCB——processor region control block,他保存了CPU类型、模式、速率、当前运行的线程等信息
0: kd> dt nt!_KPCR
+0x000 NtTib : _NT_TIB
+0x000 GdtBase : Ptr64 _KGDTENTRY64
+0x008 TssBase : Ptr64 _KTSS64
+0x010 UserRsp : Uint8B
+0x018 Self : Ptr64 _KPCR
+0x020 CurrentPrcb : Ptr64 _KPRCB
+0x028 LockArray : Ptr64 _KSPIN_LOCK_QUEUE
+0x030 Used_Self : Ptr64 Void
+0x038 IdtBase : Ptr64 _KIDTENTRY64
+0x040 Unused : [2] Uint8B
+0x050 Irql : UChar
+0x051 SecondLevelCacheAssociativity : UChar
+0x052 ObsoleteNumber : UChar
+0x053 Fill0 : UChar
+0x054 Unused0 : [3] Uint4B
+0x060 MajorVersion : Uint2B
+0x062 MinorVersion : Uint2B
+0x064 StallScaleFactor : Uint4B
+0x068 Unused1 : [3] Ptr64 Void
+0x080 KernelReserved : [15] Uint4B
+0x0bc SecondLevelCacheSize : Uint4B
+0x0c0 HalReserved : [16] Uint4B
+0x100 Unused2 : Uint4B
+0x108 KdVersionBlock : Ptr64 Void
+0x110 Unused3 : Ptr64 Void
+0x118 PcrAlign1 : [24] Uint4B
+0x180 Prcb : _KPRCB
偏移量0x180h处就是PRCB结构体
0: kd> dt nt!_KPRCB
+0x000 MxCsr : Uint4B
+0x004 LegacyNumber : UChar
+0x005 ReservedMustBeZero : UChar
+0x006 InterruptRequest : UChar
+0x007 IdleHalt : UChar
+0x008 CurrentThread : Ptr64 _KTHREAD
+0x010 NextThread : Ptr64 _KTHREAD
+0x018 IdleThread : Ptr64 _KTHREAD
+0x020 NestingLevel : UChar
+0x021 ClockOwner : UChar
+0x022 PendingTickFlags : UChar
+0x022 PendingTick : Pos 0, 1 Bit
+0x022 PendingBackupTick : Pos 1, 1 Bit
+0x023 PrcbPad00 : [1] UChar
+0x024 Number : Uint4B
+0x028 RspBase : Uint8B
+0x030 PrcbLock : Uint8B
+0x038 PriorityState : Ptr64 Char
+0x040 ProcessorState : _KPROCESSOR_STATE
+0x5f0 CpuType : Char
+0x5f1 CpuID : Char
+0x5f2 CpuStep : Uint2B
+0x5f2 CpuStepping : UChar
+0x5f3 CpuModel : UChar
+0x5f4 MHz : Uint4B
+0x5f8 HalReserved : [8] Uint8B
+0x638 MinorVersion : Uint2B
+0x63a MajorVersion : Uint2B
+0x63c BuildType : UChar
+0x63d CpuVendor : UChar
+0x63e CoresPerPhysicalProcessor : UChar
+0x63f LogicalProcessorsPerCore : UChar
+0x640 ParentNode : Ptr64 _KNODE
+0x648 GroupSetMember : Uint8B
+0x650 Group : UChar
+0x651 GroupIndex : UChar
+0x652 PrcbPad05 : [2] UChar
+0x654 ApicMask : Uint4B
+0x658 CFlushSize : Uint4B
+0x660 AcpiReserved : Ptr64 Void
+0x668 InitialApicId : Uint4B
+0x670 LockQueue : [17] _KSPIN_LOCK_QUEUE
+0x780 PPLookasideList : [16] _PP_LOOKASIDE_LIST
+0x880 PPNxPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL
+0x1480 PPNPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL
+0x2080 PPPagedLookasideList : [32] _GENERAL_LOOKASIDE_POOL
+0x2c80 PrcbPad20 : Uint8B
+0x2c88 DeferredReadyListHead : _SINGLE_LIST_ENTRY
+0x2c90 MmPageFaultCount : Int4B
+0x2c94 MmCopyOnWriteCount : Int4B
+0x2c98 MmTransitionCount : Int4B
+0x2c9c MmDemandZeroCount : Int4B
+0x2ca0 MmPageReadCount : Int4B
+0x2ca4 MmPageReadIoCount : Int4B
+0x2ca8 MmDirtyPagesWriteCount : Int4B
+0x2cac MmDirtyWriteIoCount : Int4B
+0x2cb0 MmMappedPagesWriteCount : Int4B
+0x2cb4 MmMappedWriteIoCount : Int4B
+0x2cb8 KeSystemCalls : Uint4B
+0x2cbc KeContextSwitches : Uint4B
+0x2cc0 LdtSelector : Uint2B
+0x2cc2 PrcbPad40 : Uint2B
+0x2cc4 CcFastReadNoWait : Uint4B
+0x2cc8 CcFastReadWait : Uint4B
+0x2ccc CcFastReadNotPossible : Uint4B
+0x2cd0 CcCopyReadNoWait : Uint4B
+0x2cd4 CcCopyReadWait : Uint4B
+0x2cd8 CcCopyReadNoWaitMiss : Uint4B
+0x2cdc IoReadOperationCount : Int4B
+0x2ce0 IoWriteOperationCount : Int4B
+0x2ce4 IoOtherOperationCount : Int4B
+0x2ce8 IoReadTransferCount : _LARGE_INTEGER
+0x2cf0 IoWriteTransferCount : _LARGE_INTEGER
+0x2cf8 IoOtherTransferCount : _LARGE_INTEGER
+0x2d00 PacketBarrier : Int4B
+0x2d04 TargetCount : Int4B
+0x2d08 IpiFrozen : Uint4B
+0x2d10 IsrDpcStats : Ptr64 Void
+0x2d18 DeviceInterrupts : Uint4B
+0x2d1c LookasideIrpFloat : Int4B
+0x2d20 InterruptLastCount : Uint4B
+0x2d24 InterruptRate : Uint4B
+0x2d28 PrcbPad41 : [22] Uint4B
+0x2d80 DpcData : [2] _KDPC_DATA
+0x2dd0 DpcStack : Ptr64 Void
+0x2dd8 MaximumDpcQueueDepth : Int4B
+0x2ddc DpcRequestRate : Uint4B
+0x2de0 MinimumDpcRate : Uint4B
+0x2de4 DpcLastCount : Uint4B
+0x2de8 ThreadDpcEnable : UChar
+0x2de9 QuantumEnd : UChar
+0x2dea DpcRoutineActive : UChar
+0x2deb IdleSchedule : UChar
+0x2dec DpcRequestSummary : Int4B
+0x2dec DpcRequestSlot : [2] Int2B
+0x2dec NormalDpcState : Int2B
+0x2dee ThreadDpcState : Int2B
+0x2dec DpcNormalProcessingActive : Pos 0, 1 Bit
+0x2dec DpcNormalProcessingRequested : Pos 1, 1 Bit
+0x2dec DpcNormalThreadSignal : Pos 2, 1 Bit
+0x2dec DpcNormalTimerExpiration : Pos 3, 1 Bit
+0x2dec DpcNormalDpcPresent : Pos 4, 1 Bit
+0x2dec DpcNormalLocalInterrupt : Pos 5, 1 Bit
+0x2dec DpcNormalSpare : Pos 6, 10 Bits
+0x2dec DpcThreadActive : Pos 16, 1 Bit
+0x2dec DpcThreadRequested : Pos 17, 1 Bit
+0x2dec DpcThreadSpare : Pos 18, 14 Bits
+0x2df0 LastTimerHand : Uint4B
+0x2df4 LastTick : Uint4B
+0x2df8 ClockInterrupts : Uint4B
+0x2dfc ReadyScanTick : Uint4B
+0x2e00 TimerTable : _KTIMER_TABLE
+0x5000 DpcGate : _KGATE
+0x5018 PrcbPad52 : Ptr64 Void
+0x5020 CallDpc : _KDPC
+0x5060 ClockKeepAlive : Int4B
+0x5064 PrcbPad60 : [2] UChar
+0x5066 NmiActive : Uint2B
+0x5068 DpcWatchdogPeriod : Int4B
+0x506c DpcWatchdogCount : Int4B
+0x5070 KeSpinLockOrdering : Int4B
+0x5074 PrcbPad70 : [1] Uint4B
+0x5078 CachedPtes : Ptr64 Void
+0x5080 WaitListHead : _LIST_ENTRY
+0x5090 WaitLock : Uint8B
+0x5098 ReadySummary : Uint4B
+0x509c AffinitizedSelectionMask : Int4B
+0x50a0 QueueIndex : Uint4B
+0x50a4 PrcbPad75 : [3] Uint4B
+0x50b0 TimerExpirationDpc : _KDPC
+0x50f0 ScbQueue : _RTL_RB_TREE
+0x5100 DispatcherReadyListHead : [32] _LIST_ENTRY
+0x5300 InterruptCount : Uint4B
+0x5304 KernelTime : Uint4B
+0x5308 UserTime : Uint4B
+0x530c DpcTime : Uint4B
+0x5310 InterruptTime : Uint4B
+0x5314 AdjustDpcThreshold : Uint4B
+0x5318 DebuggerSavedIRQL : UChar
+0x5319 GroupSchedulingOverQuota : UChar
+0x531a DeepSleep : UChar
+0x531b PrcbPad80 : [1] UChar
+0x531c ScbOffset : Uint4B
+0x5320 DpcTimeCount : Uint4B
+0x5324 DpcTimeLimit : Uint4B
+0x5328 PeriodicCount : Uint4B
+0x532c PeriodicBias : Uint4B
+0x5330 AvailableTime : Uint4B
+0x5334 KeExceptionDispatchCount : Uint4B
+0x5338 StartCycles : Uint8B
+0x5340 GenerationTarget : Uint8B
+0x5348 AffinitizedCycles : Uint8B
+0x5350 PrcbPad81 : [2] Uint8B
+0x5360 MmSpinLockOrdering : Int4B
+0x5364 PageColor : Uint4B
+0x5368 NodeColor : Uint4B
+0x536c NodeShiftedColor : Uint4B
+0x5370 SecondaryColorMask : Uint4B
+0x5374 PrcbPad83 : Uint4B
+0x5378 CycleTime : Uint8B
+0x5380 CcFastMdlReadNoWait : Uint4B
+0x5384 CcFastMdlReadWait : Uint4B
+0x5388 CcFastMdlReadNotPossible : Uint4B
+0x538c CcMapDataNoWait : Uint4B
+0x5390 CcMapDataWait : Uint4B
+0x5394 CcPinMappedDataCount : Uint4B
+0x5398 CcPinReadNoWait : Uint4B
+0x539c CcPinReadWait : Uint4B
+0x53a0 CcMdlReadNoWait : Uint4B
+0x53a4 CcMdlReadWait : Uint4B
+0x53a8 CcLazyWriteHotSpots : Uint4B
+0x53ac CcLazyWriteIos : Uint4B
+0x53b0 CcLazyWritePages : Uint4B
+0x53b4 CcDataFlushes : Uint4B
+0x53b8 CcDataPages : Uint4B
+0x53bc CcLostDelayedWrites : Uint4B
+0x53c0 CcFastReadResourceMiss : Uint4B
+0x53c4 CcCopyReadWaitMiss : Uint4B
+0x53c8 CcFastMdlReadResourceMiss : Uint4B
+0x53cc CcMapDataNoWaitMiss : Uint4B
+0x53d0 CcMapDataWaitMiss : Uint4B
+0x53d4 CcPinReadNoWaitMiss : Uint4B
+0x53d8 CcPinReadWaitMiss : Uint4B
+0x53dc CcMdlReadNoWaitMiss : Uint4B
+0x53e0 CcMdlReadWaitMiss : Uint4B
+0x53e4 CcReadAheadIos : Uint4B
+0x53e8 MmCacheTransitionCount : Int4B
+0x53ec MmCacheReadCount : Int4B
+0x53f0 MmCacheIoCount : Int4B
+0x53f4 PrcbPad91 : [3] Uint4B
+0x5400 PowerState : _PROCESSOR_POWER_STATE
+0x55e0 ScbList : _LIST_ENTRY
+0x55f0 PrcbPad92 : [19] Uint4B
+0x563c KeAlignmentFixupCount : Uint4B
+0x5640 DpcWatchdogDpc : _KDPC
+0x5680 DpcWatchdogTimer : _KTIMER
+0x56c0 Cache : [5] _CACHE_DESCRIPTOR
+0x56fc CacheCount : Uint4B
+0x5700 CachedCommit : Uint4B
+0x5704 CachedResidentAvailable : Uint4B
+0x5708 HyperPte : Ptr64 Void
+0x5710 WheaInfo : Ptr64 Void
+0x5718 EtwSupport : Ptr64 Void
+0x5720 InterruptObjectPool : _SLIST_HEADER
+0x5730 HypercallPageList : _SLIST_HEADER
+0x5740 HypercallPageVirtual : Ptr64 Void
+0x5748 VirtualApicAssist : Ptr64 Void
+0x5750 StatisticsPage : Ptr64 Uint8B
+0x5758 PackageProcessorSet : _KAFFINITY_EX
+0x5800 SharedReadyQueueMask : Uint8B
+0x5808 SharedReadyQueue : Ptr64 _KSHARED_READY_QUEUE
+0x5810 CoreProcessorSet : Uint8B
+0x5818 ScanSiblingMask : Uint8B
+0x5820 LLCMask : Uint8B
+0x5828 CacheProcessorMask : [5] Uint8B
+0x5850 ScanSiblingIndex : Uint4B
+0x5854 SharedReadyQueueOffset : Uint4B
+0x5858 ProcessorProfileControlArea : Ptr64 _PROCESSOR_PROFILE_CONTROL_AREA
+0x5860 ProfileEventIndexAddress : Ptr64 Void
+0x5868 PrcbPad94 : [3] Uint8B
+0x5880 SynchCounters : _SYNCH_COUNTERS
+0x5938 PteBitCache : Uint8B
+0x5940 PteBitOffset : Uint4B
+0x5948 FsCounters : _FILESYSTEM_DISK_COUNTERS
+0x5958 VendorString : [13] UChar
+0x5965 PrcbPad10 : [3] UChar
+0x5968 FeatureBits : Uint8B
+0x5970 PrcbPad11 : Uint4B
+0x5978 UpdateSignature : _LARGE_INTEGER
+0x5980 Context : Ptr64 _CONTEXT
+0x5988 ContextFlagsInit : Uint4B
+0x5990 ExtendedState : Ptr64 _XSAVE_AREA
+0x5998 IsrStack : Ptr64 Void
+0x59a0 EntropyTimingState : _KENTROPY_TIMING_STATE
+0x5af0 AbSelfIoBoostsList : _SINGLE_LIST_ENTRY
+0x5af8 AbPropagateBoostsList : _SINGLE_LIST_ENTRY
+0x5b00 AbDpc : _KDPC
+0x5b40 IoIrpStackProfilerCurrent : _IOP_IRP_STACK_PROFILER
+0x5b94 IoIrpStackProfilerPrevious : _IOP_IRP_STACK_PROFILER
+0x5be8 TimerExpirationTrace : [16] _KTIMER_EXPIRATION_TRACE
+0x5ce8 TimerExpirationTraceCount : Uint4B
+0x5d00 Mailbox : Ptr64 _REQUEST_MAILBOX
+0x5d40 RequestMailbox : [1] _REQUEST_MAILBOX
this structure is fucking massive
PCR可以在内核模式中通过寄存器去访问到
在windows中,可以通过两个内核方法来获取到EPROCESS和ETHREAD,这两个方法就是通过查询PCR/PRCB来实现的
0: kd> uf nt!psgetcurrentthread
nt!KeGetCurrentThread:
fffff801`710dcc9c 65488b042588010000 mov rax,qword ptr gs:[188h]
fffff801`710dcca5 c3 ret
因此我们可以判断出gs段寄存器就是PCR的地址,0x188h正好是0x180h+0x8h,也就是PRCB的CurrentThread成员
nt!PsGetCurrentProcess:
fffff801`711661b8 65488b042588010000 mov rax,qword ptr gs:[188h]
fffff801`711661c1 488b80b8000000 mov rax,qword ptr [rax+0B8h]
fffff801`711661c8 c3 ret
这个方法可真是套娃中的套娃
gs:[188h]是PCR结构体中的PRCB结构体的KTHREAD结构体
再加b8h就是KTHREAD结构体中的_KAPC_STATE结构体的_KPROCESS结构体
0: kd> dt nt!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x018 SListFaultAddress : Ptr64 Void
+0x020 QuantumTarget : Uint8B
+0x028 InitialStack : Ptr64 Void
+0x030 StackLimit : Ptr64 Void
+0x038 StackBase : Ptr64 Void
+0x040 ThreadLock : Uint8B
+0x048 CycleTime : Uint8B
+0x050 CurrentRunTime : Uint4B
+0x054 ExpectedRunTime : Uint4B
+0x058 KernelStack : Ptr64 Void
+0x060 StateSaveArea : Ptr64 _XSAVE_FORMAT
+0x068 SchedulingGroup : Ptr64 _KSCHEDULING_GROUP
+0x070 WaitRegister : _KWAIT_STATUS_REGISTER
+0x071 Running : UChar
+0x072 Alerted : [2] UChar
+0x074 SpareMiscFlag0 : Pos 0, 1 Bit
+0x074 ReadyTransition : Pos 1, 1 Bit
+0x074 ProcessReadyQueue : Pos 2, 1 Bit
+0x074 WaitNext : Pos 3, 1 Bit
+0x074 SystemAffinityActive : Pos 4, 1 Bit
+0x074 Alertable : Pos 5, 1 Bit
+0x074 UserStackWalkActive : Pos 6, 1 Bit
+0x074 ApcInterruptRequest : Pos 7, 1 Bit
+0x074 QuantumEndMigrate : Pos 8, 1 Bit
+0x074 UmsDirectedSwitchEnable : Pos 9, 1 Bit
+0x074 TimerActive : Pos 10, 1 Bit
+0x074 SystemThread : Pos 11, 1 Bit
+0x074 ProcessDetachActive : Pos 12, 1 Bit
+0x074 CalloutActive : Pos 13, 1 Bit
+0x074 ScbReadyQueue : Pos 14, 1 Bit
+0x074 ApcQueueable : Pos 15, 1 Bit
+0x074 ReservedStackInUse : Pos 16, 1 Bit
+0x074 UmsPerformingSyscall : Pos 17, 1 Bit
+0x074 ApcPendingReload : Pos 18, 1 Bit
+0x074 TimerSuspended : Pos 19, 1 Bit
+0x074 SuspendedWaitMode : Pos 20, 1 Bit
+0x074 Reserved : Pos 21, 11 Bits
+0x074 MiscFlags : Int4B
+0x078 AutoAlignment : Pos 0, 1 Bit
+0x078 DisableBoost : Pos 1, 1 Bit
+0x078 UserAffinitySet : Pos 2, 1 Bit
+0x078 AlertedByThreadId : Pos 3, 1 Bit
+0x078 QuantumDonation : Pos 4, 1 Bit
+0x078 EnableStackSwap : Pos 5, 1 Bit
+0x078 GuiThread : Pos 6, 1 Bit
+0x078 DisableQuantum : Pos 7, 1 Bit
+0x078 ChargeOnlySchedulingGroup : Pos 8, 1 Bit
+0x078 DeferPreemption : Pos 9, 1 Bit
+0x078 QueueDeferPreemption : Pos 10, 1 Bit
+0x078 ForceDeferSchedule : Pos 11, 1 Bit
+0x078 SharedReadyQueueAffinity : Pos 12, 1 Bit
+0x078 FreezeCount : Pos 13, 1 Bit
+0x078 TerminationApcRequest : Pos 14, 1 Bit
+0x078 AutoBoostEntriesExhausted : Pos 15, 1 Bit
+0x078 KernelStackResident : Pos 16, 1 Bit
+0x078 EtwStackTraceApcInserted : Pos 17, 8 Bits
+0x078 ReservedFlags : Pos 25, 7 Bits
+0x078 ThreadFlags : Int4B
+0x07c Spare0 : Uint4B
+0x080 SystemCallNumber : Uint4B
+0x084 Spare1 : Uint4B
+0x088 FirstArgument : Ptr64 Void
+0x090 TrapFrame : Ptr64 _KTRAP_FRAME
+0x098 ApcState : _KAPC_STATE
+0x098 ApcStateFill : [43] UChar
0: kd> dt _KAPC_STATE
nt!_KAPC_STATE
+0x000 ApcListHead : [2] _LIST_ENTRY
+0x020 Process : Ptr64 _KPROCESS
+0x028 InProgressFlags : UChar
+0x028 KernelApcInProgress : Pos 0, 1 Bit
+0x028 SpecialApcInProgress : Pos 1, 1 Bit
+0x029 KernelApcPending : UChar
+0x02a UserApcPending : UChar
终极套娃
系统调用
windows对syscall的存储和描述位于这两个结构体中:service table descriptor和一个函数指针数组
service table descriptor结构体保存了一些元数据,这些数据描述了OS支持的syscall
这些东西都是没有官方文档的,但是很多人已经通过逆向总结出了他的一些重要字段,通过分析KiSystemCall64和KiSystemservice例程,你也能分析出来
typedef struct _KSERVICE_TABLE_DESCRIPTOR
{
PULONG Base; // array of addresses or offsets
PULONG Count;
ULONG Limit; // size of the array
PUCHAR Number;
...
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
base是一个数组,里面全是函数指针
syscall number就是这个数组里的index
limit是数组的长度
内核会维护两个全局数组KSERVER_DESCRIPTOR和KSERIE_DESCRIPTORTableSHadow
可以看到后者比前者多了一部分,W32pservicetable里面都是GUI syscall
kiservicetabkle指向非GUI syscall,w32pservietable指向GUI syscall
下面是KiServiceTable的内容
不过在64bit的操作系统上就稍有不同了
在64bit操作系统上,kiservicetable指向的是一个成员为32bit的整型数的数组,该数字的高28bit是一个偏移量,指向syscall的位置,剩下的14bit表示要传给该syscall的参数个数,偏移量加上kiservicetable的地址就是syscall的真正地址
我们先看一下ntdll!ntcreatefile是如何调用syscall的
再看一下kiservicetable的base的地址
0: kd> x nt!kiservicetable
fffff803`12963cb0 nt!KiServiceTable = <no type information>
ntdll!ntcreatefile在调用syscall之前,给eax赋值0x55h,也就是kiservicetable指向的数组中的第0x55h个元素,每个元素占4bytes(32bit)
0: kd> dd nt!kiservicetable+(55*4) L1
fffff803`12963e04 01d3ed07
注意在windbg中进行混合算术运算需要使用括号
这里的L1用于限制结果数量,可以看到只输出了一组
016daf40的前28bit(右移4bit即可)加上kiservicetable即可获得syscall的真正地址
0: kd> u nt!kiservicetable+(01d3ed07>>4)
nt!NtCreateFile:
fffff803`12b37b80 4881ec88000000 sub rsp,88h
fffff803`12b37b87 33c0 xor eax,eax
fffff803`12b37b89 4889442478 mov qword ptr [rsp+78h],rax
fffff803`12b37b8e c744247020000000 mov dword ptr [rsp+70h],20h
fffff803`12b37b96 89442468 mov dword ptr [rsp+68h],eax
fffff803`12b37b9a 4889442460 mov qword ptr [rsp+60h],rax
fffff803`12b37b9f 89442458 mov dword ptr [rsp+58h],eax
fffff803`12b37ba3 8b8424e0000000 mov eax,dword ptr [rsp+0E0h]
参数个数是7,要想取得最低的4bit,直接和0xfh做与运算就行了,0xfh->1111b
0: kd> ? 01d3ed07&f
Evaluate expression: 7 = 00000000`00000007
用户模式下的API在底层都是通过多个syscall来实现的
syscall一般通过软件中断或者特定的指令实现
interrupt、traps、faults
外围设备和软件如何与处理器交互?
在当代的计算机系统中,处理器通常通过数据bus连接到外围设备
比如PCI总线、火线、或者USB
当一个设备需要引起处理器注意的时候,它会引发一个中断来强制处理器停止手头的工作转而去处理该设备的请求
那么处理器如何知道怎么去处理这个请求呢?
在高等级层面,你可以把中断想象为一个用于指示一个函数数组的index的数字,档处理器接收到终端的时候,他就会去指向该中断所指向的函数,执行完成之后处理器会返回到自己被中断的地方继续执行
这种被叫做硬件中断,因为他们是直接由外围硬件设备产生的,天生就是异步的
处理器在执行指令的时候可能会遇到exception(异常),比如除0、空指针等
异常可以被分为两种,一种叫做faults,一种叫做traps
fault是可以更正的错误,比如档处理器执行一条引用了合法内存地址的指令,但是该地址的数据不存在的时候(可能是所属内存页swap到硬盘上去了),那么此时一个page fault就会被生成,处理器会保存当前的执行状态,调用page fault handler来纠正这个错误(将page交换回内存中),然后重新执行那条引起fault的指令
trap是由执行特定的指令引起的,比如在64位中,syscall指令会导致处理器开始执行一段由MSR寄存器指向的地址处的指令,handler完了之后,执行会被恢复到syscall的下一条指令
可以看到fault和trap的最大区别就是代码在哪里恢复执行,前者会重复执行一次,后者会执行之后的代码
系统调用通常都是通过特殊的异常或者trap指令来实现的
interrupts
IDT interrupt descriptor table 中断描述符表,256项
每一项都是一个结构体,包含了中断handler的信息
IDT的地址由IDTR寄存器保存
在这256项中,只有32-255是可以由用户自己定义的,0~31是预定义的
32bit系统中,IDT是一个8bytes结构体,定义如下:
0: kd> dt nt!_kidtentry
+0x000 Offset : Uint2B
+0x002 Selector : Uint2B
+0x004 Access : Uint2B
+0x006 ExtendedOffset : Uint2B
在64bit中,IDT的定义和上面差不多,除了中断handler的地址被分成了三个成员
0: kd> dt nt!_kidtentry64
+0x000 OffsetLow : Uint2B
+0x002 Selector : Uint2B
+0x004 IstIndex : Pos 0, 3 Bits
+0x004 Reserved0 : Pos 3, 5 Bits
+0x004 Type : Pos 8, 5 Bits
+0x004 Dpl : Pos 13, 2 Bits
+0x004 Present : Pos 15, 1 Bit
+0x006 OffsetMiddle : Uint2B
+0x008 OffsetHigh : Uint4B
+0x00c Reserved1 : Uint4B
+0x000 Alignment : Uint8B
可以看到,offset被分成了high middle low三部分