莳玫 發表於 2023-8-24 09:32:00

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

<p>条件语句,也称为IF-ELSE语句,是计算机编程中的一种基本控制结构。它允许程序根据条件的真假来执行不同的代码块。条件语句在处理决策和分支逻辑时非常有用。一般来说,条件语句由IF关键字、一个条件表达式、一个或多个代码块以及可选的ELSE关键字和对应的代码块组成。条件表达式的结果通常是布尔值(True或False),决定了程序将执行IF代码块还是ELSE代码块。</p>
<p>在汇编语言中,条件跳转指令用于根据条件语句的结果在不同的代码块之间跳转,标签用于标记代码块的入口点。通过运用标签与跳转即可构建不同的条件语句,本章将以C语言中条件语句为基础,并使用汇编语言介绍如何实现它们,以让读者能更加深入的理解C语言与汇编语言之间的差异,帮助读者更好的理解并运用汇编语言。</p>
<h3 id="111-if中and语句构造">11.1 IF中AND语句构造</h3>
<p>如下所示代码定义了3个整型变量var1、var2和var3,并检查它们的值是否满足一定的条件,条件包括var1大于等于20,var2小于等于100,var3等于50。如果这些条件都成立,则输出字符串"xor eax,eax"。</p>
<p>AND运算符是逻辑运算符之一,用于连接两个条件,当且仅当两个条件都成立时,才会返回真值。在C语言中,AND运算符使用<code>&amp;&amp;</code>表示。针对and语句的执行顺序,如果等式两边只要有一边返回假,则整个等式就不需要继续下去了,只有等式1成立的情况下才会继续判断等式2是否成立,两边都成立则会执行表达式内部。</p>
<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;windows.h&gt;

int main(int argc,char * argv[])
{
int var1 = 20;
int var2 = 10;
int var3 = 50;

if (var1 &gt;= 20 and var2 &lt;= 100 and var3 == 50)
{
    printf("xor eax,eax");
}
return 0;
}
</code></pre>
<p>对于多重and比较运算,编写汇编语句时,应注意判断的转换,如果高级语言中是大于等于,那么低级语言则可转换为不大于则跳转,如果是小于等于,则对应的汇编语句则可直接转换为不小于则跳转,最后and语句必须三者全部一致,所以判断条件只需要顺序向下写即可,当所有条件全部满足则执行内部的xor指令,否则直接跳转结束本次判断。</p>
<pre><code class="language-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 &gt;= 20 and var2 &lt;= 100 and var3 == 50)
    cmp dword ptr ds:,20   ; 判断是否大于20
    jl L1                        ; 不大于则跳转
   
    cmp dword ptr ds:,100    ; 判断是否小于100
    jg L1                        ; 不小于则跳转
   
    cmp dword ptr ds:,50   ; 判断是否等于50
    jne L1                         ; 不等于则跳转

    mov dword ptr ds:,1      ; 说明等式成立 flag=1
    jmp L2

L1: mov dword ptr ds:,0
L2: cmp dword ptr ds:,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
</code></pre>
<h3 id="112-if中or语句构造">11.2 IF中OR语句构造</h3>
<p>OR运算符的特点是,它表示两个条件中只要有一个为真即可满足整个语句的条件。在进行条件判断时,如果其中一个子条件的结果为真,则整个表达式的后半部分将直接跳过,因为无论后半部分的条件是否成立,整个表达式已经返回真值。这种行为称为短路求值。</p>
<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;windows.h&gt;

int main(int argc,char * argv[])
{
int var1 = 20;
int var2 = 10;
int var3 = 50;

if (var1 &gt; var2 || var2 &lt;= var3)
{
    printf("xor eax,eax");
}
else if(var3 == 50 || var2 &gt; 10)
{
    printf("xor ebx,ebx");
}
return 0;
}
</code></pre>
<p>此处由于表达式中使用了OR语句,该语句在比较时只需要两个表达式一边为假,则表达式后半部分会直接忽略判断,所以在构建判断时,应尽可能多的使用cmp语句对变量进行比较。</p>
<pre><code class="language-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 &gt; var2 || var2 &lt;= var3)
    mov eax,dword ptr ds:
    cmp eax,dword ptr ds:   ; var1 &gt; var2
    jg L1
    mov eax,dword ptr ds:
    cmp eax,dword ptr ds:   ; var2 &lt;= var3
    jg L2                           ; 条件是 var2 &gt; var3 则跳转
L1:
    xor eax,eax                     ; printf("xor eax,eax")
    jmp lop_end
L2:
; else if(var3 == 50 || var2 &gt; 10)
    cmp dword ptr ds:,50
    je L3
    cmp dword ptr ds:,10      ; var2 &gt; 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
</code></pre>
<h3 id="113-if中and与or构造">11.3 IF中AND与OR构造</h3>
<p>在C语言中,AND和OR运算符可以混合使用,实现更加灵活的条件判断。在混合使用时,需要注意运算符的优先级和结合性。AND运算符的优先级高于OR运算符,因此,在混合使用AND和OR运算符时,AND的运算会先于OR运算进行。将AND与OR语句混用,混用后其汇编形式与单独使用差距并不明显。</p>
<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;windows.h&gt;

int main(int argc,char * argv[])
{
int var1 = 20;
int var2 = 10;
int var3 = 50;

if ((var1 &gt;= 10 &amp;&amp; var2 &lt;= 20) || (var2 == 10 &amp;&amp; var3 &gt;= 40))
{
    printf("xor eax,eax");
}
else
{
    printf("xor ebx,ebx");
}
return 0;
}
</code></pre>
<p>如上如果将And语句与Or语句连用,编译器会首先判断等式两边是否为常量,如果是常量且外部使用OR包裹,那么通常情况下会只保留等式左边的表达式,等式右边将会被优化掉,而对于人的编写逻辑则是依次作比较。</p>
<pre><code class="language-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 &gt;= 10 &amp;&amp; var2 &lt;= 20) &amp;&amp; (var2 == 10 || var3 &gt;= 40))
    cmp dword ptr ds:,10   ; var1 &gt;= 10
    jl L1
    cmp dword ptr ds:,20   ; var2 &lt;= 20
    jg L1
   
    cmp dword ptr ds:,10   ; var2 == 10
    je L2
    cmp dword ptr ds:,40   ; var3 &gt;= 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
</code></pre>
<h3 id="114-if语句条件测试">11.4 IF语句条件测试</h3>
<p>这段C++代码定义了6个整型变量,并检查它们的值是否满足多个条件。首先,它检查var1是否大于等于var2且var2小于等于var3,并进入下一个if块。接着,它检查x是否等于100或y是否等于200或z是否等于300,并进入下一个if块。最后,它检查result是否等于1,如果是,则输出字符串"xor eax,eax"。</p>
<p>条件测试语句通常情况下会使用<code>cmp</code>指令配合各种状态跳转实现,此处我分别提供两种仿写方式,来看下编译器与我们思维方式的异同。</p>
<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;windows.h&gt;

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 &gt;= var2 &amp;&amp; var2 &lt;= var3)
{
    if (x == 100 || y == 200 || z == 300)
    {
      if (result == 1)
      printf("xor eax,eax");
    }
}
return 0;
}
</code></pre>
<p>对于编译器来说,生成的代码要尽可能高效率,上方的C代码如果是编译器生成,则首先编译器会比较外层循环中的AND语句,由于是AND语句此处无法优化直接做两次比较,接着进入内层比较,依次流水线式执行下来。</p>
<pre><code class="language-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:
    cmp eax,dword ptr ds:      ; var1 &gt;= var2
    jl lop_end
   
    mov eax,dword ptr ds:
    cmp eax,dword ptr ds:      ; var2 &lt;= var3
    jg lop_end
   
    mov eax,dword ptr ds:
    cmp eax,100               ; x == 100
    jne lop_end
   
    mov eax,dword ptr ds:
    cmp eax,200               ; y == 200
    jne lop_end
   
    mov eax,dword ptr ds:
    cmp eax,300               ; z = 300
    jne lop_end
   
    mov eax,dword ptr ds:
    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
</code></pre>
<p>以下是人的逻辑方式,这段代码是本人以汇编小白视角编写的一段代码,代码中首先比较外层IF语句由于是AND所以需要比较两次,接着比较内层判断,内层是OR语句,比较时可采用流水线式比较,最终如果比较通过则直接JE跳转到语句内,否则直接跳转到结尾。</p>
<pre><code class="language-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:
    cmp eax,dword ptr ds:      ; var1 &gt;= var2
    jge L1
    jmp lop_end
L1:
    mov eax,dword ptr ds:      ; var2 &lt;= var3
    cmp eax,dword ptr ds:      
    jle L2
    jmp lop_end
L2:
    mov eax,dword ptr ds:
    cmp eax,100                     ; x == 100 ?
    je L3
    mov eax,dword ptr ds:          ; y == 200 ?
    cmp eax,200
    je L3
    mov eax,dword ptr ds:
    cmp eax,300                     ; z == 300 ?
    je L3
    jmp lop_end
L3:
    mov eax,dword ptr ds:   ; result == 1 ?
    test eax,eax                      ; eax &amp;&amp; eax != 0
    jz lop_end
    xor eax,eax
    jmp lop_end
lop_end:
    int 3

    invoke ExitProcess,0
main ENDP
END main
</code></pre>
<h3 id="115-if语句双重嵌套">11.5 IF语句双重嵌套</h3>
<p>这段C++代码定义了6个整型变量并检查它们的值是否满足多重条件。首先,它检查var1是否大于等于var2,如果满足,则进入下一个if块。在下一个if块中,它进一步检查x&lt;y且z&gt;y是否成立,如果是,则输出字符串"xor eax, eax",否则输出字符串"xor ebx, ebx"。如果var1不大于等于var2,则它将检查var2是否大于var3,如果是,则输出字符串"xor ecx, ecx"。这段代码实现了简单的条件分支逻辑。</p>
<p>双重IF嵌套语句其本质就是连续作比较,在仿写汇编指令时应该由外到内逐层解析,这样才能写出条例清晰的汇编指令。</p>
<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;windows.h&gt;

int main(int argc,char * argv[])
{
int x = 100, y = 200, z = 300;
int var1 = 20,var2 = 10,var3 = 50;

if (var1 &gt;= var2)
{
    if ((x&lt;y) &amp;&amp; (z&gt;y))
    {
      printf("xor eax,eax");
    }
    else
    {
      printf("xor ebx,ebx");
    }
}
else if (var2 &gt; var3)
{
    printf("xor ecx,ecx");
}
return 0;
}
</code></pre>
<p>如下汇编代码,首先比较外层判断<code>var1&gt;=var2</code>如果不成立则<code>jl L1</code>跳转到外层判断的第二个分支判断<code>var2 &gt; var3</code>,如果成立则jl指令不生效,继续判断内层IF语句,由于使用的是AND与运算,则需要顺序判断,判断不通过直接<code>jle l2</code>,如果判断通过则跳转到<code>jle lop_end</code>不执行,此时直接执行<code>xor ecx,ecx</code>完成分支。</p>
<pre><code class="language-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:
    cmp eax,dword ptr ds:      ; if(var1 &gt;= var2) ?
    jl L1
   
    mov eax,dword ptr ds:
    cmp eax,dword ptr ds:         ; if((x&lt;y)) ?
    jge L2
   
    mov eax,dword ptr ds:         ; if((z&gt;y)) ?
    cmp eax,dword ptr ds:
    jle L2
   
    xor eax,eax                        ; printf("xor eax,eax")
    jmp lop_end

L1:
    mov eax,dword ptr ds:
    cmp eax,dword ptr ds:      ; if(var2 &gt; 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
</code></pre>
<h3 id="116-if语句三层嵌套">11.6 IF语句三层嵌套</h3>
<p>这段C++代码定义了6个整型变量并检查它们的值是否满足多个条件。首先,它检查var1是否大于等于var2并且var2小于等于var3或者var3大于var1,如果满足,则进入下一个if块。在下一个if块中,它检查x是否为偶数或y是否为奇数,如果满足,则进一步检查result是否等于1,如果是,则输出字符串"xor eax, eax"。这段代码实现了多个条件的逻辑判断,并且包含了算术和逻辑运算。</p>
<p>三层嵌套IF语句,转换为汇编语句稍微复杂一些,但大方向不变,还是要由外部到内部,依次构建每一个分支按照此顺序构建,其实并不难。</p>
<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;windows.h&gt;

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 &gt;= var2) &amp;&amp; (var2 &lt;= var3) || (var3 &gt; var1))
{
    if ((x % 2 == 0) || (y % 2 != 0))
    {
      if (result == 1)
      printf("xor eax,eax");
    }
}
return 0;
}
</code></pre>
<p>将以上代码转为汇编语句,首先判断<code>(var1 &gt;= var2) &amp;&amp; (var2 &lt;= var3)</code>此语句由于使用了AND所以需要判断等式两边的结果,只要两边有一处不为真,就需要比较<code>(var3 &gt; var1)</code>或运算结果,如果或运算为真则跳转到<code>L1</code>标签处,继续执行内层IF比较语句。</p>
<pre><code class="language-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:
      cmp eax,dword ptr ds:       ; and var1 &gt;= var2
      jl L4
      
      mov eax,dword ptr ds:
      cmp eax,dword ptr ds:       ; and var2 &lt;= var3
      jle L1
      
    L4:
      mov eax,dword ptr ds:
      cmp eax,dword ptr ds:       ; or var3 &gt; var1
      jle lop_end
    L1:
      mov eax,dword ptr ds:
      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:
      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
</code></pre>
<h3 id="117-if语句多选择分支">11.7 IF语句多选择分支</h3>
<p>这段C++代码定义了3个整型变量并根据它们的值进行条件判断。它检查var1是否大于20,如果是,则输出字符串"xor eax, eax"。如果var1不大于20,则它将检查var2是否大于10,如果是,则输出字符串"xor ebx, ebx"。如果var2不大于10,则它将检查var2是否小于var3,如果是,则输出字符串"xor ecx, ecx"。如果以上条件都不满足,则输出字符串"xor edx, edx"。这段代码实现了简单的条件分支和逻辑判断。</p>
<p>多重选择分支结构,其本质就是对某些条件一直判断下去,直到遇到符合条件的表达式则执行表达式内的语句块。</p>
<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;windows.h&gt;

int main(int argc,char * argv[])
{
int var1 = 20,var2 = 10,var3 = 50;

if (var1 &gt; 20)
    printf("xor eax,eax");
else if (var2 &gt; 10)
    printf("xor ebx,ebx");
else if (var2 &lt; var3)
    printf("xor ecx,ecx");
else
    printf("xor edx,edx");

return 0;
}
</code></pre>
<p>多重判断语句编译器生成汇编指令与我们人的思维习惯稍有些不同,对于我们自己的思维方式,总喜欢将判断语句放置到汇编函数开头部分,通过线性比较的方式分别比较不同的分支条件,每个分支条件将被链接到底部的特定语句块上。</p>
<pre><code class="language-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:
    cmp eax,20                     ; var1 &gt; 20
    jg L1
    mov eax,dword ptr ds:
    cmp eax,10                     ; var2 &gt; 10
    jg L2
    cmp eax,dword ptr ds:
    jl L3                            ; var2 &lt; 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
</code></pre>
<p>而编译器为了尽可能优化,写出的代码可能是以下这样子的,编译器并不会采取方便我们理解的方式来生成汇编指令集,而是对分支进行排序,通过顺序依次向下执行,如果条件跳转不成立,则直接执行紧随跳转其后的语句块,当执行结束后通过<code>jmp lop_end</code>统一跳转到结束。</p>
<pre><code class="language-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:
    cmp eax,20
    jle L1
    xor eax,eax                ; printf("xor eax,eax")
    jmp lop_end
L1:
    mov eax,dword ptr ds:
    cmp eax,10
    jle L2
    xor ebx,ebx               ; printf("xor ebx,ebx")
    jmp lop_end
L2:
    mov eax,dword ptr ds:
    cmp eax,dword ptr ds:
    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
</code></pre>
<h3 id="118-if语句自增自减">11.8 IF语句自增自减</h3>
<p>执行自增自减运算需要找一个临时区域来存放自增后的数据,所以首先要开辟局部空间,多数情况下开辟空间可在栈上,例如使用<code>sub esp,12</code>来分配栈空间,并初始化后即可使用,最后需要将该空间恢复。</p>
<pre><code class="language-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: ; 指向栈中基址
    mov ecx,3                     ; 填充次数 12/4 = 3
    mov eax,0cccccccch            ; 填充物
    rep stosd                     ; 初始化开始

    mov dword ptr ss:,1
    mov dword ptr ss:,2    ; 给每个地址赋值
    mov dword ptr ss:,3
   
    mov eax,dword ptr ss: ; 取第一个数据1
    mov ebx,dword ptr ss:; 取第二个数据3
    add eax,ebx                   ; 执行递增
    mov dword ptr ss:,eax; 写回栈
   
    add esp,12                     ; 平栈
    mov esp,ebp
    pop ebp

    invoke ExitProcess,0
main ENDP
END main
</code></pre>
<p>首先我们先来编写一段简单的C代码片段,如下代码中我们使用了两种自增符号,一种是<code>var1++</code>另一种是<code>++var2</code>两种方式的汇编版本并不一致,仿写是需要格外注意。</p>
<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;windows.h&gt;

int main(int argc,char * argv[])
{
int var1 = 20,var2 = 10,var3 = 50;

if (var1++ &gt;= 20 &amp;&amp; ++var2 &gt; 10)
{
    printf("xor eax,eax");
}
return 0;
}
</code></pre>
<p>以下汇编代码中需要注意,当我们使用<code>var1++</code>时程序是将<code>++后</code>的结果赋值到了栈中存放,并让<code>var1</code>变量递增,而判断则使用的是栈中的原值,相反<code>++var1</code>则是在原值上直接进行操作,将操作结果赋值给原值后在进行判断。</p>
<pre><code class="language-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:; 指向栈中基址
    mov ecx,2                     ; 填充次数 8/4 = 2
    mov eax,0cccccccch            ; 填充物
    rep stosd                     ; 初始化开始

    mov eax,dword ptr ds:
    mov dword ptr ss:,eax   ; 将var1存入临时变量中
    add eax,1
    mov dword ptr ds:,eax    ; 将相加后的结果写回到var1
   
    cmp dword ptr ss:,20    ; 用原值与20对比
    jl L1
    mov dword ptr ss:,1   ; 局部变量存放标志=1
    jmp L2

L1: mov dword ptr ss:,0
L2: cmp dword ptr ss:,0
    je lop_end

    mov eax,dword ptr ds:    ; 继续执行 ++var2
    add eax,1
    mov dword ptr ds:,eax
    cmp dword ptr ds:,10   ; var2 &gt; 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
</code></pre>
<h3 id="119-if语句三目运算符">11.9 IF语句三目运算符</h3>
<p>C语言中提供了快捷判断语句,唯一的三目运算符,该运算符其实就是压缩版的<code>IF-ELSE</code>结构,其表达式与IF基本一致,但在AND运算符的影响下会与<code>IF-ELSE</code>结构有些许的不同。</p>
<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;Windows.h&gt;

int main(int argc,char *argv[])
{
int var1 = 20, var2 = 10, var3 = 50;

if ((var1 &gt; var2 ? 1 : 0) &amp;&amp; (var2 &lt;= var3 ? 1 : 0))
{
    printf("xor eax,eax");
}
return 0;
}
</code></pre>
<p>在仿写这段C代码的汇编版时,我们首先要注意他是一个AND比较操作,两侧必须同时为1才可,因为这个特性的存在,在编写汇编代码时,可以增加一个<code>flag</code>标志位,通过对该标志位的流水线判断实现三目运算比较。</p>
<pre><code class="language-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:
      cmp eax,dword ptr ds:   ; var1 &gt; var2 ?
      jle L1
      mov dword ptr ds:,1   ; 表达式1成立
      jmp L2

    L1: mov dword ptr ds:,0
    L2: cmp dword ptr ds:,0
      je lop_end
      
      mov eax,dword ptr ds:
      cmp eax,dword ptr ds:   ; var2 &lt;= var3
      jg L3
      mov dword ptr ds:,1   ; 表达式2成立
      jmp L4
      
    L3: mov dword ptr ds:,0
    L4: cmp dword ptr ds:,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
</code></pre>
<h3 id="1110-if语句嵌套移位">11.10 IF语句嵌套移位</h3>
<p>这段C++代码定义了两个函数func_a和func_b,它们分别包含了条件判断和逻辑运算。在函数func_a中,它首先对三个整型变量进行了位运算,然后通过逻辑或连接这些运算结果,进入下一个if块。在这个if块中,它再次进行多个逻辑判断和比较,判断条件包括被位运算处理过的变量值和固定的数值50。如果所有条件都满足,则输出字符串"xor eax, eax"。在函数func_b中,它通过取模和位运算对三个整型变量进行处理,并进入下一个if块。在if块内,它进行了大于比较,并输出字符串"xor ebx, ebx"。这段代码实现了对多个变量的复杂运算和逻辑判断。</p>
<pre><code class="language-C">#include &lt;stdio.h&gt;
#include &lt;windows.h&gt;

int func_a()
{
    int var1 = 20,var2 = 10,var3 = 50;

    if (((var1 &lt;&lt; 2) ^ (var2 &lt;&lt; 3)) || ((var2 &lt;&lt; 1) ^ (var3 &lt;&lt; 3)))
    {
      if ((var1 &gt;= var2) || (var2 &lt;= var3) &amp;&amp; (var3 == 50))
      {
            printf("xor eax,eax");
      }
    }
    return 0;
}

int func_b()
{
    int var1 = 20,var2 = 10,var3 = 50;

    if (((var1 &lt;&lt; 2) % 2) || (var3 &gt;&gt; 1) % 3)
    {
      if (((var1 &lt;&lt; 2) + 10) &gt; 50)
      {
            printf("xor ebx,ebx");
      }
    }
    return 0;
}
</code></pre>
<p>先来看第一个<code>func_a()</code>函数如何进行仿写,首先<code>(((var1 &lt;&lt; 2) ^ (var2 &lt;&lt; 3)) || ((var2 &lt;&lt; 1) ^ (var3 &lt;&lt; 3)))</code>外部嵌套是一个OR运算,按照顺序先拆分。</p>
<ul>
<li>执行<code>((var1 &lt;&lt; 2) ^ (var2 &lt;&lt; 3))</code>先将数据<code>shl</code>左移,移动后将两边数据进行<code>xor</code>异或,如果为0则比较等式2。</li>
<li>执行<code>((var2 &lt;&lt; 1) ^ (var3 &lt;&lt; 3)))</code>比较等式2,如果为真,则继续执行内层的移位与相加运算,为假跳转到结束。</li>
</ul>
<pre><code class="language-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 &lt;&lt; 2) ^ (var2 &lt;&lt; 3))
    mov eax,dword ptr ds:
    shl eax,2
    mov ecx,dword ptr ds:
    shl ecx,3
    xor eax,ecx
    je L1
   
    ; ((var2 &lt;&lt; 1) ^ (var3 &lt;&lt; 3))
    mov eax,dword ptr ds:
    shl eax,1
    mov eax,dword ptr ds:
    shl ecx,3
    xor eax,ecx
    je lop_end
   
    ; (var1 &gt;= var2)
L1: mov eax,dword ptr ds:
    cmp eax,dword ptr ds:
    jge L2
   
    ; (var2 &lt;= var3)
    mov eax,dword ptr ds:
    cmp eax,dword ptr ds:
    jg lop_end
L2:
    ; (var3 == 50)
    cmp dword ptr ds:,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
</code></pre>
<p>第二个函数<code>func_b()</code>与第一个基本一致,我们只需要将等式进行拆分,拆分后按照括号优先级依次进行仿写并增加跳转指令即可。</p>
<pre><code class="language-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 &lt;&lt; 2) % 2)
    mov eax,dword ptr ds:
    shl eax,2
    and eax,080000001h          ; var1 % 2
    jns L2                      ; 非负数则跳转

    ; (var3 &gt;&gt; 1) % 3         ; 为负数执行第二个表达式
L1: mov eax,dword ptr ds:
    sar eax,1                   ; var3 &gt;&gt; 1
    cdq                         ; 扩展为8字节
    mov ecx,3                   ; 除以3
    idiv ecx
    test edx,edx                ; 比较余数是否为0
    je lop_end

    ; ((var1 &lt;&lt; 2) + 10) &gt; 50
L2: mov eax,dword ptr ds:
    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
</code></pre>
<h3 id="1111-if语句运算符混合">11.11 IF语句运算符混合</h3>
<p>如果将多种运算符混合在一起,那么我们在仿写汇编代码是可能会麻烦一些,尤其是涉及到多种比较与运算时,我们以计算平年闰年为例,看看该如何实现复杂运算符的仿写。</p>
<ul>
<li>首先闰年时年份对400取余等于0的数,或者对4取余等于0并且对100取余不等于0的数。</li>
</ul>
<pre><code class="language-C">#include &lt;windows.h&gt;
#include &lt;stdio.h&gt;

int main(int argc,char * argv[])
{
int year = 2017;
if (year % 400 == 0 || (year % 4 == 0 &amp;&amp; year % 100 != 0))
{
    printf("%d 闰年 \n", year);
}
{
    printf("%d 平年 \n", year);
}
return 0;
}
</code></pre>
<p>老样子,按照以前的步骤,先对等式拆分,拆分后依次实现每一个等式,最后将这些等式通过判断语句串联起来即可,这段代码除使用了<code>idiv</code>除法指令外,其他地方与如上内容保持一致。</p>
<pre><code class="language-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 = 2017;
    cdq
    mov ecx,400
    idiv ecx                        ; year % 400 == 0
    test edx,edx
    je L1
   
    mov eax,dword ptr ds:
    and eax,080000003h            ; year % 4
    test eax,eax
    jne L2
   
    mov eax,dword ptr ds:
    cdq
    mov ecx,100
    idiv ecx                         ; year % 100 != 0
    test edx,edx                     ; 比较余数
    je L2                            ; 跳转则是平年
   
L1: mov eax,dword ptr ds:
    invoke crt_printf,addr szFmtR,eax   ; 是闰年
    jmp lop_end

L2: mov eax,dword ptr ds:
    invoke crt_printf,addr szFmtP,eax   ; 是平年
    jmp lop_end

lop_end:
    int 3

main ENDP
END main
</code></pre>


</div>
<div id="MySignature" role="contentinfo">
    <b>文章出处:</b>https://www.cnblogs.com/LyShark/p/17653312.html
<br>
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!<br><br>
来源:https://www.cnblogs.com/LyShark/p/17653312.html
頁: [1]
查看完整版本: 5.11 汇编语言:仿写IF条件语句