彙編語言初識

機器指令展開就是一臺機器可以正確執行的命令。機器所能識別的只有0和1。
這就是計算機的底層,使用二進制來執行命令
彙編語言的產生方便了人們更加容易去編寫代碼,進而編譯成0和1組成的機器碼讓計算機去執行命令。而我們所使用的C語言等編程語言則又比彙編語言更高級,它們會先編譯成彙編語言,再由彙編語言去編譯爲機器指令讓計算機執行。

彙編語言是什麼

簡答來說,彙編語言就是機器語言(二進制代碼)的助記符,每條彙編語言都能直接翻譯成機器語言,如下圖。計算機就是一臺各種電子設備組成的機器,它只能識別機器代碼,即一堆二進制數字。但是二進制不易於人類閱讀,而且在計算機發展初期還沒有高級語言和編譯器,因此出現了彙編語言。僅僅這樣一個微創新,就大大提升了開發效率。
在這裏插入圖片描述彙編語言常見的語法是 指令 參數1, 參數2 ,指令不同參數也不同。指令即 addmov jmp 等常見的算數、邏輯運算和跳轉等功能,參數可以是立即數、內存地址、寄存器。因此,彙編語言編程能深入到計算機編程的最底層,通常說彙編語言是一種“面向機器的語言 / 編程”。正是因爲這個特點,使得彙編語言能提供所有編程語言中最大的時間和空間的效率,因此至今依然活躍在某些計算機領域。

彙編語言都是針對特定的計算機體系結構的,例如 x86 彙編(本課重點內容)、MIPS 彙編、ARM 彙編,因此沒有讓所有計算機都通用的彙編語言。

彙編語言是比我們學習的編程語言要古老的語言。我們通過學習編程語言,可以瞭解到程序在計算機中是如何被執行的。這種本質上的理解是我們更深層次地去學習計算機所需要的。

指令集分類

所謂“指令集”,我理解就是一套操作 CPU 的指令體系集合,以及體系規範。指令集是一種上層定義,彙編就是其具體的體現和實現。指令集分兩類:

CISC 複雜指令集,以 x86 爲代表(x86 在 PC 服務器領域具有統治地位)
RISC 精簡指令集,以 ARM MIPS 爲代表(ARM 統治了手機和平板領域,MIPS 常用語手機、電腦之外的其他電子設備)

CISC

最初的計算機編程很麻煩,例如用紙帶打孔輸入,因此計算機的設計者就考慮將 CPU 做的複雜一點,以簡化這種本來就很麻煩的編程。因此有了 CISC 複雜指令集。x86 就是其中的典型代表,x86 的特點是:

指令向下兼容(這是其商業成功的重要因素之一),缺點就是會讓指令集越來越大、越來越複雜,功耗也更大(因此不適用於低功耗設備)
變長指令(MIPS 是等長的,只有 32 位),優點是節省空間、擴展性好,缺點是譯碼複雜
多種尋址方式
通用寄存器個數有限,x86-32 只有 8 個通用寄存器,x86-64 也只有 16 個寄存器
指令中,最多能有一個操作數在內存中,其他的操作數必須是立即數或者寄存器

RISC

歷史原因,RISC 是 80 年代初發明的,那時整個計算機生態系統已經形成,編譯器能力增強,就不需要 CPU 對外暴露過度複雜的指令集,因此有了 RISC 精簡指令集。
MIPS ARM 是 RISC 的代表,RISC 指令集特點是
1.只關注一些簡單常用的指令,因此簡單輕量、高性能、功耗低
2.那些不常用的複雜指令,就依賴於編譯器(即用軟件來實現,而不是依賴於硬件的複雜指令),那時編譯器已經比較強大

MIPS 特點:

以寄存器爲中心。一出手就是 32 位(即寄存器是 32 位的),而且有 32 個通用寄存器
只有 load 和 store 指令可以訪問內存,其他指令只能操作寄存器和立即數(以寄存器爲中心嘛)
指令格式規範,長度一致(32 位),導致空間利用率不高,但是譯碼效率高
尋址方式非常簡單

ARM 指令集特點:

大多數指令支持“條件執行”模式,能使得代碼比較精簡
具有 16 位壓縮指令集,低功耗、低存儲場景下很適用(ARM 在移動領域取得很大的成功,如 iphone 上的 A 系列處理器)

彙編語言是面向機器的最基礎編程,既然是編程就涉及到內存的使用和分配,於是就有了內存模型。某個程序開始運行之前,操作系統會給它分配一段內存空間,用於存儲改程序時使用的、產出的數據。具體這塊內存區域的大小和起止指針先不用關心。
棧 Stack
棧這個數據結構的特點是“先進後出”。像 C 語言這種“過程調用過程”後者“函數調用函數”的執行方式,最先調用的過程或者函數,會是最後一個結束。這一特點和棧的特點基本一致。

需要強調一點,在整個這段內存空間中,棧是自上(高地址)而下(低地址)進行累積的,即棧頂的內存地址比棧底的內存地址要小。這一點和堆正好相反,如下圖:
在這裏插入圖片描述壓棧 push

當一個過程或者函數被執行時,會有一些數據(參數、局部變量、返回地址)需要臨時存儲起來。而且在“函數調用函數”的整個過程中,會有很多這樣的操作。那麼就在每個函數執行時,將這些數據壓棧。如下圖,注意調用鏈和壓棧的關係(其中兩個 amI 是發生了遞歸調用)。
在這裏插入圖片描述當前正在執行的函數對應的棧,叫做“棧幀”,%ebp 和 %esp 兩個寄存器分別存儲了該棧幀兩端的地址。
這些棧堆的內容就是逆向和pwn的最基礎題的出題方向。

出棧 pop

棧中的數據是有聲明週期的,每個函數執行完 return 之後,其對應的數據就要被 pop ,並釋放這段內存空間。因此棧的內存空間是由系統分配、系統自動釋放,不需要人爲干預。

堆 heap
在整個程序被分配的內存空間裏,棧是系統自己使用和分配,自上而下的累積。其中還有一部分內存空間是給程序猿使用的,即你可以通過程序動態佔有一部分內存(如 C 語言的 malloc ,C++ 的 new ,其他高級語言的引用類型),這部分內存叫“堆”。它和棧不一樣:

堆是 自下(內存低地址)而上(內存高地址) 的累積的
堆沒有“先進後出”這種規則,它就是簡單粗暴的佔有和釋放
堆中被佔用的內存不會自動釋放,需要手動釋放,或者通過虛擬機定期 GC(如常見的引用計數方法、標記清除方法等)

簡單的彙編指令

addl 參數1, 參數2 加法
movl Source, Dest 賦值
leal Source, Dest 計算出地址賦值給 Dest
cmpl Src2, Src1 比較,類似於計算 Src2 - Src1
add 和 mov 等表示指令類型,後面的 l 是一個後綴,表示一次性操作 2 bytes 。這樣的後綴還有很多,例如 b w ,都有不同的含義

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章