Win32汇编:各种语句的构造方式

简介: 整理复习汇编语言的知识点,以前在学习《Intel汇编语言程序设计 - 第五版》时没有很认真的整理笔记,主要因为当时是以学习理解为目的没有整理的很详细,这次是我第三次阅读此书,每一次阅读都会有新的收获,这次复习,我想把书中的重点,再一次做一个归纳与总结(注:16位汇编部分跳过),并且继续尝试写一些有趣的案例,这些案例中所涉及的指令都是逆向中的重点,一些不重要的我就直接省略了,一来提高自己,二来分享知识,转载请加出处,敲代码备注挺难受的。

整理复习汇编语言的知识点,以前在学习《Intel汇编语言程序设计 - 第五版》时没有很认真的整理笔记,主要因为当时是以学习理解为目的没有整理的很详细,这次是我第三次阅读此书,每一次阅读都会有新的收获,这次复习,我想把书中的重点,再一次做一个归纳与总结(注:16位汇编部分跳过),并且继续尝试写一些有趣的案例,这些案例中所涉及的指令都是逆向中的重点,一些不重要的我就直接省略了,一来提高自己,二来分享知识,转载请加出处,敲代码备注挺难受的。

这次复习的重点就是高级语言,各种语句的底层实现逻辑,我们手工的来实现一些常用的表达式,逐级递增难度,本文中所仿写的汇编流程,风格,参考自VS2013编译器的Debug实现,由于不是研究编译特性的文章,故此处不考虑编译器对代码实施的各种优化措施,只注重C语言代码的汇编化。

IF/AND/OR 语句

IF中的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;
}
    .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

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

IF中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;
}
    .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

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;
}
    .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]
        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

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

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;
}
    .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 lop_end
        
        mov eax,dword ptr ds:[var2]
        cmp eax,dword ptr ds:[var3]      ; and var2 <= var3
        jle L1
        
        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

IF语句中的TEST: 这里有多种写法,第一种是比较好的写法,不需要增加太多编号,第二种是正常人的思维方式.

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

以下是人的逻辑方式.

    .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
    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

IF-ELSEIF-ELSE: 多层循环从何治,看我的,给我写。

#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

编译器是这样干的,我把他的思路写一下。

    .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

编译器对于if-elseif-else是这样处理的.

    int var1 = 20;
    int var2 = 10;
    int var3 = 50;

    if (var1 > 20)
        printf("xor eax,eax");
    else if (var2 >= 20)
        printf("xor ebx,ebx");
    else if (var3 <= 20)
        printf("xor ecx,ecx");
    else
        printf("xor edx,edx");



    .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 ?
        jle L1                         ; 不大于则跳到L1继续判断
        xor eax,eax
        jmp lop_end                    ; 最后都要跳向结束
        
    L1:    mov eax,dword ptr ds:[var2]
        cmp eax,20                     ; var1 >= 20 ?
        jl L2                          ; 不大于则继续判断L2
        xor ebx,ebx
        jmp lop_end
        
    L2:    mov eax,dword ptr ds:[var3]
        cmp eax,20                      ; var3 <= 20 ?
        jg L3                           ; 大于则跳到L3
        xor ecx,ecx
        jmp lop_end

    L3:    xor edx,edx
        jmp lop_end
        
    lop_end:
        xor esi,esi
        
        invoke ExitProcess,0
    main ENDP
END main

IF的前期脑残写法: 写的烂,没编译器生成的代码有趣,垃圾保存。
脑残1

if(var1 > var2) and (var2 < var3)
{
    xor eax,eax
}else if(var1 > var3)
{
    xor ebx,ebx
}

    .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,dword ptr ds:[var2]      ; if(var1 > var2)
        jg L1
        
        mov eax,dword ptr ds:[var1]
        cmp eax,dword ptr ds:[var3]      ; else if(var1 > var3)
        jg L3
    L1:
        mov eax,dword ptr ds:[var2]      ; if(var2 < var3)
        cmp eax,dword ptr ds:[var3]
        jl L2
    L2:
        xor eax,eax
        jmp lop
    L3:
        xor ebx,ebx
        jmp lop
    lop:
        nop
    
        invoke ExitProcess,0
    main ENDP
END main

脑残2

if var1 == var2
{
    if x > y
    {
        xchg x,y
    }
    else
    {
        x=10
        y=20
    }
}else
{
    var1 = 0
    var2 = 0
}

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
    x BYTE 6
    y BYTE 5
    var1 DWORD 10
    var2 DWORD 10

.code
    main PROC
        mov eax,dword ptr ds:[var1]
        cmp eax,dword ptr ds:[var2]     ; var1 == var2 ?
        jne L1                          ; 不等于跳转到L1
        
        mov al,byte ptr ds:[x]
        cmp al,byte ptr ds:[y]          ; x > y ?
        jg L2                           ; 大于跳到L2
        
        mov byte ptr ds:[x],0           ; 不大于则执行x=10 y=20
        mov byte ptr ds:[y],0
        jmp lop
    L1:
        mov dword ptr ds:[var1],0       ; var1 != var2 则执行
        mov dword ptr ds:[var2],0
    L2:
        mov al,byte ptr ds:[x]
        mov bl,byte ptr ds:[y]
        xchg al,bl                      ; x y 数值交换
        mov byte ptr ds:[x],al
        mov byte ptr ds:[y],bl
        jmp lop
    lop:
        nop
        invoke ExitProcess,0
    main ENDP
END main

if 双层嵌套结构: 包含有and,or运算符的连用处理.

    int var1 = 20;
    int var2 = 10;
    int var3 = 50;

    if (var1++ > 5 && var2++ >= 10)
    {
        var3 = var3 + 10;
        var3 << 2;
        if (var3 <= 100 or var3 <= 1000)
            xor eax,eax
        else
            xor ebx,ebx
    }

    .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
        inc dword ptr ds:[var1]           ; var1++
        mov eax,dword ptr ds:[var1]
        cmp eax,5                         ; var1 > 5 ?
        jg L1
        jmp lop_end
    L1:
        inc dword ptr ds:[var2]           ; var2++
        mov eax,dword ptr ds:[var2]       ; var2 >=10 ?
        cmp eax,10
        jge L2
        jmp lop_end
    L2:
        mov eax,dword ptr ds:[var3]       ; 获取 var3
        add eax,10                        ; var3 = var3 + 10
        shl eax,2                         ; var3 << 2
        
        cmp eax,100
        jle L3                            ; var3 <= 100 ?
        cmp eax,1000                      ; eax or
        jle L3                            ; var3 <= 1000 ?
        jmp L4                            ; else
    L3:
        xor eax,eax
        jmp lop_end
    L4:
        xor ebx,ebx
        jmp lop_end
        
    lop_end:
        nop
        invoke ExitProcess,0
    main ENDP
END main

编译器对于此类嵌套出处理结果是这样的,由于and指令左面如果成立则继续执行右面的判断,如果不成立右面的直接掠过,这样的话就比较有趣了,如下是我根据汇编代码推测的一段片段,。

    .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
    tmp DWORD ?
    flag DWORD ?

.code
    main PROC
        mov eax,dword ptr ds:[var1]
        mov dword ptr ds:[tmp],eax       ; 将var1原值备份到tmp
        
        mov ecx,dword ptr ds:[var1]
        add ecx,1                        ; 递增var1并会写到变量中
        mov dword ptr ds:[var1],ecx
        
        cmp dword ptr ds:[tmp],5         ; 用原值与5做比较
        jle L1                           ; 如果 var1 < var2
        
        mov dword ptr ds:[flag],1
        jmp L2
        
    L1:    mov dword ptr ds:[flag],0         ; 判断的是and的第一个等式
    L2:    cmp dword ptr ds:[flag],0
        je lop_end
        
        mov eax,dword ptr ds:[var2]
        mov dword ptr ds:[tmp],eax        ; 备份var2
        
        mov ecx,dword ptr ds:[var2]
        add ecx,1                         ; 递增运算++
        mov dword ptr ds:[var2],ecx
        
        cmp dword ptr ds:[tmp],10         ; 判断 var2>=10 ?
        jl L3                             ; 不大于则跳到L3
        mov dword dword ptr ds:[flag],1   ; 大于则标志flag=1
        jmp L4

    L3:    mov dword ptr ds:[flag],0
    L4:    cmp dword ptr ds:[flag],0
        je lop_end                         ; 不跳转则执行内部if
        
        mov eax,dword ptr ds:[var3]
        add eax,10
        mov dword ptr ds:[var3],eax         ; 递增var3
        
        mov eax,dword ptr ds:[var3]
        shl eax,2
        mov dword ptr ds:[var3],eax         ; var3 = var3 << 2
        
        cmp dword ptr ds:[var3],100         ; var3 <= 100
        jle L5
        cmp dword ptr ds:[var3],1000        ; var3<=1000
        jg L6                               ; 跳转到内层else
    L5:
        xor eax,eax
        nop
        jmp lop_end
    L6:
        xor ebx,ebx
        nop
        jmp lop_end
        
    lop_end:
        xor eax,eax
        invoke ExitProcess,0
    main ENDP
END main

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

IF嵌套中的移位1:

#include <stdio.h>
#include <windows.h>

int main(int argc,char * argv[])
{
    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;
}
    .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

第二种如果判断

#include <stdio.h>
#include <windows.h>

int main(int argc,char * argv[])
{
    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;
}
    .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

IF中的三目运算符:

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


While /For 语句构建

While/FOr 循环框架: while循环,for循环的简单框架,后期会逐步提高难度,最终实现一个循环链表结构。

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
    count DWORD ?

.code
    main PROC
        mov dword ptr ds:[count],0            ; 设置while初始化
    S1:    cmp dword ptr ds:[count],10           ; 设置最大循环数
        jge loop_end                          ; 判断是否循环结束
        
        xor eax,eax                           ; 执行循环体
        
        mov eax,dword ptr ds:[count]           ; 取出循环条件
        add eax,1                              ; 递增
        mov dword ptr ds:[count],eax           ; 写回
        jmp S1
    loop_end:
        int 3

        invoke ExitProcess,0
    main ENDP
END main

再看一下他的好基友,do-while是如何构造的,相比于while,该语句是先执行在判断,从效率上来说这个效率要高于while.

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
    count DWORD ?
.code
    main PROC
        mov dword ptr ds:[count],0     ; 初始化循环次数
    S1:    xor eax,eax                    ; 执行循环体
        
        mov eax,dword ptr ds:[count]   ; 取出计数器
        add eax,1                      ; 递增
        mov dword ptr ds:[count],eax   ; 回写
        
        cmp dword ptr ds:[count],10    ; 与10做对比
        jl S1                          ; 小于则继续循环

        int 3

        invoke ExitProcess,0
    main ENDP
END main

最后看一个for语句的实现流程,该语句的构建方式相对于While来说略显复杂些,效率远不及While,反汇编后发现,编译器是这样构建的.

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
    count DWORD ?
.code
    main PROC
        mov dword ptr ds:[count],0          ; 设置 int x = 0;
        jmp L2

    L1:    mov eax,dword ptr ds:[count]        ; x = x++
        add eax,1
        mov dword ptr ds:[count],eax

    L2:    cmp dword ptr ds:[count],10         ; 比较 x < 10
        jge lop_end
        
        xor eax,eax                         ; 执行循环体
        jmp L1
        
    lop_end:
        int 3
        invoke ExitProcess,0
    main ENDP
END main

在Python中for循环是for x in range(2,10)可以指定一个范围,我们接着尝试构建一下.

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
    start_count DWORD ?
    end_count DWORD ?
.code
    main PROC
        mov dword ptr ds:[start_count],2     ; 指定开始循环编号
        mov dword ptr ds:[end_count],5       ; 指定结束循环编号
        
        mov ecx,dword ptr ds:[start_count]
    L1:    cmp dword ptr ds:[end_count],ecx
        jle lop_end
        
        xor eax,eax                          ; 循环体内部
        
        add ecx,1                            ; 每次递增
        mov dword ptr ds:[start_count],ecx
        jmp L1
        
    lop_end:
        int 3
        invoke ExitProcess,0
    main ENDP
END main

While遍历数组: 以下案例主要通过仿写While循环结构并通过比例因子寻址,实现对一个DWORD数组的遍历.

#include <stdio.h>
#include <Windows.h>

int main(int argc,char *argv[])
{
    int Array[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int count = 0;

    while (count < sizeof(Array) / sizeof(int))
    {
        printf("value = %d \n", Array[count]);
        count = count + 1;
    }
    return 0;
}
    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    MyArray DWORD 1,2,3,4,5,6,7,8,9,10
    count DWORD ?

    szFmt BYTE 'value = %d ',0dh,0ah,0
.code
    main PROC
        mov dword ptr ds:[count],0        ; 初始化循环
        mov ecx,0                         ; 设置循环计数(比例因子)

    S1:    cmp dword ptr ds:[count],lengthof MyArray  ; 与数组总长度对比
        jge lop_end                                ; 是否结束
        
        lea esi,dword ptr ds:[MyArray]             ; 获取数组基地址
        mov ebx,dword ptr ds:[esi + ecx * 4]       ; 比例因子寻址
        invoke crt_printf,addr szFmt,ebx           ; 调用系统crt
        
        mov ecx,dword ptr ds:[count]
        add ecx,1                                   ; 计次循环递增
        mov dword ptr ds:[count],ecx
        jmp S1
    lop_end:
        int 3
    
        invoke ExitProcess,0
    main ENDP
END main

For循环尝试判断: 这次使用For循环,首先仿写For循环语句,然后在内部判断指定数值是否合格,合格输出.

#include <stdio.h>
#include <Windows.h>

int main(int argc,char *argv[])
{
    int Array[10] = { 56,78,33,45,78,90,32,44,56,67 };

    for (int x = 0; x < 10; x++)
    {
        if (Array[x] >= 50)
        {
            printf("out -> %d \n", Array[x]);
        }
    }
    return 0;
}
    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    MyArray DWORD 56,78,33,45,78,90,32,44,56,67
    count DWORD ?
    szFmt BYTE 'out -> %d ',0dh,0ah,0
.code
    main PROC
        
        mov dword ptr ds:[count],0      ; int x = 0
        jmp L1
    L2:    mov eax,dword ptr ds:[count]
        add eax,1                       ; x ++
        mov dword ptr ds:[count],eax
    L1:
        cmp dword ptr ds:[count],10     ; x < 10
        jge lop_end
        
        mov eax,dword ptr ds:[count]          ; 获取循环次数,当作因子
        lea esi,dword ptr ds:[MyArray]        ; 取数组基地址
        mov ebx,dword ptr ds:[esi + eax * 4]  ; 因子寻址
        cmp ebx,50
        jl L2                                 ; 如果小于50则跳转到下一次循环
        
        invoke crt_printf,addr szFmt,ebx      ; 调用系统crt
        jmp L2

    lop_end:
        int 3
    
        invoke ExitProcess,0
    main ENDP
END main

继续增加难度,求最大最小平均值的代码,尝试用汇编实现.

#include <stdio.h>
#include <Windows.h>

int main(int argc, char *argv[])
{
    int Array[10] = { 56,78,33,45,78,90,32,44,56,67 };
    int max_result = 0,min_result = 100,sum_result = 0,avg_result = 0;

    for (int x = 0; x < 10; x++)
    {
        if (Array[x] >= max_result)
        {
            max_result = Array[x];
        }
        if (Array[x] <= min_result)
        {
            min_result = Array[x];
        }
        sum_result = sum_result + Array[x];
        avg_result = sum_result / 10;
    }
    printf("max = %d min = %d sum = %d avg = %d \n", max_result,min_result,sum_result,avg_result);
    system("pause");
    return 0;
}

以下这段代码,写的有点小问题,但大体完善,先思考一下哪里的问题,后期我在发答案!

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    MyArray DWORD 56,78,33,45,78,90,32,44,56,67
    count DWORD ?
    max_result DWORD 0
    min_result DWORD 100
    sum_result DWORD 0
    avg_result DWORD 0
    
    szFmt BYTE 'max = %d min = %d sum = %d avg = %d ',0dh,0ah,0
.code
    main PROC
        mov dword ptr ds:[count],0      ; int x = 0
        jmp L1
    L2:    mov eax,dword ptr ds:[count]
        add eax,1                       ; x ++
        mov dword ptr ds:[count],eax
    L1:
        cmp dword ptr ds:[count],10     ; x < 10
        jge lop_end

        mov eax,dword ptr ds:[count]
        lea esi,dword ptr ds:[MyArray]
        
        mov ebx,dword ptr ds:[esi + eax * 4]
        cmp ebx,dword ptr ds:[max_result]      ; Array[x] >= max_result
        jl L3
        mov dword ptr ds:[max_result],ebx      ; max_result = Array[x];
    L3:
        mov ebx,dword ptr ds:[esi + eax * 4]
        cmp ebx,dword ptr ds:[min_result]      ; Array[x] <= min_result
        jg L4
    L4:
        mov ebx,dword ptr ds:[esi + eax * 4]
        mov edx,dword ptr ds:[sum_result]      ; sum_result + Array[x];
        add ebx,edx
        mov dword ptr ds:[sum_result],ebx      ; sum_result
        
        mov eax,dword ptr ds:[sum_result]
        cdq
        mov ecx,10
        idiv ecx                               ; sum_result / 10;
        mov dword ptr ds:[sum_result],eax      ; avg_result

        jmp L2
        
    lop_end:
        mov eax,dword ptr ds:[max_result]
        mov ebx,dword ptr ds:[min_result]
        mov ecx,dword ptr ds:[sum_result]
        mov edx,dword ptr ds:[avg_result]
        invoke crt_printf,addr szFmt,eax,ebx,ecx,edx
        int 3
        invoke ExitProcess,0
    main ENDP
END main

问题显而易见,相信大家都看出来了,我就直接公布正确代码了

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    MyArray DWORD 56,78,33,45,78,90,32,44,56,67
    count DWORD ?
    max_result DWORD 0
    min_result DWORD 100
    sum_result DWORD 0
    avg_result DWORD 0
    
    szFmt BYTE 'max = %d min= %d sum= %d avg = %d ',0dh,0ah,0
.code
    main PROC
        mov dword ptr ds:[count],0      ; int x = 0
        jmp L1
    L2:    mov eax,dword ptr ds:[count]
        add eax,1                       ; x ++
        mov dword ptr ds:[count],eax
    L1:
        cmp dword ptr ds:[count],10     ; x < 10
        jge lop_end

        mov eax,dword ptr ds:[count]
        lea esi,dword ptr ds:[MyArray]
        
        mov ebx,dword ptr ds:[esi + eax * 4]
        cmp ebx,dword ptr ds:[max_result]      ; Array[x] >= max_result
        jl L3
        mov dword ptr ds:[max_result],ebx      ; max_result = Array[x];
    L3:
        mov ebx,dword ptr ds:[esi + eax * 4]
        cmp ebx,dword ptr ds:[min_result]      ; Array[x] <= min_result
        jg L4
        mov dword ptr ds:[min_result],ebx
    L4:
    
        mov ebx,dword ptr ds:[esi + eax * 4]   ; Array[x]
        add dword ptr ds:[sum_result],ebx      ; sum_result = sum_result + Array[x];
        
        mov eax,dword ptr ds:[sum_result]
        cdq                                    ; 符号扩展
        mov ecx,10                             ; / 10
        idiv ecx                               ; sum_result / 10;
        mov dword ptr ds:[avg_result],eax      ; avg_result
        jmp L2
        
    lop_end:
        mov eax,dword ptr ds:[max_result]
        mov ebx,dword ptr ds:[min_result]
        mov ecx,dword ptr ds:[sum_result]
        mov edx,dword ptr ds:[avg_result]
        invoke crt_printf,addr szFmt,eax,ebx,ecx,edx
        int 3
    main ENDP
END main

Do-While 与跳出循环: 要说continue与break语句的唯一区别,就在于一个是跳转到了本次循环的结束位置,另一个则是条向了总循环结束位置.

#include <stdio.h>
#include <Windows.h>

int main(int argc, char *argv[])
{
    int Array[10] = { 56,78,33,45,78,90,32,15,56,67 };
    int index = 0;

    do
    {
        if (Array[index] > 10 && Array[index + 1] <= 20)
        {
            printf("array[1] => %d array[2] => %d \n", Array[index], Array[index + 1]);
            break;
        }

        index = index + 1;
    } while (index < 10);

    return 0;
}
    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    MyArray DWORD 56,78,33,45,78,90,32,15,56,67
    count DWORD ?
    
    szFmt BYTE 'array[1] => %d array[2] => %d ',0dh,0ah,0
.code
    main PROC
        mov dword ptr ds:[count],0               ; int index = 0;
    L1:
        mov eax,dword ptr ds:[count]
        cmp dword ptr ds:[MyArray + eax * 4],10  ; Array[index] > 10
        jle L2
        
        mov eax,dword ptr ds:[count]
        add eax,1
        cmp dword ptr ds:[MyArray + eax * 4],20  ; Array[index + 1] <= 20
        jg L2
        
        mov esi,dword ptr ds:[MyArray + eax * 4 - 4]   ; esi = Array[index]
        mov edi,dword ptr ds:[MyArray + eax * 4]       ; edi = Array[index+1]
        invoke crt_printf,addr szFmt,esi,edi
        jmp lop_end                                    ; break

    L2:    mov eax,dword ptr ds:[count]
        add eax,1                                 ; index = index + 1;
        mov dword ptr ds:[count],eax
        cmp dword ptr ds:[count],10               ; index < 10
        jl L1

    lop_end:                                          ; break
        int 3
    main ENDP
END main

For循环多重IF判断: 在循环中我们首先判断两个数组中元素是否大于0,大于则执行加法运算,然后输出基数或偶数.

#include <stdio.h>
#include <Windows.h>

int main(int argc, char *argv[])
{
    int SrcArray[10] = { 56,78,33,45,78,90,32,15,56,67 };
    int DstArray[10] = { 59,77,89,23,11,45,67,88,93,27 };
    int index = 0;

    for (index = 0; index < 10; index++)
    {
        if (SrcArray[index] != 0 && DstArray[index] != 0)
        {
            int sum = SrcArray[index] + DstArray[index];
            if (sum % 2 == 0)
                printf("偶数: %d \n", sum);
            else
                printf("基数: %d \n", sum);
        }
    }
    system("pause");
    return 0;
}

思考了一会,花费了一些时间,但还是使用汇编完成了.

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    SrcArray DWORD 56,78,33,45,78,90,32,15,56,67
    DstArray DWORD 59,77,89,23,11,45,67,88,93,27
    index DWORD 0
    sum DWORD 0
    
    szFmt1 BYTE '基数: %d ',0dh,0ah,0
    szFmt2 BYTE '偶数: %d ',0dh,0ah,0
.code
    main PROC
        mov dword ptr ds:[index],0        ; index = 0
        
        jmp L1
    L2:    mov eax,dword ptr ds:[index]
        add eax,1                         ; index++
        mov dword ptr ds:[index],eax
    L1:
        cmp dword ptr ds:[index],10       ; index < 10
        jge lop_end
        
        mov eax,dword ptr ds:[index];
        cmp dword ptr ds:[SrcArray + eax * 4],0
        je L2                                     ; SrcArray[index] != 0
        
        mov eax,dword ptr ds:[index]
        cmp dword ptr ds:[DstArray + eax * 4],0   ; DstArray[index] != 0
        je L2
        
        ; ------------------------------------------
        ; 另类加法,通过一个SrcArray定位DstArray完成加法
        
        mov eax,dword ptr ds:[index]                 ; 获取因子
        lea esi,dword ptr ds:[SrcArray]              ; 取数组首地址
        
        mov ebx,dword ptr ds:[esi + eax * 4]         ; 获取 SrcArray[index]
        mov ecx,dword ptr ds:[esi + eax * 4 + 40]    ; 获取 DstArray[index]
        add ebx,ecx                                  ; SrcArray[index] + DstArray[index]
        mov dword ptr ds:[sum],ebx                   ; sum = SrcArray[index] + DstArray[index]
        
        mov eax,dword ptr ds:[sum]
        and eax,080000001h                           ; sum % 2 == 0
        test eax,eax
        jne L3
        
        invoke crt_printf,addr szFmt2,dword ptr ds:[sum]  ; 偶数输出
        jmp L2
    L3:
        invoke crt_printf,addr szFmt1,dword ptr ds:[sum]  ; 基数输出
        jmp L2
    lop_end:
        int 3

    main ENDP
END main

For语句嵌套(乘法口诀表): 首先我们来接触一下For循环的嵌套实现方法,以打印99表为例,尝试使用汇编实现.

#include <stdio.h>
#include <Windows.h>

int main(int argc, char *argv[])
{
    for (int x = 1; x < 10; x++)
    {
        for (int y = 1; y <= x; y++)
        {
            int result = x*y;
            printf("%d*%d=%-3d", y, x, result);
        }
        printf("\n");
    }
    system("pause");
    return 0;
}

执行双层循环需要嵌套For语句,先来写一个简单的双层For循环的汇编版.

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    x DWORD ?
    y DWORD ?
    szFmt BYTE '内层循环: %d 外层循环: %d ',0dh,0ah,0
    szPr  BYTE '----->',0dh,0ah,0
.code
    main PROC
        mov dword ptr ds:[x],1        ; int x = 1
        jmp L1
    L2:    mov eax,dword ptr ds:[x]
        add eax,1                     ; x++
        mov dword ptr ds:[x],eax
    L1:    
        cmp dword ptr ds:[x],10       ; x < 10
        jge lop_end

        mov dword ptr ds:[y],1        ; y = 1
        jmp L3
    L5:    mov eax,dword ptr ds:[y]
        add eax,1                     ; y++
        mov dword ptr ds:[y],eax
    L3:
        mov eax,dword ptr ds:[y]
        cmp eax,dword ptr ds:[x]      ; y <= x
        jg L4
        
        ; 执行的是循环体内部
        mov eax,dword ptr ds:[x]
        mov ebx,dword ptr ds:[y]
        invoke crt_printf,addr szFmt,eax,ebx
        
        jmp L5
    L4:
        ; 执行外层循环
        invoke crt_printf,addr szPr

        jmp L2
    lop_end:
        int 3

    main ENDP
END main

最终实现只是相应的做一个替换即可.

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    x DWORD ?
    y DWORD ?
    szFmt BYTE '%d * %d = %d ',0
    szPr  BYTE ' ',0dh,0ah,0
.code
    main PROC
        mov dword ptr ds:[x],1        ; int x = 1
        jmp L1
    L2:    mov eax,dword ptr ds:[x]
        add eax,1                     ; x++
        mov dword ptr ds:[x],eax
    L1:    
        cmp dword ptr ds:[x],10       ; x < 10
        jge lop_end

        mov dword ptr ds:[y],1        ; y = 1
        jmp L3
    L5:    mov eax,dword ptr ds:[y]
        add eax,1                     ; y++
        mov dword ptr ds:[y],eax
    L3:
        mov eax,dword ptr ds:[y]
        cmp eax,dword ptr ds:[x]      ; y <= x
        jg L4
        
        ; 执行的是循环体内部
        mov eax,dword ptr ds:[x]
        imul eax,dword ptr ds:[y]
        invoke crt_printf,addr szFmt,dword ptr ds:[y],dword ptr ds:[x],eax
        
        jmp L5
    L4:
        ; 执行外层循环
        invoke crt_printf,addr szPr

        jmp L2
    lop_end:
        int 3

    main ENDP
END main

For简单循环(水仙花数): 所谓水仙花数是指一个三位数,其各位数字立方和等于该数本身.

例如: 153是一个水仙花数,因为153=1的三次方+5的三次方+3的三次方.
分析: 利用for循环控制100-999个数,每个数分解出个位,十位,百位.
#include <stdio.h>
#include <Windows.h>

int main(int argc, char *argv[])
{
    int x, y, z, n;
    for (n = 100; n < 1000; n++)
    {
        x = n / 100;
        y = n / 10 % 10;
        z = n % 10;
        if (x * 100 + y * 10 + z == x*x*x + y*y*y + z*z*z)
        {
            printf("水仙花: %-5d \n", n);
        }
    }

    system("pause");
    return 0;
}

尝试使用汇编实现计算逻辑.

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    x DWORD ?
    y DWORD ?
    z DWORD ?
    n DWORD ?
    szFmt BYTE '水仙花: %-5d ',0dh,0ah,0
.code
    main PROC
        mov dword ptr ds:[n],100     ; n = 100
        jmp L1
    L2:    mov eax,dword ptr ds:[n]
        add eax,1                    ; n++
        mov dword ptr ds:[n],eax
    L1:    mov eax,dword ptr ds:[n]
        cmp eax,1000                 ; n < 1000
        jge lop_end
        
        mov eax,dword ptr ds:[n]
        cdq
        mov ecx,100                  ; x = n / 100;
        idiv ecx
        mov dword ptr ds:[x],eax
        
        mov eax,dword ptr ds:[n]
        cdq
        mov ecx,10
        idiv ecx                     ; y = n / 10;
        cdq
        mov ecx,10
        idiv ecx                     ; y = y % 10;
        mov dword ptr ds:[y],edx
        
        mov eax,dword ptr ds:[n]
        cdq
        mov ecx,10
        idiv ecx                     ; z = n % 10;
        mov dword ptr ds:[z],edx
        
        ; 开始执行if()比较语句
        imul eax,dword ptr ds:[x],100  ; x * 100
        imul ecx,dword ptr ds:[y],10   ; y * 10
        add eax,dword ptr ds:[z]       ; + z
        add ecx,eax
        
        mov edx,dword ptr ds:[x]
        imul edx,dword ptr ds:[x]      ; x*x*x
        imul edx,dword ptr ds:[x]
        
        mov eax,dword ptr ds:[y]
        imul eax,dword ptr ds:[y]      ; y*y*y
        imul eax,dword ptr ds:[y]
        add edx,eax
        
        mov eax,dword ptr ds:[z]
        imul eax,dword ptr ds:[z]      ; z*z*z
        imul eax,dword ptr ds:[z]
        add edx,eax
        
        cmp ecx,edx   ; (x * 100 + y * 10 + z) == (x*x*x + y*y*y + z*z*z)
        jne L2
        
        mov eax,dword ptr ds:[n]
        invoke crt_printf,addr szFmt,eax
        jmp L2
        
    lop_end:
        int 3    

    main ENDP
END main

For语句嵌套(冒泡排序): 冒泡排序实现思路从后向前,大的数下沉小的数上移,C代码如下,尝试使用汇编实现.

#include <stdio.h>
#include <Windows.h>

int main(int argc, char *argv[])
{
    int Array[10] = { 34,78,65,77,89,43,23,55,67,8 };
    int x, y, temporary, ArraySize=10;

    for (x = 0; x < ArraySize - 1; x++)
    {
        for (y = ArraySize - 1; y > x; y--)
        {
            if (Array[y - 1] > Array[y])
            {
                temporary = Array[y - 1];
                Array[y - 1] = Array[y];
                Array[y] = temporary;
            }
        }
    }

    for (int x = 0; x < 10; x++)
    {
        printf("%d \n", Array[x]);
    
    system("pause");
    return 0;
}

未完待续

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    Array DWORD 34,78,65,77,89,43,23,55,67,8
    x DWORD ?
    y DWORD ?
    Temporary DWORD ?
    ArraySize DWORD ?
.code
    main PROC
        
        mov dword ptr ds:[x],0            ; x=0
        mov dword ptr ds:[ArraySize],10   ; ArraySize=10
        
        jmp L1
    L2:    mov eax,dword ptr ds:[x]
        add eax,1                          ; x++
        mov dword ptr ds:[x],eax
        
    L1:    mov eax,dword ptr ds:[ArraySize]
        sub eax,1                          ; x < ArraySize - 1
        cmp dword ptr ds:[x],eax
        jge lop_end
        
        ; 内层循环体内容
    L4:    mov eax,dword ptr ds:[ArraySize]
        sub eax,1                          ; y = ArraySize - 1
        mov dword ptr ds:[y],eax
        jmp L3
        
        mov eax,dword ptr ds:[y]
        sub eax,1                          ; y--
        mov dword ptr ds:[y],eax
    L3:
        mov eax,dword ptr ds:[y]
        cmp eax,dword ptr ds:[x]            ; y > x
        jle L2
        

        mov ecx,dword ptr ds:[y]
        mov eax,dword ptr ds:[Array + ecx * 4]  ; y
        sub ecx,1
        mov ebx,dword ptr ds:[Array + ecx * 4]  ; x
        
        xchg eax,ebx
        
        mov dword ptr ds:[Array + ecx * 4],eax
        add ecx,1
        mov dword ptr ds:[Array + ecx * 4],ebx


        jmp L4
        jmp L2
    lop_end:
        int 3

    main ENDP
END main

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    Array DWORD 34,78,65,77,89,43,23,55,67,8
    x DWORD ?
    y DWORD ?
    Temporary DWORD ?
    ArraySize DWORD ?
    szFmt BYTE '%d --> %d ',0dh,0ah,0
.code
    main PROC
        ; 初始化的部分
        mov dword ptr ds:[x],0            ; x=0
        mov dword ptr ds:[ArraySize],10   ; ArraySize=10
        
        ; 外层循环体
        jmp L1
    L2:    mov eax,dword ptr ds:[x]
        add eax,1                          ; x++
        mov dword ptr ds:[x],eax
        
    L1:    mov eax,dword ptr ds:[ArraySize]
        sub eax,1                          ; ArraySize - 1
        cmp dword ptr ds:[x],eax           ; x < ArraySize
        jge lop_end
        
        ; 内层循环体内容
        mov eax,dword ptr ds:[ArraySize]
        sub eax,1
        mov dword ptr ds:[y],eax
        
        jmp L3
    L4:    mov eax,dword ptr ds:[y]
        sub eax,1                           ; y--
        mov dword ptr ds:[y],eax
    
    L3:    mov eax,dword ptr ds:[y]
        cmp eax,dword ptr ds:[x]
        jle L2
        
        mov esi,dword ptr ds:[y]

        mov ebx,dword ptr ds:[Array + esi * 4]         ; Array[y]
        mov edx,dword ptr ds:[Array + esi * 4 - 4]     ; Array[y - 1]
        cmp edx,ebx
        jle L4
        
        mov dword ptr ds:[Array + esi * 4],edx
        mov dword ptr ds:[Array + esi * 4 - 4],ebx
    
        ; invoke crt_printf,addr szFmt,ebx,edx
        
        jmp L4
        jmp L2

    lop_end:
        int 3
    main ENDP
END main

排序完成

完整代码已经写出来了,如下所示.

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    Array DWORD 34,78,65,77,89,43,23,55,67,8
    x DWORD ?
    y DWORD ?
    Temporary DWORD ?
    ArraySize DWORD ?
    szFmt BYTE '%d --> %d ',0dh,0ah,0
.code
    main PROC
        ; 初始化的部分
        mov dword ptr ds:[x],0            ; x=0
        mov dword ptr ds:[ArraySize],10   ; ArraySize=10
        
        ; 外层循环体
        jmp L1
    L2:    mov eax,dword ptr ds:[x]
        add eax,1                          ; x++
        mov dword ptr ds:[x],eax
        
    L1:    mov eax,dword ptr ds:[ArraySize]
        sub eax,1                          ; ArraySize - 1
        cmp dword ptr ds:[x],eax           ; x < ArraySize
        jge lop_end
        
        ; 内层循环体内容
        mov eax,dword ptr ds:[ArraySize]
        sub eax,1
        mov dword ptr ds:[y],eax
        
        jmp L3
    L4:    mov eax,dword ptr ds:[y]
        sub eax,1                           ; y--
        mov dword ptr ds:[y],eax
    
    L3:    mov eax,dword ptr ds:[y]
        cmp eax,dword ptr ds:[x]            ; Array[y - 1] > Array[y]
        jle L2
        
        mov esi,dword ptr ds:[y]

        mov ebx,dword ptr ds:[Array + esi * 4]         ; Array[y]
        mov edx,dword ptr ds:[Array + esi * 4 - 4]     ; Array[y - 1]
        cmp edx,ebx
        jle L4
        
        mov dword ptr ds:[Array + esi * 4],edx         ; Array[y] = Array[y - 1]
        mov dword ptr ds:[Array + esi * 4 - 4],ebx     ; Array[y - 1] = Array[y]
        ; invoke crt_printf,addr szFmt,ebx,edx
        
        jmp L4
        jmp L2

    lop_end:
        nop

        ; 执行打印函数
        mov dword ptr ds:[Temporary],0

        jmp L5
    L7:    mov eax,dword ptr ds:[Temporary]
        add eax,1
        mov dword ptr ds:[Temporary],eax
    L5:
        mov eax,dword ptr ds:[Temporary]
        cmp eax,10
        jge L6
        
        lea esi,dword ptr ds:[Array]                ; 取数组基地址
        mov esi,dword ptr ds:[Array + eax * 4]      ; 比例因子寻址
        invoke crt_printf,addr szFmt,esi,esi
        jmp L7
    L6:
        int 3

    main ENDP
END main

先看排序部分

接着是输出部分

While 三层嵌套: 在写三层嵌套时,你需要注意了,在内层循环时需要清空变量,不然会导致循环一次整个程序结束掉了,就像下面的这种写法,乍一看没啥问题,可是一旦运行就会发现,程序每次都只运行外层一次循环就意外终止了,经过反汇编调试发现,是粗心导致没有让内层循环及时的置空。

#include <windows.h>
#include <stdio.h>

int main(int argc,char * argv[])
{
    int x=1, y=1, z=1;

    while (x < 5)
    {
        while (y < 5)
        {
            while (z < 5)
            {
                z = z + 1;
            }
            y = y + 1;
        }
        x = x + 1;
    }
    return 0;
}

来一个案例看看,使用三层嵌套完成案例,有1,2,3,4个数字,能组成多少个互补相同且不重复的三位数,尝试使用汇编实现以下这个逻辑。

#include <windows.h>
#include <stdio.h>

int main(int argc,char * argv[])
{
    int x=1, y=1, z=1;

    while (x < 5)
    {
        while (y < 5)
        {
            while (z < 5)
            {
                
                if (x != z && x != y && y != z)
                {
                    printf("%d,%d,%d \n", x, y, z);
                }
                z = z + 1;
            }
            z = 1;
            y = y + 1;
        }
        y = 1;
        x = x + 1;
    }
    return 0;
}

首先我们先来构建一个双层循环,然后再构建三层的.

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    x DWORD ?
    y DWORD ?
    szFmt BYTE '外层循环: %d ---> 内层循环:%d ',0dh,0ah,0
.code
    main PROC
        mov dword ptr ds:[x],1           ; x = 1
    
        ; 外层循环
    L1:    mov ecx,dword ptr ds:[x]
        cmp ecx,5                        ; x < 5
        jge lop_end
        
        ; 内层循环
        mov dword ptr ds:[y],1           ; y = 1
    L2:    mov ecx,dword ptr ds:[y]         ; ecx = y
        cmp ecx,5                        ; y < 5
        jge L3
        
        ; 循环过程执行
        mov esi,dword ptr ds:[x]
        mov edi,dword ptr ds:[y]
        invoke crt_printf,addr szFmt,esi,edi

        mov ecx,dword ptr ds:[y]
        add ecx,1                        ; y = y + 1
        mov dword ptr ds:[y],ecx
        jmp L2

    L3:    mov ecx,dword ptr ds:[x]
        add ecx,1                        ; x = x + 1
        mov dword ptr ds:[x],ecx
        jmp L1
    
    lop_end:
        int 3    

    main ENDP
END main

接着是构建一个三层循环体,三层循环体就像汉堡一样,前面初始化部分时面包,中间时肉,后面也是面包,放在一起,很有食欲。

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    x DWORD ?
    y DWORD ?
    z DWORD ?
    szFmt BYTE '外层循环: %d ---> 中间层循环: %d ---> 内层循环: %d  ',0dh,0ah,0
.code
    main PROC
        mov dword ptr ds:[x],1           ; x = 1
    
        ; 外层循环
    L1:    mov ecx,dword ptr ds:[x]
        cmp ecx,5                        ; x < 5
        jge lop_end
        
        ; 中间循环
        mov dword ptr ds:[y],1           ; y = 1
    L2:    mov ecx,dword ptr ds:[y]         ; ecx = y
        cmp ecx,5                        ; y < 5
        jge L3                           ; 大于跳到最外层

        ; 内层循环
        mov dword ptr ds:[z],1           ; z = 1
        
    L5:    mov ecx,dword ptr ds:[z]
        cmp ecx,5                        ; z < 5
        jge L4                           ; 大于跳到中间层
        
        ; 三层循环框架
        mov eax,dword ptr ds:[x]
        mov ebx,dword ptr ds:[y]
        mov ecx,dword ptr ds:[z]
        invoke crt_printf,addr szFmt,eax,ebx,ecx
        
        mov ecx,dword ptr ds:[z]
        add ecx,1                         ; z = z + 1
        mov dword ptr ds:[z],ecx
        
        jmp L5
        
    L4:    mov ecx,dword ptr ds:[y]
        add ecx,1                        ; y = y + 1
        mov dword ptr ds:[y],ecx
        jmp L2

    L3:    mov ecx,dword ptr ds:[x]
        add ecx,1                        ; x = x + 1
        mov dword ptr ds:[x],ecx
        jmp L1
    
    lop_end:
        int 3    

    main ENDP
END main

中间的IF语句,就是汉堡包的佐料部分,肉质丝滑,入口即化,实在是美妙至极,如下时肉质部分。

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    x DWORD ?
    y DWORD ?
    z DWORD ?
    szFmt BYTE '%d,%d,%d ',0dh,0ah,0
.code
    main PROC
        mov dword ptr ds:[x],1           ; x = 1
    
        ; 外层循环
    L1:    mov ecx,dword ptr ds:[x]
        cmp ecx,5                        ; x < 5
        jge lop_end
        
        ; 中间循环
        mov dword ptr ds:[y],1           ; y = 1
    L2:    mov ecx,dword ptr ds:[y]         ; ecx = y
        cmp ecx,5                        ; y < 5
        jge L3                           ; 大于跳到最外层

        ; 内层循环
        mov dword ptr ds:[z],1           ; z = 1
        
    L5:    mov ecx,dword ptr ds:[z]
        cmp ecx,5                        ; z < 5
        jge L4                           ; 大于跳到中间层
        
        ; 三层循环框架
        ;mov eax,dword ptr ds:[x]
        ;mov ebx,dword ptr ds:[y]
        ;mov ecx,dword ptr ds:[z]
        ;invoke crt_printf,addr szFmt,eax,ebx,ecx
        
        ; 开始在框架中搞事情
        mov eax,dword ptr ds:[x]
        cmp eax,dword ptr ds:[z]
        je L6
        mov eax,dword ptr ds:[x]
        cmp eax,dword ptr ds:[y]
        je L6
        mov eax,dword ptr ds:[y]
        cmp eax,dword ptr ds:[z]
        je L6
        
        invoke crt_printf,addr szFmt,dword ptr ds:[x],dword ptr ds:[y],dword ptr ds:[z]
        
    L6:    mov ecx,dword ptr ds:[z]
        add ecx,1                         ; z = z + 1
        mov dword ptr ds:[z],ecx
        
        jmp L5
        
    L4:    mov ecx,dword ptr ds:[y]
        add ecx,1                        ; y = y + 1
        mov dword ptr ds:[y],ecx
        jmp L2

    L3:    mov ecx,dword ptr ds:[x]
        add ecx,1                        ; x = x + 1
        mov dword ptr ds:[x],ecx
        jmp L1
    
    lop_end:
        int 3    

    main ENDP
END main

Switch与循环: Switch语句与IF语句类似,不同之处就在于Switch是将跳转地址保存在数组中,需要时去数组中通过比例因子寻找到指定的内存然后,使用一条Jmp指令跳转过去,实在美妙!

先给大家看一下,我是怎吗保存这些地址的吧,汇编代码如下所示,直接取出标号,放入数组中,也可以使用堆栈存储,随意。

    .386p
    .model flat,stdcall
    option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    MemArray DWORD ?

    szFmt BYTE '%d,%d,%d ',0dh,0ah,0
.code
    main PROC
    mov dword ptr ds:[MemArray],offset lop_end  
    mov dword ptr ds:[MemArray+4],offset L2
    
    lop_end:
        int 3    
    L2:
        xor eax,eax
        xor ebx,ebx
    main ENDP
END main

尝试构建Switch语句。

#include <windows.h>
#include <stdio.h>

int main(int argc, char * argv[])
{
    int x = 0;
    while (x < 6)
    {
        switch (x)
        {
        case 0:
            printf("1"); break;
        case 1:
            printf("2"); break;
        case 2:
            printf("3"); break;
        case 3:
            printf("4"); break;
        case 4:
            printf("5"); break;
        default:
            printf("0"); break;
        }
        x = x + 1;
    }
    return 0;
}

理解了Switch的查表法,然后再配合汇编中的语法规范就可以巧妙地构造出Switch结构.

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    MemArray DWORD 0,0,0,0,0,0,0,0,0,0
    Count DWORD ?
    szFmt BYTE '%d ',0dh,0ah,0
.code
    main PROC
        
        ; 将指定标号的地址取出,并复制到数组
        mov dword ptr ds:[MemArray],offset S0
        mov dword ptr ds:[MemArray + 4],offset S1
        mov dword ptr ds:[MemArray + 8],offset S2
        mov dword ptr ds:[MemArray + 12],offset S3
        mov dword ptr ds:[MemArray + 16],offset S4
        mov dword ptr ds:[MemArray + 20],offset S_END
    
        mov dword ptr ds:[Count],0
        
    L1:    mov ecx,dword ptr ds:[Count]
        cmp ecx,6
        jg lop_end
        
        ; 通过循环次数寻找指令地址并跳转
        mov ecx,dword ptr ds:[Count]
        cmp ecx,6
        jg S_END
        jmp dword ptr ds:[MemArray + ecx * 4]
        
    S0:    mov eax,1
        invoke crt_printf,addr szFmt,eax
        jmp L2

    S1:    mov eax,2
        invoke crt_printf,addr szFmt,eax
        jmp L2
    
    S2:    mov eax,3
        invoke crt_printf,addr szFmt,eax
        jmp L2
        
    S3:    mov eax,4
        invoke crt_printf,addr szFmt,eax
        jmp L2
        
    S4:    mov eax,5
        invoke crt_printf,addr szFmt,eax
        jmp L2
    
    S_END:    mov eax,0
        invoke crt_printf,addr szFmt,eax
        jmp L2

    L2:    mov ecx,dword ptr ds:[Count]
        add ecx,1                        ; ecx ++
        mov dword ptr ds:[Count],ecx
        jmp L1
        
    lop_end:
        int 3    
    main ENDP
END main

Loop实现排序: 如果不自己构建排序循环,使用loop实现,则冒泡排序将变得香。

先来看一个汇编案例,我想说,观察下面的代码,你说 这是不是一个死循环呢?思考一下。

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    Count DWORD 10
.code
    main PROC
        mov ecx,dword ptr ds:[Count]
        dec ecx
    L1:    push ecx

        invoke crt_printf,addr szFmt,ecx
        pop ecx    
        loop L1

    main ENDP
END main

不是,loop人家执行的时候,会自动的将ecx中的值减去1,所以他不是死循环,来实现一下这个需求。

#include <windows.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    int Array[10] = { 56,88,34,67,98,25,67,10,87,43 };
    int x=10, y, temporary;

    while (x - 1 > 0)
    {
        y = x;
        while (y > 0)
        {
            if (Array[y] > Array[y - 1])
            {
                temporary = Array[y - 1];
                Array[y - 1] = Array[y];
                Array[y] = temporary;
            }
            y--;
        }
        x--;
    }

    for (int x = 0; x < 10; x++)
        printf("%d ", Array[x]);
    return 0;
}

然后使用loop实现双层夹心汉堡,口感同样一级棒.

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    MyArray DWORD 25,74,89,33,24,67,93,15,78,92
    Count DWORD 10
    PrCount DWORD ?
    szFmt BYTE '%d ',0dh,0ah,0

.code
    main PROC
        ; 数组排序
        mov ecx,dword ptr ds:[Count]      ; 获取到数组元素数
        dec ecx                           ; 数组减1
    L1:    push ecx                          ; 入栈保存
        
        lea esi,dword ptr ds:[MyArray]    ; 得到数组基地址
        
    L2:    mov eax,dword ptr ds:[esi]
        cmp eax,dword ptr ds:[esi + 4]    ; 比较第一个数组与第二个
        jg L3
        
        xchg eax,[esi + 4]                ; 交换数据
        mov [esi],eax

    L3:    add esi,4
        loop L2
        
        pop ecx                           ; 弹出数据
        loop L1
        
        ; for循环输出元素
        mov dword ptr ds:[PrCount],0
        
        jmp LL1
        mov ecx,dword ptr ds:[PrCount]
        add ecx,1
        mov dword ptr ds:[PrCount],ecx
    LL1:
        mov ecx,dword ptr ds:[PrCount]
        cmp ecx,10
        jg lop_end
        
        lea eax,dword ptr ds:[MyArray]
        mov ebx,dword ptr ds:[eax + ecx * 4]
        invoke crt_printf,addr szFmt,ebx
        
        mov ecx,dword ptr ds:[PrCount]
        add ecx,1
        mov dword ptr ds:[PrCount],ecx
        jmp LL1
        
    lop_end:
        int 3

    main ENDP
END main

While 实现(二分法): 二分查找法也是常用查找结构,主要思想是对半分,如果中位数大于则说明元素在前半部分,如果小于则说明在后半部分,该排序唯一需要注意的是,数组必须是一个有序集合.

#include <windows.h>
#include <stdio.h>

int BinSearch(int value[], const int searchVal, int Count)
{
    int first = 0;
    int last = Count - 1;

    while (first <= last)
    {
        int mid = (last + first) / 2;      // 取中位数
        if (value[mid] < searchVal)        // 中位数小于searchVal
        {                                  // 说明元素在后面
            first = mid + 1;
        }
        else if (value[mid] > searchVal)
        {                                  // 否则说明元素在前
            last = mid - 1;
        }
        else
        { // 找到后返回中位数
            return mid;
        }
    }
    return -1;
}

int main(int argc, char *argv[])
{
    // 二分查找法,必须针对的是有序数组
    int Array[10] = { 1,2,3,4,5,6,7,8,9,10 };

    // 查找数组Array中索引7所在的下标
    int ret = BinSearch(Array, 7, 10);
    printf("数组下标: %d \n", ret);

    system("pause");
    return 0;
}

接着是尝试使用汇编实现这个查找逻辑,完整版代码已经写好了

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

include msvcrt.inc
includelib msvcrt.lib

.data
    MyArray DWORD 1,2,3,4,5,6,7,8,9,10
    
    SearchVal DWORD 7
    Count DWORD 10
    
    first DWORD ?
    last DWORD ?
    mid DWORD ?
    
    szFmt BYTE '%d ',0dh,0ah,0

.code
    main PROC
        mov dword ptr ds:[first],0         ; first = 0;
        mov edi,dword ptr ds:[SearchVal]   ; 得到要查找的数
        lea ebx,dword ptr ds:[MyArray]     ; 得到数组基地址

        ; int last = Count - 1;
        mov eax,dword ptr ds:[Count]
        sub eax,1
        mov dword ptr ds:[last],eax
        
        ; while(first <=last)
    L1:    mov ecx,dword ptr ds:[first]
        cmp ecx,dword ptr ds:[last]
        jg lop_end
        
        ; int mid = (last + first) / 2;
        mov eax,dword ptr ds:[last]
        add eax,dword ptr ds:[first]
        shr eax,1
        mov dword ptr ds:[mid],eax
        
        ; edx = value[mid]
        mov esi,dword ptr ds:[mid]
        shl esi,2
        mov edx,[ebx + esi]
        ;invoke crt_printf,addr szFmt,edx

        ; if(edx < SearchVal(edi))
        cmp edx,edi
        jge L2
        ; first = mid + 1;
        mov eax,dword ptr ds:[mid]
        add eax,1
        mov dword ptr ds:[first],eax
        jmp L1
    L2:
        ; else if (value[mid] > searchVal)
        cmp edx,edi
        jle L3
        ; last = mid - 1;
        mov eax,dword ptr ds:[mid]
        sub eax,1
        mov dword ptr ds:[last],eax
        jmp L1
    
    L3:    ; else
        mov eax,dword ptr ds:[mid]
        invoke crt_printf,addr szFmt,eax
        jmp lop_end
        jmp L1
    lop_end:
        mov eax,-1
        int 3

    main ENDP
END main

目录
相关文章
|
数据安全/隐私保护
Win32汇编:算术与伪指令
每种汇编语言都有进行操作数移位的指令,移位和循环移位指令在控制硬件设备,加密数据,以及实现高速图形运算时特别有用,移位指令也是汇编语言中最具特征的指令集,`移位(Shifting)`的含义是在操作数内向左或向右移动数据位,Intel处理器提供了多种移位指令,具体如下表所示:
244 0
|
存储 编译器 API
Win32汇编:过程与宏调用
在计算机领域,堆栈是一个不容忽视的概念,堆栈是一种`后进先出(LIFO,Last-In,First-Out)`的数据结构,这是因为最后压入堆栈的值总是最先被取出,而新数值在执行PUSH压栈时总是被加到堆栈的最顶端,数据也总是从堆栈的最顶端被取出,堆栈是个`特殊的存储区`,主要功能是暂时存放数据和地址,通常用来保护断点和现场.
89 0
|
存储 编译器
Win32汇编:算数运算指令总结
汇编中常用的运算符,加减乘除等,另外包括了移位运算等,移位又分为,算数移位,逻辑移位,循环移位,双精度移位等。
113 0
|
存储 编译器
Win32汇编:数组与标志位测试总结
整理复习汇编语言的知识点,以前在学习《Intel汇编语言程序设计 - 第五版》时没有很认真的整理笔记,主要因为当时是以学习理解为目的没有整理的很详细,这次是我第三次阅读此书,每一次阅读都会有新的收获,这次复习,我想把书中的重点,再一次做一个归纳与总结(注:16位汇编部分跳过),并且继续尝试写一些有趣的案例,这些案例中所涉及的指令都是逆向中的重点,一些不重要的我就直接省略了,一来提高自己,二来分享知识,转载请加出处,敲代码备注挺难受的。
224 0
|
存储 编译器 C语言
Win32汇编:字符串浮点数运算过程
整理复习汇编语言的知识点,以前在学习《Intel汇编语言程序设计 - 第五版》时没有很认真的整理笔记,主要因为当时是以学习理解为目的没有整理的很详细,这次是我第三次阅读此书,每一次阅读都会有新的收获,这次复习,我想把书中的重点,再一次做一个归纳与总结(注:16位汇编部分跳过),并且继续尝试写一些有趣的案例,这些案例中所涉及的指令都是逆向中的重点,一些不重要的我就直接省略了,一来提高自己,二来分享知识,转载请加出处,敲代码备注挺难受的。
232 0
Win32汇编:字符串浮点数运算过程
|
安全 数据安全/隐私保护 Windows
Win32汇编:汇编版PE结构解析器
PE格式是Windows系统下最常用的可执行文件格式,有些应用必须建立在了解PE文件格式的基础之上,如可执行文件的加密与解密,文件型病毒的查杀等,熟练掌握PE文件结构,有助于软件的分析.
193 0
|
编译器 C++
Win32汇编:仿写多态与虚函数
多态性是面向对象的重要组成部分,利用多态可以设计和实现易于扩展的程序,所谓多态就是一个类函数有多重形态,具有不同功能的函数可以用同一个函数名,实现使用一个函数名调用不同内容的函数,从而返回不同的结果,这就是多态性,多态离不开虚函数的支撑,以下案例本人将深度分析虚函数实现机制,并通过汇编实现虚函数机制。
171 0