ARM彙編基礎(iOS逆向)

來源:互聯網
上載者:User

標籤:

1. ARM彙編基礎

在逆向一個功能的時候,往往需要分析大量的彙編代碼,在iOS逆向中,ARM彙編是必須掌握的語言,本文總結了ARM彙編的基礎知識,如果你想瞭解更多,請參考狗神的小黃書《iOS逆向逆向工程》或ARM官方手冊.

1.1 寄存器,記憶體和棧

在ARM彙編裡,操作對象是寄存器,記憶體和棧
ARM的棧遵循先進後出,是滿遞減的,向下增長,也就是開口向下,新的變數被存到棧底的位置;越靠近棧底,記憶體位址越小
一個名為stackPointer的寄存器儲存棧的棧底地址,成為棧地址.
可以把一個變數給入棧(push)以儲存它的值,也可以讓它出(pop棧),恢複變數的原始值.在實際操作中,棧地址會不斷變化;但是在執行一塊代碼的前後,棧地址應該是不變的,不然程式就要出問題,

1.2 特殊用途的寄存器

ARM處理器中的部分寄存器有特殊用途 如下所示:

寄存器 用途
R0-R3 傳遞參數與傳回值
R7 幀指標,指向母函數於被調用子函數在棧中的交接
R9 在iOS3.0以前被系統保留
R12 內部程序呼叫儲存空間,dynamic linker會用到它
R13 sp寄存器
R14 LR寄存器,儲存函數返回地址
R15 PC寄存器
1.3 分支跳轉與條件判斷

處理器名為”Program counter”(簡稱PC)的寄存器用於存放下一條指令的地址.一般情況下,電腦一條接一條地順序執行指令,處理器執行完一條指令後將PC加1,讓它指向下一條指令.(1-1)
處理器順序執行指令1到指令5(2-2),但是如果把PC的值變一變,指令執行的順序就完全不同

指令執行順序被打亂,變成了指令1,指令5,指令4,指令2,指令3,指令6,這種亂序的學名叫做”分支”,或者”跳轉”,它使迴圈和subroutime成為可能,例如:

```// endless() 函數endless:    操作  運算元1, 運算元2    分支  endless    返回  // 死迴圈,執行不到這裡啦!```

在實際情況中,滿足一定條件才得以觸發的分支是最實用的,這種分支成為條件分支.if else 和 while都是基於條件分支實現的,在ARM彙編中,分支的條件一般有4種:

  • □ 操作結果為0(或不為0);
  • □ 操作結果為負數;
  • □ 操作結果有進位;
  • □ 運算溢出(比如兩個正數相加得到的數超過了寄存器位元).

這些條件的判斷準則(falg)存放在程式狀態寄存器(Program Status Register,PSR)中,資料處理相關指令會改變這些flag,分支指令再根據這些flag決定是否跳轉.下面的虛擬碼展示了一個for迴圈

for:    相加   A,#1    比較  A,#16    不為0則跳轉到for    /* 此迴圈將A和#16作比較,如果兩者不相等,則將A加1,繼續比較.     如果兩者相等,則不再迴圈,繼續往下執行. */
2. ARM/THUMB指令解讀

ARM處理器用到的指令集分為ARM和THUMB兩種:ARM指令長度均為32bit,THUMB指令長度為16bit.所有指令可大致分為三類,分別為,數組操作指令,記憶體操作指令和分支指令.

2.1 資料操作指令

資料操作指令有以下2條規則:
* 所有的運算元均為32bit;
* 所有的結果均為32bit,且只能存放在寄存器當中.
總的來說,資料操作指令的基本格式是:cp{cond}{s} Rd,Rn,Op2

其中,”cond”和”s”是另個可懸尾碼;”cond”的作用是指定指令”op”在什麼條件下執行,共有17中條件:

指令 條件
EQ 結果為0(EQual to 0)
NE 結果不為0(Not Equal to 0)
CS 有進位或借位(Carry Set)
HS 同CS(unsigned Higer or Same)
CC 沒有進位或借位(Carry Clear)
LO 同CC(unsigned LOwer)
MI 結果小於0(MInus)
PL 結果大於等於0(PLus)
VS 溢出(Overflow Set)
VC 無溢出(Overflow Clear)
HI 無符號比較大於(unsigned HIger)
LS 無符號比較小於等於(unsigned Lower or Same)
GE 有符號比較大於等於(signed Greater than or Equal)
LT 有符號比較小於(signed Less Than)
GT 有符號比較大於(signed Greater Than)
LE 有符號比較小於等於(signed Less than or Equal)
AL 無條件(Always,預設)

“cond”的用法很簡單,例如:

比較 R0, R1移動 GE R2, R0移動 LT R2, R1

比較R0和R1的值,如果R0大於等於R1,則R2 = R0;否則R2 = R1.
“s”的作用是指定指令”op”是否設定了flag,共有下面4中flag:
N(Negative)
如果結果小於0則置1,否則置0;

Z(zero)
如果結果是0則置1,否則置0;

C(Carry)
對於加操作(包括CMN)來說,如果產生進位則置1,否則置0;對於減操作(包括CMP來說),Carry相當於Not-Borrow,如果產生借位則置0,否則置1;對於有移位的非加/減操作來說,C置移出值得最後一位;對於其他的非加/減操作來說,C的值一般不變;

V(overflow)如果操作導致溢出,則置1,否則置0

需要注意一點的是,C flag表示無符號數運算結果是否溢出;V flag表示有符號數運算結果是否溢出.

算數操作指令可以大致分為4類:

  • 1.算數操作

ADD R0,R1,R2; ——————> R0 = R1 + R2

ADC R0,R1,R2; ——————> R0 = R1 + R2 + C(array)

SUB R0,R1,R2; ——————> R0 = R1 - R2

SBC R0,R1,R2; ——————> R0 = R1 - R2 - !C

RSB R0,R1,R2; ——————> R0 = R2 - R1

RSC R0,R1,R2; ——————> R0 = R2 - R1 - !C

算數操作中,ADD和SUB為基礎操作,其他均為兩者的變種.RSB是”Reverse Sub”的縮寫,僅僅是把SUB的兩個運算元調換了位置而已;以”C”結尾的變種代表沒有進位和借位的加減法,當產生進位或者借位時,將Carrry flag 置為1.

  • 2.邏輯操作

AND R0,R1,R2; ——————> R0 = R1 & R2

ORR R0,R1,R2; ——————> R0 = R1 | R2

EOR R0,R1,R2; ——————> R0 = R1 ^ R2

BIC R0,R1,R2; ——————> R0 = R1 &~ R2

MOV RO,R2; ——————> R0 = R2

MVN R0,R2; ——————> R0 = ~R2

邏輯操作指令都已經用C操作符說明了作用,但是C操作符裡的移位操作並沒有對位的邏輯操作指令,ARM採用了桶式移位,共有四種指令:

LSL 邏輯左移

LSR 邏輯右移

ASR 算術右移

ROR 迴圈右移

  • 3.比較操作

    CMP R1,R2; ——————> 執行R1 - R2並依結果設定flag


CMN R1,R2; ——————> 執行R1 + R2並依結果設定flag

TST R1,R2; ——————> 執行R1 & R2並依結果設定flag

TEQ R1,R2; ——————> 執行R1 ^ R2並依結果設定flag

比較操作其實就是改變flag的算術操作或邏輯操作,只是操作結果不保留在寄存器裡而已.

  • 4.乘法操作

MUL R4,R3,R2 ——————> R4 = R3 * R2

MLA R4,R3,R2,R1 ——————> R4 = R3 * R2 + R1

乘法操作的運算元必須來自寄存器

2.2 記憶體操作指令

記憶體操作指令的基本格式是:

op{cond}{type} Rd,[Rn,Op2]

其中Rn是基底位址暫存器,用於存放基地址;”cond”的作用與資料操作指令相同;”type”指定指令”op”操作的資料類型,共有四種:

B(unsigned Byte)無符號byte(執行時擴充到32bit,以0填充);SB(signed Byte)有符號byte(僅用於LDR指令;執行時擴充到32bit,以符號位填充);H(unsigned Halfword)無符號halfword(執行時擴充到32bit,以0填充);SH(Signed Halfword)有符號halfword(僅用於LDR指令;執行時擴充到32bit,以符號位填充).

如果不指定”type”,則預設是word
ARM記憶體操作基礎指令只有2個,LDR(loaD Register)將資料從記憶體中讀出來,存到寄存器中;STR(STore Register)將數組從寄存中讀出來,存到記憶體中.兩個指令的使用方式如下:

  • LDR
LDR Rt,[Rn {,#offset}]          ;   Rt = *(Rn {+ offset}),{}代表可選LDR Rt,[Rn, #offset]!           ;   Rt = *(Rn + offset);Rn = Rn + offsetLDR Rt,[Rn], #offset            ;   Rt = *Rn;Rn = Rn + offset
  • STR
STR Rt,[Rn {,#offset}]          ;   *(Rn {+ offset}) = RtSTR Rt,[Rn, #offset]!           ;   *(Rn + offset) = Rt;   Rn = Rn + offsetSTR Rt,[Rn], #offset            ;   *Rn = Rt;  Rn = Rn + offset

此外,LDR和STR的變種LDRD和STRD還可以操作雙字(DoubleWord),即一次性操作兩個寄存器,其基本格式如下:op{cond} Rt,Rt2, [Rn {, #offset}]
其用法與原型類似,如下:

  • STRD
SRTD R4,R5, [R9,#offset]    ; *(R9 + offset) = R4;*(R9 + offset + 4) = R5
  • LDRD
LDRD R4,R5,[R9,#offset]     ; R4 = *(R9 + offset); R5 = *(R9+offset+4)

除LDR和STR外,還可以通過LDM(LoaD Multiple)和STM(STore Multipe)進行塊傳輸,一次性操作多個寄存器.塊傳輸指令的基本格式是

op{cond}{}mode] Rd{!},reglist

其中Rd是基底位址暫存器,可選的”!”制定Rd變化後的值是否寫會Rd, reglist是一系列寄存器,用大括弧括起來,它們之間可以用”,”分割,也可以用”-“表示一個範圍,比如,{R4-R6,R8}表示寄存器,R4,R5,R6,R8;這些寄存器的順序是按照自身的編號由小到大排列的,與大括弧內的排列順序無關.

需要特別注意的是,LDM和STM的操作方向與LDR和STR完全相反:LDM是把從Rd開始,地址連續的記憶體資料存入reglist中,STM是把reglist中的值存入從Rd開始,地址連續的記憶體中.此處特別容易混淆

“cond” 的作用與資料操作指令相同.”mode”指定R4值得變化的4中規律,如下所示:

IA(Increament After)每次傳輸後增加Rd的值;IB(Increament Before)每次傳輸前增加Rd的值DA(Decrement After) 每次傳輸後減少Rd的值;DB(Decreament Before)每次傳輸前減少Rd的值.

這是什麼意思呢?下面以LDM為代表,舉一個簡單的例子,相信大家一看就明白了.在(塊傳輸指令類比環境)中,R0指向的值是5.

在執行以下命令後,R4,R5,R6的值分別變成:

foo():LDMIA R0, {R4 - R6};    R4 = 5, R5 = 6, R6 = 7LDMIB R0, {R4 - R6};    R4 = 6, R5 = 7, R6 = 8LDMDA R0, {R4 - R6};    R4 = 5, R5 = 4, R6 = 2LDMDB R0, {R4 - R6};    R4 = 4, R5 = 3, R6 = 3

STM指令的作用方式與此類似,不再贅述.LDM和STM的操作與LDR和STR完全相反

2.3 分支指令

分支指令可以分為無條件分支和條件分支兩種.

  • 無條件分支
B Label;PC = LabelBL Label;LR = PC - 4;PC = LabelBX Rd ;PC = Rd並切換指令集eg:foo():    B Label ; 跳轉到Label處並往下執行    ......  ; 得不到執行Label:    ......
  • 無條件分支

跳轉分支的cond是依照掐面的flag來判斷的,它們的對應關係如下:

cond flag
EQ Z = 1
NE Z = 0
CS C = 1
HS C = 1
CC C = 0
LO C = 0
MI N = 1
PL N = 0
VS V = 1
VC V = 0
HI C = 1 & Z = 0
LS C = 0
GE N = V
LT N != V
GT Z = 0 & N = V
LE Z = 1

在條件分支指令錢會有一條資料操作指令來設定flag,分支指令根據falg的值來決定代碼走向,舉例如下:

Label:Lable1:    LDR R0, [R1], #4    CMP R0, 0; 如果R0 == 0,Z =1 ; 否則Z = 0    BNE Label ; Z == 0則跳轉
2.4 THUMB指令

THUMB指令集是ARM指令集的一個子集,每條THUMB指令均為16bit;因此THUMB指令比ARM指令更節省空間的,且在16位元據匯流排上的傳輸效率更高.有得必有失,除了”b”之外,所有的THUMB指令均無法條件執行;桶式移位無法結合其他指令執行;大多數THUMB指令只能使用R0-R7這8個寄存器等.相對於ARM指令,THUMB指令的特點如下:

  • 指令數量減少
  • 沒有條件執行
  • 所有指令預設附帶*
  • 桶式移位無法結合其他指令執行
  • 寄存器使用受限
  • 立即數和第二運算元使用有限
  • 不支援資料寫回

ARM彙編基礎(iOS逆向)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.