芯片IC單片機解密百科

十年專注單片機解密

ARM反匯編指令

一、概述


本節主要以幾條ARM指令為例,講解如何根據文檔理解ARM指令,以及ARM匯編知識在分析中的應用。

手機遊戲基本上都運行在ARM指令集的CPU上,了解ARM匯編有助於BBIN导航對遊戲的分析深入native層,為BBIN导航修改遊戲邏輯提供分析基礎。

現在的遊戲的邏輯代碼多是寫成腳本形式,最常見的是Unity3D的C#形式,cocos2dx的lua形式,以及Unity3D的C#和lua混編形式,除此之外基於純C++的cocos2dx遊戲已經相對比較少。

了解匯編就能對遊戲引擎進行分析,定位到腳本的加載和執行函數,可以輸出函數執行Log,腳本加載Log,dump原始腳本等,對遊戲邏輯進行更直接的監視和控製。

建議安裝IDA或其他反匯編工具,加載一個ARM指令集的so文件或者ELF可執行文件,在實踐中對照學習。


1.1 ARM體係簡介


ARM架構,是一個32位精簡指令集處理器架構,是目前最為普遍使用的手機處理器架構。

ARM架構表:

關於架構,BBIN导航需要了解的是自Cortex A8以後的架構都支持硬件斷點,但各個廠商在出廠的時候並未開啟這個功能。

ARM CPU有15個通用寄存器,分別編號為R0~R15。其中R13也叫SP,用於保存棧頂地址;R14也叫LR,用於保存函數的返回地址;R15也叫PC,由於ARM架構的指令預讀,實際運行時PC寄存器並不指向當前執行的指令,而是指向當前指令地址加8處。

 

二、指令樣例講解

指令比較多,不可能也沒必要一條一條講解,以下會拿幾條常用的指令作為例子講解,其他沒有涉及的指令都可以在上麵鏈接下載的文檔裏找到更詳細的解釋。


2.1 B/BL


B/BL指令是最常用的幾條指令,作用是跳轉到指定的位置,相關的文檔解釋在ARM7-TDMI-manual-pt2.pdf的4-8頁。

指令的二進製解釋圖:



Cond位表示這條指令是會在何種條件下執行,具體到實際中每個條件都會給B指令加上後綴,比如“BNE/BEQ/BGT”等,也就是BBIN导航熟悉的條件跳轉指令,作為程序的分支使用。

如圖是IDA反匯編中一個條件跳轉示例:


圖中兩條指令表示“如果R2等於R0則跳轉到0x1C04”。

 

“101”則是B指令的標識碼。

L位表示是否需要保存該指令的下一條指令到LR寄存器,這類B指令就叫BL指令,一般用於跳轉到函數內部,其中LR保存返回地址。

Offset是目標地址與該指令的相對偏移(需要考慮到CPU的指令預讀,以及左移兩位,偏移是按4字節對齊的)。

下麵還是按上圖的那條指令作例子實際分析各個字段的意思:


對應二進製


按照二進製解釋圖中的格式排列(從高位到低位)就是:

0000 101 0 0000 0000 0000 0000 0000 1011

Cond op L offset

Cond表示EQ,等於的時候跳轉(ARM7-TDMI-manual-pt2.pdf的4-5頁)。

101是標識。

L是0,表示不需要保存下一條指令。

Offset是1011,左移兩位就是101100,也就是0x2C。當CPU執行到該指令時(0x1BD0處),PC應該指向0x1BD8,所以實際跳轉地址就是0x1BD8+0x2C,也就是0x1C04。


2.2 LDR/STR


LDR/STR指令用於向內存讀/寫數據,相關文檔在ARM7-TDMI-manual-pt2.pdf的4-28頁。

指令的二進製解釋如圖:



看起來很複雜,但BBIN导航需要關注的地方不多。不再做詳細解釋,直接看例子:

指令


對應二進製


按照二進製格式排列:

1110 01 011001 1100 0010 0000 0000 0000

Cond op IPUBWL  Rn Rd Offset

Cond表示任何條件都可以(ARM7-TDMI-manual-pt2.pdf的4-5頁)。

01是指令標識。

I是0,表示offset是一個立即數。

B是0,表示傳輸的是一個字,ARM指令集中的“字”是8個字節。

L是1,表示是讀內存。

Rn是0xC,表示基址寄存器是R12.

Rd是2,表示把內存值讀到R2。

Offset是0,表示基址寄存器需要加上0.

所以解釋成指令就是:


意思就是,讀取R12寄存器指向的內存到R2寄存器中,一共讀取4個字節(一個字)。

 

三、THUMB指令


THUMB指令是ARM指令的精簡版,在日常的分析中也經常會碰到,具體的文檔在ARM7-TDMI-manual-pt3.pdf。分析方法同上,不再綴述。一段程序中有可能同時出現這兩種指令集,主要靠CPSR寄存器中的標識位確定當前執行的是何種指令集:


紅框即是當前指令集狀態,1表示在THUMB指令集,0表示在ARM指令集。

 

四、函數傳參


從匯編層麵來看,不同的編譯方式對函數傳參的做法不同,一種比較常見的做法是把前四個參數放到R0~3寄存器中,剩下的參數放到棧中,函數的返回值放在R0中。

如圖,C語言中一個簡單的函數調用:


調用它的匯編代碼則是:


可以看到,調用R6中的函數(old_compile)後,把返回值作為參數直接調用hasHooked函數。

 

五、浮點數基礎


在看匯編代碼的時候,有時候會碰到浮點數以及浮點指令,這裏簡單說說浮點數在內存中的二進製形式。具體就是IEEE 754標準。

32位浮點數的二進製格式如圖:


S為符號位,Exp為指數位,Fraction為有效數字。單片機解密技術大全中也談到指數,指數部分即使用所謂的偏正值形式表示,偏正值為實際的指數大小與一個固定值(32位的情況是127)的和。采用這種方式表示的目的是簡化比較。因為,指數的值可能為正也可能為負,如果采用補碼表示的話,全體符號位S和Exp自身的符號位將導致不能簡單的進行大小比較。正因為如此,指數部分通常采用一個無符號的正數值存儲。單精度的指數部分是−126~+127加上偏移值127,指數值的大小從1~254(0和255是特殊值)。浮點小數計算時,指數值減去偏正值將是實際的指數大小。(摘自維基百科https://zh.wikipedia.org/wiki/IEEE_754)

具體來看個例子,浮點數十六進製:

00 00 80 3F

也就是0x3F800000

二進製

0 01111111 000 0000 0000 0000 0000 0000

S Exp Fraction

S為0,代表這是一個正數。

Exp為0x7F,根據偏正值定義,真實值是0x7F-0x7F=0。

Fraction為0,注意,這裏的Fraction是二進製小數。例如,Fraction為1011001則對應二進製小數1.1011001(整數部分恒為1,後接二進製小數),轉換成十進製就是2^(-0)+2^(-1)+2^(-3)+2^(-4)+2^(-7).

計算方法就是(S)F*2^E,其中S是符號位,E是指數位Exp,F是有效位Fraction。

所以這個值就是+2^(-0),也就是1.