5.11 汇编语言:仿写IF条件语句

条件语句,也称为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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datavar1 DWORD 20var2 DWORD 10var3 DWORD 50flag DWORD ?
.codemain PROC; if(var1 >= 20 and var2 <= 100 and var3 == 50)cmp dword ptr ds:[var1],20     ; 判断是否大于20jl L1                          ; 不大于则跳转cmp dword ptr ds:[var2],100    ; 判断是否小于100jg L1                          ; 不小于则跳转cmp dword ptr ds:[var3],50     ; 判断是否等于50jne L1                         ; 不等于则跳转mov dword ptr ds:[flag],1      ; 说明等式成立 flag=1jmp L2L1: mov dword ptr ds:[flag],0L2: cmp dword ptr ds:[flag],0je lop_end                     ; 为0则跳转,不为0则继续执行xor eax,eax                    ; 此处是执行if语句内部xor ebx,ebxxor ecx,ecxjmp lop_endlop_end:nop                            ; 直接结束invoke ExitProcess,0main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datavar1 DWORD 20var2 DWORD 10var3 DWORD 50
.codemain PROC; if (var1 > var2 || var2 <= var3)mov eax,dword ptr ds:[var1]cmp eax,dword ptr ds:[var2]     ; var1 > var2jg L1mov eax,dword ptr ds:[var2]cmp eax,dword ptr ds:[var3]     ; var2 <= var3jg L2                           ; 条件是 var2 > var3 则跳转L1:xor eax,eax                     ; printf("xor eax,eax")jmp lop_endL2:; else if(var3 == 50 || var2 > 10)cmp dword ptr ds:[var3],50je L3cmp dword ptr ds:[var2],10      ; var2 > 10jle lop_endL3:xor ebx,ebx                     ; printf("xor ebx,ebx")jmp lop_endlop_end:nopint 3invoke ExitProcess,0main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datavar1 DWORD 20var2 DWORD 10var3 DWORD 50
.codemain PROC; if ((var1 >= 10 && var2 <= 20) && (var2 == 10 || var3 >= 40))cmp dword ptr ds:[var1],10     ; var1 >= 10jl L1cmp dword ptr ds:[var2],20     ; var2 <= 20jg L1cmp dword ptr ds:[var2],10     ; var2 == 10je L2cmp dword ptr ds:[var3],40     ; var3 >= 40jl L1jmp L2L1:xor ebx,ebx               ; elsejmp lop_endL2:xor eax,eax                ; printf("xor eax,eax")jmp lop_endlop_end:int 3invoke ExitProcess,0main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datax DWORD 100y DWORD 200z DWORD 300var1 DWORD 20var2 DWORD 10var3 DWORD 50result DWORD 1
.codemain PROCmov eax,dword ptr ds:[var1]cmp eax,dword ptr ds:[var2]      ; var1 >= var2jl lop_endmov eax,dword ptr ds:[var2]cmp eax,dword ptr ds:[var3]      ; var2 <= var3jg lop_endmov eax,dword ptr ds:[x]cmp eax,100                 ; x == 100jne lop_endmov eax,dword ptr ds:[y]cmp eax,200                 ; y == 200jne lop_endmov eax,dword ptr ds:[z]cmp eax,300                 ; z = 300jne lop_endmov eax,dword ptr ds:[result]test eax,eax                 ; eax = 0 ?jz lop_endxor eax,eaxjmp lop_endlop_end:int 3invoke ExitProcess,0main ENDP
END main

以下是人的逻辑方式,这段代码是本人以汇编小白视角编写的一段代码,代码中首先比较外层IF语句由于是AND所以需要比较两次,接着比较内层判断,内层是OR语句,比较时可采用流水线式比较,最终如果比较通过则直接JE跳转到语句内,否则直接跳转到结尾。

  .386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datax DWORD 100y DWORD 200z DWORD 300var1 DWORD 20var2 DWORD 10var3 DWORD 50result DWORD 1
.codemain PROCmov eax,dword ptr ds:[var1]cmp eax,dword ptr ds:[var2]      ; var1 >= var2jge L1jmp lop_endL1:mov eax,dword ptr ds:[var2]      ; var2 <= var3cmp eax,dword ptr ds:[var3]      jle L2jmp lop_endL2:mov eax,dword ptr ds:[x]cmp eax,100                       ; x == 100 ?je L3mov eax,dword ptr ds:[y]          ; y == 200 ?  cmp eax,200je L3mov eax,dword ptr ds:[y]cmp eax,300                       ; z == 300 ?je L3jmp lop_endL3:mov eax,dword ptr ds:[result]     ; result == 1 ?test eax,eax                      ; eax && eax != 0jz lop_endxor eax,eaxjmp lop_endlop_end:int 3invoke ExitProcess,0main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datax DWORD 100y DWORD 200z DWORD 300var1 DWORD 20var2 DWORD 10var3 DWORD 50
.codemain PROCmov eax,dword ptr ds:[var1]cmp eax,dword ptr ds:[var2]        ; if(var1 >= var2) ?jl L1mov eax,dword ptr ds:[x]cmp eax,dword ptr ds:[y]           ; if((x<y)) ?jge L2mov eax,dword ptr ds:[z]           ; if((z>y)) ?cmp eax,dword ptr ds:[y]jle L2xor eax,eax                        ; printf("xor eax,eax")jmp lop_endL1:mov eax,dword ptr ds:[var2]cmp eax,dword ptr ds:[var3]      ; if(var2 > var3) ?jle lop_endxor ecx,ecx                      ; printf("xor ecx,ecx")jmp lop_endL2:xor ebx,ebx                      ; printf("xor ebx,ebx")jmp lop_endlop_end:int 3invoke ExitProcess,0main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datax DWORD 100y DWORD 200var1 DWORD 20var2 DWORD 10var3 DWORD 50result DWORD 1
.codemain PROCmov eax,dword ptr ds:[var1]cmp eax,dword ptr ds:[var2]       ; and var1 >= var2jl L4mov eax,dword ptr ds:[var2]cmp eax,dword ptr ds:[var3]       ; and var2 <= var3jle L1L4:mov eax,dword ptr ds:[var3]cmp eax,dword ptr ds:[var1]       ; or var3 > var1jle lop_endL1:mov eax,dword ptr ds:[x]and eax,080000001h                ; eax = eax % 2 = 0jns L2                            ; eax = 0 则跳转dec eaxor eax,0fffffffeh                 ; eax = eax % 2 != 0inc eaxL2:mov eax,dword ptr ds:[result]test eax,eax                      ; if(result == 1)jne L3jmp lop_endL3:xor eax,eax                       ; printf("xor eax,eax")jmp lop_endlop_end:int 3invoke ExitProcess,0main 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");elseprintf("xor edx,edx");return 0;
}

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

  .386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datavar1 DWORD 20var2 DWORD 10var3 DWORD 50
.codemain PROCmov eax,dword ptr ds:[var1]cmp eax,20                       ; var1 > 20jg L1mov eax,dword ptr ds:[var2]cmp eax,10                       ; var2 > 10jg L2cmp eax,dword ptr ds:[var3]jl L3                            ; var2 < var3xor edx,edx                      ; printf("xor edx,edx")jmp lop_endL1:xor eax,eax                      ; printf("xor eax,eax")jmp lop_endL2:xor ebx,ebx                      ; printf("xor ebx,ebx")jmp lop_endL3:xor ecx,ecx                      ; printf("xor ecx,ecx")jmp lop_endlop_end:int 3invoke ExitProcess,0main ENDP
END main

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

  .386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datavar1 DWORD 20var2 DWORD 10var3 DWORD 50
.codemain PROCmov eax,dword ptr ds:[var1]cmp eax,20jle L1xor eax,eax                ; printf("xor eax,eax")jmp lop_endL1:mov eax,dword ptr ds:[var2]cmp eax,10jle L2xor ebx,ebx                 ; printf("xor ebx,ebx")jmp lop_endL2:mov eax,dword ptr ds:[var2]cmp eax,dword ptr ds:[var3]jge L3xor ecx,ecx                  ; printf("xor ecx,ecx")  jmp lop_endL3:xor edx,edx                  ; printf("xor edx,edx")jmp lop_endlop_end:int 3invoke ExitProcess,0main ENDP
END main

11.8 IF语句自增自减

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

  .386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.codemain PROCpush ebpmov ebp,espsub 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],1mov dword ptr ss:[ebp-8],2    ; 给每个地址赋值mov dword ptr ss:[ebp-4],3mov eax,dword ptr ss:[ebp-12] ; 取第一个数据1mov ebx,dword ptr ss:[ebp-4]  ; 取第二个数据3add eax,ebx                   ; 执行递增mov dword ptr ss:[ebp-8],eax  ; 写回栈add esp,12                     ; 平栈mov esp,ebppop ebpinvoke ExitProcess,0main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datavar1 DWORD 20var2 DWORD 10var3 DWORD 50
.codemain PROCpush ebpmov ebp,espsub esp,8                     ; 开辟 2*4 =8 的空间lea edi,dword ptr ss:[ebp-8]  ; 指向栈中基址mov ecx,2                     ; 填充次数 8/4 = 2mov eax,0cccccccch            ; 填充物rep stosd                     ; 初始化开始mov eax,dword ptr ds:[var1]mov dword ptr ss:[ebp-8],eax   ; 将var1存入临时变量中add eax,1mov dword ptr ds:[var1],eax    ; 将相加后的结果写回到var1cmp dword ptr ss:[ebp-8],20    ; 用原值与20对比jl L1mov dword ptr ss:[ebp-4],1     ; 局部变量存放标志=1jmp L2L1: mov dword ptr ss:[ebp-4],0L2: cmp dword ptr ss:[ebp-4],0je lop_endmov eax,dword ptr ds:[var2]    ; 继续执行 ++var2add eax,1mov dword ptr ds:[var2],eaxcmp dword ptr ds:[var2],10     ; var2 > 10jle lop_endxor eax,eax                    ; printf("xor eax,eax")lop_end:add esp,8                     ; 平栈mov esp,ebppop ebpinvoke ExitProcess,0main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datavar1 DWORD 20var2 DWORD 10var3 DWORD 50flag DWORD ?
.codemain PROCmov eax,dword ptr ds:[var1]cmp eax,dword ptr ds:[var2]   ; var1 > var2 ?jle L1mov dword ptr ds:[flag],1     ; 表达式1成立jmp L2L1: mov dword ptr ds:[flag],0L2: cmp dword ptr ds:[flag],0je lop_endmov eax,dword ptr ds:[var2]cmp eax,dword ptr ds:[var3]   ; var2 <= var3jg L3mov dword ptr ds:[flag],1     ; 表达式2成立jmp L4L3: mov dword ptr ds:[flag],0L4: cmp dword ptr ds:[flag],0je lop_endxor eax,eax                   ; printf("xor eax,eax")jmp lop_endlop_end:int 3invoke ExitProcess,0main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datavar1 DWORD 20var2 DWORD 10var3 DWORD 50
.codemain PROC; ((var1 << 2) ^ (var2 << 3))mov eax,dword ptr ds:[var1]shl eax,2mov ecx,dword ptr ds:[var2]shl ecx,3xor eax,ecxje L1; ((var2 << 1) ^ (var3 << 3))mov eax,dword ptr ds:[var2]shl eax,1mov eax,dword ptr ds:[var3]shl ecx,3xor eax,ecxje 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_endL2: ; (var3 == 50)cmp dword ptr ds:[var3],50jnz lop_endxor eax,eax               ; printf("xor eax,eax")jmp lop_endlop_end:int 3invoke ExitProcess,0main ENDP
END main

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

  .386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datavar1 DWORD 20var2 DWORD 10var3 DWORD 50
.codemain PROC; ((var1 << 2) % 2)mov eax,dword ptr ds:[var1]shl eax,2and eax,080000001h          ; var1 % 2jns L2                      ; 非负数则跳转; (var3 >> 1) % 3           ; 为负数执行第二个表达式L1: mov eax,dword ptr ds:[var3]sar eax,1                   ; var3 >> 1cdq                         ; 扩展为8字节mov ecx,3                   ; 除以3idiv ecxtest edx,edx                ; 比较余数是否为0je lop_end; ((var1 << 2) + 10) > 50L2: mov eax,dword ptr ds:[var1]shl eax,2add eax,10cmp eax,50jle lop_endxor eax,eax                  ; printf("xor ebx,ebx")jmp lop_endlop_end:int 3invoke ExitProcess,0main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.dataYear DWORD 2017szFmtR BYTE '%d 是闰年',0dh,0ah,0szFmtP BYTE '%d 是平年',0dh,0ah,0
.codemain PROCmov eax,dword ptr ds:[Year]     ; year = 2017;cdqmov ecx,400idiv ecx                        ; year % 400 == 0test edx,edxje L1mov eax,dword ptr ds:[Year]and eax,080000003h              ; year % 4test eax,eaxjne L2mov eax,dword ptr ds:[Year]cdqmov ecx,100idiv ecx                         ; year % 100 != 0test edx,edx                     ; 比较余数je L2                            ; 跳转则是平年L1: mov eax,dword ptr ds:[Year]invoke crt_printf,addr szFmtR,eax     ; 是闰年jmp lop_endL2: 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
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/50298.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java开源 VR全景商城 saas商城 b2b2c商城 o2o商城 积分商城 秒杀商城 拼团商城 分销商城 短视频商城 小程序商城搭建 bbc

​ 1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前…

回归预测 | MATLAB实现BES-SVM秃鹰搜索优化算法优化支持向量机多输入单输出回归预测(多指标,多图)

回归预测 | MATLAB实现BES-SVM秃鹰搜索优化算法优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现BES-SVM秃鹰搜索优化算法优化支持向量机多输入单输出回归预测&#xff08;多指标&#xff0c;多图&#xff09;效…

go gorm一对多has many

has many 与另一个模型建立了一对多的连接。 不同于 has one&#xff0c;拥有者可以有零或多个关联模型。 例如&#xff0c;您的应用包含 user 和 credit card 模型&#xff0c;且每个 user 可以有多张 credit card。 定义model // User 有多张 CreditCard&#xff0c;UserI…

python中的matplotlib画直方图(数据分析与可视化)

python中的matplotlib画直方图&#xff08;数据分析与可视化&#xff09; import numpy as np import pandas as pd import matplotlib.pyplot as pltpd.set_option("max_columns",None) plt.rcParams[font.sans-serif][SimHei] plt.rcParams[axes.unicode_minus]Fa…

TCP缓冲区参数调优

1、系统原值查询 [lybadmin autopush]$ cat /proc/sys/net/core/rmem_max 4194304 [lybadmin autopush]$ cat /proc/sys/net/core/wmem_max 1048576 [lybadmin autopush]$ cat /proc/sys/net/ipv4/tcp_rmem 4096 87380 4194304 [lybadmin autopush]$ cat /proc/sys/net/ip…

centos8安装mysql

1.首先用finalShell远程连接到服务器 2.如果服务器之前安装过mysql请先卸载,我这里是用yum安装的&#xff0c;现在通过yum去卸载 yum remove -y mysql find / -name mysql //找到残留的文件&#xff0c;再通过rm -rf去删除对应的文件3.下面正式开始安装 &#xff08;1&#…

基于微信小程序+Springboot校园二手商城系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、目前专注于大学生项目实战开发,讲解,毕业答疑辅导✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3…

使用 OpenAI GPT 模型的最佳实践

推荐&#xff1a;使用NSDT场景编辑器助你快速搭建可二次编辑的3D应用场景 为了帮助用户获得最佳输出&#xff0c;OpenAI 提供了使用 GPT 模型的最佳实践。这来自体验&#xff0c;因为许多用户不断尝试使用此模型并找到了最有效的方法。 在本文中&#xff0c;我将总结使用 Ope…

分数规划(二分)

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 小咪是一个土豪手办狂魔&#xff0c;这次他去了一家店&#xff0c;发现了好多好多&#xff08;n个&#xff09;手办&#xff0c;但他是一个很怪的人&#xff0c;每次只想买k个手办&a…

JavaSE-21 【Stream流】

1 Stream的介绍 1.1 概念 stream流操作是Java 8提供一个重要新特性&#xff0c;它允许开发人员以声明性方式处理集合&#xff0c;其核心类库主要改进了对集合类的 API和新增Stream操作。Stream类中每一个方法都对应集合上的一种操作。将真正的函数式编程引入到Java中&#xf…

【PHP】数据类型运算符位运算

文章目录 数据类型简单&#xff08;基本&#xff09;数据类型&#xff1a;4个小类复合数据类型&#xff1a;2个小类特殊数据类型&#xff1a;2个小类类型转换类型判断整数类型浮点类型布尔类型 运算符赋值运算符算术运算符比较运算符逻辑运算符连接运算符错误抑制符三目运算符自…

Apache ShenYu 学习笔记一

1、简介 这是一个异步的&#xff0c;高性能的&#xff0c;跨语言的&#xff0c;响应式的 API 网关。 官网文档&#xff1a;Apache ShenYu 介绍 | Apache ShenYu仓库地址&#xff1a;GitHub - apache/shenyu: Apache ShenYu is a Java native API Gateway for service proxy, pr…

nginx+keepalived负载均衡和高可用配置

概述 nginx用来负载均衡&#xff0c;keepalived是用来实现VIP故障切换。 配置 nginxkeepalived配置中&#xff0c;后端普通服务器不需要lvs实现负载均衡。 在服务器上停止之前的lvs配置 /etc/init.d/lvs_rs stop 1.nginx的配置 在主备节点上测试 配置nginx之前需要将kee…

UE学习记录03----UE5.2 使用MVVM示例

1.打开ue5.2新建C项目 2.项目中通过类导向新建C类&#xff0c;父类选择为UMVVMViewModelBase&#xff0c;创建完成会自动打开vs 3.在VS中对新建的类进行宏定义 使用 C 类向导 创建的类声明自动通过 UCLASS() 宏进行处理。 UCLASS() 宏使得引擎意识到这个类的存在&#xff0c;并…

React Native 任务列表实战

通过 ToDo 的小项目实战&#xff0c;我们可以回顾页面布局&#xff0c;事件的监听&#xff0c;React Native 中的钩子函数使用。 整体项目框架搭建以及相关基础样式 首先我们先完成项目的整体框架搭建&#xff0c;把页面中相关的元素和样式类名定义好。并且表明对应单独组建的…

【linux】NFS调试总结

文章目录 00. ENV10. 简述20. 下载、安装、配置30. 使用1. 从uboot中设置NFS启动文件系统2. 调试 80. 问题1. NFS版本不匹配问题 90. 附件91. 服务端NFS配置项简述 00. ENV ubuntn1804 10. 简述 百度百科&#xff1a;https://baike.baidu.com/item/%E7%BD%91%E7%BB%9C%E6%96%87…

【Rust】Rust学习 第十九章高级特征

现在我们已经学习了 Rust 编程语言中最常用的部分。在第二十章开始另一个新项目之前&#xff0c;让我们聊聊一些总有一天你会遇上的部分内容。你可以将本章作为不经意间遇到未知的内容时的参考。本章将要学习的功能在一些非常特定的场景下很有用处。虽然很少会碰到它们&#xf…

阿里云短信发送模板

1.读取yml的配置Data ConfigurationProperties(prefix "aliyun.sms") public class SmsProperties {private String signName;private String templateCode;private String accessKey;private String secret; } 2.封装方法并注入容器 Bean public class SmsTempla…

上海交大ACM班总教头团队重磅新作,带你动手学机器学习(文末赠书4本)

目录 0 写在前面1 什么是机器学习&#xff1f;2 ACM 班总教头&#xff1a;俞勇3 动手学习机器学习赠书活动 0 写在前面 机器学习强基计划聚焦深度和广度&#xff0c;加深对机器学习模型的理解与应用。“深”在详细推导算法模型背后的数学原理&#xff1b;“广”在分析多个机器…

一些自定义hooks

文章目录 1、点击框外隐藏弹窗hook 1、点击框外隐藏弹窗hook **描述&#xff1a;**有一个需要自己封装弹窗的组件&#xff0c;实现点击弹窗框外时隐藏弹窗 代码&#xff1a; import { useEffect } from “react”; // 点击框外hooks import { useEffect } from "react&q…