npcap 是Nmap自帶的一個數據包處理工具,Nmap底層就是使用這個包進行收發包的,該庫,是可以進行二次開發的,不過使用C語言開發費勁,在進行滲透任務時,還是使用Python構建數據包高效,這東西沒啥意義.
Npcap 開發包解析協議: Npcap 是Nmap項目的網絡包抓取庫在Windows下的版本,其調用接口完全遵循WinPcap規范.
#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中就可以實現監控該網卡的原始數據包,配合下方的解析函數進行各種解析.
#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);
}
解析數據鏈路層
#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地址: %02X:%02X:%02X:%02X:%02X:%02X \t",
ether_src[0], ether_src[1], ether_src[2], ether_src[3], ether_src[4], ether_src[5]);
printf("目標MAC地址: %02X:%02X:%02X:%02X:%02X:%02X \n",
ether_dst[0], ether_dst[1], ether_dst[2], ether_dst[3], ether_dst[4], ether_dst[5]);
}
解析IP層數據包
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("源地址: %15s --> ", inet_ntoa(Src_Addr.sin_addr));
printf("目標地址: %15s --> ", inet_ntoa(Dst_Addr.sin_addr));
printf("校驗和: %5X --> TTL: %4d --> 協議類型: ", 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層數據包
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("源端口: %6d --> 目標端口: %6d --> 窗口大小: %7d --> 標志: (%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層數據包
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("源端口: %5d --> 目標端口: %5d --> 大小: %5d \n", sport, dport,datalen);
}
解析ICMP層數據包
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廣播數據包,這里需要先構建數據包的結構,然后在發送出去.
#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;
}