2계층 프로토콜인 이더넷(Ethernet) 프로토콜의 구조를 알아보고 패킷을 분석 해보자
Ethernet Protocol Structure
위의 구조를 참고하여 헤더 부분만 정리해보면 아래와 같다.
Destination MAC Address |
6byte |
Source MAC Address |
6byte |
Ethernet Type |
2byte |
위의 순서대로 분석하면 데이터의 앞 14byte를 해석할 수 있을것이다. 위를 기반으로 analysis.h 를 새로 생성하여 이더넷 헤더를 해석해보자
나의 경우 u_char 자료형으로 모든 요소를 배열로 선언했다. 또한 이더넷 헤더 외에도 다른 헤더를 분석할 때도 배열로 선언할 것이므로 이러한 배열에 값을 넣고 출력하는 함수를 따로 제작하였다.
사용자 정의 함수
1 2 3 4 | void datacpy(u_char* src, const u_char* data, int size){ int i; for(i=0; i<size; i++) src[i] = data[i]; } | cs |
패킷데이터에서 변수에 값을 넣는 사용자 정의 함수이다. data에서 size만큼 src에 값을 복사한다.
1 2 3 | void dataprint(const u_char* data,int size){ while(size--) printf("%.2x",*data++); } | cs |
변수들을 printf의 %s로 메모리를 한번에 출력하면 이상한 문자들이 출력이 된다. 애초에 출력용 값들이 아니기 때문에 한글자씩 출력해주는 함수를 만들었다.
analysis.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | void datacpy(u_char* src, const u_char* data, int size){ int i; for(i=0; i<size; i++) src[i] = data[i]; } void dataprint(const u_char* data,int size){ while(size--) printf("%.2x",*data++); } void Ethernet_Header(const u_char* data){ printf("[Ethernet_Header]"); u_char dst_mac[6]; u_char src_mac[6]; u_char type[2]; datacpy(dst_mac, data, 6); data+=6; datacpy(src_mac, data, 6); data+=6; datacpy(type, data, 2); data+=2; printf("\n - Dst Mac Addr : "); dataprint(dst_mac,6); printf("\n - Src Mac Addr : "); dataprint(src_mac,6); printf("\n - Ethernet Type : "); dataprint(type,2); printf("\n"); } | cs |
sniffer.c
실제로 데이터가 잘 들어갔는지 확인하기 위해 원본데이터도 출력해보았다.
소스코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | #include<stdio.h> #include<pcap.h> #include<arpa/inet.h> #include"analysis.h" void callback(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes); int loop; int main(){ pcap_if_t *alldevsp; pcap_if_t *d; char errbuf[PCAP_ERRBUF_SIZE]; bpf_u_int32 netp; bpf_u_int32 maskp; int ret,i=1; struct in_addr addr; char *net; pcap_t *pcd; /* int pcap_findalldevs - pcap_if_t **alldevsp - char *errbuf */ printf("====== pcap_findalldevs ======\n"); ret = pcap_findalldevs(&alldevsp,errbuf); if(ret == -1){ printf("pcap_findalldev ERR : %s\n",errbuf); return -1; } for(d=alldevsp; d; d=d->next){ printf("%d. %s", i++, d->name); if(d->description){ printf("(%s)\n",d->description); }else{ printf("(None)\n"); } } printf("Select device number > "); scanf("%d",&i); i--; for(d=alldevsp;i>0;d=d->next, i--); /* int pcap_lookupnet - const char *device - bpf_u_int32 *netp - bpf_u_int32 *maskp - char *errbuf */ printf("====== pcap_lookupnet ======\n"); ret = pcap_lookupnet(d->name, &netp, &maskp, errbuf); if(ret==-1){ printf("pcap_lookupnet ERR : %s\n",errbuf); pcap_freealldevs(alldevsp); return -1; } printf("dev : %s\n",d->name); addr.s_addr = netp; net = inet_ntoa(addr); if(net ==NULL){ printf("inet_ntoa ERR"); pcap_freealldevs(alldevsp); return -1; } printf("netp : %s\n",net); addr.s_addr = maskp; net = inet_ntoa(addr); if(net==NULL){ printf("inet_ntoa ERR"); pcap_freealldevs(alldevsp); return -1; } printf("maskp : %s\n",net); /* pcap_t *pcap_open_live - const char *device - int snaplen - int promisc - int to_ms - char *errbuf */ printf("====== pcap_open_live ======\n"); pcd = pcap_open_live(d->name, 100000, 1, 1000, errbuf); if(pcd==NULL){ printf("pcap_open_live ERR : %s\n",errbuf); pcap_freealldevs(alldevsp); return -1; } printf("File Descriptor : %d\n",pcap_fileno(pcd)); pcap_freealldevs(alldevsp); /* int pcap_loop - pcap_t *p - int cnt - pcap_handler callback - u_char *user */ printf("======== pcap_loop ========\n"); pcap_loop(pcd, 0, callback, NULL); pcap_close(pcd); return 0; } void callback(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes){ int i=0; loop++; printf("\n=============== Packet no.%d(%4d byte) ===============\n",loop,h->len); dataprint(bytes,h->len); printf("\n"); Ethernet_Header(bytes); } | cs |
출력결과
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | root@g0pher-virtual-machine:/home/g0pher/complete# ./sniffer ====== pcap_findalldevs ====== 1. ens33(None) 2. any(Pseudo-device that captures on all interfaces) 3. lo(None) 4. nflog(Linux netfilter log (NFLOG) interface) 5. nfqueue(Linux netfilter queue (NFQUEUE) interface) 6. usbmon1(USB bus number 1) 7. usbmon2(USB bus number 2) Select device number > 1 ====== pcap_lookupnet ====== dev : ens33 netp : 192.168.233.0 maskp : 255.255.255.0 ====== pcap_open_live ====== File Descriptor : 3 ======== pcap_loop ======== =============== Packet no.1( 122 byte) =============== 005056c00008000c29aad30b08004510006cc57540004006212fc0a8e984c0a8e90100162407d989011e630ae09f501801275436000000000030e1234a69e45910070b0f87b6908a95a8829e83f2986724ee790be147257a6eee12cda477fb104551c0985605020b8057018e4466c582f8fbffeccb254a666bfa [Ethernet_Header] - Dst Mac Addr : 005056c00008 - Src Mac Addr : 000c29aad30b - Ethernet Type : 0800 =============== Packet no.2( 60 byte) =============== 000c29aad30b005056c000080800450000281bcf00008006cb29c0a8e901c0a8e98424070016630ae09fd989016250100100184a0000000000000000 [Ethernet_Header] - Dst Mac Addr : 000c29aad30b - Src Mac Addr : 005056c00008 - Ethernet Type : 0800 =============== Packet no.3( 90 byte) =============== 005056c00008000c29aad30b08004510004cc57640004006214ec0a8e984c0a8e90100162407d9890162630ae09f50180127541600000000001070d9fe5cde1b2c4097d4c0f0822e126280d353cce763a162bc63a86cc55a60ed [Ethernet_Header] - Dst Mac Addr : 005056c00008 - Src Mac Addr : 000c29aad30b - Ethernet Type : 0800 | cs |
헤더에 대한 분류가 잘 되었음을 확인해볼 수 있다.
2계층에서 이더넷 헤더를 분류했으니 이제 상위계층의 헤더를 파악해야 한다. 그러나 상위계층에 존재하는 그 많은 프로토콜중 해당 패킷이 어떤 프로토콜을 사용하는지 어떻게 알 수 있을까?
Ethernet Type
이더넷 헤더의 요소중 하나인 Ethernet Type가 상위계층의 프로토콜을 지칭한다.
Ethernet Type Data(HEX) |
Protocol |
0800 |
Internet Portocol(IP) |
0806 |
Address Resolution Protocol(ARP) |
86DD |
Internet Protocol Version 6(IPv6) |
이 3개만 알아도 대부분의 패킷을 분석할 수 있다. 더 많은 프로토콜의 이더넷 타입값을 알고 싶다면 "http://www.cavebear.com/archive/cavebear/Ethernet/type.html" 여기서 확인해 보면 좋을것 같다.