ecn kg 発表

43
ECN KG 発発 発発発発発発発発発発発発発

Upload: virote

Post on 08-Jan-2016

73 views

Category:

Documents


1 download

DESCRIPTION

ECN KG 発表. カーネルをハックしてみよう. ネットワークスタックをいぢる. FreeBSD のカーネルコードに printf をいれて処理を追ってみる 今日のターゲットは ICMP です!. まずは QEMU を手に入れよう. 既に QEMU をダウンロードしてありますよね? http://www.ht.sfc.keio.ac.jp/~sada/etc/qemu-0.7.zip 下記媒体でも配布します。 CD-R SD カード コンパクトフラッシュ QEMU を手に入れたら、ローカルの好きな場所においてください. FreeBSD@QEMU の起動. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: ECN KG  発表

ECN KG 発表

カーネルをハックしてみよう

Page 2: ECN KG  発表

ネットワークスタックをいぢる

FreeBSD のカーネルコードに printf をいれて処理を追ってみる今日のターゲットは ICMP です!

Page 3: ECN KG  発表

まずは QEMU を手に入れよう

既に QEMU をダウンロードしてありますよね? http://www.ht.sfc.keio.ac.jp/~sada/etc/qemu-0.7

.zip

下記媒体でも配布します。 CD-R SD カード コンパクトフラッシュ

QEMU を手に入れたら、ローカルの好きな場所においてください

Page 4: ECN KG  発表

FreeBSD@QEMU の起動

qemu-freebsd.bat をダブルクリック!ユーザは root 、パスワードは・・・下記のようにタイプすると IP アドレスがとれます dhclient ed0

外部とは port 22 しか繋がりません。 qemu-freebsd.bat を編集する

と、好きな port で通信できます

QEMU

仮想ルータ10.0.2.2

FreeBSD10.0.2.16

Page 5: ECN KG  発表

カーネルのソースコード構成

どんなファイルがあるか、ざっくりチェックしてみてください。

/ usr src sys

i386

kern

net

netinet

sys

ufs

vm

X86 特有

一般的なカーネル

一般的なネットワーク

TCP/IP

カーネルヘッダ

Unix ファイルシステム

カーネルヘッダ

Page 6: ECN KG  発表

ICMP Echo Request, Reply

ip_output()

application

ether_output()

arpresolve()

rip_input()

application

ip_input()

ether_input()

icmp_input()

Page 7: ECN KG  発表

データ送信の流れ

Page 8: ECN KG  発表

ICMP ECHO Request の送信

ICMP Echo Request を送信する ping プログラムで実際にパケットの流れをみてみ

る ip_output()

• IP ヘッダの初期化• 経路選択、アドレス選択• フラグメンテーション

ether_output()• フレーム構築• インタフェースへのキューイング

arpresolve()• arp 解決

Page 9: ECN KG  発表

実際にコードを見ながら printf を入れよう

printf の基本的な使い方は大丈夫ですよね??だめ?#define DBG(fmt, arg...) printf("%s: "fmt "\n", __FUNCTION__, ## arg)↑ みたく書くと、後で色々楽です。

と、いうわけで、取りあえず送信からいきましょip_output 関数は sys/netinet/ip_output.c にあります

Page 10: ECN KG  発表

ip_output()   (IP ヘッダができるまで )

ip_output(){……

ip = mtod(m, struct ip *);

if ((flags & (IP_FORWARDING|IP_RAWOUTPUT)) == 0) { ip->ip_v = IPVERSION; ip->ip_hl = hlen >> 2; ip->ip_id = ip_newid(); ipstat.ips_localout++; } else { hlen = ip->ip_hl << 2; }   (RAW output なので、  ここでは、初期化されない)

ICMP

ICMPIP

Page 11: ECN KG  発表

ip_output()   ( 経路選択、アドレス選択 )

ip_output(){…… 経路キャッシュや経路テーブルをみて宛て先決定

dst = (struct sockaddr_in *)&ro->ro_dst;again:……送信元の決定 if (ip->ip_src.s_addr == INADDR_ANY) { /* Interface may have no addresses. */ if (ia != NULL) { ip->ip_src = IA_SIN(ia)->sin_addr; } }

Page 12: ECN KG  発表

ip_output()   ( 経路選択、アドレス選択 )

ip_output(){…… フラグメントの必要がなければ送信

error = (*ifp->if_output)(ifp, m, (struct sockaddr *)dst, ro->ro_rt); goto done; }}

送信前に IP ヘッダのメンバを表示してみよう

Page 13: ECN KG  発表

IP header

バージョンip_v

ヘッダ長ip_hl

tosip_tos

全体の長さip_len

識別子ip_id

フラグ、フラグメントオフセットip_off

TTLip_ttl

プロトコルip_p

ヘッダチェックサムip_sum

発信元 IP アドレスip_src

データ

宛て先 IP アドレスip_dst

Page 14: ECN KG  発表

32bit アドレスの表示方法

#define IP_ADDR_FORMAT(addr) \ ((addr) & 0xff),   \ (((addr) >> 8) & 0xff), \ (((addr) >> 16) & 0xff), \ (((addr) >> 24) & 0xff)

DBG(“ip_src %d.%d.%d.%d ip_dst %d.%d.%d.%d",   IP_ADDR_FORMAT(ip->ip_src.s_addr),

IP_ADDR_FORMAT(ip->ip_dst.s_addr));

Page 15: ECN KG  発表

ここでカーネルをコンパイルしてみよう

コンパイル方法 cd /usr/src/sys/i386/compile/ECN make kernel make kernel-install

ぜーーったい、 make clean をしないでください! 一から make しようとすると、それだけでこの授業時間が終わります。。。

再起動 shutdown -r now

Page 16: ECN KG  発表

Ping してみよう

タイプしてみてください dhclient ed0 ping 10.0.2.2

dmesg すると、出力はどうなりましたか?

Page 17: ECN KG  発表

spl… 関数

ip_output で splnet(), splx() を見ませんでしたか?FreeBSD では、割り込み処理に優先度が割り当てていますネットワーク周りだとこの2つが使われます。 splnet(), splimp()splimp は splnet より優先度が高く、ネットワークデバイスからの割り込みを禁止する。インタフェースキューを操作する際は、 splimp が使用される

Page 18: ECN KG  発表

spl… 関数一覧

関数 説明spl0 通常の動作モードsplsoftclock 低優先度クロック処理splnet ネットワークプロトコル処

理spltty 端末 I/Osplbio ディスクとテープの I/Osplimp ネットワークデバイス I/Osplclock 高優先度クロック処理splhigh 全ての割り込みがブロックsplx(x) 前の優先度に戻す

Page 19: ECN KG  発表

ether_output()

sys/net/if_ethersubr.c内の関数

処理は フレーム構築 インタフェースキューイング

Page 20: ECN KG  発表

ether_output(ARP 解決 )

intether_output(){…… case AF_INET: error = arpresolve(ifp, rt0, m, dst, edst); if (error) return (error == EWOULDBLOCK ? 0 : error);        type = htons(ETHERTYPE_IP); break;

もし、 ARP がまだ解決されてなければ、返る( arpresolv が ARP 解決後、再び ether_output を呼び出す)。解決されていれば、フレームの構築を行なう。

Page 21: ECN KG  発表

ether_output( フレーム構築 )

intether_output(){…… M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); if (m == NULL) senderr(ENOBUFS); eh = mtod(m, struct ether_header *); (void)memcpy(&eh->ether_type, &type, sizeof(eh->ether_type)); (void)memcpy(eh->ether_dhost, edst, sizeof (edst));

M_PREPEND で IP ヘッダの前に領域を確保後、ETHER ヘッダの中身を埋めていく

ICMPIP

ICMPIPeth

Page 22: ECN KG  発表

ether_output_frame(出力へのキューイング )

intether_output_frame(){…… IFQ_HANDOFF(ifp, m, error); return (error);}送信キューにデータをキューイングする。

というわけで、イーサヘッダも printf しよう。

Page 23: ECN KG  発表

Ether Header

発信元アドレス 送信元アドレス タイプ

struct ether_header{   u_char ether_dhost[6]; u_char ether_shost[6]; u_short ether_type;}

IPv4の場合 type は 0x0800 となる

Page 24: ECN KG  発表

MAC アドレスの表示方法は?

以下のような感じで表示させてみようマクロにしたり、 define で書いてもいいかも。

voidmac_print(u_char *macaddr){ int n;  printf("mac = ["); for (n=0; n<6; n++) printf("%02x ",(u_int)macaddr[n]); printf("]\n");}

Page 25: ECN KG  発表

arpresolve

ether_output() で ARP 解決がまだされていないときに、 ARP 解決がされますARP は大丈夫ですよね。。。。?主に 2つの関数が使われる arpresolve

• ARPエントリを検索• なければ、 arprequest を呼び出す

arprequest• ARP パケットを作成• インタフェースへ出力する

Page 26: ECN KG  発表

arpresolve(ARPエントリの検索 )

sys/netinet/if_ether.c にありますintarpresolve(){……

rt = arplookup(SIN(dst)->sin_addr.s_addr, 1, 0);…….   if ((rt->rt_expire == 0 || rt->rt_expire > time_second) && sdl->sdl_family == AF_LINK && sdl->sdl_alen != 0) {

arplookup で ARPエントリを検索後、エントリが妥当かどうか調べるもし、妥当であればその ARP 解決がされている

Page 27: ECN KG  発表

arpresolve(ARP 要求 )intarpresolve(){…… if (rt->rt_expire) { rt->rt_flags &= ~RTF_REJECT; if (la->la_asked == 0 || rt->rt_expire != time_second) { rt->rt_expire = time_second; if (la->la_asked++ < arp_maxtries) { struct in_addr sin = SIN(rt->rt_ifa->ifa_addr)->sin_addr;

RT_UNLOCK(rt); arprequest(ifp, &sin, &SIN(dst)->sin_addr, IF_LLADDR(ifp));

ARP タイマ、 ARP回数のチェック設定後、 ARP 要求送信を行う arprequest()を呼び出す

Page 28: ECN KG  発表

arprequest(ARP 要求パケット構築、送信 )

static void arprequest(){……… if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL) return;

ARP パケット用mbuf を確保……

ah->ar_pro = htons(ETHERTYPE_IP); ah->ar_hln = ifp->if_addrlen;

ah->ar_pln = sizeof(struct in_addr); ………

パケットの中身を埋める

(*ifp->if_output)(ifp, m, &sa, (struct rtentry *)0);

インタフェースへ出力

Page 29: ECN KG  発表

ARP ヘッダ ( メンバを色々出力させてみよう )■if_ether.h で定義されていますstruct arphdr { u_short ar_hrd; HW アドレスのフォーマット u_char ar_hln; ハードウェアアドレスの長さ    u_char ar_pln; プロトコルアドレスの長さ    u_short ar_op; ARP パケットの種類};

struct ether_arp { struct arphdr ea_hdr; ARP の固定サイズヘッダ    u_char arp_sha[ETHER_ADDR_LEN]; 送信元 MAC アドレス u_char arp_spa[4]; 送信元のプロトコルアドレス u_char arp_tha[ETHER_ADDR_LEN]; ターゲットの MAC アドレス u_char arp_tpa[4]; ターゲットのプロトコルアドレス};

ether_arp

arphdr

Page 30: ECN KG  発表

ここまで分かると色々できそう?

IP アドレスを詐称MAC アドレスを詐称ARP フラッディング発生

みたいなカーネルが簡単にできそうだね!!でも、よいこのみんなはやっちゃだめだよ!

Page 31: ECN KG  発表

ここでまたカーネルをコンパイルしてみよう

コンパイル方法 cd /usr/src/sys/i386/compile/ECN make kernel make kernel-install

ぜーーったい、 make clean をしないでください! 一から make しようとすると、それだけでこの授業時間が終わります。。。

再起動 shutdown -r now

Page 32: ECN KG  発表

カーネルの出力は?

dmesg で確認してみよう

Page 33: ECN KG  発表

データ受信の流れ

Page 34: ECN KG  発表

ICMP ECHO Reply の受信

ICMP Echo Reply を受信する ping プログラムで実際にパケットの流れをみて

みる ether_input()

• ether ヘッダの妥当性チェック• 受信キューに入れる

ip_input()• IP パケットの妥当性チェック• 再組み立てやオプション、転送処理• 振り分け

icmp_input()• メッセージの妥当性チェック• ICMP メッセージに応じた処理

Page 35: ECN KG  発表

ether_input( フレームのチェック)

sys/net/if_ethersubr.c 内にありますstatic voidether_input(){……… eh = mtod(m, struct ether_header *); etype = ntohs(eh->ether_type);……… ether_demux(ifp, m);}

フレームの妥当性をチェックする。ether_demux を呼び出しデマルチプレクス。ここでも、 ether ヘッダのメンバを色々表示してみよう

Page 36: ECN KG  発表

ether_demux( デマルチプレクス )void   ether_demux(){…… m_adj(m, ETHER_HDR_LEN);   ether ヘッダ削除 switch (ether_type) { case ETHERTYPE_IP: if (ip_fastforward(m)) return; isr = NETISR_IP; break;……… netisr_dispatch(isr, m); return;

ether_type により上位層への割り込みのスケジューリングがされ、適切なキューへキューイング

Page 37: ECN KG  発表

ip_input(IP パケットの妥当性チェック )

schednetisr により呼び出されるsys/netinet/ip_input.c にあります。

void   ip_input(){………… ip = mtod(m, struct ip *); if (ip->ip_v != IPVERSION) { ipstat.ips_badvers++; goto bad; }妥当性のチェックを行う受信した IP ヘッダの中身を表示させよう

Page 38: ECN KG  発表

ip_input(オプション処理&転送 )

void   ip_input(){…………   if (hlen > sizeof (struct ip) && ip_dooptions(m, 0)) return;

IP ヘッダのサイズが20より大きい場合、オプション処理を行う    LIST_FOREACH(ia, INADDR_HASH(ip->ip_dst.s_addr), ia_hash) {   if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr && (!checkif || ia->ia_ifp == m->m_pkthdr.rcvif))

アドレスリストをチェックし、自分宛か、転送するべきか判断する。

Page 39: ECN KG  発表

ip_input( リアセンブリ&振り分け )

void   ip_input(){………… if (ip->ip_off & (IP_MF | IP_OFFMASK)) { m = ip_reass(m);オフセットやフラグがあったならば、フラグメントパケット。リアセンブリ処理を行う

(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen); return;

プロトコルスイッチにより、適切なトランスポート層へデータを渡す

Page 40: ECN KG  発表

icmp_input(ICMP の妥当性チェック )

sys/netinet/ip_icmp.c内にありますvoid   icmp_input(){ ip = mtod(m, struct ip *); m->m_len -= hlen; m->m_data += hlen; icp = mtod(m, struct icmp *);

if (in_cksum(m, icmplen)) {

mbuf のデータポインタを調整後、 icmp ヘッダへのポインタを取り出し、妥当性チェック受信した ICMP ヘッダのメンバを色々表示してみよう

ICMPIP

Page 41: ECN KG  発表

icmp_input( メッセージの処理& raw input)

void   icmp_input(){……… code = icp->icmp_code; switch (icp->icmp_type) {……….

case ICMP_ECHOREPLY: default: break; }raw: rip_input(m, off); return;

Page 42: ECN KG  発表

ここでまたまたカーネルをコンパイルしてみよう

コンパイル方法 cd /usr/src/sys/i386/compile/ECN make kernel make kernel-install

ぜーーったい、 make clean をしないでください! 一から make しようとすると、それだけでこの授業時間が終わります。。。

再起動 shutdown -r now

Page 43: ECN KG  発表

ping の送受信の流れは分かりましたか?余力があれば以下の処理を見つけて表示させてみよう ICMP ECHO Request を受信後、 reply を返す所 arp request受信後、 reply を返す所 フラグメントを発生させて、フラグメント&組み立て

処理

さらに余力があれば、ネットワークスタックとは関係ないですが、、、、 カーネル起動時のデバイス初期化メッセージを色々変

えてみたら楽しいかも (e.g.そんな餌におれは釣られないクマ――等の巨大 A

A が出てくるなど。個人的に見てみたい人はどうぞ )