条件语句,也称为IF-ELSE语句,是计算机编程中的一种基本控制结构。它允许程序根据条件的真假来执行不同的代码块。条件语句在处理决策和分支逻辑时非常有用。一般来说,条件语句由IF关键字、一个条件表达式、一个或多个代码块以及可选的ELSE关键字和对应的代码块组成。条件表达式的结果通常是布尔值(True或False),决定了程序将执行IF代码块还是ELSE代码块。

在汇编语言中,条件跳转指令用于根据条件语句的结果在不同的代码块之间跳转,标签用于标记代码块的入口点。通过运用标签与跳转即可构建不同的条件语句,本章将以C语言中条件语句为基础,并使用汇编语言介绍如何实现它们,以让读者能更加深入的理解C语言与汇编语言之间的差异,帮助读者更好的理解并运用汇编语言。

11.1 IF中AND语句构造

如下所示代码定义了3个整型变量var1、var2和var3,并检查它们的值是否满足一定的条件,条件包括var1大于等于20,var2小于等于100,var3等于50。如果这些条件都成立,则输出字符串"xor eax,eax"。

AND运算符是逻辑运算符之一,用于连接两个条件,当且仅当两个条件都成立时,才会返回真值。在C语言中,AND运算符使用&&表示。针对and语句的执行顺序,如果等式两边只要有一边返回假,则整个等式就不需要继续下去了,只有等式1成立的情况下才会继续判断等式2是否成立,两边都成立则会执行表达式内部。

#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比较运算,编写汇编语句时,应注意判断的转换,如果高级语言中是大于等于,那么低级语言则可转换为不大于则跳转,如果是小于等于,则对应的汇编语句则可直接转换为不小于则跳转,最后and语句必须三者全部一致,所以判断条件只需要顺序向下写即可,当所有条件全部满足则执行内部的xor指令,否则直接跳转结束本次判断。

  .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 ; 不大于则跳转 cmp dword ptr ds:[var2],100 ; 判断是否小于100
jg L1 ; 不小于则跳转 cmp dword ptr ds:[var3],50 ; 判断是否等于50
jne L1 ; 不等于则跳转 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则跳转,不为0则继续执行 xor eax,eax ; 此处是执行if语句内部
xor ebx,ebx
xor ecx,ecx
jmp lop_end lop_end:
nop ; 直接结束 invoke ExitProcess,0
main ENDP
END main

11.2 IF中OR语句构造

OR运算符的特点是,它表示两个条件中只要有一个为真即可满足整个语句的条件。在进行条件判断时,如果其中一个子条件的结果为真,则整个表达式的后半部分将直接跳过,因为无论后半部分的条件是否成立,整个表达式已经返回真值。这种行为称为短路求值。

#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语句,该语句在比较时只需要两个表达式一边为假,则表达式后半部分会直接忽略判断,所以在构建判断时,应尽可能多的使用cmp语句对变量进行比较。

  .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 则跳转
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构造

在C语言中,AND和OR运算符可以混合使用,实现更加灵活的条件判断。在混合使用时,需要注意运算符的优先级和结合性。AND运算符的优先级高于OR运算符,因此,在混合使用AND和OR运算符时,AND的运算会先于OR运算进行。将AND与OR语句混用,混用后其汇编形式与单独使用差距并不明显。

#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包裹,那么通常情况下会只保留等式左边的表达式,等式右边将会被优化掉,而对于人的编写逻辑则是依次作比较。

  .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指令配合各种状态跳转实现,此处我分别提供两种仿写方式,来看下编译器与我们思维方式的异同。

#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代码如果是编译器生成,则首先编译器会比较外层循环中的AND语句,由于是AND语句此处无法优化直接做两次比较,接着进入内层比较,依次流水线式执行下来。

  .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所以需要比较两次,接着比较内层判断,内层是OR语句,比较时可采用流水线式比较,最终如果比较通过则直接JE跳转到语句内,否则直接跳转到结尾。

  .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"。这段代码实现了简单的条件分支逻辑。

双重IF嵌套语句其本质就是连续作比较,在仿写汇编指令时应该由外到内逐层解析,这样才能写出条例清晰的汇编指令。

#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跳转到外层判断的第二个分支判断var2 > var3,如果成立则jl指令不生效,继续判断内层IF语句,由于使用的是AND与运算,则需要顺序判断,判断不通过直接jle l2,如果判断通过则跳转到jle lop_end不执行,此时直接执行xor ecx,ecx完成分支。

  .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是否为偶数或y是否为奇数,如果满足,则进一步检查result是否等于1,如果是,则输出字符串"xor eax, eax"。这段代码实现了多个条件的逻辑判断,并且包含了算术和逻辑运算。

三层嵌套IF语句,转换为汇编语句稍微复杂一些,但大方向不变,还是要由外部到内部,依次构建每一个分支按照此顺序构建,其实并不难。

#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;
}

将以上代码转为汇编语句,首先判断(var1 >= var2) && (var2 <= var3)此语句由于使用了AND所以需要判断等式两边的结果,只要两边有一处不为真,就需要比较(var3 > var1)或运算结果,如果或运算为真则跳转到L1标签处,继续执行内层IF比较语句。

    .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 则跳转 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个整型变量并根据它们的值进行条件判断。它检查var1是否大于20,如果是,则输出字符串"xor eax, eax"。如果var1不大于20,则它将检查var2是否大于10,如果是,则输出字符串"xor ebx, ebx"。如果var2不大于10,则它将检查var2是否小于var3,如果是,则输出字符串"xor ecx, ecx"。如果以上条件都不满足,则输出字符串"xor edx, edx"。这段代码实现了简单的条件分支和逻辑判断。

多重选择分支结构,其本质就是对某些条件一直判断下去,直到遇到符合条件的表达式则执行表达式内的语句块。

#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;
}

多重判断语句编译器生成汇编指令与我们人的思维习惯稍有些不同,对于我们自己的思维方式,总喜欢将判断语句放置到汇编函数开头部分,通过线性比较的方式分别比较不同的分支条件,每个分支条件将被链接到底部的特定语句块上。

  .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

而编译器为了尽可能优化,写出的代码可能是以下这样子的,编译器并不会采取方便我们理解的方式来生成汇编指令集,而是对分支进行排序,通过顺序依次向下执行,如果条件跳转不成立,则直接执行紧随跳转其后的语句块,当执行结束后通过jmp lop_end统一跳转到结束。

  .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语句自增自减

执行自增自减运算需要找一个临时区域来存放自增后的数据,所以首先要开辟局部空间,多数情况下开辟空间可在栈上,例如使用sub esp,12来分配栈空间,并初始化后即可使用,最后需要将该空间恢复。

  .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 ; 填充次数 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] ; 取第一个数据1
mov ebx,dword ptr ss:[ebp-4] ; 取第二个数据3
add eax,ebx ; 执行递增
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两种方式的汇编版本并不一致,仿写是需要格外注意。

#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++时程序是将++后的结果赋值到了栈中存放,并让var1变量递增,而判断则使用的是栈中的原值,相反++var1则是在原值上直接进行操作,将操作结果赋值给原值后在进行判断。

  .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 ; 填充次数 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 ; 将相加后的结果写回到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] ; 继续执行 ++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结构,其表达式与IF基本一致,但在AND运算符的影响下会与IF-ELSE结构有些许的不同。

#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比较操作,两侧必须同时为1才可,因为这个特性的存在,在编写汇编代码时,可以增加一个flag标志位,通过对该标志位的流水线判断实现三目运算比较。

    .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++代码定义了两个函数func_a和func_b,它们分别包含了条件判断和逻辑运算。在函数func_a中,它首先对三个整型变量进行了位运算,然后通过逻辑或连接这些运算结果,进入下一个if块。在这个if块中,它再次进行多个逻辑判断和比较,判断条件包括被位运算处理过的变量值和固定的数值50。如果所有条件都满足,则输出字符串"xor eax, eax"。在函数func_b中,它通过取模和位运算对三个整型变量进行处理,并进入下一个if块。在if块内,它进行了大于比较,并输出字符串"xor ebx, ebx"。这段代码实现了对多个变量的复杂运算和逻辑判断。

#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()函数如何进行仿写,首先(((var1 << 2) ^ (var2 << 3)) || ((var2 << 1) ^ (var3 << 3)))外部嵌套是一个OR运算,按照顺序先拆分。

  • 执行((var1 << 2) ^ (var2 << 3))先将数据shl左移,移动后将两边数据进行xor异或,如果为0则比较等式2。
  • 执行((var2 << 1) ^ (var3 << 3)))比较等式2,如果为真,则继续执行内层的移位与相加运算,为假跳转到结束。
  .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

第二个函数func_b()与第一个基本一致,我们只需要将等式进行拆分,拆分后按照括号优先级依次进行仿写并增加跳转指令即可。

  .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 ; 非负数则跳转 ; (var3 >> 1) % 3 ; 为负数执行第二个表达式
L1: mov eax,dword ptr ds:[var3]
sar eax,1 ; var3 >> 1
cdq ; 扩展为8字节
mov ecx,3 ; 除以3
idiv ecx
test edx,edx ; 比较余数是否为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语句运算符混合

如果将多种运算符混合在一起,那么我们在仿写汇编代码是可能会麻烦一些,尤其是涉及到多种比较与运算时,我们以计算平年闰年为例,看看该如何实现复杂运算符的仿写。

  • 首先闰年时年份对400取余等于0的数,或者对4取余等于0并且对100取余不等于0的数。
#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;
}

老样子,按照以前的步骤,先对等式拆分,拆分后依次实现每一个等式,最后将这些等式通过判断语句串联起来即可,这段代码除使用了idiv除法指令外,其他地方与如上内容保持一致。

  .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 ; 比较余数
je L2 ; 跳转则是平年 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

5.11 汇编语言:仿写IF条件语句的更多相关文章

  1. 转>>在同一个sql语句中如何写不同条件的count数量

    今天在做Portal中的Dashboard展现的时候,需要对多个统计字段做展现,根据我现在的掌握水平,我只能在sql调用构建器中实现一种sql语 句返回的resultSet做展现.没有办法,只能从数据 ...

  2. 【译】写好JavaScript条件语句的5个技巧

    译文 当我们写JavaScript代码时,经常会用到到条件判断处理,这里有5个技巧能使你写出更好.更简洁的条件语句. 1.使用Array.includes处理多种条件 让我们来看一下的例子: // c ...

  3. 写出更好的 JavaScript 条件语句

    1. 使用 Array.includes 来处理多重条件 // 条件语句 function test(fruit) { if (fruit == 'apple' || fruit == 'strawb ...

  4. 2018年11月10日 input,print,pass 用法,条件语句+字符串

    name=input('请输入用户名')#永远等待用户输入 password=input('请输入密码') print(name) print(password) 变量定义的规则: 变量名只能是 字母 ...

  5. 在用 JavaScript 工作时,我们经常和条件语句打交道,这里有5条让你写出更好/干净的条件语句的建议。

    1.多重判断时使用 Array.includes 2.更少的嵌套,尽早 return 3.使用默认参数和解构 4.倾向于遍历对象而不是 Switch 语句 5.对 所有/部分 判断使用 Array.e ...

  6. 第一章:python基础语法| 字符编码| 条件语句...

    1.编程语言介绍 编程就是写代码,让计算机帮你做事情.计算机底层是电路,只认识二进制0和1.机器语言&汇编语言语言进化历史:机器.汇编.高级.机器语言只接受二进制代码:汇编语言是采用英文缩写的 ...

  7. SwiftUI - 一起来仿写微信APP之一首页列表视图

    简介 最近在学习 SwiftUI ,我一般都是先去学习界面布局,所以就想着仿写一下经常使用的软件的界面,所以先拿微信开刀.因为不想一次性发太多的内容,所以只好将主题分解,一部分一部分地去讲,接下来我们 ...

  8. 前端练手小项目——网页版qq音乐仿写

    qq音乐网页版仿写 一些步骤与注意事项 一开始肯定就是html+css布局和页面了,这段特别耗时间,耐心写完就好了 首先要说一下大致流程: 一定要先布局html!,所以一定要先分析页面布局情况,用不同 ...

  9. SQL SERVER全面优化-------写出好语句是习惯

    前几篇文章已经从整体提供了诊断数据库的各个方面问题的基本思路...也许对你很有用,也许你觉得离自己太远.那么今天我们从语句的一些优化写法及一些简单优化方法做一个介绍.这对于很多开发人员来说还是很有用的 ...

  10. Pyhton编程(二)之变量、用户输入及条件语句

    一:变量 变量定义的规则 1)只能由数字.字母.下划线组成(不能以数字开头) 2)不能使用关键字作为变量名 ['and', 'as', 'assert', 'break', 'class', 'con ...

随机推荐

  1. Docker--简介&&安装

    Docker 是一种应用容器引擎 一 容器 Linux系统提供了Namespace和Cgroup技术实现环境隔离和资源控制 其中Namespace是Linux提供的一种内核级别环境隔离的方法,能使一个 ...

  2. ABAP 辨析 标准表|排序表|哈希表

    1.文档介绍 本文档将介绍内表的区别和用法,涉及标准表.排序表.哈希表 2.用法与区别 2.1.内表种类 内表顶层为任意表,任意表分为索引表和哈希表,索引表又可分为标准表和排序表,结构如图: 2.2. ...

  3. 2018年第九届 蓝桥杯C组 C/C++决赛题解

    蓝桥杯历年国赛真题汇总:Here 1.年龄问题 s夫人一向很神秘.这会儿有人问起她的年龄,她想了想说: "20年前,我丈夫的年龄刚好是我的2倍,而现在他的年龄刚好是我的1.5倍". ...

  4. 基于HTML,CSS & Javascript 实现图像的自动轮播和手动导航按钮

    不务正业的第n天(划掉 2020年年末在完成Web网页制作课程的大作战,在写代码的时候想到用HTML + CSS & Javascript制作一个图片轮播功能增强网页的功能 简单贴一下代码:注 ...

  5. SPI 在 Dubbo中 的应用

    通过本文的学习,可以了解 Dubbo SPI 的特性及实现原理,希望对大家的开发设计有一定的启发性. 一.概述 SPI 全称为 Service Provider Interface,是一种模块间组件相 ...

  6. Web Components从技术解析到生态应用个人心得指北

    Web Components浅析 Web Components 是一种使用封装的.可重用的 HTML 标签.样式和行为来创建自定义元素的 Web 技术. Web Components 自己本身不是一个 ...

  7. volatile和synchronized和lock的区别

    volatile和synchronized的区别: volatile关键字解决的是变量在多个线程之间的可见性(对于用volatile修饰的变量,JVM虚拟机只是保证从主内存加载到线程工作内存的值是最新 ...

  8. 如虎添翼!高德地图+Serverless 护航你的假日出行

    ​ 作者 | 刘金龙(福辰) 高德团队 引言 ​ "前方事故多发地段,请注意保持车距..." "您已疲劳驾驶,请注意休息..." "前方经过泰山旅游景 ...

  9. 【驱动】以太网扫盲(二)phy寄存器简介

    PHY 寄存器的地址空间为 5 位,从 0 到 31 最多可以定义 32 个寄存器(随着芯片功能不断增加,很多 PHY 芯片采用分页技术来扩展地址空间以定义更多的寄存器),IEEE802.3 定义了地 ...

  10. 《3D编程模式》写书记录

    本书介绍 本书罗列了我从自己的实战项目中提炼出来的关于3D编程(主要包括"3D引擎/游戏引擎"."编辑器"开发)的各种编程模式 所有的写书记录 <3D编程 ...