内核调试引擎简介
本帖最后由 古月不傲 于 2020-5-29 13:40 编辑内核调试引擎就是调试器与被调试内核之间的桥梁。
Windows启动过程中会调用KdInitSystem函数让内核调试引擎初始化。
当系统分发异常时会调用KiDebugRoutinue变量所指向的函数(KdpTrap或KdpStub)。
系统的时间更新函数KeUpdateRunTime会调用KdCheckForDebugBreak来检查调试器是否发出了中断命令。
内核调试引擎使用一个数组KdpBreakpointTable来记录断点的插入、移除。
内核调试API:调试器与内核调试引擎通过API接口通信,调用API要通过发送数据包、数据包中含有访问服务号和参数,返回结果也是通过数据包回递。
系统内核控制函数:KdEnterDebugger负责将系统内核中断到调试器,KdExitDebugger负责恢复系统运行。
管理函数:KdEnableDebugger和KdDisableDebugger用于启用或禁止内核调试引擎,KdChangeOption修改选项。
WindowsXP以前,内核调试引擎的所有函数位于NTOSKRNL,EXE中。
之后,内核调试引擎的通信部分被拆分到了一个单独的DLL模块中,KDCOM.DLL。
KDCOM.DLL也会引用NTOSKRNL中导出的符号和函数,因此两者是互相依赖关系,
当NTLDR加载任意其一时会加载两者。
KDCOM.DLL中最重要的两个函数KdSendPacket、KdReceivePacket用于发包和收包。
通信方式分为:串口、1394、USB,分别对于KDCOM.DLL、KD1394.DLL、KDUSB.DLL,一般采用第一种方式,主要原因是内核调试通信协议是面向字节的,其他两种面向数据包。
Windows启动过程(简单概述 不像Windows内核原理实现那么详细...)
计算机开机后,先执行BIOS或EFI对硬件进行检测和平台初始化后,将控制权转交给磁盘上的引导程序,NTLDR。
接着NTLDR首先对CPU做必要的初始化工作,实模式到保护模式,启用分页机制,通过配置文件boot.ini或BCD(boot config data)得到Windows系统目录并加载内核模块,加载ntoskrnl或ntkrnlpa并检查它的导入表加载所依赖的文件,其中包括内核调试通信DLL(KDCOM、KD1394或KDUSB)。加载程序会根据启动配置加载其中的一个,并将模块统一成KDCOM。
而后NTLDR读取注册表的System Hive加载其中键值为0的驱动程序,包括磁盘驱动程序。
完成工作后,NTLDR从内核文件的PE头找到它的入口函数,KiSystemStartup函数,
调用这个函数。调用时传递LOADER_PARAMETER_BLOCK数据结构,内核文件得到控制权开始执行。
KiSystemStartup执行过程:
1、调用HalInitializeProcessor初始化CPU
2、调用KdInitSystem初始化内核调试引擎
3、调用KiInitializeKernel初始化内核->
(1) 调用KiInitSystem初始化系统全局数据结构
(2) 调用KiInitializeProcess创建并初始化Idle进程
(3) 调用KeInitializeThread初始化Idle线程
(4) 调用ExpInitializeExecutive对执行体层(管理层)进行阶段0初始化
① 调用MmInitSystem构建页表和内存管理器的基本数据结构
② 调用ObInitSystem建立名称空间
③ 调用SeInitSystem初始化token对象
④ 调用PsInitSytstem对进程管理器做阶段0初始化
⑤ 调用PpInitSystem让即插即用管理器初始化设备链表等等
KiInitializeKernel返回后,KiSystemStartuP降低IRQL至DISPATCH_LEVEL(非分页内存线程切换级别),然后跳到KiIdleLoop,退化为Idle进程中的第一个Idle线程。
对于多CPU系统,每个CPU都会执行KiInitializeKernel,但只有第一个CPU执行所有初始化工作,其他CPU只负责初始化Idle部分,因为每个CPU都有Idle线程。
全局变量KeNumberProcessors标识CPU的个数。
PsInitSytstem阶段0过程:
1、定义进程和线程对象类型
2、建立记录系统中所有进程的链表结构,并用PsAvtiveProcessHead全局变量指向这个链表,此后Windbg!process命令才能工作
3、为初始的进程创建一个进程对象PsIdleProcess,并命名为Idle
4、创建系统进程和线程,并将Phase1Initialization函数作为线程起始地址
Phase1Initialization虽然以创建,但是由于IRQL是2,所以要等到KiInitializeKernel返回后。
阶段1初始化过程:
调用KeStartAllProcessors初始化所有CPU,构建CPU的数据结构
调用HalStartNextProcessor将该结构赋值给下一个CPU
再次调用KdInitSystem,并调用KdDebuggerInitialize1初始化KDCOM等
阶段1结束前,创建第一个进程,会话管理器SMSS.EXE
SMSS.EXE初始化Windows子系统,创建Windows子系统和登录进程Winlogon.exe
Winlogon.exe创建LSASS进程和系统服务进程Services.exe并显示登录画面,启动基本完成。
第一次调用KdInitSystem
1、初始化调试器数据链表,使用全局变量KdpDebbugerDataListHead指向这个链表
2、初始化KdDebuggerDataBlocK数据结构,改结构包含内核基地址、模块链表指针、调试器数据链表指针等,调试器需要读取这些信息以了解目标系统。
typedef struct _KDDEBUGGER_DATA32 ->KdDebuggerDataBlocK
{
DBGKD_DEBUG_DATA_HEADER32 Header;
ULONG KernBase;
ULONG BreakpointWithStatus;
ULONG SavedContext;
USHORT ThCallbackStack;
USHORT NextCallback;
USHORT FramePointer;
USHORT PaeEnabled:1;
ULONG KiCallUserMode;
ULONG KeUserCallbackDispatcher;
ULONG PsLoadedModuleList;
ULONG PsActiveProcessHead;
ULONG PspCidTable;
ULONG ExpSystemResourcesList;
ULONG ExpPagedPoolDescriptor;
ULONG ExpNumberOfPagedPools;
ULONG KeTimeIncrement;
ULONG KeBugCheckCallbackListHead;
ULONG KiBugcheckData;
ULONG IopErrorLogListHead;
ULONG ObpRootDirectoryObject;
ULONG ObpTypeObjectType;
ULONG MmSystemCacheStart;
ULONG MmSystemCacheEnd;
ULONG MmSystemCacheWs;
ULONG MmPfnDatabase;
ULONG MmSystemPtesStart;
ULONG MmSystemPtesEnd;
ULONG MmSubsectionBase;
ULONG MmNumberOfPagingFiles;
ULONG MmLowestPhysicalPage;
ULONG MmHighestPhysicalPage;
ULONG MmNumberOfPhysicalPages;
ULONG MmMaximumNonPagedPoolInBytes;
ULONG MmNonPagedSystemStart;
ULONG MmNonPagedPoolStart;
ULONG MmNonPagedPoolEnd;
ULONG MmPagedPoolStart;
ULONG MmPagedPoolEnd;
ULONG MmPagedPoolInformation;
ULONG MmPageSize;
ULONG MmSizeOfPagedPoolInBytes;
ULONG MmTotalCommitLimit;
ULONG MmTotalCommittedPages;
ULONG MmSharedCommit;
ULONG MmDriverCommit;
ULONG MmProcessCommit;
ULONG MmPagedPoolCommit;
ULONG MmExtendedCommit;
ULONG MmZeroedPageListHead;
ULONG MmFreePageListHead;
ULONG MmStandbyPageListHead;
ULONG MmModifiedPageListHead;
ULONG MmModifiedNoWritePageListHead;
ULONG MmAvailablePages;
ULONG MmResidentAvailablePages;
ULONG PoolTrackTable;
ULONG NonPagedPoolDescriptor;
ULONG MmHighestUserAddress;
ULONG MmSystemRangeStart;
ULONG MmUserProbeAddress;
ULONG KdPrintCircularBuffer;
ULONG KdPrintCircularBufferEnd;
ULONG KdPrintWritePointer;
ULONG KdPrintRolloverCount;
ULONG MmLoadedUserImageList;
} KDDEBUGGER_DATA32, *PKDDEBUGGER_DATA32;
根据LOADER_PARAMETER_BLOCK结构寻找调试有关的选项,保存上下文。
3、初始化全局变量
(1) KdPitchDebugger:布尔类型,标识发生异常时是否需要被内核调试,当启动项包含/NODEBUG选项时,该变量为TURE。
(2) KdDebuggerEnabled:布尔类型,标识内核调试是否被启用,当启动项包含/DEBUG或/DEBUGPORT且不包含/NODEBUG时,该变量为TRUE。
(3) KiDebugRoutine:函数指针类型,用来记录内核调试引擎的异常处理回调函数,当内核调试引擎活动时,指向KdpTrap,否则指向KdpStub。
(4) KdpBreakpointTable:结构数组类型,用来记录代码断点,每个元素为一个BREAKPOINT_ENTRY数据结构,用来描述断点的信息。
第二次调用KdInitSystem
调用KeQueryPerformanceCounter来对全局变量KdPerFormanceCounterRate(性能计数器的频率)进行初始化,然后返回。
学习了
https://assets.baklib.com/5467817c-6514-4e6d-bb4b-386f8de94d26/RocksyLight%2011590729794245.gif
感谢感谢,看完感觉明了不少
页:
[1]