Realtek rtl8821ae 芯片
wifi 芯片位于 m.2 模塊上,可以筆記本電腦中被更換。它具有 2.4GHz 和 5GHz wifi 支持等功能,與 wifi 芯片的通信通過 PCIe 進(jìn)行。
要找到這些固件很容易,它在 Linux 上啟動(dòng)時(shí)從 /lib/firmware/rtlwifi 加載。實(shí)際上有兩套固件:一套用于正常使用(rtl8821aefw_29.bin),另一套稍小一些用于遠(yuǎn)程喚醒 (rtl8821aefw_wowlan.bin),這是一種通過 wifi 喚醒設(shè)備的技術(shù),英文名為Wake-on-WLAN)?!璤29.bin 在 ip link set dev wlan0 up 上加載,…_wowlan.bin 在 ip link set dev wlan0 down 上加載。它們不是永久固件,而只是加載到芯片的 RAM 中。
該芯片還有一個(gè)由realtek編寫的Linux內(nèi)核中的上行wifi驅(qū)動(dòng)程序,這并不能說明它的質(zhì)量,因?yàn)樗匀皇怯蓃ealtek編寫的驅(qū)動(dòng)程序。
在同一芯片上還有一個(gè)藍(lán)牙芯片,可以通過 USB 2.0 進(jìn)行通話。內(nèi)核中似乎實(shí)現(xiàn)了一些 BT-Coexistence 協(xié)議,以確保 wifi 和 BT 部分不會(huì)干擾。
與rtl8821ae的所有通信都是內(nèi)存映射的:有一個(gè)4K大小的配置空間,可以寫入和讀取。此外,還有一個(gè)64K的tx緩沖區(qū)和一個(gè)64K的rx緩沖區(qū)用于發(fā)送和接收數(shù)據(jù)包。
基本固件結(jié)構(gòu)
查看固件,它顯然是基于 8051 的。但是,它不會(huì)在 0x0000 處加載。當(dāng)直接在固件映像上運(yùn)行時(shí),at51 base 返回 0x3fe0。結(jié)果發(fā)現(xiàn)有一個(gè) 0x20 字節(jié)的標(biāo)頭,因此固件本身在 0x4000 處加載。
從 0x4000 開始的代碼路徑也通向 main 函數(shù):在典型的 Keil 編譯器方式中,它跳轉(zhuǎn)到 ?C_START 函數(shù),該函數(shù)用于初始化由 Keil C(X)51 編譯器發(fā)出的用于初始化靜態(tài)變量的靜態(tài)變量,之后它跳轉(zhuǎn)到 MAIN 函數(shù)。
MAIN 函數(shù)有點(diǎn)奇怪:它設(shè)置一些內(nèi)存,然后將一個(gè)地址壓入堆棧,設(shè)置一個(gè)計(jì)時(shí)器并返回到它剛剛壓入堆棧的地址。事實(shí)證明,realtek 正在使用 RTX51 tiny,這是一個(gè)非常小的實(shí)時(shí)內(nèi)核,用于管理任務(wù)和信號(hào)。內(nèi)核基本上會(huì)跟蹤任務(wù)狀態(tài)并在切換任務(wù)堆棧時(shí)重新定位它們。at51 libfind 會(huì)找出各種 Keil 庫的位置,作為其中的一部分,它還會(huì)找到 rtx51 內(nèi)核函數(shù),如 OS_SWITH_TASK 或 _ISR_SEND_SIGNAL。
普通固件有兩個(gè)任務(wù)(另外一個(gè)只用于初始化):一個(gè)只在特定信號(hào)時(shí)被激活,另一個(gè)在無限循環(huán)中運(yùn)行。
雖然沒有公開數(shù)據(jù)表記錄 8051 端的 I/O 寄存器,但大部分內(nèi)容很容易找到:Linux 驅(qū)動(dòng)程序從主機(jī)端定義配置空間中的寄存器。通過查看 8051 未訪問的寄存器,可以看到當(dāng)映射到 XDATA 偏移量 0x0 時(shí),這些寄存器與驅(qū)動(dòng)程序源中未定義的寄存器類似。然后很容易對(duì)定義寄存器的 reg.h 文件進(jìn)行一些處理,以將所有名稱導(dǎo)入 ghidra。
轉(zhuǎn)儲(chǔ)Mask ROM
MASK ROM,是制造商為了要大量生產(chǎn),事先制作一顆有原始數(shù)據(jù)的ROM或EPROM當(dāng)作樣本,然后再大量生產(chǎn)與樣本一樣的 ROM。固件在 0x4000 處加載,但它仍然會(huì)調(diào)用 0x4000 以下的函數(shù),這意味著芯片上有一個(gè)永久Mask ROM,它也負(fù)責(zé)將固件下載到芯片上。不知道這些功能會(huì)使逆向工程有點(diǎn)困難,因此獲得該固件會(huì)很好。
最簡(jiǎn)單的方法是修改固件并將字節(jié)從 CODE 空間復(fù)制到配置空間寄存器中,因?yàn)槲覀円呀?jīng)知道這些是如何映射的。但是有一個(gè)障礙:固件的最后兩個(gè)字節(jié)似乎是一個(gè)校驗(yàn)和,這意味著我們需要知道校驗(yàn)和是如何計(jì)算的。然而,校驗(yàn)和將在Mask ROM 內(nèi)計(jì)算,所以我們不能只看固件來了解它是如何完成的。幸運(yùn)的是,delsum 很快就表明它只是一個(gè) 16 位 XOR 校驗(yàn)和,相當(dāng)于 poly=0x0001 的 CRC。
從用戶空間讀取和寫入配置空間通常是不可能的,所以我需要重新編譯內(nèi)核,以添加對(duì)/dev/ mems進(jìn)行此操作的支持。我選擇通過在VM中執(zhí)行內(nèi)核并使用VFIO將wifi卡映射到其中來完成此操作,因?yàn)橹匦戮幾g傳播服務(wù)器內(nèi)核對(duì)我來說時(shí)間太長(zhǎng),并且標(biāo)準(zhǔn)配置幾乎只適用于 VM。另外,我知道 modconfig 可用,但是 VM 方法在標(biāo)準(zhǔn)配置下仍然可以正常工作,并且不會(huì)花費(fèi)太多時(shí)間。
內(nèi)核與芯片上的固件通信主要使用的是所謂的H2C,它基本上把一些簡(jiǎn)短的(比如8字節(jié))命令放入配置空間,并讓固件讀取。通過在ghidra中查看從固件到該點(diǎn)的引用,很容易找到讀取該點(diǎn)的固件代碼。不幸的是,當(dāng)我自己處理這些事務(wù)時(shí),我無法得到固件的響應(yīng)。
于是我決定硬編碼讀取到固件的地址,修改 0x4000 處的跳轉(zhuǎn),將字節(jié)從硬編碼地址復(fù)制到未使用的配置空間寄存器 (REG_ROM_VERSION),然后繼續(xù)執(zhí)行主程序。對(duì)于每個(gè)讀取地址,固件都會(huì)被修改,內(nèi)核模塊會(huì)重新加載使用新地址修改的固件。這種方法的讀取速度大約為每秒 3 個(gè)字節(jié)。雖然花了幾個(gè)小時(shí),但可能仍然比找出固件不響應(yīng) H2C 命令的原因要快。
藍(lán)牙固件
有一件事我一開始沒有意識(shí)到,但回顧起來似乎很明顯,那就是芯片還有一個(gè)單獨(dú)的藍(lán)牙固件(在/lib/firmware/rtlbt中)。這一次它不是基于8051的,但是我不能輕易地找出它的架構(gòu)是什么。為了解決這個(gè)問題,我制作了一個(gè)工具,比較不同的版本的固件通過比較他們使用成對(duì)對(duì)齊,以便在不同的固件版本之間的相同但在不同地址的部分仍然顯示并排。
需要注意的是,在許多地方,都插入了字節(jié)00 65,在此之后,兩個(gè)地址都以4個(gè)字節(jié)對(duì)齊,這意味著它被用作一種填充。當(dāng)然,用于填充的完美指令應(yīng)該是NOP。所以我進(jìn)行了搜索,但“0065 NOP”并沒有真正產(chǎn)生任何結(jié)果,但是搜索“6500 NOP”(相反的字節(jié)序)確實(shí)產(chǎn)生了一些有趣的結(jié)果,表明 mips16e(mips 的拇指擴(kuò)展)使用該操作碼進(jìn)行 NOP,事實(shí)上,這確實(shí)正確地分解了固件,使之變得有意義。
然而,我對(duì)藍(lán)牙固件本身不是那么感興趣,因?yàn)樗皇腔?051,所以我繼續(xù)我的wifi固件的分析。首先,我并沒有真正擁有任何藍(lán)牙設(shè)備。
驅(qū)動(dòng)程序如何發(fā)送數(shù)據(jù)包
wifi的工作方式是在802.11規(guī)范中規(guī)定的。打開它,你會(huì)看到3500頁密密麻麻的縮寫詞。
802.11 幀具有以太網(wǎng)幀的某種作用,但除了承載數(shù)據(jù)之外,還有各種各樣的控制幀來執(zhí)行某些操作,例如與 AP 關(guān)聯(lián)、與 AP 進(jìn)行身份驗(yàn)證、電源管理等。數(shù)據(jù)幀(以及一些控制幀)也可以使用各種密碼和協(xié)議進(jìn)行加密。
在加密幀的情況下,還有一些額外的數(shù)據(jù),如序列號(hào),其目的是重放保護(hù),這也意味著 802.11 數(shù)據(jù)幀可以根據(jù)是否加密而具有不同的大小。加密通常由 rtl8821ae 本身完成,驅(qū)動(dòng)程序只是將未加密的內(nèi)容放入加密通常所在的幀中,然后芯片使用硬件對(duì)其進(jìn)行加密。
在 802.11 數(shù)據(jù)幀內(nèi)部有一個(gè) LLC 標(biāo)頭,在大多數(shù)情況下,它僅用于告知內(nèi)容的 EtherType。
發(fā)送數(shù)據(jù)包時(shí),驅(qū)動(dòng)程序?qū)⒄麄€(gè)幀放入 tx 緩沖區(qū),在其前面有一個(gè) 40 字節(jié)的標(biāo)頭。標(biāo)頭告訴 wifi 硬件幀的大小、是否使用加密、發(fā)送的速率和類似的東西。802.11 幀內(nèi)還有一個(gè)持續(xù)時(shí)間字段,指示數(shù)據(jù)包發(fā)送所需的時(shí)間,這也是由硬件計(jì)算的。
硬件有 8 個(gè)用于不同目的的隊(duì)列,可以在標(biāo)頭中指定,一個(gè)用于信標(biāo)幀,一個(gè) MGQ(管理隊(duì)列?)無論隊(duì)列如何,數(shù)據(jù)包都以循環(huán)方式在 256 字節(jié)邊界上放入 tx 緩沖區(qū)。
歡迎使用 Realtek RealWoW Tech
在 Linux 驅(qū)動(dòng)程序中,有一個(gè)名為 rtl8821ae_set_fw_rsvdpagepkt 的函數(shù),它會(huì)在加載新固件時(shí)調(diào)用。它將信標(biāo)幀等幀和包含 ARP 響應(yīng)的數(shù)據(jù)幀加載到 tx 緩沖區(qū)的高端,驅(qū)動(dòng)程序?qū)⑵湟暈楸A魠^(qū)域,然后這些幀的偏移量通過一些H2C命令發(fā)送到固件。
這顯然也是出于wowlan的目的,這樣固件就可以在主機(jī)休眠時(shí)響應(yīng)ARP請(qǐng)求,這樣數(shù)據(jù)包就可以通過IP發(fā)送到wifi芯片。wowlan固件負(fù)責(zé)的另一件事是GTK握手,當(dāng)設(shè)備離開或加入網(wǎng)絡(luò)時(shí),AP 向所有設(shè)備發(fā)送一個(gè)新的組加密密鑰,用于廣播/多播目的。
要了解固件如何處理這些,可以查看保存這些偏移量的 H2C 命令。它將它們寫入一些 XDATA 位置,因此通過查看引用這些位置的內(nèi)容,可以找出固件的哪個(gè)部分發(fā)送了幀。
結(jié)果是在XDATA 0xfc00-0xfcff的tx緩沖區(qū)中有一個(gè)256字節(jié)的窗口,地址的更高的8位可以用0xfd10的XDATA寄存器設(shè)置。類似地,可以從XDATA 0xfb00-0xfbff訪問rx緩沖區(qū),0xfd11有更高的8位。
發(fā)送數(shù)據(jù)包時(shí),很難弄清楚具體是什么發(fā)送了數(shù)據(jù)包,因?yàn)橛泻芏嗍虑橐?。固件?tx 標(biāo)頭中設(shè)置一些字段,還在 802.11 標(biāo)頭中設(shè)置一些位,計(jì)算是否加密以找出內(nèi)容的偏移量,對(duì)于 ARP 數(shù)據(jù)包,它還檢查諸如 EtherType 之類的內(nèi)容并進(jìn)行響應(yīng)。當(dāng)然,解析數(shù)據(jù)包時(shí)會(huì)出現(xiàn)更高的復(fù)雜性。發(fā)送數(shù)據(jù)包的關(guān)鍵部分似乎是將REG_TXPKTBUF_MGQ_BDNY設(shè)置為txbuffer地址的高8位。注意,數(shù)據(jù)包是256字節(jié)對(duì)齊的,然后向REG_CPU_MGQ_INFORMATION + 3寫入0x20。
查看wowlan固件,可以發(fā)現(xiàn)還有一個(gè)H2C命令,它似乎為額外的數(shù)據(jù)包設(shè)置了更多配置。在解析代碼中,還有一個(gè)地方是在幀數(shù)據(jù)內(nèi)容的開始處檢查字節(jié)0x45,這就是IPv4數(shù)據(jù)包的開始。解析代碼然后檢查由上述 H2C 命令給出的目標(biāo)地址和 UDP 端口,并根據(jù)應(yīng)該放入 tx 緩沖區(qū)的模式檢查數(shù)據(jù)。如果一切都匹配,則將字節(jié) 0x30 作為 wowlan 喚醒原因并喚醒主機(jī)。
在Linux驅(qū)動(dòng)程序源代碼中,這個(gè)原因被命名為FW_WOW_V2_REALWOW_V2_WAKEUPPKT(并沒有真正處理)。那么這個(gè)所謂的 RealWoW 是什么呢?
不幸的是,Linux 驅(qū)動(dòng)程序沒有實(shí)現(xiàn) Realtek RealWoW Tech,并且不清楚如何從 realtek 獲得新 ID,所以我放棄了對(duì)它的嘗試。
但是對(duì)固件的分析表明它可能通過定期向服務(wù)器發(fā)送 UDP 數(shù)據(jù)包(由驅(qū)動(dòng)程序提供)來工作,服務(wù)器以某種 ACK 響應(yīng)。當(dāng)在網(wǎng)站中輸入正確的 ID 后,服務(wù)器會(huì)向設(shè)備發(fā)送一個(gè)喚醒 UDP 數(shù)據(jù)包??梢赃M(jìn)行此操作,由于定期發(fā)送UDP數(shù)據(jù)包,所以連接已經(jīng)打開,wifi固件然后通過PCIe喚醒設(shè)備。
一個(gè)純粹基于 8051 的鍵盤記錄器
既然固件已經(jīng)解決了一些問題,那么我們自己做些改變?cè)趺礃樱坑?jì)劃是讓 EC 獲取筆記本電腦鍵盤的按鍵,并將它們發(fā)送到 wifi 芯片,然后由 wifi 芯片將它們發(fā)送到網(wǎng)絡(luò)。
DMA 不能從 EC 工作,因?yàn)樗挥?LPC 總線后面,這意味著它將依賴 ISA DMA,這非常糟糕,并且只能訪問最低 的16MB 的內(nèi)存。因此,無法通過 DMA 進(jìn)行通信。
在上一篇文章中,我分析了EC固件,讓我們看一下上一篇文章中的圖表:
現(xiàn)在我寫到 wifi m.2 芯片上沒有連接 EC_TX 和 EC_RX 跟蹤(EC 和 rtl8821ae 之間的右下方)。但請(qǐng)注意,EC_RX 跟蹤也通過另一個(gè)引腳連接到 rfkill(bt) 線,并且在通往 CPU 的路上有一個(gè)電阻器,以便 EC 可以有效地覆蓋 CPU 而不會(huì)造成短路,可以試著用它來傳輸數(shù)據(jù)。
經(jīng)過一些測(cè)試,似乎該方法也阻止了wifi的射頻能力。但事實(shí)證明,如果在使用后再次將其拉低,那就不是什么大的問題,因?yàn)橹鳈C(jī)和固件似乎只看到一些數(shù)據(jù)包被下載。
因?yàn)橹挥幸粭l跡線,所以控制器必須以某種方式同步,而不是單獨(dú)的時(shí)鐘線。我決定采用 UART-ish 方法,這意味著位只是一個(gè)接一個(gè)地傳輸,它們之間有一個(gè)設(shè)定的時(shí)間間隔。
EC 已經(jīng)使用了 1ms 計(jì)時(shí)器,因此我只是修補(bǔ)了計(jì)時(shí)器中斷以檢查 16 字節(jié)密鑰緩沖區(qū)中的新密鑰,并以 1 位/毫秒的速度通過 EC_RX 引腳發(fā)送它們。
在wifi端,執(zhí)行固件時(shí)出現(xiàn)了問題。事實(shí)證明,wifi固件只是在短時(shí)間內(nèi)停止執(zhí)行以節(jié)省電量。與其嘗試讀取 realtek 驅(qū)動(dòng)程序以了解電源管理的工作方式,還不如定期寫入似乎使其保持清醒的配置空間。
現(xiàn)在wifi固件也需要知道一些時(shí)間概念,其中有標(biāo)準(zhǔn)的 8051 外設(shè),但它們似乎不會(huì)產(chǎn)生中斷,因此必須在主循環(huán)中進(jìn)行輪詢。一個(gè)快速的實(shí)驗(yàn)表明,它的頻率大約為6.67 MHz,這意味著8051使用了一個(gè)80MHz時(shí)鐘源,這似乎是由驅(qū)動(dòng)程序源中提到的80MHz所備份的。
另一個(gè)實(shí)驗(yàn)表明,更改 rfkill(bt) 行會(huì)更改 REG_GPIO_PIN_CTRL_2 的第 3 位,這樣就可以實(shí)現(xiàn)UART的另一端。我只是將定時(shí)器設(shè)置為每 1/3 ms 產(chǎn)生一次溢出,以便有一個(gè)更高的采樣率使UART正常工作。一旦傳輸了一個(gè)字節(jié),EC會(huì)在下一個(gè)字節(jié)之前暫停一段時(shí)間。
在 EC 等待期間,wifi芯片將發(fā)送一個(gè)包含一個(gè)字節(jié)的UDP數(shù)據(jù)包到配置的IP地址和端口,這是通過未加密的wifi實(shí)現(xiàn)的。
雖然加密可能是可行的,但它也需要付出一些努力。序列號(hào)必須更新,如果主機(jī)也在傳輸數(shù)據(jù)包,則必須通過tx緩沖區(qū)來找到當(dāng)前的數(shù)據(jù)包。由于重復(fù)的序列號(hào),它也無法從主機(jī)發(fā)送一些數(shù)據(jù)包。固件也有可能掃描周圍未加密的wifi網(wǎng)絡(luò),與它們關(guān)聯(lián),然后通過DNS發(fā)送數(shù)據(jù),這通常會(huì)繞過潛在的強(qiáng)制門戶。
在樹莓派上,我設(shè)置了一個(gè)小程序,它接受 UDP 數(shù)據(jù)包并將 PS/2 代碼轉(zhuǎn)換為 uinput 事件。這樣,鍵盤就可以有效地充當(dāng)樹莓派的鍵盤了。
有趣的是,這實(shí)際上是一個(gè)鍵盤記錄器,它在運(yùn)行時(shí)不會(huì)在 CPU 上運(yùn)行任何代碼。只需要刷新一次EC的固件,更換wifi固件即可加載修改后的版本。