ios安全攻防之匯編

越底層越單純!真正的程序員都需要了解的一門非常重要的語言,匯編!

機(jī)器語言

  • 我們所寫的語言最終安裝在機(jī)器上的是什么東西?
  • 是機(jī)器語言,一堆的0和1.
    如0101 001 1101 0110,由0和1組成的機(jī)器指令,說白了還是電信號.
    這些指令讓我們的cup執(zhí)行

匯編語言

  • 使用符號代替機(jī)器語言,也稱符號語言.如 mov ax,bx
  • 匯編和機(jī)器指令是一一對應(yīng)的,每一條機(jī)器指令都有與之對應(yīng)的匯編指令
  • 匯編語言可以通過編譯得到機(jī)器語言,機(jī)器語言可以通過反匯編得到匯編語言
  • 高級語言可以通過編譯得到匯編語言,但機(jī)器語言/匯編語言不能還原成高級語言

高級語言

  • 我們平時寫的Oc/swift/C/C++都屬于高級語言,更加接近人類的自然語言
我們的代碼在終端設(shè)備上是這樣的過程

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

  • 可以直接訪問、控制各種硬件設(shè)備,比如存儲器、CPU等,能最大限度地發(fā)揮硬件的功能
  • 能夠不受編譯器的限制,對生成的二進(jìn)制代碼進(jìn)行完全的控制
  • 目標(biāo)代碼簡短,占用內(nèi)存少,執(zhí)行速度快
  • 每一種CPU都有自己的機(jī)器指令集\匯編指令集,所以匯編語言不具備可移植性
  • 知識點(diǎn)過多,開發(fā)者需要對CPU等硬件結(jié)構(gòu)有所了解,不易于編寫、調(diào)試、維護(hù)
  • 不區(qū)分大小寫,比如mov和MOV是一樣的
  • 在匯編中,大部分指令都是和CPU與內(nèi)存相關(guān)的

匯編的用途

  • 編寫驅(qū)動程序、操作系統(tǒng)(比如Linux內(nèi)核的某些關(guān)鍵部分)
  • 對性能要求極高的程序或者代碼片段,可與高級語言混合使用(內(nèi)聯(lián)匯編)
  • 軟件安全
  • 病毒分析與防治
  • 逆向\加殼\脫殼\破解\外掛\免殺\加密解密\漏洞\黑客
    哇...破解 哇...外掛 哇...黑客 來自小學(xué)生的驚嘆 前段時間我弟弟讓我?guī)退I號...! 我很無奈啊有木有...??
  • 理解整個計(jì)算機(jī)系統(tǒng)的最佳起點(diǎn)和最有效途徑
  • 為編寫高效代碼打下基礎(chǔ)
  • 弄清代碼的本質(zhì)
  • 函數(shù)的本質(zhì)究竟是什么?
  • sizeof
  • ++a + ++a + ++a 底層如何執(zhí)行的?
  • 編譯器到底幫我們干了什么?
  • DEBUG模式和RELEASE模式有什么關(guān)鍵的地方被我們忽略

匯編語言的種類

  • 討論最多的匯編語言
  • 8086匯編(8086處理器是16bit的CPU)
  • Win32匯編
  • Win64匯編
  • ARM匯編(嵌入式、Mac、iOS)
  • ......
架構(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)
armv64 iPhone6s , iphone6s plus,iPhone6, iPhone6 plus,iPhone5S ,iPad Air, iPad mini2
APP/程序的執(zhí)行過程

總線

  • 每一個CPU芯片都有許多管腳,這些管腳和總線相連,CPU通過總線跟外部器件進(jìn)行交互
  • 總線:一根根導(dǎo)線的集合
  • 總線的分類
  • 地址總線
  • 數(shù)據(jù)總線
  • 控制總線
地址總線
  • 它的寬度決定了CPU的尋址能力
  • 8086的地址總線寬度是20,所以尋址能力是1M( 2^20 )
2 ^ 10  == 1024
2 ^ 20  == 2 ^ 10 * 2 ^ 10 = 1024 * 1024 = 1M
16^5 == 2^4^5 == 2^(4*5) == 2 ^ 20

2^20 == 1M
2^30 == 1G
2^32 == 1G*2^2 == 4G


數(shù)據(jù)總線
  • 它的寬度決定了cup單次傳輸數(shù)據(jù)的大小,也就是數(shù)據(jù)傳送速度
  • 數(shù)據(jù)總線直接影響cpu的吞吐量
  • 8086數(shù)據(jù)總線寬度是16,所以單次最大傳送2個字節(jié)的數(shù)據(jù).
  • 1根數(shù)據(jù)總線代表一個bit位 十根 = 1KB
每一個16進(jìn)制位代表4個bit,因?yàn)閮蓚€16進(jìn)制位位代表一個字節(jié),一個字節(jié)8個bit

一個16進(jìn)制位 = 4 bit
兩個16進(jìn)制位 = 1字節(jié)
1字節(jié) = 8bit

一個字節(jié) = 8個bit = 2個16進(jìn)制位
一個字 = 2個字節(jié) (分別為高字節(jié)和低字節(jié))

1 Byte == 8 bit
1B == 1Byte == 一個字節(jié)
1KB  == 1024 Byte
8KB == 1024 * 2^3
1MB  == 1024 * 1024 Byte
控制總線
  • 它的寬度決定了CPU對其他器件的控制能力、能有多少種控制

練習(xí)

  • 一個CPU 的尋址能力為8KB,那么它的地址總線的寬度為 (13)
尋址能力均是以字節(jié)為單元的
1KB = 10根線
1KB = 1024Byte
8=2^3
1024=2^10
8KB = 8*1024=2^3*2^10=2^(3+10)=2^13
  • 8080,8088,80286,80386 的地址總線寬度分別為16根,20根,24根,32根.那么他們的尋址能力分別為多少(64)KB, (1)MB
16根 = 2^6 = 64KB
20根 = 2^20 = 1MB
24根 = 2^24 = 2^20 * 2^4 = 16MB
32根 = 2^32 = 1G*2^2 = 4G  
  • 從內(nèi)存中讀取1024字節(jié)的數(shù)據(jù),8086至少要讀(512)次,80386至少要讀取(256)次.
8086數(shù)據(jù)總線寬度是16,單次最大傳送2個字節(jié)的數(shù)據(jù)
```


#寄存器
* 對程序員來說,CPU中最主要部件是寄存器,可以通過改變寄存器的內(nèi)容來實(shí)現(xiàn)對CPU的控制
* 不同的CPU,寄存器的個數(shù)、結(jié)構(gòu)是不相同的(8086是16位結(jié)構(gòu)的CPU)
* 8086有14個寄存器
  * 都是16位寄存器,可以放兩個字節(jié)
![8086內(nèi)部寄存器](http://upload-images.jianshu.io/upload_images/6990647-3392e0666c641b12.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

#####通用寄存器
* AX、BX、CX、DX這4個寄存器通常用來存放一般性的數(shù)據(jù),稱為通用寄存器(有時也有特定用途)
* 通常,CPU會先將內(nèi)存中的數(shù)據(jù)存儲到通用寄存器中,然后再對通用寄存器中的數(shù)據(jù)進(jìn)行運(yùn)算

  * 假設(shè)內(nèi)存中有塊紅色內(nèi)存空間的值是3,現(xiàn)在想把它的值加1,并將結(jié)果存儲到藍(lán)色內(nèi)存空間
![](http://upload-images.jianshu.io/upload_images/6990647-aaa413d7a51629bc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

  * 過程
    * cpu通過地址總線找到紅色內(nèi)存空間
    * 再通過控制總線告訴內(nèi)存條我要讀
    - 再通過數(shù)據(jù)總線讀給cpu(cpu通過運(yùn)算器+1 )
    - 在通過cpu通過地址總線找到藍(lán)色內(nèi)存
    - 通過控制總線告訴內(nèi)存我要寫
    - 通過數(shù)據(jù)總線把值傳給藍(lán)色內(nèi)存
  * 代碼過程
    - mov ax,紅色內(nèi)存空間
    - add ax,1
    - mov 藍(lán)色內(nèi)存,ax

- 上一代8086的寄存器都是8位的,為了保證兼容, AX、BX、CX、DX都可分為2個獨(dú)立的8位寄存器來使用
 - H代表高位寄存器
 - L代表低位寄存器

![](http://upload-images.jianshu.io/upload_images/6990647-608b1e8abe5aa552.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://upload-images.jianshu.io/upload_images/6990647-0775d3c4209e7cbe.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

#8086的尋址方式
* CPU訪問內(nèi)存單元時,要給出內(nèi)存單元的地址,所有的內(nèi)存單元都有唯一的地址,叫做物理地址
* 8086有20位地址總線,可以傳送20位的地址,1M的尋址能力
* 但它又是16位結(jié)構(gòu)的CPU,它內(nèi)部能夠一次性處理、傳輸、暫時存儲的地址為16位。如果將地址從內(nèi)部簡單地發(fā)出,那么它只能送出16位的地址,表現(xiàn)出來的尋址能力只有64KB

**8086采用一種在內(nèi)部用2個16位地址合成的方法來生成1個20位的物理地址**
![](http://upload-images.jianshu.io/upload_images/6990647-423c026e836e94ea.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![](http://upload-images.jianshu.io/upload_images/6990647-27dfee5fbe7449b3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

#內(nèi)存分段管理
- 8086是用“基礎(chǔ)地址(段地址×16) + 偏移地址 = 物理地址”的方式給出物理地址
- 在編程時可以根據(jù)需要,將若干連續(xù)地址的內(nèi)存單元看做一個段,用段地址×16定為段的起始地址(基礎(chǔ)地址),用偏移地址定位段中的內(nèi)存單元

#段寄存器
 * 8086有4個段寄存器:CS、DS、SS、ES,當(dāng)CPU需要訪問內(nèi)存時由這4個段寄存器提供內(nèi)存單元的段地址
  - CS (Code Segment):代碼段寄存器
  - DS (Data Segment):數(shù)據(jù)段寄存器
  - SS (Stack Segment):堆棧段寄存器
  - ES (Extra Segment):附加段寄存器

#CS和IP
 - CS為代碼段寄存器,IP為指令指針寄存器,它們指示了CPU當(dāng)前要讀取指令的地址
 - 任意時刻,8086CPU都會將CS:IP指向的指令作為下一條需要取出執(zhí)行的指令 (指向下一條指令的地址)

#指令和數(shù)據(jù)
 * 在內(nèi)存或者磁盤上,指令和數(shù)據(jù)沒有任何區(qū)別,都是二進(jìn)制信息
 - CPU在工作的時候把有的信息看做指令,有的信息看做數(shù)據(jù),為同樣的信息賦予了不同的意義
 - CPU根據(jù)什么將內(nèi)存中的信息看做指令?
  - CPU將CS:IP指向的內(nèi)存單元的內(nèi)容看做指令
  - 如果內(nèi)存中的某段內(nèi)容曾被CPU執(zhí)行過,那么它所在的內(nèi)存單元必然被CS:IP指向過

#jmp指令<修改cs、ip的值>
- CPU從何處執(zhí)行指令是由CS、IP中的內(nèi)容決定的,我們可以通過改變CS、IP的內(nèi)容來控制CPU執(zhí)行目標(biāo)指令
- 8086提供了一個mov指令(傳送指令),可以用來修改大部分寄存器的值,比如
 - mov ax,10、mov bx,20、mov cx,30、mov dx,40
- 但是,mov指令不能用于設(shè)置CS、IP的值,8086沒有提供這樣的功能
- 8086提供了另外的指令來修改CS、IP的值,這些指令統(tǒng)稱為轉(zhuǎn)移指令,最簡單的是jmp指令

#DS和[address]
- CPU要讀寫一個內(nèi)存單元時,必須要先給出這個內(nèi)存單元的地址,在8086中,內(nèi)存地址由段地址和偏移地址組成
- 8086中有一個DS段寄存器,通常用來存放要訪問數(shù)據(jù)的段地址
```
mov bx,1000H
mov ds,bx
mov al,[0]
```
 - 上面3條指令的作用將10000H(1000:0)中的內(nèi)存數(shù)據(jù)賦值到al寄存器中
 - mov al,[address]的意思將DS:address中的內(nèi)存數(shù)據(jù)賦值到al寄存器中
 - 由于al是8位寄存器,所以是將一個字節(jié)的數(shù)據(jù)賦值給al寄存器

- 8086不支持將數(shù)據(jù)直接送入段寄存器中,mov ds,1000H是錯誤的

---
 ** 寫幾條指令,將al中的數(shù)據(jù)送入內(nèi)存單元1000H中 **
```
mov bx,1000H
mov ds,bx
mov [0],al
```
>注意:“mov 內(nèi)存單元, 內(nèi)存單元”是不允許的,比如mov[0], [1]

#大小端
 - 大端模式,是指數(shù)據(jù)的高字節(jié)保存在內(nèi)存的低地址中,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的高地址中(高低\低高)(Big Endian)
 - 小端模式,是指數(shù)據(jù)的高字節(jié)保存在內(nèi)存的高地址中,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的低地址中(高高\(yùn)低低) (Little Endian)

>注意:ARM既可以工作在大端模式,也可以工作在小端模式

#棧

![表情最豐富的羽毛球](http://upload-images.jianshu.io/upload_images/6990647-4f53a210562f1591.gif?imageMogr2/auto-orient/strip)

- 棧:是一種具有特殊的訪問方式的存儲空間(后進(jìn)先出, Last In Out Firt,LIFO)
- 8086會將SS作為棧段的段地址,任意時刻,SS:SP指向棧頂元素(偏移地址)
- 8086提供了PUSH(入棧)和POP(出棧)指令來操作棧段的數(shù)據(jù)
 - 比如push ax是將ax的數(shù)據(jù)入棧,pop ax是將棧頂?shù)臄?shù)據(jù)送入ax

![](http://upload-images.jianshu.io/upload_images/6990647-c89e59550c287bca.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

>棧是沒有底的,底是我們自己想象的,棧頂是有的,就是sp指針
內(nèi)存里是絕對有數(shù)據(jù)的,哪怕是00它也是數(shù)據(jù),push相當(dāng)于改變內(nèi)存里的數(shù)據(jù),我們平時所說的新開辟一塊內(nèi)存空間,里面沒有數(shù)據(jù)并不是里面就是0000,而是對于我們來說,這里面我可以改它而不去讀它,讀的話就讀了一個野指針,讀的這塊野指針已經(jīng)被釋放了,所謂的野指針報(bào)錯,就是我們的地址被釋放了,里面并沒有存任何的數(shù)據(jù),這時候去拿野指針里的面的東西,我不認(rèn)識!這叫野.??

- 棧大小可以叫內(nèi)存的大小嗎?
  - 8086 棧大小只有64K

- 內(nèi)存區(qū)域里越往高地址走,我們系統(tǒng)自帶的一些內(nèi)容不讓我們修改(系統(tǒng)不讓我們修改的內(nèi)容在高地址)
- pop的時候sp是加(也就是往高地址走)

>pop和push只是偏移我們的sp指針
  pop僅僅是讀數(shù)據(jù),并沒有改,把sp偏移的數(shù)據(jù)讀出來
  push是改數(shù)據(jù),把sp偏移的內(nèi)存改數(shù)據(jù)
  pop越讀越大
  push越改越小

###棧越界
- 當(dāng)棧滿的時候在使用push指令入棧,或棧空的時候再使用pop指令出棧,都將發(fā)生棧頂超界的問題

- push超界比pop越界要危險(xiǎn)的多,因?yàn)閜op只是讀,而push是改,一旦push越界他有可能把別人的數(shù)據(jù)改掉(我這個app本來好好的你過來把我的數(shù)據(jù)改了,那這個app就完蛋了)
 >棧以外的地址很有可能存放著具有其他用途的數(shù)據(jù),代碼等.這些數(shù)據(jù)代碼可能在我們的程序中,也有可能在別的程序中(但是由于我們?nèi)霔r不小心修改這些代碼,數(shù)據(jù),會引發(fā)一連串的錯誤)


#棧段
- 對于8086來說,在編程時,可以根據(jù)需要,將一組內(nèi)存單元定義為一個段
- 我們可以將一組長度為N(N<=64KB)、地址連續(xù)、起始地址為16倍數(shù)的內(nèi)存單元,當(dāng)做棧空間來使用,稱為棧段。比如用10010H~1001FH這段內(nèi)存空間當(dāng)做棧來使用,我們就可以認(rèn)為10010H~1001FH是一個棧段,它的段地址為1001H,長度為16字節(jié)
- 如何使用push、pop等棧操作指令訪問我們定義的棧段
 - 用SS存放棧段的段地址,用SP存放棧頂?shù)钠频刂?
#Loop指令
- loop指令和cx寄存器配合使用,用于循環(huán)操作類似高級語言的for,while
- 使用格式
```
mov cx,循環(huán)次數(shù)
標(biāo)號:
    循環(huán)執(zhí)行的程序代碼
    loop 標(biāo)號
```

- loop指令執(zhí)行流程
步驟1 先將cx寄存器的值 - 1, cx = cx - 1
步驟2 判斷cx 的值
   - 如果不為零執(zhí)行標(biāo)號的代碼,又執(zhí)行 步驟 1
   - 如果為零執(zhí)行l(wèi)oop后面的代碼

>補(bǔ)充:
獲取數(shù)據(jù),除了通過ds段來獲取.還可以利用其它段地址來獲取
mov ax,ds:[0]
mov ax,cs:[0]
mov ax,ss:[0]
mov ax,es:[0]

#8086偽指令
- db(define byte) 自定義字節(jié)
- dw(define word)自定義字

#Call和ret指令
 - call指令  (相當(dāng)于執(zhí)行一個函數(shù))
   - call標(biāo)號
   - 將下一條指令的偏移地址入棧
   - 跳轉(zhuǎn)到定位的地址執(zhí)行指令!

- ret指令
  - ret指令就是將棧頂?shù)闹礟OP給IP,也就是執(zhí)行下一條指令
  - 棧頂?shù)弥凳鞘裁?
    - 下一條指令的偏移地址 











###**此文章用作學(xué)習(xí)筆記**
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 8086匯編 本筆記是筆者觀看小甲魚老師(魚C論壇)《零基礎(chǔ)入門學(xué)習(xí)匯編語言》系列視頻的筆記,在此感謝他和像他一樣...
    Gibbs基閱讀 37,385評論 8 114
  • 匯編總結(jié) 匯編的發(fā)展史 機(jī)械語言 由0和1組成的機(jī)器指令(如:0101 0001 1101 0110) 匯編語言(...
    iChuck閱讀 1,336評論 1 8
  • 王爽匯編全書知識點(diǎn)大綱 第一章 基礎(chǔ)知識 機(jī)器語言 匯編語言的產(chǎn)生 匯編語言的組成 存儲器 cpu對存儲器的讀寫 ...
    2c3ba901516f閱讀 2,453評論 0 1
  • 文/熊大 窗外的一縷陽光透過半開的窗戶,折射進(jìn)了他的臥房。正好照在他的臉上,他用手擋了擋眼睛。可是,陽光越來越強(qiáng)烈...
    挪威的熊大閱讀 771評論 0 1
  • 最新研究稱,全球現(xiàn)嚴(yán)重肥胖疫情,中國肥胖人數(shù)全球第一 瘦身 肥胖的危害 1. 肥胖危害大腦,易患阿爾茨海默癥 瘦身...
    吃癡的瘦閱讀 196評論 0 1