5.11 匯編語言:仿寫IF條件語句
條件語句,也稱為IF-ELSE語句,是計算機編程中的一種基本控制結(jié)構(gòu)。它允許程序根據(jù)條件的真假來執(zhí)行不同的代碼塊。條件語句在處理決策和分支邏輯時非常有用。一般來說,條件語句由IF關(guān)鍵字、一個條件表達式、一個或多個代碼塊以及可選的ELSE關(guān)鍵字和對應(yīng)的代碼塊組成。條件表達式的結(jié)果通常是布爾值(True或False),決定了程序?qū)?zhí)行IF代碼塊還是ELSE代碼塊。
在匯編語言中,條件跳轉(zhuǎn)指令用于根據(jù)條件語句的結(jié)果在不同的代碼塊之間跳轉(zhuǎn),標簽用于標記代碼塊的入口點。通過運用標簽與跳轉(zhuǎn)即可構(gòu)建不同的條件語句,本章將以C語言中條件語句為基礎(chǔ),并使用匯編語言介紹如何實現(xiàn)它們,以讓讀者能更加深入的理解C語言與匯編語言之間的差異,幫助讀者更好的理解并運用匯編語言。
### 11.1 IF中AND語句構(gòu)造
如下所示代碼定義了3個整型變量var1、var2和var3,并檢查它們的值是否滿足一定的條件,條件包括var1大于等于20,var2小于等于100,var3等于50。如果這些條件都成立,則輸出字符串"xor eax,eax"。
AND運算符是邏輯運算符之一,用于連接兩個條件,當且僅當兩個條件都成立時,才會返回真值。在C語言中,AND運算符使用`&&`表示。針對and語句的執(zhí)行順序,如果等式兩邊只要有一邊返回假,則整個等式就不需要繼續(xù)下去了,只有等式1成立的情況下才會繼續(xù)判斷等式2是否成立,兩邊都成立則會執(zhí)行表達式內(nèi)部。
```C
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
? int var1 = 20;
? int var2 = 10;
? int var3 = 50;
? if (var1 >= 20 and var2 <= 100 and var3 == 50)
? {
? ? printf("xor eax,eax");
? }
? return 0;
}
```
對于多重and比較運算,編寫匯編語句時,應(yīng)注意判斷的轉(zhuǎn)換,如果高級語言中是大于等于,那么低級語言則可轉(zhuǎn)換為不大于則跳轉(zhuǎn),如果是小于等于,則對應(yīng)的匯編語句則可直接轉(zhuǎn)換為不小于則跳轉(zhuǎn),最后and語句必須三者全部一致,所以判斷條件只需要順序向下寫即可,當所有條件全部滿足則執(zhí)行內(nèi)部的xor指令,否則直接跳轉(zhuǎn)結(jié)束本次判斷。
```ASM
? .386p
? .model flat,stdcall
? option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
? var1 DWORD 20
? var2 DWORD 10
? var3 DWORD 50
? flag DWORD ?
.code
? main PROC
? ; if(var1 >= 20 and var2 <= 100 and var3 == 50)
? ? cmp dword ptr ds:[var1],20? ? ?; 判斷是否大于20
? ? jl L1? ? ? ? ? ? ? ? ? ? ? ? ? ; 不大于則跳轉(zhuǎn)
? ??
? ? cmp dword ptr ds:[var2],100? ? ; 判斷是否小于100
? ? jg L1? ? ? ? ? ? ? ? ? ? ? ? ? ; 不小于則跳轉(zhuǎn)
? ??
? ? cmp dword ptr ds:[var3],50? ? ?; 判斷是否等于50
? ? jne L1? ? ? ? ? ? ? ? ? ? ? ? ?; 不等于則跳轉(zhuǎn)
? ? mov dword ptr ds:[flag],1? ? ? ; 說明等式成立 flag=1
? ? jmp L2
? L1: mov dword ptr ds:[flag],0
? L2: cmp dword ptr ds:[flag],0
? ? je lop_end? ? ? ? ? ? ? ? ? ? ?; 為0則跳轉(zhuǎn),不為0則繼續(xù)執(zhí)行
? ??
? ? xor eax,eax? ? ? ? ? ? ? ? ? ? ; 此處是執(zhí)行if語句內(nèi)部
? ? xor ebx,ebx
? ? xor ecx,ecx
? ? jmp lop_end
? lop_end:
? ? nop? ? ? ? ? ? ? ? ? ? ? ? ? ? ; 直接結(jié)束
??
? ? invoke ExitProcess,0
? main ENDP
END main
```
### 11.2 IF中OR語句構(gòu)造
OR運算符的特點是,它表示兩個條件中只要有一個為真即可滿足整個語句的條件。在進行條件判斷時,如果其中一個子條件的結(jié)果為真,則整個表達式的后半部分將直接跳過,因為無論后半部分的條件是否成立,整個表達式已經(jīng)返回真值。這種行為稱為短路求值。
```C
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
? int var1 = 20;
? int var2 = 10;
? int var3 = 50;
? if (var1 > var2 || var2 <= var3)
? {
? ? printf("xor eax,eax");
? }
? else if(var3 == 50 || var2 > 10)
? {
? ? printf("xor ebx,ebx");
? }
? return 0;
}
```
此處由于表達式中使用了OR語句,該語句在比較時只需要兩個表達式一邊為假,則表達式后半部分會直接忽略判斷,所以在構(gòu)建判斷時,應(yīng)盡可能多的使用cmp語句對變量進行比較。
```ASM
? .386p
? .model flat,stdcall
? option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
? var1 DWORD 20
? var2 DWORD 10
? var3 DWORD 50
.code
? main PROC
? ; if (var1 > var2 || var2 <= var3)
? ? mov eax,dword ptr ds:[var1]
? ? cmp eax,dword ptr ds:[var2]? ? ?; var1 > var2
? ? jg L1
? ? mov eax,dword ptr ds:[var2]
? ? cmp eax,dword ptr ds:[var3]? ? ?; var2 <= var3
? ? jg L2? ? ? ? ? ? ? ? ? ? ? ? ? ?; 條件是 var2 > var3 則跳轉(zhuǎn)
? L1:
? ? xor eax,eax? ? ? ? ? ? ? ? ? ? ?; printf("xor eax,eax")
? ? jmp lop_end
? L2:
? ; else if(var3 == 50 || var2 > 10)
? ? cmp dword ptr ds:[var3],50
? ? je L3
? ? cmp dword ptr ds:[var2],10? ? ? ; var2 > 10
? ? jle lop_end
? L3:
? ? xor ebx,ebx? ? ? ? ? ? ? ? ? ? ?; printf("xor ebx,ebx")
? ? jmp lop_end
??
? lop_end:
? ? nop
? ? int 3
? ? invoke ExitProcess,0
? main ENDP
END main
```
### 11.3 IF中AND與OR構(gòu)造
在C語言中,AND和OR運算符可以混合使用,實現(xiàn)更加靈活的條件判斷。在混合使用時,需要注意運算符的優(yōu)先級和結(jié)合性。AND運算符的優(yōu)先級高于OR運算符,因此,在混合使用AND和OR運算符時,AND的運算會先于OR運算進行。將AND與OR語句混用,混用后其匯編形式與單獨使用差距并不明顯。
```C
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
? int var1 = 20;
? int var2 = 10;
? int var3 = 50;
? if ((var1 >= 10 && var2 <= 20) || (var2 == 10 && var3 >= 40))
? {
? ? printf("xor eax,eax");
? }
? else
? {
? ? printf("xor ebx,ebx");
? }
? return 0;
}
```
如上如果將And語句與Or語句連用,編譯器會首先判斷等式兩邊是否為常量,如果是常量且外部使用OR包裹,那么通常情況下會只保留等式左邊的表達式,等式右邊將會被優(yōu)化掉,而對于人的編寫邏輯則是依次作比較。
```ASM
? .386p
? .model flat,stdcall
? option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
? var1 DWORD 20
? var2 DWORD 10
? var3 DWORD 50
.code
? main PROC
? ; if ((var1 >= 10 && var2 <= 20) && (var2 == 10 || var3 >= 40))
? ? cmp dword ptr ds:[var1],10? ? ?; var1 >= 10
? ? jl L1
? ? cmp dword ptr ds:[var2],20? ? ?; var2 <= 20
? ? jg L1
? ??
? ? cmp dword ptr ds:[var2],10? ? ?; var2 == 10
? ? je L2
? ? cmp dword ptr ds:[var3],40? ? ?; var3 >= 40
? ? jl L1
? ? jmp L2
??
? L1:
? ? xor ebx,ebx? ? ? ? ? ? ? ?; else
? ? jmp lop_end
? L2:
? ? xor eax,eax? ? ? ? ? ? ? ? ; printf("xor eax,eax")
? ? jmp lop_end
? lop_end:
? ? int 3
? ? invoke ExitProcess,0
? main ENDP
END main
```
### 11.4 IF語句條件測試
這段C++代碼定義了6個整型變量,并檢查它們的值是否滿足多個條件。首先,它檢查var1是否大于等于var2且var2小于等于var3,并進入下一個if塊。接著,它檢查x是否等于100或y是否等于200或z是否等于300,并進入下一個if塊。最后,它檢查result是否等于1,如果是,則輸出字符串"xor eax,eax"。
條件測試語句通常情況下會使用`cmp`指令配合各種狀態(tài)跳轉(zhuǎn)實現(xiàn),此處我分別提供兩種仿寫方式,來看下編譯器與我們思維方式的異同。
```C
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
? int x = 100, y = 200, z = 300;
? int var1 = 20,var2 = 10,var3 = 50;
? int result = 1;
? if (var1 >= var2 && var2 <= var3)
? {
? ? if (x == 100 || y == 200 || z == 300)
? ? {
? ? ? if (result == 1)
? ? ? ? printf("xor eax,eax");
? ? }
? }
? return 0;
}
```
對于編譯器來說,生成的代碼要盡可能高效率,上方的C代碼如果是編譯器生成,則首先編譯器會比較外層循環(huán)中的AND語句,由于是AND語句此處無法優(yōu)化直接做兩次比較,接著進入內(nèi)層比較,依次流水線式執(zhí)行下來。
```ASM
? .386p
? .model flat,stdcall
? option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
? x DWORD 100
? y DWORD 200
? z DWORD 300
? var1 DWORD 20
? var2 DWORD 10
? var3 DWORD 50
? result DWORD 1
.code
? main PROC
? ? mov eax,dword ptr ds:[var1]
? ? cmp eax,dword ptr ds:[var2]? ? ? ; var1 >= var2
? ? jl lop_end
? ??
? ? mov eax,dword ptr ds:[var2]
? ? cmp eax,dword ptr ds:[var3]? ? ? ; var2 <= var3
? ? jg lop_end
? ??
? ? mov eax,dword ptr ds:[x]
? ? cmp eax,100? ? ? ? ? ? ? ? ?; x == 100
? ? jne lop_end
? ??
? ? mov eax,dword ptr ds:[y]
? ? cmp eax,200? ? ? ? ? ? ? ? ?; y == 200
? ? jne lop_end
? ??
? ? mov eax,dword ptr ds:[z]
? ? cmp eax,300? ? ? ? ? ? ? ? ?; z = 300
? ? jne lop_end
? ??
? ? mov eax,dword ptr ds:[result]
? ? test eax,eax? ? ? ? ? ? ? ? ?; eax = 0 ?
? ? jz lop_end
? ? xor eax,eax
? ? jmp lop_end
? ??
? lop_end:
? ? int 3
? ? invoke ExitProcess,0
? main ENDP
END main
```
以下是人的邏輯方式,這段代碼是本人以匯編小白視角編寫的一段代碼,代碼中首先比較外層IF語句由于是AND所以需要比較兩次,接著比較內(nèi)層判斷,內(nèi)層是OR語句,比較時可采用流水線式比較,最終如果比較通過則直接JE跳轉(zhuǎn)到語句內(nèi),否則直接跳轉(zhuǎn)到結(jié)尾。
```ASM
? .386p
? .model flat,stdcall
? option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
? x DWORD 100
? y DWORD 200
? z DWORD 300
? var1 DWORD 20
? var2 DWORD 10
? var3 DWORD 50
? result DWORD 1
.code
? main PROC
? ? mov eax,dword ptr ds:[var1]
? ? cmp eax,dword ptr ds:[var2]? ? ? ; var1 >= var2
? ? jge L1
? ? jmp lop_end
? L1:
? ? mov eax,dword ptr ds:[var2]? ? ? ; var2 <= var3
? ? cmp eax,dword ptr ds:[var3]? ? ??
? ? jle L2
? ? jmp lop_end
? L2:
? ? mov eax,dword ptr ds:[x]
? ? cmp eax,100? ? ? ? ? ? ? ? ? ? ? ?; x == 100 ?
? ? je L3
? ? mov eax,dword ptr ds:[y]? ? ? ? ? ; y == 200 ???
? ? cmp eax,200
? ? je L3
? ? mov eax,dword ptr ds:[y]
? ? cmp eax,300? ? ? ? ? ? ? ? ? ? ? ?; z == 300 ?
? ? je L3
? ? jmp lop_end
? L3:
? ? mov eax,dword ptr ds:[result]? ? ?; result == 1 ?
? ? test eax,eax? ? ? ? ? ? ? ? ? ? ? ; eax && eax != 0
? ? jz lop_end
? ? xor eax,eax
? ? jmp lop_end
? lop_end:
? ? int 3
? ? invoke ExitProcess,0
? main ENDP
END main
```
### 11.5 IF語句雙重嵌套
這段C++代碼定義了6個整型變量并檢查它們的值是否滿足多重條件。首先,它檢查var1是否大于等于var2,如果滿足,則進入下一個if塊。在下一個if塊中,它進一步檢查x<y且z>y是否成立,如果是,則輸出字符串"xor eax, eax",否則輸出字符串"xor ebx, ebx"。如果var1不大于等于var2,則它將檢查var2是否大于var3,如果是,則輸出字符串"xor ecx, ecx"。這段代碼實現(xiàn)了簡單的條件分支邏輯。
雙重IF嵌套語句其本質(zhì)就是連續(xù)作比較,在仿寫匯編指令時應(yīng)該由外到內(nèi)逐層解析,這樣才能寫出條例清晰的匯編指令。
```C
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
? int x = 100, y = 200, z = 300;
? int var1 = 20,var2 = 10,var3 = 50;
? if (var1 >= var2)
? {
? ? if ((x<y) && (z>y))
? ? {
? ? ? printf("xor eax,eax");
? ? }
? ? else
? ? {
? ? ? printf("xor ebx,ebx");
? ? }
? }
? else if (var2 > var3)
? {
? ? printf("xor ecx,ecx");
? }
? return 0;
}
```
如下匯編代碼,首先比較外層判斷`var1>=var2`如果不成立則`jl L1`跳轉(zhuǎn)到外層判斷的第二個分支判斷`var2 > var3`,如果成立則jl指令不生效,繼續(xù)判斷內(nèi)層IF語句,由于使用的是AND與運算,則需要順序判斷,判斷不通過直接`jle l2`,如果判斷通過則跳轉(zhuǎn)到`jle lop_end`不執(zhí)行,此時直接執(zhí)行`xor ecx,ecx`完成分支。
```ASM
? .386p
? .model flat,stdcall
? option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
? x DWORD 100
? y DWORD 200
? z DWORD 300
? var1 DWORD 20
? var2 DWORD 10
? var3 DWORD 50
.code
? main PROC
? ? mov eax,dword ptr ds:[var1]
? ? cmp eax,dword ptr ds:[var2]? ? ? ? ; if(var1 >= var2) ?
? ? jl L1
? ??
? ? mov eax,dword ptr ds:[x]
? ? cmp eax,dword ptr ds:[y]? ? ? ? ? ?; if((x<y)) ?
? ? jge L2
? ??
? ? mov eax,dword ptr ds:[z]? ? ? ? ? ?; if((z>y)) ?
? ? cmp eax,dword ptr ds:[y]
? ? jle L2
? ??
? ? xor eax,eax? ? ? ? ? ? ? ? ? ? ? ? ; printf("xor eax,eax")
? ? jmp lop_end
? L1:
? ? mov eax,dword ptr ds:[var2]
? ? cmp eax,dword ptr ds:[var3]? ? ? ; if(var2 > var3) ?
? ? jle lop_end
? ? xor ecx,ecx? ? ? ? ? ? ? ? ? ? ? ; printf("xor ecx,ecx")
? ? jmp lop_end
? L2:
? ? xor ebx,ebx? ? ? ? ? ? ? ? ? ? ? ; printf("xor ebx,ebx")
? ? jmp lop_end
? ??
? lop_end:
? ? int 3
? ? invoke ExitProcess,0
? main ENDP
END main
```
### 11.6 IF語句三層嵌套
這段C++代碼定義了6個整型變量并檢查它們的值是否滿足多個條件。首先,它檢查var1是否大于等于var2并且var2小于等于var3或者var3大于var1,如果滿足,則進入下一個if塊。在下一個if塊中,它檢查x是否為偶數(shù)或y是否為奇數(shù),如果滿足,則進一步檢查result是否等于1,如果是,則輸出字符串"xor eax, eax"。這段代碼實現(xiàn)了多個條件的邏輯判斷,并且包含了算術(shù)和邏輯運算。
三層嵌套IF語句,轉(zhuǎn)換為匯編語句稍微復(fù)雜一些,但大方向不變,還是要由外部到內(nèi)部,依次構(gòu)建每一個分支按照此順序構(gòu)建,其實并不難。
```C
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
? int x = 100, y = 200, z = 300;
? int var1 = 20,var2 = 10,var3 = 50;
? int result = 1;
? if ((var1 >= var2) && (var2 <= var3) || (var3 > var1))
? {
? ? if ((x % 2 == 0) || (y % 2 != 0))
? ? {
? ? ? if (result == 1)
? ? ? ? printf("xor eax,eax");
? ? }
? }
? return 0;
}
```
將以上代碼轉(zhuǎn)為匯編語句,首先判斷`(var1 >= var2) && (var2 <= var3)`此語句由于使用了AND所以需要判斷等式兩邊的結(jié)果,只要兩邊有一處不為真,就需要比較`(var3 > var1)`或運算結(jié)果,如果或運算為真則跳轉(zhuǎn)到`L1`標簽處,繼續(xù)執(zhí)行內(nèi)層IF比較語句。
```ASM
? ? .386p
? ? .model flat,stdcall
? ? option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
? ? x DWORD 100
? ? y DWORD 200
? ? var1 DWORD 20
? ? var2 DWORD 10
? ? var3 DWORD 50
? ? result DWORD 1
.code
? ? main PROC
? ??
? ? ? ? mov eax,dword ptr ds:[var1]
? ? ? ? cmp eax,dword ptr ds:[var2]? ? ? ?; and var1 >= var2
? ? ? ? jl L4
? ? ? ??
? ? ? ? mov eax,dword ptr ds:[var2]
? ? ? ? cmp eax,dword ptr ds:[var3]? ? ? ?; and var2 <= var3
? ? ? ? jle L1
? ? ? ??
? ? L4:
? ? ? ? mov eax,dword ptr ds:[var3]
? ? ? ? cmp eax,dword ptr ds:[var1]? ? ? ?; or var3 > var1
? ? ? ? jle lop_end
? ? L1:
? ? ? ? mov eax,dword ptr ds:[x]
? ? ? ? and eax,080000001h? ? ? ? ? ? ? ? ; eax = eax % 2 = 0
? ? ? ? jns L2? ? ? ? ? ? ? ? ? ? ? ? ? ? ; eax = 0 則跳轉(zhuǎn)
? ? ? ??
? ? ? ? dec eax
? ? ? ? or eax,0fffffffeh? ? ? ? ? ? ? ? ?; eax = eax % 2 != 0
? ? ? ? inc eax
? ? L2:
? ? ? ? mov eax,dword ptr ds:[result]
? ? ? ? test eax,eax? ? ? ? ? ? ? ? ? ? ? ; if(result == 1)
? ? ? ? jne L3
? ? ? ? jmp lop_end
? ? L3:
? ? ? ? xor eax,eax? ? ? ? ? ? ? ? ? ? ? ?; printf("xor eax,eax")
? ? ? ? jmp lop_end
? ? lop_end:
? ? ? ? int 3
? ? ? ? invoke ExitProcess,0
? ? main ENDP
END main
```
### 11.7 IF語句多選擇分支
這段C++代碼定義了3個整型變量并根據(jù)它們的值進行條件判斷。它檢查var1是否大于20,如果是,則輸出字符串"xor eax, eax"。如果var1不大于20,則它將檢查var2是否大于10,如果是,則輸出字符串"xor ebx, ebx"。如果var2不大于10,則它將檢查var2是否小于var3,如果是,則輸出字符串"xor ecx, ecx"。如果以上條件都不滿足,則輸出字符串"xor edx, edx"。這段代碼實現(xiàn)了簡單的條件分支和邏輯判斷。
多重選擇分支結(jié)構(gòu),其本質(zhì)就是對某些條件一直判斷下去,直到遇到符合條件的表達式則執(zhí)行表達式內(nèi)的語句塊。
```C
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
? int var1 = 20,var2 = 10,var3 = 50;
? if (var1 > 20)
? ? printf("xor eax,eax");
? else if (var2 > 10)
? ? printf("xor ebx,ebx");
? else if (var2 < var3)
? ? printf("xor ecx,ecx");
? else
? ? printf("xor edx,edx");
? return 0;
}
```
多重判斷語句編譯器生成匯編指令與我們?nèi)说乃季S習(xí)慣稍有些不同,對于我們自己的思維方式,總喜歡將判斷語句放置到匯編函數(shù)開頭部分,通過線性比較的方式分別比較不同的分支條件,每個分支條件將被鏈接到底部的特定語句塊上。
```ASM
? .386p
? .model flat,stdcall
? option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
? var1 DWORD 20
? var2 DWORD 10
? var3 DWORD 50
.code
? main PROC
??
? ? mov eax,dword ptr ds:[var1]
? ? cmp eax,20? ? ? ? ? ? ? ? ? ? ? ?; var1 > 20
? ? jg L1
? ? mov eax,dword ptr ds:[var2]
? ? cmp eax,10? ? ? ? ? ? ? ? ? ? ? ?; var2 > 10
? ? jg L2
? ? cmp eax,dword ptr ds:[var3]
? ? jl L3? ? ? ? ? ? ? ? ? ? ? ? ? ? ; var2 < var3
? ? xor edx,edx? ? ? ? ? ? ? ? ? ? ? ; printf("xor edx,edx")
? ? jmp lop_end
? L1:
? ? xor eax,eax? ? ? ? ? ? ? ? ? ? ? ; printf("xor eax,eax")
? ? jmp lop_end
? L2:
? ? xor ebx,ebx? ? ? ? ? ? ? ? ? ? ? ; printf("xor ebx,ebx")
? ? jmp lop_end
? L3:
? ? xor ecx,ecx? ? ? ? ? ? ? ? ? ? ? ; printf("xor ecx,ecx")
? ? jmp lop_end
? lop_end:
? ? int 3
? ? invoke ExitProcess,0
? main ENDP
END main
```
而編譯器為了盡可能優(yōu)化,寫出的代碼可能是以下這樣子的,編譯器并不會采取方便我們理解的方式來生成匯編指令集,而是對分支進行排序,通過順序依次向下執(zhí)行,如果條件跳轉(zhuǎn)不成立,則直接執(zhí)行緊隨跳轉(zhuǎn)其后的語句塊,當執(zhí)行結(jié)束后通過`jmp lop_end`統(tǒng)一跳轉(zhuǎn)到結(jié)束。
```ASM
? .386p
? .model flat,stdcall
? option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
? var1 DWORD 20
? var2 DWORD 10
? var3 DWORD 50
.code
? main PROC
? ? mov eax,dword ptr ds:[var1]
? ? cmp eax,20
? ? jle L1
? ? xor eax,eax? ? ? ? ? ? ? ? ; printf("xor eax,eax")
? ? jmp lop_end
? L1:
? ? mov eax,dword ptr ds:[var2]
? ? cmp eax,10
? ? jle L2
? ? xor ebx,ebx? ? ? ? ? ? ? ? ?; printf("xor ebx,ebx")
? ? jmp lop_end
? L2:
? ? mov eax,dword ptr ds:[var2]
? ? cmp eax,dword ptr ds:[var3]
? ? jge L3
? ? xor ecx,ecx? ? ? ? ? ? ? ? ? ; printf("xor ecx,ecx")??
? ? jmp lop_end
? L3:
? ? xor edx,edx? ? ? ? ? ? ? ? ? ; printf("xor edx,edx")
? ? jmp lop_end
? lop_end:
? ? int 3
? ? invoke ExitProcess,0
? main ENDP
END main
```
### 11.8 IF語句自增自減
執(zhí)行自增自減運算需要找一個臨時區(qū)域來存放自增后的數(shù)據(jù),所以首先要開辟局部空間,多數(shù)情況下開辟空間可在棧上,例如使用`sub esp,12`來分配??臻g,并初始化后即可使用,最后需要將該空間恢復(fù)。
```ASM
? .386p
? .model flat,stdcall
? option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.code
? main PROC
? ? push ebp
? ? mov ebp,esp
? ? sub esp,12? ? ? ? ? ? ? ? ? ? ; 開辟 3*4 =12 的空間
? ??
? ? lea edi,dword ptr ss:[ebp-12] ; 指向棧中基址
? ? mov ecx,3? ? ? ? ? ? ? ? ? ? ?; 填充次數(shù) 12/4 = 3?
? ? mov eax,0cccccccch? ? ? ? ? ? ; 填充物
? ? rep stosd? ? ? ? ? ? ? ? ? ? ?; 初始化開始
? ? mov dword ptr ss:[ebp-12],1
? ? mov dword ptr ss:[ebp-8],2? ? ; 給每個地址賦值
? ? mov dword ptr ss:[ebp-4],3
? ??
? ? mov eax,dword ptr ss:[ebp-12] ; 取第一個數(shù)據(jù)1
? ? mov ebx,dword ptr ss:[ebp-4]? ; 取第二個數(shù)據(jù)3
? ? add eax,ebx? ? ? ? ? ? ? ? ? ?; 執(zhí)行遞增
? ? mov dword ptr ss:[ebp-8],eax? ; 寫回棧
? ??
? ? add esp,12? ? ? ? ? ? ? ? ? ? ?; 平棧
? ? mov esp,ebp
? ? pop ebp
??
? ? invoke ExitProcess,0
? main ENDP
END main
```
首先我們先來編寫一段簡單的C代碼片段,如下代碼中我們使用了兩種自增符號,一種是`var1++`另一種是`++var2`兩種方式的匯編版本并不一致,仿寫是需要格外注意。
```C
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
? int var1 = 20,var2 = 10,var3 = 50;
? if (var1++ >= 20 && ++var2 > 10)
? {
? ? printf("xor eax,eax");
? }
? return 0;
}
```
以下匯編代碼中需要注意,當我們使用`var1++`時程序是將`++后`的結(jié)果賦值到了棧中存放,并讓`var1`變量遞增,而判斷則使用的是棧中的原值,相反`++var1`則是在原值上直接進行操作,將操作結(jié)果賦值給原值后在進行判斷。
```ASM
? .386p
? .model flat,stdcall
? option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
? var1 DWORD 20
? var2 DWORD 10
? var3 DWORD 50
.code
? main PROC
? ? push ebp
? ? mov ebp,esp
? ? sub esp,8? ? ? ? ? ? ? ? ? ? ?; 開辟 2*4 =8 的空間
? ??
? ? lea edi,dword ptr ss:[ebp-8]? ; 指向棧中基址
? ? mov ecx,2? ? ? ? ? ? ? ? ? ? ?; 填充次數(shù) 8/4 = 2
? ? mov eax,0cccccccch? ? ? ? ? ? ; 填充物
? ? rep stosd? ? ? ? ? ? ? ? ? ? ?; 初始化開始
? ? mov eax,dword ptr ds:[var1]
? ? mov dword ptr ss:[ebp-8],eax? ?; 將var1存入臨時變量中
? ? add eax,1
? ? mov dword ptr ds:[var1],eax? ? ; 將相加后的結(jié)果寫回到var1
? ??
? ? cmp dword ptr ss:[ebp-8],20? ? ; 用原值與20對比
? ? jl L1
? ? mov dword ptr ss:[ebp-4],1? ? ?; 局部變量存放標志=1
? ? jmp L2
??
? L1: mov dword ptr ss:[ebp-4],0
? L2: cmp dword ptr ss:[ebp-4],0
? ? je lop_end
? ? mov eax,dword ptr ds:[var2]? ? ; 繼續(xù)執(zhí)行 ++var2
? ? add eax,1
? ? mov dword ptr ds:[var2],eax
? ? cmp dword ptr ds:[var2],10? ? ?; var2 > 10
? ? jle lop_end
? ??
? ? xor eax,eax? ? ? ? ? ? ? ? ? ? ; printf("xor eax,eax")
? lop_end:
? ? add esp,8? ? ? ? ? ? ? ? ? ? ?; 平棧
? ? mov esp,ebp
? ? pop ebp
??
? ? invoke ExitProcess,0
? main ENDP
END main
```
### 11.9 IF語句三目運算符
C語言中提供了快捷判斷語句,唯一的三目運算符,該運算符其實就是壓縮版的`IF-ELSE`結(jié)構(gòu),其表達式與IF基本一致,但在AND運算符的影響下會與`IF-ELSE`結(jié)構(gòu)有些許的不同。
```C
#include <stdio.h>
#include <Windows.h>
int main(int argc,char *argv[])
{
? int var1 = 20, var2 = 10, var3 = 50;
? if ((var1 > var2 ? 1 : 0) && (var2 <= var3 ? 1 : 0))
? {
? ? printf("xor eax,eax");
? }
? return 0;
}
```
在仿寫這段C代碼的匯編版時,我們首先要注意他是一個AND比較操作,兩側(cè)必須同時為1才可,因為這個特性的存在,在編寫匯編代碼時,可以增加一個`flag`標志位,通過對該標志位的流水線判斷實現(xiàn)三目運算比較。
```ASM
? ? .386p
? ? .model flat,stdcall
? ? option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
? ? var1 DWORD 20
? ? var2 DWORD 10
? ? var3 DWORD 50
? ? flag DWORD ?
.code
? ? main PROC
? ? ? ? mov eax,dword ptr ds:[var1]
? ? ? ? cmp eax,dword ptr ds:[var2]? ?; var1 > var2 ?
? ? ? ? jle L1
? ? ? ? mov dword ptr ds:[flag],1? ? ?; 表達式1成立
? ? ? ? jmp L2
? ? L1: mov dword ptr ds:[flag],0
? ? L2: cmp dword ptr ds:[flag],0
? ? ? ? je lop_end
? ? ? ??
? ? ? ? mov eax,dword ptr ds:[var2]
? ? ? ? cmp eax,dword ptr ds:[var3]? ?; var2 <= var3
? ? ? ? jg L3
? ? ? ? mov dword ptr ds:[flag],1? ? ?; 表達式2成立
? ? ? ? jmp L4
? ? ? ??
? ? L3: mov dword ptr ds:[flag],0
? ? L4: cmp dword ptr ds:[flag],0
? ? ? ? je lop_end
? ? ? ??
? ? ? ? xor eax,eax? ? ? ? ? ? ? ? ? ?; printf("xor eax,eax")
? ? ? ? jmp lop_end
? ? ? ??
? ? lop_end:
? ? ? ? int 3
? ? ? ??
? ? ? ? invoke ExitProcess,0
? ? main ENDP
END main
```
### 11.10 IF語句嵌套移位
這段C++代碼定義了兩個函數(shù)func_a和func_b,它們分別包含了條件判斷和邏輯運算。在函數(shù)func_a中,它首先對三個整型變量進行了位運算,然后通過邏輯或連接這些運算結(jié)果,進入下一個if塊。在這個if塊中,它再次進行多個邏輯判斷和比較,判斷條件包括被位運算處理過的變量值和固定的數(shù)值50。如果所有條件都滿足,則輸出字符串"xor eax, eax"。在函數(shù)func_b中,它通過取模和位運算對三個整型變量進行處理,并進入下一個if塊。在if塊內(nèi),它進行了大于比較,并輸出字符串"xor ebx, ebx"。這段代碼實現(xiàn)了對多個變量的復(fù)雜運算和邏輯判斷。
```C
#include <stdio.h>
#include <windows.h>
int func_a()
{
? ? int var1 = 20,var2 = 10,var3 = 50;
? ? if (((var1 << 2) ^ (var2 << 3)) || ((var2 << 1) ^ (var3 << 3)))
? ? {
? ? ? ? if ((var1 >= var2) || (var2 <= var3) && (var3 == 50))
? ? ? ? {
? ? ? ? ? ? printf("xor eax,eax");
? ? ? ? }
? ? }
? ? return 0;
}
int func_b()
{
? ? int var1 = 20,var2 = 10,var3 = 50;
? ? if (((var1 << 2) % 2) || (var3 >> 1) % 3)
? ? {
? ? ? ? if (((var1 << 2) + 10) > 50)
? ? ? ? {
? ? ? ? ? ? printf("xor ebx,ebx");
? ? ? ? }
? ? }
? ? return 0;
}
```
先來看第一個`func_a()`函數(shù)如何進行仿寫,首先`(((var1 << 2) ^ (var2 << 3)) || ((var2 << 1) ^ (var3 << 3)))`外部嵌套是一個OR運算,按照順序先拆分。
?- 執(zhí)行`((var1 << 2) ^ (var2 << 3))`先將數(shù)據(jù)`shl`左移,移動后將兩邊數(shù)據(jù)進行`xor`異或,如果為0則比較等式2。
?- 執(zhí)行`((var2 << 1) ^ (var3 << 3)))`比較等式2,如果為真,則繼續(xù)執(zhí)行內(nèi)層的移位與相加運算,為假跳轉(zhuǎn)到結(jié)束。
```ASM
? .386p
? .model flat,stdcall
? option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
? var1 DWORD 20
? var2 DWORD 10
? var3 DWORD 50
.code
? main PROC
? ? ; ((var1 << 2) ^ (var2 << 3))
? ? mov eax,dword ptr ds:[var1]
? ? shl eax,2
? ? mov ecx,dword ptr ds:[var2]
? ? shl ecx,3
? ? xor eax,ecx
? ? je L1
? ??
? ? ; ((var2 << 1) ^ (var3 << 3))
? ? mov eax,dword ptr ds:[var2]
? ? shl eax,1
? ? mov eax,dword ptr ds:[var3]
? ? shl ecx,3
? ? xor eax,ecx
? ? je lop_end
? ??
? ? ; (var1 >= var2)
? L1: mov eax,dword ptr ds:[var1]
? ? cmp eax,dword ptr ds:[var2]
? ? jge L2
? ??
? ? ; (var2 <= var3)
? ? mov eax,dword ptr ds:[var2]
? ? cmp eax,dword ptr ds:[var3]
? ? jg lop_end
? L2:?
? ? ; (var3 == 50)
? ? cmp dword ptr ds:[var3],50
? ? jnz lop_end
??
? ? xor eax,eax? ? ? ? ? ? ? ?; printf("xor eax,eax")
? ? jmp lop_end
??
? lop_end:
? ? int 3
? ? invoke ExitProcess,0
? main ENDP
END main
```
第二個函數(shù)`func_b()`與第一個基本一致,我們只需要將等式進行拆分,拆分后按照括號優(yōu)先級依次進行仿寫并增加跳轉(zhuǎn)指令即可。
```ASM
? .386p
? .model flat,stdcall
? option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
? var1 DWORD 20
? var2 DWORD 10
? var3 DWORD 50
.code
? main PROC
? ? ; ((var1 << 2) % 2)
? ? mov eax,dword ptr ds:[var1]
? ? shl eax,2
? ? and eax,080000001h? ? ? ? ? ; var1 % 2
? ? jns L2? ? ? ? ? ? ? ? ? ? ? ; 非負數(shù)則跳轉(zhuǎn)
??
? ? ; (var3 >> 1) % 3? ? ? ? ? ?; 為負數(shù)執(zhí)行第二個表達式
? L1: mov eax,dword ptr ds:[var3]
? ? sar eax,1? ? ? ? ? ? ? ? ? ?; var3 >> 1
? ? cdq? ? ? ? ? ? ? ? ? ? ? ? ?; 擴展為8字節(jié)
? ? mov ecx,3? ? ? ? ? ? ? ? ? ?; 除以3
? ? idiv ecx
? ? test edx,edx? ? ? ? ? ? ? ? ; 比較余數(shù)是否為0
? ? je lop_end
? ? ; ((var1 << 2) + 10) > 50
? L2: mov eax,dword ptr ds:[var1]
? ? shl eax,2
? ? add eax,10
? ? cmp eax,50
? ? jle lop_end
? ??
? ? xor eax,eax? ? ? ? ? ? ? ? ? ; printf("xor ebx,ebx")
? ? jmp lop_end
? lop_end:
? ? int 3
? ? invoke ExitProcess,0
? main ENDP
END main
```
### 11.11 IF語句運算符混合
如果將多種運算符混合在一起,那么我們在仿寫匯編代碼是可能會麻煩一些,尤其是涉及到多種比較與運算時,我們以計算平年閏年為例,看看該如何實現(xiàn)復(fù)雜運算符的仿寫。
?- 首先閏年時年份對400取余等于0的數(shù),或者對4取余等于0并且對100取余不等于0的數(shù)。
```C
#include <windows.h>
#include <stdio.h>
int main(int argc,char * argv[])
{
? int year = 2017;
? if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0))
? {
? ? printf("%d 閏年 \n", year);
? }
? {
? ? printf("%d 平年 \n", year);
? }
? return 0;
}
```
老樣子,按照以前的步驟,先對等式拆分,拆分后依次實現(xiàn)每一個等式,最后將這些等式通過判斷語句串聯(lián)起來即可,這段代碼除使用了`idiv`除法指令外,其他地方與如上內(nèi)容保持一致。
```ASM
? .386p
? .model flat,stdcall
? option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
? Year DWORD 2017
? szFmtR BYTE '%d 是閏年',0dh,0ah,0
? szFmtP BYTE '%d 是平年',0dh,0ah,0
.code
? main PROC
? ??
? ? mov eax,dword ptr ds:[Year]? ? ?; year = 2017;
? ? cdq
? ? mov ecx,400
? ? idiv ecx? ? ? ? ? ? ? ? ? ? ? ? ; year % 400 == 0
? ? test edx,edx
? ? je L1
? ??
? ? mov eax,dword ptr ds:[Year]
? ? and eax,080000003h? ? ? ? ? ? ? ; year % 4
? ? test eax,eax
? ? jne L2
? ??
? ? mov eax,dword ptr ds:[Year]
? ? cdq
? ? mov ecx,100
? ? idiv ecx? ? ? ? ? ? ? ? ? ? ? ? ?; year % 100 != 0
? ? test edx,edx? ? ? ? ? ? ? ? ? ? ?; 比較余數(shù)
? ? je L2? ? ? ? ? ? ? ? ? ? ? ? ? ? ; 跳轉(zhuǎn)則是平年
? ??
? L1: mov eax,dword ptr ds:[Year]
? ? invoke crt_printf,addr szFmtR,eax? ? ?; 是閏年
? ? jmp lop_end
? L2: mov eax,dword ptr ds:[Year]
? ? invoke crt_printf,addr szFmtP,eax? ? ?; 是平年
? ? jmp lop_end?
? lop_end:
? ? int 3?
? main ENDP
END main
```
本文作者: 王瑞
本文鏈接: https://www.lyshark.com/post/3cc3d473.html
版權(quán)聲明: 本博客所有文章除特別聲明外,均采用 BY-NC-SA 許可協(xié)議。轉(zhuǎn)載請注明出處!