当前位置:   article > 正文

C语言通过调用npcap库发送以太网报文_npcap sdk下载地址

npcap sdk下载地址

写在前面:C语言通过调用Npcap库发送以太网报文,可自定义Mac地址以及协议类型。

首先,Npcap 是专为 Windows 开发的一款网络抓包 SDK,该 SDK 提供了被应用程序调用的库文件和系统驱动程序。通过 Npcap,我们可以得到原始(raw)网络数据,即未经过 TCP/IP 协议栈的数据,也就是网卡收到的数据。也就是可直接通过Mac地址发送报文。

1.使用前准备事项

1.1安装Npcap,安装过wireshark可直接跳过这一步骤

1.2下载头文件和库文件,下载地址:https://npcap.com/#download

      下载SDK,未安装wireshark记得下载installer

安装包包含以下文件,主要使用到Include和Lib

2.正式使用

菜单栏项目-属性中进行设置

C/C++ -> 常规,附加包含目录,添加 npcap-sdk-1.13\Include。
连接器 -> 常规,附加库目录,添加 npcap-sdk-1.13\Lib\x64。以上需根据自己补全路径。
链接器 -> 输入,附加依赖项,加上 Ws2_32.lib;wpcap.lib;。
链接器 -> 输入,延迟加载的 DLL,填入 wpcap.dll。

---------------------------------------------------------------------------------------------------------------------

完成以上步骤,准备工作就已完成。
 

本文发送数据包用的是 int pcap_sendpacket(pcap_t *p, const u_char *buf, int size); 这个函数。下面先给出代码工程所需的.c和.h文件代码。

  1. // npcap.c
  2. #ifdef _MSC_VER
  3. #define _CRT_SECURE_NO_WARNINGS
  4. #endif
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include "common.h"
  8. // 合成 MAC 地址(只能是常量)
  9. #define MAC(aa, bb, cc, dd, ee, ff) (int8_t[6]) { 0x##aa, 0x##bb, 0x##cc, 0x##dd, 0x##ee, 0x##ff }
  10. #define ETHERNET_TYPE_IPV4 0x0800 // 以太网类型 IPv4
  11. #define ETHERNET_TYPE_END 0x3412 // 结束包,自定义类型
  12. #ifdef _WIN32
  13. #include <tchar.h>
  14. // 加载 Npcap DLL
  15. int LoadNpcapDlls()
  16. {
  17. _TCHAR npcap_dir[512];
  18. UINT len;
  19. len = GetSystemDirectory(npcap_dir, 480);
  20. if (!len) {
  21. fprintf(stderr, "Error in GetSystemDirectory: %x", GetLastError());
  22. return 0;
  23. }
  24. _tcscat_s(npcap_dir, 512, _T("\\Npcap"));
  25. if (SetDllDirectory(npcap_dir) == 0) {
  26. fprintf(stderr, "Error in SetDllDirectory: %x", GetLastError());
  27. return 0;
  28. }
  29. return 1;
  30. }
  31. #endif
  32. // 按点分十进制输出 IPv4 地址
  33. void pretty_print_ipv4(uint8_t ip[4])
  34. {
  35. printf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
  36. }
  37. // 输出 MAC 地址
  38. void pretty_print_mac(uint8_t mac[6])
  39. {
  40. printf("%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  41. }
  42. //组包
  43. int make_ethernet_packet(
  44. uint8_t target_address[6], // 目的 MAC 地址
  45. uint8_t source_address[6], // 源 MAC 地址
  46. int16_t ethertype, // 以太网类型(DIX Ethernet II)/帧长(IEEE 802.3)
  47. uint8_t* payload, // 数据部分
  48. size_t payload_length, // 数据长度(比特数)
  49. uint8_t** data, // 返回值:以太网帧。由调用者释放内存。
  50. size_t* data_length // 返回值:以太网帧长度(字节数))
  51. {
  52. // 计算总长度并分配内存
  53. *data_length = 6 + 6 + 2 + payload_length;
  54. // 最大帧长 1500-4 字节
  55. if (*data_length > 1496)
  56. return -2;
  57. // 最小帧长 64-4(FCS长度)-6-6-2(头长度)=46 字节
  58. // 不够要填充额外数据
  59. if (*data_length < 46)
  60. {
  61. int8_t* new_payload = calloc(60, sizeof(int8_t));
  62. memcpy(new_payload, payload, payload_length);
  63. // 覆盖掉原始 payload
  64. payload = new_payload;
  65. payload_length = 46;
  66. // 重新计算 data_length
  67. *data_length = 6 + 6 + 2 + payload_length;
  68. }
  69. *data = calloc(*data_length, sizeof(int8_t));
  70. if (data == NULL)
  71. return -1;
  72. // 处理大小端问题
  73. ethertype = htons(ethertype);
  74. // 填充头部
  75. memcpy(*data, target_address, 6);
  76. memcpy(*data + 6, source_address, 6);
  77. memcpy(*data + 12, &ethertype, sizeof(ethertype));
  78. // 填充数据
  79. memcpy(*data + 14, payload, payload_length);
  80. return 0;
  81. }
  82. int network_main(pcap_t* pcap)
  83. {
  84. uint8_t payload[56] = { 0x36, 00,0x2e, 00, 00, 00, 00, 00 ,07, 00 ,00 ,00 ,0x43
  85. ,0x4e ,0x31 ,00, 00 ,00, 00, 00, 00 ,00, 00, 00, 00, 00, 00, 00, 00, 00,
  86. 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
  87. 00, 00, 00, 00, 0xf7, 00, 00, 00 }; //命名报文
  88. uint8_t* data = NULL;
  89. size_t data_length = 0;
  90. int ret = make_ethernet_packet(
  91. MAC(11, 22, 33, 44, 55, 66), // 目的地址,板卡Mac
  92. MAC(5e, 60, ba, 37, ee, b6), // 源地址,主机Mac
  93. ETHERNET_TYPE_END, //结束包 0x1234
  94. payload,
  95. sizeof(payload) / sizeof(int8_t),
  96. &data,
  97. &data_length);
  98. if (ret != 0)
  99. {
  100. printf("创建以太网帧失败");
  101. return -5;
  102. }
  103. if (pcap_sendpacket(pcap, data, (int)data_length) != 0) // 返回值不为 0 表示出错
  104. printf("发送数据包时出错:%s\n", pcap_geterr(pcap));
  105. free(data); // 最后发完了记得 free 掉
  106. return 0;
  107. }
  108. int main()
  109. {
  110. char error_buffer[PCAP_ERRBUF_SIZE]; // 用于储存错误信息
  111. // 载入 DLL
  112. #ifdef _WIN32
  113. if (!LoadNpcapDlls())
  114. {
  115. fprintf(stderr, "无法加载 Npcap。\n");
  116. return -1;
  117. }
  118. #endif
  119. // 初始化
  120. if (pcap_init(PCAP_CHAR_ENC_LOCAL, error_buffer) != 0) {
  121. printf("初始化 pcap 库失败: %s\n", error_buffer);
  122. return -2;
  123. }
  124. // 获取所有适配器并让用户选择
  125. pcap_if_t* devices;
  126. char err_buffer[1024]; // 用来储存错误信息
  127. if (pcap_findalldevs(&devices, err_buffer) == -1)
  128. {
  129. printf("pcap_findalldevs 时出错:%s\n", err_buffer);
  130. exit(1);
  131. }
  132. // 打印所有适配器名称(devices 是个链表)
  133. /*int i = 0;
  134. for (pcap_if_t* device = devices; device; device = device->next)
  135. {
  136. i++;
  137. printf("[%d] %s | 名称=%s\n", i, device->description, device->name);
  138. }*/
  139. //int choice = 0;
  140. //printf("请选择一个适配器:");
  141. //scanf("%d", &choice);
  142. int choice = 6; //默认选择以太网2
  143. // 找到选中的适配器
  144. pcap_if_t* device = devices;
  145. for (int i = 0; i != choice - 1; i++)
  146. {
  147. device = device->next;
  148. }
  149. // 打开它
  150. pcap_t* pcap = pcap_open_live(
  151. device->name, // 适配器名称
  152. 0,
  153. 0,
  154. 1000, // 超时时间,毫秒
  155. error_buffer
  156. );
  157. if (pcap == NULL)
  158. {
  159. printf("打开适配器 %s 失败。\n", device->description);
  160. return -3;
  161. }
  162. // 打开之后要释放之前的适配器列表
  163. pcap_freealldevs(devices);
  164. // 检查数据链路层协议
  165. int datalink_type = pcap_datalink(pcap);
  166. if (datalink_type != DLT_EN10MB) // 10Mb 以上的以太网
  167. {
  168. printf("不支持的数据链路层协议。");
  169. return -4;
  170. }
  171. int ret = network_main(pcap); //发送配置报文
  172. pcap_close(pcap);
  173. system("pause");
  174. return ret;
  175. }

以上是c代码,下面是.h文件代码。主要函数如下:

  1. // npcap.h
  2. #pragma once
  3. #include <inttypes.h>
  4. #include <pcap.h>
  5. // 加载 Npcap DLL,需要在调用任何 pcap 函数之前调用先本函数。
  6. int LoadNpcapDlls();
  7. // 按点分十进制输出 IPv4 地址
  8. void pretty_print_ipv4(uint8_t ip[4]);
  9. // 输出 MAC 地址
  10. void pretty_print_mac(uint8_t mac[6]);
  11. // 程序入口
  12. int network_main(pcap_t* pcap);

按照以上配置可实现自定义发送数据链路层报文。通过wireshark抓取到的以太网口报文如下,前六字节为目的Mac,后六字节为源Mac。

最后,以上为学习中做笔记所用,未有任何商业用途,谢谢XcantloadX分享。欢迎大家一起讨论。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号