pcap 이란?
간단히 말해 말그대로 packet capture 를 뜻한다.
윈도우에서는 Winpcap을, 유닉스 계열애서는 libpcap을 이용하여 네트워크 상의 패킷을 캡쳐해 분석할 수 있다. 여기서 짚고 넘어갈점은 "catch" 가 아니라 "capture" 라는 것이다. 패킷을 잡아서 어떻게 할 수 있다는게 아니라 지나가는것을 들여다 보기만 한다.
목적.
pcap 프로그래밍을 하면서 가장 큰 목적은 패킷을 분석할 줄 알고, 패킷 헤더의 구조를 이해하는 것이라고 생각한다.
(헤더구조를 모르겠다면 https://velys-log.tistory.com/4 참고)
개발환경.
우분투, Qt
pcap_test.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
#include <stdio.h> #include <stdlib.h> #include <pcap.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h>
#define BUFSIZE 1024
typedef struct EthernetHeader{ unsigned char DesMac[6]; unsigned char SrcMac[6]; unsigned short Type; }EthernetH; typedef struct IPHeader{ unsigned char Version : 4; unsigned char IHL : 4; unsigned char TOS; u_short TotalLen; unsigned short Identifi; unsigned char Flagsx : 1; unsigned char FlagsD : 1; unsigned char FlagsM : 1; unsigned int FO : 13; unsigned char TTL; unsigned char Protocal; unsigned short HeaderCheck; struct in_addr SrcAdd; struct in_addr DstAdd; }IPH; typedef struct TCPHeader{ unsigned short SrcPort; unsigned short DstPort; unsigned int SN; unsigned int AN; unsigned char Offset : 4; unsigned char Reserved : 4; unsigned char FlagsC : 1; unsigned char FlagsE : 1; unsigned char FlagsU : 1; unsigned char FlagsA : 1; unsigned char FlagsP : 1; unsigned char FlagsR : 1; unsigned char FlagsS : 1; unsigned char FlagsF : 1; unsigned short Window; unsigned short Check; unsigned short UP; }TCPH; typedef struct HttpH { uint16_t HTP[16]; }HttpH;
void PrintEthernetHeader(const u_char *packet); void PrintIPHeader(const u_char *packet); void PrintTCPHeader(const u_char *packet); void PrintHttpHeader(const uint8_t *packet);
void help(){ printf("Write Interface Name\n"); printf("Sample : pcap_test ens33\n"); }
int main(int argc, char* argv[]) { if (argc != 2){ help(); exit(1); } struct pcap_pkthdr* header; const u_char* packet; char* dev = argv[1]; char errbuf[PCAP_ERRBUF_SIZE]; IPH *tlen; u_int lengh; pcap_t* handle = pcap_open_live(dev, BUFSIZE, 1, 1000, errbuf); if (handle == NULL){ printf("%s : %s \n", dev, errbuf); exit(1); }
while(1){
int res = pcap_next_ex(handle, &header, &packet); if (res == 0) continue; if (res == -1 || res == -2) exit(1); PrintEthernetHeader(packet); packet += 14; PrintIPHeader(packet); tlen = (IPH *)packet; lengh = htons(tlen->TotalLen) - (uint16_t)(tlen->IHL)*4; packet +=(uint16_t)(tlen->IHL)*4; PrintTCPHeader(packet); packet += ( u_char)lengh; PrintHttpHeader(packet); }
pcap_close(handle); return 0; }
void PrintEthernetHeader(const u_char *packet){ EthernetH *eh; eh = (EthernetH *)packet; printf("\n======== Ethernet Header ========\n"); printf("Dst Mac %02x:%02x:%02x:%02x:%02x:%02x \n",eh -> DesMac[0],eh -> DesMac[1],eh -> DesMac[2],eh -> DesMac[3],eh -> DesMac[4],eh -> DesMac[5]); printf("Src Mac %02x:%02x:%02x:%02x:%02x:%02x \n",eh -> SrcMac[0],eh -> SrcMac[1],eh -> SrcMac[2],eh -> SrcMac[3],eh -> SrcMac[4],eh -> SrcMac[5]);
}
void PrintIPHeader(const u_char *packet){ IPH *ih; ih = (IPH *)packet; printf("======== IP Header ========\n"); if (ih -> Protocal == 0x06) printf ("TCP\n"); printf("Src IP : %s\n", inet_ntoa(ih->SrcAdd) ); printf("Dst IP : %s\n", inet_ntoa(ih->DstAdd) );
}
void PrintTCPHeader(const u_char *packet){ TCPH *th; th = (TCPH *)packet; printf("======== TCP Heather ========\n"); printf("Src Port : %d\n", ntohs(th ->SrcPort)); printf("Dst Port : %d\n", ntohs(th -> DstPort)); }
void PrintHttpHeader(const uint8_t *packet){ HttpH *hh; hh = (HttpH *)packet; printf("======== Http Heather ========\n"); for(int i =0; i<16; i++) { printf("%02x ",hh -> HTP[i]); } printf("\n"); } |
cs |
10줄~53줄 : 각각 프로토콜의 헤더를 공부하면서 직접만든 구조체
55줄~58줄 : Print해줄 함수의 선언
66줄~69줄 : 인자값이 2개가 아니라면 help함수 호출후 강제종료
76줄~80줄 : pcap을 오픈하는데 NULL 값을 반환한다면 오류 print후 강제종료
84줄~86줄 : 다음 패킷을 읽는데 -1 또는 -2를 리턴한다면 강제종료
87줄~95줄 : 읽은 패킷의 값을 각 Print함수로 보낸다
98줄 : 오픈한 pcap을 닫는다
102줄~137줄 : 패킷의 데이터 값들을 출력
참고 : https://github.com/jungvely97/pcap_test
사용한 pcap 함수
1. pcap_t *pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
-피캡을 오픈하는 함수
device : 패킷을 오픈할 인터페이스
snaplen : 받아들일수 있는 패킷의 최대 크기(byte)
promisc : 네트워크 디바이스를 promiscuous mode 로 할것인지를 결정하기 위해서 사용한다. promisc 가 1일경우 promiscuous 모드가 되며, 로컬 네트웍의 모든 패킷을 캡쳐하게 된다. 0 일경우 에는 자기에게만 향하는 패킷을 캡쳐하게 되는데, 몇몇 경우에 있어서 promiscuous 모드로 작동하기도 한다.
to_ms : 읽기 시간초과(time out) 지정을 위해서 사용되며 millisecond 단위이다
.ebuf : pcap_open_live 함수 호출에 문제가 생겼을경우 에러 메시지를 저장하기 위해서 사용한다.
return :성공시 패킷 캡쳐 descriptor를 반환, NULL 을 리턴하고 에러내용을 ebuf에 복사한다.
2. int pcap_next_ex(pcap_t *p, struct pcap_pkthdr **pkt_header, const u_char **pkt_data)
- 다음 패킷을 읽는 함수
p : 오픈된 피캡
pkt_header : 패킷을 위한 구조체를 가르킴
pkt_data : 읽은 패킷의 데이터
return : 성공시 1을 반환, timeout이 만기될 경우 0을 반환, 에러시 -1을 반환, EOF시 -2를 반환
3. void pcap_close (pcap_t * p)
- p와 연관된 파일을 닫고 자원을 할당 해제
'Logs' 카테고리의 다른 글
[Ubuntu] 우분투 도커 다운받기, 도커 에러 (0) | 2020.11.08 |
---|---|
[Ubuntu] 우분투 버전 확인하기 (0) | 2020.11.07 |
MariaDB 설치부터 계정 생성, 권한부여 서버 연결까지 (*속성) (0) | 2020.11.06 |
아이디, 패스워드 Database 생성하기 (0) | 2020.08.14 |
ETH,IP,TCP 헤더 구조 (0) | 2019.05.20 |