適用于IDA Pro的CGEN框架介紹
一切都始于我想要分析一些MeP代碼的時候。我通常在IDA Pro中做逆向工作,但是有一小部分處理器IDA并不支持。幸運的是,objdump可以支持這些小眾的處理器架構。經過一番摸索之后,我確定將這些反匯編代碼移植到IDA中會比直接在objdump的輸出中做一些標注和修改更好一些。
過程
互聯網上很少有關于編寫IDA處理模塊的資料。SDK說明文檔太簡單了(只是讓你去讀示例代碼和頭文件)關聯到兩個文檔:Online gide已經找不到了和Chris Eagle寫的《IDA權威指南》。
打開這本書關于編寫處理器模塊的章節(19章),在多次失敗的嘗試之后你可能會打退堂鼓(只是記錄一下缺乏相關的文檔做不出來)。
Chris Eagle中《IDA權威指南》中提到:
編寫處理器模塊的難處在于processor_t結構包含56個需要被初始化的字段,而且其中26個字段是函數指針,其中一個指針指向了一個指針數組,里面又包含了59個字段指向不同的結構(asm_t)需要被初始化。是不是很簡單啊?
但是,我不是那么容易放棄的,繼續讀下去并逐漸熟悉了創建一個處理器模塊的過程。我不打算詳細的描述這個過程,因為Chris已經中書上寫的很清楚了,但我會給出一個簡要的提綱。
IDA處理器模塊
處理器模塊由四個部分組成。“分析器”解析機器碼的二進制數據并生成指令信息。“仿真器”使用這些信息來幫助IDA做下一步的分析。舉個例子,如一個指令引用數據,你的模塊可以告訴IDA查找那個地址上的數據。如果那個指令執行函數調用,你的模塊可以讓IDA創建一個函數。與它的名字相反,它其實并沒有真正的模擬指令集。“輸出者”只是給出分析器生成的數據,向用戶輸出匯編代碼。最后是架構信息,雖然在別的地方不認為它是一個組件,但我認為它是一個組件。這些不是代碼,但是是一些靜態結構告訴IDA一些有用的信息比如寄存器的名字,指令助記符,對齊等等。
CGEN
MeP的binutils(objdump)是CGEN框架機器生成的。CGEN試圖將編寫CPU(assemblers,disassemblers,simulators等等)的工具抽象成一些編寫CPU的定義。這些定義使用Scheme語言對CPU(包括硬件元素,指令集,操作符等等)進行描述。 CGEN為所有需要的CPU工具進行定義并輸出C/C++代碼。開始我想繞開CGEN只是將binutils代碼包含到IDA模塊中。理論上你的模塊沒有必要依照上面的方法。你可以讓分析器記錄二進制數據,仿真器什么也不做,輸出器使用bunutils去生成完整的一行然后進行輸出。然而這樣做的話,你本質上并沒有使用到IDA的強大功能(尋找交叉引用,棧空間布局等等)。沒用使用到CGEN CPU定義給出的信息也是很可惜的。理論上這些定義足夠強大能夠生成RTL代碼來完成這個過程,所以我們會盡可能的向IDA提供信息。
CGEN生成器
生成器也是用Scheme語言寫的(CGEN的文檔將生成器定義為“應用”)。我之前一行功能代碼也沒有寫過,所以我花了一天的時間去理解一個很小的代碼庫。CGEN自定義了一個叫做COS的對象系統。CPU相關的所有定義都變成了對象,并且每個生成器都給這些對象一個輸出自己的方法。例如:模擬器會給操作對象一個“生成代碼來獲得值”的方法。然后通過指令的語義來生成C代碼會用到這個對象的方法。就像一個軟件工程師一樣,我將模擬器,反匯編器,架構描述相關的代碼單獨分割出來,然后寫代碼將他們整合到一起來生成IDA模塊的各個組件。
分析器作為基礎來模擬指令解碼生成器。我必須修改CGEN來記錄指令語法中指定操作數的順序(只有一個地方是修改CGEN自身,其他都是添加的)。然后我重寫了模擬器從指令中提取操作數的方法來填充IDA的”cmd”結構(需要被指定的操作數)。
模擬器使用了模擬模塊生成最基本的信息,這是最難寫的地方(在代碼復雜度方面)。主要問題在于當模擬器生成后期望代碼有序運行并存儲狀態信息,IDA的模擬器并不存儲狀態信息,并且IDA無法保證模擬器像指令描述的那樣運行。這意味著我們不能依賴于狀態,我們的模擬器只能基于指令單獨運行。由于我們只關心通過模擬器尋找數據和代碼引用,我們可以做如下簡化:
1.任何條件都有可能被剝離并且所有路徑都可能被采用 2.使用從寄存器取出的任何值將會使模擬器停止并立即返回 3.對寄存器設置任何的值都將會對其值進行評估,但是會將結果拋棄
第一點允許我們不需要條件即可找到引用。舉個例子,一個條件分支將會允許代碼引用被生成。第二點說的是由于我們不知道狀態,所以任何依賴于寄存器值的條件沒有被剔除的話將會使查找引用變得困難。其實我們可以一直使用這個方法來找到偏移引用,但是我們必須知道只有加減法的使用以及單寄存器的使用會增加復雜性。第三點允許我們捕獲內存讀取的動作。通過這些簡化方法,我們可以知道在狀態未知的情況下找到的任何內存的讀寫和任何PC讀寫都能夠被轉換成交叉引用。
輸出器使用語法分析(binutils的操作碼構建器)作為基礎。它讀取指令序列來輸出正確的括號序列,命令等等。我只是替換了硬件對象生成輸出方法來生成IDA輸出函數。
結果
在所有基礎層級,你的生成模塊會輸出你所預期的跟objdump中一樣的輸出。分析器會找到操作數的正確類型。模擬器試圖找到所有常量地址和加法指令的引用(代碼引用和數據引用)。輸出器會輸出所有正確的指令,如果需要的話還會輸出操作數正確的類型/大小/名字。
無法正確執行的最主要的東西是沒有辦法保持對棧指針的追蹤。另外也沒有做到跳轉和調用分支的標識(需要CF_CALL標簽)。也沒有辦法標識指令如果沒有繼續執行流的話(需要CF_STOP標簽)(添加這些東西其實是件小事,但難點在于將其添加到其他生成器而不引入模擬器代碼。由于人工標識指令很容易,所以我決定不實現)。
使用
一旦你生成IDA模塊組件,你仍然需要人工編寫processor_t結構,與notify()函數相關,并且實現特殊的輸出函數(按CPU定義的來)。然后你可以從binutils拷貝CGEN頭并用IDA SDK進行編譯。將MeP模塊作為例子。你可以重復大部分未生成的代碼(只改變一些字符串和常量)。如果你在運行過程中碰到任何問題,請聯系我。我并沒有在MeP以外的任何情況下做測試由于我太懶了,但是我希望這個代碼能夠更通用一些。
下載
CGEN代碼下載: https://github.com/yifanlu/cgen
MeP模塊下載: https://github.com/yifanlu/toshiba-mep-idp
MeP模塊有基礎的棧追蹤功能并且可以人工添加調用標識。我有時間的時候,我會繼續為大家提供剩余的IDA CGEN模塊支持。
* 原文鏈接: yifan.lu ,轉載請注明來自FreeBuf黑客與極客(FreeBuf.COM)
來自: http://www.freebuf.com/articles/security-management/92938.html