[yummyKit] 5 day

7 분 소요


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


지난 4일차 브리핑 코드 기억나시나욤!? 각 헤더에 대해 설명해드리고 끝나부렀서욤 ㅜㅜ 그래서 오늘은 드디어 main 함수로 진입!!! 그런데 코드가 기억이 안난닷!?! 좋은 툴을 이용해서 코드 넣어 보즈아!!


Http Get Header example

main function

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;
}


왜 이것을 진즉에 몰라가지구 항상 스크린샷을 그렇게 찍어댔을까욤 ㅜㅜ 역시 머리가 멍충하면 몸이 고생한다구... 힝


code review

u_int 자료형은 unsigned int 예욤! int 자료형이 4바이트 크기만큼 –2,147,483,648 ~ 2,147,483,647 (2^31 ~ 2^31 - 1) 범위이니 unsigned 는 음수가 없다는 것이므로 0 ~ 4,294,967,295 (2^32 - 1) 범위가 되는 것이죠!
우리는 패킷을 다루면서 음수가 없는 1byte 를 다루어야 하니 u_char 를 많이 사용할 거예요! 그렇기에 중요한 unsigned 입니당!


그 다음으로 pcap library 의 자료형과 구조체가 나와욤! pcap_if_t 자료형과 struct pcap_pkthdr 구조체욤!
pcap_if_t 자료형은 typedef struct pcap_if pcap_if_t 이므로 결국 pcap_if 구조체의 alias 예요! 이 구조체를 살펴보면,


struct pcap_if {
    pcap_if* next // if not NULL, a pointer to the next element in the list; NULL for the last element of the list
    char* name // a pointer to a string giving a name for the device to pass to pcap_open_live()
    char* description // if not NULL, a pointer to a string giving a human-readable description of the device
    pcap_addr* addresses // a pointer to the first element of a list of addresses for the interface
    u_int flags // PCAP_IF_ interface flags. Currently the only possible flag is PCAP_IF_LOOPBACK, that is set if the interface is a loopback interface.
};


이렇다고 나와있네욧!! 다음 구조체로 넘어가는 노드와 device 명(interface card 명이라고 생각하시면 됨니다!!), device 의 설명과 주소, 플래그가 멤버변수네욤!! 이 구조체를 통해 NIC 를 선택해서 패킷을 잡을 예정인가봅니당!! 키야 싱기방기동방싱기~!

다음 구조체인 struct pcap_pkthdr 의 경우 캡쳐된 헤더의 길이와 총 길이를 확인할 수 있는 멤버변수인 caplen 과 len 두 가지가 있어욤! 이는 길이가 넘어가지 않도록, overflow 에 문제 없도록 제어해줄 때 사용되어질 거예욧!!

그리고 나오는 것은 패킷의 데이터를 담을 u_char * 자료형 data 변수가 선언되었어욤!!

이제 4일차 포스팅에서 드린 httpGet.pcap 파일 있지욤!? 이제 그 파일을 읽을거예요!!

우선 pcap_t * 자료형의 pcap 변수를 선언하고 pcap_open_offline() 함수로 초기화하네욧!


struct pcap {
    int fd;
    int snapshot;
    int linktype;
    int tzoff;      /* timezone offset */
    int offset;     /* offset for proper alignment */

    struct pcap_sf sf;
    struct pcap_md md;

    /*
     * Read buffer.
     */
    int bufsize;
    u_char *buffer;
    u_char *bp;
    int cc;

    /*
     * Place holder for pcap_next().
     */
    u_char *pkt;


    /*
     * Placeholder for filter code if bpf not in kernel.
     */
    struct bpf_program fcode;

    char errbuf[PCAP_ERRBUF_SIZE];
};


요 구조체가 pcap_t 자료형의 typedef 되어있는 실제 struct pcap 구조체예요! 이 구조체 변수를 pcap_open_offline() 함수로 초기화 해주는 것이죠!!

참 여러분 매개변수, 인수, 인자의 차이를 아시나욤? 전 한글로는 전부 매개변수이고 영어로만 다른 줄 알았더니 한글에서도 다르더군뇨!!


매개변수란 parameter 를 일컫는데, parameter 는 callee 에서의 입장이예요. 그리고 인수라고 부르기도, 인자라고 부르기도 하는 argument 가 있어요. 찾아보니 많은 사람들이 인자는 매개변수를 일컫는다고 하는데, 실제로는 전달인자라고 하여 인수라는 말에 적합합니다! argument 는 caller 에서의 입장이구요!

예를 들어 아래와 같은 코드가 있다고 하면,


int sum(int x, int y) {
    return x + y;
}

int main() {
    printf("sum: %d\n", sum(10, 20));
    return 0;
}


여기서 int sum() 함수 선언부의 int x, int y 를 매개변수(parameter)라고 하구요! main 함수에서 sum 함수를 호출할 때 10, 20 을 넘겨주었잖아요? 이것을 인수(인자, argument)라고 합니다! 호출하는 입장에서 argument 라고 하는것이고, 호출된 입장에서 parameter 라고 하는것이죵!!


흫흐흫 마크다운 너무 잘 사용하지 않나욤? 아 너무 뿌-듯하네욤 이런게 있다닛...

코드에서 pcap_open_offline() 함수의 인자값으로 "httpGet.pcap" 파일명과 에러 버퍼를 담을 errbuf 배열을 넘겨주었어요! "httpGet.pcap" 파일의 경우 지난번 포스팅에서 파일로 드렸을거예욥!! 같은 경로에 있지않으면 Full Path 를 적어주셔야한답니당~!

그리고 pcap 이 제대로 열렸는지 if(pcap == NULL) 예외처리를 해주어야 하는데 호곡.. 안해주었군뇨.. 야미의 불찰!


만약 파일명의 인자값으로 "-" 를 넣어주면 stdin 으로 인식한답니다!! 우리가 리눅스에서 pipeline 을 통해 | vi - 를 넘길 때 사용되는 "-" 는 stdin 인것이죵!!


이 저장된 파일을 읽어서 패킷을 분석할 수가 있게 되는거예욧!! 키야 와이어샤크같은걸 만든다니 너무도 황홀하기 그지없네욧 히히

이제 다음으로는 pcap_findalldevs_ex() 함수를 사용하는데, www.winpcap.org 의 documentation 을 보면 아직도 pcap_findalldevs() 함수로 예제가 되어있어욤.. 해당 함수는 예전 버전이라 취약해져서 현재 pcap library 에서는 deprecated 되어버렸습니당~!!


pcap_findalldevs_ex() 함수의 첫 번째 인자값으로는 2가지가 올 수 있어욤!

PCAP_SRC_FILE_STRING "file://"

PCAP_SRC_IF_STRING "rpcap://"

이 두 가지의 차이는 파일명 혹은 경로에서 가져오느냐, device 명을 가져오느냐에 대한 차이예욤!! 우리는 NIC를 가져와야 하니 아래 PCAP_SRC_IF_STRING 변수를 사용할검니당!! 해당 두 가지의 차이점은 출처를 드리지욥!!

(출처: https://www.winpcap.org/docs/docs_411/html/group__remote__source__string.html#g6d7103b8a7e1eca8c325bd8f32c361c3)


두 번째 인자값으로는 권한이예욤! 기본적으로 device 접근권한은 관리자(root)만 가능해야하므로 해당 프로그램을 root 로만 실행할 수 있도록 해야하거든요! 그게 아니라면 이 곳에 권한을 주어야 함니다! 테스트 환경에서는 관리자권한으로 테스트 할 것이니 해당 인자값은 NULL로 주겠습니당~!!


다음 인자값으로는 NIC 리스트를 가져오는 것이지욤!! 이제 이 인자값을 통해 가져온 리스트를 ->next 해보면서 모든 디바이스를 가져올거예욤!! 그리고 마지막 인자값은 에러 버퍼를 담당하는 것이구욧!!


이제 가져온 디바이스 리스트를 for loop 을 통해서 출력시켜줄 것인데, 어랏? 왜 ex 라는 변수를 사용하지? 그냥 위에서 가져온 alldevs 를 통해서 next 하면 안되는건감?

넵~! 안됩니당!! 해당 구조체는 next 만을 가지고 있기에 한 번 next 하게되면 다시 가져올 수가 없어요~!! 그러니 우리가 이 전 내역을 볼 수 있더라도 선택을 할 수 없게되니 대입하여 데이터를 복사해 사용하게 되는것이죵!!


오늘 너무 코드분석만 빡시게 나가는것같군뇸! 잠시 눈 정화가 필요하겠죠!? 고전 짤이지만..



예쁘면 다냐!! ㅋㅋㅋㅋ이것도 참 유명하죠!! 재밌어욤 귀여운 꼬마아이 둘... 키킼

이제 찾은 디바이스 명에 대해 scanf_s 를 통해 입력받고, 잘못된 인덱스로 가지 않도록 예외처리를 해준 후 디바이스 지정까지 끝나게 되어욤!! 야호 짝짝짝~ 이것이 우리 와이어샤크의 아래 사진과 같은 것이예욧!!



많이 보신 광경이죠?! 막 lo 도 있고.. wlan0 도 있고.. 바로 얘에요 얘!! 우리가 이것까지 지금 한거라구욤!! 꺄륵~

다음으로 while loop 가 나오네욧!! 드디어 데이터를... 패킷을 분석할 수 있는거신가 두준두준!


while 조건식에 있는 pcap_next_ex() 는 예상하셨다시피 pcap_next() 함수의 보완버전이예욤!! 우리가 지정한 pcap 을 통해(open_live 인지 파일인지 무엇인지 지정된 것, 그 외에도 필터기능, 문자열 파싱 기능, 모드 지정 기능 등 기능을 집어넣을 수도 있답니다!!) 헤더를 header 변수에, 패킷 데이터를 data 변수에 넣어주는 것이죠!!

pcap_next() 함수의 경우 return 값이 패킷이어서 data = pcap_next(pcap, &header) 와 같이 사용했는데, 이젠 함수에서 주소값에 직접 넣어주도록 바뀌었어욤 ㅎㅎ

가져온 패킷 데이터가 담긴 data 변수에서 각 프로토콜 헤더의 길이만큼 넘어가면 해당 프로토콜이 나오겠죠!? 그것을 해당하는 프로토콜의 구조체에 넣어준 후 출력만 시켜주면 끝!! 만약 패킷이 잘못 된 것 같다.. 싶으시면 직접 printf() 해보시면서 패킷 사이즈에 맞춰 헤더를 지정해주셔야 해욧!!


소켓 프로그래밍을 많이 안해보셨다면 여기서 또 난관이 하나 봉착되시겠군뇨!! htons() 는 또 무엇이고 inet_ntoa() 는 또 무엇인가..!!

이것은 네트워크의 Big-endian, Little-endian 주소지정방식부터, 소켓프로그래밍에 필요한 구조체 등을 알려드려야하니 Network 카테고리에 알려드릴게욤!! 글 쓰면 링크 첨부해놔야겠답 히히

이만큼 하면 저장된 파일에 대한 패킷 분석을 할 수 있게 되는검미다!! 우와 와이어샤크의 초기모델을 만들었다니 너무 뿌듯하지 않으신가욤!? 뿌듯한 마음과 함께 사이다 같은 유병재님을 보면서 이번 포스팅을 마쳐보겠습니당~!



ㅋㅋㅋㅋ넘 웃겨욤 정말 우리의 마음을 대리만족처럼 표현해주시는 것 같아욥 ㅋㅋㅋ 내말이 틀려 이 XX들아!!?

다음에는 캡쳐된 파일이 아닌 실제 live 패킷을 잡아 분석하는 포스팅으로 가겠습니당ㅎㅎ고생하셨습니다~!!


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

카테고리:

업데이트:

댓글남기기