儘管我們可在網頁瀏覽器中透過像是 AdBlock 這類的 extension 來過濾廣告,但需要額外的設定和佔用更多系統資源,倘若我們能透過 netfilter,直接在核心層級過濾網路廣告,那所有應用程式都有機會受益。
重現去年 Netfilter 實驗,記錄下來
TLS ; https ; layer 4 firewall ; OSI 七層
TCP 3-way handshake SYN, sliding window
實驗前清除所有規則
使用命令查找目標網頁的資訊
將youtube.com
加入到 iptable 的 reject 名單
使用以下指令查看 iptable 內容
REJECT 中指定了目標是cg-in-f0.1e100.net/15
,這可能不是 YouTube 所有的 IP 地址,因此瀏覽器還可以順利打開
拒絕所有可能是 YouTube 的 IP 地址
測試連接 : 連接至youtube.com
失敗
根據 Use the iptables firewall to block ads on your Linux machine 可以使用上述方式手動更改 iptable,但如果使用核心模組來過濾廣告不僅更靈活,同時也可以提高瀏覽器性能,主要差別如下:
Netfilter 是一個框架,嵌入在 Linux 核心中,用於執行各種封包的操作任務,包括網路地址轉換 (NAT) 和封包過濾。它通過幾個預定義的鉤子 (hook) 在封包傳輸過程中運行。以下是五個主要鉤子及其用途:
NF_IP_PRE_ROUTING
在做任何路由決策之前觸發,當作檢查和修改封包的初始點,適用於路由前的目標 NAT 和封包處理NF_IP_LOCAL_IN
針對發往本地系統的封包, 允許檢查和修改那些要交給本機的入站封包NF_IP_FORWARD
用於在系統中從一個網路介面轉發到另一個網路介面的封包,對應用轉發規則和過濾不屬於本地系統的封包非常重要NF_IP_POST_ROUTING
在所有路由決策做出之後但在封包實際傳送之前觸發,通常用於源 NAT 和在封包傳送前的附加封包處理NF_IP_LOCAL_OUT
針對本地系統生成的封包,在它們實際發出之前,提供修改出站封包、設置源 NAT 或應用其他出站過濾規則的機會
圖片來源 Netfilter – Linux netfilter Hacking HOWTO: Netfilter Architecture
參照 adriver,使用 Linux 核心模組,即可在核心裡頭註冊不同的 hook 來處理封包,為了在使用者瀏覽網站時屏蔽廣告,因此 hook 必須作用在進入本機之前,也就是 Local In 或 Pre Routing
而 hook 函式需接受三個參數
priv
: 為一個私有指針,通常用於傳遞 hook 函式自身需要的私有資料skb
: 是一個指向 Linux 中網路封包(socket buffer)的指針。在 netfilter 中,封包通過系統中的不同階段(如 INPUT、OUTPUT 等)時,可以被 hook 函式捕獲和處理。skb 對象包含了網路封包的所有信息,包括協定頭、有效資料等。state
: 是一個指向 nf_hook_state
構體的指針,這個結構體提供了有關 hook 點和系統狀態的詳細信息my_hook
函式主要由 should_run_get_sfilter()
與 should_run_dns_sfilter()
組成,兩個 funtion 的功用在於檢查並提取傳入的封包是否為 DNS 封包或 HTTP封包,並將其資料存儲到 buf
結構體中
run_get_sfilters(&buf)
與 run_dns_sfilters(&buf)
則將 buf
與預先設置好的 DNS 過濾規則或 HTTP 過濾規則做比對,若比對成功說明其封包包含與廣告相關的內容,因此在函式中將 ret
設置為 NF_DROP
,即丟棄該封包
hook function 的返回值共五個:
NF_DROP
丟棄封包NF_ACCEPT
允許封包通過NF_STOLEN
表示封包已被當前 hook 函式"偷走"。封包的所有權已經轉移到當前 hook 函式中,不在繼續沿Netfilter 處理路徑傳輸NF_QUEUE
將封包送往 nfqueueNF_REPEAT
重新呼叫這個 hook function從 UDP 通訊協定中獲取 DNS 資訊
檢查傳入的網路封包(skb
)是否為 DNS 封包需經過一連串條件審核,步驟如下:
skb
是否不為空skb
是否包含 ip_header
ip_header
裡的協定是否為 UDP 通訊協定udp_header
是否不為空埠號 53 是用於 DNS(域名系統)服務的預設埠,且許多廣告和跟蹤服務使用 DNS 請求來進行域名解析,過濾埠號 53 上的數據包可以有效地阻止這些廣告和跟蹤請求
在確認某網路封包就是目標封包後,提取其 DNS 資訊,及下圖中的 Data。
udp_header
指標位置 + udphdr
長度,即為 Data 位置
dns_data
,即 DNS Message Packet,用 16 進位法表示如下:
de b2 01 20 00 01 00 00 00 00 00 01 09 67 6f 6f 67 6c 65 61 64 73 01 67 0b 64 6f 75 62 6c 65 63 6c 69 63 6b 03 6e 65 74 0b 64 6c 69 6e 6b 72 6f 75 74 65 72 00 00 1c 00 01 00 00 29 04 b0 00 00
前 12 個 bytes 為 DNS Header,分別代表:
Question Name 欄位存放所欲查詢的 FQDN 名稱,每一名稱長度不定,因此,此欄位的長度也不定。網域名稱的存放是以 ASCII 字元表示,最長限制在 64 個字元之內,名稱中的『.』(dot),並不表示出來,而以字元計數取代。譬如:
09 67 6f 6f 67 6c 65 61 64 73 01 67 0b 64 6f 75 62 6c 65 63 6c 69 63 6b 03 6e 65 74 0b 64 6c 69 6e 6b 72 6f 75 74 65 72
Fully Qualified Domain Name: googleads.g.doubleclick.net.dlinkrouter
參考資料: DNS 訊息格式
封包在執行完 should_run_dns_sfilter
之後,若包含 DNS 資訊的話會將其存入 buf->data
裡,用來與 dns_sfilters[]
裡的字串做比對,若比對成功則丟棄封包,dns_sfilters[]
包含許多過濾規則,內容如下:
使用 sudo dmesg
查看核心訊息
從 TCP 通訊協定中獲取 HTTP 資訊
檢查傳入的網路封包(skb
)是否為 DNS 封包需經過一連串條件審核,步驟如下:
skb
是否不為空skb
是否包含 ip_header
ip_header
裡的協定是否為 TCP 通訊協定tcp_header
是否不為空我在解析 TCP 通訊協定的資料時,獲取 payload 的方式與 UDP 一樣,但印出來的都是以 16 03 03
或 17 03 03
為開頭且無法被閱讀的資料。我嘗試理解格式訊息發現資料使用 TLS 進行加密,因此無法被解析
16 03 03
16
TLS Handshake protocol03 03
SSL 3.3 (TLS 1.2)00 7a
Length of handshake message (122 bytes)17 03 03
17
TLS Application Data03 03
version 1.200 45
length of encrypted data (69 bytes)得到 tcp_header
後,嘗試讀取其 Source port 及 Destination port
埠號為 443 說明這個 TCP 連接是用於 HTTPS 通訊,是 HTTP 協定的安全版本,通過 TLS/SSL 進行加密,確保資料在傳輸過程中受到保護,這也解釋上述 payload 無法被解析的原因
$ sudo insmod adblock.ko
$ lsmod | grep adblock
瀏覽網頁會發現許多廣告被屏蔽了
使用ifconfig
查詢你的系統中有哪些網卡,並確定你要監聽的網卡名稱,須先下載套件net-tools
lo
顯示 running 使用此網路介面