ARM匯編相關(guān)的iOS逆向理論基礎(chǔ)
ARM官方資料:http://infocenter.arm.com
1.1 ARM匯編基礎(chǔ)
“ARM匯編的基本概念相當(dāng)于26個字母和音標(biāo);指令相當(dāng)于單詞,它們的變種相當(dāng)于單詞的各種形態(tài);調(diào)用規(guī)則相當(dāng)于語法,定義句子之間的聯(lián)系。”
1.1.1
1.寄存器、內(nèi)存和棧
在ARM匯編里,操作對象是寄存器(register)、內(nèi)存和棧(stack)。
其中,寄存器可以看成CPU自帶的變量,它們的數(shù)量一般是很有限的;
當(dāng)需要更多變量時,就可以把它們存放在內(nèi)存中;
不過,數(shù)量上去了,質(zhì)量也下來了,對內(nèi)存的操作比對寄存器的操作要慢得多。
棧其實也是一片內(nèi)存區(qū)域,但它具有棧的特點:先進后出。ARM的棧是滿遞減(Full Descending)的,向下增長,也就是開口朝下,新的變量被存放到棧底的位置;越靠近棧底,內(nèi)存地址越小,”
“一個名為“stack pointer”(簡稱SP)的寄存器保存棧的棧底地址,稱為棧地址;可以把一個變量給入(push)棧以保存它的值,也可以讓它出(pop)棧,恢復(fù)變量的原始值。在實際操作中,棧地址會不斷變化;但是在執(zhí)行一塊代碼的前后,棧地址應(yīng)該是不變的,不然程序就要出問題了。”
2.特殊用途的寄存器
ARM處理器中的部分寄存器有特殊用途
R0-R3 傳遞參數(shù)與返回值
R7 幀指針,指向母函數(shù)與被調(diào)用子函數(shù)在棧中的交界
R9 在iOS 3.0以前被系統(tǒng)保留
R12 內(nèi)部過程調(diào)用寄存器,dynamic linker會用到它
R13 SP寄存器
R14 LR寄存器,保存函數(shù)返回地址
R15 PC寄存器
3.分支跳轉(zhuǎn)與條件判斷
處理器中名為“program counter”(簡稱PC)的寄存器用于存放下一條指令的地址。一般情況下,計算機一條接一條地順序執(zhí)行指令,處理器執(zhí)行完一條指令后將PC加1,讓它指向下一條指令,
亂序”的學(xué)名叫“分支”(branch):指令的執(zhí)行順序被打亂”
條件分支:滿足一定條件才得以觸發(fā)的分支是最實用的
·操作結(jié)果為0(或不為0);
·操作結(jié)果為負(fù)數(shù);
·操作結(jié)果有進位;
·運算溢出(比如兩個正數(shù)相加得到的數(shù)超過了寄存器位數(shù))。”
1.1.2 ARM/THUMB指令解讀
ARM處理器用到的指令集分為ARM和THUMB兩種;
ARM指令長度均為32bit,
THUMB指令長度均為16bit。
所有指令可大致分為3類,分別是數(shù)據(jù)操作指令、內(nèi)存操作指令和分支指令。
1.數(shù)據(jù)操作指令
1)所有操作數(shù)均為32bit;
2)所有結(jié)果均為32bit,且只能存放在寄存器中。
基本格式:op{cond}{s} Rd, Rn, Op2
“cond”和“s”是兩個可選后綴;
“cond”的作用是指定指令“op”在什么條件下執(zhí)行”
17種條件
EQ 結(jié)果為0(EQual to 0)
NE 結(jié)果不為0(Not Equal to 0)
CS 有進位或借位(Carry Set)
HS 同CS(unsigned Higher or Same)
CC 沒有進位或借位(Carry clear)
LO 同CC(unsigned LOwer)
MI 結(jié)果小于0(MInus)
PL 結(jié)果大于等于0(PLus)
VS 溢出(oVerflow Set)
VC 無溢出(oVerflow Clear)
HI 無符號比較大于(unsigned HIgher)
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,默認(rèn))
"cond"的用法:
比較 R0, R1
移動 GE R2, R0
移動 LT R2, R1
等價:
if(R0 >=R1){
R2 =R0
}else{
R2=R!
}
“s”的作用是指定指令“op”是否設(shè)置flag
有下面4種flag:
N(Negative)
如果結(jié)果小于0則置1,否則置0;
Z(Zero)
如果結(jié)果是0則置1,否則置0;
C(Carry)
對于加操作(包括CMN)來說,如果產(chǎn)生進位則置1,否則置0;對于減操作(包括CMP)來說,Carry相當(dāng)于Not-Borrow,如果產(chǎn)生借位則置0,否則置1;對于有移位操作的非加/減操作來說,C置移出值的最后一位;對于其他的非加/減操作來說,C的值一般不變;
V(oVerflow)
如果操作導(dǎo)致溢出,則置1,否則置0。
C flag表示無符號數(shù)運算結(jié)果是否溢出;V flag表示有符號數(shù)運算結(jié)果是否溢出。
算數(shù)操作:
ADD R0, R1, R2 ; R0 = R1 + R2
ADC R0, R1, R2 ; R0 = R1 + R2 + C(arry)
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是基礎(chǔ),其他是變種;
RSB=Reverse SuB =把SUB的兩個操作數(shù)調(diào)換位置;
以C結(jié)尾=Carry=代表有進位和錯位的加減法,當(dāng)產(chǎn)生進位或沒有借位的時,將Carryflage置1
邏輯操作:
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 R0, R2 ; R0 = R2
LSL 邏輯左移;
LSR 邏輯右移;
ASR算數(shù)右移;
ROR循環(huán)右移;
比較操作:
CMP R1, R2 ; 執(zhí)行R1 - R2并依結(jié)果設(shè)置flag
CMN R1, R2 ; 執(zhí)行R1 + R2并依結(jié)果設(shè)置flag
TST R1, R2 ; 執(zhí)行R1 & R2并依結(jié)果設(shè)置flag
TEQ R1, R2 ; 執(zhí)行R1 ^ R2并依結(jié)果設(shè)置flag
乘法操作:
MUL R4, R3, R2 ; R4 = R3 * R2
MLA R4, R3, R2, R1 ; R4 = R3 * R2 + R1
乘法操作的操作數(shù)必須來自寄存器。
2.內(nèi)存操作指令
基本格式:
op{cond}{type} Rd, [Rn,?Op2]
其中Rn是基址寄存器,用于存放基地址;
“cond”的作用與數(shù)據(jù)操作指令相同;
“type”指定指令“op”操作的數(shù)據(jù)類型,共有4種:
B(unsigned Byte)
無符號byte(執(zhí)行時擴展到32bit,以0填充);
SB(Signed Byte)
有符號byte(僅用于LDR指令;執(zhí)行時擴展到32bit,以符號位填充);
H(unsigned Halfword)
無符號halfword(執(zhí)行時擴展到32bit,以0填充);
SH(Signed Halfword)
有符號halfword(僅用于LDR指令;執(zhí)行時擴展到32bit,以符號位填
充)。
如果不指定“type”,則默認(rèn)數(shù)據(jù)類型是word。
ARM內(nèi)存操作基礎(chǔ)指令只有兩個:
LDR(LoaD Register)將數(shù)據(jù)從內(nèi)存中讀出來,存到寄存器中;STR(STore Register)將數(shù)據(jù)從寄存器中讀出來,存到內(nèi)存中。
LDR
LDR Rt, [Rn {, #offset}] ; Rt = *(Rn {+ offset}),{}代表可選
LDR Rt, [Rn, #offset]! ; Rt = *(Rn + offset); Rn = Rn + offset
LDR Rt, [Rn], #offset ; Rt = *Rn; Rn = Rn + offset
STR
STR Rt, [Rn {, #offset}] ; *(Rn {+ offset}) = Rt
STR Rt, [Rn, #offset]! ; *(Rn {+ offset}) = Rt; Rn = Rn + offset
STR Rt, [Rn], #offset ; *Rn = Rt; Rn = Rn + offset
變種,操作雙字,一次性操作2個寄存器:LDRD和STRD
格式:
op{cond} Rt, Rt2, [Rn {, #offset}]
STRD
STRD R4, R5, [R9,#offset] ; *(R9 + offset)= R4; *(R9 + offset + 4)= R5
·LDRD
LDRD R4, R5, [R9,#offset] ; R4 = *(R9 + offset); R5 = *(R9 + offset + 4)
LDM(LoaD Multiple)和STM(STore Multiple)進行塊傳輸;
一次性操作多個寄存器;
基本格式:
op{cond}{mode} Rd{!}, reglist
其中Rd是基址寄存器,
可選的“!”指定Rd變化后的值是否寫回Rd;
reglist是一系列寄存器,用大括號括起來,它們之間可以用“,”分隔,也可以用“-”表示一個范圍,比如,{R4–R6,R8}表示寄存器R4、R5、R6、R8;這些寄存器的順序是按照自身的編號由小到大排列的,與大括號內(nèi)的排列順序無關(guān)。
需要特別注意的是,LDM和STM的操作方向與LDR和STR完全相反:LDM是把從Rd開始,地址連續(xù)的內(nèi)存數(shù)據(jù)存入reglist中,STM是把reglist中的值存入從Rd開始,地址連續(xù)的內(nèi)存中。
“cond”的作用與數(shù)據(jù)操作指令相同。“mode”指定Rd值的4種變化規(guī)律,如下所示:
IA(Increment After)
每次傳輸后增加Rd的值;
IB(Increment Before)
每次傳輸前增加Rd的值;
DA(Decrement After)
每次傳輸后減少Rd的值;
DB(Decrement Before)
每次傳輸前減少Rd的值。
例子:
foo():
LDMIA R0, {R4 – R6} ; R4 = 5, R5 = 6, R6 = 7
LDMIB R0, {R4 – R6} ; R4 = 6, R5 = 7, R6 = 8
LDMDA R0, {R4 – R6} ; R4 = 5, R5 = 4, R6 = 3
LDMDB R0, {R4 – R6} ; R4 = 4, R5 = 3, R6 = 2
3.分支指令
無條件分支和條件分支;
無條件分支:
B Label ; PC = Label
BL Label ; LR = PC – 4; PC = Label
BX Rd ; PC = Rd并切換指令集
例子:
foo():
B Label ; 跳轉(zhuǎn)到Label處往下執(zhí)行
...... ; 得不到執(zhí)行
Label:
......
條件分支
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 | Z = 1
GE N = V
LT N != V
GT Z = 0 & N = V
LE Z = 1 | N != V
在條件分支指令前會有一條數(shù)據(jù)操作指令來設(shè)置flag,分支指令根據(jù)flag的值來決定代碼走向:
Label:
LDR R0, [R1], #4
CMP R0, 0 ; 如果R0 == 0,Z = 1;否則Z = 0
BNE Label ; Z == 0則跳轉(zhuǎn)
4.THUMB指令
THUMB指令集是ARM指令集的一個子集,每條THUMB指令均為16bit;因此THUMB指令比ARM指令更節(jié)省空間,且在16位數(shù)據(jù)總線上的傳輸效率更高。有得必有失,除了“b”之外,所有THUMB指令均無法條件執(zhí)行;桶式移位無法結(jié)合其他指令執(zhí)行;大多數(shù)THUMB指令只能使用R0~R7這8個寄存器等。
THUMB特點:
1.指令數(shù)減少;
2.沒有條件執(zhí)行
3.所有指令默認(rèn)附帶"s";
4.桶式移位無法結(jié)合其他指令執(zhí)行;
5.寄存器使用受限;
6.立即數(shù)和第二操作數(shù)使用受限
7.不支持?jǐn)?shù)據(jù)
1.1.3ARM 調(diào)用規(guī)則
當(dāng)一個函數(shù)調(diào)用另一個函數(shù)時,常常需要傳遞參數(shù)和返回值;如何傳遞這些數(shù)據(jù),稱為ARM匯編的調(diào)用規(guī)則。
函數(shù)的前4個參數(shù)存放在R0到R3中,其他參數(shù)存放在棧中;返回值放在R0中
2.tweak的編寫套路
1.進入進程
$cycript -p XXX
2.查看UI層次
開啟expand
cy#?expand
expand==true
cy# [[UIApp keyWindow] recursiveDescription]
3.通過Cycript的"#"操作符可以拿到windows上的任意view
cy# tabView = #0x146e1af0
4.app所有windows
cy# [UIApp windows]
5.隱藏控件:
cy# [#0x146e6060 setHidden:YES]
6.獲取響應(yīng)函數(shù):
“cy# button = #0x14798410
#"<UIToolbarButton: 0x14798410; frame = (285 0; 23 44); hidden = YES; opaque = NO; gestureRecognizers = <NSArray: 0x14799510>; layer = <CALayer: 0x14798510>>"
cy# [button allTargets]
[NSSet setWithArray:@[#"<ComposeButtonItem: 0x14609d00>"]]]
cy# [button allControlEvents]
64
cy# [button actionsForTarget:#0x14609d00 forControlEvent:64]
@["_sendAction:withEvent:"]”
7.nextResponder
利用響應(yīng)者鏈,可以找到某個view的父類;
cy# [#0x17f92890 nextResponder]
#"<UITableViewWrapperView: 0x17eb4fc0; frame = (0 0; 320 504); gestureRecognizers = <NSArray: 0x17ee5230>; layer = <CALayer: 0x17ee5170>; contentOffset: {0, 0}; contentSize: {320, 504}>"
cy# [#0x17eb4fc0 nextResponder]
#"<UITableView: 0x16c69e00; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x17f4ace0>; layer = <CALayer: 0x17f4ac20>; contentOffset: {0, -64}; contentSize: {320, 717.5}>"
cy# [#0x16c69e00 nextResponder]
3LLDB的使用技巧
...待完善