각각의 함수를 활용하면 패킷을 로우레벨에서 분석할수 있다. 물론 다른 함수들도 사용되긴 하지만 주로 사용되는 함수는 4개라고 보면 될것같다.
함수 레퍼런스 : http://www.winpcap.org/docs/docs_40_2/html/group__wpcapfunc.html#gae6abe06e15c87b803f69773822beca8
1. TCP 패킷 감청 프로그램 만들기
1). pcap_open_live
현재 네트워크에서 사용하는 네트워크 디바이스의 패킷을 캡처하는 함수이다. 아래는 함수에대한 프로토타입이다. 첫번째는 오픈한 네트워크 어뎁터에 대한 장치드라이버에 대한 이름이고, 두번째 snaplen은 캡처할 패킷의 크기이다. 세번째가 중요한데 캡처 모드인데 보통 1 (모든 패킷을 캡처)을 준다. to_ms는 타임아웃 설정 delay이고, ebuf는 캡처에 실패했을때 에러메시지를 저장해주는 버퍼이다. 성공시 pcap_t* 를 리턴해주고 실패시 NULL을 리턴한다.
- pcap_t* pcap_open_live(const char *device,
- int snaplen,
- int promisc,
- int to_ms,
- char * ebuf)
2). pcap_compile
캡처한 패킷에 대한 구조체를 인자로 받아서 필터룰을 적용해준다. 매개변수로 앞서 설명한 pcap_open_live 에서 리턴한 pcap_t* 구조체를 매개변수로 받는다. 여기서 bpf_program *fp는 날아댕기는 패킷에대한 필터를 만들어주고 이걸 기반으로 패킷을 잡게된다. 3번째 인자 char *str은 필터에 대한 룰을 정해주는데 룰에대한 문자열 정의는 아래와 같이 정해줬다.
#define FILTER_RULE "host 165.246.12.215 and port 7778" |
룰에 대한 정의에 대한 설명은 http://www.winpcap.org/docs/docs_40_2/html/group__language.html 을 참고해 주기바란다.optimize 옵션은 보통 1을 주고. 마지막 인자인 netmask는 ipv4에서 broadcast를 감청할때 쓰일수있는 인자이다. 실패시 -1 을 리턴
- int pcap_compile(pcap_t *p,
- struct bpf_program *fp,
- char *str,
- int optimize,
- bpf_u_int32 netmask
- )
3). pcap_setfilter
컴파일한 필터를 실제 패킷 핸들에 적용해주는 함수이다. 앞서 설명한 pcap_compile 에서 나온 인자들 2개를 넣어주면 된다.
- int pcap_setfilter(pcap_t *p,
- struct bpf_program *fp
- )
4). pcap_next_ex
마지막으로 캡처한 패킷 데이터를 실제 가져오는 함수이다. 실제는 packet_handler라는 콜백 함수가 데이터들을 채워주는데. 이중 포인터를 인자로 받는다.
- int pcap_next_ex(pcap_t *p,
- struct pcap_pkthdr ** pkt_header,
- const u_char ** pkt_data
- )
실제는 사용하는 방식은 아래와 같다.
- int res;
- struct pcap_pkthdr *header;
- const unsigned char *pkt_data;
- while((res=pcap_next_ex(fp, &header,&pkt_data))>=0){
- if (res==0) continue;
- /* blah~~ */
- }
- #include "pcap.h"
- #include <stdio.h>
- #include <winsock2.h>
- #pragma comment (lib, "wpcap.lib")
- #pragma comment (lib, "ws2_32.lib" )
- #define FILTER_RULE "host 165.246.12.215 and port 7778"
- struct ether_addr
- {
- unsigned char ether_addr_octet[6];
- };
- struct ether_header
- {
- struct ether_addr ether_dhost;
- struct ether_addr ether_shost;
- unsigned short ether_type;
- };
- struct ip_header
- {
- unsigned char ip_header_len:4;
- unsigned char ip_version:4;
- unsigned char ip_tos;
- unsigned short ip_total_length;
- unsigned short ip_id;
- unsigned char ip_frag_offset:5;
- unsigned char ip_more_fragment:1;
- unsigned char ip_dont_fragment:1;
- unsigned char ip_reserved_zero:1;
- unsigned char ip_frag_offset1;
- unsigned char ip_ttl;
- unsigned char ip_protocol;
- unsigned short ip_checksum;
- struct in_addr ip_srcaddr;
- struct in_addr ip_destaddr;
- };
- struct tcp_header
- {
- unsigned short source_port;
- unsigned short dest_port;
- unsigned int sequence;
- unsigned int acknowledge;
- unsigned char ns:1;
- unsigned char reserved_part1:3;
- unsigned char data_offset:4;
- unsigned char fin:1;
- unsigned char syn:1;
- unsigned char rst:1;
- unsigned char psh:1;
- unsigned char ack:1;
- unsigned char urg:1;
- unsigned char ecn:1;
- unsigned char cwr:1;
- unsigned short window;
- unsigned short checksum;
- unsigned short urgent_pointer;
- };
- void print_ether_header(const unsigned char *data);
- int print_ip_header(const unsigned char *data);
- int print_tcp_header(const unsigned char *data);
- void print_data(const unsigned char *data);
- int main(){
- pcap_if_t *alldevs=NULL;
- char errbuf[PCAP_ERRBUF_SIZE];
- int offset=0;
- // find all network adapters
- if (pcap_findalldevs(&alldevs, errbuf)==-1){
- printf("dev find failed\n");
- return -1;
- }
- if (alldevs==NULL){
- printf("no devs found\n");
- return -1;
- }
- // print them
- pcap_if_t *d; int i;
- for(d=alldevs,i=0; d!=NULL; d=d->next){
- printf("%d-th dev: %s ", ++i, d->name);
- if (d->description)
- printf(" (%s)\n", d->description);
- else
- printf(" (No description available)\n");
- }
- int inum;
- printf("enter the interface number: ");
- scanf("%d", &inum);
- for(d=alldevs, i=0; i<inum-1; d=d->next, i++); // jump to the i-th dev
- // open
- pcap_t *fp;
- if ((fp = pcap_open_live(d->name, // name of the device
- 65536, // capture size
- 1, // promiscuous mode
- 20, // read timeout
- errbuf
- ))==NULL){
- printf("pcap open failed\n");
- pcap_freealldevs(alldevs);
- return -1;
- }
- printf("pcap open successful\n");
- struct bpf_program fcode;
- if (pcap_compile(fp, // pcap handle
- &fcode, // compiled rule
- FILTER_RULE, // filter rule
- 1, // optimize
- NULL) < 0){
- printf("pcap compile failed\n");
- pcap_freealldevs(alldevs);
- return -1;
- }
- if (pcap_setfilter(fp, &fcode) <0 ){
- printf("pcap compile failed\n");
- pcap_freealldevs(alldevs);
- return -1;
- }
- pcap_freealldevs(alldevs); // we don't need this anymore
- struct pcap_pkthdr *header;
- const unsigned char *pkt_data;
- int res;
- while((res=pcap_next_ex(fp, &header,&pkt_data))>=0){
- if (res==0) continue;
- print_ether_header(pkt_data);
- pkt_data = pkt_data + 14; // raw_pkt_data의 14번지까지 이더넷
- offset = print_ip_header(pkt_data);
- pkt_data = pkt_data + offset; // ip_header의 길이만큼 오프셋
- offset = print_tcp_header(pkt_data);
- pkt_data = pkt_data + offset; //print_tcp_header *4 데이터 위치로 오프셋
- print_data(pkt_data);
- }
- return 0;
- }
- void print_ether_header(const unsigned char *data)
- {
- struct ether_header *eh; // 이더넷 헤더 구조체
- unsigned short ether_type;
- eh = (struct ether_header *)data; // 받아온 로우 데이터를 이더넷 헤더구조체 형태로 사용
- ether_type=ntohs(eh->ether_type); // 숫자는 네트워크 바이트 순서에서 호스트 바이트 순서로 바꿔야함
- if (ether_type!=0x0800)
- {
- printf("ether type wrong\n");
- return ;
- }
- // 이더넷 헤더 출력
- printf("\n============ETHERNET HEADER==========\n");
- printf("Dst MAC Addr [%02x:%02x:%02x:%02x:%02x:%02x]\n", // 6 byte for dest
- eh->ether_dhost.ether_addr_octet[0],
- eh->ether_dhost.ether_addr_octet[1],
- eh->ether_dhost.ether_addr_octet[2],
- eh->ether_dhost.ether_addr_octet[3],
- eh->ether_dhost.ether_addr_octet[4],
- eh->ether_dhost.ether_addr_octet[5]);
- printf("Src MAC Addr [%02x:%02x:%02x:%02x:%02x:%02x]\n", // 6 byte for src
- eh->ether_shost.ether_addr_octet[0],
- eh->ether_shost.ether_addr_octet[1],
- eh->ether_shost.ether_addr_octet[2],
- eh->ether_shost.ether_addr_octet[3],
- eh->ether_shost.ether_addr_octet[4],
- eh->ether_shost.ether_addr_octet[5]);
- }
- int print_ip_header(const unsigned char *data)
- {
- struct ip_header *ih;
- ih = (struct ip_header *)data; // 마찬가지로 ip_header의 구조체 형태로 변환
- printf("\n============IP HEADER============\n");
- printf("IPv%d ver \n", ih->ip_version);
- // Total packet length (Headers + data)
- printf("Packet Length : %d\n", ntohs(ih->ip_total_length)+14);
- printf("TTL : %d\n", ih->ip_ttl);
- if(ih->ip_protocol == 0x06)
- {
- printf("Protocol : TCP\n");
- }
- printf("Src IP Addr : %s\n", inet_ntoa(ih->ip_srcaddr) );
- printf("Dst IP Addr : %s\n", inet_ntoa(ih->ip_destaddr) );
- // return to ip header size
- return ih->ip_header_len*4;
- }
- int print_tcp_header(const unsigned char *data)
- {
- struct tcp_header *th;
- th = (struct tcp_header *)data;
- printf("\n============TCP HEADER============\n");
- printf("Src Port Num : %d\n", ntohs(th->source_port) );
- printf("Dest Port Num : %d\n", ntohs(th->dest_port) );
- printf("Flag :");
- if(ntohs(th->cwr))
- {
- printf(" CWR ");
- }
- if(ntohs(th->ecn))
- {
- printf(" ENC ");
- }
- if(ntohs(th->urg))
- {
- printf(" URG ");
- }
- if(ntohs(th->ack))
- {
- printf(" ACK ");
- }
- if(ntohs(th->psh))
- {
- printf(" PUSH ");
- }
- if(ntohs(th->rst))
- {
- printf(" RST ");
- }
- if(ntohs(th->syn))
- {
- printf(" SYN ");
- }
- if(ntohs(th->fin))
- {
- printf(" FIN ");
- }
- printf("\n");
- // return to tcp header size
- return th->data_offset*4;
- }
- void print_data(const unsigned char *data)
- {
- printf("\n============DATA============\n");
- printf("%s\n", data);
- }
2. TCP 패킷 분석하기
위의 화면은 클라이언트가 서버에 접속 요청을 했을 때 날아간 패킷이다.
주요 패킷 정보를 판별할 수 있고 SYN 플래그를 통해 접속 요청을 확인할 수있다.
- ACK/SYN
위의 화면은 서버로부터 접속 승낙을 받았을때 패킷이다.
- ACK
클라이언트도 ACK를 보냈다. 여기까지의 과정이 Three way handshake 이다.
이를 통해 connect가 성공적으로 이루어졌음을 확인할수 있다.
2) 데이터 전송
-ACK/PUSH
클라이언트에서 데이터를 보낸 패킷 내용
3) 접속 종료할떄 과정
- ACK/FIN
데이터를 보내고 클라이언트에서 소켓을 닫게 된다. ACK 플래그와 함께 FIN 플래그를 클라이언트가 보내준다.
(클라이언트가 접속을 끊으므로 FIN을 날렸다.)
- ACK
서버도 FIN 플래그에 동의한다고 ACK를 보내준다.
- ACK/FIN
이제는 서버도 FIN을 날려줘서 연결을 끊자고 패킷을 보냄
- ACK
마지막으로 클라이언트도 ACK를 날리고 연결을 끊는다.
* 관련 링크참조
'IT > WinpcapProgramming' 카테고리의 다른 글
TCP/IP IP 패킷 체크섬(Checksum) C언어로 구현하기 (1) | 2015.04.25 |
---|---|
windump를 사용하여 TCP/IP 패킷 분석하기 (0) | 2015.04.03 |
windump 사용법 (0) | 2015.04.03 |
TCP,IP,Ethernet 헤더 구조체 (0) | 2015.03.31 |
WinPcap 개발 환경 구축하기 (2) | 2015.03.27 |