[C 프로젝트] 패킷 분석기 Part 7. TCP 프로토콜 스택 분석 구현

안녕하세요. 언제나 휴일에 언휴입니다.

프로젝트에서 사용할 PCAP 파일 다운로드 (PCAP 파일이면 다른 파일도 관계 없어요.)

* 동영상 강의에서는 앞쪽 200개만 자른 파일로 시연하였습니다.

패킷 분석기 구현 프로젝트는 Tcpdump 유틸 등으로 수집한 pcap 파일을 분석하는 프로그램 제작입니다.

현재 PACP 파일 분석, ethernet 프로토콜 , IPv4 프로토콜 스택 정보를 출력하는 부분까지 구현한 상태입니다.

이번 강의는 TCP 프로토콜 스택 정보를 출력하는 부분을 구현합니다.

구현 과정은 동영상 강의를 참고하세요.

  • TCP.h
#pragma once
#include "PcapFile.h"

typedef struct _TCPHeader TCPHeader;
struct _TCPHeader
{
    ushort src_port;
    ushort dst_port;
    uint seqno;
    uint ackno;
    uchar hdlen;
    uchar fin : 1;
    uchar syn : 1;
    uchar rst : 1;
    uchar psh : 1;
    uchar ack : 1;
    uchar urg : 1;
    uchar reserved : 2;
    ushort winsize;
    ushort checksum;
    ushort upoint;
};

void ParseTCP(uchar* base, uint len);
  • TCP.c
#include "TCP.h"
void ParseTCP(uchar* base, uint len)
{
    TCPHeader* th = (TCPHeader*)base;
    printf("★★★    TCP 헤더  ★★★\n");
    printf("\tsrc port:%u\n", ntohs(th->src_port));
    printf("\tdest port:%u\n", ntohs(th->dst_port));
    printf("\tseq no:%u\n", ntohl(th->seqno));
    printf("\tack no:%u\n", ntohs(th->ackno));
    printf("\t");
    if (th->syn) { printf("SYN "); }
    if (th->ack) { printf("ACK "); }
    if (th->fin) { printf("FIN "); }
    if (th->rst) { printf("RST "); }
    if (th->psh) { printf("PSH "); }
    if (th->urg) { printf("URG "); }
    printf("\n");
    printf("\twindow size:%u\n", ntohs(th->winsize));
    printf("\turg pointer:%u\n", ntohs(th->upoint));
}
  • IPv4.h
#pragma once
#include "PcapFile.h"

#define PRO_ICMPv4 0x01
#define PRO_IGMP    0x02
#define PRO_TCP     0x06
#define PRO_UDP     0x11
#define PRO_OSPF    0x59

typedef struct _IPv4Header IPv4Header;
struct _IPv4Header
{    
    uchar hlen : 4;
    uchar version : 4;
    uchar tos;
    ushort tlen;
    ushort id;
    ushort fl_off;
#define DONT_FRAG(x) (x&0x40)
#define MORE_FRAG(x) (x&0x20)
#define FRAG_OFF(x) ntohs(x&0xFF1F)
    uchar ttl;
    uchar protocol;
    ushort checksum;
    uint srcaddr;
    uint dstaddr;
};

void ParseIPv4(uchar *base, uint len);
  • IPv4.c
#include "IPv4.h"
#include "TCP.h"

void PrintIPv4(uint addr)
{
    uchar *up = (uchar *)&addr;
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        printf("%d.", up[i]);
    }
    printf("%d", up[i]);

}
void ParseIPv4(uchar *base, uint len) 
{
    IPv4Header *iph = (IPv4Header *)base;
    printf("☆☆☆☆☆     IPv4 Header    ☆☆☆☆☆☆\n");
    printf("\n version:%d\n", iph->version);
    printf("\n hlen:%d bytes\n", iph->hlen * 4);
    printf("\t total length :%d bytes\n", ntohs(iph->tlen));
    printf("\t id:%d\n", ntohs(iph->id));

    if (DONT_FRAG(iph->fl_off))
    {
        printf("\t Don't Fragment\n");
    }
    if (MORE_FRAG(iph->fl_off))
    {
        printf("\t More Fragment\n");
    }
    printf("\t offset:%d bytes\n", FRAG_OFF(iph->fl_off));
    printf("\t Time To Live:%d\n", iph->ttl);
    printf("\t Protocol :%d\n", iph->protocol);
    printf("\t ICMP:%d IGMP:%d TCP:%d UDP:%d OSPF:%d\n", 
        PRO_ICMPv4, PRO_IGMP, PRO_TCP, PRO_UDP, PRO_OSPF);
    printf("\t src:");
    PrintIPv4(iph->srcaddr);
    printf("\t");
    PrintIPv4(iph->dstaddr);
    printf("\n");
    uchar *next = base + (iph->hlen * 4);
    len = len - (iph->hlen * 4);
    switch (iph->protocol)
    {
    case PRO_ICMPv4: printf("\t to be defined\n"); break;
    case PRO_TCP: ParseTCP(next,len);  break;
    default: printf("\t Not supported\n"); break;
    }
}
  • EHEther.h
#pragma once
#include "PcapFile.h"

#define L3_IPv4 0x0800
#define L3_ARP 0x0806

typedef struct  _EtherHeader EtherHeader;
struct  _EtherHeader
{
    uchar dst_mac[6];
    uchar src_mac[6];
    ushort l3type;
};

void ParseEther(uchar *buffer, uint len);
  • EHEther.c
#include "EHEther.h"
#include "IPv4.h"
void ViewEther(EtherHeader *eh);
void ParseEther(uchar *buffer, uint len)
{
    EtherHeader *eh = (EtherHeader *)buffer;
    uchar *next = buffer + sizeof(EtherHeader);
    len = len - sizeof(EtherHeader);

    ViewEther(eh);
    switch (ntohs(eh->l3type))
    {
    case L3_IPv4: ParseIPv4(next, len); break;
    case L3_ARP: printf("ARP: to be defined\n"); break;
    default: printf("Not support\n");	break;
    }
}

void ViewMac(const char *msg, uchar *base);
void ViewEther(EtherHeader *eh)
{
    printf("★★★★★     ethernet header    ★★★★★\n");
    ViewMac("dest", eh->dst_mac);
    ViewMac("source", eh->src_mac);
    printf("\tL3Type:%#x\n", ntohs(eh->l3type));
    printf("\t(IPv4:0x0800 ARP:0x0806)\n");
}
void ViewMac(const char *msg, uchar *base)
{
    printf("\t%s", msg);
    printf("%02x", base[0]);
    for (int i = 1; i < 6; i++)
    {
        printf(":%02x", base[i]);
    }
    printf("\n");
}
  • PcapFile.h
#pragma once
#include 
#pragma comment(lib,"ws2_32")
#include 
typedef unsigned int uint;
typedef unsigned short ushort;
typedef unsigned char uchar;

typedef struct _PFHeader PFHeader;
struct _PFHeader //패킷 파일 헤더
{
    uint magic;//0xA1B2C3D4
    ushort major;
    ushort minor;
    uint gmt_to_local;
    uint timestamp;
    uint max_caplen;
    uint linktype;
};

typedef struct _PACKETHEADER PackHeader;
struct _PACKETHEADER //패킷 헤더
{
    uint captime;//second
    uint caputime;//u second
    uint caplen;
    uint packlen;
};

#define PF_MAGIC 0xA1B2C3D4
#define LT_ETHER   0x01
void ParseEthers(FILE *fp);
int ParsePCapFile(FILE *fp, PFHeader *pfh);
  • PCapFile.c
#include "PcapFile.h"
#include "EHEther.h"
char buffer[0x100000];
void ViewPCapFile(PFHeader *pfh);
void ViewPacketHeaderInfo(PackHeader *ph, int pno);

void ParseEthers(FILE *fp)
{
    PackHeader ph = { 0 };
    int pno = 0;
    while (fread(&ph, sizeof(PackHeader), 1, fp) == 1)
    {
        pno++;
        ViewPacketHeaderInfo(&ph, pno);
        fread(buffer, sizeof(uchar), ph.caplen, fp);
        ParseEther(buffer, ph.caplen);
    }
}

int ParsePCapFile(FILE *fp, PFHeader *pfh)
{
    fread(pfh, sizeof(PFHeader), 1, fp);
    if (pfh->magic != PF_MAGIC)
    {
        return 0;
    }
    ViewPCapFile(pfh);
    return 1;
}
void ViewPCapFile(PFHeader *pfh)
{
    printf("=========== PCAP File 헤더 정보 ============\n");
    printf("\t 버전:%d.%d\n", pfh->major, pfh->minor);
    printf("\t최대 캡쳐 길이:%d bytes\n", pfh->max_caplen);
}
void ViewPacketHeaderInfo(PackHeader *ph, int pno)
{
    printf("!!! <%4d th> 프레임 !!!\n", pno);
    printf("패킷:%6d bytes 캡쳐:%6d\n", ph->packlen, ph->caplen);
}
  • Program.c
//https://ehpub.co.kr
#include 
#include 
#include 
#include "PcapFile.h"
int main()
{
    setlocale(LC_ALL, "KOREAN");
    char fname[256 + 1] = "";
    printf("분석할 pcap 파일명:");
    scanf_s("%s", fname, sizeof(fname));

    FILE *fp = 0;
    fopen_s(&fp, fname, "rb");
    if (fp == 0)
    {
        perror("파일 열기 실패");
        system("pause");
        return 0;
    }

    PFHeader pfh = { 0, };
    if (ParsePCapFile(fp, &pfh) == 0)
    {
        printf("PCAP 파일이 아닙니다\n");
        fclose(fp);
        system("pause");
        return 0;
    }

    switch (pfh.linktype)
    {
    case LT_ETHER: ParseEthers(fp); break;
    default: printf("Not Support\n"); break;
    }
    fclose(fp);
    system("pause");
    return 0;
}