iOS逆向 01:初識(shí)匯編

iOS 底層原理 + 逆向 文章匯總

在逆向開(kāi)發(fā)中,非常重要的一個(gè)環(huán)節(jié)就是靜態(tài)分析,眾所周知,手機(jī)上安裝App其本質(zhì)是一個(gè)二進(jìn)制文件,而靜態(tài)分析是建立在分析二進(jìn)制上面的。所以在學(xué)習(xí)逆向之前,需要提前了解相關(guān)的匯編知識(shí)

匯編語(yǔ)言的發(fā)展

機(jī)器語(yǔ)言

是由0和1組成的機(jī)器指令,表示特定的功能,如下所示

加:0100 0000
減:0100 1000
乘:1111 0111 1110 0000
除:1111 0111 1111 0000

匯編語(yǔ)言(assembly language)

由于使用機(jī)器語(yǔ)言表示時(shí)不方便記憶,于是開(kāi)始使用助記符來(lái)代替機(jī)器語(yǔ)言,,例如下面使用助記符表示的加減乘除

加:INC EAX 通過(guò)編譯器 0100 0000
減:DEC EAX 通過(guò)編譯器 0100 1000
乘:MUL EAX 通過(guò)編譯器 1111 0111 1110 0000
除:DIV EAX 通過(guò)編譯器 1111 0111 1111 0000

高級(jí)語(yǔ)言(high-level programming language)

在后期,為了更加高效的編程,在匯編語(yǔ)言的基礎(chǔ)之上有了更高級(jí)的語(yǔ)言,例如C/C++/Java/OC/Swift等,這些語(yǔ)言更加接近人類的自然語(yǔ)言。例如c語(yǔ)言表示的加減乘除

加:A+B 通過(guò)編譯器 0100 0000
減:A-B 通過(guò)編譯器 0100 1000
乘:A*B 通過(guò)編譯器 1111 0111 1110 0000
除:A/B 通過(guò)編譯器 1111 0111 1111 0000

最終的代碼在終端設(shè)備上顯示的過(guò)程是這樣的


代碼在終端的編譯過(guò)程

有以下幾點(diǎn)說(shuō)明:

  • 1、匯編語(yǔ)言機(jī)器語(yǔ)言是一一對(duì)應(yīng)的,每一條機(jī)器指令都有與之對(duì)應(yīng)的匯編指令
  • 2、匯編語(yǔ)言可以通過(guò)編譯得到機(jī)器語(yǔ)言,機(jī)器語(yǔ)言可以通過(guò)反匯編得到匯編語(yǔ)言
  • 3、高級(jí)語(yǔ)言可以通過(guò)編譯得到匯編語(yǔ)言/機(jī)器語(yǔ)言,但匯編語(yǔ)言/機(jī)器語(yǔ)言幾乎不可能還原成高級(jí)語(yǔ)言(因?yàn)椴煌脑O(shè)備,對(duì)應(yīng)不同的CPU架構(gòu),而CPU架構(gòu)對(duì)應(yīng)不同的指令集)

匯編語(yǔ)言的特點(diǎn)

匯編語(yǔ)言主要有以下幾個(gè)特點(diǎn):

  • 1、可以直接訪問(wèn)、控制各種硬件設(shè)備,例如存儲(chǔ)器、CPU等,能最大限度的發(fā)揮硬件的功能

  • 2、能夠不受編譯器的限制,對(duì)生成的二進(jìn)制代碼進(jìn)行完全的控制

  • 3、目標(biāo)代碼簡(jiǎn)潔,占用內(nèi)存少,執(zhí)行速度快

  • 4、匯編指令是機(jī)器指令的助記符,同機(jī)器指令一一對(duì)。每一種CPU都有自己的機(jī)器指令集/匯編指令集,所以匯編語(yǔ)言不具備可移植性

  • 5、知識(shí)點(diǎn)過(guò)多,要求過(guò)高,需要開(kāi)發(fā)者對(duì)CPU等硬件結(jié)構(gòu)有所了解,不易于編寫(xiě)、調(diào)試,以及維護(hù)

  • 6、不區(qū)分大小寫(xiě),例如mov和MOV是一樣的

匯編語(yǔ)言的用途

按理說(shuō),匯編這么難,為什么還要學(xué)呢,以及學(xué)了能做什么?

匯編學(xué)習(xí)的目的

任何高級(jí)語(yǔ)言最終都會(huì)被編譯成匯編,學(xué)習(xí)了解匯編的相關(guān)知識(shí),可以更好的日常開(kāi)發(fā)、學(xué)習(xí)探索中幫助我們更好的排查問(wèn)題、理解底層運(yùn)行的機(jī)制。大致分為有以下幾點(diǎn):

  • 1、是理解整個(gè)計(jì)算機(jī)系統(tǒng)的最佳起點(diǎn)和最有效途徑
  • 2、為編寫(xiě)高效代碼打下基礎(chǔ)
  • 3、理解代碼的本質(zhì),例如:
    • 函數(shù)的本質(zhì)是什么?
    • ++a底層是如何執(zhí)行的?
    • 編譯器在底層到底幫我們做了哪些工作?
    • DEBUG模式和RELEASE模式到底有哪些地方是不同的,以及被我們忽略的?

匯編使用的場(chǎng)景

  • 1、可以編寫(xiě)驅(qū)動(dòng)程序、操作系統(tǒng),例如Linux內(nèi)核的某些關(guān)鍵字
  • 2、對(duì)性能要求極高的程序或者代碼片段,可與高級(jí)語(yǔ)言混合使用(稱為內(nèi)聯(lián)匯編)
  • 3、用于軟件安全方面
    • 病毒的分析與防治
    • 逆向、加殼、脫殼、破解、外掛、免殺、加解密、黑客等

所以綜上所述,匯編是所有程序猿都需要了解的一門非常重要的語(yǔ)言,這也是為什么大學(xué)中計(jì)算機(jī)相關(guān)專業(yè)學(xué)生的必修課,就好比修房子,地基穩(wěn)了,高樓才能平地起!

匯編語(yǔ)言的種類

目前討論較多的婚變語(yǔ)言有以下幾種

  • 1、8086匯編(8086處理器是16bit的CPU)
  • 2、Win32匯編
  • 3、Win64匯編
  • 4、ARM匯編(嵌入式、Mac、iOS)
  • ...

在iPhone中使用的是ARM匯編,但是不同的設(shè)備之間也會(huì)因?yàn)镃PU架構(gòu)的不同而有所差異,下面是iPhone中不同架構(gòu)所對(duì)應(yīng)的設(shè)備

架構(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以后

必備常識(shí)

想要學(xué)好匯編,需要有以下幾個(gè)認(rèn)知

  • 1、首先需要了解CPU等硬件的架構(gòu)

  • 2、App/程序的執(zhí)行過(guò)程


    App的執(zhí)行過(guò)程
    • 可執(zhí)行文件:程序/App在本地磁盤的文件
    • image(鏡像文件):可執(zhí)行文件裝載到內(nèi)存的文件(在早期,其本質(zhì)與可執(zhí)行文件是一模一樣的)
    • 內(nèi)存中除了指令,還有數(shù)據(jù),但是都是0和1組合,CPU是如何區(qū)分的?是通過(guò)CPU上的部件PC寄存器來(lái)區(qū)分
  • 3、硬件相關(guān)最為重要的是CPU/內(nèi)存

  • 4、在匯編中,大部分指令都是與CPU/內(nèi)存相關(guān)的

總線

總線是CPU與內(nèi)存之間的橋梁,如下圖所示是iPhone X上的A11(CPU芯片)


A11

從圖中可以看出:每一個(gè)CPU芯片都有很多管腳,這些管腳和總線相連,CPU通過(guò)總線跟外部器件進(jìn)行交互

總線:是一根根導(dǎo)線的集合

總線的分類
總線主要分為三類,如下圖所示:

總線分類

  • 1、地址總線:CPU是通過(guò)地址總線來(lái)指定存儲(chǔ)單元的

  • 2、數(shù)據(jù)總線:CPU與內(nèi)存/其他部件之間的數(shù)據(jù)傳送通道

  • 3、控制總線:CPU通過(guò)控制總線對(duì)外部器件進(jìn)行控制

舉例:CPU從內(nèi)存的3號(hào)單元讀取數(shù)據(jù)

總線舉例

  • 1、CPU想操作內(nèi)存中的數(shù)據(jù),首先需要找到內(nèi)存地址:CPU通過(guò)地址總線,將3這個(gè)地址傳遞給內(nèi)存,即尋址到內(nèi)存的3號(hào)單元

  • 2、需要操作3單元的數(shù)據(jù),還需要確定是讀還是寫(xiě):CPU通過(guò)控制總線告訴內(nèi)存需要進(jìn)行的操作,假設(shè)是讀

  • 3、內(nèi)存知道了CPU想要進(jìn)行的操作:內(nèi)存將3號(hào)單元的數(shù)據(jù)通過(guò)數(shù)據(jù)線傳遞給CPU

地址總線

  • 它的寬度決定了CPU的尋址能力,即地址總線決定了CPU所能訪問(wèn)的最大內(nèi)存空間的大小,例如10根地址線能訪問(wèn)的最大內(nèi)存是 2^10 = 1024位二進(jìn)制數(shù)據(jù)(即1B)

  • 地址總線是地址線數(shù)量之和

  • 8086地址總線寬度是20,所以尋址能力是1M(即2^20)

  • 內(nèi)存地址的單元是 字節(jié)byte(簡(jiǎn)寫(xiě)為B),每個(gè)字節(jié)里面可以放8位(即bit),以下是內(nèi)存條的圖示


    內(nèi)存條圖示

注意區(qū)分數(shù)量單位容量單位

  • 數(shù)量單位: 1M=1024k,1k=1024
  • 容量單位: 字節(jié)Byte(B) 1024B=1KB,1024KB=1MB,IBM銀行的獨(dú)立系統(tǒng)是以2字節(jié)為一個(gè)單位,常用的電腦是以1字節(jié)為一個(gè)單位
  • 網(wǎng)絡(luò)帶寬100M=100Mbpt(比特位,每秒傳輸12.5MB/s)

數(shù)據(jù)總線

  • 它的寬度決定了CPU的單次數(shù)據(jù)傳送量(即吞吐量),也就是數(shù)據(jù)傳送速度即CPU和外界的數(shù)據(jù)傳送速度

  • 每條數(shù)據(jù)線一次只能傳輸一位二進(jìn)制數(shù)據(jù),例如 8根數(shù)據(jù)線一次可傳送一個(gè)8位二進(jìn)制數(shù)據(jù)(即1個(gè)字節(jié)的數(shù)據(jù))

  • 數(shù)據(jù)總線是數(shù)據(jù)線數(shù)量之和

  • 8086的數(shù)據(jù)總線寬度是16,所以單次最大傳遞2個(gè)字節(jié)的數(shù)據(jù)

我們常說(shuō)的32位(4字節(jié))、64位(8字節(jié))CPU,這里的32、64指的就是數(shù)據(jù)吞吐量

控制總線

  • 它的寬度決定了CPU對(duì)其他器件的控制能力,能有多少種控制,即CPU對(duì)外部器件的控制能力

  • 控制總線是控制線數(shù)量之和

內(nèi)存

  • CPU是通過(guò)總線和硬件設(shè)備連接的

  • 內(nèi)存有RAM主存儲(chǔ)器、RAM主存儲(chǔ)器(內(nèi)存條)


    CPU圖示-01
  • 下圖是按照物理地址劃分的內(nèi)存,有主存儲(chǔ)器、顯存地址、顯卡地址、網(wǎng)卡地址


    CPU圖示-02

其中內(nèi)存中的低地址是給用戶用的,高地址是給系統(tǒng)用的,如下所示

內(nèi)存圖示

  • 內(nèi)存地址空間的大小受CPU地址總線寬度的限制,8086的地址總線寬度為20,可以定位2^20個(gè)不同的內(nèi)存單元(內(nèi)存地址范圍0x00000~0xFFFFF),所以8086的內(nèi)存空間大小為1MB

  • 0x00000~0x9FFFF:主存儲(chǔ)器,可讀可寫(xiě)

  • 0xA0000~0xBFFFF:向顯存中寫(xiě)入數(shù)據(jù),這些數(shù)據(jù)會(huì)被顯卡輸出到顯示器,可讀可寫(xiě)

  • 0xC0000~0xFFFFF:存儲(chǔ)各種硬件/系統(tǒng)信息,只讀

進(jìn)制

每一種進(jìn)制都是完美的,而我們通??偸且允M(jìn)制為依托去考慮其他進(jìn)制,總是先轉(zhuǎn)換為十進(jìn)制,在轉(zhuǎn)換為其他進(jìn)制,其實(shí)這種方式是錯(cuò)誤的。所以想要學(xué)好進(jìn)制,首先需要忘掉十進(jìn)制,其次是忘掉進(jìn)制間的轉(zhuǎn)換

進(jìn)制的定義

  • 八進(jìn)制由8個(gè)符號(hào)組成:0 1 2 3 4 5 6 7 逢八進(jìn)一
  • 十進(jìn)制由10個(gè)符號(hào)組成:0 1 2 3 4 5 6 7 8 9 逢十進(jìn)一
  • N進(jìn)制就是由N個(gè)符號(hào)組成:逢N進(jìn)一
<!--練習(xí)-->
- 1+1在___情況下等于3

<!--答案-->
十進(jìn)制由10個(gè)符號(hào)組成: 0 1 3 2 8 A B E S 7 逢十進(jìn)一
如果這樣定義十進(jìn)制:1+1等于3

<!--目的-->
傳統(tǒng)定義的十進(jìn)制和自定義的十進(jìn)制不一樣,如果不告訴別人符號(hào)表,別人是無(wú)法拿到具體的數(shù)據(jù)的。這樣的應(yīng)用場(chǎng)景主要是用于加密

進(jìn)制的運(yùn)算

八進(jìn)制加法表

0  1  2  3  4  5  6  7 
10 11 12 13 14 15 16 17
20 21 22 23 24 25 26 27
...

1+1 = 2                     
1+2 = 3   2+2 = 4               
1+3 = 4   2+3 = 5   3+3 = 6
1+4 = 5   2+4 = 6   3+4 = 7   4+4 = 10  
1+5 = 6   2+5 = 7   3+5 = 10  4+5 = 11  5+5 = 12
1+6 = 7   2+6 = 10  3+6 = 11  4+6 = 12  5+6 = 13  6+6 =14
1+7 = 10  2+7 = 11  3+7 = 12  4+7 = 13  5+7 = 14  6+7 =15  7+7 = 16

八進(jìn)制乘法表

0 1 2 3 4 5 6 7 10 11 12 13 14 15 16 17 20 21 22 23 24 25 26 27...
1*1 = 1                     
1*2 = 2   2*2 = 4               
1*3 = 3   2*3 = 6   3*3 = 11    
1*4 = 4   2*4 = 10  3*4 = 14  4*4 = 20
1*5 = 5   2*5 = 12  3*5 = 17  4*5 = 24  5*5 = 31
1*6 = 6   2*6 = 14  3*6 = 22  4*6 = 30  5*6 = 36  6*6 = 44
1*7 = 7   2*7 = 16  3*7 = 25  4*7 = 34  5*7 = 43  6*7 = 52  7*7 = 61

做個(gè)練習(xí)

  • 2 + 3 = __ , 2 * 3 = __ ,4 + 5 = __ ,4 * 5 = __.
  • 277 + 333 = __ , 276 * 54 = __ , 237 - 54 = __ , 234 / 4 = __ .
    第一排的計(jì)算,我們可以很好的得出結(jié)論
    但是第二排涉及數(shù)據(jù)較大時(shí),如何計(jì)算呢,下面以277+333為例,計(jì)算過(guò)程如下


    277+333計(jì)算過(guò)程

實(shí)戰(zhàn)四則運(yùn)算

   277         236         276         234
+  333       -  54       *  54       /   4
--------    --------    --------    --------   
   632         162        1370          47
                       + 1666     

二進(jìn)制的簡(jiǎn)寫(xiě)形式

       二進(jìn)制: 1 0 1 1 1 0 1 1 1 1 0 0
三個(gè)二進(jìn)制一組: 101 110 111 100
       八進(jìn)制:   5   6   7   4
四個(gè)二進(jìn)制一組: 1011 1011 1100
     十六進(jìn)制:    b    b    c

使用二進(jìn)制從0寫(xiě)到1111:0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
發(fā)現(xiàn)這樣使用二進(jìn)制太麻煩,所以將其改為更簡(jiǎn)單一點(diǎn)的符號(hào)
0 1 2 3 4 5 6 7 8 9 A B C D E F 這就是十六進(jìn)制了

數(shù)據(jù)的寬度

數(shù)學(xué)上的數(shù)字是沒(méi)有大小限制的,可以無(wú)限大
但是在計(jì)算機(jī)中,由于硬件的制約,數(shù)據(jù)都是有長(zhǎng)度限制的(稱為數(shù)據(jù)寬度),超過(guò)最多寬度的數(shù)據(jù)會(huì)被丟棄

#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]));
    }
}

斷點(diǎn)調(diào)試結(jié)果如下,可以發(fā)現(xiàn)1溢出了

斷點(diǎn)調(diào)試-01

也可以通過(guò)獲取的地址,然后在Debug-Debug Workflow-ViewMemory中輸入地址查看
斷點(diǎn)調(diào)試-02

計(jì)算機(jī)中常見(jiàn)的數(shù)據(jù)寬度

  • 位(Bit):1個(gè)位就是1個(gè)二進(jìn)制位,即0或1
  • 字節(jié)(Byte):1個(gè)字節(jié)由8個(gè)Bit組成,內(nèi)存中的最小單元Byte
  • 字(Word):1個(gè)字由兩個(gè)字節(jié)組成(16位),第2個(gè)字節(jié)分別稱為高字節(jié)和低字節(jié)
  • 雙字(DoubleWord):1個(gè)雙字由兩個(gè)字組成(32位)

那么計(jì)算機(jī)存儲(chǔ)數(shù)據(jù)它會(huì)分為有符號(hào)數(shù)無(wú)符號(hào)數(shù),如下圖所示

負(fù)數(shù)+正數(shù)圖示

  • 無(wú)符號(hào)數(shù),直接換算
  • 有符號(hào)數(shù),符號(hào)放在第1位,第1位是0即正數(shù),為1即負(fù)數(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

自定義進(jìn)制符號(hào)

練習(xí)1
現(xiàn)在有10進(jìn)制數(shù) 10個(gè)符號(hào)分別是:2,9,1,7,6,5,4, 8,3 , A 逢10進(jìn)1 那么: 123 + 234 = ____(AA6)

十進(jìn)制:    0  1  2  3  4  5  6  7  8  9
自定義:    2  9  1  7  6  5  4  8  3  A
(十進(jìn)制10)92 99 91 97 96 95 94 98 93 9A
         12 19 11 17 16 15 14 18 13 1A
         72 79 71 77 76 75 74 78 73 7A
         62 69 61 67 66 65 64 68 63 6A
         52 59 51 57 56 55 54 58 53 5A
         42 49 41 47 46 45 44 48 43 4A
         82 89 81 87 86 85 84 88 83 8A
         32 39 31 37 36 35 34 38 33 3A
         922
         
(00)2  (01)9  (02)1  (03)7  (04)6  (05)5  (06)4  (07)8  (08)3  (09)A
(10)92 (11)99 (12)91 (13)97 (14)96 (15)95 (16)94 (17)98 (18)93 (19)9A
(20)12 (21)19 (22)11 (23)17 (24)16 (25)15 (26)14 (27)18 (28)13 (29)1A
(30)72 (31)79 (32)71 (33)77 (34)76 (35)75 (36)74 (37)78 (38)73 (39)7A
(40)62 (41)69 (42)61 (43)67 (44)66 (45)65 (46)64 (47)68 (48)63 (49)6A
(50)52 59 51 57 56 55 54 58 53 5A
(60)42 49 41 47 46 45 44 48 43 4A
(70)82 89 81 87 86 85 84 88 83 8A
(80)32 39 31 37 36 35 34 38 33 3A
(90)922

剛才通過(guò)10進(jìn)制運(yùn)算可以轉(zhuǎn)換,然后查表,但是如果是其他進(jìn)制,就不能轉(zhuǎn)換,要學(xué)會(huì)直接查表

練習(xí)2
現(xiàn)在有9進(jìn)制數(shù) 9個(gè)符號(hào)分別是:2,9,1,7,6,5,4, 8,3 逢9進(jìn)1 那么: 123 + 234 = ____(9926)

十進(jìn)制:    0  1  2  3  4  5  6  7  8  
自定義:    2  9  1  7  6  5  4  8  3  
         92 99 91 97 96 95 94 98 93 
         12 19 11 17 16 15 14 18 13 
         72 79 71 77 76 75 74 78 73 
         62 69 61 67 66 65 64 68 63 
         52 59 51 57 56 55 54 58 53 
         42 49 41 47 46 45 44 48 43 
         82 89 81 87 86 85 84 88 83 
         32 39 31 37 36 35 34 38 33 
         922
         
(00)2  (01)9  (02)1  (03)7  (04)6  (05)5  (06)4  (07)8  (08)3  
(10)92 (11)99 (12)91 (13)97 (14)96 (15)95 (16)94 (17)98 (18)93 
(20)12 19 11 17 16 15 14 18 13 
(30)72 79 71 77 76 75 74 78 73 
(40)62 69 61 67 66 65 64 68 63 
(50)52 59 51 57 56 55 54 58 53 
(60)42 49 41 47 46 45 44 48 43 
(70)82 89 81 87 86 85 84 88 83 
(80)32 39 31 37 36 35 34 38 33 
(90)922

CPU & 寄存器

內(nèi)部部件之間是由總線連接,如下圖所示


cpu圖示
  • CPU除了有控制器、運(yùn)算器,還有寄存器,其中寄存器的作用就是進(jìn)行數(shù)據(jù)的臨時(shí)存儲(chǔ)

  • CPU的運(yùn)算速度是非??斓模瑸榱诵阅蹸PU在內(nèi)部開(kāi)辟了一小塊臨時(shí)存儲(chǔ)區(qū)域,并在進(jìn)行運(yùn)算時(shí)先將數(shù)據(jù)從內(nèi)存中復(fù)制到這一小塊臨時(shí)區(qū)域中。運(yùn)算就在這一小塊臨時(shí)存儲(chǔ)區(qū)進(jìn)行,稱這一小塊臨時(shí)存儲(chǔ)區(qū)域?yàn)?code>寄存器

針對(duì)arm64的CPU來(lái)說(shuō),

  • 如果寄存器以x開(kāi)頭,則表明是一個(gè)64位的寄存器

  • 如果寄存器以w開(kāi)頭,則表明是一個(gè)32位的寄存器

在系統(tǒng)中沒(méi)有提供16位和32位的寄存器供訪問(wèn)和使用,其中32位的寄存器是64位寄存器的低32位部分,并不是獨(dú)立存在的

  • 對(duì)于程序員來(lái)說(shuō),CPU中最主要的部件是寄存器,可以通過(guò)改變寄存器的內(nèi)容來(lái)實(shí)現(xiàn)對(duì)CPU的控制

  • 不同CPU,寄存器的個(gè)數(shù)和結(jié)構(gòu)是不相同的

浮點(diǎn)和向量寄存器

因?yàn)楦↑c(diǎn)數(shù)的存儲(chǔ)以及其運(yùn)算的特殊性,CPU中專門提供浮點(diǎn)寄存器來(lái)處理浮點(diǎn)數(shù)

  • 浮點(diǎn)寄存器
    • 64位:D0 - D31
    • 32位:S0 - S31

現(xiàn)在CPU支持向量運(yùn)算(向量運(yùn)算在圖形處理相關(guān)的領(lǐng)域用的非常多),為了支持向量計(jì)算,系統(tǒng)也提供了眾多的向量寄存器

  • 向量寄存器 128位:V0 - V31

通用寄存器

  • 通用寄存器也稱為數(shù)據(jù)地址寄存器。通常用來(lái)做數(shù)據(jù)計(jì)算的臨時(shí)存儲(chǔ)、累加、計(jì)數(shù)、地址保存等功能。定義這些寄存器的作用主要是用于在CPU指令中保存操作數(shù),在CPU中當(dāng)做一些常規(guī)變量來(lái)使用
  • arm64擁有32個(gè)64位的通用寄存器X0-X30,以及XZR(零寄存器)。這些通用寄存器有時(shí)也有特定用途
    • 那么w0-w28這些32位的,因?yàn)?4位CPU可以兼容32位,所以可以只使用64位寄存器的低32位
    • 例如w0就是x0的低32位

注意:了解過(guò)8086匯編的一定知道,還有一種特殊的寄存器段寄存器:CS、DS、SS、ES。這四個(gè)寄存器主要用來(lái)保存這些段的基地址,是屬于Intel架構(gòu)的CPU的,在arm中并沒(méi)有

  • 通常,CPU會(huì)先將內(nèi)存中的數(shù)據(jù)存儲(chǔ)到通用寄存器中,然后再對(duì)寄存器中的數(shù)據(jù)進(jìn)行運(yùn)算
    假設(shè)內(nèi)存中有塊紅色內(nèi)存空間的值是3,現(xiàn)在想把它的值加1,并將結(jié)果存儲(chǔ)到藍(lán)色內(nèi)存空間
    內(nèi)存案例
    • CPU首先會(huì)將紅色內(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ù)沒(méi)有任何區(qū)別,都是二進(jìn)制信息
  • CPU在工作時(shí),將有的信息看作指令,有的看作數(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)容曾經(jīng)被CPU執(zhí)行過(guò),那么它所在的內(nèi)存單元必然被pc指向過(guò)
寄存器案例分析

下面通過(guò)以下代碼的例子來(lái)分析

#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]));
    }
}
  • demo中寄存器種類如下所示


    image
  • 其中通用寄存器如下所示


    image
  • 以下是demo的匯編圖示


    image

pc寄存器調(diào)試

  • 打印pc寄存器,現(xiàn)在是fdec4


    pc寄存器調(diào)試-01
  • 按住control+Step into,繼續(xù)打印


    pc寄存器調(diào)試-02

    pc寄存器調(diào)試-03

除了讀還可以寫(xiě)

  • register write pc 0x104c31ecc
  • register read pc 此時(shí)是讀不出來(lái)的,因?yàn)閿帱c(diǎn)斷住了,如果step into,此時(shí)斷點(diǎn)斷在哪里?最終通過(guò)驗(yàn)證發(fā)現(xiàn),會(huì)斷在cc的下一行


    pc寄存器調(diào)試-04

    pc寄存器調(diào)試-05

    此時(shí)pc指向cc,會(huì)將cc中的指令拿出來(lái)執(zhí)行,執(zhí)行完畢后走到cc的下一條指令,而此時(shí)d0的指令是還沒(méi)有執(zhí)行的

高速緩存

iPhoneX上搭載的arm處理器A11,它的1級(jí)緩存的容量是64kb,2級(jí)緩存的容量是8M

CPU每執(zhí)行一條指令前都需要從內(nèi)存中將質(zhì)量讀取到CPU內(nèi)存并執(zhí)行,而寄存器的運(yùn)行速度相比內(nèi)存讀寫(xiě)要快很多,為了性能,CPU還集成了一個(gè)高速緩存區(qū)域。當(dāng)程序運(yùn)行時(shí),先將要執(zhí)行的指令代碼以及數(shù)據(jù)復(fù)制到高速緩存中(由操作系統(tǒng)完成),然后CPU直接從高速緩存依次讀取指令來(lái)執(zhí)行

bl指令

  • CPU從何處執(zhí)行指令,是由pc中的內(nèi)容決定的,可以通過(guò)改變pc的內(nèi)容來(lái)控制CPU執(zhí)行目標(biāo)指令
  • arm64提供了一個(gè)mov指令(傳送指令),可以用來(lái)改變大部分寄存器的值,例如 mov x0,#10、mov x1,#20
  • 但是,mov指令并不能用于設(shè)置pc的值,arm64沒(méi)有提供這樣的功能
  • arm64提供了另外的指令來(lái)修改pc的值,這些指令統(tǒng)一稱為轉(zhuǎn)移指令,其中最簡(jiǎn)單的是bl指令

bl指令練習(xí)
現(xiàn)在有兩段代碼!假設(shè)程序先執(zhí)行A,請(qǐng)寫(xiě)出指令執(zhí)行順序.最終寄存器x0的值是多少?

_A:
    mov x0,#0xa0
    mov x1,#0x00
    add x1, x0, #0x14
    mov x0,x1
    bl _B
    mov x0,#0x0
    ret

_B:
    add x0, x0, #0x10
    ret
    
<!--結(jié)果-->
流程:
    mov x0,#0xa0            -- x0:0xa0
    mov x1,#0x00            -- x1:0x00
    add x1, x0, #0x14       -- x1:0xa0+0x14=0xb4
    mov x0,x1               -- x0:0xb4
    add x0, x0, #0x10       -- x0:0xb4+0x10=0xc4
    ret                     -- 回到bl跳轉(zhuǎn)的下一行
    mov x0,#0x0             -- x0:0x00

x0的值:0x00
匯編代碼驗(yàn)證

下面來(lái)寫(xiě)一段匯編代碼驗(yàn)證(com+n -- empty -- asm.s(匯編代碼文件,會(huì)編譯成源碼))


匯編代碼驗(yàn)證-匯編代碼

如何執(zhí)行呢?

  • 在VC中定義函數(shù)的聲明


    匯編代碼驗(yàn)證-01
  • 在A()執(zhí)行處加斷點(diǎn),并執(zhí)行程序,開(kāi)啟匯編調(diào)試


    匯編代碼驗(yàn)證-02

    匯編代碼驗(yàn)證-03
  • 按住control+step into,進(jìn)入A的具體匯編


    匯編代碼驗(yàn)證-04

然后開(kāi)始lldb調(diào)試,下面是一步步到0x00的過(guò)程

  • 進(jìn)入?yún)R編代碼A


    匯編代碼驗(yàn)證-05

    匯編代碼驗(yàn)證-06

    匯編代碼驗(yàn)證-07
  • 進(jìn)入?yún)R編代碼B


    匯編代碼驗(yàn)證-08

    匯編代碼驗(yàn)證-09
  • 執(zhí)行到A的ret

    匯編代碼驗(yàn)證-10

    疑問(wèn):發(fā)現(xiàn)走到這里死循環(huán)了,這是為什么呢?預(yù)知后事如何,請(qǐng)看下一篇文章

總結(jié)

  • 1、匯編概述

    • 使用助記符代替機(jī)器指令的一種編程語(yǔ)言

    • 匯編和機(jī)器指令是一一對(duì)應(yīng)的關(guān)系,拿到二進(jìn)制就可以反匯編

    • 由于匯編和CPU的指令集是對(duì)應(yīng)的,所以匯編不具備移植性

  • 2、總線:是由一堆導(dǎo)線的集合

    • 地址總線:其寬度決定了尋址能力

    • 數(shù)據(jù)總線:其寬度決定了CPU數(shù)據(jù)的吞吐量

    • 控制總線:其寬度決定了CPU對(duì)其他器件的控制能力

  • 3、進(jìn)制

    • 任意進(jìn)制都是由對(duì)應(yīng)個(gè)數(shù)的符號(hào)組成的,符號(hào)可以自定義

    • 2/8/16是相對(duì)完美的進(jìn)制,他們之間的關(guān)系

      • 3個(gè)二進(jìn)制 使用一個(gè)8進(jìn)制標(biāo)識(shí)

      • 4個(gè)二進(jìn)制 使用一個(gè)16進(jìn)制標(biāo)識(shí)

      • 兩個(gè)16進(jìn)制可以標(biāo)識(shí)一個(gè)字節(jié),即8位

    • 數(shù)量單位

      • 1024 = 1k,1024k = 1M,1024M = 1G
    • 容量單位

      • 1024 = 1KB,1024KB = 1MB,1024MB = 1GB
    • 數(shù)據(jù)的寬度

      • 計(jì)算機(jī)中的數(shù)據(jù)是有寬度的,超過(guò)了就會(huì)溢出
  • 4、寄存器:CPU為了性能,自內(nèi)部開(kāi)辟了一小塊臨時(shí)存儲(chǔ)區(qū)域

    • 浮點(diǎn)向量寄存器:用于浮點(diǎn)數(shù)/向量的存儲(chǔ)及運(yùn)算

    • 異常狀態(tài)寄存器

    • 通用寄存器:除了存放數(shù)據(jù)有時(shí)也有特殊的用途

      • ARM64擁有32個(gè)64位的通用寄存器X0-X30以及XZR(零寄存器)

      • 為了兼容32位,所以arm64位擁有W0-W28以及WZR 30個(gè)32位寄存器

      • 32位寄存器并不是獨(dú)立存在的,例如 W0是X0的低32位

    • PC寄存器:指令指針寄存器

      • PC寄存器里面的保存的就是CPU接下來(lái)需要執(zhí)行的指令地址

      • 改變PC的值可以改變程序的執(zhí)行流程

      • mov指令不能更改PC寄存器的值,需要通過(guò)bl跳轉(zhuǎn)指令來(lái)改變PC寄存器的值

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容