用瀏覽器時,OSI 的全部七層都替你打理好了,你只需專注於瀏覽而非協定。但要玩網路弱點,就得剝開這些層、看清底下發生什麼。
從主機名稱到 IP:DNS 查詢#
連到數字 IP 位址很簡單,但為了方便,人們常用命名位址。telnet 會自動做 **DNS(Domain Name Service)**查詢,把 www.internic.net 解析成 IP——就像知道名字就能在電話簿查號碼。
netdb.h 提供 gethostbyname():接受命名位址字串、回傳 hostent 結構指標(錯誤回傳 NULL),其中含網路位元組順序的 32 位元 IP。
// host_lookup.c 核心
host_info = gethostbyname(argv[1]);
address = (struct in_addr *) (host_info->h_addr);
printf("%s has address %s\n", argv[1], inet_ntoa(*address));結合 socket 函式,就能寫出網頁伺服器辨識程式:連上目標埠 80、送出 HEAD / HTTP/1.0、逐行讀取回應、用 strncasecmp() 找出以 Server: 開頭的那行:
reader@hacking:~/booksrc $ ./webserver_id www.microsoft.com
The web server for www.microsoft.com is Microsoft-IIS/7.0
Tinyweb:一個極小的網頁伺服器#
網頁伺服器不必比前面的簡單伺服器複雜多少:接受 TCP/IP 連線後,再用 HTTP 實作更進一步的通訊層即可。tinyweb.c 把連線處理拆成獨立的 handle_connection() 函式,處理瀏覽器來的 GET 與 HEAD 請求,在本地 webroot 目錄找資源,找不到就回 404。
ptr = strstr(request, " HTTP/"); // 找出看似合法的請求
if(ptr == NULL) { printf(" NOT HTTP!\n"); }
else {
*ptr = 0; // 在 URL 結尾終止緩衝區
if(strncmp(request, "GET ", 4) == 0) ptr = request+4;
if(strncmp(request, "HEAD ", 5) == 0) ptr = request+5;
...
fd = open(resource, O_RDONLY, 0); // 嘗試開檔
if(fd == -1) { /* 送 404 */ }
else { /* 送 200 OK,GET 則連檔案內容一起送 */ }
}綁定 1024 以下的任何連接埠都需要 root 權限,所以 tinyweb 設為 setuid root 執行。
127.0.0.1是路由到本機的特殊 loopback 位址。瀏覽器請求首頁後會自動再請求image.jpg與favicon.ico。

圖 0x400-3:瀏覽器對 tinyweb 伺服器(http://127.0.0.1)請求的結果
底層協定:一場公司內部的官僚體系#
一則 HTTP 訊息穿過不同的通訊面向時,會被多層層層包起。
這可以想成電影《巴西》(Brazil)裡繁複的公司內部官僚體系:每一層有個高度專精、只懂該層語言與協定的接待員。封包傳輸時,每個接待員做完該層的事,把封包裝進內部信封、在外面寫上標頭,往下一層遞。不論封包內容是金融資料、郵件還是別的,把資料從 A 點搬到 B 點的低層協定通常都一樣。 一旦你懂了這套官僚體系,就能偷看傳輸中信封的內容,甚至偽造文件來操縱系統。
資料連結層:MAC 位址與 ARP#
最低的可見層。若把下面的實體層想成公司內部的送信推車、上面的網路層想成全球郵政,那麼資料連結層就是公司內部的內勤郵件系統。
乙太網路存在於這層,提供標準定址系統。這些位址稱為 MAC 位址(Media Access Control):
- 每個乙太裝置有一個全球唯一的六位元組位址,通常寫成
xx:xx:xx:xx:xx:xx。 - 又稱硬體位址,儲存在裝置的積體電路記憶體中——就像硬體的身分證字號。
- 乙太標頭 14 位元組,含來源與目的 MAC 位址;還有特殊的廣播位址
ff:ff:ff:ff:ff:ff,送到此位址的封包會發給所有連接的裝置。
MAC 位址不該改變,但 IP 位址可能經常變。這層沒有 IP 位址的概念、只有硬體位址,所以需要一種方法關聯兩種定址方案——這就是 ARP(Address Resolution Protocol,位址解析協定)。
ARP 有四種訊息,最重要的兩種:
- ARP request:送到廣播位址,含發送者的 IP 與 MAC,等於問:「誰有這個 IP?是你的話請回覆你的 MAC。」
- ARP reply:直接回給請求者,說:「這是我的 MAC,我有這個 IP。」

圖 0x400-4:ARP 請求(送往廣播位址)與 ARP 回覆
大多數實作會暫時**快取(cache)**收到的 MAC/IP 配對,這樣就不必每個封包都做 ARP——這些快取就像內部的座位表。
網路層:IP 與 ICMP#
網路層像全球郵政,提供把東西送到各處的定址與遞送方法。這層用的協定正是 IP(Internet Protocol),多數網際網路用 IPv4。IP 標頭 20 位元組,欄位與位元旗標定義於 RFC 791。
標準協定有絕佳的文件。RFC 791 用一張出奇詳盡的 ASCII 圖顯示標頭欄位與位置。如同乙太標頭,IP 標頭也有描述封包資料型別的協定欄位、路由用的來源與目的位址,還有偵測傳輸錯誤的檢查碼,以及處理封包分片的欄位。
**ICMP(Internet Control Message Protocol)**也存在於這層,用於訊息傳遞與診斷:
- IP 比郵局還不可靠——不保證封包一定抵達;出問題時會回傳 ICMP 封包通知發送者。
ping工具用 ICMP Echo Request / Echo Reply:主機送出 Echo Request,遠端收到後回 Echo Reply,可用來判斷兩主機間的連線延遲。
ICMP 與 IP 都是**無連線(connectionless)**的:這整層真正在乎的,只是把封包送到目的位址。
分片(fragmentation):有時網路連結對封包大小有限制。IP 把封包拆成能通過的較小片段、各加 IP 標頭送出,每片有不同的分片偏移值存在標頭;目的地用偏移值重組原始封包。

圖 0x400-5:大型 IP 封包被拆成多個較小的分片,各自加上 IP 標頭
傳輸層:TCP 與 UDP#
傳輸層可想成第一線的辦公室接待員,從網路層取信。這層兩大協定是 TCP 與 UDP。
TCP 是網際網路服務最常用的協定(telnet、HTTP、SMTP、FTP 都用它),受歡迎的原因是它在兩個 IP 位址間提供透明、可靠、雙向的連線。串流 socket 就用 TCP/IP:
- 雙向像打電話——撥號建立連線後雙方都能通訊。
- 可靠指 TCP 確保所有資料以正確順序抵達;封包亂序抵達時 TCP 會重排後才交給上層;中途遺失時,目的地會保留手上封包,等來源重傳。
這些功能靠一組 TCP 旗標與追蹤**序號(sequence number)**達成:
| TCP 旗標 | 意義 | 用途 |
|---|---|---|
URG | Urgent | 標示重要資料 |
ACK | Acknowledgment | 確認封包;連線大部分時間都開著 |
PSH | Push | 要接收方直接推送資料而非緩衝 |
RST | Reset | 重置連線 |
SYN | Synchronize | 連線開頭同步序號 |
FIN | Finish | 雙方道別、優雅關閉連線 |
- 客戶端送 SYN 開、ACK 關 的封包給伺服器。
- 伺服器回 SYN 與 ACK 都開 的封包。
- 客戶端回 SYN 關、ACK 開 的封包,完成連線。
之後連線中每個封包都是 ACK 開、SYN 關。只有前兩個封包 SYN 開,因為它們用來同步序號。

圖 0x400-6:TCP 三向交握的 SYN、SYN/ACK、ACK 封包與序號
序號讓 TCP 能重排亂序封包、判斷封包是否遺失、避免混淆不同連線的封包。連線建立時各方各生成一個初始序號,在前兩個 SYN 封包中告知對方;之後每送一個封包,序號就依資料部分的位元組數遞增。每個 TCP 標頭還有一個確認號,就是對方的序號加一。
TCP 適合需要可靠與雙向通訊的應用,代價是通訊開銷。UDP 開銷與內建功能都少得多,行為更像 IP:無連線、不可靠。它把建立連線與維持可靠性的責任交給應用程式。UDP 標頭(RFC 768)相對迷你,依序只含四個 16 位元值:來源埠、目的埠、長度、檢查碼。