作者:小黑豬(朱文哲)@銀河安全實驗室
公眾號:銀河安全實驗室
VxWorks 操作系統是美國WindRiver公司于1983年設計開發的一種嵌入式實時操作系統(RTOS),它以其良好的可靠性和卓越的實時性被廣泛地應用在通信、軍事、航空及航天等高精尖技術領域中。2012年8月登陸的好奇號,以及近日成功降落火星的洞察號均使用了VxWorks系統。
本文將以施耐德昆騰系列PLC的NOE-711以太網模塊的固件為例,講解一下基于VxWorks操作系統的嵌入式設備固件的一些常用分析方法。
1. 固件提取
通常我們能夠通過設備廠商的官網獲取到設備固件的升級包,從該升級包中將真正的固件進行提取后才能進行分析。提取固件的常用方法是使用Binwalk等二進制分析工具對固件升級包進行自動化分析,待確認升級包類型后再進行固件提取或其他操作。
通過Binwalk進行自動分析可以發現,NOE 771模塊的升級包NOE77101.bin中內嵌了一個使用zlib壓縮的文件。

通過調用Binwalk的-e參數進行自動提取后,Binwalk會把自動提取后的文件以偏移地址命名并存儲在特定的目錄中。

繼續使用Binwalk對提取的385文件進行分析可以確認,該文件的確是我們所需要分析的VxWorks固件,因此提取的385文件也就是我們需要分析的固件文件了。

2. 分析固件內存加載地址
為了對VxWorks系統固件進行逆向分析,我們首先必須要知道固件在內存中的加載地址。加載地址的偏差會影響到一些絕對地址的引用例如跳轉函數表、字符串表的引用等。
下圖是VxWorks系統在PowerPC架構下的內存分布圖,如圖所示VxWorks的系統固件將會被加載到一個由BSP (Board Support Package)定義的內存地址中。

2.1 方法一:通過封裝的二進制文件頭直接識別加載地址
某些設備的固件會使用某種格式進行封裝,比較常見的是使用ELF(executable and linkable format)格式進行封裝。采用ELF格式封裝后的文件頭部有特定的數據位記錄了該固件的加載地址,因此針對該情況我們可以直接使用greadelf等工具直接讀取ELF文件頭,從而直接獲取到固件的加載地址,此外IDA也能直接識別ELF格式封裝的VxWorks固件,無需額外處理即可進行自動化分析。

2.2 方法二:分析固件頭部的初始化代碼,尋找加載地址的特征
在很多情況下我們拿到的固件是沒有采用ELF格式封裝的,這時就需要我們通過對固件的某些特征進行分析來判斷具體的加載地址。還是以施耐德的NOE 711固件為例, 在具體分析某個固件時首先我們需要知道目標設備的CPU架構,具體可以如下圖所示通過binwalk -A指令來對固件的CPU架構進行分析,此外也可以用binwalk -Y指令來調用capstone反匯編引擎來進行輔助判斷,不過我實際測試下來存在一些誤報的情況會把NOE 711的固件識別成arm架構的。

在得知目標CPU架構后就可以使用IDA加載固件并對其代碼進行初步分析。


下圖是默認加載后的IDA界面,僅僅分析出了極少數的函數。接下來就需要根據固件頭部的這段代碼來尋找加載地址的特征。

在固件頭部有如下圖所示的一段有趣的代碼,在對r1和r3寄存器進行賦值后進行了跳轉。

下圖是PowerPC的寄存器用途說明,從圖中可以看到R1寄存器是棧指針,而R3寄存器則是第一個參數。

現在回到我們之前看的固件頭部代碼處,這段代碼相當于是先將棧地址設置為0x10000,將第一個參數(r3寄存器)設置為0x0,隨后在棧上開辟0x10個字節的空間后跳轉到當前地址+0x1cd34處執行。

根據VxWorks官網文檔對對內存布局的描述,Initial Stack是usrInit函數的初始化棧。

而usrInit函數則是VxWorks系統引導后運行的第一個函數,再結合之前我們分析的那段代碼,可以基本確定在大部分情況下第一個跳轉的地址就是usrInit這個函數的地址。

隨后我們再回憶一下之前看到的VxWorks PowerPC內存布局圖可以發現,初始化棧的地址同時也是固件的內存加載地址,因此r1寄存器指向的0x10000就是我們所尋找的固件加載地址。

2.3 方法三:基于bss區數據初始化代碼的特征,計算加載地址
另一個分析固件加載地址的常用方法是,找到bss區域的初始化代碼后間接計算出固件加載地址。bss(Block Started by Symbol)區在VxWorks系統中主要用于存儲一些當前未賦值的變量,在系統啟動過程中VxWorks會使用bzero函數對bss區的數據進行清零 。
如下圖所示我們可以得知VxWorks固件自身有三個section,text、*data*以及bss這三個部分共同組成了VxWorks固件。

從下圖所示的內存布局中可以看到bss區緊跟著固件的text和data段之后,因此只要我們找到bss區清零的函數,分析出清零函數的結束位置及后將其減去固件文件的大小即可獲得固件的內存加載地址。

接下來我們再看一下VxWorks中的userInit函數,從下圖可以看到usrInit除了是系統引導后執行的第一個函數外,在這個函數中還會首先對bss區的數據進行清理。

對usrInit這個函數進行查看后,可以發現其中有不少的bl跳轉函數。根據usrInit的描述,第一個跳轉的函數就是負責初始化BBS區的函數。

下圖是BSS初始化函數的代碼,結合PowerPC的寄存器用途可知r3和r4寄存器分別是函數sub_18D59C的兩個參數。r3的值為0x339418, r4的值為0x490D2C - 0x339418 = 0x157914相當于長度。因此我們可以得知0x339418就是bss區的起始地址,0x490D2C就是bss區的結束地址。

在得到bss區結束地址后,我們就可以進一步的計算出固件的加載地址,不過使用這個方法有一個前提條件就是提取出的固件文件本身是完整的,如果提取出的固件文件不完整這個方法則會失效。

上面介紹了三種比較常用的VxWorks固件加載地址分析方法,此外還有通過焊接UART接口查看系統引導過程的串口輸出等各種其他非常規手段。在分析出固件加載地址后就可以使用新的加載地址重新加載固件進行分析了

下圖是Windows下的IDA6.8中重新加載后固件的對比圖,Mac下的IDA 在修復了加載地址后還是只能關聯識別出很少的函數。

3. 利用符號表修復函數名
雖然IDA此時能夠正確的識別函數及其調用關系,但依然無法自動識別出函數名,這對固件的分析工作造成了很大的阻礙。 此時可以查看固件在編譯時是否編入了符號表,如固件編入了符號表那么我們就可以利用符號表中的內容來修復IDA中所顯示的函數名。
通過使用binwalk可以幫助我們輔助分析VxWorks固件中是否編入了符號表,并識別出符號表在固件中的位置。如下圖所示binwalk識別出的符號表地址在文件偏移0x301E74處。

如下圖所示,VxWorks 5系列的符號表有他獨特的格式,他以16個字節為一組數據,前4個字節是0x00,之后是符號名字符串所在的內存地址,后4個字節是符號所在的內存地址,最后4個字節是符號的類型,例如0x500為函數名。

基于符號表的特征,我們能夠輕松的獲取到固件中符號表的起始及結束位置。此時我們就可以使用IDA的api來修復函數名,使用加載地址0x00重新加載固件后使用如下圖所示的Python腳本即可進行修復。

完成函數名修復后的IDA界面如下圖所示,通過修復符號表IDA識別出了8000多個函數。至此VxWorks系統固件的預處理工作就全部完成了,現在我們就可以根據函數名來對一些關鍵服務的代碼進行靜態分析了。

4. 固件分析
在完成上述的一些預處理工作后,一個固件分析的入手點就是查看例如loginUserAdd等關鍵函數的調用關系。如下圖所示loginUserAdd函數的用途是在登錄表中添加一個用戶,這個賬號可以用于登錄例如telnet及ftp等服務。

通過分析loginUserAdd函數的調用,可以看到在usrAppInit等函數中均調用了loginUserAdd函數。

再進一步查看usrAppInit函數可以發現在這個函數中所添加的用戶及密碼哈希。此類方法也是發現后門賬號的有效手段之一。

此外還可以關注某些服務的初始化函數,例如在usrNetAppInit函數中就可以發現許多網絡服務的初始化函數調用。

至此VxWorks固件分析的常用方法就介紹完畢了,通過使用類似的分析方法,我們也能夠在基于VxWorks的其他嵌入式設備的固件中發現一些有趣的信息,下面是其中的兩個例子。


5. 引用
- 測試附件下載地址
- Developing PowerPC Embedded Application Binary Interface (EABI) Compliant Programs
- Reference Materials VxWorks for PowerPC
- usrConfig
- loginLib
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/771/
暫無評論