赞
踩
首先,Npcap 是专为 Windows 开发的一款网络抓包 SDK,该 SDK 提供了被应用程序调用的库文件和系统驱动程序。通过 Npcap,我们可以得到原始(raw)网络数据,即未经过 TCP/IP 协议栈的数据,也就是网卡收到的数据。也就是可直接通过Mac地址发送报文。
1.1安装Npcap,安装过wireshark可直接跳过这一步骤
1.2下载头文件和库文件,下载地址:https://npcap.com/#download
下载SDK,未安装wireshark记得下载installer
安装包包含以下文件,主要使用到Include和Lib
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文件代码。
- // npcap.c
- #ifdef _MSC_VER
- #define _CRT_SECURE_NO_WARNINGS
- #endif
-
- #include <stdlib.h>
- #include <string.h>
-
- #include "common.h"
-
- // 合成 MAC 地址(只能是常量)
- #define MAC(aa, bb, cc, dd, ee, ff) (int8_t[6]) { 0x##aa, 0x##bb, 0x##cc, 0x##dd, 0x##ee, 0x##ff }
-
- #define ETHERNET_TYPE_IPV4 0x0800 // 以太网类型 IPv4
- #define ETHERNET_TYPE_END 0x3412 // 结束包,自定义类型
-
- #ifdef _WIN32
- #include <tchar.h>
- // 加载 Npcap DLL
- int LoadNpcapDlls()
- {
- _TCHAR npcap_dir[512];
- UINT len;
- len = GetSystemDirectory(npcap_dir, 480);
- if (!len) {
- fprintf(stderr, "Error in GetSystemDirectory: %x", GetLastError());
- return 0;
- }
- _tcscat_s(npcap_dir, 512, _T("\\Npcap"));
- if (SetDllDirectory(npcap_dir) == 0) {
- fprintf(stderr, "Error in SetDllDirectory: %x", GetLastError());
- return 0;
- }
- return 1;
- }
- #endif
-
- // 按点分十进制输出 IPv4 地址
- void pretty_print_ipv4(uint8_t ip[4])
- {
- printf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
- }
-
- // 输出 MAC 地址
- void pretty_print_mac(uint8_t mac[6])
- {
- printf("%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
- }
-
-
- //组包
- int make_ethernet_packet(
- uint8_t target_address[6], // 目的 MAC 地址
- uint8_t source_address[6], // 源 MAC 地址
- int16_t ethertype, // 以太网类型(DIX Ethernet II)/帧长(IEEE 802.3)
- uint8_t* payload, // 数据部分
- size_t payload_length, // 数据长度(比特数)
- uint8_t** data, // 返回值:以太网帧。由调用者释放内存。
- size_t* data_length // 返回值:以太网帧长度(字节数))
- {
- // 计算总长度并分配内存
- *data_length = 6 + 6 + 2 + payload_length;
- // 最大帧长 1500-4 字节
- if (*data_length > 1496)
- return -2;
- // 最小帧长 64-4(FCS长度)-6-6-2(头长度)=46 字节
- // 不够要填充额外数据
- if (*data_length < 46)
- {
- int8_t* new_payload = calloc(60, sizeof(int8_t));
- memcpy(new_payload, payload, payload_length);
- // 覆盖掉原始 payload
- payload = new_payload;
- payload_length = 46;
- // 重新计算 data_length
- *data_length = 6 + 6 + 2 + payload_length;
- }
- *data = calloc(*data_length, sizeof(int8_t));
- if (data == NULL)
- return -1;
- // 处理大小端问题
- ethertype = htons(ethertype);
- // 填充头部
- memcpy(*data, target_address, 6);
- memcpy(*data + 6, source_address, 6);
- memcpy(*data + 12, ðertype, sizeof(ethertype));
- // 填充数据
- memcpy(*data + 14, payload, payload_length);
- return 0;
- }
- int network_main(pcap_t* pcap)
- {
- uint8_t payload[56] = { 0x36, 00,0x2e, 00, 00, 00, 00, 00 ,07, 00 ,00 ,00 ,0x43
- ,0x4e ,0x31 ,00, 00 ,00, 00, 00, 00 ,00, 00, 00, 00, 00, 00, 00, 00, 00,
- 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
- 00, 00, 00, 00, 0xf7, 00, 00, 00 }; //命名报文
- uint8_t* data = NULL;
- size_t data_length = 0;
- int ret = make_ethernet_packet(
- MAC(11, 22, 33, 44, 55, 66), // 目的地址,板卡Mac
- MAC(5e, 60, ba, 37, ee, b6), // 源地址,主机Mac
- ETHERNET_TYPE_END, //结束包 0x1234
- payload,
- sizeof(payload) / sizeof(int8_t),
- &data,
- &data_length);
- if (ret != 0)
- {
- printf("创建以太网帧失败");
- return -5;
- }
- if (pcap_sendpacket(pcap, data, (int)data_length) != 0) // 返回值不为 0 表示出错
- printf("发送数据包时出错:%s\n", pcap_geterr(pcap));
- free(data); // 最后发完了记得 free 掉
- return 0;
- }
- int main()
- {
- char error_buffer[PCAP_ERRBUF_SIZE]; // 用于储存错误信息
- // 载入 DLL
- #ifdef _WIN32
- if (!LoadNpcapDlls())
- {
- fprintf(stderr, "无法加载 Npcap。\n");
- return -1;
- }
- #endif
- // 初始化
- if (pcap_init(PCAP_CHAR_ENC_LOCAL, error_buffer) != 0) {
- printf("初始化 pcap 库失败: %s\n", error_buffer);
- return -2;
- }
- // 获取所有适配器并让用户选择
- pcap_if_t* devices;
- char err_buffer[1024]; // 用来储存错误信息
- if (pcap_findalldevs(&devices, err_buffer) == -1)
- {
- printf("pcap_findalldevs 时出错:%s\n", err_buffer);
- exit(1);
- }
- // 打印所有适配器名称(devices 是个链表)
- /*int i = 0;
- for (pcap_if_t* device = devices; device; device = device->next)
- {
- i++;
- printf("[%d] %s | 名称=%s\n", i, device->description, device->name);
- }*/
- //int choice = 0;
- //printf("请选择一个适配器:");
- //scanf("%d", &choice);
- int choice = 6; //默认选择以太网2
- // 找到选中的适配器
- pcap_if_t* device = devices;
- for (int i = 0; i != choice - 1; i++)
- {
- device = device->next;
- }
- // 打开它
- pcap_t* pcap = pcap_open_live(
- device->name, // 适配器名称
- 0,
- 0,
- 1000, // 超时时间,毫秒
- error_buffer
- );
- if (pcap == NULL)
- {
- printf("打开适配器 %s 失败。\n", device->description);
- return -3;
- }
- // 打开之后要释放之前的适配器列表
- pcap_freealldevs(devices);
- // 检查数据链路层协议
- int datalink_type = pcap_datalink(pcap);
- if (datalink_type != DLT_EN10MB) // 10Mb 以上的以太网
- {
- printf("不支持的数据链路层协议。");
- return -4;
- }
-
- int ret = network_main(pcap); //发送配置报文
- pcap_close(pcap);
- system("pause");
- return ret;
- }
以上是c代码,下面是.h文件代码。主要函数如下:
- // npcap.h
- #pragma once
- #include <inttypes.h>
- #include <pcap.h>
-
- // 加载 Npcap DLL,需要在调用任何 pcap 函数之前调用先本函数。
- int LoadNpcapDlls();
- // 按点分十进制输出 IPv4 地址
- void pretty_print_ipv4(uint8_t ip[4]);
- // 输出 MAC 地址
- void pretty_print_mac(uint8_t mac[6]);
- // 程序入口
- int network_main(pcap_t* pcap);
-
按照以上配置可实现自定义发送数据链路层报文。通过wireshark抓取到的以太网口报文如下,前六字节为目的Mac,后六字节为源Mac。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。