-
[WINPCAP] 스니핑 프로그램Network/NetProg 2017. 10. 5. 01:02#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)
123456789101112struct 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)
12345678910111213141516171819struct 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)
12345678910111213141516171819202122struct 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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195#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 65536struct 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 정보와 평문으로 전송된 패킷들이 그대로 잡히는 모습을 확인할 수 있다.
'Network > NetProg' 카테고리의 다른 글
ARP Spoofing Tool ( + 자동화 , 코드 보완 ) (3) 2020.02.06 [C / lpcap] ARP Spoofing (1) 2017.12.14 댓글