ARM匯編基礎(chǔ)
在逆向一個(gè)功能的時(shí)候,往往需要分析大量的匯編代碼,在iOS逆向中,ARM匯編是必須掌握的語(yǔ)言,本文總結(jié)了ARM匯編的基礎(chǔ)知識(shí),如果你想了解更多,請(qǐng)參考狗神的小黃書(shū)《iOS逆向逆向工程》或ARM官方手冊(cè).
寄存器,內(nèi)存和棧
在ARM匯編里,操作對(duì)象是寄存器,內(nèi)存和棧
ARM的棧遵循先進(jìn)后出,是滿遞減的,向下增長(zhǎng),也就是開(kāi)口向下,新的變量被存到棧底的位置;越靠近棧底,內(nèi)存地址越小
一個(gè)名為stackPointer的寄存器保存棧的棧底地址,成為棧地址.
可以把一個(gè)變量給入棧(push)以保存它的值,也可以讓它出棧(pop),恢復(fù)變量的原始值.在實(shí)際操作中,棧地址會(huì)不斷變化;但是在執(zhí)行一塊代碼的前后,棧地址應(yīng)該是不變的,不然程序就要出問(wèn)題,
特殊用途的寄存器
ARM處理器中的部分寄存器有特殊用途 如下所示:
寄存器 | 用途 |
---|---|
R0-R3 | 傳遞參數(shù)與返回值 |
R7 | 幀指針,指向母函數(shù)與被調(diào)用子函數(shù)在棧中的交界 |
R9 | 在iOS3.0以前被系統(tǒng)保留 |
R12 | 內(nèi)部過(guò)程調(diào)用存儲(chǔ)器,dynamic linker會(huì)用到它 |
R13 | sp寄存器 |
R14 | LR寄存器,保存函數(shù)返回地址 |
R15 | PC寄存器 |
分支跳轉(zhuǎn)與條件判斷
處理器名為”P(pán)rogram counter”(簡(jiǎn)稱PC)的寄存器用于存放下一條指令的地址.一般情況下,計(jì)算機(jī)一條接一條地順序執(zhí)行指令,處理器執(zhí)行完一條指令后將PC加1,讓它指向下一條指令.例如處理器順序執(zhí)行指令1到指令5,但是如果把PC的值變一變,指令執(zhí)行的順序就完全不同指令執(zhí)行順序被打亂,變成了指令1,指令5,指令4,指令2,指令3,指令6,這種亂序的學(xué)名叫做”分支”,或者”跳轉(zhuǎn)”,它使循環(huán)和subroutime(子程序)成為可能,例如:
// endless() 函數(shù)
在實(shí)際情況中,滿足一定條件才得以觸發(fā)的分支是最實(shí)用的,這種分支成為條件分支.if else 和 while都是基于條件分支實(shí)現(xiàn)的,在ARM匯編中,分支的條件一般有4種:
□ 操作結(jié)果為0(或不為0);
□ 操作結(jié)果為負(fù)數(shù);
□ 操作結(jié)果有進(jìn)位;
□ 運(yùn)算溢出(比如兩個(gè)正數(shù)相加得到的數(shù)超過(guò)了寄存器位數(shù)).
這些條件的判斷準(zhǔn)則(flag)存放在程序狀態(tài)寄存器(Program Status Register,PSR)中,數(shù)據(jù)處理相關(guān)指令會(huì)改變這些flag,分支指令再根據(jù)這些flag決定是否跳轉(zhuǎn).下面的偽代碼展示了一個(gè)for循環(huán)
for:
ARM/THUMB指令解讀
ARM處理器用到的指令集分為ARM和THUMB兩種:ARM指令長(zhǎng)度均為32bit,THUMB指令長(zhǎng)度為16bit.所有指令可大致分為三類,分別為,數(shù)組操作指令,內(nèi)存操作指令和分支指令.
數(shù)據(jù)操作指令
數(shù)據(jù)操作指令有以下2條規(guī)則:
- 所有的操作數(shù)均為32bit;
- 所有的結(jié)果均為32bit,且只能存放在寄存器當(dāng)中.
總的來(lái)說(shuō),數(shù)據(jù)操作指令的基本格式是:
op{cond}{s} Rd,Rn,Op2
其中,”cond”和”s”是另個(gè)可選后綴;”cond”的作用是指定指令”op”在什么條件下執(zhí)行,共有17中條件:
指令 | 條件 |
---|---|
EQ | 結(jié)果為0(EQual to 0) |
NE | 結(jié)果不為0(Not Equal to 0) |
CS | 有進(jìn)位或借位(Carry Set) |
HS | 同CS(unsigned Higer or Same) |
CC | 沒(méi)有進(jìn)位或借位(Carry Clear) |
LO | 同CC(unsigned LOwer) |
MI | 結(jié)果小于0(MInus) |
PL | 結(jié)果大于等于0(PLus) |
VS | 溢出(Overflow Set) |
VC | 無(wú)溢出(Overflow Clear) |
HI | 無(wú)符號(hào)比較大于(unsigned HIger) |
LS | 無(wú)符號(hào)比較小于等于(unsigned Lower or Same) |
GE | 有符號(hào)比較大于等于(signed Greater than or Equal) |
LT | 有符號(hào)比較小于(signed Less Than) |
GT | 有符號(hào)比較大于(signed Greater Than) |
LE | 有符號(hào)比較小于等于(signed Less than or Equal) |
AL | 無(wú)條件(Always,默認(rèn)) |
“cond”的用法很簡(jiǎn)單,例如:
比較 R0, R1
比較R0和R1的值,如果R0大于等于R1,則R2 = R0;否則R2 = R1.
“s”的作用是指定指令”op”是否設(shè)置了flag,共有下面4種flag:
N(Negative)如果結(jié)果小于0則置1,否則置0;
Z(zero)如果結(jié)果是0則置1,否則置0;
C(Carry)對(duì)于加操作(包括CMN)來(lái)說(shuō),如果產(chǎn)生進(jìn)位則置1,否則置0;對(duì)于減操作(包括CMP來(lái)說(shuō)),Carry相當(dāng)于Not-Borrow,如果產(chǎn)生借位則置0,否則置1;對(duì)于有移位的非加/減操作來(lái)說(shuō),C置移出值得最后一位;對(duì)于其他的非加/減操作來(lái)說(shuō),C的值一般不變;
V(overflow)如果操作導(dǎo)致溢出,則置1,否則置0
需要注意一點(diǎn)的是,C flag表示無(wú)符號(hào)數(shù)運(yùn)算結(jié)果是否溢出;V flag表示有符號(hào)數(shù)運(yùn)算結(jié)果是否溢出.
算數(shù)操作指令可以大致分為4類:
算數(shù)操作
ADD R0,R1,R2; ——————> R0 = R1 + R2ADC R0,R1,R2; ——————> R0 = R1 + R2 + C(array)SUB R0,R1,R2; ——————> R0 = R1 - R2SBC R0,R1,R2; ——————> R0 = R1 - R2 - !CRSB R0,R1,R2; ——————> R0 = R2 - R1RSC R0,R1,R2; ——————> R0 = R2 - R1 - !C算數(shù)操作中,ADD和SUB為基礎(chǔ)操作,其他均為兩者的變種.RSB是”Reverse Sub”的縮寫(xiě),僅僅是把SUB的兩個(gè)操作數(shù)調(diào)換了位置而已;以”C”結(jié)尾的變種代表沒(méi)有進(jìn)位和借位的加減法,當(dāng)產(chǎn)生進(jìn)位或者借位時(shí),將Carrry flag 置為1.
邏輯操作
AND R0,R1,R2; ——————> R0 = R1 & R2ORR R0,R1,R2; ——————> R0 = R1 | R2EOR R0,R1,R2; ——————> R0 = R1 ^ R2BIC R0,R1,R2; ——————> R0 = R1 &~ R2MOV RO,R2; ——————> R0 = R2MVN R0,R2; ——————> R0 = ~R2邏輯操作指令都已經(jīng)用C操作符說(shuō)明了作用,但是C操作符里的移位操作并沒(méi)有對(duì)位的邏輯操作指令,ARM采用了桶式移位,共有四種指令:LSL 邏輯左移 LSR 邏輯右移 ASR 算術(shù)右移ROR 循環(huán)右移
比較操作
CMP R1,R2; ——————> 執(zhí)行R1 - R2并依結(jié)果設(shè)置flag
CMN R1,R2; ——————> 執(zhí)行R1 + R2并依結(jié)果設(shè)置flagTST R1,R2; ——————> 執(zhí)行R1 & R2并依結(jié)果設(shè)置flagTEQ R1,R2; ——————> 執(zhí)行R1 ^ R2并依結(jié)果設(shè)置flag比較操作其實(shí)就是改變flag的算術(shù)操作或邏輯操作,只是操作結(jié)果不保留在寄存器里而已.
乘法操作
MUL R4,R3,R2 ——————> R4 = R3 * R2MLA R4,R3,R2,R1 ——————> R4 = R3 * R2 + R1乘法操作的操作數(shù)必須來(lái)自寄存器
內(nèi)存操作指令
內(nèi)存操作指令的基本格式是:
op{cond}{type} Rd,[Rn,Op2]
其中Rn是基址寄存器,用于存放基地址;”cond”的作用與數(shù)據(jù)操作指令相同;”type”指定指令”op”操作的數(shù)據(jù)類型,共有四種:
B(unsigned Byte)
如果不指定”type”,則默認(rèn)是word
ARM內(nèi)存操作基礎(chǔ)指令只有2個(gè),LDR(loaD Register)將數(shù)據(jù)從內(nèi)存中讀出來(lái),存到寄存器中;STR(STore Register)將數(shù)組從寄存中讀出來(lái),存到內(nèi)存中.兩個(gè)指令的使用情況如下:
LDR
LDR Rt,[Rn {,#offset}] ; Rt = *(Rn {+ offset}),{}代表可選
STR
STR Rt,[Rn {,#offset}] ; *(Rn {+ offset}) = Rt
此外,LDR和STR的變種LDRD和STRD還可以操作雙字(DoubleWord),即一次性操作兩個(gè)寄存器,其基本格式如下:
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外,還可以通過(guò)LDM(LoaD Multiple)和STM(STore Multipe)進(jìn)行塊傳輸,一次性操作多個(gè)寄存器.塊傳輸指令的基本格式是
op{cond}{}mode] Rd{!},reglist
其中Rd是基址寄存器,可選的”!”制定Rd變化后的值是否寫(xiě)會(huì)Rd, reglist是一系列寄存器,用大括號(hào)括起來(lái),它們之間可以用”,”分割,也可以用”-“表示一個(gè)范圍,比如,{R4-R6,R8}表示寄存器,R4,R5,R6,R8;這些寄存器的順序是按照自身的編號(hào)由小到大排列的,與大括號(hào)內(nèi)的排列順序無(wú)關(guān).需要特別注意的是,LDM和STM的操作方向與LDR和STR完全相反:LDM是把從Rd開(kāi)始,地址連續(xù)的內(nèi)存數(shù)據(jù)存入reglist中,STM是把reglist中的值存入從Rd開(kāi)始,地址連續(xù)的內(nèi)存中.此處特別容易混淆“cond” 的作用與數(shù)據(jù)操作指令相同.”mode”指定R4值得變化的4中規(guī)律,如下所示:
IA(Increament After)每次傳輸后增加Rd的值;
這是什么意思呢?下面以LDM為代表,舉一個(gè)簡(jiǎn)單的例子,相信大家一看就明白了.
在執(zhí)行以下命令后,R4,R5,R6的值分別變成:
foo():
STM指令的作用方式與此類似,不再贅述.LDM和STM的操作與LDR和STR完全相反
分支指令
分支指令可以分為無(wú)條件分支和條件分支兩種.
無(wú)條件分支
B Label;PC = Label
無(wú)條件分支
跳轉(zhuǎn)分支的cond是依照前面的flag來(lái)判斷的,它們的對(duì)應(yīng)關(guān)系如下:
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 |
在條件分支指令錢會(huì)有一條數(shù)據(jù)操作指令來(lái)設(shè)置flag,分支指令根據(jù)falg的值來(lái)決定代碼走向,舉例如下:
Label:
THUMB指令
THUMB指令集是ARM指令集的一個(gè)子集,每條THUMB指令均為16bit;因此THUMB指令比ARM指令更節(jié)省空間,且在16位數(shù)據(jù)總線上的傳輸效率更高.有得必有失,除了”b”之外,所有的THUMB指令均無(wú)法條件執(zhí)行;桶式移位無(wú)法結(jié)合其他指令執(zhí)行;大多數(shù)THUMB指令只能使用R0-R7這8個(gè)寄存器等.相對(duì)于ARM指令,THUMB指令的特點(diǎn)如下:
指令數(shù)量減少
沒(méi)有條件執(zhí)行
所有指令默認(rèn)附帶*
桶式移位無(wú)法結(jié)合其他指令執(zhí)行
寄存器使用受限
立即數(shù)和第二操作數(shù)使用有限
不支持?jǐn)?shù)據(jù)寫(xiě)回
查看原文:GitHub