[WINPCAP] 스니핑 프로그램
* pcap함수 정리
- pcap_findalldevs(pcap_if_t *alldevs, char *errbuf)
네트워크 장치들을 찾아주는 함수다.
실패하면 -1 반환, 성공 시 장치이름을 반환하고 없으면 NULL을 반환한다.
- pcap_open_live(const char *device_name, int snaplen, int promisc, int to_ms, char *errbuf)
현재 네트워크에서 해당 네트워크 장치 패킷을 캡처하는 함수이다.
int snaplen - 캡처할 패킷의 길이
int promisc - 보통 1(true)로 설정한다. true로 설정하면 무차별모드로 설정되어 모든 패킷을 잡게된다.
int to_ms - 패킷을 읽는 시간을 설정하는 부분이다. 0이 아닌 충분한 시간으로 설정해줘야 한다.
- pcap_compile(pcap_t *use_dev,
struct bpf_program *filter_struc,
char *FILTER_RULE,
int optimize,
bpf_u_int32 netmask)
문자열형식인 FILTER_RULE을 handle에 적용하기 위해 구조체형식으로 컴파일해주는 함수다.
실패시 -1을 반환한다.
struct bpf_program *filter_stuc - 구조체형식의 FILTER_RULE
char *FILTER_RULE - 문자열형식의 FILTER_RULE
Ex) host 127.0.0.1 and 8080
자세한 양식은 https://www.winpcap.org/docs/docs_40_2/html/group__language.html 를 참고하길 바란다.
int optimize - 결과코드의 최적화 수행여부 ( 보통은 1)
bpf_u_int32 netmask - 브로드캐스트를 감청할 때 쓰일 수 있는 인자이다. (보통은 NULL)
- pcap_setfilter(pcap_t *use_dev, struct bpf_program *filter_struc)
구조체형식의 FILTER_RULE을 실제 handle에 적용하는 함수이다.
- pcap_next_ex(pcap_t *use_dev, struct pcap_pkthdr **header, const unsigned **pkt_data)
마지막에 캡처된 패킷 데이터를 가져오는 함수이다.
실제로는 packet_handler 라는 콜백함수가 데이터들을 채워준다.
실패시 -1 반환
* HEADER구조 정의
- Ethernet header 구조(총 14bytes)
1 2 3 4 5 6 7 8 9 10 11 12 | struct ether_add { unsigned char mac_add[6]; }; struct ether_header { struct ether_add src_mac; struct ether_add des_mac; unsigned short eth_type; //14bytes }; | cs |
mac_add[6] : 각각 6bytes의 길이를 가지고 있다.
eth_type : 2bytes의 길이를 가지고있다.
short의 크기는 2byte이고, 음수의 값은 필요없으니 unsigned로 선언한다.
-IP header 구조 총(20bytes)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | struct ip_header { unsigned char ip_version : 4; unsigned char ip_header_length : 4; unsigned char ip_TOS; unsigned short ip_total_length; unsigned short ip_iden; unsigned char flag_x : 1; unsigned char flag_D : 1; unsigned char flag_M : 1; unsigned char offset_part_1 : 5; unsigned char offset_part_2; unsigned char TTL; unsigned char ip_protocol; unsigned short chk_sum; struct in_addr ip_src_add; struct in_addr ip_des_add; //20bytes }; | cs |
-Bit filed?
각 구조의 크기를 보면 Version은 4bit, Header Length도 4bit의 크기를 가진다.
이에 대한 정보를 최대한 효율적으로 저장하려면 1byte인 char형을 써야할 것이다.
하지만, char형도 그리 효율적이지 못하다.
1byte = 8bit 이므로 나머지 4비트는 버려지게 되는 것이다.
그래서 C/C++은 Bit filed라는 것을 사용한다.
선언은 unsigned char a:1 : 뒤에 할당 bit수를 적어주는 식으로 한다.
unsigned로 선언하는 이유는 부호비트까지 사용하기 위해서 unsigned로 선언한다.
이렇게 데이터를 나눠서 사용하면, char형은 8bit int형은 16bit까지 세세하게 효율적으로 사용할 수 있다.
- Tcp header 구조 (총 20bytes)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | struct tcp_header { unsigned short src_port; unsigned short des_port; unsigned long sqn_num; unsigned long ack_num; unsigned char offset : 4; unsigned char ns : 1; unsigned char reserve : 3; unsigned char flag_cwr : 1; unsigned char flag_ece : 1; unsigned char flag_urgent : 1; unsigned char flag_ack : 1; unsigned char flag_push : 1; unsigned char flag_reset : 1; unsigned char flag_syn : 1; unsigned char flag_fin : 1; unsigned short window; unsigned short chk_sum; unsigned short urgent_point; //20bytes }; | cs |
* CODE
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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | #define _WINSOCK_DEPRECATED_NO_WARNINGS #include "pcap.h" #include <stdio.h> #include <winsock2.h> #include <string.h> #pragma comment (lib,"ws2_32.lib") #pragma comment (lib,"wpcap.lib") #define BUF_SIZE 100 #define IP_HEADER_JMP 14 #define TCP_HEADER_JMP 20 #define DATA_JMP 20 #define SNAPLEN 65536 struct ether_add { unsigned char mac_add[6]; }; struct ether_header { struct ether_add src_mac; struct ether_add des_mac; unsigned short eth_type; //14bytes }; struct ip_header { unsigned char ip_version : 4; unsigned char ip_header_length : 4; unsigned char ip_TOS; unsigned short ip_total_length; unsigned short ip_iden; unsigned char flag_x : 1; unsigned char flag_D : 1; unsigned char flag_M : 1; unsigned char offset_part_1 : 5; unsigned char offset_part_2; unsigned char TTL; unsigned char ip_protocol; unsigned short chk_sum; struct in_addr ip_src_add; struct in_addr ip_des_add; //20bytes }; struct tcp_header { unsigned short src_port; unsigned short des_port; unsigned long sqn_num; unsigned long ack_num; unsigned char offset : 4; unsigned char ns : 1; unsigned char reserve : 3; unsigned char flag_cwr : 1; unsigned char flag_ece : 1; unsigned char flag_urgent : 1; unsigned char flag_ack : 1; unsigned char flag_push : 1; unsigned char flag_reset : 1; unsigned char flag_syn : 1; unsigned char flag_fin : 1; unsigned short window; unsigned short chk_sum; unsigned short urgent_point; //20bytes }; void print_ether_header(const unsigned char *pkt_data); void print_ip_header(const unsigned char *pkt_data); void print_tcp_header(const unsigned char *pkt_data); void print_data(const unsigned char *pkt_data); int main(int argc, char **argv) { pcap_if_t *alldevs = NULL; pcap_if_t *dev; pcap_t *use_dev; char errbuf[BUF_SIZE]; char FILTER_RULE[BUF_SIZE]="tcp"; struct bpf_program rule_struct; int i,dev_num,res; struct pcap_pkthdr *header; const unsigned char *pkt_data; //port설정 인자값이 있으면 port룰 설정 //없으면, 모든 패킷 감청 if (argv[1]) { strcpy(FILTER_RULE, "port "); strcat(FILTER_RULE, argv[1]); // FILTER_RULE (port)설정 } if (pcap_findalldevs(&alldevs, errbuf) < 0) { printf("Device Find Error\n"); return -1; } for (dev = alldevs, i = 0; dev != NULL; dev = dev->next) printf("%d번 Device : %s (%s)\n", ++i, dev->name,dev->description); printf("사용할 디바이스 번호 입력 : "); scanf("%d", &dev_num); for (dev = alldevs, i = 0; i < dev_num-1; dev = dev->next,i++); if ((use_dev = pcap_open_live(dev->name, SNAPLEN, 1, 1000, errbuf)) == NULL) { printf("pcap_open ERROR!\n"); pcap_freealldevs(alldevs); return -1; } printf("pcap_open 성공!\n"); printf("FILTER_RULE : %s\n", FILTER_RULE); ////////// pcap_open_success //////////// if (pcap_compile(use_dev, &rule_struct, FILTER_RULE, 1, NULL) < 0) { printf("pcap_compile ERROR!\n"); pcap_freealldevs(alldevs); return -1; } if (pcap_setfilter(use_dev, &rule_struct) < 0) { printf("pcap_setfilter ERROR!\n"); pcap_freealldevs(alldevs); return -1; } pcap_freealldevs(alldevs); //캡처 네트워크를 제외한 네트워크 해제 while ((res = pcap_next_ex(use_dev, &header, &pkt_data)) >= 0) { if (res == 0) continue; print_ether_header(pkt_data); pkt_data += IP_HEADER_JMP; print_ip_header(pkt_data); pkt_data += TCP_HEADER_JMP; print_tcp_header(pkt_data); } } ///////////////////////////////////print_function/////////////////////////////////////// void print_ether_header(const unsigned char *pkt_data) { struct ether_header *eh; eh = (struct ether_header *)pkt_data; unsigned short ether_type = ntohs(eh->eth_type); if (ether_type == 0x0800) printf("===== IPv4 =====\n"); printf("Src MAC : "); for (int i = 0; i <= 5; i++) printf("%02x ", eh->src_mac.mac_add[i]); printf("\nDes MAC : "); for (int i = 0; i <= 5; i++)printf("%02x ", eh->des_mac.mac_add[i]); printf("\n"); } void print_ip_header(const unsigned char *pkt_data) { struct ip_header *ih; ih = (struct ip_header *)pkt_data; if (ih->ip_protocol == 0x06) { printf("(TCP)"); printf("Src IP : %s\n",inet_ntoa(ih->ip_src_add)); printf("(TCP)"); printf("Des IP : %s\n", inet_ntoa(ih->ip_des_add)); } if (ih->ip_protocol == 0x17) { printf("(UDP)"); printf("Src IP : %s\n", inet_ntoa(ih->ip_src_add)); printf("(UDP)"); printf("Des IP : %s\n", inet_ntoa(ih->ip_des_add)); } } void print_tcp_header(const unsigned char *pkt_data) { struct tcp_header *th; th = (struct tcp_header *)pkt_data; printf("Src Port : %d\n", ntohs(th->src_port)); printf("Des Port : %d\n", ntohs(th->des_port)); printf("====================\n\n"); } | cs |
* 실행
1:1 채팅 프로그램으로 통신하는 상황을 스니핑하는 모습이다.
Server에서 4540번 포트를 열고 1:1 통신한 패킷들이다.
MAC/IP/Port 정보와 평문으로 전송된 패킷들이 그대로 잡히는 모습을 확인할 수 있다.