네트워크 및 보안/pcap 파일 분석기 만들기 with C언어

[pcap 파일 분석기 만들기 wich C언어] 2. 패킷 분석기 예광탄 - 패킷 개수 및 바이트 수 출력

언제나휴일 2016. 5. 20. 14:29
반응형


2. 패킷 분석기 예광탄 - 패킷 개수 및 바이트 수 출력



안녕하세요. 언제나 휴일, 언휴예요.


이전 게시글에서는 pcap 파일 구조를 간략하게 살펴보았었죠. 


이번에는 "pcap 파일 분석기 만들기 with C언어"의 첫 번째 프로그램 실습을 할 거예요.


이번 실습은 패킷 분석기 예광탄으로 네트워크 트래픽을 수집한 pcap 포멧의 추적 파일을 열어 수집한 패킷의 수와 패킷 별로 바이트 수를 콘솔 화면에 출력하는 간단한 응용입니다.


앞으로 이어지는 강의에서 프로토콜 별로 패킷을 분석하는 부분을 추가하는 작업을 진행할 거예요. 그리고 몇 가지 필요한 요소 기술을 익힌 후에 패킷 분석기를 만드는 최종 프로젝트로 들어갑니다. 


이번 실습 이후에 당분간은 패킷을 프로토콜 별로 분석하는 작업을 한다고 보시면 되겠죠.


다음은 이번에 실습할 프로그램의 동작 화면입니다. 사용자는 네트워크 트래픽을 수집한 추적 파일명을 입력하면 추적 파일을 열어 수집한 패킷의 수와 패킷 별로 바이트 수를 콘솔 화면에 출력하고 있음을 알 수 있어요.


C언어나 C++언어를 선택하시고 비어있는 형태의 콘솔 프로젝트를 만들어 소스 파일을 추가하세요.



먼저 pcap 파일 헤더 구조체를 정의합시다. pcap 파일 헤더는 magic(4 바이트 0xa1b2c3d4)과 major 버전(2 바이트)과 minor 버전(2 바이트), 표준시와의 차이(4 바이트), 타임 스탬프(4 바이트), 캡쳐할 수 있는 상한 바이트 수(4 바이트), 링크 타입(4바이트)로 구성하고 있죠.

typedef struct _pcap_file_header

{

        #define MAGIC   0xa1b2c3d4 //pcap magic

        int magic;

        unsigned short version_major;

        unsigned short version_minor;

        int thiszone; /* gmt to localcorrection */

        unsigned sigfigs; /* accuracy oftimestamps */

        unsigned snaplen;            /* maxlength savedportion of each pkt */

        unsigned linktype;            /* datalink type (LINKTYPE_*) */

}pcap_file_header;



그리고 패킷 헤더 구조체를 정의합시다. 패킷 헤더는 패킷을 수집한 타임 스탬프(8바이트), 캡쳐 길이(4바이트), 패킷 길이(4바이트)로 구성하고 있어요. 


먼저 타임 스태프를 초단위와 마이크로 초 단위의 구조체를 정의하세요. 

typedef struct _timeval

{

        long    tv_sec;         /* seconds*/

        long    tv_usec;        /*andmicroseconds */

}timeval;



그리고 패킷 헤더 구조체를 정의하세요. 

typedef struct _pcap_header

{

        timeval ts;    /*time stamp */

        unsigned caplen; /* length of portionpresent */

        unsigned len;     /*length thispacket (off wire) */

}pcap_header;

 


여기에서는 최대 MAX_PACKET 개수를 분석할 수 있게 할게요.

#define MAX_PACKET  10000


패킷 헤더를 보관할 배열을 전역에 선언할게요. 자신의 프로그래밍 철학이 전역 변수를 선언하는 것은 맞지 않는다고 하신다면 자신의 스타일에 맞게 수정하세요. 앞으로 예광탄에서는 프로그래밍 철학보다는 요소 기술 전달에 중점을 두고 작성할게요.

pcap_header headers[MAX_PACKET];//패킷 헤더들을 보관할배열


추적한 패킷 개수를 기억할 변수도 선언하세요.

int pcnt; //패킷 개수


추적 파일을 분석하는 부분은 별도의 함수(Parsing)에서 작성하기로 합시다.

int Parsing(FILE *fp);



int main()

{

        char fname[256];

        FILE *fp = 0; 

사용자로부터 분석할 추적 파일 명을 입력받습니다.

        printf("파일 명:");

        gets_s(fname, sizeof(fname));


파일을 열고 난 후에 분석하는 Parsing 함수를 호출하세요.

        fopen_s(&fp,fname, "rb");//전달받은 파일을 읽기/바이너리모드로 열기

        Parsing(fp);//분석하기

작업이 끝나면 파일을 닫습니다.

        fclose(fp);//파일 닫기

        return 0;

}

 

이번 실습에서는 패킷 번호와 패킷 바이트 수만 출력하겠지만 다음 실습에서는 ethernet 프로토콜 부분을 분석하는 실습을 할 거예요. 이번 실습에서는 패킷 헤더 정보만 출력하고 ethernet 프로토콜 정보를 분석하는 부분은 생략할 거예요. 이를 위한 함수를 선언하세요.

void ParsingEthernet(FILE *fp);

int Parsing(FILE *fp)

{

        pcap_file_header pfh;

먼저 pcap 파일 헤더를 읽습니다.

        fread(&pfh, sizeof(pfh), 1, fp);//pcap 파일 헤더읽기   


만약 매직이 다르면 분석할 수 없겠죠.

        if (pfh.magic != MAGIC//매직이 다르면

        {

               printf("this file format is not correct \n");

               return -1;

        }

        printf("version:%d.%d\n", pfh.version_major, pfh.version_minor);//pcap 헤더 정보 출력

 

링크 타입에 따라 분석할 것인데 여기에서는 ethernet 일 때만 분석할 거예요.

        switch (pfh.linktype)//링크 타입에 따라

        {

        case 1:ParsingEthernet(fp); break//Ethernet 방식으로 분석

        case 6:printf("Not support Token Ring\n");break;

        case 10:printf("Not support FDDI\n"); break;

        case 0:printf("Not support Loopback\n"); break;

        default:printf("Unknown\n"); break;

        }

        return 0;

}

 

이제 ParingEthernet 함수를 작성합시다. 여기에서는 먼저 패킷 헤더 정보를 출력하고 ethernet 정보를 출력하는 작업을 수행합니다. 물론 이번 실습에서는 ethernet 프로토콜을 분석하여 출력하는 부분은 빈 상태의 함수로 작성할 거예요.

void ViewPacketHeader(pcap_header *ph);

void ViewEthernet(char *buf);

void ParsingEthernet(FILE *fp)

{

        char buf[65536];

        pcap_header *ph = headers;//ph를 패킷 헤더의 시작 위치로 초기화

        int i = 0;

파일의 끝을 만날 때까지 반복합시다.

        while (feof(fp) == 0) //파일의 끝이 아니면 반복

        {

파일 헤더 읽기를 실패하면 현재 작업을 멈추고 함수를 종료합니다.

               if (fread(ph, sizeof(pcap_header), 1, fp) != 1)//패킷 헤더 읽기를 실패하면

               {

                       break;//루프 탈출

               }

 

패킷의 헤더 정보를 출력합니다.

               ViewPacketHeader(ph); //패킷 헤더 정보 출력


패킷 헤더의 캡쳐 길이만큼 읽습니다.

               fread(buf, 1, ph->caplen, fp); //패킷 읽기

그리고 읽은 버퍼를 인자로 이더넷 정보를 출력하는 함수를 호출하세요.

               ViewEthernet(buf); //이더넷 정보 출력

패킷 헤더의 위치를 다음으로 이동합니다.

               ph++;//ph를 다음 위치로 이동

        }

}


패킷 정보를 출력하는 함수를 작성합시다. 

void ViewPacketHeader(pcap_header *ph)

{

        pcnt++;//패킷 개수를 1 증가

패킷 번호와 패킷 수집 시각 정보와 캡쳐 길이 및 패킷 길이를 출력합시다.

        printf("\nNo:%dtime:%08d:%06d caplen:%u length:%u \n",

               pcnt, ph->ts.tv_sec, ph->ts.tv_usec, ph->caplen, ph->len);

}


이더넷 정보를 출력하는 함수는 비어있는 상태로 만드세요. 이 부분은 다음 실습에서 구현할 부분이예요.

void ViewEthernet(char *buf)

{

        //to be defined

}


이제 프로그램을 실행해서 정상적으로 동작하는지 확인해 보세요.


이상으로 패킷 분석기 예광탄 - 패킷 개수 및 바이트 수를 출력하는 프로그램 실습을 마칠게요.


모두 행복한 하루~  


실습에 사용할 추적파일(pcap 포멧 파일이면 다른 파일도 관계 없어요.)

demo.pcap


소스 파일

Program.c



반응형