天天看點

C/C++ Npcap包實作資料嗅探

npcap 是Nmap自帶的一個資料包處理工具,Nmap底層就是使用這個包進行收發包的,該庫,是可以進行二次開發的,不過使用C語言開發費勁,在進行滲透任務時,還是使用Python建構資料包高效,這東西沒啥意義.

Npcap 開發包解析協定: Npcap 是Nmap項目的網絡包抓取庫在Windows下的版本,其調用接口完全遵循WinPcap規範.

#include 
#include 
#include 


#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 
#include 
#include 


#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 
#include 
#include 
#include 


#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;
}