[yummyKit] 4 day

10 분 소요

※주의사항※
연구 목적으로 작성된 것이며, 허가 받지 않은 공간에서는 테스트를 절대 금지합니다. 악의적인 목적으로 이용할 시 발생할 수 있는 법적 책임은 자신한테 있습니다. 이는 해당 글을 열람할 때 동의하였다는 것을 의미합니다.


짜란~~ 오랜만에 yummyKit 브리핑을 하게 되었어여!! 야미킷 개발을 못한지도 벌써 몇달이 지난지 모르겠네요 ㅜㅠ흉흉 얼른 해야할텐뎅...

저는 요즘 회사에서 Shell Script + C언어 개발을 하고있어여! 스크립트가 쉽다고 생각하면 쉬울 수 있지만 이거 어떻게 trim 할 지, 어떤 명령어를 사용할 지, 참 다양하더라구여!! 그 중 가장 unix 에 common 하도록 사용할 수 있어야 어느 곳에 설치해도 동작할 수 있으니 여러가지를 따져 최대한 라이브러리를 사용하지 않도록 만들고 있지여 ㅎㅎㅎ


갑자기 회사 업무나 얘기하고있다닝 T^T 여러분 넘나 반가워영 ㅜㅠ 요즘 잠을 너무 못자서 주말엔 잠밖에 안자써여...(미드 News Room 보면서여 헿헤헤헿 여러분 뉴스룸보세여!! 시즌 1은 10편, 시즌 2는 9편, 시즌 3는 6편으로 총 25부작짜리 미드예여!! 완전재밌다능!!



멋지지 않나여?! 이렇게 진실을 밝힐 수 있다니.. 감격에 벅차서 눙물이 날 것만 같아여 T^T

그래서 저는 손석희선생님의 JTBC 뉴스룸도 이것과 연관성이 있지 않나~ 싶어여!! 시리즈 보시다보면 언론과 현실에 대해(물론 드라마이기 때문에 비현실적인 부분이 상당하겠지만!!) 다루는게 너무나도 멋있더라구여!! 추천추천 강추할게여!


자 그럼 이제 본론으로 들어가볼까여!? 이 전까지의 포스팅 기억하시나여?! 고작 3개밖에 없지만... 넘나 오랜만에 와서여 ㅜㅠ

첫 번째 pcap 파일을 통해 패킷 분석 프로그램을 만들어 보셨나여!? 어떤 패킷 파일을 말하냐구여? ㅜㅠㅜ 제가 넘나 오랜만에 와서 다 잊으신건가여..!? 뭐 파일이야 첨부하면 그만이니 마구 남발해드리져 헿헤헤헤ㅔㅎㅎ!!


httpGet.pcap


바로 이파일이여!! 기억나셨나여!? 그리고 이 파일의 파일명을 보시면서 혹시 yummyKit 브리핑 2일차는 기억안나셨나여!? 바로 아래와 같이 제가 그림판 수작업한 설명이여!!


Http Get Header example


위 사진이 바로 저 위 httpGet.pcap 파일을 설명하는 설명그림이예여!! 위 그림과 같이 분석을 한다면


analysis

Destination Mac Address = 085ddd45b9a1

Source Mac Address = b8975107caa0

Ethernet Type = IP

Source IP Address = 192.168.219.100

Destination IP Address = 125.209.210.116

Source Port = 53106

Destination Port = 80


까지 printf() 로 출력할 수 있으면 기본적인 패킷 분석 입문은 완료예여!!

지난 포스팅에서 초기 설정도 완료했고, winpcap 예제 페이지도 드렸으니 충분히 하셨겠져!?! 코드나 밝히라구여? 넵 ㅜㅠ쭈굴..

source code

#pragma comment(lib, "ws2_32.lib")

/*
This code is made by yummyHit using Microsoft Visual C++ 2010 Express.
You must have winpcap and add to library, include directory at your VC/bin directory.
Must be add linker: ws2_32.lib; wpcap.lib; Packet.lib
*/
#define HAVE_REMOTE
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <pcap.h>

struct mac {        // Mac Address Struct. Each of 2 bytes. Total size is 6 bytes.
    u_short byte1, byte2, byte3;
};

struct ethernet_h {    // Destination Mac Address, Source Mac Address, Ethernet Type Struct.
    struct mac dst_mac, src_mac;
    u_short type;    // Don't use it. It just exist for struct size.
};

struct ip_h {        // IP frame struct. First, there is each flags; such as vhl, service and so on.
    u_char ip_v; u_char ip_hl; // 2
    u_short total_len;    u_short identification; // 4
    u_short ip_off; // 2
    u_char ttl;        // 1
    u_char ip_protocol;    // 1
    u_short chksum;    // 2
    struct in_addr ip_src, ip_dst;    // We use it for recognize to ip address.
};

struct tcp_h {        // TCP segment struct. We just get two member what port number.
    u_short src_port;
    u_short dst_port;
    int seq, ack;
};

int main() {
    int num, i = 0;    // num variable get interface number, i variable is only integer variable.
    u_int pktCnt = 0;    // Packet Count variable.
    pcap_if_t *alldevs, *ex;    // alldevs pointer variable is devices search, ex pointer variable is descript each device.
    struct pcap_pkthdr header;
    const u_char *data;            // data pointer variable will have packet content after pcap_next
    struct ethernet_h *eth; struct ip_h *ip; struct tcp_h *tcp;    // Each of struct pointer variable.

    char file[] = "httpGet.pcap";

    char errbuf[PCAP_ERRBUF_SIZE];
    pcap_t *pcap = pcap_open_offline(file, errbuf);    // pcap file open!

    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) {
        fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
        exit(1);
    }

    for(ex = alldevs; ex; ex=ex->next) {
        printf("%d. %s", ++i, ex->name);
        if (ex->description) printf(" (%s)\n", ex->description);
        else printf(" (No description available)\n");
    }

    if(i == 0) {
        printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
        return -1;
    }

    printf("Enter the interface number (1-%d):", i);
    scanf_s("%d", &num);

    if(num < 1 || num > i) {
        printf("\nInterface number out of range.\n");
        pcap_freealldevs(alldevs);
        return -1;
    }

    for(ex = alldevs; ex; ex = ex->next, ++i) if(i == num) break;
    printf("You select Num %d\n And NIC is %s\n", num, ex->name);

    while(pcap_next_ex(pcap, &header, &data) > 0) {
        eth = (struct ethernet_h *) data;    // eth point to packet's first part.
        ip = (struct ip_h*) (data + sizeof(struct ethernet_h));    // ip point to part of packet in sequence.
        tcp = (struct tcp_h*) (data + sizeof(struct ethernet_h) + sizeof(struct ip_h));    // tcp point to part of packet in sequence.

        printf("Packet No .%i\n", ++pktCnt);    // print packet number
        printf("Packet size : %d bytes\n", header.len);    // print packet length
        printf("MAC src: %04x:%04x:%04x\n", htons(eth->src_mac.byte1), htons(eth->src_mac.byte2), htons(eth->src_mac.byte3));    // print Source Mac Address
        printf("MAC dest: %04x:%04x:%04x\n", htons(eth->dst_mac.byte1), htons(eth->dst_mac.byte2), htons(eth->dst_mac.byte3));    // print Destination Mac Address
        printf("IP src : %s\n", inet_ntoa(ip->ip_src));        // print Source IP Address
        printf("IP dest : %s\n", inet_ntoa(ip->ip_dst));    // print Destination IP Address
        printf("Src port : %d\n", ntohs(tcp->src_port));    // print Source TCP Port
        printf("Dst port : %d\n", ntohs(tcp->dst_port));    // print Destination TCP Port

        if(header.len != header.caplen) printf("Capture size error. Different size : %ld bytes\n", header.len);

        printf("\n\n");
    }
    return 0;
}


헤헿 야미가 제대로 코드를 짜기 시작한 것은 이 프로그램이 처음이예여!! 뭘 어떻게 구현해야하는지도 몰랐고, 그냥 winpcap 예제를 참고해서 제 맘대로 해보았었찌여!! 이렇게 구현했더니 결과물은 다음과 같이 빠밤~! 하고 나타나여!


output


처음 우리가 기대했던것과 같이 나오네여!! 자 그럼 소스코드 분석을 시작해보아여!! 처음 전처리 부분부터 볼까여?


code review

#pragma comment(lib, "ws2_32.lib")

--> lib 종속성 comment 부분으로 ws2_32.lib 파일을 추가하겠다~ 고 선언하는 선언부분이예여!! pragma 지시자 처음보신다구여?!


#pragma는 #include 혹은 #define 과 같이 전처리 지시문으로 분류되는데요, 차이점이라면 #define 혹은 #include는 OEP 수행 전 처리해야할 지시문이고, #pragma는 컴파일 이전 기능을 사용하도록 해주는 지시문이예요! 저번 포스팅에서도 깜☆짝★등☆장 했었지요!?

yummyHitOS 포스팅을 보시면 #pragma once 혹은 #pragma pack(push or pop) 으로 패킹 방법 지시를 할 수도 있다는 것을 알 쑤가 있지여!! gcc 에서 기본적으로 제공하는 C 표준 지시어예요. 정해진 것이 몇 가지 없으니 깊게 파고들지 않아도 될 것 같아요!


#define HAVE_REMOTE

--> 패킷 캡쳐를 위해 전처리 지시자에 추가해야 한다고 WinPcap 에서 정의한 선언이예요. remote-ext.h 헤더파일에 직접 추가하지 않고 remotable 하게 전처리로 선언하는 방법을 택한데는 이유가 있겠져..? ㅜㅠ 전 그렇게 똑똑이가 아니라 그것까지 파헤치기엔 winpcap 소스코드를 전부 분석해야 할 것 같아서 우선은 보류할래여!!


#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <pcap.h>

--> 우리에게 익숙하며 이 소스에서 사용될 함수들을 정의한 헤더파일 선언문이예요! standard input/output 의 줄임말인 stdio 와, standard library 의 줄임말인 stdlib 관련 헤더파일, windows socket programming 에 필요한 winsock 헤더파일, windows packet capture 에 필요한 pcap 헤더파일이 추가되어 이 헤더파일에 정의된 함수를 이제 사용하지요!


여기까지 이해 되셨나여!? 기본적인 C언어 문법정도 되는데.. 여러분은 충분히 지루하리만큼 이해하셨을거라 생각해여!! 저보다 똑똑하신 분들이잖아여!! 사실 저 코드를 보여주기도 참 창피했어여ㅜㅠ 아직 2년이 채 안되었지만 그래도.. 지금보면 너무나 허술한 코드여서여 ㅜㅠ..


이제 다음으로 4개의 구조체를 선언해여!!



첫 번째 구조체인 mac 이라는 이름의 구조체는 unsigned short 자료형의 변수 3개가 멤버로 있는데, 먼저 short 자료형인 이유는 Mac Address 는 총 6바이트이므로 2바이트씩 3자리로 나눌 수 있기 때문에 short 자료형을 사용했어요! 또한 Mac Address 엔 음수가 없으므로 unsigned를 사용했구요!



그리고 이제 TCP/IP 5계층에서 Physical Layer인 1계층에선 HardWare 적인 부분만 다루지만(OSI 7계층은 너무나도 유명하니 다들 아시겠져!? 나중에 Network 파트에서 다룰게여!!) 바로 다음 계층인 DataLink Layer에서 조금이나마 SoftWare 적인 부분인 패킷헤더를 알 수 있게 되어요!!


DataLink Layer는 Ethernet Layer라고도 부르는데, Ethernet Packet이 있기 때문이예요. Ethernet 헤더에는 목적지(Destination) Mac Address와 출발지(Source) Mac Address, 그리고 이 후 Layer의 Type을 2바이트로 알려주어 총 14Bytes 의 헤더를 가지고 있는 계층이랍니다!!


바로 위 ethernet_h 구조체를 살펴보면, 아까 전 Mac Address 를 표현하는 mac 구조체 변수가 2개(dst_mac, src_mac) 선언되어 있으며, Type 필드를 표현할 type 변수가 unsigned short 자료형으로 선언되어 있지요!!


여기까지는 이해가 되셨나여!? httpGet.pcap 파일 설명 그림과 함께 보시면 조금 이해가 되실거예여!! C언어에 너무 겁먹지 않으시면 이정도는 금방 이해하실거라 믿어요!!



이제 Ethernet Layer 다음 계층인 Network Layer 예요! OSI 7 Layer에서는 Network Layer라고 부르지만 TCP/IP Layer 에서는 IP Layer 라고도 부른답니다!! 대표적인 프로토콜을 본따서 계층의 이름을 나타낸 것이지요!! 이 IP 계층의 헤더는 다음과 같이 이루어져 있어요.

구글에 툭 치면 탁 튀어나오는 그림이지만, 아무래도 야미의 포스팅을 읽는 분께 이래라 저래라 하면 미움받을테니까여!? 헿헤



쨔란~~ 네트워크에 관한 어느 책에나 있고 네트워크라고 검색하면 거의 메인 이미지로 있는 너무나도 흔하고 중요한 IP 헤더 구조예요!! 뭔가 막 복잡하게 숫자도 적혀있고.. 영문도 적혀있고... 헷갈리지않나여?! 전 처음에 이거 보고 극혐했어여 으으으 그켬...

위 0~3 빨간 숫자는 바이트, 각 조그마한 칸의 크기는 1 bit예요. 또한 좌측에 있는 0~20까지의 숫자 역시 byte를 뜻하지요!


자 그럼 처음부터 천천히 보면, IP 헤더의 첫 4bit는 Version을 의미해요. '4' 이면 IPv4(XXX.XXX.XXX.XXX 로 해당되는 우리가 지금 사용하는 IP를 의미하죠!), '6' 이면 IPv6(늘어나는 인구와 컴퓨터 사용량으로 4byte IP가 아닌 16byte IP를 사용하려고 나온 발전된 IP 버젼이라고 할까요!?)


그 다음 4bit는 IP Header Length를 의미하는데, 4바이트 단위의 워드로 표시한다고 해요. 최소 5의 값부터 최대 15까지의 값이 있다고 하는데, 평균적으로 5라고 암기하셔도 괜찮을 거예요!! 물론 4바이트 * 5 = 20바이트이므로, 20바이트 이후에 TCP/UDP 가 존재하는 Transport Layer 의 시작 필드가 아니라면 IP Header 에 Option 필드가 있다고 보시면 되구요!!

음.. 말이 조금 어려웠나요!? IP 헤더에 옵션이 없다면 기본적으로 20byte 크기를 갖지만, 옵션이 있다면 달라지므로 IP 헤더의 시작부터 + 20byte 를 보았을 때 4계층인지 아닌지 여부를 확인하여 4계층이라면 IP 헤더에 옵션이 없다는 것이고, 4계층이 아니라면 IP 헤더에 옵션이 있다는 말이예요!!


그 다음으로 있는 1byte 짜리 ToS는 IP 헤더 내의 서비스 유형 또는 혼잡 알림을 나타내는 필드예요. DS필드(6bit) + ECN필드(2bit)로 이루어져 있지요!

DS필드(Differentiated Service,차등 서비스)는 DSCP 코드로 이루어져 있어요. 이 코드는 우선순위, 제어권 등을 나타내지요! 점점 파고드는 이 양파같은 녀석~ :D

그리고 ECN필드(Explicit Congestion Notification,명시적 혼잡 알림)는 00이면 패킷이 ECN 기능을 사용하지 않는 것을 나타내고, 01 또는 10이면 발신측에서 종단점이 ECN 기능을 수용함을 나타내며, 11이라면 라우터가 혼잡이 발생했음을 알리고자하는 표식이예요.

소스코드 분석에서 헤더 설명까지가다니.. 넘나 머리아프지않나여!? 잠깐 쉬었다 갈까여?!



흫헿헤헿 이런거 넘나 재밌지않나여? 저만 재밌다구여..? ㅜㅠㅜ 이런유머가 에바참치꽁치삼치보다 더 많아졌으면 좋겠어여... 급식체보다 더 재밌는뎅 ㅜㅠㅜ


그리고 네 번째로 있는 Total Length는 IP 헤더 이후의 모든 데이터를 포함한 전체 패킷 크기를 나타내요.(최대 64KB 랍니다~) 64KB를 나타내기 위하여 2바이트를 사용하는 것 같네여!! 2의 16제곱은 65,536이니깐여!!


그 다음으로 2바이트에 표시되어 있는 Identification 이라고 적힌 필드는 Fragment Identifier 이라고도 하며, 같은 IP 대역에 속했을 때 서로의 ID 값을 통해 공유되어지므로 확인하는 시리얼 번호라고 생각하시면 될거예요!


이 후에 알록달록이쁜색으로 적혀있는 Flags 3bit는 하단에 적혀있는 것과 같이 x D M 이라고 하네요! 설명도 사진 아래에 있으니(왜이렇게 얼렁뚱땅 넘어가는지 조금 있다가 말씀드릴게옇ㅎㅎ) 참고해주시구여!!


그리고 Fragment Offset 이 있어요!! 지금 3번 연속으로 Fragment 라는 단어가 자꾸 등장하네여!? ID 필드와 Flags 필드와 Offset 필드가 전부 이 한 가지를 위해 있다는 것이져!! IP 단편화와 재조립!!


IP 단편화(Fragmentation, Segmentation)는 큰 IP 패킷들이 적은 MTU(Maximum Transmission Unit)를 갖는 링크를 통하여 전송되어야 하므로 단편화(조각조각 내는 것을 의미해요)가 필요하게 되지요. 여러개의 작은 패킷으로 쪼개어/조각화 되어 전송되어야 해요. 즉, 거치는 각 라우터 마다 전송에 적합한 DayaLink Layer 프레임으로 변환이 필요하다는 것이죠.

IP 재조립(Reassembly)은 일단 단편화되어 최종 목적지에 도달할 때까지 재조립되지 않는 것이 일반적이지만, IPv4에서는 발신지 뿐만 아니라 중간 라우터에서도 IP 단편화가 가능하고, 재조립은 항상 최종 수신지에서 만 가능하다고 해요!


그리고 TTL은 OS 환경에 따라 달라져요. 보편적으로 Unix/Linux는 64, Windows는 128 이라고 하는데, 이 TTL은 네트워크에서 나오는 Hop 의 수를 의미하며, Hop이란 라우터를 거쳐갈 때마다 줄어드는 카운트라고 생각하시면 되어요!

즉 집->지역->도시->LG 기지국->도시->지역->친구집 으로 통한다면 총 6개의 라우터를 통하므로 64 - 6 또는 128 - 6 의 TTL이 나오죠! 우리가 평소에 인터넷 상태를 확인하기 위해 Ping 명령을 이용하면, 이 때 나타내는 TTL이 바로 이 Hop Count를 의미해요. 그리고 실질적으로 Hop Count는, 즉 TTL은 최대 255의 크기를 가져요! 8비트이기 때문이져!!


이제 다음 필드인 Protocol ID를 살펴보면, 이것은 Ethernet Layer에서의 Type과 같이 다음 계층의 프로토콜이 무엇인지 나타내게 되어요. TCP인지, UDP인지, ICMP, EGP, EIGRP, OSPF 등등 프로토콜을 나타내주죠!


드디어!! 오류검출하는 체크섬 필드를 지나 우리가 알아야 하는 Source IP Address 와 Destination IP Address 가 자리하네요!!

이제 다시 구조체를 살펴볼까여!?


저의 예전 코드는... 첫 번째 변수를 잘못 지정 했네요ㅜㅜ u_char ip_v, ip_hl; 변수는 각 각 1바이트를 차지하므로 실질적으로 ip_v 변수에 Version 정보와 IHL 값이 들어가며, ip_hl 변수에 ToS 필드가 차지한다는 것을 눈치 채셨나요? 흑.. 이 창피함이란...

그렇다면 4비트짜리 자료형은 없는데?! 어떻게 선언하나용!?


u_char ip_v_hl; 를 선언하여 다음과 같은 3단계를 거치시면 된답니당!!


ip_v_hl = 4;

ip_v_hl = ip_v_hl << 4;

ip_v_hl = 5;


위와 같이 선언하면 앞 4bit는 0100, 뒤 4bit는 0101로 선언되어 IPv4 와 20byte를 의미하게 되지여!! 시프트 연산이란 네트워크에서 자주 사용되는 것이니 잘 알아두세용!!


그리고 ip_h 구조체의 나머지 멤버는 위 설명과 동일해요. 마지막 멤버인 Source IP Address 와 Destination IP Address 를 담고있는 in_addr 구조체는, 소켓통신에서 사용되어지는 GNU C 표준에서는 sys/socket.h 헤더와 netinet/in.h 헤더에 담겨져있으며, 윈도우에서는 Winsock.h 헤더에 담겨있어요. IP주소와 INET 정보, 포트 등을 담고있는 구조체이지요!


IP 헤더만 이야기해도 이렇게 스크롤이 슉슉 내려가네용 ㅜㅠ 마지막으로 TCP 헤더에 대해 알아보아요!!



TCP 헤더에는 더 많은 정보와 더 중요한 필드가 있지만, 이는 데이터 통신에 중요한 것이지 아직 우리가 패킷 분석을 하기엔 너무나도 복잡한 구조를 이루고 있으니... 나중에 네트워크 카테고리에서 설명드릴게여!! 그저 우선 포트만 분석하자구여!! 헤헿


그래서 tcp_h 구조체에는 단지 src_port 와 dst_port 라는 포트에 관한 unsigned short 자료형 변수만 존재해요! main 함수 분석은 다음 브리핑으로 넘겨야겠네여 ㅜㅠ 얼른 다 해드리고 싶었찌만... 최대한 빠른 시일 내에 5일차 브리핑과 함께 main 함수 분석으로 다시 찾아뵐게여!!! 뿅-☆



Check out the yummyhit’s website for more info on who am i. If you have questions, you can ask them on E-mail.

카테고리:

업데이트:

댓글남기기