天天看點

Android逆向基礎筆記—Android中的常用ARM彙編指令

一  ARM寄存器

1.通用寄存器

1).未分組寄存器:R0~R7

2).分組寄存器:R8~812

R13:SP,常用作堆棧指針,始終指向堆棧的頂部,當一個資料(32位)推入堆棧時,SP(R13的值減4)向下浮動指向下一個位址,即新的棧頂,當資料從堆棧中彈出時,SP(R13的值加4)向上浮動指向新的棧頂。

R14:連接配接寄存器(LR),當執行BL子程式調用指令時,R14中得到R15(程式計數器PC)的備份,其他情況下,R14用作通用寄存器。

R15:程式計數器(PC):用于控制程式中指令的執行順序。正常運作時,PC指向CPU運作的下一條指令。每次取值後PC的值會自動修改以指向下一條指令,進而保證了指令按一定的順序執行。當程式的執行順序發生改變(如轉移)時,需要修改PC的值。

2.狀态寄存器

CPSR(R16):目前程式狀态寄存器,用來儲存ALU中的目前操作資訊,控制允許和禁止中斷、設定處理器的工作模式等。

SPSRs:五個備份的程式狀态寄存器,用來進行異常處理。當異常發生時,SPSR用于儲存CPSR的目前值,從異常退出時可由SPSR來恢複CPSR。

img

N、Z、C、V均為條件碼标志位,他們的内容可被運算的結果所改變。

N:正負标志,N=1表示運算的結果為負,N=0表示運算的結果為正或0

Z:零标志,Z=1表示運算的結果為0,Z=0表示運算的結果為非0

C:進位标志,加法運算産生了進位時則C=1,否則C=0

  借位标志,減肥運算産生了借位則C=0,否則C=1

V:溢出标志,V=1表示有溢出,V=0表示無溢出

3.位址空間

程式正常執行時,每執行一條ARM指令,目前指令計數器增加4個位元組

二 彙編語言

1.彙編指令格式

<opcode>{<cond>}{S}<Rd>,<Rn>{,<OP2>}

格式中<>的内容必不可少,{}中的内容可省略

<opcode>:表示操作碼,如ADD表示算術加法

{<cond>}:表示指令執行的條件域,如EQ、NE等。

{S}:決定指令的執行結果是否影響CPSR的值,使用該字尾則指令執行的結果影響CPSR的值,否則不影響

<Rd>:表示目的寄存器

<Rn>:表示第一個操作數,為寄存器

<op2>:表示第二個操作數,可以是立即數、寄存器或寄存器移位操作數

例子:ADDEQS R0,R1,#8;其中操作碼為ADD,條件域cond為EQ,S表示該指令的執行影響CPSR寄存器的值,目的寄存器Rd為R0,第一個操作數寄存器Rd為R1,第二個操作數OP2為立即數#8

2.指令的可選字尾

S:指令執行後程式狀态寄存器的條件标志位将被重新整理

    ADDS R1,R0,#2

!:指令中的位址表達式中含有!字尾時,指令執行後,基址寄存器中的位址值将發生變化,變化的結果是:基址寄存器中的值(指令執行後)=指令執行前的值 + 位址偏移量

    LDR R3,[R0,#2]!    指令執行後,R0 = R0 + 2

3.指令的條件執行

指令的條件字尾隻是影響指令是否執行,不影響指令的内容

條件碼 助記符字尾 标志 含義
0000 EQ Z置位 相等
0001 NE Z清零 不相等
0010 CS C指令 無符号數大于或等于
0011 CC C清零 無符号數小于
0100 MI N置位 負數
0101 PL N清零 正數或零
0110 VS V置位 溢出
0111 VC V清零 未溢出
1000 HI C置位Z清零 無符号數大于
1001 LS C清零Z置位 無符号數小于或等于
1010 GE N等于V 帶符号數大于或等于
1011 LT N不等于V 帶符号數小于
1100 GT Z清零且(N等于V) 帶符号數大于
1101 LE Z置位或(N不等于V) 帶符号數小于或等于
1110 AL 忽略 無條件執行

例子:ADDEQ R4,R3,#1 相等則相加,即CPSR中Z置位時該指令執行,否則不執行。

4.ARM指令分類

助記符 指令功能描述 助記符 指令功能描述
ADC 帶進位加法指令 MRC 從協處理器寄存器到ARM寄存器的資料傳輸指令
ADD 加法指令 MRS 傳送CPSR或SPSR的内容到通用寄存器指令
AND 邏輯與指令 MSR 傳送通用寄存器到CPSR或SPSR的指令
B 分支指令 MUL 32位乘法指令
BIC 位清零指令 MLA 32位乘加指令
BL 帶傳回的分支指令 MVN 資料取反傳送指令
BLX 帶傳回和狀态切換的分支指令 ORR 邏輯或指令
BX 帶狀态切換的分支指令 RSB 逆向減法指令
CDP 協處理器資料操作指令 RSC 帶錯位的逆向減法指令
CMN 比較反值指令 SBC 帶錯位減法指令
CMP 比較指令 STC 協處理器寄存器寫入存儲器指令
EOR 異或指令 STM 批量記憶體字寫入指令
LDC 存儲器到協處理器的資料傳輸指令 STR 寄存器到存儲器的資料存儲指令
LDM 加載多個寄存器指令 SUB 減法指令
LDR 存儲器到寄存器的資料加載指令 SWI 軟體中斷指令
MCR 從ARM寄存器到協處理器寄存器的資料傳輸指令 TEQ 相等測試指令
MOV 資料傳送指令 TST 位測試指令

5.ARM尋址方式

尋址方式就是根據指令中操作數的資訊來尋找操作數實際實體位址的方式

1)立即數尋址

MOV R0,#15       #15就是立即數

2)寄存器尋址

ADD R0, R1, R2    将R1和R2的内容相加,其結果存放在寄存器R0中

3)寄存器間接尋址

LDR R0, [R4]      以寄存器R4的值作為操作數的位址,在存儲器中取得一個操作數存入寄存器R0中

4)寄存器移位尋址

ADD R0,R1,R2,LSL #1    将R2的值左移一位,所得值與R1相加,存放到R0中

MOV R0,R1,LSL R3    将R1的值左移R3位,然後将結果存放到R0中

5)基址變址尋址

LDR R0,[R1,#4]    将R1的值加4作為操作數的位址,在存儲器中取得操作數放入R0中

LDR R0,[R1,#4]!   将R1的值加4作為操作數的位址,在存儲器中取得操作數放入R0中,然後R1 = R1+4

LDR R0,[R1],#4    R0 = [R1],R1 = R1 +4

LDR R0,[R1,R2]   R0 = [R1+R2]

6).多寄存器尋址

一條指令可以完成多個寄存器值的傳送(最多可傳送16個通用寄存器),連續的寄存器用“-”,否則用“,”

LDMIA R0!,{R1 - R4}   R1 = [R0],R2=[R0+4],R3=[R0+8],R4=[R0+12]

字尾IA表示在每次執行玩加載/存儲操作後,R0按自長度增加。

7).相對尋址

以程式計數器PC的目前值為基位址,指令中的位址标号作為偏移量,将兩者相加之後得到操作數的有效位址,如下圖的BL分支跳轉

     BL proc        跳轉到子程式proc處執行

     ...

proc MOV R0,#1

     ...

8).堆棧尋址

按先進先出的方式工作,堆棧指針用R13表示,總是指向棧頂,LDMFD和STMFD分别表示POP出棧和PUSH進棧

STMFD R13!,{R0 - R4};

LDMFD R13!,{R0 - R4};

6.資料處理指令

1). MOV指令

MOV {<cond>}{S} Rd,op2          将op2傳給Rd

MOV R1, R0          将寄存器R0的值傳到寄存器R1

MOV PC,R14          将寄存器R14的值傳到PC,常用于子程式傳回

MOV R1,R0,LSL #3    将寄存器R0的值左移3位後傳給R1

MOV R0,#5           将立即數5傳給R0

2). MVN指令

MVN {<cond>}{S}Rd, op2          将op2取反傳給Rd

MVN R0,#0           将0取反後傳給R0,R0 = -1

MVN R1,R2           将R2取反,結果儲存到R1

3). 移位指令

LSL 邏輯左移

LSR 邏輯右移

ASR 算術右移

ROR 循環右移

RRX 帶擴充的循環右移

4). ADD加法指令

ADD{<cond>}{S}Rd, Rn, op2

ADD R0,R1,R2            R0 = R1 + R2

ADD R0,R1,#5            R0 = R1 + 5

ADD R0,R1,R2,LSL #2     R0 = R1 + (R2左移2位)

5). ADC帶進位加法指令

ADC{<cond>}{S} Rd,Rn,op2    将Rn的值和操作數op2相加,再加上CPSR中C條件标志位的值,并将結果儲存到Rd中

例:用ADC完成64位加法,設第一個64位操作數儲存在R2,R3中,第二個64位操作數放在R4,R5中,結果儲存在R0,R1中

ADDS R0,R2,R4       低32位相加,産生進位

ADC R1,R3,R5        高32位相加,加上進位

6). SUB減法指令

SUB{<cond>}{S} Rd,Rn,op2    Rd = Rn - op2

SUB R0,R1,R2            R0 = R1 - R2

SUB R0,R1,#6            R0 = R1 -6

SUB R0,R2,R3,LSL #1     R0 = R2 - (R3左移1位)

7). SBC帶借位減法指令

SBC{<cond>}{S} Rd,Rn,op2    把Rn的值減去操作數op2,再減去CPSR中的C标志位的反碼,并将結果儲存到Rd中,Rd = Rn - op2 - !C

例:用SBC完成64位減法,設第一個64位操作數儲存在R2,R3中,第二個64位操作數放在R4,R5中,結果儲存在R0,R1中

SUBS R0,R2,R4       低32位相減,S影響CPSR

SBC R1,R3,R5        高32位相減,去除C的反碼

8). RSC帶借位的逆向減法指令

RSC{<cond>}{S} Rd,Rn,op2    把操作數op2減去Rn,再減去CPSR中的C标志位的反碼,并将結果儲存到Rd中,Rd = op2 - Rn - !C

9). 邏輯運算指令

AND{<cond>}{S} Rd,Rn,op2    按位與,Rd = Rn AND op2

ORR{<cond>}{S} Rd,Rn,op2    按位或,Rd = Rn OR op2

EOR{<cond>}{S} Rd,Rn,op2    按位異或,Rd = Rn EOR op2

10). CMP比較指令

CMP{<cond>}{S} Rd,Rn,op2    将Rn的值和op2進行比較,同時更新CPSR中條件标志位的值(實際上是執行一次減法,但不存儲結果),當操作數Rn大于op2時,則此後帶有GT字尾的指令将可以執行(根據相應的指令判斷是否執行,如GT,LT等)。

CMP R1,#10                  比較R1和10,并設定CPSR的标志位

ADDGT R0,R0,#5              如果R1>10,則執行ADDGT指令,将R0加5

11). CMN反值比較指令

CMN{<cond>}{S} Rd,Rn,op2    将Rn的值和op2取反後進行比較,同時更新CPSR中條件标志位的值(實際上将Rn和op2相加),後面的指令就可以根據條件标志位決定是否執行。  

CMN R0,R1       将R0和R1相加,并設定CPSR的值

12). MUL/MLA/SMULL/SMLAL/UMULL/UMLAL乘法指令

MUL     32位乘法指令

MLA     32位乘加指令

SMULL   64位有符号數乘法指令

SMLAL   64位有符号數乘加指令

UMULL   64位無符号數乘法指令

UMLAL   64位無符号數乘加指令

MUL{<cond>}{S} Rd,Rm,Rs         Rd = Rm * Rs

MULS R0,R1,R2

MLA{<cond>}{S} Rd,Rm,Rs,Rn      Rd = (Rm * Rs) + Rn

MLAS R0,R1,R2,R3

7.資料加載與存儲指令

助記符 說明 操作
LDR{}Rd,addr 加載字資料 Rd = [addr]
LDRB{}Rd,addr 加載無符号位元組資料 Rd = [addr]
LDRT{}Rd,addr 以使用者模式加載字資料 Rd = [addr]
LDRBT{}Rd,addr 以使用者模式加載無符号位元組資料 Rd = [addr]
LDRH{}Rd,addr 加載無符号半字資料 Rd = [addr]
LDRSB{}Rd,addr 加載有符号位元組資料 Rd = [addr]
LDRSH{}Rd,addr 加載有符号半字資料 Rd = [addr]
STR{}Rd,addr 存儲字資料 [addr] = Rd
STRB{}Rd,addr 存儲位元組資料 [addr] = Rd
STRT{}Rd,addr 以使用者模式存儲字資料 [addr] = Rd
STRBT{}Rd,addr 以使用者模式存儲位元組資料 [addr] = Rd
STRH{}Rd,addr 存儲半字資料 [addr] = Rd
LDM{}{type}Rn{!},regs 多寄存器加載 reglist = [Rn...]
STM{}{type}Rn{!},regs 多寄存器存儲 [Rn...] = reglist
SWP{}Rd,Rm,[Rn] 寄存器和存儲器字資料交換 Rd=[Rn],[Rn]=Rm(Rn!=Rd或Rm)
SWP{}B Rd,Rm,[Rn] 寄存器和存儲器位元組資料交換 Rd = [Rn],[Rn] = Rm(Rn!=Rd或Rm)

1). LDR/STR字資料加載/存儲指令

LDR/STR{<cond>}{T}Rd,addr       LDR指令用于從存儲器中将一個32位的字資料加載到目的寄存器Rd中,當程式計數器PC作為目的寄存器時,指令從存儲器中讀取的字資料被當做目的位址,進而可以實作程式流程的跳轉。

STR指令用于從源寄存器中将一個32位的字資料存儲到存儲器中,和LDR相反。字尾T可選。

LDR R4,START            将存儲位址為START的字資料讀入R4

STR R5,DATA1            将R5存入存儲位址為DATA1中

LDR R0,[R1]             将存儲器位址為R1的字資料讀入存儲器R0

LDR R0,[R1,R2]          将存儲器位址為R1+R2的字資料讀入存儲器R0

LDR R0,[R1,#8]          将存儲器位址為R1+8的字資料讀入存儲器R0

LDR R0,[R1,R2,LSL #2]   将存儲器位址為R1+R2*4的字資料讀入存儲區R0

STR R0,[R1,R2]!         将R0字資料存入存儲器位址R1+R2的存儲單元中,并将新位址R2+R2寫入R2

STR R0,[R1,#8]!         将R0字資料存入存儲器位址R1+8的存儲單元中,并将新位址R2+8寫入R2

STR R0,[R1,R2,LSL #2]   将R0字資料存入存儲器位址R1+R2*4的存儲單元中,并将新位址R2+R2*4寫入R1

LDR R0,[R1],#8          将存儲器位址為R1的字資料讀入寄存器R0,并将新位址R1+8寫入R1  

LDR R0,[R1],R2          将存儲器位址為R1的字資料讀入寄存器R0,并将新位址R1+R2寫入R1

LDR R0,[R1],R2,LSL #2   将存儲器位址為R1的字資料讀入寄存器R0,并将新位址R1+R2*4寫入R1

2). LDRB/STRB位元組資料加載/存儲指令

LDRB/STRB{<cond>}{T}Rd,addr         LDRB指令用于從存儲器中将一個8位的位元組資料加載到目的寄存器中,同時将寄存器的高24位清零,當程式計數器PC作為目的寄存器時,指令從存儲器中讀取的字資料被當做目的位址,進而可以實作程式流程的跳轉。

STRB指令用于從源寄存器中将一個8位的位元組資料存儲到存儲器中,和LDRB相反。字尾T可選。

3). LDRH/STRH半字資料加載/存儲指令

LDRH/STRH{<cond>}{T}Rd,addr         LDRH指令用于從存儲器中将一個16位的半字資料加載到目的寄存器中,同時将寄存器的高16位清零,當程式計數器PC作為目的寄存器時,指令從存儲器中讀取的字資料被當做目的位址,進而可以實作程式流程的跳轉。

STRH指令用于從源寄存器中将一個16位的半字資料存儲到存儲器中,和LDRH相反。字尾T可選。

4). LDM/STM批量資料加載/存儲指令

LDM/STM{<cond>}{<type>}Rn{!},<regs>{^}      LDM用于從基址寄存器所訓示的一片連續存儲器中讀取資料到寄存器清單所指向的多個寄存器中,記憶體單元的起始位址為基址寄存器Rn的值,各個寄存器由寄存器清單regs表示,該指令一般用于多個寄存器資料的出棧操作

STM用于将寄存器清單所指向的多個寄存器中的值存入由基址寄存器所指向的一片連續存儲器中,記憶體單元的起始位址為基址寄存器Rn的值,各個寄存器又寄存器清單regs表示。該指令一般用于多個寄存器資料的進棧操作。

type表示類型,用于資料的存儲與讀取有以下幾種情況:

IA:每次傳送後位址值加。

IB:每次傳送前位址值加。

DA:每次傳送後位址值減。

DB:每次傳送前位址值減。

用于堆棧操作時有如下幾種情況:

FD:滿遞減堆棧

ED:空遞減堆棧

FA:滿遞增堆棧

EA:空遞增堆棧

5). SWP字資料交換指令

SWP{<cond>}<Rd>,<Rm>,[<Rn>]         Rd = [Rn],[Rn] = Rm,當寄存器Rm和目的寄存器Rd為同一個寄存器時,指令交換該急促親和存儲器的内容

SWP R0,R1,[R2]          R0 = [R2],[R2] = R1

SWP R0,R0,[R1]          R0 = [R1],[R1] = R0

SWPB指令用于将寄存器Rn指向的存儲器中的位元組資料加載到目的寄存器Rd中,目的寄存器的高24位清零,同時将Rm中的字資料存儲到Rn指向的存儲器中。

8.分支語句

助記符                        說明                                                                  操作

B{cond}label          分支指令                                           PC<-label  

BL{cond}label         帶傳回的分支指令                 PC<-label,LR=BL後面的第一條指令位址

BX{cond}Rm           帶狀态切換的分支指令   PC = Rm & 0xffffffe,T=Rm[0] & 1

BLX{cond}label Rm                                                        帶傳回和狀态切換的分支指令 | PC=label,T=1 PC; PC = Rm                                                                                 &0xffffffe,T=Rm[0] & 1;LR = BLX後面的第一條指令位址

1). 分支指令B

B{<cond>}label          跳轉到label處執行,PC=label

例子:

backword    SUB R1,R1,#1

            CMP R1,#0           比較R1和0

            BEQ forward         如果R1=0,跳轉到forware處執行

            SUB R1,R2,#3

            SUB R1,R1,#1

forward     ADD R1,R2,#4

            ADD R2,R3,#2

            B backword          無條件跳轉到backword處執行

2). 帶傳回的分支指令BL

BL{<cond>}label         在跳轉之前,将PC的目前内容儲存在R14(LR)中儲存,是以,可以通過将R14的内容重新加載到PC中,傳回到跳轉指令之後的指令處執行。該指令用于實作子程式的調用,程式的傳回可通過把LR寄存器的值複制到PC寄存器中來實作。

例子:

BL func             跳轉到子程式

ADD R1,R2,#2        子程式調用完傳回後執行的語句,傳回位址

....

func                子程式

...

MOV R15,R14         複制傳回位址到PC,實作子程式的傳回

3). 帶狀态切換的分支指令BX

BX{<cond>} Rm       當執行BX指令時,如果條件cond滿足,則處理器會判斷Rm的位[0]是否為1,如果為1則跳轉時自動将CPSR寄存器的标志T置位,并将目标位址的代碼解釋為Thumb代碼來執行,則處理器會切換到Thumb狀态,反之,若Rm的位[0]為0,則跳轉時自動将CPSR寄存器的标志T複位,并将目标位址處的代碼解釋為ARM代碼來執行,即處理器會切換到ARM狀态。

注意:bx lr的作用等同于mov pc,lr。即跳轉到lr中存放的位址處。 非零值存儲在R0中傳回。

那麼lr存放的是什麼位址呢?lr就是連接配接寄存器(Link Register, LR),在ARM體系結構中LR的特殊用途有兩種:一是用來儲存子程式傳回位址;二是當異常發生時,LR中儲存的值等于異常發生時PC的值減4(或者減2),是以在各種異常模式下可以根據LR的值傳回到異常發生前的相應位置繼續執行。  

當通過BL或BLX指令調用子程式時,硬體自動将子程式傳回位址儲存在R14寄存器中。在子程式傳回時,把LR的值複制到程式計數器PC即可實作子程式傳回。

9.堆棧

1). 進棧出棧

出棧使用LDM指令,進棧使用STM指令。LDM和STM指令往往結合下面一些參數實作堆棧的操作。

FD:滿遞減堆棧。

ED:空遞減堆棧。

FA:滿遞增堆棧。

EA:空遞增堆棧。

滿堆棧是指SP(R13)指向堆棧的最後一個已使用位址或滿位置(也就是SP指向堆棧的最後一個資料項的位置);相反,空堆棧是指SP指向堆棧的第一個沒有使用的位址或空位置。

LDMFD和STMFD分别指POP出棧和PUSH入棧

2). PUSH指令

PUSH{cond} reglist      PUSH将寄存器推入滿遞減堆棧

PUSH {r0,r4-r7}         将R0,R4-R7寄存器内容壓入堆棧

2.9.3. POP指令

POP{cond} reglist       POP從滿遞減堆棧中彈出資料到寄存器

POP {r0,r4-r7}          将R0,R4-R7寄存器從堆棧中彈出