Network/NetProg

[WINPCAP] 스니핑 프로그램

pyozzi 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)

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, 11000, 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, 1NULL< 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 == 0continue;
 
        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 == 0x0800printf("===== 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 정보와 평문으로 전송된 패킷들이 그대로 잡히는 모습을 확인할 수 있다.