我們在學(xué)習(xí)逆向開發(fā)之前,我們要了解一個基本的逆向原理.首先我們是逆向iOS系統(tǒng)上面的APP.那么我們知道,一個APP安裝在手機上面的可執(zhí)行文件本質(zhì)上是二進制文件.因為iPhone手機本質(zhì)上執(zhí)行的指令是二進制.是由手機上的CPU執(zhí)行的.所以逆向開發(fā)是建立在分析二進制上面.所以今天我們接下來的課程從非?;A(chǔ)的東西開始講解.
匯編語言的發(fā)展
機器語言
由0和1組成的機器指令.
加:0100 0000
減:0100 1000
乘:1111 0111 1110 0000?
除:1111 0111 1111 0000?
匯編語言(assembly language)
使用助記符代替機器語言
如:
加:INC EAX 通過編譯器 0100 0000
減:DEC EAX 通過編譯器 0100 1000
乘:MUL EAX 通過編譯器 1111 0111 1110 0000
除:DIV EAX 通過編譯器 1111 0111 1111 0000
高級語言(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
我們的代碼在終端設(shè)備上是這樣的過程:

匯編語言與機器語言一一對應(yīng),每一條機器指令都有與之對應(yīng)的匯編指令
匯編語言可以通過編譯得到機器語言,機器語言可以通過反匯編得到匯編語言
高級語言可以通過編譯得到匯編語言?\?機器語言,但匯編語言\機器語言幾乎不可能還原成高級語言
匯編語言的特點
可以直接訪問、控制各種硬件設(shè)備,比如存儲器、CPU等,能最大限度地發(fā)揮硬件的功能
能夠不受編譯器的限制,對生成的二進制代碼進行完全的控制
目標(biāo)代碼簡短,占用內(nèi)存少,執(zhí)行速度快
匯編指令是機器指令的助記符,同機器指令一一對應(yīng)。每一種CPU都有自己的機器指令集\匯編指令集,所以匯編語言不具備可移植性
知識點過多,開發(fā)者需要對CPU等硬件結(jié)構(gòu)有所了解,不易于編寫、調(diào)試、維護
不區(qū)分大小寫,比如mov和MOV是一樣的
匯編的用途(哥么我學(xué)了能干啥?)
編寫驅(qū)動程序、操作系統(tǒng)(比如Linux內(nèi)核的某些關(guān)鍵部分)
對性能要求極高的程序或者代碼片段,可與高級語言混合使用(內(nèi)聯(lián)匯編)
軟件安全
病毒分析與防治
逆向\加殼\脫殼\破解\外掛\免殺\加密解密\漏洞\黑客
理解整個計算機系統(tǒng)的最佳起點和最有效途徑
為編寫高效代碼打下基礎(chǔ)
弄清代碼的本質(zhì)
函數(shù)的本質(zhì)究竟是什么?
++a + ++a + ++a 底層如何執(zhí)行的?
編譯器到底幫我們干了什么?
DEBUG模式和RELEASE模式有什么關(guān)鍵的地方被我們忽略
......
最后來句裝13的話
越底層越單純!真正的程序員都需要了解的一門非常重要的語言,匯編!
匯編語言的種類
目前討論比較多的匯編語言有
8086匯編(8086處理器是16bit的CPU)
Win32匯編
Win64匯編
ARM匯編(嵌入式、Mac、iOS)
......
我們iPhone里面用到的是ARM匯編,但是不同的設(shè)備也有差異.因CPU的架構(gòu)不同.
架構(gòu)設(shè)備
armv6iPhone, iPhone2, iPhone3G, 第一代、第二代 iPod Touch
armv7iPhone3GS, iPhone4, iPhone4S,iPad, iPad2, iPad3(The New iPad), iPad mini, iPod Touch 3G, iPod Touch4
armv7siPhone5, iPhone5C, iPad4(iPad with Retina Display)
arm64iPhone5S 以后 iPhoneX , iPad Air, iPad mini2以后
幾個必要的常識
要想學(xué)好匯編,首先需要了解CPU等硬件結(jié)構(gòu)
APP/程序的執(zhí)行過程

硬件相關(guān)最為重要是CPU/內(nèi)存
在匯編中,大部分指令都是和CPU與內(nèi)存相關(guān)的
總線


每一個CPU芯片都有許多管腳,這些管腳和總線相連,CPU通過總線跟外部器件進行交互
總線:一根根導(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í)
* 一個CPU 的尋址能力為8KB,那么它的地址總線的寬度為____
* 8080,8088,80286,80386 的地址總線寬度分別為16根,20根,24根,32根.那么他們的尋址能力分別為多少____KB, ____MB,____MB,____GB?
* 8080,8088,8086,80286,80386 的數(shù)據(jù)總線寬度分別為8根,8根,16根,16根,32根.那么它們一次可以傳輸?shù)臄?shù)據(jù)為:____B,____B,____B,____B,____B,
* 從內(nèi)存中讀取1024字節(jié)的數(shù)據(jù),8086至少要讀____次,80386至少要讀取____次.
答案

內(nèi)存



內(nèi)存地址空間的大小受CPU地址總線寬度的限制。8086的地址總線寬度為20,可以定位220個不同的內(nèi)存單元(內(nèi)存地址范圍0x00000~0xFFFFF),所以8086的內(nèi)存空間大小為1MB
0x00000~0x9FFFF:主存儲器??勺x可寫
0xA0000~0xBFFFF:向顯存中寫入數(shù)據(jù),這些數(shù)據(jù)會被顯卡輸出到顯示器??勺x可寫
0xC0000~0xFFFFF:存儲各種硬件\系統(tǒng)信息。只讀
進制
學(xué)習(xí)進制的障礙
很多人學(xué)不好進制,原因是總以十進制為依托去考慮其他進制,需要運算的時候也總是先轉(zhuǎn)換成十進制,這種學(xué)習(xí)方法是錯誤的.
我們?yōu)槭裁匆欢ㄒD(zhuǎn)換十進制呢?僅僅是因為我們對十進制最熟悉,所以才轉(zhuǎn)換.
每一種進制都是完美的,想學(xué)好進制首先要忘掉十進制,也要忘掉進制間的轉(zhuǎn)換!
進制的定義
八進制由8個符號組成:0 1 2 3 4 5 6 7 逢八進一
十進制由10個符號組成:0 1 2 3 4 5 6 7 8 9逢十進一
N進制就是由N個符號組成:逢N進一
做個練習(xí)
1 + 1 在____情況下等于 3 ?
十進制由10個符號組成: 0 1 3 2 8 A B E S 7 逢十進一
如果這樣定義十進制: 1 + 1 = 3!就對了!
這樣的目的何在?
傳統(tǒng)我們定義的十進制和自定義的十進制不一樣.那么這10個符號如果我們不告訴別人這個符號表,別人是沒辦法拿到我們的具體數(shù)據(jù)的!用于加密!
十進制由十個符號組成,逢十進一,符號是可以自定義的!!
進制的運算
做個練習(xí)
八進制運算
2 + 3 = __ , 2 * 3 = __ ,4 + 5 = __ ,4 * 5 = __.
277 + 333 = __ , 276 * 54 = __ , 237 - 54 = __ , 234 / 4 = __ .
八進制加法表
八進制乘法表
實戰(zhàn)四則運算
二進制的簡寫形式
二進制:從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 這就是十六進制了
數(shù)據(jù)的寬度
數(shù)學(xué)上的數(shù)字,是沒有大小限制的,可以無限的大。但在計算機中,由于受硬件的制約,數(shù)據(jù)都是有長度限制的(我們稱為數(shù)據(jù)寬度),超過最多寬度的數(shù)據(jù)會被丟棄。
計算機中常見的數(shù)據(jù)寬度
位(Bit): 1個位就是1個二進制位.0或者1
字節(jié)(Byte): 1個字節(jié)由8個Bit組成(8位).內(nèi)存中的最小單元Byte.
字(Word): 1個字由2個字節(jié)組成(16位),這2個字節(jié)分別稱為高字節(jié)和低字節(jié).
雙字(Doubleword): 1個雙字由兩個字組成(32位)
那么計算機存儲數(shù)據(jù)它會分為有符號數(shù)和無符號數(shù).那么關(guān)于這個看圖就理解了!

自定義進制符號
練習(xí)
現(xiàn)在有10進制數(shù) 10個符號分別是:2,9,1,7,6,5,4, 8,3 , A 逢10進1 那么: 123 + 234 = ____
那么剛才通過10進制運算可以轉(zhuǎn)化10進制然后查表!但是如果是其他進制.我們就不能轉(zhuǎn)換,要直接學(xué)會查表
現(xiàn)在有9進制數(shù) 9個符號分別是:2,9,1,7,6,5,4, 8,3 逢9進1 那么: 123 + 234 = ____
寄存器
內(nèi)部部件之間由總線連接
對程序員來說,CPU中最主要部件是寄存器,可以通過改變寄存器的內(nèi)容來實現(xiàn)對CPU的控制
不同的CPU,寄存器的個數(shù)、結(jié)構(gòu)是不相同的
通用寄存器
ARM64擁有有31個64位的通用寄存器 x0 到 x30,這些寄存器通常用來存放一般性的數(shù)據(jù),稱為通用寄存器(有時也有特定用途)
那么w0 到 w28 這些是32位的. 因為64位CPU可以兼容32位.所以可以只使用64位寄存器的低32位.
比如 w0 就是 x0的低32位!
通常,CPU會先將內(nèi)存中的數(shù)據(jù)存儲到通用寄存器中,然后再對通用寄存器中的數(shù)據(jù)進行運算
假設(shè)內(nèi)存中有塊紅色內(nèi)存空間的值是3,現(xiàn)在想把它的值加1,并將結(jié)果存儲到藍色內(nèi)存空間
CPU首先會將紅色內(nèi)存空間的值放到X0寄存器中:mov X0,紅色內(nèi)存空間
然后讓X0寄存器與1相加:add X0,1
最后將值賦值給內(nèi)存空間:mov 藍色內(nèi)存空間,X0
pc寄存器(program counter)
為指令指針寄存器,它指示了CPU當(dāng)前要讀取指令的地址
在內(nèi)存或者磁盤上,指令和數(shù)據(jù)沒有任何區(qū)別,都是二進制信息
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指向過
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指令
bl指令 -- 練習(xí)
現(xiàn)在有兩段代碼!假設(shè)程序先執(zhí)行A,請寫出指令執(zhí)行順序.最終寄存器x0的值是多少?