Top Banner
ECN KG 発発 発発発発発発発発発発発発発
43

ECN KG 発表

Jan 08, 2016

Download

Documents

virote

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
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
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 が出てくるなど。個人的に見てみたい人はどうぞ )