Windows软件调试初探-架构和系统部件
Windows软件调试初探-架构和系统部件
系统概览
在内核空间中:
硬件抽象层HAL隔离CPU架构层面核心硬件的硬件差异性,使内核和顶层模块可使同一的方式访问硬件,不负责的外设设备硬件通过I/O管理器加载不同设备驱动程序解决。执行体如内存管理器、进程管理器、I/O管理器。Windows子系统驱动程序Win32K.sys包括USER和GDI两部分,USER负责窗口管理、用户输入等,GDI负责显示输出和各种图形操作,还有DxgKrnl.sys图形核心负责管理GPU 有关核心任务。内核支持模块有用于内核调试的KDCOM.DLL,用于启动截断显示驱动的BOOTVID.DLL,用于检查模块完好性的CI.DLL,用于支持日志功能的CLFS.SYS,支持WHEA的PSHED.DLL,管理流媒体的KS.sys,网络套接字WinSock的内核空间接口驱动AFD.sys,管理网卡驱动的NDIS.sys,管理网络过滤驱动的Windows过滤平台清凉过滤器驱动程序Wfplw.sys,支持ACPI标准的ACPI.sys,PCI总线的PCI.sys,NTFS文件系统的实现NTFS.SYS。
在用户空间中:
会话管理器进程SMSS.EXE是系统中第一个根据映像文件创建的进程,它加载初始化Win32K.SYS,创建CSRSS.EXE和WinLogon.EXE。Windows客户端/服务端运行时子系统服务器进程CSRSS.EXE为Windows子系统各个进程提供服务如登记进程和线程、管理控制台窗口、管理DOS程序虚拟机VDM等。登录进程WinLogon.EXE负责用户登录和安全相关,它创建LSASS进程和Services.EXE进程,实现sfc.dll和sfc_os.dll的文件保护功能。本地安全和认证进程LSASS.EXE负责用户身份验证。服务管理进程Services.EXE负责启动和管理系统服务程序。外壳程序Shell默认为Explorer.exe。
内核和HAL模块
所谓内核文件为NTOSKRNL.EXE,32位且支持物理地址扩展PAE的改为NTKRNLPA.EXE,对于x64架构的只有前者,后者用不上就没了。Windows系统启动时由系统加载程序NTLDR或WinLoad根据启动选项是否启用PAE加载其中一个。
HAL文件有多个版本,系统中不一定是哪个,如下表。注意原始文件名指的是安装文件复制出的文件名,后期都将改为hal.dll,文件右键属性详细信息中能够看到原始文件名。
原始文件名 | 适用平台 |
---|---|
hal.dll | 标准平台 |
halacpi.dll | 符合ACPI的标准硬件平台 |
halapic.dll | 支持高级可编程中断控制器APIC的硬件平台 |
halaacpi.dll | 同时支持ACPI 和APIC的硬件平台 |
halmps.dll | 系统中有一个多处理器 |
halmacpi.dll | ACPI的多处理器系统 |
函数nt!KzLowerIrql
直接操作APIC任务优先级寄存器TPR的别名CR8寄存器,用于降低中断请求级别IRQL。
!process
不显示空闲进程,内核调试中先用!prcb
显示处理器控制块。Threads行的Current字段为当前CPU 正执行线程的ETHREAD结构,Next为等待执行的线程,Idle为当前CPU的空闲线程ETHREAD结构地址,用!thread
显示线程详细信息。Cid字段为0表示空闲进程,系统进程为4,Owning Process为空闲进程EPROCESS,Image映像名Idle是瞎扯的,UserTime为0表示只在内核模式执行,KernelTime为在内核模式执行时间。
1 | 1: kd> !prcb |
例如查看该空闲进程回溯栈:
1 | 1: kd> .process /r /p fffff80753b24a00 |
nt!PoIdle
是电源执行体Power为空闲线程设计的工作函数,一般调用CPU处理器电源管理PPM模块进入省电模式,其下面的nt!KiIdleLoop
为空闲线程入口和主函数。PPM模块导出一系列函数供nt!PpmIdleExecuteTransition
调用。CPU在空闲线程中执行时可能用nt!KiRetireDpcList
执行挂在延迟过程调用DPC队伍里的任务。
系统进程
1 | 1: kd> !process 4 1 |
一些常见的高CPU时间的系统线程有:管理GPU内存的VidMM线程,工作函数dxgmms2!VidMmWorkerThreadProc
;调度GPU任务的VidSch线程,工作函数dxgmms2!VidSchiWorkerThread
,其内调用dxgmms2!VidsSchiRun_PriorityTable
;内存管理器工作集平衡线程,工作函数KeBalanceSetManager
扫描每个进程页表,必要时把暂时不用的内存也交换到虚拟内存;内存管理器清零线程,工作函数ZeroPageThread
,把需要的内存页清零。
NTDLL.DLL
映像加载器LDR是NTDLL.DLL的一部分,其负责在进程空间创建,即形成初始线程后,用异步过程调用APC机制让新线程在用户空间运行。Ldr前缀函数为接口函数,Ldrp前缀函数为内部函数。其中ntdll!LdrInitializeThunk
是CPU从内核空间切换到用户空间的着陆点,ntdll!LdrpInitializeProcess
是执行进程初始化的核心函数。
1 | 0:000> k |
Rtl开头的是运行时库函数,这类函数NTDLL.DLL中数量最多,达2000多个。
环境子系统
POSIX和OS/2子系统已过时,Windows子系统在用户模式下主要结构有:
重要文件 | 描述 |
---|---|
CSRSS.EXE | Windows子系统服务进程主程序 |
ADVAPI32.DLL | 包含API:数据加密Crpt、用户账号管理Lsa 、注册表操作Reg、WMI、终端服务Wts |
GDI32.DLL | 包含图形文字绘制API入口,TextOut 、BitBlt 等 |
KERNEL32.DLL | 包含API:进程线程管理如CreateThread 、调试Debug、文件操作、内存分配Local或Global |
USER32.DLL | 包含窗口管理、消息处理、用户输入API,EndDialog 、BeginPaint 、SetWindowPos 、MessageBox 等 |
原生进程
不依赖任何子系统,通过特殊私有结构直接与内核交互的进程叫原生进程,常见的有磁盘检查程序autochk.exe、SMSS、CSRSS等,他们是标准的PE格式,但头信息Sybsystem字段为0001表示IMAGE_SUBSYSTEM_NATIVE。下面是个最简单原生进程的例子:
1 | VOID NTAPI NTProcessStartup(PSTARTUP_ARGUMENT pArgument){ |
对于SMSS进程的创建过程中nt!Phase1Initialization
代表内核启动过程中执行体阶段1初始化,之后nt!StartFirstUserProcess
启动第一个用户进程,即SMSS是NT启动过程第一个EXE方式创建的进程,最后nt!MmCreateProcessAddressSpace
创建进程地址空间。对于SMSS创建磁盘检查进程autochk.exe时,最开始smss!NtProcessStartupW
为SMSS入口,然后smss!SmpLoadDataFromRegistry
读取注册表,其次用smss!SmpExecuteCommand
执行HKLM\System\CurrentControlSet\Control\Session Manager\BootExecute表键指定的程序。
系统启动后,SMSS内部运行两个线程,一个主线程用NtWaitForMultipleObjects
等待CSRSS和WinLogon,这俩意外退出时触发蓝屏;另一个线程用NtWaitForWorkViaWorkerFactory
等待登录会话有关任务。