搗鼓了一段時間的iOS逆向相關(guān)的東西,在動態(tài)分析過程中會閱讀匯編代碼,分析代碼的執(zhí)行流程,在此記錄下閱讀匯編代碼過程中經(jīng)常遇到的一些指令账忘。
當然如果不玩逆向也有必要學習匯編,在定位某些crash問題的時候會很有幫助蝙昙,比如有時候程序掛在系統(tǒng)庫里面闪萄,如果能讀懂匯編代碼,再利用一些調(diào)試技巧奇颠,可會達到意想不到的效果败去。
除此之外,學習匯編有利于幫助自己更深層次的理解程序烈拒,理解計算機圆裕。
說明:下面所描述的都是在arm64架構(gòu)的匯編指令,在arm32或者x86架構(gòu)會有所區(qū)別荆几。
寄存器
在arm64架構(gòu)下吓妆,所有的寄存器都是64位,并且每個寄存器都有名字的吨铸,按照功能來劃分行拢,可分為一下幾類,分別為:
- 通用寄存器
- 程序計數(shù)器
- 堆棧指針
- 鏈接寄存器
- 程序狀態(tài)寄存器
- 零寄存器
下面分別說明這些寄存器的作用
通用寄存器
64bit : x0 ~ x28 诞吱,每個寄存器都是64bit
32bit:w0 ~ w28舟奠,實際上是x0~x28寄存器的低32bit
x0~x7:通過用來存儲函數(shù)的參數(shù),如果函數(shù)有更多的參數(shù)使用堆棧來傳遞
x0:通常用來存放函數(shù)的返回值
程序計數(shù)器
程序計數(shù)器叫Program Counter房维,俗稱PC沼瘫,也就是x32寄存器,存儲著CPU當前正在執(zhí)行的地址咙俩。
堆棧指針
SP (Stack Pointer)耿戚,就是x31寄存器,存儲的是棧頂的地址
FP (Frame Pointer)阿趁,FP也就是x29寄存器膜蛔,存儲著棧底的地址
隨著函數(shù)的調(diào)用,SP脖阵、FP會不斷的變化飞几。
鏈接寄存器
LR(Link Register),也就是x30寄存器独撇,存儲著函數(shù)的返回地址屑墨。
當函數(shù)結(jié)束時,就是通過LR寄存器的值纷铣,跳轉(zhuǎn)到調(diào)用函數(shù)的位置繼續(xù)往下執(zhí)行卵史。
程序狀態(tài)寄存器
CPSR (Current Program Status Register),各個bit的含義如下圖:
SPSR (Saved Program Status Register)搜立,在異常狀態(tài)下使用以躯,當發(fā)生異常時,會把CPSR的內(nèi)容寫入SPSR, 等異匙挠唬恢復之后忧设,又會把SPSR寫會到CPSR中。
零寄存器
WZR
XZR
里面存儲的值都是0颠通。
常用指令
算術(shù)運算指令
mov 賦值指令
mov x0, #2 // 把2這個值賦值給x0寄存器
mov x0, x1 // 把x1寄存器中的值賦值給x0寄存器中
add
兩個操作數(shù)相加址晕,相加的結(jié)果存放到一個寄存器中
add, x2, x0, x1 //把x0的值與x1的值相加,得到的結(jié)果存放到x2寄存器中
add, x2, x0, #3 // 把x0的值與3相加顿锰,得到的結(jié)果存放到x2寄存器中
sub
第一個操作數(shù)減第一個操作數(shù)谨垃,得到的結(jié)果存放到一個寄存器中
sub, x2, x1, x0 // x1的值減去x0的值,得到的結(jié)果存放到*x2*寄存器中
sub, x2, x1, #4 // x1的值減去4硼控,得到的結(jié)果存放到x2寄存器中
mul 乘法指令
mul x3, x1, x2 // x1 乘以 x2 的結(jié)果存放在 x3 中
sdiv 除法指令
sdiv w0, w0, w1 // w0 除以 w1 的結(jié)果存放在 w0 中
邏輯運算指令
這里的運算是指位運算
-
LSL 邏輯左移
按操作數(shù)所指定的數(shù)量向左移位刘陶,低位用零來填充 -
ASL 算術(shù)左移
通邏輯左移,ASL 與 LSL等價
lsl x0, x0, #1
asl x1, x1, x0
LSR 邏輯右移
按操作數(shù)所指定的數(shù)量向右移位牢撼,左端用零來填充匙隔。ASR 算術(shù)右移
按操作數(shù)所指定的數(shù)量向右移位,左端用最高位位的值來填充 熏版,如果是負數(shù)纷责,最高位為1
lsr w1, w2, #1
asr x1, x2, #2
-
ROR 循環(huán)右移
按操作數(shù)所指定的數(shù)量向右循環(huán)移位, 左端用右端移出的位來填充纳决。其中碰逸,操作數(shù)可以是通用寄存器,也可以是立即數(shù)阔加。 當進行寄存器bit位數(shù)的循環(huán)右移操作時饵史,通用寄存器中的值不改變。
ror x0, #6
ror w0, #32 // 循環(huán)移動了32位胜榔,w0的值不變
跳轉(zhuǎn)指令
ret
相當于高級編程語言的return胳喷,函數(shù)返回。
cmp
將兩個操作數(shù)相減夭织,相減的結(jié)果會影響cpsr 寄存器的標志位吭露,當結(jié)果小于0時,CPSR寄存器的N位為1尊惰, 等于0時讲竿, CPSR寄存器的Z為位1泥兰。
cmp x0, x1
b
跳轉(zhuǎn)指令,跳轉(zhuǎn)找指定的標記處執(zhí)行题禀;可以帶條件跳轉(zhuǎn)鞋诗,一般跟cmp配合使用,使用到的條件域如下:
- EQ:equal
- NE:not equal
- GT:great than
- GE:great equal
- LT:less than
- LE:less equal
普通跳轉(zhuǎn)
b testCode
迈嘹,testCode是匯編代碼中的一個標記
條件跳轉(zhuǎn)削彬,當x0和x1的值相等時,才跳轉(zhuǎn)到testCode標記處執(zhí)行代碼
cmp x0, x1
beq testCode
bl
帶返回值的跳轉(zhuǎn)指令秀仲,這個指令會做兩個操作
- 將下一條指令的地址存儲到lr (x30)寄存器中
- 跳轉(zhuǎn)到標記處開始執(zhí)行代碼
bl testCode
融痛,當執(zhí)行完testCode標記處代碼后,又會返回來執(zhí)行bl指令下面的指令神僵。
內(nèi)存操作
load從內(nèi)存中讀取數(shù)據(jù)
- ldr 地址沒有偏移或者偏移為正數(shù)時使用
-
ldur 地址偏移為負數(shù)時使用
a)str x5 [x0]
x0
中是內(nèi)存的地址雁刷,讀取的值存放在x5
寄存器中, x寄存器讀取8個字節(jié)
b)str w6 [x0]
x0
中是內(nèi)存的地址,讀取的值存放在w5
寄存器中, w寄存器讀取4個字節(jié)
說明: 地址還可以偏移 str x5 [x0, #0x4]
, stur x5 [x0, #-0x4]
, 偏移量為正數(shù)往高地址偏移挑豌,使用str
指令安券、偏移量為負數(shù)往低地址偏移,使用stur
指令氓英。
-
ldp 從指定內(nèi)存中讀取數(shù)據(jù)到一對寄存器中侯勉, p 是 pair的意思,這一對寄存器必須是同類型的铝阐,要么x類型, 要么w類型址貌。其中低位讀取到第一個寄存器、高位讀取到第二個寄存器
ldp w5, w6, [x0]
, 地址可以偏移ldp x5, x6, [x0, #-x04]
store 往指定的內(nèi)存寫入數(shù)據(jù)
- str
str x1, [x0]
str w2, [x1]
str w3, [x1, #4]
- stur
stur x3, [x0, #-4]
stur w2, [x1, #-4]
- stp
stp x2, x3, [x0]
stp w4, w5, [x0]
使用方法與從內(nèi)存中讀取數(shù)據(jù)類似徘键,只不過是往內(nèi)存寫入數(shù)據(jù)练对。
總結(jié)
本文整理了一些在逆向iOS程序時常見的一些匯編指令,當然在實際逆向的過程所看到的匯編指令更加復雜吹害,比如還有函數(shù)調(diào)用棧螟凭,這是下篇的內(nèi)容。如有錯誤請指正它呀。
Refrence
- iOS開發(fā)同學的arm64匯編入門 - 劉坤的技術(shù)博客
- ARM匯編電子書