前言
今天開始學(xué)習(xí)逆向(掉頭發(fā))啦!
在逆向開發(fā)中缰贝,非常重要的一個環(huán)節(jié)就是靜態(tài)分析.首先我們是逆向iOS系統(tǒng)上面的APP.那么我們知道,一個APP安裝在手機(jī)上面的可執(zhí)行文件本質(zhì)上是二進(jìn)制文件.因為iPhone手機(jī)本質(zhì)上執(zhí)行的指令是二進(jìn)制.是由手機(jī)上的CPU執(zhí)行的.所以靜態(tài)分析是建立在分析二進(jìn)制上面.所以今天我們接下來的課程從非仇信#基礎(chǔ)的東西開始講解.
一 蒜焊、 匯編初識
我們的代碼在終端設(shè)備上是這樣的過程:
-
匯編語言
與機(jī)器語言
一一對應(yīng)碍讨,每一條機(jī)器指令都有與之對應(yīng)的匯編指令
-
匯編語言
可以通過編譯得到機(jī)器語言
独泞,機(jī)器語言
可以通過反匯編
得到匯編語言
-
高級語言
可以通過編譯得到匯編語言 \ 機(jī)器語言
亥至,但匯編語言\機(jī)器語言幾乎不可能還原成
高級語言
匯編語言的特點
可以
直接訪問
、控制
各種硬件設(shè)備禀挫,比如存儲器旬陡、CPU等,能最大限度地發(fā)揮硬件的功能
能夠不受編譯器的限制语婴,對生成的二進(jìn)制代碼進(jìn)行完全的控制
目標(biāo)代碼簡短,占用
內(nèi)存少
驶睦,執(zhí)行速度快
匯編指令是機(jī)器指令的助記符,同機(jī)器指令一一對應(yīng)砰左。每一種CPU都有自己的機(jī)器指令集\匯編指令集,所以匯編語言不具備可移植性
知識點過多场航,開發(fā)者需要對CPU等硬件結(jié)構(gòu)有所了解缠导,不易于編寫、調(diào)試溉痢、維護(hù)
不區(qū)分大小寫僻造,比如mov和MOV是一樣的
匯編的用途
- 編寫驅(qū)動程序、操作系統(tǒng)(比如Linux內(nèi)核的某些關(guān)鍵部分)
- 對性能要求極高的程序或者代碼片段孩饼,可與高級語言混合使用(內(nèi)聯(lián)匯編)
- 軟件安全
- 病毒分析與防治
逆向
\加殼\脫殼\破解\外掛\免殺\加密解密\漏洞\黑客- 理解整個計算機(jī)系統(tǒng)的最佳起點和最有效途徑
- 為編寫高效代碼打下基礎(chǔ)
- 弄清代碼的本質(zhì)
- 函數(shù)的本質(zhì)究竟是什么?
- ++a + ++a + ++a 底層如何執(zhí)行的?
- 編譯器到底幫我們干了什么?
- DEBUG模式和RELEASE模式有什么關(guān)鍵的地方被我們忽略
越底層越單純!真正的程序員都需要了解的一門非常重要的語言,匯編!
匯編語言的種類
-
目前討論比較多的匯編語言有
- 8086匯編(8086處理器是16bit的CPU)
- Win32匯編
- Win64匯編
-
ARM匯編
(嵌入式髓削、Mac
、iOS
)
......
我們iPhone里面用到的是ARM匯編,但是不同的設(shè)備也有差異.因CPU的架構(gòu)不同.
架構(gòu) | 設(shè)備 |
---|---|
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 以后 iPhoneX , iPad Air, iPad mini2以后 |
二立膛、幾個必要的常識
- 要想學(xué)好匯編,首先需要了解CPU等硬件結(jié)構(gòu)
- APP/程序的執(zhí)行過程
- 硬件相關(guān)最為重要是CPU/內(nèi)存
- 在匯編中,大部分指令都是和CPU與內(nèi)存相關(guān)的
2.1 總線
- 每一個CPU芯片都有許多管腳,這些管腳和總線相連梯码,CPU通過總線跟外部器件進(jìn)行交互
- 總線:一根根導(dǎo)線的集合
- 總線的分類
- 地址總線
- 數(shù)據(jù)總線
- 控制總線
舉個例子:
- 地址總線
- 它的寬度決定了
CPU
的尋址能力
- 8086的地址總線寬度是20宝泵,所以尋址能力是1M( 220 )
- 它的寬度決定了
- 數(shù)據(jù)總線
- 它的
寬度
決定了CPU
的單次數(shù)據(jù)傳送量
,也就是數(shù)據(jù)傳送速度
; - 8086的數(shù)據(jù)總線寬度是16轩娶,所以單次最大傳遞2個字節(jié)的數(shù)據(jù);
- 它的
- 控制總線
- 它的寬度決定了CPU對其他器件的控制能力儿奶、能有多少種控制;
小練習(xí):
2.2 內(nèi)存
內(nèi)存地址空間的大小受CPU地址總線寬度的限制。8086的地址總線寬度為20鳄抒,可以定位220個不同的內(nèi)存單元(內(nèi)存地址范圍0x00000~0xFFFFF)闯捎,所以8086的內(nèi)存空間大小為1MB
0x00000~0x9FFFF:主存儲器搅窿。可讀可寫
0xA0000~0xBFFFF:向顯存中寫入數(shù)據(jù)隙券,這些數(shù)據(jù)會被顯卡輸出到顯示器男应。可讀可寫
0xC0000~0xFFFFF:存儲各種硬件\系統(tǒng)信息娱仔。只讀
2.3 進(jìn)制
進(jìn)制的定義
- 八進(jìn)制由8個符號組成:0 1 2 3 4 5 6 7 逢八進(jìn)一
- 十進(jìn)制由10個符號組成:0 1 2 3 4 5 6 7 8 9逢十進(jìn)一
- N進(jìn)制就是由N個符號組成:
逢 N 進(jìn) 一
2.4 數(shù)據(jù)的寬度
數(shù)學(xué)上的數(shù)字沐飘,是沒有大小限制的,可以無限的大牲迫。但在計算機(jī)中耐朴,由于受硬件的制約,數(shù)據(jù)都是有長度限制的(我們稱為數(shù)據(jù)寬度)盹憎,超過最多寬度的數(shù)據(jù)會被丟棄筛峭。
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int test(){
int cTemp = 0x1FFFFFFFF;
return cTemp;
}
int main(int argc, char * argv[]) {
printf("%x\n",test());
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
計算機(jī)中常見的數(shù)據(jù)寬度
- 位(Bit): 1個位就是1個二進(jìn)制位.0或者1
- 字節(jié)(Byte): 1個字節(jié)由8個Bit組成(8位).內(nèi)存中的最小單元Byte.
- 字(Word): 1個字由2個字節(jié)組成(16位),這2個字節(jié)分別稱為高字節(jié)和低字節(jié).
- 雙字(Doubleword): 1個雙字由兩個字組成(32位)
那么計算機(jī)存儲數(shù)據(jù)它會分為有符號數(shù)
和無符號數(shù)
.那么關(guān)于這個看圖就理解了!
無符號數(shù),直接換算!
有符號數(shù):
正數(shù): 0 1 2 3 4 5 6 7
負(fù)數(shù): F E D B C A 9 8
-1 -2 -3 -4 -5 -6 -7 -8
三、CPU&寄存器
內(nèi)部部件之間由總線連接
CPU除了有控制器陪每、運算器還有寄存器影晓。其中寄存器的作用就是進(jìn)行數(shù)據(jù)的臨時存儲。
CPU的運算速度是非抽莺蹋快的挂签,為了性能CPU在內(nèi)部開辟一小塊臨時存儲區(qū)域,并在進(jìn)行運算時先將數(shù)據(jù)從內(nèi)存復(fù)制到這一小塊臨時存儲區(qū)域中盼产,運算時就在這一小快臨時存儲區(qū)域內(nèi)進(jìn)行饵婆。我們稱這一小塊臨時存儲區(qū)域為寄存器。
-
對于arm64系的CPU來說戏售, 如果
寄存器以x開頭則表明的是一個64位的寄存器
侨核,如果以w開頭則表明是一個32位的寄存器
,在系統(tǒng)中沒有提供16位和8位的寄存器供訪問和使用灌灾。其中32位的寄存器是64位寄存器的低32位部分并不是獨立存在的
搓译。- 對程序員來說,
CPU中最主要部件是寄存器
紧卒,可以通過改變寄存器的內(nèi)容來實現(xiàn)對CPU的控制
- 不同的CPU侥衬,寄存器的個數(shù)、結(jié)構(gòu)是不相同的
- 對程序員來說,
浮點和向量寄存器
因為浮點數(shù)的存儲以及其運算的特殊性,CPU中專門提供浮點數(shù)寄存器來處理浮點數(shù)
-
浮點寄存器 64位
: D0 - D31 32位: S0 - S31
現(xiàn)在的CPU支持向量運算.(向量運算在圖形處理相關(guān)的領(lǐng)域用得非常的多)為了支持向量計算系統(tǒng)了也提供了眾多的向量寄存器.
-
向量寄存器
128位:V0-V31
通用寄存器
-
通用寄存器
也稱數(shù)據(jù)地址寄存器
通常用來做數(shù)據(jù)計算的臨時存儲
跑芳、做累加
轴总、計數(shù)
、地址保存
等功能博个。定義這些寄存器的作用主要是用于在CPU指令中保存操作數(shù)
怀樟,在CPU中當(dāng)做一些常規(guī)變量
來使用。 - ARM64 擁有有 32個64位的通用寄存器 x0 到 x30盆佣,以及XZR(零寄存器), 這些通用寄存器有時也有特定用途往堡。
- 那么w0 到 w28 這些是32位的. 因為64位CPU可以兼容32位. 所以可以只使用64位寄存器的低32位.
- 比如 w0 就是 x0的低32位!
注意:
了解過8086匯編的同學(xué)知道械荷,有一種特殊的寄存器段寄存器:CS,DS,SS,ES四個寄存器來保存這些段的基地址,這個屬于Intel架構(gòu)CPU中.在ARM中并沒有
- 通常,CPU會先將內(nèi)存中的數(shù)據(jù)存儲到通用寄存器中虑灰,然后再對通用寄存器中的數(shù)據(jù)進(jìn)行運算
- 假設(shè)內(nèi)存中有塊紅色內(nèi)存空間的值是3吨瞎,現(xiàn)在想把它的值加1,并將結(jié)果存儲到藍(lán)色內(nèi)存空間
- CPU首先會將紅色內(nèi)存空間的值放到X0寄存器中:mov X0,紅色內(nèi)存空間
- 然后讓X0寄存器與1相加:add X0,1
- 最后將值賦值給內(nèi)存空間:mov 藍(lán)色內(nèi)存空間,X0
pc寄存器(program counter)
- 為
指令指針寄存器
穆咐,它指示了CPU當(dāng)前要讀取指令的地址
- 在內(nèi)存或者磁盤上颤诀,指令和數(shù)據(jù)沒有任何區(qū)別,都是
二進(jìn)制信息
- CPU在工作的時候把有的信息看做指令对湃,有的信息看做數(shù)據(jù)崖叫,為同樣的信息賦予了不同的意義
- 比如 1110 0000 0000 0011 0000 1000 1010 1010
- 可以當(dāng)做數(shù)據(jù) 0xE003008AA
- 也可以當(dāng)做指令 mov x0, x8
- CPU根據(jù)什么將內(nèi)存中的信息看做指令?
CPU將pc指向的內(nèi)存單元的內(nèi)容看做指令
如果內(nèi)存中的某段內(nèi)容曾被CPU執(zhí)行過拍柒,那么它所在的內(nèi)存單元必然被pc指向過
高速緩存
iPhoneX上搭載的ARM處理器A11它的1級緩存的容量是64KB心傀,2級緩存的容量8M.
CPU每執(zhí)行一條指令前都需要從內(nèi)存中將指令讀取到CPU內(nèi)并執(zhí)行。而寄存器的運行速度相比內(nèi)存讀寫要快很多,為了性能,CPU還集成了一個高速緩存存儲區(qū)域.當(dāng)程序在運行時拆讯,先將要執(zhí)行的指令代碼以及數(shù)據(jù)復(fù)制到高速緩存中去(由操作系統(tǒng)完成).CPU直接從高速緩存依次讀取指令來執(zhí)行.
bl指令
CPU從何處執(zhí)行指令是由
pc中的內(nèi)容
決定的脂男,我們可以通過改變pc的內(nèi)容
來控制CPU執(zhí)行目標(biāo)指令
-
ARM64提供了一個
mov
指令(傳送指令
),可以用來修改
大部分寄存器的值往果,比如- mov x0,#10疆液、mov x1,#20
但是,mov指令不能用于設(shè)置pc的值陕贮,ARM64沒有提供這樣的功能
ARM64提供了另外的指令來修改PC的值,這些指令統(tǒng)稱為
轉(zhuǎn)移指令
潘飘,最簡單的是bl指令
狀態(tài)寄存器
???CPU內(nèi)部的寄存器中,有一種特殊的寄存器(對于不同的處理器,個數(shù)和結(jié)構(gòu)都可能不同).這種寄存器在ARM中,被稱為狀態(tài)寄存器就是CPSR(current program status register)寄存器
CPSR和其他寄存器不一樣,其他寄存器是用來存放數(shù)據(jù)的,都是整個寄存器具有一個含義.而CPSR寄存器是按位起作用的,也就是說,它的每一位都有專門的含義,記錄特定的信息.
注:CPSR寄存器是32位的
- CPSR的低8位(包括I肮之、F、T和M[4:0])稱為控制位卜录,程序無法修改,除非CPU運行于特權(quán)模式下,程序才能修改控制位!
- N戈擒、Z、C艰毒、V均為條件碼標(biāo)志位筐高。它們的內(nèi)容可被算術(shù)或邏輯運算的結(jié)果所改變,并且可以決定某條指令是否被執(zhí)行!意義重大!
N(Negative)標(biāo)志
CPSR的第31位是 N丑瞧,符號標(biāo)志位柑土。它記錄相關(guān)指令執(zhí)行后,其結(jié)果是否為負(fù).如果為負(fù) N = 1,如果是非負(fù)數(shù) N = 0.
???注意,在ARM64的指令集中,有的指令的執(zhí)行時影響狀態(tài)寄存器的,比如add\sub\or等,他們大都是運算指令(進(jìn)行邏輯或算數(shù)運算);
Z(Zero)標(biāo)志
CPSR的第30位是Z绊汹,0標(biāo)志位稽屏。它記錄相關(guān)指令執(zhí)行后,其結(jié)果是否為0.如果結(jié)果為0.那么Z = 1.如果結(jié)果不為0,那么Z = 0.
???對于Z的值,我們可以這樣來看,Z標(biāo)記相關(guān)指令的計算結(jié)果是否為0,如果為0,則Z要記錄下"是0"這樣的肯定信息.在計算機(jī)中1表示邏輯真,表示肯定.所以當(dāng)結(jié)果為0的時候Z = 1,表示"結(jié)果是0".如果結(jié)果不為0,則Z要記錄下"不是0"這樣的否定信息.在計算機(jī)中0表示邏輯假,表示否定,所以當(dāng)結(jié)果不為0的時候Z = 0,表示"結(jié)果不為0"。
C(Carry)標(biāo)志
CPSR的第29位是C西乖,進(jìn)位標(biāo)志位狐榔。一般情況下,進(jìn)行無符號數(shù)的運算坛增。
加法運算:當(dāng)運算結(jié)果產(chǎn)生了進(jìn)位時(無符號數(shù)溢出),C=1薄腻,否則C=0收捣。
減法運算(包括CMP):當(dāng)運算時產(chǎn)生了借位時(無符號數(shù)溢出),C=0庵楷,否則C=1罢艾。
???對于位數(shù)為N的無符號數(shù)來說,其對應(yīng)的二進(jìn)制信息的最高位嫁乘,即第N - 1位昆婿,就是它的最高有效位,而假想存在的第N位蜓斧,就是相對于最高有效位的更高位仓蛆。如下圖所示:
進(jìn)位
???我們知道,當(dāng)兩個數(shù)據(jù)相加的時候挎春,有可能產(chǎn)生從最高有效位想更高位的進(jìn)位看疙。比如兩個32位數(shù)據(jù):0xaaaaaaaa + 0xaaaaaaaa,將產(chǎn)生進(jìn)位。由于這個進(jìn)位值在32位中無法保存直奋,我們就只是簡單的說這個進(jìn)位值丟失了能庆。其實CPU在運算的時候,并不丟棄這個進(jìn)位制脚线,而是記錄在一個特殊的寄存器的某一位上搁胆。ARM下就用C位來記錄這個進(jìn)位值。比如邮绿,下面的指令
mov w0,#0xaaaaaaaa渠旁;0xa 的二進(jìn)制是 1010
adds w0,w0,w0; 執(zhí)行后 相當(dāng)于 1010 << 1 進(jìn)位1(無符號溢出) 所以C標(biāo)記 為 1
adds w0,w0,w0船逮; 執(zhí)行后 相當(dāng)于 0101 << 1 進(jìn)位0(無符號沒溢出) 所以C標(biāo)記 為 0
adds w0,w0,w0顾腊; 重復(fù)上面操作
adds w0,w0,w0
借位
???當(dāng)兩個數(shù)據(jù)做減法的時候,有可能向更高位借位挖胃。再比如杂靶,兩個32位數(shù)據(jù):0x00000000 - 0x000000ff,將產(chǎn)生借位,借位后酱鸭,相當(dāng)于計算0x100000000 - 0x000000ff吗垮。得到0xffffff01 這個值。由于借了一位凛辣,所以C位 用來標(biāo)記借位抱既。C = 0.比如下面指令:
mov w0,#0x0
subs w0,w0,#0xff ;
subs w0,w0,#0xff
subs w0,w0,#0xff
V(Overflow)溢出標(biāo)志
CPSR的第28位是V,溢出標(biāo)志位扁誓。在進(jìn)行有符號數(shù)運算的時候防泵,如果超過了機(jī)器所能標(biāo)識的范圍蚀之,稱為溢出。
- 正數(shù) + 正數(shù) 為負(fù)數(shù) 溢出
- 負(fù)數(shù) + 負(fù)數(shù) 為正數(shù) 溢出
- 正數(shù) + 負(fù)數(shù) 不可能溢出