這是匯編語言基礎最后一篇,以后還會更新更高級的匯編知識,並且這部分知識會應用到
逆向編程的環節,這一章介紹匯編基礎--機器指令。
一個16比特位的匯編指令:
opcode操作碼占用3個比特位,可以表示2的3次方為8種操作
寄存器占用2個比特位,可表示2的2次方為4種可用寄存器
地址空間為2的11次方為2048個可能的內存單元地址可用。
inc 指令和dec指令
從圖中可以看出這些指令長度僅為一字節,因為每個指令地址相差為1字節。
inc eax 這條指令的機器碼為40, 40為16進制表示,轉為二進制為 0100 0000
inc ecx 這條指令機器碼為41,41位16進制表示,轉換為二進制為0100 0001
依次展開會發現機器碼的規律,開頭都為01000XXX
而XXX實際就是寄存器在機器中表示的二進制機器碼。
下面是各個寄存器的機器碼:
下面看下dec 寄存器指令圖:
dec 寄存器指令的格式可以總結為
01001xxx, xxx為寄存器二進制指令格式。
mov 指令
mov eax, reg指令圖
每個指令長度為2字節,從地址偏移可以看出。
將上面指令機器碼轉為二進制
mov ecx, eax 十六進制為8B C8,二進制表示第二個字節 1100 1000
mov ecx, ecx 十六進制為 8B C9,二進制表示第二個字節為 1100 1001
通過對比二進制,發現第一個字節都為8B,第二個字節分別為 C0,C1,C2,C3...C9
mov reg, reg 指令格式為
10001011 11XXXYYY
XXX為目的寄存器, YYY為源寄存器
mov reg, imm 即將一個立即數移動到寄存器中指令的機器碼會是什么樣呢?
下圖為將立即數移動到寄存器的圖示:
mov eax, 1 指令機器碼為 B8 00000001
mov ecx, 10 指令機器碼為 B9 0000000A
兩條指令地址相差5個字節,每個字節8bit,可計算出每條指令為40bit長度。
即10個十六進制數表示。而 B8 00000001 和 B9 0000000A 恰巧為10個16進制數字組成。
機器碼B8 00000001 從左向右數,去掉B8占用的一個字節,剩下的四個字節可以看出用來表示
立即數 1。同樣的道理,可以看出 B9 0000000A也是這個原理。
如果移動的為負數,怎么表示呢?
mov edx, -1 這個指令我們分析一下 -1在機器中的表現形式
負數在機器中以補碼的形式表現,-1 的補碼計算規則為:
1的源碼為 0000 0000 0000 0000 0000 0000 0000 0001
按位取反 為 1111 1111 1111 1111 1111 1111 1111 1110
末尾+1 位 1111 1111 1111 1111 1111 1111 1111 1111
轉換為十六進制為 F F F F F F F F 恰好就是 機器碼的最后三個字節表示。
同樣的道理適用於mov ebx, -10
下面分析前兩個字節 BA, BB, B8, B9 分別有什么關聯。
同步對比可以看出 前幾個比特位是一樣的,都為 10111
后三個比特位分別為 000, 001, 010, 011,這四個二進制碼恰好為
幾個寄存器的二進制表示方法。所以
mov reg, imm 機器指令為
10111XXX YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY
mov reg, mem 將內存數據移動到寄存器中 的機器指令怎么表示?
mov mem, reg 將寄存器中的數據移動到內存中,機器指令如何表示?
下圖定義了變量num1, num2, num3
num1 地址為 0000 0000
num2 地址為 0000 0004
num3 地址為 0000 0008
下圖為指令對應的機器碼:
除去A1,A3開始的一個字節,剩下的4個字節分別為 十六進制4 和十六進制8,分別為num2的地址
和num3 的地址。
下面分析第一個字節A1和A3 規律:
可以得出結論,無論將內存數據移動到eax中,還是將eax中的數據移動到內存中,
最后的4個字節表示的都是內存的地址,第一個字節表示的不同,用來表示兩種移動方式的區別。
總結規律如下:
mov eax, mem 10100001 YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY
mov mem , eax 10100011 YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY
下面圖表表示了ebx,ecx,edx三種寄存器和內存數據移動指令
可以看出其他寄存器(eax, ecx, edx)和內存之間移動數據的操作指令大小為6字節,多出的為第二字節,
0D,15,1D。
第一字節8B表示從內存移動數據到寄存器,89表示從寄存器移動到內存,如下圖所示:
第二個字節圖表如下:
通過二進制可以看出 前兩位都為 00, 中間三位為 001 ,010, 011 分別表示ecx, edx, ebx
最后三位為101, 其實這個字節不僅僅用於表示移動,還可以表示很多操作,因為ecx為循環控制,
ebx為基址寄存器, edx可用於存余數等等,所以 前兩位為00,且最后三位為101,這個組合表示移動
操作。中間三位表示操作的寄存器是什么。
該字節概括為如下圖所示:
mod 字段為00, 且r/m字段值為101,它表示地址模式數據置換,也就是指向內存地址模式。
add 指令 sub 指令
add指令很簡單,給出圖表讀者自己分析。
字節轉為二進制可以看出從右往左數第3到1位為第二個操作的寄存器,
從右往左數第6到4位表示第一個操作的寄存器, 兩個寄存器操作模式為 第7~16位所表示。
movoffset 指令 lea 指令
num2 的值為5, 地址為 00000004, 分別將num2 的內容移動到esi和edi,再通過lea指令將num2地址
放入esi和edi
通過對比可以看出后四個字節都為 0000 0004 , 但是前兩個字節是不一樣的。將前兩個字節展開
為二進制
由於mov esi, num2 是將num2數據存入esi,而 lea esi, num2 是將num2地址放入esi,所以
第一個字節的倒數第二位不同,第一個字節分別為10001011 , 10001101,mov和lea第二個字節是相同的。
下面對比兩個lea指令前兩個字節 ,第一個字節是相同的,第二個字節為 00 110 101 和 00 111 101
第二個字節中間三位不同,分別為110(esi), 111(edi)表示寄存器。
所以可以總結一下, mov 指令和 lea指令區別在於第一個字節,計算機用第一個字節區別mov和lea指令。
計算機用第二個字節中間三位區別lea指令操作的不同寄存器。
第二個字節和我們上面說過的:
mod 字段為00, 且r/m字段值為101,它表示地址模式數據置換,也就是指向內存地址模式。
下面看一下 mov esi, offset mem 和 mov edi, offset mem兩條指令。
可以看出mov offset指令為5個字節,比 mov 和lea指令少了一個字節,因為mov offset僅僅在編譯的時候加載地址,
所以不需要lea的第二個字節表示 數據移動操作。 mov offset是靜態的。
mov offset 指令esi和 edi區別僅僅在第一個字節,展開后可以看到:
第一個字節的后三位 分別為 110(esi), 111(edi)。
可以得出結論mov offset 的指令第一個字節后三位區別esi還是edi,其余不變。
jmp指令
看一則jmp指令操作
jmp 指令 機器碼為EB + 相對偏移地址 ,
如 jmp around 為EB 04 ,通知計算機跳轉到當前指令指針位置+4字節的位置,
需要普及一個知識,當程序運行的時候,指令指針或者CPU中的指令指針指向下一條將要取到CPU中
被后續執行的指令。當運行到 jump around時,指令指針實際指向了 地址000000D8,指令 above:nop的位置,
EB 04指向 為 000 000 DB 加上4個字節地址即為 000 000 DC, 恰好是 around:nop指令地址。
符合邏輯。
下面看下 jump above指令會跳轉到哪里。
EB FC 指令 FC 為 1111 1100 , 該數值為某個負數的補碼,負數補碼的計算規則為
符號位不變,其他位按位取反末位+1。
同樣的道理,負數補碼轉為原碼,符號位不變,按位取反末位+1
1111 1100 符號位不變,按位取反 1000 0011, 末位+1,
變為 1000 0100 表示-4.
jmp above 機器指令 EB FC 跳轉到 指令指針地址向前移動四個字節的位置。
jmp above 指令運行時,指令指針指向下一條將要取出的指令位置,即 000 000 DC,
000 000 DC - 4 為 000 000 08,即 above:nop 的位置。
到此為止機器指令的知識介紹完畢,以后會介紹高級匯編和反匯編的知識,
匯編基礎介紹告一段落。
我的微信公眾號,謝謝關注。