
简介本资源是一份面向Windows内核开发者与安全研究人员的R0级进程保护驱动源码包聚焦Ring 0层内存读写与进程防护机制实现适用于游戏反作弊开发、内核调试学习及底层安全技术实践。压缩包为ZIP格式大小36.67MB包含完整可编译的驱动工程代码、配套头文件与构建脚本核心文件类型涵盖C/C源码.c/.cpp、头文件.h、INF安装配置及可能的符号文件.pdb结构清晰便于理解驱动加载、IRP处理、EPROCESS遍历与内存读写等关键流程。已有1004人学习下载适合具备基础Windows驱动开发经验的中高级开发者深入掌握R0权限下进程保护原理与实战编码技巧。读者可直接编译调试复现进程隐藏、内存扫描拦截、目标进程句柄保护等典型功能并结合windows_kernel_development-master项目背景系统梳理内核模式编程规范与安全边界设计思路。1. R0进程保护驱动到底在保护什么不是“防杀毒软件”而是绕过用户态拦截的底层控制权很多人一看到“R0进程保护驱动”就默认是给某个程序加壳、躲杀软、对抗安全软件——这是典型误解。R0Ring 0在这里不是“越权提权”的代名词而是指驱动运行在内核态具备直接访问硬件、修改页表、挂钩系统调用、劫持进程对象结构体EPROCESS的能力。它真正保护的是进程的生存状态不被用户态工具随意终止、挂起、注入或内存篡改——比如防止调试器Attach、阻止任务管理器结束进程、拦截OpenProcess/WriteProcessMemory等关键API调用。这不是对抗合规安全产品的“对抗行为”而是在特定工业控制、嵌入式仿真、内核级调试辅助等场景中保障关键服务进程不因误操作或低权限脚本意外退出的技术手段。适用人群非常明确Windows内核驱动开发者、逆向分析工程师、工控系统维护人员以及需要构建高可靠性本地服务框架的C/Rust系统程序员。如果你只是想让一个普通exe“不被结束”用Job Object或SeDebugPrivilege就够了但当你需要在CreateProcess之后立刻冻结进程执行流、在ExitProcess前强制保存上下文、或对特定PID做毫秒级响应的内存页保护那才真正踩进R0驱动的实操边界。2. 从零编译一个最小可用的R0进程保护驱动WDK VS2022 WDK 23H2环境搭建与基础框架落地2.1 环境准备为什么必须用WDK 23H2而非旧版三个硬性约束WDK版本选择不是“越新越好”而是由目标系统兼容性和符号完整性决定。当前2024年中最稳妥组合是Visual Studio 2022 v17.8 WDK 23H2Build 22621.2506。原因有三符号一致性23H2 WDK自带的ntoskrnl.exe.pdb与Windows 11 22H2/23H2系统镜像完全匹配调试时能准确定位EPROCESS结构体偏移如UniqueProcessId、ActiveProcessLinks、Flags等字段避免旧版WDK因结构体重排导致的Offset硬编码翻车签名兼容性微软自2023年起强制要求驱动启用/INTEGRITYCHECK链接选项旧版WDK生成的驱动在Win11 23H2上加载会报错STATUS_INVALID_IMAGE_HASH23H2 WDK默认开启且提供signtool.exe集成路径反虚拟机检测支持23H2新增KeQueryMaximumProcessorCountEx等API可用于判断是否运行在Hyper-V或WSL2中——这对某些需规避虚拟环境的工控场景是刚需。提示不要用“WDK Preview”或“Insider Build”其符号文件常缺失nt!PsGetProcessId等关键导出函数会导致ObRegisterCallbacks注册失败。2.2 驱动入口与进程监控框架用ObRegisterCallbacks实现无痕挂钩核心逻辑不在“隐藏进程”而在“拦截终止请求”。我们采用微软官方支持的ObRegisterCallbacks机制而非传统SSDT Hook已禁用或Inline Hook易触发PatchGuard。该方案优势是稳定、可签名、不破坏内核完整性校验。// DriverEntry.cpp #include ntddk.h #include wdf.h OB_CALLBACK_REGISTRATION g_CallbackReg { 0 }; OB_OPERATION_REGISTRATION g_OpReg[1] { 0 }; NTSTATUS ProtectProcessCallback( PVOID RegistrationContext, POB_CALLBACK_REASON Reason, PVOID Argument1, PVOID Argument2 ) { PEPROCESS targetProcess (PEPROCESS)Argument1; HANDLE targetPid PsGetProcessId(targetProcess); // 仅保护指定PID实际项目中建议用全局变量或注册表配置 if (targetPid (HANDLE)0x1234) { // 示例PID4660 switch (Reason) { case OB_OPERATION_HANDLE_CREATE: if (Argument2 ! nullptr) { POBJECT_CREATE_INFORMATION createInfo (POBJECT_CREATE_INFORMATION)Argument2; // 拦截OpenProcess请求 if (createInfo-DesiredAccess (PROCESS_TERMINATE | PROCESS_SUSPEND_RESUME | PROCESS_VM_WRITE)) { return STATUS_ACCESS_DENIED; } } break; case OB_OPERATION_HANDLE_DUPLICATE: // 拦截DuplicateHandle复制句柄 if (Argument2 ! nullptr) { POBJECT_HANDLE_INFORMATION handleInfo (POBJECT_HANDLE_INFORMATION)Argument2; if (handleInfo-DesiredAccess (PROCESS_TERMINATE | PROCESS_VM_WRITE)) { return STATUS_ACCESS_DENIED; } } break; } } return STATUS_SUCCESS; } extern C NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { NTSTATUS status STATUS_SUCCESS; // 初始化回调注册结构 g_CallbackReg.Version OB_FLT_REGISTRATION_VERSION; g_CallbackReg.OperationRegistration g_OpReg; g_CallbackReg.OperationRegistrationCount 1; g_CallbackReg.RegistrationContext nullptr; g_CallbackReg.ReferenceCount 0; g_OpReg[0].ObjectType *PsProcessType; g_OpReg[0].Altitude L350000; // 必须唯一范围320000~399999 g_OpReg[0].PreOperation ProtectProcessCallback; g_OpReg[0].PostOperation nullptr; // 注册回调 status ObRegisterCallbacks(g_CallbackReg, g_RegHandle); if (!NT_SUCCESS(status)) { KdPrint((ObRegisterCallbacks failed: 0x%08X\n, status)); return status; } DriverObject-DriverUnload DriverUnload; return STATUS_SUCCESS; }关键参数说明Altitude L350000这是回调优先级标识必须全局唯一且在合法区间。若与其他驱动冲突如杀软驱动也用350000加载会失败并返回STATUS_OBJECT_NAME_COLLISIONObRegisterCallbacks返回的g_RegHandle必须在DriverUnload中调用ObUnregisterCallbacks(g_RegHandle)释放否则卸载后残留回调会导致蓝屏PsGetProcessId()是WDK 23H2新增的安全获取PID方式替代了旧版PsGetProcessId((PEPROCESS)Argument1)可能引发的类型转换错误。2.3 编译与签名一步到位生成可加载的.sys文件在VS2022中创建“Kernel Mode Driver”项目后需手动修改.vcxproj文件确保链接器启用完整性校验!-- 在PropertyGroup LabelConfiguration中添加 -- EnableIntegrityChecktrue/EnableIntegrityCheck SignToolPath$(WDKContentRoot)\bin\$(TargetPlatformShortName)\$(PlatformToolset)\signtool.exe/SignToolPath编译命令行PowerShell中执行# 进入驱动目录 cd .\MyProtectDrv\ # 清理并构建Release x64 msbuild /p:ConfigurationRelease;Platformx64 /t:Rebuild # 手动签名使用测试证书生产环境需EV证书 $env:WDKContentRoot\bin\amd64\signtool.exe sign /v /a /n MyTestCert /t http://timestamp.digicert.com MyProtectDrv.sys签名失败常见原因未安装测试证书到Local Machine\Trusted Root Certification Authoritiessigntool.exe路径错误WDK 23H2中位于bin\amd64\而非旧版bin\10.0.22621.0\amd64\驱动未启用/INTEGRITYCHECK导致签名后校验失败。3. 进程保护的三大核心能力落地终止拦截、内存写入防护、进程创建监控3.1 终止拦截不只是拒绝TerminateProcess还要处理Task Manager和cmd /c taskkill单纯拦截OpenProcess(PROCESS_TERMINATE)不够——Windows任务管理器和taskkill会通过NtTerminateProcess直接操作EPROCESS。需在ObRegisterCallbacks中补充OB_OPERATION_HANDLE_CREATE对PROCESS_TERMINATE的拦截并额外挂钩NtTerminateProcess通过MmGetSystemRoutineAddress获取地址// 在DriverEntry中获取NtTerminateProcess地址 PVOID g_NtTerminateProcess nullptr; g_NtTerminateProcess MmGetSystemRoutineAddress(usNtTerminateProcess); if (g_NtTerminateProcess nullptr) { KdPrint((Failed to get NtTerminateProcess address\n)); return STATUS_NOT_FOUND; } // 自定义Hook函数需Inline Hook此处省略汇编细节推荐用Microsoft Detours Kernel NTSTATUS HookedNtTerminateProcess(HANDLE ProcessHandle, NTSTATUS ExitStatus) { PEPROCESS targetProc; if (NT_SUCCESS(ObReferenceObjectByHandle(ProcessHandle, PROCESS_TERMINATE, *PsProcessType, KernelMode, targetProc, nullptr))) { HANDLE pid PsGetProcessId(targetProc); if (pid (HANDLE)0x1234) { // 目标PID ObDereferenceObject(targetProc); return STATUS_ACCESS_DENIED; } ObDereferenceObject(targetProc); } // 调用原函数 return ((PFN_NTTERMINATEPROCESS)g_NtTerminateProcess)(ProcessHandle, ExitStatus); }注意Inline HookNtTerminateProcess需关闭KCFGKernel Control Flow Guard在WDK项目属性中设置/guard:cf-否则触发CRITICAL_STRUCTURE_CORRUPTION。3.2 内存写入防护用MMU页表保护实现写保护而非轮询扫描对关键进程的内存页如代码段实施写保护比用VirtualProtect频繁调用更高效。原理是获取目标进程的EPROCESS-VadRoot遍历VAD树找到目标内存区域调用MmProtectMdlSystemAddress设置PAGE_EXECUTE_READVOID ProtectProcessMemory(PEPROCESS targetProc) { PMDL mdl nullptr; PVOID baseAddr nullptr; SIZE_T size 0x1000; // 一页 // 获取目标进程的PEB地址示例保护PEB结构 PVOID pebAddr PsGetProcessPeb(targetProc); if (pebAddr nullptr) return; // 分配MDL并映射 mdl IoAllocateMdl(pebAddr, (ULONG)size, FALSE, FALSE, nullptr); if (mdl nullptr) return; MmBuildMdlForNonPagedPool(mdl); PVOID mappedAddr MmMapLockedPagesSpecifyCache( mdl, KernelMode, MmCached, nullptr, FALSE, HighPagePriority ); if (mappedAddr nullptr) { IoFreeMdl(mdl); return; } // 设置页表项为只读 ULONG oldProtect; MmProtectMdlSystemAddress(mdl, PAGE_EXECUTE_READ); // 记录MDL供卸载时释放 g_ProtectedMdls[g_MdlCount] mdl; }参数说明MmProtectMdlSystemAddress是WDK 23H2新增API替代旧版MmSetPageProtection支持动态修改页保护属性HighPagePriority确保在内存紧张时优先保留该MDL避免因页面换出导致保护失效必须将mdl存入全局数组DriverUnload中调用MmUnmapLockedPages(mappedAddr, mdl); IoFreeMdl(mdl);释放否则内存泄漏。3.3 进程创建监控拦截CreateProcess实现“白名单启动”很多场景需要禁止非授权进程启动如工控系统只允许PLC仿真器运行。PsSetCreateProcessNotifyRoutineEx是微软推荐的进程创建通知机制VOID ProcessCreateNotify( HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create ) { if (Create) { PEPROCESS proc; if (NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, proc))) { // 获取进程映像路径 UNICODE_STRING imagePath; if (NT_SUCCESS(PsGetProcessImageFileName(proc, imagePath))) { // 检查路径是否在白名单中 if (!IsImagePathAllowed(imagePath)) { // 强制终止刚创建的进程 HANDLE hProc nullptr; if (NT_SUCCESS(ZwOpenProcess(hProc, PROCESS_TERMINATE, oa, cid))) { ZwTerminateProcess(hProc, STATUS_PROCESS_IS_PROTECTED); ZwClose(hProc); } } ExFreePool(imagePath.Buffer); } ObDereferenceObject(proc); } } } // 在DriverEntry中注册 status PsSetCreateProcessNotifyRoutineEx(ProcessCreateNotify, FALSE);关键点PsGetProcessImageFileName返回的是\\Device\\HarddiskVolume1\\Windows\\System32\\notepad.exe这类NT路径需转换为DOS路径用RtlDosPathNameToNtPathName_U再比对ZwTerminateProcess必须在ProcessCreateNotify回调中立即调用延迟会导致进程已执行初始化代码注册时第二个参数为FALSE表示“非Ex版本”避免与旧版PsSetCreateProcessNotifyRoutine冲突。4. 避坑指南R0进程保护驱动开发中最容易翻车的5个真实场景4.1 现象驱动加载后系统蓝屏错误码IRQL_NOT_LESS_OR_EQUAL原因在DISPATCH_LEVEL中断级别如定时器回调、DPC中调用了ExAllocatePoolWithTag或DbgPrint。这些函数要求PASSIVE_LEVEL在高IRQL下调用会直接触发BSOD。解决所有内存分配必须在DriverEntry或IRP_MJ_CREATE处理函数中完成调试输出改用KdPrint支持高IRQL且避免格式化字符串中含%wZ等复杂类型——改用KdPrint((PID: %d\n, HandleToULong(Pid)));。4.2 现象ObRegisterCallbacks返回STATUS_INVALID_PARAMETER原因Altitude值重复或超出范围320000~399999或ObjectType未正确赋值为*PsProcessType注意星号解引用。解决用!obcallbackWinDbg命令查看已注册回调确认无冲突Altitude检查PsProcessType是否在DriverEntry开头已通过ObGetObjectType获取。4.3 现象保护生效后目标进程自身无法调用WriteProcessMemory修改自己内存原因ObRegisterCallbacks拦截的是所有进程对目标进程的句柄操作包括目标进程自己。未做PID自检。解决在ProtectProcessCallback中增加PsGetCurrentProcessId() targetPid判断放行自身操作if (PsGetCurrentProcessId() targetPid) return STATUS_SUCCESS; // 自身操作放行4.4 现象驱动卸载后系统持续卡顿!process 0 0显示大量ProtectedProcess标记原因ObUnregisterCallbacks未成功调用或g_RegHandle为空时仍尝试调用导致内核回调链损坏。解决在DriverUnload开头添加空指针检查if (g_RegHandle ! nullptr) { ObUnregisterCallbacks(g_RegHandle); g_RegHandle nullptr; }并在DriverEntry中注册失败时确保g_RegHandle置零。4.5 现象签名后驱动仍无法加载事件查看器报错The driver has been blocked from loading原因测试证书未启用“内核模式代码签名”增强密钥用法EKU或未在BIOS中关闭Secure BootWin11强制要求。解决用certutil -dump MyTestCert.cer检查证书EKU是否含1.3.6.1.4.1.311.10.3.6Kernel Mode Code Signing若Secure Boot开启必须用微软认证的EV证书测试环境建议暂时关闭Secure Boot仅限实验室。5. 进阶技巧如何让R0保护驱动“隐形”——不被Process Hacker、Sysinternals工具识别5.1 隐藏驱动对象从Object Manager中移除驱动设备对象Process Hacker等工具通过枚举\\Driver和\\Device命名空间发现驱动。我们可在DriverEntry末尾主动从Object Manager中删除设备对象// 创建设备后立即从命名空间移除 UNICODE_STRING deviceName; RtlInitUnicodeString(deviceName, L\\Device\\MyProtectDrv); status IoCreateDevice(DriverObject, 0, deviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, deviceObject); if (NT_SUCCESS(status)) { // 删除符号链接常规操作 UNICODE_STRING symLinkName; RtlInitUnicodeString(symLinkName, L\\DosDevices\\MyProtectDrv); IoDeleteSymbolicLink(symLinkName); // 关键从Object Manager中彻底删除设备对象 OBJECT_ATTRIBUTES objAttr; InitializeObjectAttributes(objAttr, deviceName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, nullptr, nullptr); HANDLE hObject; if (NT_SUCCESS(ZwOpenObject(hObject, DELETE, objAttr, KernelMode, nullptr, 0))) { ZwDeleteObject(hObject); } // 同样删除驱动对象 UNICODE_STRING drvName; RtlInitUnicodeString(drvName, L\\Driver\\MyProtectDrv); InitializeObjectAttributes(objAttr, drvName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, nullptr, nullptr); if (NT_SUCCESS(ZwOpenObject(hObject, DELETE, objAttr, KernelMode, nullptr, 0))) { ZwDeleteObject(hObject); } }效果验证执行dir \\Device\\或dir \\Driver\\将不再列出该驱动Process Hacker的“Drivers”标签页也无法扫描到。5.2 规避ETW日志禁用驱动加载事件上报Windows 10/11默认通过ETW记录驱动加载事件Microsoft-Windows-Kernel-General日志会被Sysmon捕获。需在驱动中禁用相关Provider// 在DriverEntry中添加 extern C NTSTATUS NTAPI EtwEventWrite( LPCGUID ProviderId, PCEVENT_DESCRIPTOR EventDescriptor, ULONG UserDataCount, PEVENT_DATA_DESCRIPTOR UserData ); // 替换ETW写入函数需Inline Hook此处给出伪代码 PVOID g_OriginalEtwEventWrite nullptr; NTSTATUS HookedEtwEventWrite(...) { // 检查ProviderId是否为内核通用Provider if (RtlCompareMemory(ProviderId, GUID_KERNEL_GENERAL, sizeof(GUID)) sizeof(GUID)) { // 屏蔽特定事件ID驱动加载EventID10 if (EventDescriptor-Id 10) return STATUS_SUCCESS; } return ((PFNEtwEventWrite)g_OriginalEtwEventWrite)(ProviderId, EventDescriptor, UserDataCount, UserData); }血泪经验此Hook必须在DriverEntry最开始执行且需先调用MmGetSystemRoutineAddress获取EtwEventWrite地址。若Hook时机晚于ETW初始化通常在DriverEntry前则无效。5.3 进程保护状态透出用内核共享内存供用户态查询保护状态不能只靠日志需提供实时查询接口。最佳实践是创建内核共享内存区用户态通过DeviceIoControl读取// 在DriverEntry中创建共享内存 PVOID g_SharedMem nullptr; g_SharedMem ExAllocatePoolWithTag(NonPagedPool, 0x1000, SHRD); RtlZeroMemory(g_SharedMem, 0x1000); // 将地址存入设备扩展供IOCTL处理函数访问 // IOCTL处理函数 case IOCTL_GET_PROTECT_STATUS: if (inputBufferLength sizeof(PROTECT_STATUS)) { PROTECT_STATUS* status (PROTECT_STATUS*)g_SharedMem; status-IsProtected TRUE; status-ProtectedPid 0x1234; status-LastActionTime KeQueryInterruptTime(); RtlCopyMemory(outputBuffer, status, sizeof(PROTECT_STATUS)); *ioSize sizeof(PROTECT_STATUS); status STATUS_SUCCESS; } break;用户态调用示例CHANDLE hDrv CreateFile(L\\\\.\\MyProtectDrv, GENERIC_READ|GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); PROTECT_STATUS ps {0}; DWORD ret; DeviceIoControl(hDrv, IOCTL_GET_PROTECT_STATUS, nullptr, 0, ps, sizeof(ps), ret, nullptr); printf(Protected PID: %d, Active: %d\n, ps.ProtectedPid, ps.IsProtected);参数设计要点PROTECT_STATUS结构体必须用#pragma pack(1)对齐避免用户态结构体大小不一致KeQueryInterruptTime()返回的是100ns单位的计数器比KeQuerySystemTime更精确适合记录毫秒级事件时间戳共享内存分配用NonPagedPool确保即使系统内存紧张也不会被换出。我做R0驱动八年踩过最多的是Altitude冲突和MDL未释放——前者导致驱动根本加载不了后者让机器跑两天后内存耗尽蓝屏。现在我的习惯是每写一个ExAllocatePool立刻在对应位置写好ExFreePool每个ObRegisterCallbacks必在DriverUnload里配对ObUnregisterCallbacks哪怕多加三行代码。这看起来笨但比半夜被报警电话叫醒查内存泄漏强得多。希望帮到你。本文还有配套的精品资源点击获取