DriverEntry.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. // DriverEntry.cpp - KMDF 驱动入口
  2. // 职责:初始化 WDF 驱动对象、注册 EvtDeviceAdd(PnP 过滤驱动)
  3. // 创建控制设备 \\.\CommModifyKit 接收 DLL 的 IOCTL
  4. // 注意:本驱动作为串口设备类的 UpperFilters 加载
  5. // 当 PnP Manager 创建串口 PDO 时,调用 EvtDeviceAdd attach 过滤设备
  6. #include <ntddk.h>
  7. #include <wdf.h>
  8. #include <initguid.h> // 必须在包含定义 GUID 的头文件之前
  9. #include <ntddser.h> // GUID_DEVINTERFACE_COMPORT
  10. #include "DeviceContext.h"
  11. #include "QueueCallback.h"
  12. #include "ClientConnection.h"
  13. #include "SerialFilter.h" // g_sequence_counter
  14. #include "EventRingBuffer.h" // EventRingBuffer
  15. #include "../common/CommKitEvents.h" // COMMKIT_OP_OPEN / OP_CLOSE
  16. extern "C" {
  17. DRIVER_INITIALIZE DriverEntry;
  18. EVT_WDF_DRIVER_DEVICE_ADD EvtDeviceAdd;
  19. EVT_WDF_DRIVER_UNLOAD EvtDriverUnload;
  20. EVT_WDF_OBJECT_CONTEXT_CLEANUP EvtDeviceContextCleanup;
  21. } // extern "C"
  22. // PnP 回调使用 C++ 链接(WDF 宏内部处理)
  23. EVT_WDF_DEVICE_PREPARE_HARDWARE EvtDevicePrepareHardware;
  24. namespace commkit_driver {
  25. // 全局驱动对象(仅用于内部日志/调试)
  26. static WDFDRIVER g_wdf_driver = nullptr;
  27. } // namespace commkit_driver
  28. extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT driver_object,
  29. PUNICODE_STRING registry_path) {
  30. DbgPrint("[CommModifyKit] DriverEntry ENTERED\n");
  31. NTSTATUS status;
  32. WDF_DRIVER_CONFIG cfg;
  33. // 初始化全局客户端连接管理器
  34. commkit_driver::GetClientConnection().Initialize();
  35. // 初始化 WDF 配置
  36. WDF_DRIVER_CONFIG_INIT(&cfg, EvtDeviceAdd);
  37. cfg.EvtDriverUnload = EvtDriverUnload;
  38. // 创建 WDF 驱动对象
  39. status = WdfDriverCreate(driver_object, registry_path,
  40. WDF_NO_OBJECT_ATTRIBUTES, &cfg,
  41. &commkit_driver::g_wdf_driver);
  42. if (!NT_SUCCESS(status)) {
  43. return status;
  44. }
  45. // 初始化 ClientConnection(创建控制设备)
  46. status = commkit_driver::CreateControlDevice(commkit_driver::g_wdf_driver);
  47. if (!NT_SUCCESS(status)) {
  48. DbgPrint("[CommModifyKit] CreateControlDevice FAILED 0x%08X\n", status);
  49. return status;
  50. }
  51. DbgPrint("[CommModifyKit] DriverEntry OK, EvtDeviceAdd registered\n");
  52. return STATUS_SUCCESS;
  53. }
  54. extern "C" NTSTATUS EvtDeviceAdd(WDFDRIVER driver, PWDFDEVICE_INIT init) {
  55. UNREFERENCED_PARAMETER(driver);
  56. InterlockedIncrement(&g_diag_evt_device_add_count);
  57. DbgPrint("[CommModifyKit] >>> EvtDeviceAdd ENTER (count=%ld)\n", g_diag_evt_device_add_count);
  58. NTSTATUS status;
  59. // 1. 标记为过滤设备
  60. WdfFdoInitSetFilter(init);
  61. DbgPrint("[CommModifyKit] EvtDeviceAdd: WdfFdoInitSetFilter OK\n");
  62. // 2. 注册 PnP 电源回调
  63. WDF_PNPPOWER_EVENT_CALLBACKS pnp_callbacks;
  64. WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnp_callbacks);
  65. pnp_callbacks.EvtDevicePrepareHardware = EvtDevicePrepareHardware;
  66. WdfDeviceInitSetPnpPowerEventCallbacks(init, &pnp_callbacks);
  67. DbgPrint("[CommModifyKit] EvtDeviceAdd: PnpPower callbacks registered\n");
  68. // 2.5 不使用 WdfDeviceInitSetFileObjectConfig
  69. // -----------------------------------------------
  70. // 原因:WdfDeviceInitSetFileObjectConfig 会让 WDF 为每个 CREATE 创建
  71. // WDFFILEOBJECT,并在 WdfDeviceWdmDispatchPreprocessedIrp 中强制查找它。
  72. // 当 WDFFILEOBJECT 查找失败时(异步 SEND_AND_FORGET 转发时序问题、
  73. // 或 FileObject 不匹配),WDF 返回 STATUS_FILE_FORCED_CLOSED (0xC0000182),
  74. // 导致其他工具的 READ/WRITE IRP 全部失败。
  75. //
  76. // 改用 IRP_MJ_CREATE/CLOSE 预处理回调手动追踪文件打开/关闭和捕获 FileObject。
  77. // 没有 WdfDeviceInitSetFileObjectConfig,WdfDeviceWdmDispatchPreprocessedIrp
  78. // 不再查找 WDFFILEOBJECT,直接转发 IRP 到下层设备。
  79. // 3. 注册 WDM IRP 预处理回调
  80. status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
  81. init, commkit_driver::SerialFilter::DispatchCreate,
  82. IRP_MJ_CREATE, nullptr, 0);
  83. if (!NT_SUCCESS(status)) { InterlockedExchange(&g_diag_last_failure_status, status); return status; }
  84. DbgPrint("[CommModifyKit] EvtDeviceAdd: IRP_MJ_CREATE preprocess registered\n");
  85. status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
  86. init, commkit_driver::SerialFilter::DispatchClose,
  87. IRP_MJ_CLOSE, nullptr, 0);
  88. if (!NT_SUCCESS(status)) { InterlockedExchange(&g_diag_last_failure_status, status); return status; }
  89. DbgPrint("[CommModifyKit] EvtDeviceAdd: IRP_MJ_CLOSE preprocess registered\n");
  90. status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
  91. init, commkit_driver::SerialFilter::DispatchRead,
  92. IRP_MJ_READ, nullptr, 0);
  93. if (!NT_SUCCESS(status)) { InterlockedExchange(&g_diag_last_failure_status, status); return status; }
  94. DbgPrint("[CommModifyKit] EvtDeviceAdd: IRP_MJ_READ preprocess registered\n");
  95. status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
  96. init, commkit_driver::SerialFilter::DispatchWrite,
  97. IRP_MJ_WRITE, nullptr, 0);
  98. if (!NT_SUCCESS(status)) { InterlockedExchange(&g_diag_last_failure_status, status); return status; }
  99. DbgPrint("[CommModifyKit] EvtDeviceAdd: IRP_MJ_WRITE preprocess registered\n");
  100. // IRP_MJ_CLEANUP / IRP_MJ_DEVICE_CONTROL 不注册预处理回调。
  101. // CLEANUP 由 WDF 自动转发(过滤驱动模式)。
  102. // DEVICE_CONTROL 由 WDF 自动转发给下层设备,串口 IOCTL(波特率/DCB 等)
  103. // 由虚拟串口驱动直接处理。
  104. InterlockedIncrement(&g_diag_irp_preprocess_ok);
  105. // 4. SDDL: WDF filter device 不支持 WdfDeviceInitAssignSDDLString
  106. // 它会返回 STATUS_INVALID_SECURITY_DESCR (0xC0000079)
  107. // Filter device 会继承下层设备 (serial.sys) 的安全描述符
  108. // 不需要显式设置 SDDL
  109. // 5. 创建 WDF 设备
  110. WDFDEVICE device;
  111. WDF_OBJECT_ATTRIBUTES attrs;
  112. WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attrs, DEVICE_CONTEXT);
  113. attrs.EvtCleanupCallback = EvtDeviceContextCleanup;
  114. DbgPrint("[CommModifyKit] EvtDeviceAdd: calling WdfDeviceCreate...\n");
  115. status = WdfDeviceCreate(&init, &attrs, &device);
  116. if (!NT_SUCCESS(status)) {
  117. DbgPrint("[CommModifyKit] EvtDeviceAdd: WdfDeviceCreate FAILED 0x%08X\n", status);
  118. InterlockedExchange(&g_diag_last_failure_status, status);
  119. return status;
  120. }
  121. DbgPrint("[CommModifyKit] EvtDeviceAdd: WdfDeviceCreate OK device=%p\n", device);
  122. InterlockedIncrement(&g_diag_device_create_ok);
  123. // 6. 初始化设备上下文
  124. PDEVICE_CONTEXT ctx = DeviceGetContext(device);
  125. ctx->ComNumber = 0;
  126. ctx->MonitoringEnabled = FALSE;
  127. ctx->RingBuffer = nullptr;
  128. ctx->WdfDevice = device;
  129. ctx->LowerDevice = WdfDeviceWdmGetAttachedDevice(device);
  130. ctx->SavedFileObject = nullptr;
  131. KeInitializeSpinLock(&ctx->FileObjectLock);
  132. DbgPrint("[CommModifyKit] EvtDeviceAdd: ctx initialized, LowerDevice=%p\n", ctx->LowerDevice);
  133. // 注意:不再设置过滤设备 / 下层设备的 SecurityDescriptor。
  134. // 之前直接覆盖 ctx->LowerDevice->SecurityDescriptor 是严重错误:
  135. // 1. 下层设备不归本驱动管理(由虚拟串口驱动创建)
  136. // 2. 原始 SD 内存被泄漏
  137. // 3. 时机错误:EvtDeviceAdd 中下层设备可能未完成初始化
  138. // 4. 破坏下层驱动的内部状态,导致 CreateFile 异常失败
  139. // 过滤设备是未命名的,其 SD 不参与 CreateFile 的安全检查
  140. // (I/O Manager 查的是命名设备即下层 FDO 的 SD)。
  141. // 若需放宽非 elevated 进程的访问权限,应通过 INF 文件在设备类
  142. // 注册表项 HKLM\...\Control\Class\{Ports GUID}\Security 中设置。
  143. // 7. 注册到全局端口表
  144. commkit_driver::GetClientConnection().RegisterFilterDevice(device);
  145. DbgPrint("[CommModifyKit] EvtDeviceAdd: RegisterFilterDevice done, EvtDeviceAdd COMPLETE\n");
  146. return STATUS_SUCCESS;
  147. }
  148. NTSTATUS EvtDevicePrepareHardware(WDFDEVICE device,
  149. WDFCMRESLIST resources,
  150. WDFCMRESLIST resources_translated) {
  151. UNREFERENCED_PARAMETER(resources);
  152. UNREFERENCED_PARAMETER(resources_translated);
  153. NTSTATUS status;
  154. PDEVICE_CONTEXT ctx = DeviceGetContext(device);
  155. InterlockedIncrement(&g_diag_prepare_hardware_count);
  156. DbgPrint("[CommModifyKit] EvtDevicePrepareHardware ENTER\n");
  157. // 从 PnP 属性解析 COM 编号
  158. // 方式:获取 DevicePropertyFriendlyName 或 DevicePropertyDeviceDescription,
  159. // 匹配 "COM%d" 模式
  160. WDFMEMORY memory;
  161. status = WdfDeviceAllocAndQueryProperty(
  162. device, DevicePropertyFriendlyName, NonPagedPoolNx, 0, &memory);
  163. if (!NT_SUCCESS(status)) {
  164. DbgPrint("[CommModifyKit] PrepareHardware: FriendlyName query failed 0x%08X, trying DeviceDescription\n", status);
  165. // 尝试 DeviceDescription 作为后备
  166. status = WdfDeviceAllocAndQueryProperty(
  167. device, DevicePropertyDeviceDescription, NonPagedPoolNx, 0, &memory);
  168. }
  169. if (NT_SUCCESS(status)) {
  170. PWCHAR name = (PWCHAR)WdfMemoryGetBuffer(memory, nullptr);
  171. if (name) {
  172. // 打印 FriendlyName/DeviceDescription 前 40 字符用于诊断
  173. DbgPrint("[CommModifyKit] PrepareHardware: name='%ws'\n", name);
  174. // 解析 "COMx" 模式(可能出现在 "COMx (port)" 或 "通信端口 (COMx)" 等格式中)
  175. ULONG name_len = (ULONG)wcslen(name);
  176. for (ULONG i = 0; i + 3 <= name_len; i++) {
  177. if ((name[i] == L'C' || name[i] == L'c') &&
  178. (name[i+1] == L'O' || name[i+1] == L'o') &&
  179. (name[i+2] == L'M' || name[i+2] == L'm')) {
  180. if (name[i+3] >= L'0' && name[i+3] <= L'9') {
  181. ULONG num = 0;
  182. ULONG j = i + 3;
  183. while (j < name_len && name[j] >= L'0' && name[j] <= L'9') {
  184. num = num * 10 + (name[j] - L'0');
  185. j++;
  186. }
  187. if (num > 0 && num <= 256) {
  188. ctx->ComNumber = num;
  189. DbgPrint("[CommModifyKit] PrepareHardware: parsed COM%u from name\n", num);
  190. break;
  191. }
  192. }
  193. }
  194. }
  195. }
  196. WdfObjectDelete(memory);
  197. }
  198. // 后备方案:FriendlyName/DeviceDescription 均未解析出 COM 编号时,
  199. // 从 PDO 名称查询 HKLM\HARDWARE\DEVICEMAP\SERIALCOMM 获取 "COMx" 映射
  200. if (ctx->ComNumber == 0) {
  201. DbgPrint("[CommModifyKit] PrepareHardware: trying SERIALCOMM registry fallback\n");
  202. WDFMEMORY pdo_memory;
  203. status = WdfDeviceAllocAndQueryProperty(
  204. device, DevicePropertyPhysicalDeviceObjectName,
  205. NonPagedPoolNx, 0, &pdo_memory);
  206. if (NT_SUCCESS(status)) {
  207. PWCHAR pdo_name = (PWCHAR)WdfMemoryGetBuffer(pdo_memory, nullptr);
  208. if (pdo_name && pdo_name[0] != L'\0') {
  209. DbgPrint("[CommModifyKit] PrepareHardware: PDO name='%ws'\n", pdo_name);
  210. UNICODE_STRING key_path;
  211. RtlInitUnicodeString(&key_path,
  212. L"\\Registry\\Machine\\HARDWARE\\DEVICEMAP\\SERIALCOMM");
  213. OBJECT_ATTRIBUTES obj_attr;
  214. InitializeObjectAttributes(&obj_attr, &key_path,
  215. OBJ_CASE_INSENSITIVE, nullptr, nullptr);
  216. HANDLE hkey;
  217. status = ZwOpenKey(&hkey, KEY_READ, &obj_attr);
  218. if (NT_SUCCESS(status)) {
  219. UNICODE_STRING value_name;
  220. RtlInitUnicodeString(&value_name, pdo_name);
  221. UCHAR result_buf[256] = {0};
  222. ULONG result_len = 0;
  223. status = ZwQueryValueKey(hkey, &value_name,
  224. KeyValuePartialInformation,
  225. result_buf, sizeof(result_buf), &result_len);
  226. if (NT_SUCCESS(status)) {
  227. KEY_VALUE_PARTIAL_INFORMATION* kv_info =
  228. (KEY_VALUE_PARTIAL_INFORMATION*)result_buf;
  229. if (kv_info->Type == REG_SZ && kv_info->DataLength >= sizeof(WCHAR)) {
  230. PWCHAR com_name = (PWCHAR)kv_info->Data;
  231. ULONG com_len = kv_info->DataLength / sizeof(WCHAR);
  232. DbgPrint("[CommModifyKit] PrepareHardware: SERIALCOMM value='%ws'\n", com_name);
  233. // 解析 "COMx"
  234. if (com_len >= 4 &&
  235. (com_name[0] == L'C' || com_name[0] == L'c') &&
  236. (com_name[1] == L'O' || com_name[1] == L'o') &&
  237. (com_name[2] == L'M' || com_name[2] == L'm') &&
  238. com_name[3] >= L'0' && com_name[3] <= L'9') {
  239. ULONG num = 0;
  240. ULONG j = 3;
  241. while (j < com_len &&
  242. com_name[j] >= L'0' && com_name[j] <= L'9') {
  243. num = num * 10 + (com_name[j] - L'0');
  244. j++;
  245. }
  246. if (num > 0 && num <= 256) {
  247. ctx->ComNumber = num;
  248. DbgPrint("[CommModifyKit] PrepareHardware: parsed COM%u from SERIALCOMM\n", num);
  249. }
  250. }
  251. }
  252. } else {
  253. DbgPrint("[CommModifyKit] PrepareHardware: SERIALCOMM query failed 0x%08X\n", status);
  254. }
  255. ZwClose(hkey);
  256. } else {
  257. DbgPrint("[CommModifyKit] PrepareHardware: ZwOpenKey(SERIALCOMM) failed 0x%08X\n", status);
  258. }
  259. }
  260. WdfObjectDelete(pdo_memory);
  261. }
  262. }
  263. // 后备方案 2:查询本设备注册的 GUID_DEVINTERFACE_COMPORT 接口
  264. // 虚拟串口可能不在 SERIALCOMM 中,但会注册 COMPORT 接口
  265. // 注意:必须传入本设备 PDO,不能传 nullptr 枚举所有设备后用 IoGetDeviceObjectPointer 打开,
  266. // 因为那会发送 IRP_MJ_CREATE 到正在初始化的设备栈,导致死锁
  267. if (ctx->ComNumber == 0) {
  268. DbgPrint("[CommModifyKit] PrepareHardware: trying GUID_DEVINTERFACE_COMPORT fallback\n");
  269. PDEVICE_OBJECT currentPdo = WdfDeviceWdmGetPhysicalDevice(device);
  270. if (currentPdo) {
  271. PWSTR interfaceList = nullptr;
  272. status = IoGetDeviceInterfaces(
  273. &GUID_DEVINTERFACE_COMPORT,
  274. currentPdo, // 仅查询本设备的接口
  275. DEVICE_INTERFACE_INCLUDE_NONACTIVE,
  276. &interfaceList);
  277. if (NT_SUCCESS(status) && interfaceList) {
  278. PWCHAR p = interfaceList;
  279. while (*p && ctx->ComNumber == 0) {
  280. ULONG len = (ULONG)wcslen(p);
  281. DbgPrint("[CommModifyKit] PrepareHardware: interface='%ws'\n", p);
  282. // 从符号链接路径解析 COM 编号(如 "\\?\COM12")
  283. for (ULONG i = 0; i + 3 <= len; i++) {
  284. if ((p[i] == L'C' || p[i] == L'c') &&
  285. (p[i+1] == L'O' || p[i+1] == L'o') &&
  286. (p[i+2] == L'M' || p[i+2] == L'm') &&
  287. p[i+3] >= L'0' && p[i+3] <= L'9') {
  288. ULONG num = 0;
  289. ULONG j = i + 3;
  290. while (j < len && p[j] >= L'0' && p[j] <= L'9') {
  291. num = num * 10 + (p[j] - L'0');
  292. j++;
  293. }
  294. if (num > 0 && num <= 256) {
  295. ctx->ComNumber = num;
  296. DbgPrint("[CommModifyKit] PrepareHardware: parsed COM%u from interface\n", num);
  297. break;
  298. }
  299. }
  300. }
  301. p += len + 1;
  302. }
  303. ExFreePool(interfaceList);
  304. } else {
  305. DbgPrint("[CommModifyKit] PrepareHardware: IoGetDeviceInterfaces failed 0x%08X or empty\n", status);
  306. }
  307. }
  308. }
  309. // 更新端口表中的 COM 编号
  310. commkit_driver::GetClientConnection().UpdatePortComNumber(device, ctx->ComNumber);
  311. DbgPrint("[CommModifyKit] EvtDevicePrepareHardware COMPLETE, ComNumber=%u\n", ctx->ComNumber);
  312. return STATUS_SUCCESS;
  313. }
  314. // 设备上下文清理回调:设备移除时注销端口表条目
  315. VOID EvtDeviceContextCleanup(WDFOBJECT object) {
  316. WDFDEVICE device = (WDFDEVICE)object;
  317. PDEVICE_CONTEXT ctx = DeviceGetContext(device);
  318. if (!ctx) return;
  319. // 释放保存的 FileObject
  320. KIRQL old_irql;
  321. KeAcquireSpinLock(&ctx->FileObjectLock, &old_irql);
  322. PFILE_OBJECT saved = ctx->SavedFileObject;
  323. ctx->SavedFileObject = nullptr;
  324. KeReleaseSpinLock(&ctx->FileObjectLock, old_irql);
  325. if (saved) {
  326. ObDereferenceObject(saved);
  327. }
  328. if (ctx->ComNumber != 0) {
  329. commkit_driver::GetClientConnection().UnregisterFilterDevice(ctx->ComNumber);
  330. }
  331. }
  332. // 文件打开/关闭/数据捕获现在通过 IRP_MJ_CREATE/CLOSE/READ/WRITE 预处理回调处理
  333. // (见 SerialFilter.cpp 中的 DispatchCreate/DispatchClose/DispatchRead/DispatchWrite)。
  334. // 不再使用 WdfDeviceInitSetFileObjectConfig + EvtDeviceFileCreate/EvtFileClose/EvtFileCleanup,
  335. // 因为 WdfDeviceInitSetFileObjectConfig 会导致 WdfDeviceWdmDispatchPreprocessedIrp
  336. // 强制查找 WDFFILEOBJECT,查找失败时返回 STATUS_FILE_FORCED_CLOSED (0xC0000182)。
  337. extern "C" VOID EvtDriverUnload(WDFDRIVER driver) {
  338. UNREFERENCED_PARAMETER(driver);
  339. commkit_driver::GetClientConnection().Cleanup();
  340. commkit_driver::g_wdf_driver = nullptr;
  341. }