winpcapとlibtins

パケットキャプチャの手法を調査してみました。

###**winpcapでのsnoop**
オンラインのチュートリアル、ほぼそのまま。簡単簡単＾＾
特定ポートのTCP/IPキャプチャはこんな感じでスレッドまわしときゃOK。

```
typedef struct ip_hdr
{
  unsigned char ip_header_len:4; // 4-bit header length (in 32-bit words) normally=5 (Means 20 Bytes may be 24 also)
  unsigned char ip_version :4; // 4-bit IPv4 version
  unsigned char ip_tos; // IP type of service
  unsigned short ip_total_length; // Total length
  unsigned short ip_id; // Unique identifier
  
  unsigned char ip_frag_offset :5; // Fragment offset field
  
  unsigned char ip_more_fragment :1;
  unsigned char ip_dont_fragment :1;
  unsigned char ip_reserved_zero :1;
  
  unsigned char ip_frag_offset1; //fragment offset
  
  unsigned char ip_ttl; // Time to live
  unsigned char ip_protocol; // Protocol(TCP,UDP etc)
  unsigned short ip_checksum; // IP checksum
  unsigned int ip_srcaddr; // Source address
  unsigned int ip_destaddr; // Source address
}IPV4_HDR;
 
// TCP header
typedef struct tcp_header
{
  unsigned short source_port; // source port
  unsigned short dest_port; // destination port
  unsigned int sequence; // sequence number - 32 bits
  unsigned int acknowledge; // acknowledgement number - 32 bits
  
  unsigned char ns :1; //Nonce Sum Flag Added in RFC 3540.
  unsigned char reserved_part1:3; //according to rfc
  unsigned char data_offset:4; /*The number of 32-bit words in the TCP header.
                                 This indicates where the data begins.
                                 The length of the TCP header is always a multiple
                                 of 32 bits.*/
  
  unsigned char fin :1; //Finish Flag
  unsigned char syn :1; //Synchronise Flag
  unsigned char rst :1; //Reset Flag
  unsigned char psh :1; //Push Flag
  unsigned char ack :1; //Acknowledgement Flag
  unsigned char urg :1; //Urgent Flag
  unsigned char ecn :1; //ECN-Echo Flag
  unsigned char cwr :1; //Congestion Window Reduced Flag
  
  unsigned short window; // window
  unsigned short checksum; // checksum
  unsigned short urgent_pointer; // urgent pointer
}TCP_HDR;

#include <winsock2.h>
#pragma comment(lib , "ws2_32.lib")
#include <windows.h>
#define HAVE_REMOTE
#include <pcap/pcap.h>
#pragma comment(lib , "wpcap.lib")

DWORD WINAPI thrProcCap(LPVOID lpparam)
{
  pcap_t *pcap;
  unsigned char pcap_errbuf[PCAP_ERRBUF_SIZE];

  /* Open the device */
  if((pcap=pcap_open(ether_devname,
                     65536 /*snaplen*/,
                     PCAP_OPENFLAG_PROMISCUOUS /*flags*/,
                     10 /*read timeout*/,
                     NULL /* remote authentication */,
                     (char *)pcap_errbuf))==NULL){
    return (DWORD)-1;
  }

  int pcap_res;
  struct pcap_pkthdr *header;
  const unsigned char *pd;

  while((pcap_res=pcap_next_ex(pcap, &header, &pd))>=0){

    if(WaitForSingleObject(hEventExit, 0)==WAIT_OBJECT_0){
      CloseHandle(hEventExit);
      pcap_close(pcap);
      return 0;
    };

    if(pcap_res==0) continue; // Timeout elapsed

    if(pcap_res==-1){
      pcap_close(pcap);
      return (DWORD)-1;
    }

    unsigned char *pkt_data=(unsigned char *)pd;
    int pkt_size=header->caplen;

    ETHER_HDR *ethhdr=(ETHER_HDR *)pkt_data;
    int etherhdrlen=sizeof(ETHER_HDR);

    if(ntohs(ethhdr->type)==0x0800){

      IPV4_HDR *iphdr=(IPV4_HDR *)(pkt_data+etherhdrlen);
      int iphdrlen=iphdr->ip_header_len*4;

      if(iphdr->ip_protocol==6){
        // TCP/IP
        TCP_HDR *tcpheader=(TCP_HDR*)(pkt_data+etherhdrlen+iphdrlen);
        int tcphdrlen=tcpheader->data_offset*4;
        
        unsigned char *data=(pkt_data+etherhdrlen+iphdrlen+tcphdrlen);
        int datalen=(pkt_size-etherhdrlen-iphdrlen-tcphdrlen);
        
        // port check
        int src_port=ntohs(tcpheader->source_port);
        int dst_port=ntohs(tcpheader->dest_port);
        
        if(dst_port==SelectedPort){
          ProcTCPSendPacket(data, datalen, tcpheader);
        }else if(src_port==SelectedPort){
          ProcTCPRecvPacket(data, datalen, tcpheader);
        }
      }
    }

  }
  return 0;
}
```

ただし、お遊びでやる分にはそんなに気にしなくていいのですが、Sequence, Acknowledge, 各種フラグを元にして、パケットの組み立てを自分でやんなくちゃいけないのがめんどくさいですね。


###**libtinsでのTCPパケットcrafting**
winpcapのpcap_sendpacket()により、オープンしたデバイスに任意のデータを送りつけることが簡単にできるようです。(まだ実際にテストしてませんが＾＾；；；)
しかし、sendするパケットを自前で用意しなければならない。。。TCPヘッダ、IPヘッダにはチェックサムをつける必要があるので、自前で用意して検証するのはちょっと面倒です。

そこで、ちょっとググッてみていくつか覗いてみたところ、libtinsというのが使い勝手がよさそうなので使ってみました。
(余談ですが、wikipedia英語版にはちゃんとpacket craftingの項目があって、libtins含めて、知られているライブラリが載っているんですよね。日本語だけだと探し出すのに苦労します。。。)

これも実際にはまだ試していないんですが、こんな簡単な記述でポンッとデータを作ってpcap_sendpacket()に放り込めます。

```
#include <tins/tins.h>
#pragma comment(lib , "iphlpapi.lib")
#pragma comment(lib , "tins.lib")

{
  using namespace Tins;

  unsigned char sendbuf[6]={0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  IP pkt=IP("XX.XX.XX.XX", "YY.YY.YY.YY")/TCP(dst_port, src_port)/RawPDU(sendbuf, 6);

  PDU::serialization_type buf=pkt.serialize();
  int buf_size=static_cast<int>(buf.size());
  pcap_sendpacket(pcap, (unsigned char *)&buf[0], buf_size);
}

```