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, 11000, errbuf);

    if (handle == NULL){

        printf("%s : %s \n", dev, errbuf);

        exit(1);

    }

 

    while(1){

 

        int res = pcap_next_ex(handle, &header, &packet);

        if (res == 0continue;

        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 == 0x06printf ("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");

}

Colored by Color Scripter

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와 연관된 파일을 닫고 자원을 할당 해제

+ Recent posts