組合語言
CH1 基本觀念
架構
Image Not Showing
Possible Reasons
- The image file may be corrupted
- The server hosting the image is unavailable
- The image path is incorrect
- The image format is not supported
Learn More →
- Level 4 -> C++、Java…
- Level 3 -> 組語
- Level 2 -> ISA
- Level 1 -> 邏輯層
CH2 x86 程序架構
- IA-32 -> 32 bits Intel Architecture (32位頻寬Intel架構)
General-Purpose Registers (GPA) 通用暫存器
暫存器名稱 |
中文名稱 |
用途 |
AX |
累加器暫存器 |
用在算術運算 |
BX |
基址暫存器 |
作為一個指向資料的指標 |
CX |
計數器暫存器 |
用於移位/迴圈指令和迴圈 |
DX |
資料暫存器 |
用在算術運算和I/O操作 |
SP |
堆疊指標暫存器 |
用於指向堆疊的頂部 |
BP |
棧基址指標暫存器 |
用於指向堆疊的底部 |
SI |
源變址暫存器 |
在流操作中用作源的一個指標 |
DI |
標的索引暫存器 |
用作在流操作中指向標的的指標 |
暫存器 |
|
累加器 |
|
基址 |
|
計數器 |
|
資料 |
32-bit |
|
EAX |
|
EBX |
|
ECX |
|
EDX |
16-bit |
|
AX |
|
BX |
|
CX |
|
DX |
8-bit |
AH |
AL |
BH |
BL |
CH |
CL |
DH |
DL |
| 暫存器 | 堆疊指標 | 棧基址指標 | 源變址 | 標的索引 |
|––––|:––:|:––-:|:––:|:––-:|:––:|:––:|
| 32-bit |ESP | EBP | ESI | EDI |
| 16-bit | SP | BP | SI | DI |
- AH 與 AL 實際上是獨立不互相影響的,但對於 AX 來說 AH 與 AL 又是其一部份
Segment 區段暫存器
暫存器名稱 |
中文名稱 |
CS |
程式區段暫存器 |
DS |
資料區段暫存器 |
SS |
堆疊區段暫存器 |
ES |
額外區段暫存器 |
FS |
額外區段暫存器 |
GS |
額外區段暫存器 |
EFLAGS 暫存器
FLAG名稱 |
中文名稱 |
CF (Carry) |
進位旗標 |
OF (Overflow) |
溢位旗標 |
ZF (Zero) |
零值旗標 |
AF (Auxiliary) |
輔助進位旗標 |
PF (Parity) |
奇偶旗標 |
DF (Direction) |
方向旗標 |
IF (Interrupt) |
中斷旗標 |
TF (Trap) |
單步旗標 |
Instruction Pointer Register
- 簡稱 IP
- 用來指向下一個 CPU 準備執行的指令所在的 memory address
- 當指令執行後,IP 就會自動指向下一個準備執行的指令所在的 memory address。
CH3 基本組語程式架構
Basic Element
-
數字結尾
-
指令格式
- label
- mnemonic
- operand
- comment 用 「;」
- label: mnemonic [operands] [;comment]
-
指令範本
Data Types
Unsigned Data Types |
功用 |
Signed Data Types |
功用 |
BYTE |
8-bit unsigned integer |
SBYTE |
8-bit signed integer |
WORD |
16-bit unsigned integer |
SWORD |
16-bit signed integer |
DWORD |
32-bit unsigned integer |
SDWORD |
32-bit signed integer |
QWORD |
64-bit integer |
|
|
TBYTE |
80-bit integer |
|
|
Data Types |
功用 |
REAL4 |
32-bit (4 byte) IEEE短實數 |
REAL8 |
64-bit (8 byte) IEEE長實數 |
REAL10 |
80-bit (10 byte) IEEE延伸實數 |
定義
利用 DUP()
directive
EQU & Symbolic Constants
$ (current location counter)
EQU
- 用來定義有名稱的常數
- 定義好之後,值不可再度變更
- 類似 #define
TEXTEQU
- 可以用來定義定串,變成一個變數名稱
- 字串可以是文字、計算式、指令
= (equal-sign directive)
矩陣
- 存到記憶體裡的順序
- 用小頭端 (little-endian)
- 多位元組數值的最低位位元組首先寫入
- 高位元去大的位置、低位元去小的位置
- ex:list DWORD 12345678h

- 矩陣的存取
- 加上 [] 表示要取值
- 每移動的距離以 byte 為單位
CH4 Data Transfers, Addressing, and Arithmetic
Instruction operands 指令運算元
-
register
-
memory
- 指向儲存於 memory 中的資料,而 memory address 有可能是直接寫於指令中,或是由 register 的值所組成。
-
immediate
- 列於指令中的固定值,儲存於指令本身(在 code segment 中),而非在 data segment 中。
- 也就是常數,ex:45
-
implied
- 此種 operand 不會明確的顯示。例如:將某個 register 中的值加 1。
Instruction 指令
MOV 搬移資料
- 目地及來源,都不能是 immediate (變數名稱)
- 目地及來源,不能一起是 memory (變數名稱)
- 目地及來源,都必須相同大小 (表示 AX 中的值不能 mov 到 BL 中)
MOVZX 搬移資料(小到大)
- Zero Extension
- 當從 AL 搬到 AX 時 (小搬大)
- AH 的地方會填滿 0

MOVSX 搬移資料(小到大)
- Sign Extension
- 當從 AL 搬到 AX 時 (小搬大)
- AH 的地方會根據你目前的 sign 值(MSB),來把它填滿

XCHG 交換資料
- 目地及來源,都不能是 immediate (變數名稱)
- 目地及來源,不能一起是 memory (變數名稱)
INC 加一 & DEC 減一
ADD 加 & SUB 減
NEG 正負號交換
- neg 也就是把那個數字的位元變成二補數
- 因為 0 的二補數還是 0 (00000000),所以 neg 0 是 0
- 因為 -128 的二補數還是 -128 (10000000),所以 neg -128 是 -128
Flag
- 當 Flag 值為 1 時 -> set
- 當 Flag 值為 0 時 -> clear
Zero Flag (ZF) 看結果是否為 0
- 如果 ZF = 1 -> 值為 0
- 如果 ZF = 0 -> 值不為 0
Sign Flag (SF) 看是正是負
- 如果 SF = 0 -> 值為正的
- 如果 SF = 1 -> 值為負的
- SF 的判斷其實就是看最高的位元數字 (MSB)
Carry Flag (CF) 看 unsigned value 是否超出邊界
- 看無號數 unsigned value
- 當相加 時 or 最高位有進位時
- 當相減由小減大時 or 最高位有借位時
- 如果一個運算的結果超過 [0, 255]
Overflow Flag (OF) 看 signed value 是否超出邊界
- 看有號數 signed value
- 當兩個正數相加變成負數時
- 當兩個負數相加變成正數時
- 如果一個運算的結果超過 [-128, 127]
OFFSET

- 傳回變數從所在區段開始的偏移距離
- 也就是傳回變數的起使位置
PTR
- 由大的型態轉成小的型態
- 轉成多大的型態,就從最小位元開始算幾個byte
- 由小的型態轉成大的型態
- CPU 會自動把 byte 顛倒過來
TYPE
LENGTHOF
SIZEOF
- 傳回的值等於 LENGTHOF 乘上 TYPE 的值
- 如果目標矩陣為二元矩陣時
- LENGTHOF 及 SIZEOF 只會計算第一列定義的部分
LABEL
- 賦予下一行宣告的資料另一個名字和另一種型態
- LABEL 那一行並不會在記憶體獲取空間
- 取代 PTR 的效果
Indirect Addressing 間接定址
- 間接定址 -> 暫存器當作指標
- 利用指標的方式去存取矩陣中的值
- 跟 C 的指標很像
Indirect Operands 間接運算元
- 利用 esi 當作指標,去存取矩陣的元素
- esi 指標加 1 * 矩陣大小(byte) 後為往下一個格子存取
- 如果 OFFSET 的目標的一個變數而不是矩陣的話
Indexed Operands 索引運算元
Pointers
- 直接定義一個 pointer varible 指標變數
- 去存取其它變數的位置
- 指標大小必須為 DWORD
JMP and LOOP Instructions 迴圈
JMP 架構
LOOP 架構
- 使用 ecx 暫存器,存入要重複的次數
- ecx 會先減 1 再進入迴圈
- JMP 就會重覆執行特定區段的指令
- 迴圈的目的必須在現在位置-128到+127之間
- 如果 ecx 為 0 時
- 因為 ecx 會先減 1 再進入迴圈
Nested Loop
Ex:把矩陣數字加總
Ex:字串複製
CH5 Procedures 程序
使用 Library
- 先 INCLUDE Irvine32
- 作者寫的 Library
- 用 call 去叫 Function
一些 function
- call Clrscr 清空螢幕
- call DumpRegs 把暫存器和 flag 的值 print 出來
- call Crlf 換行
- call WriteBin 印成二進位
- call WriteDec 印成十進位
- call WriteHex 印成十六進位
- call WriteString 印字串
Stack Operations 堆疊操作
- 堆疊 -> 後進先出 (LIFO)
- 使用 ESP (stack pointer) 來指向堆疊

PUSH

- 先將堆疊指標的值減四 (在保護模式下)
- 再將資料拷貝到堆疊指標所指的位置
POP
- 複製堆疊中由 esp 所指向的內容到一個16-bit或 32-bit 的目的運算元
- 並存在變數或暫存器裡
- 再將 ESP 加 N
- 如果指到 16-bit esp+2
- 如果指到 32-bit esp+4
其它堆疊指令
- PUSHFD
- POPFD
- PUSHAD
- 將一般用途的32-bit暫存器以 eax ecx edx ebx esp ebp esi edi 順序 push 到堆疊
- POPAD
- PUSHA
- 將一般用途的16-bit途暫存器以 ax cx dx bx sp bp si di 順序 push 到堆疊
- POPA
指令操作
- ex:用 Stack 來實作 Nested Loop
Defining & Using Procedures 定義及使用程序
- 大問題可以分成一個個小區塊
- 這樣比較好管理
- 類似 function 的功能
創造一個程序
程序例子 call & ret
-
call 呼叫程序
-
ret (return) 回傳
-
call 會 push 00000025 到 stack 上
-
然後把 00000040 存進 EIP(下一個執行指標) 裡
- 也就是把 call 下一行的 offset 先存進 stack 裡
- 這樣到時候 call 跑完後才會知道要從 main 的哪裡繼續執行
-
ret 會從 stack pop 出 00000025 到 EIP 裡面
- 把剛剛存的 offset 重新存回 EIP
- 回到 main 繼續執行


傳遞參數
- 一般都會使用暫存器作為存參數的地方
- 不會有 global local 的問題
USES 運算子
- 列出程序中使用到的暫存器,就會被系統保留起來
- 在程序開始之初產生push指令
- 將暫存器值儲存到堆疊中
- 程序結束時產生pop指令回復
CH6 Conditional Processing 條件處理
布林值運算
AND
- 兩運算元中每對相對應位元間的 and 布林運算,結果存放目的運算元
- 總是清除 OF 及 CF,根據目的運算元的值修改 SF、ZF、PF
OR
- 兩運算元中每對相對應位元間的 or 布林運算,結果存放目的運算元
- 總是清除 OF 及 CF,根據目的運算元的值修改 SF、ZF、PF
XOR
- 兩運算元中每對相對應位元間的 互斥 布林運算,結果存放目的運算元
- 總是清除 OF 及 CF,根據目的運算元的值修改 SF、ZF、PF
NOT
TEST
- 其實就是兩變數做 AND
- 但是結果不會存進目的變數裡面
- 不過結果會觸發 ZF(zero flag)
- 常被用於發現運算元個別的各個位元是否為 1
CMP
- 兩變數作比較,看誰大誰小
- 其實就是把兩個變數相減,目的變數減來源變數
- 結果不會存進目的變數裡面
-
與 flag 的互動
-
無號數
- 如果目的 > 來源 ZF 0, OF 0
- 如果目的 = 來源 ZF 1, OF 0
- 如果目的 < 來源 ZF 0, OF 1
-
有號數
- 如果目的 > 來源 SF OF
- 如果目的 = 來源 ZF 1
- 如果目的 < 來源 SF OF
清除或設定個別 CPU flag
Conditional Jmps 條件跳越
-
條件跳越指令的類型
- 特殊的 flag 值
- 兩運算元是否相等
- Unsigned 數字間的比較
- Sign 數字間的比較
-
一些幫助記憶的代碼
- JA 無號數的大於、JB 無號數的小於
- JG 有號數的大於、JL 有號數的小於
- E -> equal 等於
- N -> not 反向
-
特別的 jump
- JB、JC -> 如果 Carry flag 是 1 會跳到這個 label
- JE、JZ -> 如果 Zero flag 是 1 會跳到這個 label
- JS -> 如果 Sign flag 是 1 會跳到這個 label
- JNE、JNZ -> 如果 Zero flag 是 0 會跳到這個 label
- JECXZ -> 如果 ECX 是 0 會跳到這個 label
特殊的 flag 值
助憶符 |
說明 |
旗標 |
JZ |
若為零則跳 |
ZF = 1 |
JNZ |
若不為零則跳 |
ZF = 0 |
JC |
若進位則跳 |
CF = 1 |
JNC |
若不進位則跳 |
CF = 0 |
JO |
若溢位則跳 |
OF = 1 |
JNO |
若不溢位則跳 |
OF = 0 |
JS |
若負號則跳 |
SF = 1 |
JNS |
若非負號則跳 |
SF = 0 |
JP |
同位(偶)則跳 |
PF = 1 |
JNP |
非同位(偶)則跳 |
PF = 0 |
兩運算元是否相等
助憶符 |
說明 |
JE |
相等則跳 |
JNE |
不相等則跳 |
JCXZ |
若 CX = 0 則跳 |
JECXZ |
若 ECX = 0 則跳 |
Unsigned 數字間的比較
助憶符 |
說明 |
JA |
較大則跳 |
JNBE |
不是較小或相等則跳 (=JA) |
JAE |
較大或相等則跳 |
JNB |
不是較小則跳 (=JAE) |
JB |
較小則跳 |
JNAE |
不是較大或相等則跳 (=JB) |
JBE |
較小或相等則跳 |
JNA |
不是較大則跳 (=JBE) |
Sign 數字間的比較
助憶符 |
說明 |
JG |
較大則跳 |
JNLE |
不是較小或相等則跳 (=JG) |
JGE |
較大或相等則跳 |
JNL |
不是較小則跳 (=JGE) |
JL |
較小則跳 |
JNGE |
不是較大或相等則跳 (=JL) |
JLE |
較小或相等則跳 |
JNG |
不是較大則跳 (=JLE) |
一些例子
- 任務:如果 unsigned EAX 大於 EBX,就跳到標籤
- 任務:如果 signed EAX 大於 EBX,就跳到標籤
- 任務:如果 unsigned EAX 小於等於 Val1,就跳到標籤
- 任務:如果signed EAX 小於等於 Val1,就跳到標籤
- 任務:比較 EAX EBX,把比較大的那個存進 Large 變數裡
- 任務:如果 ESI 指向的 WORD 記憶體大小等於 0,就跳到標籤
- 任務:如果 ESI 指向的 DWORD 記憶體大小是偶數,就跳到標籤
- 任務:如果 AL 裡的第0, 1, 3位元都是 1 的話,就跳到標籤
位元測試指令
BT (Bit Test)
- 從一個暫存器中,把第 n 個 bit 複制到 Carry flag 去
- EX:如果 AX 中第 9 個 bit 是 1 就跳到 L1 去
條件迴圈指令
LOOPZ & LOOPE
- LOOPZ 與 LOOPE 相同
- LOOPZ (loop if zero)
- 允許迴圈繼續的條件
- 運行邏輯
- ecx = ecx - 1
- 若 ecx > 0 且 ZF = 1,則跳至目的
- 否則不進行跳越而繼續下一個指令
- 常用在檢查矩陣中的值是否等於一個給定的值
LOOPNZ & LOOPNE
- LOOPNZ & LOOPNE 相同
- LOOPNZ (loop if not zero)
- 允許迴圈繼續的條件
- 運行邏輯
- ecx = ecx - 1
- 若 ecx > 0 且 ZF = 0,則跳至目的
- 否則不進行跳越而繼續下一個指令
- 常用在檢查矩陣中的值是否不等於一個給定的值
EX:在矩陣中找第一個正數
條件結構 ( Conditional Structures )
If 結構
if 中有 and
if 中有 OR
while 結構
CH7 Integer Arithmetic 整數算數
logical shift 邏輯位移

Arithmetic Shifts 算術位移

- 在移動位元後
- 在新建立的位元位置,填入該數值原本的正負號位元
SHL (shift left)
- 對目標運算元執行向左邏輯移位,並且將最低位元填入 0。
SHR (shift right)
- 對目標運算元執行向右邏輯移位,並且將最高位元填入 0。
- SHR 可以執行 2 的次方數的高速除法
SAL 和 SAR

- SAL 和 SHL 功能一樣
- SAR 可以對其目標運算元,執行向右算數位移的運算
- 算術位移保持數字的符號
ROL 和 ROR
- ROL 將每個位元向左移位
- 最高位元會複製到 CF 以及 最低位元位置
- 沒有位元遺失

- ROR 將每個位元向右移位
- 最低位元會複製到 CF 以及 最高位元位置
- 沒有位元遺失

RCL 和 RCR
- RCL 將每個位元向左移
- CF 會複製到最小有效位元
- 最大位元複製到 CF 中

- RCR 將每個位元向右移
- CF 會複製到最大有效位元
- 最小位元複製到 CF 中

SHLD 和 SHRD
- SHLD 將運算元向左移位指定的位元數
- 移位過程中所產生的未定位元,填入來源運算元最大有效位元的位元值
- 來源運算元的值並不會受影響
- EX:
- 將 wval 左移 4 位元
- 將 AX 的最高四個位元,插入 wval 的最低 四個位元

- SHRD 將運算元向右移位指定的位元數
- 移位過程中所產生的未定位元,填入來源運算元最小有效位元的位元值
- 來源運算元的值並不會受影響
- EX:
- 將 wval 右移 4 位元
- 將 AX 的最小四個位元,插入 wval 的最高四個位元

一些例子
算算數
取日期

CH8 Advanced Procedures 程序
函式呼叫與 stack
-
使用 stack 來存取
- return 的位址
- 傳遞的參數
- 函式內的區域變數
-
什麼是 EBP
- EBP -> base pointer
- EBP 中存著在 stack 中的 return 位址
- EBP 的指標位址並不會改變
-
為什麼要 EBP
- 因為 ESP 的值會隨著 stack 的增加而變動
- 指到記憶體的位址會有所不同
- 這樣去存取變數很麻煩
- 所以設計一個 EBP 使它等於一開始的 ESP




-
stack 注意事項
- PUSH、POP 兩個指令
- ESP (stack pointer) EBP register 的搭配
- PUSH 會將 ESP 中的位址 - 4
-
如何實作一個 stack
- 呼叫一個函式
- 把參數 push 到 stack 裡
- 把 return 的位址 push 到 stack 裡
- 在函式裡把 EBP push 到 stack裡
- 把 EBP 等於 ESP
- 如果有需要使用到區域變數,要先將 ESP 往下移出一些空間出來

區域變數
如何定義一個區域變數
- 使用 LOCAL 自訂變數名稱
- 使用 stack 中的 EBP 直接指向記憶體
- EBP 減掉所有區域變數的大小
LOCAL
- 定義一個區域變數
- 直接接在 PROC 後面
- 需要給定大小
使用 EBP

- 第一個參數 -> EBP + 8
- 第二個參數 -> EBP + 12
- 第一個區域變數 -> EBP - 4
- 第二個區域變數 -> EBP - 8
.
.
.
- 函式的例子
CH9 Strings and Arrays
LOASx 與 STOSx
LOASx (Load)
- 把 ESI 指向的值存入 AL/AX/EAX
- EX:把一個陣列內的值印出來
STOSx (Store)
- 把在 AL/AX/EAX 中存入的值,放進 EDI 所指向的位置
- EX:把一個陣列放滿 0FFh
MOVSx
- 將記憶體中 ESI 指向的位置的值複製到 EDI 指向的位置
- EX:把一個陣列的值複製到另陣列中
CMPSx
- 將記憶體中比較 ESI 位置與 EDI 位置的值
- EX:如果 source > target,跳至 label L1,不然跳至 label L2
SCASx
- 將記憶體中比較 AL/AX/EAX 中存入的值與 EDI 位置的值
- EX:在一個陣列中找 "F" 這個字元
二維陣列

CH10 Structure and Macro
Structure 結構
-
給予邏輯相關變數的模板或模式。
-
巢狀結構
-
SIZE 與 LENGTH 皆為其所有變數的總和
- 宣告成結構陣列要特別小心,每次換到下一個陣列值的地址差距
-
定義方式
Union 聯集
- 與結構相似,但是內部所有變數共享一個記憶體位置
- SIZE 與 LENGTH 由變數中最大者決定
- 宣告方式和使用方式與Struture類似
MACRO 巨集指令
- MACRO 類似於指令函數,定義完後可以重複使用,組譯時會將所有MACRO全部取代成定義好的指令集
- 定義方式
Conditional-Assembly Directive 條件式組譯指引
IFB
- 若參數為空(呼叫時沒給),進入IFB'
- EXITM 後面可以加 <-1>(ture) 或 <0>(false)
IF, ELSE, ENDIF
IFIDN, IFIDNI
- 比較兩個給予的參數名稱,若相同 = true
- IFIDN 有大小寫分別、IFIDNI 沒有
Special Operators 特殊運算子
- Substitution (&) 用於代替參數成被MACRO定義的對象
-
Expansion (%) 用於將參數從常數改成文本提供MACRO輸入
- mGotoXY %( 5*10), %( 3+4) → mGotoXY 50, 7
-
Literal-Text (<>) 用於將多個文本參數組合成單一文本
-
Literal-Character (!) 用於將上述特殊運算子改成正常文本
Repeat Directive 迴圈指引
WHILE
REPEAT
FOR
- 迴圈會重複將引數代入參數後執行,然後重複至所有引數都被代入過
FORC
Reference
-
小信豬的原始部落
-
組語維基教科書
-
Xuite 筆記