for() */ 패킷 처리 소스
Download
Report
Transcript for() */ 패킷 처리 소스
Libpcap & Libnet
Sungjin OK
By PresenterMedia.com
ON TARGET
INDEX
BPF
Libpcap function
Libpcap API
Libpcap 예제
Libnet
Libnet 예제
ON TARGET
BPF 구조
Berkeley Packet Filter 구조
tcpdump
유저 프로세스들~
sniffer
ngrep
TCP, UDP
IP, ICMP, IGMP
필터 필터 필터
BPF 드라이버
복사 받은 패킷
보낸 패킷
Ethernet
device driver
ON TARGET
패킷 캡쳐
패킷을 캡쳐하는 순서
- 1. 네트웍 인터페이스 명칭 지정
- 2. 네트웍 인터페이스 디스크립터 얻음
- 3. 필터룰을 컴파일 시킴
- 4. 컴파일된 필터룰을 네트웍 인터페이스에 적용
- 5. 패킷을 모으고, 패킷에 따른 프로세싱
ON TARGET
Libpcap 사용
헤더파일을 포함시킴
#include <pcap.h>
/* or */
#include <pcap/pcap.h>
컴파일 방법
# gcc 소스.c -lpcap
/* version 0.2.6-9 */
ON TARGET
Libpcap function
Char* Pcap_lookdev();
Device = pcap_lookupdev(ebuf);
네트웍 장치를 가져오는 함수
가능한 장치중 가장 번호가 낮은 장치를 가져오게 된다
리눅스라면 eth0 같은
다른 장치를 통해 캡쳐하려면 직접 장치명을 써준다
Device = “wlan0”;
Pcap_t Pcap_open_live();
Pd = pcap_open_live(device, snlen, promiscuous, time, ebuf);
실제 기기를 사용할수 있도록 열어주는 함수
Device : 네트웍장치
Snlen : 패킷당 저장할 바이트크기 (실제 패킷에서 가져오고 싶은 크기)
Promiscuous : promiscuous 패킷의 저장 여부 1이면 저장 0이면 무시
Time : 패킷대기 시간
패킷이 버퍼로 전달될 때 바로 전달되는 것이 아님
명시한 시간을 넘겼을때 나 버퍼가 다 채워질경우만 전달됨
단위는 milliseconds
ON TARGET
Libpcap function
pcap_lookupnet(device, &localnet, &netmask, ebuf);
열려진 패킷 캡쳐 장치에 네트웍 주소와 서브넷 마스크를 넘겨주는 함수
성공 여부를 0 또는 -1로 리턴
localnet : 네트웍 주소(ip) 를 저장하기 위한 공간
Netmask : 서브넷 마스크를 저장하기 위한 공간
크기는 Uint32 정도로 구성(ipv4의 경우)
Pcap_compile(pd, &fcod, filter_rule, optimize, netmask);
우리가 원하는 패킷을 보기위해서는 필터를 거쳐야 한다
해당 필터룰(tcpdump 의 필터) 프로그램으로 사용하기 위해 컴파일 하는 과정
Struct bpf_program *fcod : 컴파일 결과
Filter_rule : tcpdump필터 룰을 따른 문자열
Int optimize : 최적화 여부
Netmask : 해당 장치의 netmask
ON TARGET
Libpcap function
Pcap_setfilter()
pcap_setfilter(pd, &fcode);
컴파일한 필터를 지정해 주는데 사용된다
pcap_datalink()
printer = lookup_printer(pcap_datalink(pd));
패킷 캡쳐 디바이스의 datalink계층의 종류를 넘겨 받아 이에 따른 적절한 함수포인터를 할당하게 됩니
다.
if (pcap_datalink (handle) == DLT_EN10MB)
{
wired = 1; /* ethernet link */
} else {
if (pcap_datalink (handle) == DLT_IEEE802_11_RADIO_AVS)
{
wired = 0; /* wireless */
} else {
fprintf (stderr, "I don't support this interface type!\n");
exit (1);
}
}
ON TARGET
Libpcap function
Pcap_loop()
pcap_loop(pd, packetcnt, printer, pcap_userdata)
실제 패킷을 잡아서 실행할 함수를 지정해 주는 함수
packetcnt의 수만큼 패킷을 잡아서 잡을 때 마다 해당 패킷을 printer가 포인터하는 함수에게 전
달하고 함수를 수행
packetcnt를 0으로 지정하면 무한대로 함수를 실행합니다.
Userdata : 핸들러 함수로 넘어갈 때의 데이터
Pcap_next()
함수형 포인터를 사용하지 않고 함수를 호출할 때마다 패킷을 리턴해주는 함수로 앞의 pcap_datalink()
와 pcap_loop()를 대체할 수 있습니다.
ON TARGET
Libpcap API 예제
pcap_t *pdevice;
char *device;
………
if (device == NULL) {
if ((device = pcap_lookupdev(err_buffer)) == NULL) {
perror(err_buffer);
exit(1);
}
}
…………
if ((pdevice = pcap_open_live(device, snaplen, PROMISCUOUS, to_ms, err_buffer))
== NULL) {
fprintf(stdout, "\nerror : pcap_open_live()\n");
perror(err_buffer);
exit(1);
}
ON TARGET
Libpcap API 예제
………………
struct bpf_program fcode;
………………
if (pcap_compile(pdevice, &fcode, filter_rule, 0, netmask) < 0) {
fprintf(stdout, "%s\n", pcap_geterr(pdevice));
exit(1);
유저 프로세스들~
}
tcpdump
sniffer
ngrep
if (pcap_setfilter(pdevice, &fcode) < 0) {
fprintf(stdout, "%s\n", pcap_geterr(pdevice));
exit(1);
}
필터 필터 필터
if (pcap_loop(pdevice, packetcnt, handler_pcap, pcap_userdata) < 0) {
BPF 드라이버
fprintf(stdout, "%s\n", pcap_geterr(pdevice));
exit(1);
}
ON TARGET
패킷 캡쳐 부분
#include <pcap/pcap.h>
#define PROMISCUOUS 1
/* 모든 패킷에 대하여 수신 모드 */
int main(int argc, char *argv[]) {
bpf_u_int32 localnet, netmask;
pcap_t *pdevice;
char *device = NULL;
char *filter_rule;
/* unsigned 32bit int */
/* 장치에 대한 정보가 저장될 구조체 */
/* 장치명 */
/* 필터룰 문자열 */
char err_buffer[PCAP_ERRBUF_SIZE];
struct pcap_stat ps;
/* pcap_stats() */
ON TARGET
패킷 캡쳐 부분
int snaplen = 1514; /* 버퍼에 캡쳐될 패킷 최대 크기를 결정 한다. */
int to_ms = 1000; /* 버퍼가 채워지길 기다리는 시간 (ms) */
int snapshotlen;
/* pcap_snapshot() 에서 리턴 값 */
int packetcnt = -1; /* 캡쳐될 패킷 카운트 default : 무한값 */
int datalink;
/* 데이터링크 타입 저장, pcap_datalink() */
unsigned char *pcap_userdata;
struct bpf_program fcode;
/* 컴파일된 필터룰이 저장될 구조체 */
filter_rule = argv[1];
/* ex) src host xxx.xxx.xxx.xxx and tcp port 80 */
이더넷 프레임
이더넷 헤더
14 bytes
IP 헤더
TCP 헤더
애플리케이션
데이타
46 ~ 1500 bytes
이더넷
트레일러
ON TARGET
패킷 캡쳐 부분
/* 장치 명칭이(eth0 or eth1...) 결정되지 않았을 경우에 자동으로 할당 한다. */
if (device == NULL) {
/* pcap_lookupnet(), pcap_open_live() 에서 사용될
네트웍 디바이스에 대한 포인터를 리턴한다. */
/* 함수 호출시 에러가 발생하면 NULL 이 리턴되며
errbuf 에 적절한 에러메세지가 담겨진다. */
if ((device = pcap_lookupdev(err_buffer)) == NULL) {
fprintf(stdout, "\nerror : pcap_lookupdev()\n");
perror(err_buffer);
exit(1);
}
}
ON TARGET
패킷 캡쳐 부분
/* 네트워크 상에서 패킷을 인식하기 위한 패킷 캡쳐 디스크립터를 얻는데 사용된다. */
/*
/*
/*
/*
/*
device 는 open 시킬 네트워크 디바이스를 가리키는 문자열 */
snaplen 는 캡쳐할 최대 바이트 수를 나타낸다. */
PROMISCUOUS 는 인터페이스가 PROMISCUOUS 모드 상태로 전환하기위한 값 */
to_ms 는 버퍼가 채워지길 기다리는 시간을 milliseconds 단위로 설정한 것이다. */
err_buffer 는 NULL 이 리턴될 경우 에러메세지가 저장된다. */
if ((pdevice = pcap_open_live(device, snaplen, PROMISCUOUS, to_ms, err_buffer))
== NULL) {
fprintf(stdout, "\nerror : pcap_open_live()\n");
perror(err_buffer);
exit(1);
}
ON TARGET
패킷 캡쳐 부분
/* pcap_open_live() 가 호출될때 지정된 snapshot 의 길이를 리턴한다. */
if ((snapshotlen = pcap_snapshot(pdevice)) > snaplen) {
fprintf(stdout, "\nerror : pcap_snapshot()\n");
fprintf(stdout, "%s\n", pcap_geterr(pdevice));
exit(1);
}
fprintf(stdout, "snapshotlen : %d\n", snapshotlen);
ON TARGET
패킷 캡쳐 부분
/* 네트워크 디바이스와 관계된 네트워크 넘버, 서브넷 마스크 등을 결정 */
/* localnet, netmask 는 각각 네트워크 넘버와 서브넷 마스크를 나타내고 bpf_u_int32
타입이다. */
/* 에러가 발생하면 1 을 리턴한다. */
if (pcap_lookupnet(device, &localnet, &netmask, err_buffer) < 0) {
fprintf(stdout, "\nerror : pcap_lookupnet()\n");
perror(err_buffer);
exit(1);
}
fprintf(stdout, "localnet : %s\n", inet_ntoa(localnet));
fprintf(stdout, "netmask : %s\n", inet_ntoa(netmask));
ON TARGET
패킷 캡쳐 부분
fprintf(stdout, "filter_rule = %s\n", filter_rule);
/* pcap_compile 함수는 filter_rule 문자열을 필터 프로그램에 컴파일 시킴 */
/* fcode 는 필터 프로그램의 포인터이다. struct bpf_program */
/* 네번째 인수인 0 는 optimize 로서 필터 프로그램의 결과 코드의 최적화를 조절 */
if (pcap_compile(pdevice, &fcode, filter_rule, 0, netmask) < 0) {
fprintf(stdout, "\nerror : pcap_compile()\n");
fprintf(stdout, "%s\n", pcap_geterr(pdevice));
exit(1);
}
필터 필터 필터
BPF 드라이버
ON TARGET
패킷 캡쳐 부분
/* 컴파일된 필터 프로그램을 지정한다. */
/* fcode 는 bpf_program struct 의 배열을 가리키는 포인터이다. */
/* 함수 실패시 -1 을 리턴하며, 성공시 0 이 리턴된다. */
if (pcap_setfilter(pdevice, &fcode) < 0) {
fprintf(stdout, "\nerror : pcap_setfilter()\n");
fprintf(stdout, "%s\n", pcap_geterr(pdevice));
exit(1);
}
유저 프로세스들~
tcpdump
sniffer
ngrep
필터 필터 필터
BPF 드라이버
ON TARGET
패킷 캡쳐 부분
/* 데이터링크의 타입을 구해온다. */
datalink = pcap_datalink(pdevice);
/* bpf.h 헤더파일 참조 */
/* 패킷을 모으고 프로세싱을 한다. */
/* packetcnt 는 캡쳐될 패킷의 갯수를 나타내며, -1 일때는 무한 루프, 0 이면 에러가
발생하거나 데이터의 끝일때까지 모든 패킷을 처리한다. */
/* pcap_userdata 는 u_char 형으로 사용자가 임의로 넣을 수 있다. */
/* handler_pcap 는 패킷을 처리할 핸들러 함수 이다. */
/* handler_pcap 로 네 번 째 인 수 인 pcap_userdata, pcap_pkthdr 구 조 체 에 대 한
포인터(실제 네트웍 헤더와 데이터의 첫부분을 가리킴), 패킷 데이터에 대한 u_char
형 포인터, 이상 3개의 인수가 전달 된다. */
if (pcap_loop(pdevice, packetcnt, handler_pcap, pcap_userdata) < 0) {
fprintf(stdout, "\nerror : pcap_loop()\n");
fprintf(stdout, "%s\n", pcap_geterr(pdevice));
exit(1);
}
ON TARGET
패킷 캡쳐 부분
/* 패킷 캡쳐가 시작된 이후부터 이 함수가 호출될 시점까지의 패킷 캡쳐의 통계치를
구한다. */
/* 두번째 인수 pcap_stat 구조체에 통계치를 저장한다. */
/* 실패시 -1 을 리턴한다. */
/* 단, 한번더 호출시 전 통계치는 리셋 */
if (pcap_stats(pdevice, &ps) < 0) {
fprintf(stdout, "\nerror : pcap_stats()\n");
fprintf(stdout, "%s\n", pcap_geterr(pdevice));
exit(1);
}
fprintf(stdout, "received : %u, dropped : %u\n", ps.ps_recv, ps.ps_drop);
/* 구조체 pcap 에 사용된 파일과 메모리를 해제한다. */
pcap_close(pdevice);
ON TARGET
Pcap_handler
pcap_handler
- 패킷을 수신시 패킷의 처리를 담당하는 함수
- pcap_loop() 함수에서 handler 함수명을 지정해 주어야 함
/* pcap_loop() 에 의해 패킷을 잡을 때마다 불리어지는 함수 */
void handler_pcap(unsigned char *user, const struct pcap_pkthdr *h,
const unsigned char *p) {
/* 사용자 인수 인 user, 패킷 데이터에 대한 u_char 형 포인터 p */
/* pcap_pkthdr 구조체에 대한 포인터 */
…………
return;
}
ON TARGET
패킷 처리
ethernet 헤더
- net/ehternet.h 중에서
/* 10Mb/s ethernet header */
struct ether_header {
u_int8_t ether_dhost[ETH_ALEN];
/* destination eth addr */
u_int8_t ether_shost[ETH_ALEN];
/* source ether addr */
u_int16_t ether_type;
/* packet type ID field */
};
이더넷 프레임
이더넷 헤더
14 bytes
IP 헤더
TCP 헤더
애플리케이션
데이타
이더넷
트레일러
ON TARGET
패킷 처리
IP 헤더
- netinet/ip.h 중에서
struct iphdr {
unsigned int version:4;
unsigned int ihl:4;
u_int8_t tos; /* type of service */
u_int16_t tot_len;
//전체의 길이
u_int16_t id;
u_int16_t frag_off;
/* 3-bit flags 포함 */
u_int8_t ttl;
u_int8_t protocol;
u_int16_t check;
IP 데이터그램
u_int32_t saddr;
u_int32_t daddr;
};
TCP 헤더
IP 헤더
20 bytes
애플리케이션
데이타
ON TARGET
패킷 처리
ICMP 헤더
- netinet/ip_icmp.h 중에서
struct icmp {
u_int8_t icmp_type;
u_int8_t icmp_code;
u_int16_t icmp_cksum;
/* type of message, see below */
/* type sub code */
/* ones complement checksum of struct */
union {
………….
}
ICMP 메세지
};
IP 헤더
ICMP 헤더
8 bytes
IP header of datagram
That generated error
UDP 헤더
ON TARGET
패킷 처리
TCP 헤더
- netinet/tcp.h 중에서
struct tcphdr {
u_int16_t source;
u_int16_t dest;
u_int32_t seq;
u_int32_t ack_seq;
u_int16_t doff:4; //header length
u_int16_t res1:4;
u_int16_t res2:2;
u_int16_t window;
u_int16_t check;
u_int16_t urg_ptr;
};
u_int16_t urg:1;
u_int16_t ack:1;
u_int16_t psh:1;
u_int16_t rst:1;
u_int16_t syn:1;
u_int16_t fin:1;
IP 헤더
TCP 세그먼트
TCP 헤더
20 bytes
TCP 데이터
ON TARGET
패킷 처리
UDP 헤더
- netinet/udp.h 중에서
struct udphdr {
u_int16_t
u_int16_t
u_int16_t
u_int16_t
};
source;
dest;
len;
check;
UDP 데이터그램
IP 헤더
UDP 헤더
8 bytes
UDP 데이터
ON TARGET
패킷 처리
ARP 헤더
- netinet/if_ether.h 중에서
struct ether_arp {
struct arphdr ea_hdr;
/* fixed-size header */
u_int8_t arp_sha[ETH_ALEN];
/* sender hardware address */
u_int8_t arp_spa[4];
/* sender protocol address */
u_int8_t arp_tha[ETH_ALEN];
/* target hardware address */
u_int8_t arp_tpa[4];
/* target protocol address */
};
ARP 리퀘스트/리플라이
이더넷 헤더
ARP 헤더
28 bytes
ON TARGET
패킷 처리 소스
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <netinet/if_ether.h>
/*
/*
/*
/*
/*
/*
이더넷 */
IP 헤더 */
TCP 헤더 */
UDP 헤더 */
ICMP 헤더 */
ARP 헤더 */
/* pcap_loop() 에 의해 패킷을 캡쳐할 때마다 불리어지는 함수 */
void handler_pcap(unsigned char *user, const struct pcap_pkthdr *h,
const unsigned char *p) {
/* 사용자 인수 인 user, 패킷 데이터에 대한 u_char 형 포인터 p */
/* pcap_pkthdr 구조체에 대한 포인터 */
ON TARGET
패킷 처리 소스
/* 헤더 구조체, netinet 폴더에 *.h 참조 */
struct ether_header *eh;
struct iphdr *iph;
struct tcphdr *tcph;
struct udphdr *udph;
struct icmp *icmph;
struct ether_arp *arph;
unsigned char *tcpdata;
unsigned char *udpdata;
unsigned char *icmpdata;
register int i, j;
/* for() */
/* net/ethernet.h 참조 */
ON TARGET
패킷 처리 소스
/* 이더넷 헤더 맵핑 */
eh = (struct ether_header *) p;
/* 데이터링크 레이어 */
/* ================== 데이터링크 레이어 ================== */
for (i = 0; i < ETH_ALEN; i++) {
/* ETH_ALEN = 6, linux/if_ether.h 참조 */
fprintf(stdout, "%02x", eh->ether_shost[i]); /* 출발지 MAC 주소 */
if (i != 5) {
fprintf(stdout, ":");
}
}
fprintf(stdout, " ----> ");
이더넷 프레임
이더넷 헤더
14 bytes
IP 헤더
TCP 헤더
애플리케이션
데이타
이더넷
트레일러
ON TARGET
패킷 처리 소스
for (i = 0; i < ETH_ALEN; i++) {
fprintf(stdout, "%02x", eh->ether_dhost[i]); /* 목적지 MAC 주소 */
if (i != 5) {
fprintf(stdout, ":");
}
}
fprintf(stdout, "\n");
fprintf(stdout, "Ether_type: %x\n", ntohs(eh->ether_type)); /* 16 비트 */
ON TARGET
패킷 처리 소스
/* IP 헤더 */
IP 데이터그램
IP 헤더
TCP 헤더
if (ntohs(eh->ether_type) == ETHERTYPE_IP) {
/* IP 헤더 맵핑 */
iph = (struct iphdr *) (sizeof(struct ether_header) + p);
/* IP 헤더로 포인터를 맞춤 */
20 bytes
/* ==================
IP 헤더
================== */
fprintf(stdout, "%s ---->", inet_ntoa(iph->saddr));
fprintf(stdout, "%s\n", inet_ntoa(iph->daddr));
fprintf(stdout, "Version:
%d\n", iph->version);
fprintf(stdout, "Header Length:
%d\n", iph->ihl);
fprintf(stdout, "Service:
%x\n", iph->tos);
애플리케이션
데이타
ON TARGET
패킷 처리 소스
fprintf(stdout, "Total Length:
fprintf(stdout, "Identification:
%d\n", ntohs(iph->tot_len));
%d\n", ntohs(iph->id));
fprintf(stdout, "RF:%d ", (ntohs(iph->frag_off) & 0x8000) >> 15);
fprintf(stdout, "DF:%d ", (ntohs(iph->frag_off) & 0x4000) >> 14);
fprintf(stdout, "MF:%d\n", (ntohs(iph->frag_off) & 0x2000) >> 14);
fprintf(stdout, "Fragmenting bits: %d\n", (ntohs(iph->frag_off) & 0x1fff) * 8);
fprintf(stdout, "Time to Live:
fprintf(stdout, "Protocol:
fprintf(stdout, "Checksum:
%d\n", iph->ttl);
%d\n", iph->protocol);
%d\n", ntohs(iph->check));
RF DF MF
Fragmenting bits
3 bits
13 bits
ON TARGET
패킷 처리 소스
/* TCP 헤더 */
if (iph->protocol == IPPROTO_TCP) {
/* netinet/in.h 참조 */
TCP 세그먼트
IP 헤더
TCP 헤더
TCP 데이터
/* TCP 헤더 맵핑 */
tcph = (struct tcphdr *) (sizeof(struct ether_header)20+bytes
(iph->ihl * 4) + p);
/* ==================
TCP 헤더
================== */
fprintf(stdout, "Source Port:
%d\n", ntohs(tcph->source));
fprintf(stdout, "Destination Port:
%d\n", ntohs(tcph->dest));
fprintf(stdout, "Sequence Number:
%u\n", ntohl(tcph->seq));
fprintf(stdout, "Acknowledgment Number: %u\n", ntohl(tcph->ack_seq));
fprintf(stdout, "Header Length:
%d\n", tcph->doff);
ON TARGET
패킷 처리 소스
fprintf(stdout, "Reserved1:%d ", tcph->res1);
fprintf(stdout, "Reserved2:%d\n", tcph->res2);
fprintf(stdout, "URG:%d ", tcph->urg);
fprintf(stdout, "ACK:%d ", tcph->ack);
fprintf(stdout, "PSH:%d ", tcph->psh);
fprintf(stdout, "RST:%d ", tcph->rst);
fprintf(stdout, "SYN:%d ", tcph->syn);
fprintf(stdout, "FIN:%d\n", tcph->fin);
fprintf(stdout, "Windows Size:
fprintf(stdout, "Checksum:
fprintf(stdout, "Urgent Pointer:
/* 플래그 */
%d\n", ntohs(tcph->window));
%d\n", ntohs(tcph->check));
%d\n", ntohs(tcph->urg_ptr));
ON TARGET
패킷 처리 소스
/* TCP 데이터 */
tcpdata = (unsigned char *) (p + sizeof(struct ether_header)
+ (iph->ihl * 4) + (tcph->doff * 4));
/* 포인터 조정 */
/* ================== TCP DATA(HEX) ================== */
j = 0;
for (i = (iph->ihl * 4) + (tcph->doff * 4); i <= ntohs(iph->tot_len) - 1; i++) {
fprintf(stdout, "%02x ", *(tcpdata++));
if ((++j % 16) == 0) {
fprintf(stdout, "\n");
}
TCP 세그먼트
}
fprintf(stdout, "\n");
IP 헤더
TCP 헤더
20 bytes
TCP 데이터
ON TARGET
패킷 처리 소스
/* ================== TCP DATA(char) ================== */
for (i = (iph->ihl * 4) + (tcph->doff * 4); i <= ntohs(iph->tot_len) - 1; i++) {
if ((*tcpdata == 0x0d) && (*(tcpdata + 1) == 0x0a)) {
fprintf(stdout, "\n");
tcpdata += 2;
i++;
continue;
}
if (*tcpdata >= ' ' && *tcpdata < 0x7f) {
/* 출력 못하는 문자 ‘.’ 대치 */
fprintf(stdout, ".");
}
else {
fprintf(stdout, "%c", *tcpdata);
}
tcpdata++;
}
ON TARGET
패킷 처리 소스
/* UDP 헤더 */
else if (iph->protocol == IPPROTO_UDP) {
/* netinet/in.h 참조 */
UDP 데이터그램
IP 헤더
UDP 헤더
UDP 데이터
/* UDP 헤더 맵핑 */
udph = (struct udphdr *) (p + sizeof(struct ether_header) + (iph->ihl * 4));
8 bytes
/* ==================
UDP 헤더
==================\n"); */
fprintf(stdout, "Source Port:
%d\n", ntohs(udph->source));
fprintf(stdout, "Destination Port:
%d\n", ntohs(udph->dest));
fprintf(stdout, "Length:
%d\n", ntohs(udph->len));
fprintf(stdout, "Checksum:
%d\n", ntohs(udph->check));
ON TARGET
패킷 처리 소스
/* UDP 데이터 */
udpdata = (unsigned char *) (p + sizeof(struct ether_header)
+ (iph->ihl * 4) + sizeof(struct udphdr));
/* 포인터 위치 시킴 */
/* ================== UDP DATA ================== */
/* TCP 의 데이터 부분과 동일 */
UDP 데이터그램
IP 헤더
UDP 헤더
8 bytes
UDP 데이터
ON TARGET
패킷 처리 소스
/* ICMP 헤더 */
else if (iph->protocol == IPPROTO_ICMP) {
/* netinet/in.h 참조 */
ICMP 메세지
IP 헤더
ICMP 데이터
ICMP 헤더
/* ICMP 헤더 맵핑 */
icmph = (struct icmp *) (p + sizeof(struct ether_header) + (iph->ihl * 4));
8 bytes
/* ICMP 헤더는 type 과 code 별로 여러가지 형태의 헤더가 나타난다. */
/* 아래는 default 이다. */
/* ==================
fprintf(stdout, "Type:
fprintf(stdout, "Code:
fprintf(stdout, "Checksum:
ICMP 헤더
================== */
%d\n", icmph->icmp_type);
%d\n", icmph->icmp_code);
%d\n", ntohs(icmph->icmp_cksum));
ON TARGET
패킷 처리 소스
/* ICMP 데이터 */
icmpdata = (unsigned char *) (p + sizeof(struct ether_header)
+ (iph->ihl * 4) + sizeof(struct icmp)); /* 포인터 위치 시킴 */
/* ================== ICMP DATA(HEX) ================== */
j = 0;
for (i = (iph->ihl * 4) + sizeof(struct icmp); i <= ntohs(iph->tot_len) - 1; i++) {
fprintf(stdout, "%02x ", *(icmpdata++));
if ((++j % 16) == 0) {
fprintf(stdout, "\n");
}
}
ICMP 메세지
fprintf(stdout, "\n");
}
IP 헤더
ICMP 헤더
8 bytes
ICMP 데이터
ON TARGET
패킷 처리 소스
/* ARP 헤더 */
else if (ntohs(eh->ether_type) == ETHERTYPE_ARP) {
ARP 리퀘스트/리플라이
/* ARP 헤더 맵핑 */
이더넷 헤더
arph = (struct ether_arp *) (p + sizeof(struct ether_header));
ARP 헤더
/* ==================
ARP 헤더
==================
*/
28 bytes
fprintf(stdout, "Hardware (Type): %d\n", ntohs(arph->ea_hdr.ar_hrd));
fprintf(stdout, "Protocol(Type):
%d\n", ntohs(arph->ea_hdr.ar_pro));
fprintf(stdout, "Hardware(Size):
%d\n", arph->ea_hdr.ar_hln);
fprintf(stdout, "Protocol(Size):
%d\n", arph->ea_hdr.ar_pln);
fprintf(stdout, "ARP opcode (command):
ntohs(arph->ea_hdr.ar_op));
%d\n",
ON TARGET
패킷 처리 소스
fprintf(stdout, "Sender Hardware(Ethernet) address: ");
for (i = 0; i < ETH_ALEN; i++) {
/* MAC 주소 나타내는 것과 동일 */
fprintf(stdout, "%02X", arph->arp_sha[i]);
if (i != 5) {
fprintf(stdout, ":");
/* XX:XX:XX:XX:XX:XX, 48 bits */
}
}
fprintf(stdout, "Sender Protocol(IP) address: ");
for (i = 0; i < 4; i++){
fprintf(stdout, "%d", arph->arp_spa[i]);
if (i != 3) {
fprintf(stdout, ".");
}
}
/* Target 부분, Sender 와 동일 */
ON TARGET
Libnet
Libnet
응용 프로그램이 패킷을 생성하여 보낼 수 있게 해주는 라이브러리
패킷 생성 과정
1. 네트워크의 초기화
2. 메모리의 초기화
3. 패킷의 생성
4. 패킷의 체크섬(Check Sum) 생성
5. 패킷의 투입 (Injection)
ON TARGET
Libnet 사용
헤더파일을 포함시킴
#include <libnet.h>
컴파일 방법
# gcc 소스.c -lnet
ON TARGET
Libnet
네트웍 초기화
네트웍 injection 인터페이스 갖고 오기
raw 소켓 인터페이스
적당한 프로토콜 (보통 IPPROTO_RAW)을 가지고 libnet_open_raw_socket()을
호출
이 호출은 커널에 libnet이 IP 헤더를 만들 것이라는 것을 알려주는
IP_HDRINCL이 찍혀진 raw 소켓을 리턴
링크-layer 인터페이스
적당한 디바이스 인자를 가지고 libnet_open_link_interface() 를 호출
이 함수는 링크-인터페이스 구조에 갈 준비가 되어 있는 포인터를 리턴
메모리 Allocation
패킷을 위한 메모리를 allocate 시킨다
방법은 libnet_init_packet()를 호출
raw 소켓 인터페이스를 사용하는 30바이트의 payload를 가진 간단한 TCP 패
킷을 위해서는 70바이트가 필요(IP 헤더 + TCP 헤더 + payload)
링크-layer 인터페이스를 사용하는 같은 패킷은 84바이트가 필요
(이더넷 헤더 + IP 헤더 + TCP 헤더 + payload).
메모리 작업이 끝나면, libnet_destroy_packet()를 호출하여 메모리를 놓아주
어야 한다.
ON TARGET
Libnet
패킷 생성
패킷은 모듈적으로 생성
각각의 프로토콜 layer에 각각에 맞는 libnet_build
함수에 대한 호출이 있다.
raw 소켓 에서는 libnet_build_ip()와
libnet_build_tcp()가 쓰임
링크-layer 에서는 libnet_build_Ethernet()이 쓰임.
패킷 생성 함수 호출의 순서는 중요하지 않다.
단지 이 함수들에 정확한 메모리 오프셋이 전달되는 것이
중요하다. (현재 버전에서는 tag로 처리)
함수들은 패킷 버퍼에 패킷 헤더를 만들어야 한다. 그리
고 이것은 어떤 순서로도 될 수 있다.
ON TARGET
Libnet
ON TARGET
Libnet
패킷 버퍼는 libnet_init_packet()을 통해서 초기화
libnet_build_Ethernet()는 오프셋 0을 가진 버퍼의 맨 처음이 전달된다.
(이더넷 헤드는 패킷의 맨 앞에 만들어져야 하므로)
libnet_build_ip() 정확한 위치에 IP 헤더를 만들기 위해서 14바이트의 오
프셋이 전달받고,
libnet_build_tcp() 은 이것보다 20바이트 뒤의 오프셋을 전달받는다.
패킷 체크섬
패킷 체크섬을 계산
(패킷이 어떤 종류의 IP 패킷이라고 가정할 때)
raw 소켓 인터페이스에서는 커널이 IP 체크섬을 다룰 것이므로 프로그래머는
transparent layer 체크섬만을 계산하면 된다. (패킷이 transparent layer를 가지고 있다고
가정할 때)
링크-layer 인터페이스에서는 IP 체크섬은 명확하게 계산되어야 한다.
체크섬은 패킷의 IP헤더에 대한 포인터를 받는 libnet_do_checksum()을 통해서 계
산된다.
ON TARGET
Libnet
패킷 Injection
네트웍에 패킷을 쓰는 것
raw 소켓 인터페이스에서는 libnet_write_ip()을 통해서 쓰고,
링크-layer 인터페이스에서는 libnet_write_link_layer()을 통해서 쓴다.
두 개의 함수 모두 씌여진 바이트 수를 리턴하고, 오류가 나면 -1을 리턴한다.
ON TARGET
Libnet 소스
소스 설명으로 대체
더 자세한 함수는
http://libnet.sourcearchive.com 참조
ON TARGET
THANK YOU