PCAP Programming

수갱22·2024년 4월 6일
0

WHS

목록 보기
1/4

PCAP Prgramming

• C, C++ 기반 PCAP API를 활용하여 PACKET의 정보를 출력하는 프로그램 작성
• Ethernet Header: src mac / dst mac
• IP Header: src ip / dst ip
• TCP Header: src port / dst port
• Message도 출력하면 좋음. (적당한 길이로)
• TCP protocol 만을 대상으로 진행 (UDP는 무시), sniff_improved.c, myheader.h 코드 참고
• *IP header, tcp header 에 있는 길이 정보를 잘 사용할 것. (ip_header_len)

myheader.h


/* Ethernet header */
struct ethheader {
    u_char  ether_dhost[6];    /* destination host address */
    u_char  ether_shost[6];    /* source host address */
    u_short ether_type;                     /* IP? ARP? RARP? etc */
};

/* IP Header */
struct ipheader {
  unsigned char      iph_ihl:4, //IP header length
                     iph_ver:4; //IP version
  unsigned char      iph_tos; //Type of service
  unsigned short int iph_len; //IP Packet length (data + header)
  unsigned short int iph_ident; //Identification
  unsigned short int iph_flag:3, //Fragmentation flags
                     iph_offset:13; //Flags offset
  unsigned char      iph_ttl; //Time to Live
  unsigned char      iph_protocol; //Protocol type
  unsigned short int iph_chksum; //IP datagram checksum
  struct  in_addr    iph_sourceip; //Source IP address
  struct  in_addr    iph_destip;   //Destination IP address
};

/* ICMP Header  */
struct icmpheader {
  unsigned char icmp_type; // ICMP message type
  unsigned char icmp_code; // Error code
  unsigned short int icmp_chksum; //Checksum for ICMP Header and data
  unsigned short int icmp_id;     //Used for identifying request
  unsigned short int icmp_seq;    //Sequence number
};

/* UDP Header */
struct udpheader
{
  u_int16_t udp_sport;           /* source port */
  u_int16_t udp_dport;           /* destination port */
  u_int16_t udp_ulen;            /* udp length */
  u_int16_t udp_sum;             /* udp checksum */
};

/* TCP Header */
struct tcpheader {
    u_short tcp_sport;               /* source port */
    u_short tcp_dport;               /* destination port */
    u_int   tcp_seq;                 /* sequence number */
    u_int   tcp_ack;                 /* acknowledgement number */
    u_char  tcp_offx2;               /* data offset, rsvd */
#define TH_OFF(th)      (((th)->tcp_offx2 & 0xf0) >> 4)
    u_char  tcp_flags;
#define TH_FIN  0x01
#define TH_SYN  0x02
#define TH_RST  0x04
#define TH_PUSH 0x08
#define TH_ACK  0x10
#define TH_URG  0x20
#define TH_ECE  0x40
#define TH_CWR  0x80
#define TH_FLAGS        (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
    u_short tcp_win;                 /* window */
    u_short tcp_sum;                 /* checksum */
    u_short tcp_urp;                 /* urgent pointer */
};

/* Psuedo TCP header */
struct pseudo_tcp
{
        unsigned saddr, daddr;
        unsigned char mbz;
        unsigned char ptcl;
        unsigned short tcpl;
        struct tcpheader tcp;
        char payload[1500];
};
  • myheader.h에서 정의한 헤더 구조체들을 통해서 각각 ethernet, IP, TCP 정보를 확인한다.

sniff_improved.c

#include <stdlib.h>
#include <stdio.h>
#include <pcap.h>
#include <arpa/inet.h>


/* Ethernet header */
struct ethheader {
  u_char  ether_dhost[6]; /* destination host address */
  u_char  ether_shost[6]; /* source host address */
  u_short ether_type;     /* protocol type (IP, ARP, RARP, etc) */
};

/* IP Header */
struct ipheader {
  unsigned char      iph_ihl:4, //IP header length
                     iph_ver:4; //IP version
  unsigned char      iph_tos; //Type of service
  unsigned short int iph_len; //IP Packet length (data + header)
  unsigned short int iph_ident; //Identification
  unsigned short int iph_flag:3, //Fragmentation flags
                     iph_offset:13; //Flags offset
  unsigned char      iph_ttl; //Time to Live
  unsigned char      iph_protocol; //Protocol type
  unsigned short int iph_chksum; //IP datagram checksum
  struct  in_addr    iph_sourceip; //Source IP address
  struct  in_addr    iph_destip;   //Destination IP address
};

void got_packet(u_char *args, const struct pcap_pkthdr *header,
                              const u_char *packet)
{
  struct ethheader *eth = (struct ethheader *)packet;

  if (ntohs(eth->ether_type) == 0x0800) { // 0x0800 is IP type
    struct ipheader * ip = (struct ipheader *)
                           (packet + sizeof(struct ethheader)); 

    printf("       From: %s\n", inet_ntoa(ip->iph_sourceip));   
    printf("         To: %s\n", inet_ntoa(ip->iph_destip));    

    /* determine protocol */
    switch(ip->iph_protocol) {                                 
        case IPPROTO_TCP:
            printf("   Protocol: TCP\n");
            return;
        case IPPROTO_UDP:
            printf("   Protocol: UDP\n");
            return;
        case IPPROTO_ICMP:
            printf("   Protocol: ICMP\n");
            return;
        default:
            printf("   Protocol: others\n");
            return;
    }
  }
}

int main()
{
  pcap_t *handle;
  char errbuf[PCAP_ERRBUF_SIZE];
  struct bpf_program fp;
  char filter_exp[] = "icmp";
  bpf_u_int32 net;

  // Step 1: Open live pcap session on NIC with name enp0s3
  handle = pcap_open_live("enp0s3", BUFSIZ, 1, 1000, errbuf);

  // Step 2: Compile filter_exp into BPF psuedo-code
  pcap_compile(handle, &fp, filter_exp, 0, net);
  if (pcap_setfilter(handle, &fp) !=0) {
      pcap_perror(handle, "Error:");
      exit(EXIT_FAILURE);
  }

  // Step 3: Capture packets
  pcap_loop(handle, -1, got_packet, NULL);

  pcap_close(handle);   //Close the handle
  return 0;
}

  • sniff_improve.c를 통해 사용된 기본 패킷 분석 로직 확장함->pcap lib 함수, 필터 사용 등 참고함.

네트워크 인터페이스를 확인하기 위해 ifconfig로 확인하면 ens33인 것을 확인할 수 있다.

Code 및 자세한 설명

#include <stdio.h>
#include <pcap.h>               // pcap lib 헤더, 패킷 캡처 및 네트워크 분석 기능 제공
#include <arpa/inet.h>          // IP 주소 처리 함수 제공
#include <netinet/if_ether.h>   // Ethernet 프레임 처리를 위한 함수 및 구조체 정의
#include <netinet/ip.h>         // IP 패킷 처리를 위한 구조체 정의 
#include <netinet/tcp.h>        // TCP 세그먼트 처리를 위한 구조체 정의 

// 패킷 캡쳐시 호출되는 콜백 함수 정의
void got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) {
    // 캡처된 패킷에서 Ethernet 헤더 추출
    const struct ether_header *eth = (struct ether_header *)packet;

    // Ethernet 헤더에서 소스 및 목적지 MAC 주소를 추출하고, 사람이 읽을 수 있는 형태로 변환하여 출력
    printf("Src MAC: %s, Dst MAC: %s\n", ether_ntoa((struct ether_addr *)&eth->ether_shost), ether_ntoa((struct ether_addr *)&eth->ether_dhost));

    // 패킷이 IP 패킷인지 확인 (Ethernet Type이 IP인 경우)
    if (ntohs(eth->ether_type) == 0x0800) {
        // Ethernet 헤더 이후의 데이터에서 IP 헤더 추출
        const struct ip *ip = (struct ip *)(packet + sizeof(struct ether_header));

        // IP 헤더에서 소스 및 목적지 IP 주소 추출 및 출력
        printf("From: %s, To: %s\n", inet_ntoa(ip->ip_src), inet_ntoa(ip->ip_dst));

        // 패킷이 TCP 프로토콜을 사용하는지 확인
        if (ip->ip_p == IPPROTO_TCP) {
            // IP 헤더 이후의 데이터에서 TCP 헤더 추출
            const struct tcphdr *tcp = (struct tcphdr *)(packet + sizeof(struct ether_header) + ip->ip_hl * 4);

            // TCP 헤더에서 소스 및 목적지 포트 번호 추출 및 출력
            printf("Protocol: TCP, Src port: %d, Dst port: %d\n", ntohs(tcp->th_sport), ntohs(tcp->th_dport));

            // TCP 페이로드 크기 계산->len 이용
            int ip_header_len = ip->ip_hl * 4;
            int tcp_header_len = tcp->th_off * 4;
            int payload_len = ntohs(ip->ip_len) - ip_header_len - tcp_header_len;
            // TCP 페이로드 시작 위치 계산
            const char *payload = (const char *)(packet + sizeof(struct ether_header) + ip_header_len + tcp_header_len);

            // 페이로드가 존재하면, 16진수 형태로 출력
            if (payload_len > 0) {
                printf("Payload (%d bytes):\n", payload_len);
                for(int i = 0; i < payload_len; i++) {
                    printf("%02x ", (unsigned char)payload[i]);
                    if ((i + 1) % 16 == 0) printf("\n"); // 16바이트마다 줄바꿈
                }
                printf("\n\n");
            }
        }
    }
}

int main() {
    char errbuf[PCAP_ERRBUF_SIZE]; // 에러 메시지를 저장하기 위한 버퍼
    pcap_t *handle; // 패킷 캡처 세션 핸들

    // 지정된 네트워크 인터페이스에서 패킷 캡처를 위한 pcap 세션을 엽니다.
    handle = pcap_open_live("ens33", BUFSIZ, 1, 1000, errbuf);
    if (handle == NULL) {
        fprintf(stderr, "Couldn't open device ens33: %s\n", errbuf);
        return 2;
    }

    // TCP 패킷만 캡처하기 위한 패킷 필터링
    struct bpf_program fp;
    char filter_exp[] = "tcp"; //tcp만 필터링
    if (pcap_compile(handle, &fp, filter_exp, 0, PCAP_NETMASK_UNKNOWN) == -1 || pcap_setfilter(handle, &fp) == -1) { //필터 적용
        fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle));
        pcap_close(handle); // 필터 적용 실패시 세션 종료
        return 2;
    }

    // 무한 루프 내에서 패킷 캡처 시작, 새로운 패킷이 캡처될 때마다 got_packet 콜백 함수 호출
    pcap_loop(handle, -1, got_packet, NULL);

    // 모든 작업 완료 후 pcap 세션 닫기
    pcap_close(handle);
    return 0;
}

추가 내용

  • 위와 같이 일부 데이터들이 글씨가 깨지는 문제가 발생함-> 바이너리 데이터를 16진수로 출력

해결 후 코드 실행 시 캡쳐된 패킷'

github주소: https://github.com/sooglasses64/WHS.git

profile
Cyber Security

0개의 댓글

관련 채용 정보