Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863543640

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

4.研究Xtensa架構特性現在我們已經將所有段加載到適當的地址,我們可以開始逆向工程了。

但為了高效地做到這一點,我們需要更多地了解Xtensa 架構,包括:

1.指令中的參數順序

2.條件跳轉的執行細節

3.編譯器調用約定

4.堆棧組織

首先要探索的是指令中的參數順序。例如:MOV R1, R2.您可以在所有架構中找到此類指令,但這可能意味著將R1 複製到R2 或將R2 複製到R1。因此,了解指令中源代碼的位置以及目標寄存器的位置至關重要。您可以在GitHub 上找到Xtensa 指令集描述。

至於該MOV指令,在Xtensa中,表示將R2複製到R1。因此,第一個參數將是大多數簡單指令(例如數學相關指令)中的目的地。例如,指令addi a14, a1,0x38意味著a14=a1 +0x38。

但對於存儲數據的指令,情況則相反。例如,該指令s32i.n a5, a1,0x10意味著的值a5必須存儲在地址處(a1 +0x10)。

要學習的第二件事是條件跳轉的完成方式。有兩種方法可以做到這一點:

1.使用專用指令進行比較操作,設置標誌寄存器,然後進行條件跳轉。

2.使用一條指令一次性執行所有這些操作。

Xtensa 執行後者:beqz a10, loc_400E1C54

使用單個指令來檢查是否a10等於零,然後它要么跳轉到loc_400E1C54,要么不跳轉。

第三步是檢查編譯器使用的調用約定:將參數傳遞給函數的方式以及如何返回值。

Xtensa 以一種非常不尋常的方式傳遞參數。參數在調用指令之前放入寄存器中。但它們在函數中出現的寄存器與調用之前所在的寄存器不同:

image.png

以下是如何在彙編程序級別將參數傳遞給函數的示例:

image.png

這裡我們有三個論據:

a10 是目的地址

a11 是源地址

a12 是要復印的尺寸

然而,一旦代碼進入memcpy函數,這些值就會自動分別傳輸到a2、a3和a4寄存器中。

同樣的技巧也用於返回值。在memcpy函數內部,該值存儲在寄存器中a2,但從函數返回後,該值出現在a10.

返回的樣子如下0:

image.png

這就是檢查返回值的樣子:

image.png

benz.na10在從調用返回時檢查寄存器的值。

最後,有必要了解堆棧是如何組織的。

Xtensa 使用a1 寄存器來創建堆棧幀。每個函數都以入口指令開始:entry a1,0xC0,其中0xC0是堆棧幀的大小,即函數需要用於堆棧變量的堆棧量。

通常,這些函數從初始化堆棧變量開始:

image.png

寄存器中的零值a5被寫入基於a1寄存器的堆棧變量中。

在獲得有關Xtensa 架構的所有必要知識後,我們終於可以開始逆向其代碼了。

5. 在IDA 中對Xtensa 代碼進行逆向工程與ARM、MIPS 和PowerPC 相比,Xtensa 不是最流行的架構,並且沒有完整的功能列表。因此,IDA處理器模塊會存在一些我們需要克服的限制。

IDA 中Xtensa 處理器模塊的主要限制是:

函數參數沒有自動註釋

堆棧幀不會自動創建

一些ESP32 函數屬於IROM,因此存在對硬編碼地址的調用

部分Xtensa指令未反彙編

讓我們討論一些克服這些挑戰的技巧。

5.1.函數參數的類型系統和註釋從IDA 7.7 開始提供Xtensa類型系統。在IDA 中擁有可用的類型系統非常重要,因為它使逆向變得方便。特別是,它允許您導入C 結構的定義並指定IDA 使用的函數原型,以便在傳輸函數參數的指令附近放置自動註釋。

但是,如果您沒有類型系統,還有一個解決方法。

首先,讓我們看看有類型系統時函數是什麼樣子的:

image.png

屏幕截圖13. 當有可用的類型系統時函數的外觀

函數原型設置有參數的名稱和類型,以便IDA 可以使用此信息在調用站點註釋參數:

image.png

屏幕截圖14. 函數原型

但Xtensa 不會有這樣的事情。另一種方法是使用IDA 中的可重複註釋功能。如果您在函數的開頭設置可重複的註釋,它將顯示在所有調用站點上。

image.png

屏幕截圖15. 設置可重複註釋

image.png

屏幕截圖16. 可重複的註釋顯示在所有調用站點上

因此,我們可以使用此功能來定義函數參數:

image.png

屏幕截圖17. 使用可重複註釋功能定義函數參數

調用站點將如下所示:

image.png

屏幕截圖18. 調用站點

您可以在註釋中選擇寄存器名稱,IDA 會在代碼中突出顯示它。因此,您可以輕鬆找到參數值。

5.2.恢復堆棧幀要恢復堆棧幀,您需要手動指定堆棧大小,然後通過在每個與堆棧一起使用的指令處按K 鍵來顯示IDA 的使用位置。

讓我們探索一下config_router_safe函數,例如:

image.png

屏幕截圖19. config_router_safe 函數

很明顯這裡的棧幀大小是0xC0。我們在函數的堆棧設置中使用該值(Alt+P):

image.png

屏幕截圖20. 使用0xC0 值(堆棧幀大小)

從視覺上看,什麼也不會發生,但是如果您通過按Ctrl+K 轉到該函數的堆棧幀,您會注意到堆棧空間現在已分配:

image.png

屏幕截圖21. 分配堆棧空間

接下來要做的是使用entry指令指定堆棧移位。在此之前,我們建議啟用堆棧指針可視化,如下面的屏幕截圖所示:

image.png

屏幕截圖22. 啟用堆棧指針可視化

現在,代碼應該如下所示:

image.png

屏幕截圖23.啟用堆棧指針可視化後的代碼

000是當前堆棧指針移位值,我們需要將其移位0xC0。為此,請將光標置於入口指令處,然後按Alt+K以查看以下窗口,您可以在其中指定新舊堆棧指針之間所需的差異:

image.png

屏幕截圖24. 將當前堆棧指針值移動0xC0

作為此操作的結果,代碼將如下所示:

image.png

屏幕截圖25. 移動當前堆棧指針移位值後的代碼

現在,如果您開始在與寄存器一起使用的每條指令處按Ka1,IDA 將創建堆棧變量:

image.png

屏幕截圖26.IDA 創建新的堆棧變量

還可以編寫IDA 腳本來自動執行這些操作。

5.3.調用IROM調用位於CPU 的IROM 部分而不是固件中的某些低級API 的情況並不少見。在這種情況下,固件僅與包含定義的IROM 函數地址的特殊鏈接器定義文件鏈接。

在逆向期間,IROM 函數調用如下所示:

image.png

屏幕截圖27. IROM 函數調用

40058E4C是IROM 內的地址。但不可能知道固件調用了哪個函數。因此有必要檢查ESP32 工具鏈以查找鏈接器定義。

ESP32 芯片的IDE 是Espressif IDE。在IDE 文件中搜索IROM 地址,我們會找到:C:\Espressif\frameworks\esp-idf-v4.4.2\components\esptool_py\esptool\flasher_stub\ld\rom_32.ld

image.png

屏幕截圖28. ESP32 ROM 地址表

這些值可以輕鬆轉換為枚舉數據類型:

image.png

屏幕截圖29. 將值轉換為枚舉數據類型

然後,我們需要導入IDA,以便將enum 應用於IROM 地址值:

image.png

屏幕截圖30. 將枚舉應用於IROM 地址值

如果我們在IROM 地址附近添加可重複的註釋,它將使所有內容更容易閱讀:

image.png

屏幕截圖31. 在IROM地址附近添加可重複註釋後的代碼

5.4.無法識別的指令經常發生的情況是,處理器模塊已針對指令集的某些特定變體實現。然後製造商製造出具有99% 兼容指令集的新CPU,其中包含10 多個新指令,這是最初沒人想到的。因此IDA、Ghidra和Radare等工具可能無法反彙編一些新指令。

克服這一挑戰的正確方法是擴展處理器模塊並添加對新指令的支持。這需要對反彙編器API 有深入的了解,而這些API 並不那麼容易理解。

讓我們討論一種可能的解決方法,用於解決以下情況:儘管存在一些無法識別的指令,但您只想讓IDA 創建函數。假設IDA 不知道RER 指令,並且在包含RER 操作碼的情況下無法創建該函數:

image.png

屏幕截圖32. 如果函數包含RER 操作碼,IDA 無法創建該函數

您可以按P多次。除了控制台窗口中出現錯誤外,不會發生任何事情:

image.png

屏幕截圖33. 控制台窗口中的錯誤

但是,這並不意味著IDA 無法創建遵循RER 指令的指令。您可以跳過RER 指令的三個字節,然後創建代碼:

image.png

屏幕截圖34. 跳過RER 指令的三個字節後創建代碼

接下來,您可以選擇從輸入到最後的整段代碼retw.n,然後按P:

image.png

屏幕截圖35. 選擇從Entry 到retw.n 的整段代碼

之後,IDA 將創建該函數:

image.png

屏幕截圖36.IDA 創建一個函數

通常,反彙編程序無法識別的擴展指令在逆向過程中不會產生太大差異。可能導致問題的是執行調用、跳轉或加載/存儲等操作的新指令,因為代碼流丟失並且對數據的引用丟失。

結論對於涉及逆向工程物聯網固件的項目來說,在轉向業務邏輯之前研究未知的硬件架構至關重要。儘管逆向工程師可能需要幾週的時間來學習該架構,但從長遠來看,這種深入的研究有助於提高進一步工作的速度。