寄存器
匯編指令棧栗子
逆向工程繞不過的一部分就是匯編指令的分析。我們iPhone里面用到的是ARM匯編,但是不同的設備也有差異,因CPU的架構不同。
架構 | 設備 |
---|---|
armv6 | iPhone, iPhone2, iPhone3G, 第一代、第二代 iPod Touch |
armv7 | iPhone3GS, iPhone4, iPhone4S,iPad, iPad2, iPad3(The New iPad), iPad mini, iPod Touch 3G, iPod Touch4 |
armv7s | iPhone5, iPhone5C, iPad4(iPad with Retina Display) |
arm64 | iPhone5S 及以后版本 |
從iPhone5s之后的蘋果手機都是ARM64位操作系統了,所以我們直接從ARM64匯編指令開始。
寄存器
我們都知道CPU的典型構成中有寄存器、控制器和運算器等組成,部件之間通過總線連接。運算器負責信息處理,控制器負責控制其他期間進行工作,寄存器用於信息存儲。對我們程序員來說寄存器是最主要部件,可以通過改變寄存器的內容來實現對CPU的控制。
不同的CPU,寄存器的個數和結構不相同。像8086CPU有14個寄存器。ARM64 有34個寄存器,包括31個通用寄存器、SP、PC、CPSR。
寄存器 | 位數 | 描述 |
---|---|---|
X0-X30 | 64bit | 通用寄存器,如果有需要可以當做32bit使用:WO-W30 |
FP(x29) | 64bit | 保存棧幀地址(棧底指針) |
LR (X30) | 64bit | 通常稱X30為程序鏈接寄存器,保存跳轉返回信息地址 |
SP | 64bit | 保存棧指針 |
PC | 64bit | 程序計數器,俗稱PC指針,總是指向即將要執行的下一條指令 |
X0-X7: 用於子程序調用時的參數傳遞,X0還用於返回值傳遞
X8: 間接尋址結果
LR: 保存子程序結束后需要執行的下一條指令
Xcode在真機中運行項目,然后在viewWillAppear添加斷點,lldb中查看各寄存器狀態register read
匯編指令
下面介紹ARM64經常用到的匯編指令
MOV X1,X0 ; 將寄存器X0的值傳送到寄存器X1
ADD X0,X1,X2 ; 寄存器X1和X2的值相加后傳送到X0
SUB X0,X1,X2 ; 寄存器X1和X2的值相減后傳送到X0
AND X0,X0,#0xF ; X0的值與0xF相位與后的值傳送到X0
ORR X0,X0,#9 ; X0的值與9相或后的值傳送到X0
EOR X0,X0,#0xF ; X0的值與0xF相異或后的值傳送到X0
LDR X5,[X6,#0x08] ;X6寄存器加0x08的和的地址值內的數據傳送到X5
STR X0, [SP, #0x8] ;X0寄存器的數據傳送到SP+0x8地址值指向的存儲空間
STP x29, x30, [sp, #0x10] ; 入棧指令
LDP x29, x30, [sp, #0x10] ; 出棧指令
CBZ ; 比較(Compare),如果結果為零(Zero)就轉移(只能跳到后面的指令)
CBNZ ; 比較,如果結果非零(Non Zero)就轉移(只能跳到后面的指令)
CMP ; 比較指令,相當於SUBS,影響程序狀態寄存器
CPSR B/BL ; 絕對跳轉#imm, 返回地址保存到LR(X30)
RET ; 子程序返回指令,返回地址默認保存在LR(X30)
NZCV是狀態寄存器中存的幾個狀態值,分別代表運算過程中產生的狀態,其中:
-
N, negative condition flag,一般代表運算結果是負數
-
Z, zero condition flag, 運算結果為0
-
C, carry condition flag, 無符號運算有溢出時,C=1。
-
V, oVerflow condition flag 有符號運算有溢出時,V=1。
棧
棧就是指令執行時存放臨時變量的內存空間,具有特殊的訪問方式:后進先出, Last In Out Firt。
-
棧是從高地址到低地址存儲數據的,棧底是高地址,棧頂是高地址。
-
FP指向棧底
-
SP指向棧頂
栗子
下面我們寫一個簡單求和的子函數調用,看看編譯成ARM64匯編指令是什么樣子的。
-
testarm.m的內容如下:
#include<stdio.h>
int mySum(inta , intb)
{
intc=a+b;
returnc;
}
int main (intargc, char*argv[])
{
int outA = 10;
int outB = 20;
int result = mySum(10, 20);
printf("%d",result);
return0;
}
-
用clang編譯成arm64匯編代碼
編譯命令如下:
clang -O0-archarm64 -isysroot`xcrun --sdk iphoneos --show-sdk-path`-otestarm01 testarm.m
testarm01: 輸出文件名
testarm.m: 需要編譯的文件
arm64:輸出匯編類型
-
分析匯編
使用IDA或者Hopper查看匯編代碼,下面我粘貼處主要匯編代碼分析。
-
mySum對應的匯編:
從SUB SP, SP, #0x10開始分析
SUB SP, SP, #0x10 ; 分配棧控件16個字節; 下面是先存儲參數,然后取出來用
-
STR W0, [SP,#0x10+var_4] ; 把W0入棧,即a
-
STR W1, [SP,#0x10+var_8] ; 把W1入棧,即b
-
LDR W0, [SP,#0x10+var_4] ;出棧a,存儲到W0
-
LDR W1, [SP,#0x10+var_8] ;出棧b,存儲到W1;
-
主代碼到了,求和ADD W0, W0, W1 ;求和,並把和存儲到W0,相當於int c = a + b;;
-
返回值處理 STR W0, [SP,#0x10+var_C] ;把和W0入棧
-
LDR W0, [SP,#0x10+var_C] ; 把和W0出棧,現在W0存儲的就是結果了。
-
ADD SP, SP, #0x10 ;平棧,采用平棧方式是add
-
RET ;子程序結束
-
下面是main函數的匯編代碼:
想必通過上邊sum函數的講解,大家也能基本能看懂main函數的匯編
主要代碼解釋
MOV X0, X8 ;實參outA: #0xA = 10
-
MOV X1, X9 ;實參outB: #0x14 = 20
-
BL _mySum ;調用mySum子函數