溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

iOS逆向工程之Hopper中的ARM指令

發(fā)布時間:2020-06-13 09:50:52 來源:網(wǎng)絡(luò) 閱讀:530 作者:zsdnr 欄目:移動開發(fā)

一、Hopper中的ARM指令

ARM處理器就不多說了,ARM處理器因為低功耗等原因,所以大部分移動設(shè)備上用的基本上都是ARM架構(gòu)的處理器。當(dāng)然作為移動設(shè)備的Android手機,iPhone也是用的ARM架構(gòu)的處理器。如果你想對iOS系統(tǒng)以及你的應(yīng)用進(jìn)一步的了解,那么對ARM指令集的了解是必不可少的,ARM指令集應(yīng)該也算得上是iOS逆向工程的基礎(chǔ)了。

當(dāng)你使用Hopper進(jìn)行反編譯時,里邊全是ARM的指令,那是看的一個爽呢。下面就是使用Hopper打開MobileNote.app的一個Hopper的界面。從主窗口中可以看到全是ARM的指令呢,如果你對ARM指令不了解,那么如何進(jìn)行分析呢,對吧。所以對ARM指令的了解,是iOS逆向工程的基礎(chǔ)呢。今天這篇博客就總結(jié)一下ARM指令集的基礎(chǔ)指令。

  iOS逆向工程之Hopper中的ARM指令

 

Hopper的功能是非常強大的,在Hopper中你可以對ARM指令進(jìn)行修改,并且生成一個新的可執(zhí)行文件。當(dāng)然Hopper強大的功能可以幫助你更好的理解ARM匯編語言的業(yè)務(wù)邏輯,Hopper會根據(jù)ARM匯編生成相關(guān)的邏輯圖,如下所示。從下方的邏輯圖中你就能清楚的看到相關(guān)ARM匯編的指令邏輯。紅線表明條件不成立時的跳轉(zhuǎn),藍(lán)線則表明條件成立時的跳轉(zhuǎn)。

  iOS逆向工程之Hopper中的ARM指令

 

Hopper的功能強大到可以將ARM匯編生成相應(yīng)的偽代碼,如果你看ARM指令不直觀的話,那么偽代碼對你來說會更好一些。下方就是Hopper根據(jù)ARM指令生成的偽代碼,如下所示。

  iOS逆向工程之Hopper中的ARM指令

貌似有點跑偏了,今天的主題是ARM指令集,Hopper的東西就不做過多贅述了。

 

二、ARM指令集綜述

ARM指令主要是對寄存器,棧、內(nèi)存的操作。寄存器位于CPU中,個數(shù)少速度快,ARM指令集中大部分指令都是對寄存器操作,但有些指令是對棧和內(nèi)存的操作。下方會對操作棧、寄存器以及內(nèi)存的指令進(jìn)行介紹。

 

1.棧操作---- push 與pop

先簡單的聊一下棧的概念,“?!闭f白了就是數(shù)據(jù)結(jié)構(gòu)的一種,棧的數(shù)據(jù)結(jié)構(gòu)具有LIFO(last in first out) ---- 后進(jìn)先出的特點。棧在ARM中所指的其實是一塊具有棧數(shù)據(jù)結(jié)構(gòu)特點內(nèi)存區(qū)。棧中主要用來暫存寄存器中的值得,比如R0寄存器正在使呢,可是現(xiàn)在有一個優(yōu)先級比較高的函數(shù)要使用R0, 那么就先把R0的值Push到棧中暫存,然后等R0被優(yōu)先級更高的函數(shù)使用完畢后在從棧中Pop出之前的值。在函數(shù)調(diào)用時一般會對棧進(jìn)行操作。

對棧操作的命令就是push和pop了,一般會成對出現(xiàn),在函數(shù)開始時將該函數(shù)執(zhí)行時要使用的寄存器中的值push入棧,然后在函數(shù)結(jié)束時將之前push到棧中的值在pop到相應(yīng)的寄存器中。

下方就是push和pop的用法的一個實例。在下方函數(shù)開始執(zhí)行前,將該函數(shù)要使用的寄存器r4, r5, r7, lr使用push進(jìn)行入棧操作,lr是該函數(shù)執(zhí)行后要返回的地址。在函數(shù)執(zhí)行完畢后,使用pop命令將函數(shù)執(zhí)行前入棧的值在pop到相應(yīng)的寄存器中。有一點需要注意的是將lr寄存器中的值在函數(shù)結(jié)束后pop到pc (Program Counter)寄存器中,pc寄存器中存儲的是將要執(zhí)行的命令的地址。這樣一來,函數(shù)執(zhí)行后就會返回到之前執(zhí)行的地址上繼續(xù)執(zhí)行。

  iOS逆向工程之Hopper中的ARM指令

 

2. pc寄存器中的中的標(biāo)志位

此處我們以32位指令為例,pc寄存器中的后四位是標(biāo)志位,第28 - 31位分別對應(yīng)著V (oVerflow),C (Carry),Z (Zero),N (Negative)。下面分別來介紹一下這四種符號所表示的狀態(tài)。

  • N (Negative): 如果結(jié)果是負(fù)數(shù)則置位。

  • Z (Zero): 如果結(jié)果是零則置位。

  • C (Carry): 如果有進(jìn)位則置位。

  • V (Overflow): 在發(fā)生溢出的時候置位。

 

3. 命令操作符

下方是ARM指令集中常用的算術(shù)操作:

(1)加法操作

  • ADD R0, R1, R2       ; R0 = R1 + R2

    • 上面的命令就比較簡單,就是講兩個數(shù)值進(jìn)行相加。

  • ADC R0, R1, R2       ; R0 = R1 + R2 + C (Carry)

    • 帶進(jìn)位的加法,ADC將把兩個操作數(shù)加起來,并把結(jié)果放置到目的寄存器中。ADC使用了C--進(jìn)位標(biāo)志,這樣就可以做比32位大的加法了。下方就是128位的數(shù)字進(jìn)行加法操作的匯編代碼。

    • 我們現(xiàn)在要對一個128位的數(shù)字進(jìn)行加法操作,因為我們使用的是32位的寄存器,所以要存儲一個128位的數(shù)字,我們需要4個(128 / 32 = 4)寄存器。所以我們假設(shè)R0,R1,R2,R3寄存器中分別由低到高存儲著第一個數(shù)字,而R4, R5, R6, R7存儲著第二個數(shù)字。下方就是兩個128數(shù)字相加操作的ARM匯編指令。我們將結(jié)果存儲在R8, R9, R10, R11這四個寄存器中。首先我們執(zhí)行的是將兩個數(shù)的最低位相加并設(shè)置C標(biāo)志位(ADDS R8, R0, R4),然后在進(jìn)行下一位的操作,對R1和R5中的值進(jìn)行相加,在相加后再加上上次操作的進(jìn)位,然后再設(shè)置標(biāo)志位,以此類推。這樣我們最終的值就存儲在了R8-R11這四個寄存器中。

     iOS逆向工程之Hopper中的ARM指令

(2)減法操作

  • SUB R0, R1, R2       ; R0 = R1 - R2

    • 這個命名比較簡單,就是使用R1寄存器中的值減去R2寄存器中的值,然后存儲到R0中。

  • SBC R0, R1, R2       ; R0 = R1 - R2 - !C

    • 帶借位的減法,假如我們當(dāng)前的寄存器是32Bit, 如果兩個64bit的數(shù)值進(jìn)行減法操作就要使用到SBC借位操作。因為當(dāng)兩個數(shù)值在進(jìn)行減法操作時,如果需要借位時就會把C標(biāo)志位進(jìn)行清零操作,所以在進(jìn)行SBC操作時需要將C標(biāo)志位進(jìn)行取反操作。下面我們一128位數(shù)值相減為例。該實例與上述的ADC命令類似,在此就不做過多贅述了。

    iOS逆向工程之Hopper中的ARM指令 

  • RSB R0, R1, R2       ; R0 = R2 - R1

    • 反向減法

  • RSC R0, R1, R2       ; R0 = R2 - R1 - !C

    • 帶借位的反向減法,上面這兩個命令與SUB和SBC命令差不多,都是進(jìn)行減法操作的,不過操作數(shù)的計算順序不同。

(3)、乘法指令

在ARM指令集中,乘法指令有兩種第一個是MUL, 第二個是帶累加的乘法MLA。當(dāng)然,這兩個指令使用起來都不復(fù)雜。

  • MUL: 乘法指令 MUL{條件}{S} R0, R1, R2     ;R0 = R1 * R2

  • MLA: 乘法累加指令 MLA{條件}{S} R0, R1, R2, R3   ;R0 = R1 * R2 + R3

 

(4)、邏輯操作

邏輯操作比較好理解一些,與我們編程中使用的邏輯操作大同小異,無非是一些與、或、非、異或這些操作。

  • AND R0, R1, R2       ; R0 = R1 & R2 

    • 與操作, 1 & 1 = 1, 1 & 0 = 1, 0 & 1 = 1,0 & 0 = 0;

  • ORR R0, R1, R2       ; R0 = R1 | R2

    • 或操作, 1 | 1 = 1, 1 | 0 = 1, 0 | 1 = 1, 0 | 0 = 0;

  • EOR R0, R1, R2       ; R0 = R1 ^ R2

    • 異或,1 ^ 1 =  1,  1 ^ 0 = 0,  0 ^ 1 = 0, 0 ^ 0 = 1;

  • BIC R0, R1, R2         ; R0 = R1 &~ R2

    • 位清除指令,現(xiàn)將R2進(jìn)行取反,然后再與R1進(jìn)行與操作。R1 & (~R2)

    • 將R0的后四位清零:BIC R0, R0,#0x0F

  • MOV R0, R1      ;R0 = R1

    • 賦值操作,將R1的值賦給R0

  • MVN R0, R1      ;R0 = ~R1

    • 按位取反操作,將R1的每一位進(jìn)行取反操作,然后賦值給R0

 

4、寄存器的裝載和存儲

有時我們需要將內(nèi)存中的數(shù)據(jù)裝載到寄存器中進(jìn)行操作,或者將寄存器中運算后的數(shù)據(jù)存儲到內(nèi)存中,此時我們就會用到寄存器的裝載和存儲的相關(guān)命令。下方就一一的總結(jié)了這些命令。

(1)、傳送單一數(shù)據(jù)

LDR{條件} Rd, <地址>   ;將地址中的數(shù)據(jù)加載到Rd寄存器中

STR{條件} Rd, <地址>   ;將寄存器Rd中的數(shù)值存儲到<地址>中的內(nèi)存中

LDR{條件}B  Rd, <地址>   ;將內(nèi)存地址所對應(yīng)值得低8位加載到Rd的寄存器中。

STR{條件}B  Rd, <地址>   ;將寄存器Rd的后8為存的到內(nèi)存地址中。

  • LDR (Load Register) : 將數(shù)據(jù)從內(nèi)存中取出,加載到寄存器。

    • LDR Rt, [Rn], #offset   ;Rt = *Rn; Rn = Rn + offset

    • LDR Rt, [Rn, #offset]!  ; Rt = *(Rn + offset); Rn = Rn + offset

 

  • STR (Store Register): 將寄存器中的數(shù)據(jù),存儲到內(nèi)存。

    • STR Rt, [Rn], #offset   ;*Rn = Rt; Rn = Rn + offset

    • STR Rt, [Rn, #offset]!  ;*(Rn + offset) = Rn; Rn = Rn + offset(地址回寫)

 

(2)、一次傳送兩個數(shù)據(jù)

  • LDRD (Load Register Double): 一次填充兩個寄存器

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

 

  • STRD (Store Register Double):一次存儲兩個值到內(nèi)存

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

 

(3)、塊數(shù)據(jù)存取 

  • LDM (Load Mutiple): 將一塊數(shù)據(jù)從寄存器中加載到內(nèi)存中(reg list)。

  • STM (Store Multiple): 將塊數(shù)據(jù)從內(nèi)存中加載到寄存器。

  • LDM與STM塊內(nèi)存操作都有一個后綴,下方就是這四種條件,我們假設(shè)下方R0寄存器中存儲的值是0(R0 = 6)

    • 如:LDMDB R0, {R1 - R3}    ;R1 = 5, R2 = 4, R3 = 3

    • 如: LDMDA R0, {R1 - R3}    ;R1 = 6, R2 = 5, R3 = 4

    • 如:LDMIB R0, {R1 - R3}    ;R1 = 7, R2 = 8, R3 = 9

    • 如:LDMIA R0, {R1 - R3}    ;R1 = 6, R2 = 7, R3 = 8

    • IA (Increment After): 傳輸后再增加值,

    • IB (Increment Befor): 傳輸前增加值

    • DA (Decrement After):傳輸后減少值

    • DB (Decrement Before):傳輸前減少值

 

(4)、單一數(shù)據(jù)交換:SWP

SWP命令用來交換寄存器與內(nèi)存直接的值,下方是SWP的指令格式:

SWP{條件}{B} Rd, Rm, [Rn]

上述命令表示將Rn中內(nèi)存地址所指向內(nèi)存中的數(shù)據(jù)加載到Rd中,然后將寄存器Rm中的值存儲到該內(nèi)存地址指向的區(qū)域中。如果Rd = Rm, 那么Rn指向的內(nèi)存中的值就會與Rd進(jìn)行交換。如果加上條件后綴的話,就說明在滿足該條件時進(jìn)行操作,后綴B則是操作低8位。

 

5、比較、分支與條件指令

分支與條件指令是編程中不可或缺的指令,在處理一些特定的業(yè)務(wù)邏輯時會經(jīng)常使用到分支與條件指令。分支說白了就是跳轉(zhuǎn),而分支與條件結(jié)合使用就是當(dāng)滿足一定條件后進(jìn)行特定的跳轉(zhuǎn)。接下來,將總結(jié)一下ARM指令集中常用的分支指令與條件指令,更確切的說是條件后綴。

(1)、比較指令

在ARM指令集中使用到的比較指令有CMN、CMP、TEQ、TST。有一點需要注意的是CMN與CMP是算術(shù)指令,TEQ和TST屬于邏輯指令。比較指令在執(zhí)行后總是會設(shè)置標(biāo)志位(N、Z、C、V), 因為條件后綴是根據(jù)被設(shè)置的標(biāo)志位來判斷比較結(jié)果是否滿足條件的。下方會給出詳細(xì)的條件后綴。比較命令后方也是可以添加條件后綴的。

  • CMN (Compare Negative) ---- 比較負(fù)值, CMN相同于CMP, 但他允許你對負(fù)值進(jìn)行比較

    • CMN R0, R1       ;Status = R0 - R1

  • CMP (Compare) ---- 之所以說CMP,CMN指令是算術(shù)指令,是因為他們講操作數(shù)進(jìn)行減法操作,并且設(shè)置相應(yīng)的標(biāo)志位,但是不記錄計算結(jié)果。CMN與CMP進(jìn)行的是算術(shù)減法操作,所以會影響C -- Carry標(biāo)志。

    • CMP R0, R1       ;Status = R0 - R1

  • TEQ (Test Equivalence) ---- 測試等價,TEQ對操作數(shù)進(jìn)行異或(EOR)邏輯操作,來判斷兩個操作數(shù)是否相同。因為TEQ做的是異或運算,所以不會影響Carry標(biāo)志位。

    • TEQ R0, R1           ;Status = R0 EOR R1

  • TST (Test bits) ---- 測試位,使用TST命令來檢查是否設(shè)置了特定的位。TST命中令其實是將兩個操作數(shù)進(jìn)行按位與(AND)操作,將結(jié)果存儲在標(biāo)志位中??梢允褂肨ST來測試寄存器中某些位的特定值。

    • TST R0, R1        ;Status = R0 AND R1

(2)、分支指令

常用的分支指令是B、BL、BX這三個指令。

  • B Lable ;該指令表示將PC設(shè)置成Lable, 而PC就是指向下一條將要執(zhí)行的指令,所以B Lable執(zhí)行后,接下來就會跳轉(zhuǎn)到Label出進(jìn)行下一條命令的執(zhí)行。

  • BL Label ; 執(zhí)行該指令說明將LR設(shè)置成PC - 4, 然后再將PC設(shè)置成Lable。在執(zhí)行BL Lable這條命令時,PC中存儲的就是當(dāng)前BL這條命令,而PC - 4就是上一條指令的地址,將PC - 4賦值給LR,也就是記錄下跳轉(zhuǎn)執(zhí)行完指令后要返回的地址。如果BL在添加上一些條件,那么BL{條件}就可以進(jìn)行循環(huán)了。

  • BX Rd ; 該指令說明將Rd賦值給PC, 然后切換指令集(如從ARM指令集切換到Thumb指令集)。

 

(3)、條件后綴

上述的分支指令與條件后綴結(jié)合才能發(fā)揮其強大的功能和作用,解析這部分介紹的是就是我們的條件后綴。條件后綴不能單獨的使用,要和其他命令一塊結(jié)合使用,然后根據(jù)條件的結(jié)果來做一些操作。下方是所有條件后綴,條件是否成立是根據(jù)NZCV這四個標(biāo)志位來判斷的,因為我們在對一些數(shù)值進(jìn)行比較時,會設(shè)置相應(yīng)的標(biāo)志位。然后我們就可以使用這些標(biāo)志位來判斷條件是否成立。NZCV就是我們之前所提到的幾個標(biāo)志位,Z(是否為零), C(是否進(jìn)位), N(是否為負(fù)), V(是否溢出)四種標(biāo)準(zhǔn)位來判斷的。

  • EQ: Equal   等于,(Z = 1)

  • NE: Not Equal   不等于 (Z = 0)

  • CS: Carry Set   有進(jìn)位 (C = 1)

  • HS: (unsigned Higher Or Same) 同CS (C = 1)

  • CC: (Carry Clear) 沒有進(jìn)位 (C = 0)

  • LO: (unsigned Lower) 同CC  (C = 0)

  • MI: (Minus) 結(jié)果小于0  (N = 1)

  • PL: (Plus) 結(jié)果大于等于0 (N = 0)

  • VS: (oVerflow Set) 溢出 (V = 1)

  • VC: (oVerflow Clear) 無溢出 (V = 0)

  • HI : (unsigned Higher) 無符號比較,大于 (C = 1 & Z = 0)

  • LS: (unsigned Lower or Same) 無符號比較,小于等于 (C = 0 & Z = 1)

  • GE: (signed Greater than or Equal) 有符號比較,大于等于 (N = V)

  • LT: (signed Less Than) 有符號比較,小于  (N != V)

  • GT: (signed Greater Than) 有符號比較,大于 (Z = 0 & N = V)

  • LE: (signed Less Than or Equal) 有符號比較,小于等于 (Z = 1 | N != V)

  • AL: (Always) 無條件,默認(rèn)值

  • NV: (Never) 從不執(zhí)行

 

6. 移位操作(LSL、ASL、LSR、ASR、ROR、RRX)

移位操作在ARM指令集中不作為單獨的命令使用,它在指令格式中是一個字段。接下來將會介紹一下各種移位操作。如果你之前學(xué)過“數(shù)字電路”這門課的話,那么你肯定對這些移位操作并不陌生。

(1)、LSL ---- 邏輯左移(Logical Shift Left)與 ASL ---- 算術(shù)左移 (Arithmetic Shift Left)

邏輯左移與算術(shù)左移的操作是一樣的,都是將操作數(shù)向左移位,低位補零,移除的高位進(jìn)行丟棄。接下來我們來看一個示例,根據(jù)這個示例來看一下LSL或者ASL的工作方式。

MOV  R0, #5

MOV  R1, R0, LSL #2

上述命令,就是將5存儲到R0寄存器上(R0 = 5), 然后將R0邏輯左移2位后傳送到R1寄存器中。十進(jìn)制5的二進(jìn)制數(shù)值是0101,進(jìn)行邏輯左移2位就是0001_0100, 也就是十進(jìn)制中的20。其實沒邏輯左移1位就相當(dāng)于原數(shù)值進(jìn)行乘2操作,5邏輯左移2位其實就是5 x 2^2 = 20。下方是該操作的原理圖

  iOS逆向工程之Hopper中的ARM指令

 

(2)、LSR ---- 邏輯右移(Logical Shift Right)

邏輯右移與邏輯左移是相對的,邏輯右移其實就是往右移位,左邊補零。用法與LSL類似,在此就不做過多贅述了。

 

(3)、ASR ---- 算術(shù)右移(Arithmetic Shift Right)

ASR與LSR類似,唯一不同的是,LSR的高位補零,而ASR的高位補符號位。符號位為1,那么就補1,符號位為0那么就補零。

 

(4)、ROR ---- 循環(huán)右移(Rotate Right)

循環(huán)右移,見名知意,就是循環(huán)著往右移動,右邊移除的位往高位進(jìn)行填補。


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI