iOS【安全攻防】初識匯編
今天我們來學習以下內容:
- 匯編概述
- 總線
- 進制
- 寄存器
我們在學習逆向開發之前,我們要了解一個基本的逆向原理。首先我們是逆向iOS系統上面的APP,那麽我們知道,一個APP安裝在手機上面的可執行文件本質上是二進制文件,因為iPhone手機本質上執行的指令是二進制,是由手機上的CPU執行的,所以逆向開發是建立在分析二進制上面。
一、匯編概述
1、匯編語言的發展
(1)機器語言
由0和1組成的機器指令(說白了是電信號)
-
- 加:0100 0000
- 減:0100 1000
- 乘:1111 0111 1110 0000
- 除:1111 0111 1111 0000
我們所寫的代碼最終跑在機器上的是什麽東西?是機器語言,是一堆的0,1,也叫做指令。
(2)匯編語言(assembly language)
使用助記符代替機器語言,也稱為符號語言
-
- 加:INC EAX 通過編譯器 0100 0000
- 減:DEC EAX 通過編譯器 0100 1000
- 乘:MUL EAX 通過編譯器 1111 0111 1110 0000
- 除:DIV EAX 通過編譯器 1111 0111 1111 0000
(3)高級語言(High-level programming language)
C\C++\Java\OC\Swift,更加接近人類的自然語言
比如C語言:
- 加:A+B 通過編譯器 0100 0000
- 減:A-B 通過編譯器 0100 1000
- 乘:A*B 通過編譯器 1111 0111 1110 0000
- 除:A/B 通過編譯器 1111 0111 1111 0000
我們的代碼在終端設備上是這樣的過程:
- 匯編語言與機器語言一一對應,每一條機器指令都有與之對應的匯編指令
- 匯編語言可以通過編譯得到機器語言,機器語言可以通過反匯編得到匯編語言
- 高級語言可以通過編譯得到匯編語言 \ 機器語言,但匯編語言\機器語言幾乎不可能還原成高級語言
2、匯編語言的特點
- 可以直接訪問、控制各種硬件設備,比如存儲器、CPU等,能最大限度地發揮硬件的功能
- 能夠不受編譯器的限制,對生成的二進制代碼進行完全的控制
- 目標代碼簡短,占用內存少,執行速度快
- 匯編指令是機器指令的助記符,同機器指令一一對應。每一種CPU都有自己的機器指令集\匯編指令集,所以匯編語言不具備可移植性
- 知識點過多,開發者需要對CPU等硬件結構有所了解,不易於編寫、調試、維護
- 不區分大小寫,比如mov和MOV是一樣的
3、匯編的用途
- 編寫驅動程序、操作系統(比如Linux內核的某些關鍵部分)
- 對性能要求極高的程序或者代碼片段,可與高級語言混合使用(內聯匯編)
- 軟件安全
- 病毒分析與防治
- 逆向\加殼\脫殼\破解\外掛\免殺\加密解密\漏洞\黑客
- 理解整個計算機系統的最佳起點和最有效途徑
- 為編寫高效代碼打下基礎
- 弄清代碼的本質
- 函數的本質究竟是什麽?
- sizeof
- ++a + ++a + ++a 底層如何執行的?
- 編譯器到底幫我們幹了什麽?
- DEBUG模式和RELEASE模式有什麽關鍵的地方被我們忽略
- ......
越底層越單純!真正的程序員都需要了解的一門非常重要的語言,匯編!
4、匯編語言的種類
目前討論比較多的匯編語言有
- 8086匯編(8086處理器是16bit的CPU)
- Win32匯編
- Win64匯編
- ARM匯編(嵌入式、Mac、iOS)
- ......
我們iPhone裏面用到的是ARM匯編,但是不同的設備也有差異.因CPU的架構不同.
架構 | 設備 |
---|---|
armv6 | iPhone, iPhone2, iPhone3G, 第一代、第二代 iPod Touc |
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以後 |
5、幾個必要的常識
- 要想學好匯編,首先需要了解CPU等硬件結構
- APP/程序的執行過程
- 硬件相關最為重要是CPU/內存
- 在匯編中,大部分指令都是和CPU與內存相關的
二、總線
- 每一個CPU芯片都有許多管腳,這些管腳和總線相連,CPU通過總線跟外部器件進行交互
- 總線:一根根導線的集合
- 總線的分類
- 地址總線
- 數據總線
- 控制總線
舉個例子:
1、地址總線
- 它的寬度決定了CPU的尋址能力
- 8086的地址總線寬度是20,所以尋址能力是1M( 220 )
2、數據總線
- 它的寬度決定了CPU的單次數據傳送量,也就是數據傳送速度
- 8086的數據總線寬度是16,所以單次最大傳遞2個字節的數據
3、控制總線
- 它的寬度決定了CPU對其他器件的控制能力、能有多少種控制
做個小練習 * 一個CPU 的尋址能力為8KB,那麽它的地址總線的寬度為____ * 8080,8088,80286,80386 的地址總線寬度分別為16根,20根,24根,32根.那麽他們的尋址能力分別為多少____KB, ____MB,____MB,____GB? * 8080,8088,8086,80286,80386 的數據總線寬度分別為8根,8根,16根,16根,32根.那麽它們一次可以傳輸的數據為:____B,____B,____B,____B,____B, * 從內存中讀取1024字節的數據,8086至少要讀____次,80386至少要讀取____次.
答案:
* 13
* 64KB 1MB 16MB 4GB
* 1B 1B 2B 2B 4B
* 512 256
-
內存地址空間的大小受CPU地址總線寬度的限制。8086的地址總線寬度為20,可以定位220個不同的內存單元(內存地址範圍0x00000~0xFFFFF),所以8086的內存空間大小為1MB
-
0x00000~0x9FFFF:主存儲器。可讀可寫
-
0xA0000~0xBFFFF:向顯存中寫入數據,這些數據會被顯卡輸出到顯示器。可讀可寫
-
0xC0000~0xFFFFF:存儲各種硬件\系統信息。只讀
三、進制
學習進制的障礙:
很多人學不好進制,原因是總以十進制為依托去考慮其他進制,需要運算的時候也總是先轉換成十進制,這種學習方法是錯誤的.
我們為什麽一定要轉換十進制呢?僅僅是因為我們對十進制最熟悉,所以才轉換.
每一種進制都是完美的,想學好進制首先要忘掉十進制,也要忘掉進制間的轉換!
1、進制的定義
- 八進制由8個符號組成:0 1 2 3 4 5 6 7 逢八進一
- 十進制由10個符號組成:0 1 2 3 4 5 6 7 8 9逢十進一
- N進制就是由N個符號組成:逢N進一
1 + 1 在____情況下等於 3 ? 十進制由10個符號組成: 0 1 3 2 8 A B E S 7 逢十進一,如果這樣定義十進制: 1 + 1 = 3!就對了!這樣的目的何在?傳統我們定義的十進制和自定義的十進制不一樣,那麽這10個符號如果我們不告訴別人這個符號表,別人是沒辦法拿到我們的具體數據的!用於加密!十進制由十個符號組成,逢十進一,符號是可以自定義的!!
2、進制的運算
做個練習: 八進制運算 2 + 3 = __ , 2 * 3 = __ ,4 + 5 = __ ,4 * 5 = __. 277 + 333 = __ , 276 * 54 = __ , 237 - 54 = __ , 234 / 4 = __ . 實戰四則運算: 277 236 276 234 + 333 - 54 * 54 / 4 -------- -------- -------- --------
八進制加法表:
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
八進制乘法表:
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
3、二進制的簡寫形式
二進制: 1 0 1 1 1 0 1 1 1 1 0 0 三個二進制一組: 101 110 111 100 八進制: 5 6 7 4 四個二進制一組: 1011 1011 1100 十六進制: b b c 二進制:從0 寫到 1111 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 這種二進制使用起來太麻煩,改成更簡單一點的符號: 0 1 2 3 4 5 6 7 8 9 A B C D E F 這就是十六進制了 //十六進制古時候就有了,古秤,半斤八兩
4、數據的寬度
數學上的數字,是沒有大小限制的,可以無限的大。但在計算機中,由於受硬件的制約,數據都是有長度限制的(我們稱為數據寬度),超過最多寬度的數據會被丟棄。
輸入以下代碼:
#import <UIKit/UIKit.h> #import "AppDelegate.h" int test(){ int cTemp = 0x1FFFFFFFF; return cTemp; } int main(int argc, char * argv[]) { printf("%x\n",test());//ffffffff @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }
我們可以看到1是存不進去的,int類型占4個字節(如下圖)
計算機中常見的數據寬度:
- 位(Bit): 1個位就是1個二進制位.0或者1
- 字節(Byte): 1個字節由8個Bit組成(8位).內存中的最小單元Byte.
- 字(Word): 1個字由2個字節組成(16位),這2個字節分別稱為高字節和低字節.
- 雙字(Doubleword): 1個雙字由兩個字組成(32位)
那麽計算機存儲數據它會分為有符號數和無符號數.那麽關於這個看圖就理解了!
無符號數,直接換算! 有符號數: 正數: 0 1 2 3 4 5 6 7 負數: F E D B C A 9 8 -1 -2 -3 -4 -5 -6 -7 -8
5、自定義進制符號
練習 現在有10進制數 10個符號分別是:2,9,1,7,6,5,4, 8,3 , A 逢10進1 那麽: 123 + 234 = ____ 十進制: 0 1 2 3 4 5 6 7 8 9 自定義: 2 9 1 7 6 5 4 8 3 A 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 那麽剛才通過10進制運算可以轉化10進制然後查表!但是如果是其他進制.我們就不能轉換,要直接學會查表 現在有9進制數 9個符號分別是:2,9,1,7,6,5,4, 8,3 逢9進1 那麽: 123 + 234 = ____ 十進制: 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
四、寄存器
內部部件之間由總線連接
- 對程序員來說,CPU中最主要部件是寄存器,可以通過改變寄存器的內容來實現對CPU的控制
- 不同的CPU,寄存器的個數、結構是不相同的
1、通用寄存器
- ARM64擁有有31個64位的通用寄存器 x0 到 x30,這些寄存器通常用來存放一般性的數據,稱為通用寄存器(有時也有特定用途)
- 那麽w0 到 w28 這些是32位的. 因為64位CPU可以兼容32位.所以可以只使用64位寄存器的低32位.
- 比如 w0 就是 x0的低32位!
-
通常,CPU會先將內存中的數據存儲到通用寄存器中,然後再對通用寄存器中的數據進行運算
-
假設內存中有塊紅色內存空間的值是3,現在想把它的值加1,並將結果存儲到藍色內存空間
-
- CPU首先會將紅色內存空間的值放到X0寄存器中:mov X0,紅色內存空間
- 然後讓X0寄存器與1相加:add X0,1
- 最後將值賦值給內存空間:mov 藍色內存空間,X0
2、pc寄存器(program counter)
- 為指令指針寄存器,它指示了CPU當前要讀取指令的地址
- 在內存或者磁盤上,指令和數據沒有任何區別,都是二進制信息
- CPU在工作的時候把有的信息看做指令,有的信息看做數據,為同樣的信息賦予了不同的意義
- 比如 1110 0000 0000 0011 0000 1000 1010 1010
- 可以當做數據 0xE003008AA
- 也可以當做指令 mov x0, x8
- CPU根據什麽將內存中的信息看做指令?
- CPU將pc指向的內存單元的內容看做指令
- 如果內存中的某段內容曾被CPU執行過,那麽它所在的內存單元必然被pc指向過
3、bl指令
- CPU從何處執行指令是由pc中的內容決定的,我們可以通過改變pc的內容來控制CPU執行目標指令
- ARM64提供了一個mov指令(傳送指令),可以用來修改大部分寄存器的值,比如
- mov x0,#10、mov x1,#20
-
但是,mov指令不能用於設置pc的值,ARM64沒有提供這樣的功能
-
ARM64提供了另外的指令來修改PC的值,這些指令統稱為轉移指令,最簡單的是bl指令
bl指令 -- 練習 現在有兩段代碼!假設程序先執行A,請寫出指令執行順序.最終寄存器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
iOS【安全攻防】初識匯編