DriverEntry.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  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"
  14. extern "C" {
  15. DRIVER_INITIALIZE DriverEntry;
  16. EVT_WDF_DRIVER_DEVICE_ADD EvtDeviceAdd;
  17. EVT_WDF_DRIVER_UNLOAD EvtDriverUnload;
  18. EVT_WDF_OBJECT_CONTEXT_CLEANUP EvtDeviceContextCleanup;
  19. } // extern "C"
  20. // PnP 回调使用 C++ 链接(WDF 宏内部处理)
  21. EVT_WDF_DEVICE_PREPARE_HARDWARE EvtDevicePrepareHardware;
  22. namespace commkit_driver {
  23. // 全局驱动对象(仅用于内部日志/调试)
  24. static WDFDRIVER g_wdf_driver = nullptr;
  25. } // namespace commkit_driver
  26. extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT driver_object,
  27. PUNICODE_STRING registry_path) {
  28. NTSTATUS status;
  29. WDF_DRIVER_CONFIG cfg;
  30. // 初始化全局客户端连接管理器
  31. commkit_driver::GetClientConnection().Initialize();
  32. // 初始化 WDF 配置
  33. WDF_DRIVER_CONFIG_INIT(&cfg, EvtDeviceAdd);
  34. cfg.EvtDriverUnload = EvtDriverUnload;
  35. // 创建 WDF 驱动对象
  36. status = WdfDriverCreate(driver_object, registry_path,
  37. WDF_NO_OBJECT_ATTRIBUTES, &cfg,
  38. &commkit_driver::g_wdf_driver);
  39. if (!NT_SUCCESS(status)) {
  40. return status;
  41. }
  42. // 初始化 ClientConnection(创建控制设备)
  43. status = commkit_driver::CreateControlDevice(commkit_driver::g_wdf_driver);
  44. if (!NT_SUCCESS(status)) {
  45. return status;
  46. }
  47. return STATUS_SUCCESS;
  48. }
  49. extern "C" NTSTATUS EvtDeviceAdd(WDFDRIVER driver, PWDFDEVICE_INIT init) {
  50. UNREFERENCED_PARAMETER(driver);
  51. NTSTATUS status;
  52. // 1. 标记为过滤设备
  53. WdfFdoInitSetFilter(init);
  54. // 2. 注册 PnP 电源回调(EvtDevicePrepareHardware 解析 COM 编号)
  55. WDF_PNPPOWER_EVENT_CALLBACKS pnp_callbacks;
  56. WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnp_callbacks);
  57. pnp_callbacks.EvtDevicePrepareHardware = EvtDevicePrepareHardware;
  58. WdfDeviceInitSetPnpPowerEventCallbacks(init, &pnp_callbacks);
  59. // 3. 注册 WDM IRP 预处理回调(拦截 READ/WRITE/DEVICE_CONTROL)
  60. status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
  61. init, commkit_driver::SerialFilter::DispatchRead,
  62. IRP_MJ_READ, nullptr, 0);
  63. if (!NT_SUCCESS(status)) {
  64. return status;
  65. }
  66. status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
  67. init, commkit_driver::SerialFilter::DispatchWrite,
  68. IRP_MJ_WRITE, nullptr, 0);
  69. if (!NT_SUCCESS(status)) {
  70. return status;
  71. }
  72. status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
  73. init, commkit_driver::SerialFilter::DispatchDeviceControl,
  74. IRP_MJ_DEVICE_CONTROL, nullptr, 0);
  75. if (!NT_SUCCESS(status)) {
  76. return status;
  77. }
  78. // 注册 CREATE/CLOSE/CLEANUP 预处理回调(确保正确转发)
  79. status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
  80. init, commkit_driver::SerialFilter::DispatchCreate,
  81. IRP_MJ_CREATE, nullptr, 0);
  82. if (!NT_SUCCESS(status)) {
  83. return status;
  84. }
  85. status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
  86. init, commkit_driver::SerialFilter::DispatchClose,
  87. IRP_MJ_CLOSE, nullptr, 0);
  88. if (!NT_SUCCESS(status)) {
  89. return status;
  90. }
  91. status = WdfDeviceInitAssignWdmIrpPreprocessCallback(
  92. init, commkit_driver::SerialFilter::DispatchCleanup,
  93. IRP_MJ_CLEANUP, nullptr, 0);
  94. if (!NT_SUCCESS(status)) {
  95. return status;
  96. }
  97. // 4. 设置宽松 SDDL(匹配 serial.sys 行为)
  98. // WDF 默认 SDDL 仅 System+Administrators,会导致非 elevated 进程
  99. // 无法打开 \\.\COMx(即使 Administrator 账户在 filtered token 下也失败)
  100. // AU=Authenticated Users, WD=Everyone 给读写权限
  101. static DECLARE_CONST_UNICODE_STRING(
  102. sddl_filter,
  103. L"D:P(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW;;;AU)(A;;GRGW;;;WD)");
  104. status = WdfDeviceInitAssignSDDLString(init, &sddl_filter);
  105. if (!NT_SUCCESS(status)) {
  106. DbgPrint("[CommModifyKit] WdfDeviceInitAssignSDDLString failed 0x%08X\n", status);
  107. return status;
  108. }
  109. DbgPrint("[CommModifyKit] SDDL assigned OK\n");
  110. // 5. 创建 WDF 设备
  111. WDFDEVICE device;
  112. WDF_OBJECT_ATTRIBUTES attrs;
  113. WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attrs, DEVICE_CONTEXT);
  114. attrs.EvtCleanupCallback = EvtDeviceContextCleanup;
  115. status = WdfDeviceCreate(&init, &attrs, &device);
  116. if (!NT_SUCCESS(status)) {
  117. DbgPrint("[CommModifyKit] WdfDeviceCreate failed 0x%08X\n", status);
  118. return status;
  119. }
  120. DbgPrint("[CommModifyKit] WdfDeviceCreate OK\n");
  121. // 5. 初始化设备上下文
  122. PDEVICE_CONTEXT ctx = DeviceGetContext(device);
  123. ctx->ComNumber = 0; // 延迟到 EvtDevicePrepareHardware 解析
  124. ctx->MonitoringEnabled = FALSE;
  125. ctx->RingBuffer = nullptr;
  126. ctx->WdfDevice = device;
  127. ctx->LowerDevice = WdfDeviceWdmGetAttachedDevice(device);
  128. // 6. 注册到全局端口表(com_number 暂为 0,PrepareHardware 时更新)
  129. commkit_driver::GetClientConnection().RegisterFilterDevice(device);
  130. return STATUS_SUCCESS;
  131. }
  132. NTSTATUS EvtDevicePrepareHardware(WDFDEVICE device,
  133. WDFCMRESLIST resources,
  134. WDFCMRESLIST resources_translated) {
  135. UNREFERENCED_PARAMETER(resources);
  136. UNREFERENCED_PARAMETER(resources_translated);
  137. NTSTATUS status;
  138. PDEVICE_CONTEXT ctx = DeviceGetContext(device);
  139. // 从 PnP 属性解析 COM 编号
  140. // 方式:获取 DevicePropertyFriendlyName 或 DevicePropertyDeviceDescription,
  141. // 匹配 "COM%d" 模式
  142. WDFMEMORY memory;
  143. status = WdfDeviceAllocAndQueryProperty(
  144. device, DevicePropertyFriendlyName, NonPagedPoolNx, 0, &memory);
  145. if (!NT_SUCCESS(status)) {
  146. // 尝试 DeviceDescription 作为后备
  147. status = WdfDeviceAllocAndQueryProperty(
  148. device, DevicePropertyDeviceDescription, NonPagedPoolNx, 0, &memory);
  149. }
  150. if (NT_SUCCESS(status)) {
  151. PWCHAR name = (PWCHAR)WdfMemoryGetBuffer(memory, nullptr);
  152. if (name) {
  153. // 打印 FriendlyName/DeviceDescription 前 40 字符用于诊断
  154. DbgPrint("[CommModifyKit] FriendlyName='%ws'\n", name);
  155. // 解析 "COMx" 模式(可能出现在 "COMx (port)" 或 "通信端口 (COMx)" 等格式中)
  156. ULONG name_len = (ULONG)wcslen(name);
  157. for (ULONG i = 0; i + 3 <= name_len; i++) {
  158. if ((name[i] == L'C' || name[i] == L'c') &&
  159. (name[i+1] == L'O' || name[i+1] == L'o') &&
  160. (name[i+2] == L'M' || name[i+2] == L'm')) {
  161. if (name[i+3] >= L'0' && name[i+3] <= L'9') {
  162. ULONG num = 0;
  163. ULONG j = i + 3;
  164. while (j < name_len && name[j] >= L'0' && name[j] <= L'9') {
  165. num = num * 10 + (name[j] - L'0');
  166. j++;
  167. }
  168. if (num > 0 && num <= 256) {
  169. ctx->ComNumber = num;
  170. break;
  171. }
  172. }
  173. }
  174. }
  175. }
  176. WdfObjectDelete(memory);
  177. }
  178. // 后备方案:FriendlyName/DeviceDescription 均未解析出 COM 编号时,
  179. // 从 PDO 名称查询 HKLM\HARDWARE\DEVICEMAP\SERIALCOMM 获取 "COMx" 映射
  180. if (ctx->ComNumber == 0) {
  181. WDFMEMORY pdo_memory;
  182. status = WdfDeviceAllocAndQueryProperty(
  183. device, DevicePropertyPhysicalDeviceObjectName,
  184. NonPagedPoolNx, 0, &pdo_memory);
  185. if (NT_SUCCESS(status)) {
  186. PWCHAR pdo_name = (PWCHAR)WdfMemoryGetBuffer(pdo_memory, nullptr);
  187. if (pdo_name && pdo_name[0] != L'\0') {
  188. UNICODE_STRING key_path;
  189. RtlInitUnicodeString(&key_path,
  190. L"\\Registry\\Machine\\HARDWARE\\DEVICEMAP\\SERIALCOMM");
  191. OBJECT_ATTRIBUTES obj_attr;
  192. InitializeObjectAttributes(&obj_attr, &key_path,
  193. OBJ_CASE_INSENSITIVE, nullptr, nullptr);
  194. HANDLE hkey;
  195. status = ZwOpenKey(&hkey, KEY_READ, &obj_attr);
  196. if (NT_SUCCESS(status)) {
  197. UNICODE_STRING value_name;
  198. RtlInitUnicodeString(&value_name, pdo_name);
  199. UCHAR result_buf[256] = {0};
  200. ULONG result_len = 0;
  201. status = ZwQueryValueKey(hkey, &value_name,
  202. KeyValuePartialInformation,
  203. result_buf, sizeof(result_buf), &result_len);
  204. if (NT_SUCCESS(status)) {
  205. KEY_VALUE_PARTIAL_INFORMATION* kv_info =
  206. (KEY_VALUE_PARTIAL_INFORMATION*)result_buf;
  207. if (kv_info->Type == REG_SZ && kv_info->DataLength >= sizeof(WCHAR)) {
  208. PWCHAR com_name = (PWCHAR)kv_info->Data;
  209. ULONG com_len = kv_info->DataLength / sizeof(WCHAR);
  210. // 解析 "COMx"
  211. if (com_len >= 4 &&
  212. (com_name[0] == L'C' || com_name[0] == L'c') &&
  213. (com_name[1] == L'O' || com_name[1] == L'o') &&
  214. (com_name[2] == L'M' || com_name[2] == L'm') &&
  215. com_name[3] >= L'0' && com_name[3] <= L'9') {
  216. ULONG num = 0;
  217. ULONG j = 3;
  218. while (j < com_len &&
  219. com_name[j] >= L'0' && com_name[j] <= L'9') {
  220. num = num * 10 + (com_name[j] - L'0');
  221. j++;
  222. }
  223. if (num > 0 && num <= 256) {
  224. ctx->ComNumber = num;
  225. }
  226. }
  227. }
  228. }
  229. ZwClose(hkey);
  230. }
  231. }
  232. WdfObjectDelete(pdo_memory);
  233. }
  234. }
  235. // 后备方案 2:查询本设备注册的 GUID_DEVINTERFACE_COMPORT 接口
  236. // 虚拟串口可能不在 SERIALCOMM 中,但会注册 COMPORT 接口
  237. // 注意:必须传入本设备 PDO,不能传 nullptr 枚举所有设备后用 IoGetDeviceObjectPointer 打开,
  238. // 因为那会发送 IRP_MJ_CREATE 到正在初始化的设备栈,导致死锁
  239. if (ctx->ComNumber == 0) {
  240. PDEVICE_OBJECT currentPdo = WdfDeviceWdmGetPhysicalDevice(device);
  241. if (currentPdo) {
  242. PWSTR interfaceList = nullptr;
  243. status = IoGetDeviceInterfaces(
  244. &GUID_DEVINTERFACE_COMPORT,
  245. currentPdo, // 仅查询本设备的接口
  246. DEVICE_INTERFACE_INCLUDE_NONACTIVE,
  247. &interfaceList);
  248. if (NT_SUCCESS(status) && interfaceList) {
  249. PWCHAR p = interfaceList;
  250. while (*p && ctx->ComNumber == 0) {
  251. ULONG len = (ULONG)wcslen(p);
  252. // 从符号链接路径解析 COM 编号(如 "\\?\COM12")
  253. for (ULONG i = 0; i + 3 <= len; i++) {
  254. if ((p[i] == L'C' || p[i] == L'c') &&
  255. (p[i+1] == L'O' || p[i+1] == L'o') &&
  256. (p[i+2] == L'M' || p[i+2] == L'm') &&
  257. p[i+3] >= L'0' && p[i+3] <= L'9') {
  258. ULONG num = 0;
  259. ULONG j = i + 3;
  260. while (j < len && p[j] >= L'0' && p[j] <= L'9') {
  261. num = num * 10 + (p[j] - L'0');
  262. j++;
  263. }
  264. if (num > 0 && num <= 256) {
  265. ctx->ComNumber = num;
  266. break;
  267. }
  268. }
  269. }
  270. p += len + 1;
  271. }
  272. ExFreePool(interfaceList);
  273. }
  274. }
  275. }
  276. // 更新端口表中的 COM 编号
  277. commkit_driver::GetClientConnection().UpdatePortComNumber(device, ctx->ComNumber);
  278. DbgPrint("[CommModifyKit] EvtDevicePrepareHardware OK, ComNumber=%u\n", ctx->ComNumber);
  279. return STATUS_SUCCESS;
  280. }
  281. // 设备上下文清理回调:设备移除时注销端口表条目
  282. VOID EvtDeviceContextCleanup(WDFOBJECT object) {
  283. WDFDEVICE device = (WDFDEVICE)object;
  284. PDEVICE_CONTEXT ctx = DeviceGetContext(device);
  285. if (ctx && ctx->ComNumber != 0) {
  286. commkit_driver::GetClientConnection().UnregisterFilterDevice(ctx->ComNumber);
  287. }
  288. }
  289. extern "C" VOID EvtDriverUnload(WDFDRIVER driver) {
  290. UNREFERENCED_PARAMETER(driver);
  291. commkit_driver::GetClientConnection().Cleanup();
  292. commkit_driver::g_wdf_driver = nullptr;
  293. }