经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
驱动开发:内核封装WFP防火墙入门
来源:cnblogs  作者:lyshark  时间:2023/6/8 9:25:57  对本文有异议

WFP框架是微软推出来替代TDIHOOK传输层驱动接口网络通信的方案,其默认被设计为分层结构,该框架分别提供了用户态与内核态相同的AIP函数,在两种模式下均可以开发防火墙产品,以下代码我实现了一个简单的驱动过滤防火墙。

WFP 框架分为两大层次模块,用户态基础过滤引擎BFE (BaseFilteringEngine) ,以及内核态过滤引擎 KMFE (KMFilteringEngine),基础过滤引擎对上提供C语言调用方式的API以及RPC接口,这些接口都被封装在FWPUCLNT.dll模块中,开发时可以调用该模块中的导出函数.

  • WFP程序工作流程:
  • 使用 FwpmEngineOpen() 开启 WFP 引擎,获得WFP使用句柄
  • 使用 FwpmTransactionBegin() 设置对网络通信内容的过滤权限 (只读/允许修改)
  • 使用 FwpsCalloutRegister(),FwpmCalloutAdd(),FwpmFilterAdd() 选择要过滤的内容,并添加过滤器对象和回调函数.
  • 使用 FwpmTransactionCommit() 确认刚才的内容,让刚才添加的回调函数开始生效.
  • 使用 FwpmFilterDeleteById(),FwpmCalloutDeleteById(),FwpsCalloutUnregisterById()函数撤销对象和回调函数.
  • 使用 FwpmEngineClose() 关闭WFP引擎类句柄.

默认情况下WFP一次需要注册3个回调函数,只有一个是事前回调,另外两个是事后回调,通常情况下我们只关注事前回调即可,此外WFP能过滤很对内容,我们需要指定过滤条件标志来输出我们所需要的数据.

  • 一般可设置为FWPM_LAYER_ALE_AUTH_CONNECT_V4意思是设置IPV4过滤.
  • 还需要设置一个GUID值,该值可随意设置,名称为GUID_ALE_AUTH_CONNECT_CALLOUT_V4宏.

首先我们通过上方的流程实现一个简单的网络控制驱动,该驱动运行后可对自身机器访问指定地址端口进行控制,例如实现指定应用断网,禁止指定页面被访问等,在配置WFP开发环境时需要在链接器选项卡中的附加依赖项中增加fwpkclnt.lib,uuid.lib这两个库文件,并且需要使用WDM开发模板,否则编译将不通过。

  1. // 署名权
  2. // right to sign one's name on a piece of work
  3. // PowerBy: LyShark
  4. // Email: me@lyshark.com
  5. #define NDIS_SUPPORT_NDIS6 1
  6. #define DEV_NAME L"\\Device\\MY_WFP_DEV_NAME"
  7. #define SYM_NAME L"\\DosDevices\\MY_WFP_SYM_NAME"
  8. #include <ntifs.h>
  9. #include <fwpsk.h>
  10. #include <fwpmk.h>
  11. #include <stdio.h>
  12. // 过滤器引擎句柄
  13. HANDLE g_hEngine;
  14. // 过滤器引擎中的callout的运行时标识符
  15. ULONG32 g_AleConnectCalloutId;
  16. // 过滤器的运行时标识符
  17. ULONG64 g_AleConnectFilterId;
  18. // 指定唯一UUID值(只要不冲突即可,内容可随意)
  19. GUID GUID_ALE_AUTH_CONNECT_CALLOUT_V4 = { 0x6812fc83, 0x7d3e, 0x499a, 0xa0, 0x12, 0x55, 0xe0, 0xd8, 0x5f, 0x34, 0x8b };
  20. // ------------------------------------------------------------------------------
  21. // 头部函数声明
  22. // ------------------------------------------------------------------------------
  23. // 注册Callout并设置过滤点
  24. NTSTATUS RegisterCalloutForLayer(
  25. IN PDEVICE_OBJECT pDevObj,
  26. IN const GUID *layerKey,
  27. IN const GUID *calloutKey,
  28. IN FWPS_CALLOUT_CLASSIFY_FN classifyFn,
  29. IN FWPS_CALLOUT_NOTIFY_FN notifyFn,
  30. IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn,
  31. OUT ULONG32 *calloutId,
  32. OUT ULONG64 *filterId,
  33. OUT HANDLE *engine);
  34. // 注册Callout
  35. NTSTATUS RegisterCallout(
  36. PDEVICE_OBJECT pDevObj,
  37. IN const GUID *calloutKey,
  38. IN FWPS_CALLOUT_CLASSIFY_FN classifyFn,
  39. IN FWPS_CALLOUT_NOTIFY_FN notifyFn,
  40. IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn,
  41. OUT ULONG32 *calloutId);
  42. // 设置过滤点
  43. NTSTATUS SetFilter(
  44. IN const GUID *layerKey,
  45. IN const GUID *calloutKey,
  46. OUT ULONG64 *filterId,
  47. OUT HANDLE *engine);
  48. // Callout函数 flowDeleteFn
  49. VOID NTAPI flowDeleteFn(
  50. _In_ UINT16 layerId,
  51. _In_ UINT32 calloutId,
  52. _In_ UINT64 flowContext
  53. );
  54. // Callout函数 classifyFn
  55. #if (NTDDI_VERSION >= NTDDI_WIN8)
  56. VOID NTAPI classifyFn(
  57. _In_ const FWPS_INCOMING_VALUES0* inFixedValues,
  58. _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
  59. _Inout_opt_ void* layerData,
  60. _In_opt_ const void* classifyContext,
  61. _In_ const FWPS_FILTER2* filter,
  62. _In_ UINT64 flowContext,
  63. _Inout_ FWPS_CLASSIFY_OUT0* classifyOut
  64. );
  65. #elif (NTDDI_VERSION >= NTDDI_WIN7)
  66. VOID NTAPI classifyFn(
  67. _In_ const FWPS_INCOMING_VALUES0* inFixedValues,
  68. _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
  69. _Inout_opt_ void* layerData,
  70. _In_opt_ const void* classifyContext,
  71. _In_ const FWPS_FILTER1* filter,
  72. _In_ UINT64 flowContext,
  73. _Inout_ FWPS_CLASSIFY_OUT0* classifyOut
  74. );
  75. #else
  76. VOID NTAPI classifyFn(
  77. _In_ const FWPS_INCOMING_VALUES0* inFixedValues,
  78. _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
  79. _Inout_opt_ void* layerData,
  80. _In_ const FWPS_FILTER0* filter,
  81. _In_ UINT64 flowContext,
  82. _Inout_ FWPS_CLASSIFY_OUT0* classifyOut
  83. );
  84. #endif
  85. // Callout函数 notifyFn
  86. #if (NTDDI_VERSION >= NTDDI_WIN8)
  87. NTSTATUS NTAPI notifyFn(
  88. _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType,
  89. _In_ const GUID* filterKey,
  90. _Inout_ FWPS_FILTER2* filter
  91. );
  92. #elif (NTDDI_VERSION >= NTDDI_WIN7)
  93. NTSTATUS NTAPI notifyFn(
  94. _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType,
  95. _In_ const GUID* filterKey,
  96. _Inout_ FWPS_FILTER1* filter
  97. );
  98. #else
  99. NTSTATUS NTAPI notifyFn(
  100. _In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType,
  101. _In_ const GUID* filterKey,
  102. _Inout_ FWPS_FILTER0* filter
  103. );
  104. #endif
  105. // ------------------------------------------------------------------------------
  106. // 函数实现部分
  107. // ------------------------------------------------------------------------------
  108. // 协议判断
  109. NTSTATUS ProtocalIdToName(UINT16 protocalId, PCHAR lpszProtocalName)
  110. {
  111. NTSTATUS status = STATUS_SUCCESS;
  112. switch (protocalId)
  113. {
  114. case 1:
  115. {
  116. // ICMP
  117. RtlCopyMemory(lpszProtocalName, "ICMP", 5);
  118. break;
  119. }
  120. case 2:
  121. {
  122. // IGMP
  123. RtlCopyMemory(lpszProtocalName, "IGMP", 5);
  124. break;
  125. }
  126. case 6:
  127. {
  128. // TCP
  129. RtlCopyMemory(lpszProtocalName, "TCP", 4);
  130. break;
  131. }
  132. case 17:
  133. {
  134. // UDP
  135. RtlCopyMemory(lpszProtocalName, "UDP", 4);
  136. break;
  137. }
  138. case 27:
  139. {
  140. // RDP
  141. RtlCopyMemory(lpszProtocalName, "RDP", 6);
  142. break;
  143. }
  144. default:
  145. {
  146. // UNKNOW
  147. RtlCopyMemory(lpszProtocalName, "UNKNOWN", 8);
  148. break;
  149. }
  150. }
  151. return status;
  152. }
  153. // 启动WFP
  154. NTSTATUS WfpLoad(PDEVICE_OBJECT pDevObj)
  155. {
  156. NTSTATUS status = STATUS_SUCCESS;
  157. // 注册Callout并设置过滤点
  158. // classifyFn, notifyFn, flowDeleteFn 注册三个回调函数,一个事前回调,两个事后回调
  159. status = RegisterCalloutForLayer(pDevObj, &FWPM_LAYER_ALE_AUTH_CONNECT_V4, &GUID_ALE_AUTH_CONNECT_CALLOUT_V4,
  160. classifyFn, notifyFn, flowDeleteFn, &g_AleConnectCalloutId, &g_AleConnectFilterId, &g_hEngine);
  161. if (!NT_SUCCESS(status))
  162. {
  163. DbgPrint("注册回调失败 \n");
  164. return status;
  165. }
  166. return status;
  167. }
  168. // 卸载WFP
  169. NTSTATUS WfpUnload()
  170. {
  171. if (NULL != g_hEngine)
  172. {
  173. // 删除FilterId
  174. FwpmFilterDeleteById(g_hEngine, g_AleConnectFilterId);
  175. // 删除CalloutId
  176. FwpmCalloutDeleteById(g_hEngine, g_AleConnectCalloutId);
  177. // 清空Filter
  178. g_AleConnectFilterId = 0;
  179. // 反注册CalloutId
  180. FwpsCalloutUnregisterById(g_AleConnectCalloutId);
  181. // 清空CalloutId
  182. g_AleConnectCalloutId = 0;
  183. // 关闭引擎
  184. FwpmEngineClose(g_hEngine);
  185. g_hEngine = NULL;
  186. }
  187. return STATUS_SUCCESS;
  188. }
  189. // 注册Callout并设置过滤点
  190. NTSTATUS RegisterCalloutForLayer(IN PDEVICE_OBJECT pDevObj, IN const GUID *layerKey, IN const GUID *calloutKey, IN FWPS_CALLOUT_CLASSIFY_FN classifyFn, IN FWPS_CALLOUT_NOTIFY_FN notifyFn, IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn, OUT ULONG32 *calloutId, OUT ULONG64 *filterId, OUT HANDLE *engine)
  191. {
  192. NTSTATUS status = STATUS_SUCCESS;
  193. // 注册Callout
  194. status = RegisterCallout(pDevObj, calloutKey, classifyFn, notifyFn, flowDeleteNotifyFn, calloutId);
  195. if (!NT_SUCCESS(status))
  196. {
  197. return status;
  198. }
  199. // 设置过滤点
  200. status = SetFilter(layerKey, calloutKey, filterId, engine);
  201. if (!NT_SUCCESS(status))
  202. {
  203. return status;
  204. }
  205. return status;
  206. }
  207. // 注册Callout
  208. NTSTATUS RegisterCallout(PDEVICE_OBJECT pDevObj, IN const GUID *calloutKey, IN FWPS_CALLOUT_CLASSIFY_FN classifyFn, IN FWPS_CALLOUT_NOTIFY_FN notifyFn, IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn, OUT ULONG32 *calloutId)
  209. {
  210. NTSTATUS status = STATUS_SUCCESS;
  211. FWPS_CALLOUT sCallout = { 0 };
  212. // 设置Callout
  213. sCallout.calloutKey = *calloutKey;
  214. sCallout.classifyFn = classifyFn;
  215. sCallout.flowDeleteFn = flowDeleteNotifyFn;
  216. sCallout.notifyFn = notifyFn;
  217. // 注册Callout
  218. status = FwpsCalloutRegister(pDevObj, &sCallout, calloutId);
  219. if (!NT_SUCCESS(status))
  220. {
  221. DbgPrint("注册Callout失败 \n");
  222. return status;
  223. }
  224. return status;
  225. }
  226. // 设置过滤点
  227. NTSTATUS SetFilter(IN const GUID *layerKey, IN const GUID *calloutKey, OUT ULONG64 *filterId, OUT HANDLE *engine)
  228. {
  229. HANDLE hEngine = NULL;
  230. NTSTATUS status = STATUS_SUCCESS;
  231. FWPM_SESSION session = { 0 };
  232. FWPM_FILTER mFilter = { 0 };
  233. FWPM_CALLOUT mCallout = { 0 };
  234. FWPM_DISPLAY_DATA mDispData = { 0 };
  235. // 创建Session
  236. session.flags = FWPM_SESSION_FLAG_DYNAMIC;
  237. status = FwpmEngineOpen(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &hEngine);
  238. if (!NT_SUCCESS(status))
  239. {
  240. return status;
  241. }
  242. // 开始事务
  243. status = FwpmTransactionBegin(hEngine, 0);
  244. if (!NT_SUCCESS(status))
  245. {
  246. return status;
  247. }
  248. // 设置Callout参数
  249. mDispData.name = L"MY WFP LyShark";
  250. mDispData.description = L"WORLD OF DEMON";
  251. mCallout.applicableLayer = *layerKey;
  252. mCallout.calloutKey = *calloutKey;
  253. mCallout.displayData = mDispData;
  254. // 添加Callout到Session中
  255. status = FwpmCalloutAdd(hEngine, &mCallout, NULL, NULL);
  256. if (!NT_SUCCESS(status))
  257. {
  258. return status;
  259. }
  260. // 设置过滤器参数
  261. mFilter.action.calloutKey = *calloutKey;
  262. mFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
  263. mFilter.displayData.name = L"MY WFP LyShark";
  264. mFilter.displayData.description = L"WORLD OF DEMON";
  265. mFilter.layerKey = *layerKey;
  266. mFilter.subLayerKey = FWPM_SUBLAYER_UNIVERSAL;
  267. mFilter.weight.type = FWP_EMPTY;
  268. // 添加过滤器
  269. status = FwpmFilterAdd(hEngine, &mFilter, NULL, filterId);
  270. if (!NT_SUCCESS(status))
  271. {
  272. return status;
  273. }
  274. // 提交事务
  275. status = FwpmTransactionCommit(hEngine);
  276. if (!NT_SUCCESS(status))
  277. {
  278. return status;
  279. }
  280. *engine = hEngine;
  281. return status;
  282. }
  283. // Callout函数 classifyFn 事前回调函数
  284. VOID NTAPI classifyFn(_In_ const FWPS_INCOMING_VALUES0* inFixedValues, _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, _Inout_opt_ void* layerData, _In_opt_ const void* classifyContext, _In_ const FWPS_FILTER2* filter, _In_ UINT64 flowContext, _Inout_ FWPS_CLASSIFY_OUT0* classifyOut)
  285. {
  286. // 数据包的方向,取值 FWP_DIRECTION_INBOUND = 1 或 FWP_DIRECTION_OUTBOUND = 0
  287. WORD wDirection = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.int8;
  288. // 定义本机地址与本机端口
  289. ULONG ulLocalIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32;
  290. UINT16 uLocalPort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16;
  291. // 定义对端地址与对端端口
  292. ULONG ulRemoteIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32;
  293. UINT16 uRemotePort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16;
  294. // 获取当前进程IRQ
  295. KIRQL kCurrentIrql = KeGetCurrentIrql();
  296. // 获取进程ID
  297. ULONG64 processId = inMetaValues->processId;
  298. UCHAR szProcessPath[256] = { 0 };
  299. CHAR szProtocalName[256] = { 0 };
  300. RtlZeroMemory(szProcessPath, 256);
  301. // 获取进程路径
  302. for (ULONG i = 0; i < inMetaValues->processPath->size; i++)
  303. {
  304. // 里面是宽字符存储的
  305. szProcessPath[i] = inMetaValues->processPath->data[i];
  306. }
  307. // 获取当前协议类型
  308. ProtocalIdToName(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16, szProtocalName);
  309. // 设置默认规则 允许连接
  310. classifyOut->actionType = FWP_ACTION_PERMIT;
  311. // 禁止指定进程网络连接
  312. if (NULL != wcsstr((PWCHAR)szProcessPath, L"iexplore.exe"))
  313. {
  314. // 设置拒绝规则 拒绝连接
  315. classifyOut->actionType = FWP_ACTION_BLOCK;
  316. classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  317. classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  318. DbgPrint("[LyShark.com] 拦截IE网络链接请求... \n");
  319. }
  320. // 输出对端地址字符串 并阻断链接
  321. char szRemoteAddress[256] = { 0 };
  322. char szRemotePort[128] = { 0 };
  323. char szLocalAddress[256] = { 0 };
  324. char szLocalPort[128] = { 0 };
  325. sprintf(szRemoteAddress, "%u.%u.%u.%u", (ulRemoteIp >> 24) & 0xFF, (ulRemoteIp >> 16) & 0xFF, (ulRemoteIp >> 8) & 0xFF, (ulRemoteIp)& 0xFF);
  326. sprintf(szRemotePort, "%d", uRemotePort);
  327. sprintf(szLocalAddress, "%u.%u.%u.%u", (ulLocalIp >> 24) & 0xFF, (ulLocalIp >> 16) & 0xFF, (ulLocalIp >> 8) & 0xFF, (ulLocalIp)& 0xFF);
  328. sprintf(szLocalPort, "%d", uLocalPort);
  329. // DbgPrint("本端: %s : %s --> 对端: %s : %s \n", szLocalAddress, szLocalPort, szRemoteAddress, szRemotePort);
  330. // 如果对端地址是 8.141.58.64 且对端端口是 443 则拒绝连接
  331. if (strcmp(szRemoteAddress, "8.141.58.64") == 0 && strcmp(szRemotePort, "443") == 0)
  332. {
  333. DbgPrint("[LyShark.com] 拦截网站访问请求 --> %s : %s \n", szRemoteAddress, szRemotePort);
  334. // 设置拒绝规则 拒绝连接
  335. classifyOut->actionType = FWP_ACTION_BLOCK;
  336. classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  337. classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  338. }
  339. else if (strcmp(szRemotePort, "0") == 0)
  340. {
  341. DbgPrint("[LyShark.com] 拦截Ping访问请求 --> %s \n", szRemoteAddress);
  342. // 设置拒绝规则 拒绝连接
  343. classifyOut->actionType = FWP_ACTION_BLOCK;
  344. classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  345. classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  346. }
  347. // 显示
  348. DbgPrint("[LyShark.com] 方向: %d -> 协议类型: %s -> 本端地址: %u.%u.%u.%u:%d -> 对端地址: %u.%u.%u.%u:%d -> IRQL: %d -> 进程ID: %I64d -> 路径: %S \n",
  349. wDirection,
  350. szProtocalName,
  351. (ulLocalIp >> 24) & 0xFF,
  352. (ulLocalIp >> 16) & 0xFF,
  353. (ulLocalIp >> 8) & 0xFF,
  354. (ulLocalIp)& 0xFF,
  355. uLocalPort,
  356. (ulRemoteIp >> 24) & 0xFF,
  357. (ulRemoteIp >> 16) & 0xFF,
  358. (ulRemoteIp >> 8) & 0xFF,
  359. (ulRemoteIp)& 0xFF,
  360. uRemotePort,
  361. kCurrentIrql,
  362. processId,
  363. (PWCHAR)szProcessPath);
  364. }
  365. // Callout函数 notifyFn 事后回调函数
  366. NTSTATUS NTAPI notifyFn(_In_ FWPS_CALLOUT_NOTIFY_TYPE notifyType, _In_ const GUID* filterKey, _Inout_ FWPS_FILTER2* filter)
  367. {
  368. NTSTATUS status = STATUS_SUCCESS;
  369. return status;
  370. }
  371. // Callout函数 flowDeleteFn 事后回调函数
  372. VOID NTAPI flowDeleteFn(_In_ UINT16 layerId, _In_ UINT32 calloutId, _In_ UINT64 flowContext)
  373. {
  374. return;
  375. }
  376. // 默认派遣函数
  377. NTSTATUS DriverDefaultHandle(PDEVICE_OBJECT pDevObj, PIRP pIrp)
  378. {
  379. NTSTATUS status = STATUS_SUCCESS;
  380. pIrp->IoStatus.Status = status;
  381. pIrp->IoStatus.Information = 0;
  382. IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  383. return status;
  384. }
  385. // 创建设备
  386. NTSTATUS CreateDevice(PDRIVER_OBJECT pDriverObject)
  387. {
  388. NTSTATUS status = STATUS_SUCCESS;
  389. PDEVICE_OBJECT pDevObj = NULL;
  390. UNICODE_STRING ustrDevName, ustrSymName;
  391. RtlInitUnicodeString(&ustrDevName, DEV_NAME);
  392. RtlInitUnicodeString(&ustrSymName, SYM_NAME);
  393. status = IoCreateDevice(pDriverObject, 0, &ustrDevName, FILE_DEVICE_NETWORK, 0, FALSE, &pDevObj);
  394. if (!NT_SUCCESS(status))
  395. {
  396. return status;
  397. }
  398. status = IoCreateSymbolicLink(&ustrSymName, &ustrDevName);
  399. if (!NT_SUCCESS(status))
  400. {
  401. return status;
  402. }
  403. return status;
  404. }
  405. // 卸载驱动
  406. VOID UnDriver(PDRIVER_OBJECT driver)
  407. {
  408. // 删除回调函数和过滤器,关闭引擎
  409. WfpUnload();
  410. UNICODE_STRING ustrSymName;
  411. RtlInitUnicodeString(&ustrSymName, SYM_NAME);
  412. IoDeleteSymbolicLink(&ustrSymName);
  413. if (driver->DeviceObject)
  414. {
  415. IoDeleteDevice(driver->DeviceObject);
  416. }
  417. }
  418. // 驱动入口
  419. NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
  420. {
  421. NTSTATUS status = STATUS_SUCCESS;
  422. Driver->DriverUnload = UnDriver;
  423. for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
  424. {
  425. Driver->MajorFunction[i] = DriverDefaultHandle;
  426. }
  427. // 创建设备
  428. CreateDevice(Driver);
  429. // 启动WFP
  430. WfpLoad(Driver->DeviceObject);
  431. Driver->DriverUnload = UnDriver;
  432. return STATUS_SUCCESS;
  433. }

上方代码是一个最基本的WFP过滤框架头部函数,声明部分来源于微软的定义此处不做解释,需要注意GUID_ALE_AUTH_CONNECT_CALLOUT_V4代表的是一个随机UUID值,该值可以任意定义只要不一致即可,驱动程序运行后会率先执行WfpLoad()这个函数,该函数内部通过RegisterCalloutForLayer()注册了一个过滤点,此处我们必须要注意三个回调函数,classifyFn, notifyFn, flowDeleteFn 他们分别的功能时,事前回调,事后回调,事后回调,而WFP框架中我们最需要注意的也就是对这三个函数进行重定义,也就是需要重写函数来实现我们特定的功能。

  1. NTSTATUS RegisterCalloutForLayer
  2. (
  3. IN const GUID* layerKey,
  4. IN const GUID* calloutKey,
  5. IN FWPS_CALLOUT_CLASSIFY_FN classifyFn,
  6. IN FWPS_CALLOUT_NOTIFY_FN notifyFn,
  7. IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn,
  8. OUT UINT32* calloutId,
  9. OUT UINT64* filterId
  10. }

既然是防火墙那么必然classifyFn事前更重要一些,如果需要监控网络流量则需要在事前函数中做处理,而如果是监视则可以在事后做处理,既然要在事前进行处理,那么我们就来看看事前是如何处理的流量。

  1. // Callout函数 classifyFn 事前回调函数
  2. VOID NTAPI classifyFn(_In_ const FWPS_INCOMING_VALUES0* inFixedValues, _In_ const FWPS_INCOMING_METADATA_VALUES0* inMetaValues, _Inout_opt_ void* layerData, _In_opt_ const void* classifyContext, _In_ const FWPS_FILTER2* filter, _In_ UINT64 flowContext, _Inout_ FWPS_CLASSIFY_OUT0* classifyOut)
  3. {
  4. // 数据包的方向,取值 FWP_DIRECTION_INBOUND = 1 或 FWP_DIRECTION_OUTBOUND = 0
  5. WORD wDirection = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.int8;
  6. // 定义本机地址与本机端口
  7. ULONG ulLocalIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32;
  8. UINT16 uLocalPort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16;
  9. // 定义对端地址与对端端口
  10. ULONG ulRemoteIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32;
  11. UINT16 uRemotePort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16;
  12. // 获取当前进程IRQ
  13. KIRQL kCurrentIrql = KeGetCurrentIrql();
  14. // 获取进程ID
  15. ULONG64 processId = inMetaValues->processId;
  16. UCHAR szProcessPath[256] = { 0 };
  17. CHAR szProtocalName[256] = { 0 };
  18. RtlZeroMemory(szProcessPath, 256);
  19. // 获取进程路径
  20. for (ULONG i = 0; i < inMetaValues->processPath->size; i++)
  21. {
  22. // 里面是宽字符存储的
  23. szProcessPath[i] = inMetaValues->processPath->data[i];
  24. }
  25. // 获取当前协议类型
  26. ProtocalIdToName(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16, szProtocalName);
  27. // 设置默认规则 允许连接
  28. classifyOut->actionType = FWP_ACTION_PERMIT;
  29. // 禁止指定进程网络连接
  30. if (NULL != wcsstr((PWCHAR)szProcessPath, L"qq.exe"))
  31. {
  32. // 设置拒绝规则 拒绝连接
  33. classifyOut->actionType = FWP_ACTION_BLOCK;
  34. classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  35. classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  36. }
  37. // 输出对端地址字符串 并阻断链接
  38. char szRemoteAddress[256] = { 0 };
  39. char szRemotePort[128] = { 0 };
  40. char szLocalAddress[256] = { 0 };
  41. char szLocalPort[128] = { 0 };
  42. sprintf(szRemoteAddress, "%u.%u.%u.%u", (ulRemoteIp >> 24) & 0xFF, (ulRemoteIp >> 16) & 0xFF, (ulRemoteIp >> 8) & 0xFF, (ulRemoteIp)& 0xFF);
  43. sprintf(szRemotePort, "%d", uRemotePort);
  44. sprintf(szLocalAddress, "%u.%u.%u.%u", (ulLocalIp >> 24) & 0xFF, (ulLocalIp >> 16) & 0xFF, (ulLocalIp >> 8) & 0xFF, (ulLocalIp)& 0xFF);
  45. sprintf(szLocalPort, "%d", uLocalPort);
  46. // DbgPrint("本端: %s : %s --> 对端: %s : %s \n", szLocalAddress, szLocalPort, szRemoteAddress, szRemotePort);
  47. // 如果对端地址是 8.141.58.64 且对端端口是 443 则拒绝连接
  48. if (strcmp(szRemoteAddress, "8.141.58.64") == 0 && strcmp(szRemotePort, "443") == 0)
  49. {
  50. DbgPrint("拦截网站访问请求 --> %s : %s \n", szRemoteAddress, szRemotePort);
  51. // 设置拒绝规则 拒绝连接
  52. classifyOut->actionType = FWP_ACTION_BLOCK;
  53. classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  54. classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  55. }
  56. else if (strcmp(szRemotePort, "0") == 0)
  57. {
  58. DbgPrint("拦截Ping访问请求 --> %s \n", szRemoteAddress);
  59. // 设置拒绝规则 拒绝连接
  60. classifyOut->actionType = FWP_ACTION_BLOCK;
  61. classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  62. classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  63. }
  64. /*
  65. // 显示
  66. DbgPrint("方向: %d -> 协议类型: %s -> 本端地址: %u.%u.%u.%u:%d -> 对端地址: %u.%u.%u.%u:%d -> IRQL: %d -> 进程ID: %I64d -> 路径: %S \n",
  67. wDirection,
  68. szProtocalName,
  69. (ulLocalIp >> 24) & 0xFF,
  70. (ulLocalIp >> 16) & 0xFF,
  71. (ulLocalIp >> 8) & 0xFF,
  72. (ulLocalIp)& 0xFF,
  73. uLocalPort,
  74. (ulRemoteIp >> 24) & 0xFF,
  75. (ulRemoteIp >> 16) & 0xFF,
  76. (ulRemoteIp >> 8) & 0xFF,
  77. (ulRemoteIp)& 0xFF,
  78. uRemotePort,
  79. kCurrentIrql,
  80. processId,
  81. (PWCHAR)szProcessPath);
  82. */
  83. }

当有新的网络数据包路由到事前函数时,程序中会通过如下案例直接得到我们所需要的数据包头,ProtocalIdToName函数则是一个将特定类型数字转为字符串的转换函数。

  1. // 数据包的方向,取值 FWP_DIRECTION_INBOUND = 1 或 FWP_DIRECTION_OUTBOUND = 0
  2. WORD wDirection = inFixedValues->incomingValue[FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_DIRECTION].value.int8;
  3. // 定义本机地址与本机端口
  4. ULONG ulLocalIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32;
  5. UINT16 uLocalPort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16;
  6. // 定义对端地址与对端端口
  7. ULONG ulRemoteIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32;
  8. UINT16 uRemotePort = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16;
  9. // 获取当前进程IRQ
  10. KIRQL kCurrentIrql = KeGetCurrentIrql();
  11. // 获取进程ID
  12. ULONG64 processId = inMetaValues->processId;
  13. UCHAR szProcessPath[256] = { 0 };
  14. CHAR szProtocalName[256] = { 0 };
  15. RtlZeroMemory(szProcessPath, 256);
  16. // 获取进程路径
  17. for (ULONG i = 0; i < inMetaValues->processPath->size; i++)
  18. {
  19. // 里面是宽字符存储的
  20. szProcessPath[i] = inMetaValues->processPath->data[i];
  21. }
  22. // 获取当前协议类型
  23. ProtocalIdToName(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16, szProtocalName);

拦截浏览器上网: 防火墙的默认规则我们将其改为放行所有classifyOut->actionType = FWP_ACTION_PERMIT;,当我们需要拦截特定进程上网时则只需要判断调用原,如果时特定进程则直接设置拒绝网络访问。

  1. // 设置默认规则 允许连接
  2. classifyOut->actionType = FWP_ACTION_PERMIT;
  3. // 禁止指定进程网络连接
  4. if (NULL != wcsstr((PWCHAR)szProcessPath, L"iexplore.exe"))
  5. {
  6. // 设置拒绝规则 拒绝连接
  7. classifyOut->actionType = FWP_ACTION_BLOCK;
  8. classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  9. classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  10. DbgPrint("[LyShark.com] 拦截IE网络链接请求... \n");
  11. }

当这段驱动程序被加载后,则用户使用IE访问任何页面都将提示无法访问。

拦截指定IP地址: 防火墙的另一个重要功能就是拦截主机自身访问特定网段,此功能只需要增加过滤条件即可实现,如下当用户访问8.141.58.64这个IP地址是则会被拦截,如果监测到用户时Ping请求则也会被拦截。

  1. // 如果对端地址是 8.141.58.64 且对端端口是 443 则拒绝连接
  2. if (strcmp(szRemoteAddress, "8.141.58.64") == 0 && strcmp(szRemotePort, "443") == 0)
  3. {
  4. DbgPrint("拦截网站访问请求 --> %s : %s \n", szRemoteAddress, szRemotePort);
  5. // 设置拒绝规则 拒绝连接
  6. classifyOut->actionType = FWP_ACTION_BLOCK;
  7. classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  8. classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  9. }
  10. else if (strcmp(szRemotePort, "0") == 0)
  11. {
  12. DbgPrint("拦截Ping访问请求 --> %s \n", szRemoteAddress);
  13. // 设置拒绝规则 拒绝连接
  14. classifyOut->actionType = FWP_ACTION_BLOCK;
  15. classifyOut->rights = classifyOut->rights & (~FWPS_RIGHT_ACTION_WRITE);
  16. classifyOut->flags = classifyOut->flags | FWPS_CLASSIFY_OUT_FLAG_ABSORB;
  17. }

当这段驱动程序被加载后,则用户主机无法访问8.141.58.64且无法使用ping命令。

抓取底层数据包: 如果仅仅只是想要输出流经自身主机的数据包,则只需要对特定数据包进行解码即可得到原始数据。

  1. // 输出对端地址字符串 并阻断链接
  2. char szRemoteAddress[256] = { 0 };
  3. char szRemotePort[128] = { 0 };
  4. char szLocalAddress[256] = { 0 };
  5. char szLocalPort[128] = { 0 };
  6. sprintf(szRemoteAddress, "%u.%u.%u.%u", (ulRemoteIp >> 24) & 0xFF, (ulRemoteIp >> 16) & 0xFF, (ulRemoteIp >> 8) & 0xFF, (ulRemoteIp)& 0xFF);
  7. sprintf(szRemotePort, "%d", uRemotePort);
  8. sprintf(szLocalAddress, "%u.%u.%u.%u", (ulLocalIp >> 24) & 0xFF, (ulLocalIp >> 16) & 0xFF, (ulLocalIp >> 8) & 0xFF, (ulLocalIp)& 0xFF);
  9. sprintf(szLocalPort, "%d", uLocalPort);
  10. // 显示
  11. DbgPrint("[LyShark.com] 方向: %d -> 协议类型: %s -> 本端地址: %u.%u.%u.%u:%d -> 对端地址: %u.%u.%u.%u:%d -> IRQL: %d -> 进程ID: %I64d -> 路径: %S \n",
  12. wDirection,
  13. szProtocalName,
  14. (ulLocalIp >> 24) & 0xFF,
  15. (ulLocalIp >> 16) & 0xFF,
  16. (ulLocalIp >> 8) & 0xFF,
  17. (ulLocalIp)& 0xFF,
  18. uLocalPort,
  19. (ulRemoteIp >> 24) & 0xFF,
  20. (ulRemoteIp >> 16) & 0xFF,
  21. (ulRemoteIp >> 8) & 0xFF,
  22. (ulRemoteIp)& 0xFF,
  23. uRemotePort,
  24. kCurrentIrql,
  25. processId,
  26. (PWCHAR)szProcessPath);

当这段驱动程序被加载后,则用户可看到流经本机的所有数据包。

原文链接:https://www.cnblogs.com/LyShark/p/17134954.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号