npcap 是Nmap自带的一个数据包处理工具,Nmap底层就是使用这个包进行收发包的,该库,是可以进行二次开发的,不过使用C语言开发费劲,在进行渗透任务时,还是使用Python构建数据包高效,这东西没啥意义.
Npcap 开发包解析协议: Npcap 是Nmap项目的网络包抓取库在Windows下的版本,其调用接口完全遵循WinPcap规范.
代码语言:javascript复制#include <WinSock2.h>
#include <Windows.h>
#include <pcap.h>
#pragma comment(lib, "packet.lib")
#pragma comment(lib, "wpcap.lib")
int enumAdapters()
{
pcap_if_t *allAdapters; // 所有网卡设备保存
pcap_if_t *ptr; // 用于遍历的指针
int index = 0;
char errbuf[PCAP_ERRBUF_SIZE];
/* 获取本地机器设备列表 */
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &allAdapters, errbuf) != -1)
{
/* 打印网卡信息列表 */
for (ptr = allAdapters; ptr != NULL; ptr = ptr->next)
{
// printf("网卡地址: %x 网卡ID: %s n", ptr->addresses, ptr->name);
index;
if (ptr->description)
printf("ID: %d --> Name: %s n", index,ptr->description);
}
}
/* 不再需要设备列表了,释放它 */
pcap_freealldevs(allAdapters);
return index;
}
int main(int argc,char *argv[])
{
int network = enumAdapters();
printf("网卡数量: %d n", network);
system("Pause");
}
接着我们通过获取到的的网卡对应的值,填入MonitorAdapter中就可以实现监控该网卡的原始数据包,配合下方的解析函数进行各种解析.
代码语言:javascript复制#include <WinSock2.h>
#include <Windows.h>
#include <pcap.h>
#pragma comment(lib, "packet.lib")
#pragma comment(lib, "wpcap.lib")
void MonitorAdapter(int nChoose)
{
pcap_if_t *adapters;
char errbuf[PCAP_ERRBUF_SIZE];
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &adapters, errbuf) != -1)
{
// 找到指定的网卡
for (int x = 0; x < nChoose - 1; x)
adapters = adapters->next;
char errorBuf[PCAP_ERRBUF_SIZE];
// PCAP_OPENFLAG_PROMISCUOUS = 网卡设置为混杂模式
// 1000 => 1000毫秒如果读不到数据直接返回超时
pcap_t * handle = pcap_open(adapters->name, 65534, 1, PCAP_OPENFLAG_PROMISCUOUS, 0, 0);
if (adapters == NULL)
return;
// printf("开始侦听: % n", adapters->description);
pcap_pkthdr *Packet_Header; // 数据包头
const u_char * Packet_Data; // 数据本身
int retValue;
while ((retValue = pcap_next_ex(handle, &Packet_Header, &Packet_Data)) >= 0)
{
if (retValue == 0)
continue;
// printf("侦听长度: %d n", Packet_Header->len);
PrintEtherHeader(Packet_Data);
}
}
}
int main(int argc,char *argv[])
{
MonitorAdapter(3);
}
解析数据链路层
代码语言:javascript复制#define hcons(A) (((WORD)(A)&0xFF00)>>8) | (((WORD)(A)&0x00FF)<<8)
// 输出 数据链路层
void PrintEtherHeader(const u_char * packetData)
{
typedef struct ether_header {
u_char ether_dhost[6]; // 目标地址
u_char ether_shost[6]; // 源地址
u_short ether_type; // 以太网类型
} ether_header;
struct ether_header * eth_protocol;
eth_protocol = (struct ether_header *)packetData;
u_short ether_type = ntohs(eth_protocol->ether_type); // 以太网类型
u_char *ether_src = eth_protocol->ether_shost; // 以太网原始MAC地址
u_char *ether_dst = eth_protocol->ether_dhost; // 以太网目标MAC地址
printf("类型: 0x%x t", ether_type);
printf("原MAC地址: X:X:X:X:X:X t",
ether_src[0], ether_src[1], ether_src[2], ether_src[3], ether_src[4], ether_src[5]);
printf("目标MAC地址: X:X:X:X:X:X n",
ether_dst[0], ether_dst[1], ether_dst[2], ether_dst[3], ether_dst[4], ether_dst[5]);
}
解析IP层数据包
代码语言:javascript复制void PrintIPHeader(const u_char * packetData)
{
typedef struct ip_header
{
char version : 4;
char headerlength : 4;
char cTOS;
unsigned short totla_length;
unsigned short identification;
unsigned short flags_offset;
char time_to_live;
char Protocol;
unsigned short check_sum;
unsigned int SrcAddr;
unsigned int DstAddr;
}ip_header;
struct ip_header *ip_protocol;
// 14 跳过数据链路层
ip_protocol = (struct ip_header *)(packetData 14);
SOCKADDR_IN Src_Addr, Dst_Addr = { 0 };
u_short check_sum = ntohs(ip_protocol->check_sum);
int ttl = ip_protocol->time_to_live;
int proto = ip_protocol->Protocol;
Src_Addr.sin_addr.s_addr = ip_protocol->SrcAddr;
Dst_Addr.sin_addr.s_addr = ip_protocol->DstAddr;
printf("源地址: s --> ", inet_ntoa(Src_Addr.sin_addr));
printf("目标地址: s --> ", inet_ntoa(Dst_Addr.sin_addr));
printf("校验和: %5X --> TTL: M --> 协议类型: ", check_sum, ttl);
switch (ip_protocol->Protocol)
{
case 1: printf("ICMP n"); break;
case 2: printf("IGMP n"); break;
case 6: printf("TCP n"); break;
case 17: printf("UDP n"); break;
case 89: printf("OSPF n"); break;
default: printf("None n"); break;
}
}
解析TCP层数据包
代码语言:javascript复制void PrintTCPHeader(const unsigned char * packetData)
{
typedef struct tcp_header
{
short SourPort; // 源端口号16bit
short DestPort; // 目的端口号16bit
unsigned int SequNum; // 序列号32bit
unsigned int AcknowledgeNum; // 确认号32bit
unsigned char reserved : 4, offset : 4; // 预留偏移
unsigned char flags; // 标志
short WindowSize; // 窗口大小16bit
short CheckSum; // 检验和16bit
short surgentPointer; // 紧急数据偏移量16bit
}tcp_header;
struct tcp_header *tcp_protocol;
// 14 跳过数据链路层 20 跳过IP层
tcp_protocol = (struct tcp_header *)(packetData 14 20);
u_short sport = ntohs(tcp_protocol->SourPort);
u_short dport = ntohs(tcp_protocol->DestPort);
int window = tcp_protocol->WindowSize;
int flags = tcp_protocol->flags;
printf("源端口: m --> 目标端口: m --> 窗口大小: } --> 标志: (%d)",
sport, dport, window, flags);
if (flags & 0x08) printf("PSH 数据传输n");
else if (flags & 0x10) printf("ACK 响应n");
else if (flags & 0x02) printf("SYN 建立连接n");
else if (flags & 0x20) printf("URG n");
else if (flags & 0x01) printf("FIN 关闭连接n");
else if (flags & 0x04) printf("RST 连接重置n");
else printf("None 未知n");
}
解析UDP层数据包
代码语言:javascript复制void PrintUDPHeader(const unsigned char * packetData)
{
typedef struct udp_header {
uint32_t sport; // 源端口
uint32_t dport; // 目标端口
uint8_t zero; // 保留位
uint8_t proto; // 协议标识
uint16_t datalen; // UDP数据长度
}udp_header;
struct udp_header *udp_protocol;
// 14 跳过数据链路层 20 跳过IP层
udp_protocol = (struct udp_header *)(packetData 14 20);
u_short sport = ntohs(udp_protocol->sport);
u_short dport = ntohs(udp_protocol->dport);
u_short datalen = ntohs(udp_protocol->datalen);
printf("源端口: ] --> 目标端口: ] --> 大小: ] n", sport, dport,datalen);
}
解析ICMP层数据包
代码语言:javascript复制void PrintICMPHeader(const unsigned char * packetData)
{
typedef struct icmp_header {
uint8_t type; // ICMP类型
uint8_t code; // 代码
uint16_t checksum; // 校验和
uint16_t identification; // 标识
uint16_t sequence; // 序列号
uint32_t init_time; // 发起时间戳
uint16_t recv_time; // 接受时间戳
uint16_t send_time; // 传输时间戳
}icmp_header;
struct icmp_header *icmp_protocol;
// 14 跳过数据链路层 20 跳过IP层
icmp_protocol = (struct icmp_header *)(packetData 14 20);
int type = icmp_protocol->type;
int init_time = icmp_protocol->init_time;
int send_time = icmp_protocol->send_time;
int recv_time = icmp_protocol->recv_time;
if (type == 8)
{
printf("发起时间戳: %d --> 传输时间戳: %d --> 接收时间戳: %d 方向: ",
init_time, send_time, recv_time);
switch (type)
{
case 0: printf("回显应答报文 n"); break;
case 8: printf("回显请求报文 n"); break;
default:break;
}
}
}
Npcap发送ARP数据包: 通过使用Npcap实现发送一个ARP广播数据包,这里需要先构建数据包的结构,然后在发送出去.
代码语言:javascript复制#include <stdio.h>
#include <winsock2.h>
#include <Windows.h>
#include <pcap.h>
#pragma comment(lib, "packet.lib")
#pragma comment(lib, "wpcap.lib")
#pragma comment(lib,"WS2_32.lib")
#define ETH_ARP 0x0806 // 以太网帧类型表示后面数据的类型,对于ARP请求或应答来说,该字段的值为x0806
#define ARP_HARDWARE 1 // 硬件类型字段值为表示以太网地址
#define ETH_IP 0x0800 // 协议类型字段表示要映射的协议地址类型值为x0800表示IP地址
#define ARP_REQUEST 1 // ARP请求
#define ARP_RESPONSE 2 // ARP应答
//14字节以太网首部
struct EthernetHeader
{
u_char DestMAC[6]; // 目的MAC地址6字节
u_char SourMAC[6]; // 源MAC地址 6字节
u_short EthType; // 上一层协议类型,如0x0800代表上一层是IP协议,0x0806为arp 2字节
};
//28字节ARP帧结构
struct ArpHeader
{
unsigned short hdType; // 硬件类型
unsigned short proType; // 协议类型
unsigned char hdSize; // 硬件地址长度
unsigned char proSize; // 协议地址长度
unsigned short op; // 操作类型,ARP请求(1),ARP应答(2),RARP请求(3),RARP应答(4)。
u_char smac[6]; // 源MAC地址
u_char sip[4]; // 源IP地址
u_char dmac[6]; // 目的MAC地址
u_char dip[4]; // 目的IP地址
};
//定义整个arp报文包,总长度42字节
struct ArpPacket {
EthernetHeader ed;
ArpHeader ah;
};
// 获取到指定网卡的句柄
pcap_t * OpenPcap(int nChoose)
{
pcap_t *pcap_handle; //打开网络适配器,捕捉实例,是pcap_open返回的对象
pcap_if_t *alldevs;
char errbuf[PCAP_ERRBUF_SIZE]; //错误缓冲区,大小为256
// 获取到所有设备列表
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
exit(0);
// 找到指定的网卡设备
for (int x = 0; x < nChoose - 1; x)
alldevs = alldevs->next;
if ((pcap_handle = pcap_open(alldevs->name, // 设备名
65536, // 每个包长度
PCAP_OPENFLAG_PROMISCUOUS, // 混杂模式
1000, // 读取超时时间
NULL, // 远程机器验证
errbuf // 错误缓冲池
)) == NULL)
{
pcap_freealldevs(alldevs);
exit(0);
}
return pcap_handle;
}
int main(int argc, char *argv[])
{
pcap_t *handle; // 打开网络适配器
EthernetHeader eh; // 定义以太网包头
ArpHeader ah; // 定义ARP包头
unsigned char sendbuf[42]; // arp包结构大小42个字节
unsigned char src_mac[6] = { 0xaa, 0xaa, 0xaa, 0xaa, 0xff, 0xff };
unsigned char src_ip[4] = { 0x01, 0x02, 0x03, 0x04 };
handle = OpenPcap(3); // 拿到第三个网卡的句柄
// 开始填充ARP包
memset(eh.DestMAC, 0xff, 6); // 以太网首部目的MAC地址,全为广播地址
memcpy(eh.SourMAC, src_mac, 6); // 以太网首部源MAC地址
memcpy(ah.smac, src_mac, 6); // ARP字段源MAC地址
memset(ah.dmac, 0xff, 6); // ARP字段目的MAC地址
memcpy(ah.sip, src_ip, 4); // ARP字段源IP地址
memset(ah.dip, 0x05, 4); // ARP字段目的IP地址
// 赋值MAC地址
eh.EthType = htons(ETH_ARP); //htons:将主机的无符号短整形数转换成网络字节顺序
ah.hdType = htons(ARP_HARDWARE);
ah.proType = htons(ETH_IP);
ah.hdSize = 6;
ah.proSize = 4;
ah.op = htons(ARP_REQUEST);
// 构造一个ARP请求
memset(sendbuf, 0, sizeof(sendbuf)); // ARP清零
memcpy(sendbuf, &eh, sizeof(eh)); // 首先把eh以太网结构填充上
memcpy(sendbuf sizeof(eh), &ah, sizeof(ah)); // 接着在eh后面填充arp结构
// 发送数据包
if (pcap_sendpacket(handle, sendbuf, 42) == 0)
{
printf("发送ARP数据包成功! n");
}
system("pause");
return 0;
}