Win32汇编:各种语句的构造方式
整理复习汇编语言的知识点,以前在学习《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,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
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,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
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,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
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,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]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
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,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
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,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 lop_endmov eax,dword ptr ds:[var2]cmp eax,dword ptr ds:[var3] ; and var2 <= var3jle L1mov 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
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,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
以下是人的逻辑方式.
.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 L2L2: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
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");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
编译器是这样干的,我把他的思路写一下。
.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
编译器对于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");elseprintf("xor edx,edx");.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 > 20 ?jle L1 ; 不大于则跳到L1继续判断xor eax,eaxjmp lop_end ; 最后都要跳向结束L1: mov eax,dword ptr ds:[var2]cmp eax,20 ; var1 >= 20 ?jl L2 ; 不大于则继续判断L2xor ebx,ebxjmp lop_endL2: mov eax,dword ptr ds:[var3]cmp eax,20 ; var3 <= 20 ?jg L3 ; 大于则跳到L3xor ecx,ecxjmp lop_endL3: xor edx,edxjmp lop_endlop_end:xor esi,esiinvoke ExitProcess,0main ENDP
END main
IF的前期脑残写法: 写的烂,没编译器生成的代码有趣,垃圾保存。
脑残1
if(var1 > var2) and (var2 < var3)
{xor eax,eax
}else if(var1 > var3)
{xor ebx,ebx
}.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,dword ptr ds:[var2] ; if(var1 > var2)jg L1mov eax,dword ptr ds:[var1]cmp eax,dword ptr ds:[var3] ; else if(var1 > var3)jg L3L1:mov eax,dword ptr ds:[var2] ; if(var2 < var3)cmp eax,dword ptr ds:[var3]jl L2L2:xor eax,eaxjmp lopL3:xor ebx,ebxjmp loplop:nopinvoke ExitProcess,0main ENDP
END main
脑残2
if var1 == var2
{if x > y{xchg x,y}else{x=10y=20}
}else
{var1 = 0var2 = 0
}.386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datax BYTE 6y BYTE 5var1 DWORD 10var2 DWORD 10.codemain PROCmov eax,dword ptr ds:[var1]cmp eax,dword ptr ds:[var2] ; var1 == var2 ?jne L1 ; 不等于跳转到L1mov al,byte ptr ds:[x]cmp al,byte ptr ds:[y] ; x > y ?jg L2 ; 大于跳到L2mov byte ptr ds:[x],0 ; 不大于则执行x=10 y=20mov byte ptr ds:[y],0jmp lopL1:mov dword ptr ds:[var1],0 ; var1 != var2 则执行mov dword ptr ds:[var2],0L2:mov al,byte ptr ds:[x]mov bl,byte ptr ds:[y]xchg al,bl ; x y 数值交换mov byte ptr ds:[x],almov byte ptr ds:[y],bljmp loplop:nopinvoke ExitProcess,0main 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,eaxelsexor ebx,ebx}.386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datavar1 DWORD 20var2 DWORD 10var3 DWORD 50.codemain PROCinc dword ptr ds:[var1] ; var1++mov eax,dword ptr ds:[var1]cmp eax,5 ; var1 > 5 ?jg L1jmp lop_endL1:inc dword ptr ds:[var2] ; var2++mov eax,dword ptr ds:[var2] ; var2 >=10 ?cmp eax,10jge L2jmp lop_endL2:mov eax,dword ptr ds:[var3] ; 获取 var3add eax,10 ; var3 = var3 + 10shl eax,2 ; var3 << 2cmp eax,100jle L3 ; var3 <= 100 ?cmp eax,1000 ; eax orjle L3 ; var3 <= 1000 ?jmp L4 ; elseL3:xor eax,eaxjmp lop_endL4:xor ebx,ebxjmp lop_endlop_end:nopinvoke ExitProcess,0main ENDP
END main
编译器对于此类嵌套出处理结果是这样的,由于and指令左面如果成立则继续执行右面的判断,如果不成立右面的直接掠过,这样的话就比较有趣了,如下是我根据汇编代码推测的一段片段,。
.386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datavar1 DWORD 20var2 DWORD 10var3 DWORD 50tmp DWORD ?flag DWORD ?.codemain PROCmov eax,dword ptr ds:[var1]mov dword ptr ds:[tmp],eax ; 将var1原值备份到tmpmov ecx,dword ptr ds:[var1]add ecx,1 ; 递增var1并会写到变量中mov dword ptr ds:[var1],ecxcmp dword ptr ds:[tmp],5 ; 用原值与5做比较jle L1 ; 如果 var1 < var2mov dword ptr ds:[flag],1jmp L2L1: mov dword ptr ds:[flag],0 ; 判断的是and的第一个等式L2: cmp dword ptr ds:[flag],0je lop_endmov eax,dword ptr ds:[var2]mov dword ptr ds:[tmp],eax ; 备份var2mov ecx,dword ptr ds:[var2]add ecx,1 ; 递增运算++mov dword ptr ds:[var2],ecxcmp dword ptr ds:[tmp],10 ; 判断 var2>=10 ?jl L3 ; 不大于则跳到L3mov dword dword ptr ds:[flag],1 ; 大于则标志flag=1jmp L4L3: mov dword ptr ds:[flag],0L4: cmp dword ptr ds:[flag],0je lop_end ; 不跳转则执行内部ifmov eax,dword ptr ds:[var3]add eax,10mov dword ptr ds:[var3],eax ; 递增var3mov eax,dword ptr ds:[var3]shl eax,2mov dword ptr ds:[var3],eax ; var3 = var3 << 2cmp dword ptr ds:[var3],100 ; var3 <= 100jle L5cmp dword ptr ds:[var3],1000 ; var3<=1000jg L6 ; 跳转到内层elseL5:xor eax,eaxnopjmp lop_endL6:xor ebx,ebxnopjmp lop_endlop_end:xor eax,eaxinvoke ExitProcess,0main ENDP
END main
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
#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
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,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
第二种如果判断
#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,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
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,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
While /For 语句构建
While/FOr 循环框架: while循环,for循环的简单框架,后期会逐步提高难度,最终实现一个循环链表结构。
.386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datacount DWORD ?.codemain PROCmov 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 S1loop_end:int 3invoke ExitProcess,0main ENDP
END main
再看一下他的好基友,do-while是如何构造的,相比于while,该语句是先执行在判断,从效率上来说这个效率要高于while.
.386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datacount DWORD ?
.codemain PROCmov 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 3invoke ExitProcess,0main ENDP
END main
最后看一个for语句的实现流程,该语句的构建方式相对于While来说略显复杂些,效率远不及While,反汇编后发现,编译器是这样构建的.
.386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datacount DWORD ?
.codemain PROCmov dword ptr ds:[count],0 ; 设置 int x = 0;jmp L2L1: mov eax,dword ptr ds:[count] ; x = x++add eax,1mov dword ptr ds:[count],eaxL2: cmp dword ptr ds:[count],10 ; 比较 x < 10jge lop_endxor eax,eax ; 执行循环体jmp L1lop_end:int 3invoke ExitProcess,0main ENDP
END main
在Python中for循环是for x in range(2,10)
可以指定一个范围,我们接着尝试构建一下.
.386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.lib.datastart_count DWORD ?end_count DWORD ?
.codemain PROCmov 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],ecxjle lop_endxor eax,eax ; 循环体内部add ecx,1 ; 每次递增mov dword ptr ds:[start_count],ecxjmp L1lop_end:int 3invoke ExitProcess,0main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.dataMyArray DWORD 1,2,3,4,5,6,7,8,9,10count DWORD ?szFmt BYTE 'value = %d ',0dh,0ah,0
.codemain PROCmov 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 ; 调用系统crtmov ecx,dword ptr ds:[count]add ecx,1 ; 计次循环递增mov dword ptr ds:[count],ecxjmp S1lop_end:int 3invoke ExitProcess,0main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.dataMyArray DWORD 56,78,33,45,78,90,32,44,56,67count DWORD ?szFmt BYTE 'out -> %d ',0dh,0ah,0
.codemain PROCmov dword ptr ds:[count],0 ; int x = 0jmp L1L2: mov eax,dword ptr ds:[count]add eax,1 ; x ++mov dword ptr ds:[count],eaxL1:cmp dword ptr ds:[count],10 ; x < 10jge lop_endmov eax,dword ptr ds:[count] ; 获取循环次数,当作因子lea esi,dword ptr ds:[MyArray] ; 取数组基地址mov ebx,dword ptr ds:[esi + eax * 4] ; 因子寻址cmp ebx,50jl L2 ; 如果小于50则跳转到下一次循环invoke crt_printf,addr szFmt,ebx ; 调用系统crtjmp L2lop_end:int 3invoke ExitProcess,0main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.dataMyArray DWORD 56,78,33,45,78,90,32,44,56,67count DWORD ?max_result DWORD 0min_result DWORD 100sum_result DWORD 0avg_result DWORD 0szFmt BYTE 'max = %d min = %d sum = %d avg = %d ',0dh,0ah,0
.codemain PROCmov dword ptr ds:[count],0 ; int x = 0jmp L1L2: mov eax,dword ptr ds:[count]add eax,1 ; x ++mov dword ptr ds:[count],eaxL1:cmp dword ptr ds:[count],10 ; x < 10jge lop_endmov 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_resultjl L3mov 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_resultjg L4L4:mov ebx,dword ptr ds:[esi + eax * 4]mov edx,dword ptr ds:[sum_result] ; sum_result + Array[x];add ebx,edxmov dword ptr ds:[sum_result],ebx ; sum_resultmov eax,dword ptr ds:[sum_result]cdqmov ecx,10idiv ecx ; sum_result / 10;mov dword ptr ds:[sum_result],eax ; avg_resultjmp L2lop_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,edxint 3invoke ExitProcess,0main ENDP
END main
问题显而易见,相信大家都看出来了,我就直接公布正确代码了
.386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.dataMyArray DWORD 56,78,33,45,78,90,32,44,56,67count DWORD ?max_result DWORD 0min_result DWORD 100sum_result DWORD 0avg_result DWORD 0szFmt BYTE 'max = %d min= %d sum= %d avg = %d ',0dh,0ah,0
.codemain PROCmov dword ptr ds:[count],0 ; int x = 0jmp L1L2: mov eax,dword ptr ds:[count]add eax,1 ; x ++mov dword ptr ds:[count],eaxL1:cmp dword ptr ds:[count],10 ; x < 10jge lop_endmov 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_resultjl L3mov 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_resultjg L4mov dword ptr ds:[min_result],ebxL4: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 ; / 10idiv ecx ; sum_result / 10;mov dword ptr ds:[avg_result],eax ; avg_resultjmp L2lop_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,edxint 3main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.dataMyArray DWORD 56,78,33,45,78,90,32,15,56,67count DWORD ?szFmt BYTE 'array[1] => %d array[2] => %d ',0dh,0ah,0
.codemain PROCmov 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] > 10jle L2mov eax,dword ptr ds:[count]add eax,1cmp dword ptr ds:[MyArray + eax * 4],20 ; Array[index + 1] <= 20jg L2mov 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,edijmp lop_end ; breakL2: mov eax,dword ptr ds:[count]add eax,1 ; index = index + 1;mov dword ptr ds:[count],eaxcmp dword ptr ds:[count],10 ; index < 10jl L1lop_end: ; breakint 3main 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);elseprintf("基数: %d \n", sum);}}system("pause");return 0;
}
思考了一会,花费了一些时间,但还是使用汇编完成了.
.386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.dataSrcArray DWORD 56,78,33,45,78,90,32,15,56,67DstArray DWORD 59,77,89,23,11,45,67,88,93,27index DWORD 0sum DWORD 0szFmt1 BYTE '基数: %d ',0dh,0ah,0szFmt2 BYTE '偶数: %d ',0dh,0ah,0
.codemain PROCmov dword ptr ds:[index],0 ; index = 0jmp L1L2: mov eax,dword ptr ds:[index]add eax,1 ; index++mov dword ptr ds:[index],eaxL1:cmp dword ptr ds:[index],10 ; index < 10jge lop_endmov eax,dword ptr ds:[index];cmp dword ptr ds:[SrcArray + eax * 4],0je L2 ; SrcArray[index] != 0mov eax,dword ptr ds:[index]cmp dword ptr ds:[DstArray + eax * 4],0 ; DstArray[index] != 0je 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 == 0test eax,eaxjne L3invoke crt_printf,addr szFmt2,dword ptr ds:[sum] ; 偶数输出jmp L2L3:invoke crt_printf,addr szFmt1,dword ptr ds:[sum] ; 基数输出jmp L2lop_end:int 3main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.datax DWORD ?y DWORD ?szFmt BYTE '内层循环: %d 外层循环: %d ',0dh,0ah,0szPr BYTE '----->',0dh,0ah,0
.codemain PROCmov dword ptr ds:[x],1 ; int x = 1jmp L1L2: mov eax,dword ptr ds:[x]add eax,1 ; x++mov dword ptr ds:[x],eaxL1: cmp dword ptr ds:[x],10 ; x < 10jge lop_endmov dword ptr ds:[y],1 ; y = 1jmp L3L5: mov eax,dword ptr ds:[y]add eax,1 ; y++mov dword ptr ds:[y],eaxL3:mov eax,dword ptr ds:[y]cmp eax,dword ptr ds:[x] ; y <= xjg L4; 执行的是循环体内部mov eax,dword ptr ds:[x]mov ebx,dword ptr ds:[y]invoke crt_printf,addr szFmt,eax,ebxjmp L5L4:; 执行外层循环invoke crt_printf,addr szPrjmp L2lop_end:int 3main ENDP
END main
最终实现只是相应的做一个替换即可.
.386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.datax DWORD ?y DWORD ?szFmt BYTE '%d * %d = %d ',0szPr BYTE ' ',0dh,0ah,0
.codemain PROCmov dword ptr ds:[x],1 ; int x = 1jmp L1L2: mov eax,dword ptr ds:[x]add eax,1 ; x++mov dword ptr ds:[x],eaxL1: cmp dword ptr ds:[x],10 ; x < 10jge lop_endmov dword ptr ds:[y],1 ; y = 1jmp L3L5: mov eax,dword ptr ds:[y]add eax,1 ; y++mov dword ptr ds:[y],eaxL3:mov eax,dword ptr ds:[y]cmp eax,dword ptr ds:[x] ; y <= xjg 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],eaxjmp L5L4:; 执行外层循环invoke crt_printf,addr szPrjmp L2lop_end:int 3main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.datax DWORD ?y DWORD ?z DWORD ?n DWORD ?szFmt BYTE '水仙花: %-5d ',0dh,0ah,0
.codemain PROCmov dword ptr ds:[n],100 ; n = 100jmp L1L2: mov eax,dword ptr ds:[n]add eax,1 ; n++mov dword ptr ds:[n],eaxL1: mov eax,dword ptr ds:[n]cmp eax,1000 ; n < 1000jge lop_endmov eax,dword ptr ds:[n]cdqmov ecx,100 ; x = n / 100;idiv ecxmov dword ptr ds:[x],eaxmov eax,dword ptr ds:[n]cdqmov ecx,10idiv ecx ; y = n / 10;cdqmov ecx,10idiv ecx ; y = y % 10;mov dword ptr ds:[y],edxmov eax,dword ptr ds:[n]cdqmov ecx,10idiv ecx ; z = n % 10;mov dword ptr ds:[z],edx; 开始执行if()比较语句imul eax,dword ptr ds:[x],100 ; x * 100imul ecx,dword ptr ds:[y],10 ; y * 10add eax,dword ptr ds:[z] ; + zadd ecx,eaxmov edx,dword ptr ds:[x]imul edx,dword ptr ds:[x] ; x*x*ximul edx,dword ptr ds:[x]mov eax,dword ptr ds:[y]imul eax,dword ptr ds:[y] ; y*y*yimul eax,dword ptr ds:[y]add edx,eaxmov eax,dword ptr ds:[z]imul eax,dword ptr ds:[z] ; z*z*zimul eax,dword ptr ds:[z]add edx,eaxcmp ecx,edx ; (x * 100 + y * 10 + z) == (x*x*x + y*y*y + z*z*z)jne L2mov eax,dword ptr ds:[n]invoke crt_printf,addr szFmt,eaxjmp L2lop_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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.dataArray DWORD 34,78,65,77,89,43,23,55,67,8x DWORD ?y DWORD ?Temporary DWORD ?ArraySize DWORD ?
.codemain PROCmov dword ptr ds:[x],0 ; x=0mov dword ptr ds:[ArraySize],10 ; ArraySize=10jmp L1L2: mov eax,dword ptr ds:[x]add eax,1 ; x++mov dword ptr ds:[x],eaxL1: mov eax,dword ptr ds:[ArraySize]sub eax,1 ; x < ArraySize - 1cmp dword ptr ds:[x],eaxjge lop_end; 内层循环体内容L4: mov eax,dword ptr ds:[ArraySize]sub eax,1 ; y = ArraySize - 1mov dword ptr ds:[y],eaxjmp L3mov eax,dword ptr ds:[y]sub eax,1 ; y--mov dword ptr ds:[y],eaxL3:mov eax,dword ptr ds:[y]cmp eax,dword ptr ds:[x] ; y > xjle L2mov ecx,dword ptr ds:[y]mov eax,dword ptr ds:[Array + ecx * 4] ; ysub ecx,1mov ebx,dword ptr ds:[Array + ecx * 4] ; xxchg eax,ebxmov dword ptr ds:[Array + ecx * 4],eaxadd ecx,1mov dword ptr ds:[Array + ecx * 4],ebxjmp L4jmp L2lop_end:int 3main ENDP
END main
.386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.dataArray DWORD 34,78,65,77,89,43,23,55,67,8x DWORD ?y DWORD ?Temporary DWORD ?ArraySize DWORD ?szFmt BYTE '%d --> %d ',0dh,0ah,0
.codemain PROC; 初始化的部分mov dword ptr ds:[x],0 ; x=0mov dword ptr ds:[ArraySize],10 ; ArraySize=10; 外层循环体jmp L1L2: mov eax,dword ptr ds:[x]add eax,1 ; x++mov dword ptr ds:[x],eaxL1: mov eax,dword ptr ds:[ArraySize]sub eax,1 ; ArraySize - 1cmp dword ptr ds:[x],eax ; x < ArraySizejge lop_end; 内层循环体内容mov eax,dword ptr ds:[ArraySize]sub eax,1mov dword ptr ds:[y],eaxjmp L3L4: mov eax,dword ptr ds:[y]sub eax,1 ; y--mov dword ptr ds:[y],eaxL3: mov eax,dword ptr ds:[y]cmp eax,dword ptr ds:[x]jle L2mov 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,ebxjle L4mov dword ptr ds:[Array + esi * 4],edxmov dword ptr ds:[Array + esi * 4 - 4],ebx; invoke crt_printf,addr szFmt,ebx,edxjmp L4jmp L2lop_end:int 3main ENDP
END main
排序完成
完整代码已经写出来了,如下所示.
.386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.dataArray DWORD 34,78,65,77,89,43,23,55,67,8x DWORD ?y DWORD ?Temporary DWORD ?ArraySize DWORD ?szFmt BYTE '%d --> %d ',0dh,0ah,0
.codemain PROC; 初始化的部分mov dword ptr ds:[x],0 ; x=0mov dword ptr ds:[ArraySize],10 ; ArraySize=10; 外层循环体jmp L1L2: mov eax,dword ptr ds:[x]add eax,1 ; x++mov dword ptr ds:[x],eaxL1: mov eax,dword ptr ds:[ArraySize]sub eax,1 ; ArraySize - 1cmp dword ptr ds:[x],eax ; x < ArraySizejge lop_end; 内层循环体内容mov eax,dword ptr ds:[ArraySize]sub eax,1mov dword ptr ds:[y],eaxjmp L3L4: mov eax,dword ptr ds:[y]sub eax,1 ; y--mov dword ptr ds:[y],eaxL3: mov eax,dword ptr ds:[y]cmp eax,dword ptr ds:[x] ; Array[y - 1] > Array[y]jle L2mov 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,ebxjle L4mov 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,edxjmp L4jmp L2lop_end:nop; 执行打印函数mov dword ptr ds:[Temporary],0jmp L5L7: mov eax,dword ptr ds:[Temporary]add eax,1mov dword ptr ds:[Temporary],eaxL5:mov eax,dword ptr ds:[Temporary]cmp eax,10jge L6lea esi,dword ptr ds:[Array] ; 取数组基地址mov esi,dword ptr ds:[Array + eax * 4] ; 比例因子寻址invoke crt_printf,addr szFmt,esi,esijmp L7L6:int 3main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.datax DWORD ?y DWORD ?szFmt BYTE '外层循环: %d ---> 内层循环:%d ',0dh,0ah,0
.codemain PROCmov dword ptr ds:[x],1 ; x = 1; 外层循环L1: mov ecx,dword ptr ds:[x]cmp ecx,5 ; x < 5jge lop_end; 内层循环mov dword ptr ds:[y],1 ; y = 1L2: mov ecx,dword ptr ds:[y] ; ecx = ycmp ecx,5 ; y < 5jge L3; 循环过程执行mov esi,dword ptr ds:[x]mov edi,dword ptr ds:[y]invoke crt_printf,addr szFmt,esi,edimov ecx,dword ptr ds:[y]add ecx,1 ; y = y + 1mov dword ptr ds:[y],ecxjmp L2L3: mov ecx,dword ptr ds:[x]add ecx,1 ; x = x + 1mov dword ptr ds:[x],ecxjmp L1lop_end:int 3 main ENDP
END main
接着是构建一个三层循环体,三层循环体就像汉堡一样,前面初始化部分时面包,中间时肉,后面也是面包,放在一起,很有食欲。
.386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.datax DWORD ?y DWORD ?z DWORD ?szFmt BYTE '外层循环: %d ---> 中间层循环: %d ---> 内层循环: %d ',0dh,0ah,0
.codemain PROCmov dword ptr ds:[x],1 ; x = 1; 外层循环L1: mov ecx,dword ptr ds:[x]cmp ecx,5 ; x < 5jge lop_end; 中间循环mov dword ptr ds:[y],1 ; y = 1L2: mov ecx,dword ptr ds:[y] ; ecx = ycmp ecx,5 ; y < 5jge L3 ; 大于跳到最外层; 内层循环mov dword ptr ds:[z],1 ; z = 1L5: mov ecx,dword ptr ds:[z]cmp ecx,5 ; z < 5jge 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,ecxmov ecx,dword ptr ds:[z]add ecx,1 ; z = z + 1mov dword ptr ds:[z],ecxjmp L5L4: mov ecx,dword ptr ds:[y]add ecx,1 ; y = y + 1mov dword ptr ds:[y],ecxjmp L2L3: mov ecx,dword ptr ds:[x]add ecx,1 ; x = x + 1mov dword ptr ds:[x],ecxjmp L1lop_end:int 3 main ENDP
END main
中间的IF语句,就是汉堡包的佐料部分,肉质丝滑,入口即化,实在是美妙至极,如下时肉质部分。
.386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.datax DWORD ?y DWORD ?z DWORD ?szFmt BYTE '%d,%d,%d ',0dh,0ah,0
.codemain PROCmov dword ptr ds:[x],1 ; x = 1; 外层循环L1: mov ecx,dword ptr ds:[x]cmp ecx,5 ; x < 5jge lop_end; 中间循环mov dword ptr ds:[y],1 ; y = 1L2: mov ecx,dword ptr ds:[y] ; ecx = ycmp ecx,5 ; y < 5jge L3 ; 大于跳到最外层; 内层循环mov dword ptr ds:[z],1 ; z = 1L5: mov ecx,dword ptr ds:[z]cmp ecx,5 ; z < 5jge 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 L6mov eax,dword ptr ds:[x]cmp eax,dword ptr ds:[y]je L6mov eax,dword ptr ds:[y]cmp eax,dword ptr ds:[z]je L6invoke 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 + 1mov dword ptr ds:[z],ecxjmp L5L4: mov ecx,dword ptr ds:[y]add ecx,1 ; y = y + 1mov dword ptr ds:[y],ecxjmp L2L3: mov ecx,dword ptr ds:[x]add ecx,1 ; x = x + 1mov dword ptr ds:[x],ecxjmp L1lop_end:int 3 main ENDP
END main
Switch与循环: Switch语句与IF语句类似,不同之处就在于Switch是将跳转地址保存在数组中,需要时去数组中通过比例因子寻找到指定的内存然后,使用一条Jmp指令跳转过去,实在美妙!
先给大家看一下,我是怎吗保存这些地址的吧,汇编代码如下所示,直接取出标号,放入数组中,也可以使用堆栈存储,随意。
.386p.model flat,stdcalloption casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.dataMemArray DWORD ?szFmt BYTE '%d,%d,%d ',0dh,0ah,0
.codemain PROCmov dword ptr ds:[MemArray],offset lop_end mov dword ptr ds:[MemArray+4],offset L2lop_end:int 3 L2:xor eax,eaxxor ebx,ebxmain 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.dataMemArray DWORD 0,0,0,0,0,0,0,0,0,0Count DWORD ?szFmt BYTE '%d ',0dh,0ah,0
.codemain PROC; 将指定标号的地址取出,并复制到数组mov dword ptr ds:[MemArray],offset S0mov dword ptr ds:[MemArray + 4],offset S1mov dword ptr ds:[MemArray + 8],offset S2mov dword ptr ds:[MemArray + 12],offset S3mov dword ptr ds:[MemArray + 16],offset S4mov dword ptr ds:[MemArray + 20],offset S_ENDmov dword ptr ds:[Count],0L1: mov ecx,dword ptr ds:[Count]cmp ecx,6jg lop_end; 通过循环次数寻找指令地址并跳转mov ecx,dword ptr ds:[Count]cmp ecx,6jg S_ENDjmp dword ptr ds:[MemArray + ecx * 4]S0: mov eax,1invoke crt_printf,addr szFmt,eaxjmp L2S1: mov eax,2invoke crt_printf,addr szFmt,eaxjmp L2S2: mov eax,3invoke crt_printf,addr szFmt,eaxjmp L2S3: mov eax,4invoke crt_printf,addr szFmt,eaxjmp L2S4: mov eax,5invoke crt_printf,addr szFmt,eaxjmp L2S_END: mov eax,0invoke crt_printf,addr szFmt,eaxjmp L2L2: mov ecx,dword ptr ds:[Count]add ecx,1 ; ecx ++mov dword ptr ds:[Count],ecxjmp L1lop_end:int 3 main ENDP
END main
Loop实现排序: 如果不自己构建排序循环,使用loop实现,则冒泡排序将变得香。
先来看一个汇编案例,我想说,观察下面的代码,你说 这是不是一个死循环呢?思考一下。
.386p.model flat,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.dataCount DWORD 10
.codemain PROCmov ecx,dword ptr ds:[Count]dec ecxL1: push ecxinvoke crt_printf,addr szFmt,ecxpop ecx loop L1main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.dataMyArray DWORD 25,74,89,33,24,67,93,15,78,92Count DWORD 10PrCount DWORD ?szFmt BYTE '%d ',0dh,0ah,0.codemain PROC; 数组排序mov ecx,dword ptr ds:[Count] ; 获取到数组元素数dec ecx ; 数组减1L1: push ecx ; 入栈保存lea esi,dword ptr ds:[MyArray] ; 得到数组基地址L2: mov eax,dword ptr ds:[esi]cmp eax,dword ptr ds:[esi + 4] ; 比较第一个数组与第二个jg L3xchg eax,[esi + 4] ; 交换数据mov [esi],eaxL3: add esi,4loop L2pop ecx ; 弹出数据loop L1; for循环输出元素mov dword ptr ds:[PrCount],0jmp LL1mov ecx,dword ptr ds:[PrCount]add ecx,1mov dword ptr ds:[PrCount],ecxLL1:mov ecx,dword ptr ds:[PrCount]cmp ecx,10jg lop_endlea eax,dword ptr ds:[MyArray]mov ebx,dword ptr ds:[eax + ecx * 4]invoke crt_printf,addr szFmt,ebxmov ecx,dword ptr ds:[PrCount]add ecx,1mov dword ptr ds:[PrCount],ecxjmp LL1lop_end:int 3main 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,stdcalloption casemap:noneinclude windows.inc
include kernel32.inc
includelib kernel32.libinclude msvcrt.inc
includelib msvcrt.lib.dataMyArray DWORD 1,2,3,4,5,6,7,8,9,10SearchVal DWORD 7Count DWORD 10first DWORD ?last DWORD ?mid DWORD ?szFmt BYTE '%d ',0dh,0ah,0.codemain PROCmov 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,1mov 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,1mov dword ptr ds:[mid],eax; edx = value[mid]mov esi,dword ptr ds:[mid]shl esi,2mov edx,[ebx + esi];invoke crt_printf,addr szFmt,edx; if(edx < SearchVal(edi))cmp edx,edijge L2; first = mid + 1;mov eax,dword ptr ds:[mid]add eax,1mov dword ptr ds:[first],eaxjmp L1L2:; else if (value[mid] > searchVal)cmp edx,edijle L3; last = mid - 1;mov eax,dword ptr ds:[mid]sub eax,1mov dword ptr ds:[last],eaxjmp L1L3: ; elsemov eax,dword ptr ds:[mid]invoke crt_printf,addr szFmt,eaxjmp lop_endjmp L1lop_end:mov eax,-1int 3main ENDP
END main
Win32汇编:各种语句的构造方式相关推荐
- win32汇编实现拼接SQL语句
字符串合并,在汇编语言,一般是用loop循环和cx寄存器,自己编程实现: 如果是win32汇编,可以使用movsb指令: 一般开发应用程序都会碰到拼接SQL语句,在C#这些语言用字符串连接的加号就可以 ...
- Win32 汇编语句模板
Win32 汇编语句模板 一 变量 ;句柄 hInstance dd 0 hWnd dd 0 hPen dd 0 hPend dd 0 oldPen dd 0;过程变量 hInst :DWORD hP ...
- win32 汇编基础概念整理
一.关于寄存器 寄存器有EAX,EBX,ECX,EDX,EDI,ESI,ESP,EBP等,似乎IP也是寄存器,但只有在CALL/RET在中会默认使用它,其它情况很少使用到,暂时可以不用理会. EAX是 ...
- win32汇编基础概念
一.关于寄存器 寄存器有EAX,EBX,ECX,EDX,EDI,ESI,ESP,EBP等,似乎IP也是寄存器,但只有在CALL/RET在中会默认使用它,其它情况很少使用到,暂时可以不用理会. EAX是 ...
- Win32汇编_基础
Win32汇编_基础 包含全部段的源程序结构: .386 .model flat, stdcall Option casemap:none ;<一些include语句> .stack [堆 ...
- Win32汇编基本编程框架
Win32汇编编程框架如下: .386 .model flat,stdcall option casemap:none <一些include语句> .stack [堆栈段的大小] .dat ...
- C指针原理(23)-win32汇编及.NET调试
2018-12-28 20:36:07 在WINDOWS系统能用到汇编的机会不多,基本都可以用C或C++代劳,更何况现在MICROSOFT的Visual Studio 系列工具非常强大,WINDOWS ...
- Win32汇编学习笔记之基础篇
基础篇 第一章 背景知识 1.1 Win32的软硬件平台 1.1.1 80x86系列处理器简史 Win32可以在多种硬件平台上运行,但使用最广泛的硬件平台是基于Intel公司80x86系列处理器 ...
- win32汇编 MASM03
http://blog.fishc.com/738.html#codesyntax_5 让编程改变世界 Change the world by program 代码段 .code段是代码段,所有的指令 ...
最新文章
- cacti监控(3)配置cacti
- 基于机器学习的捡球机器人设计与实现(探索)第3篇——opencv基础知识学习(2019-02-02)
- 神PS!老爸把儿子的画作P成现实,看完我笑哭了
- 【公益】开放一台Eureka注册中心给各位Spring Cloud爱好者
- Unreal Engine 4 的 光和影
- 虹软人脸识别在 linux中so文件加载不到的问题
- VS2017编写C++多文件时,出现LNK2005、LNK1169报错的解决方法
- plsql存过声明游标_PLSQL游标使用
- 数据科学工程篇_AB实验原理与实践
- 均线颜色怎么区分_六种颜色的均线包括哪六种颜色,其代表的含义分别是什么?...
- 公告模块phpcms
- ACM Southwestern European Regional Programming Contest (SWERC) 2017 J 智商题(模m同余)
- Python比较文本相似度的7种方法(详细)
- [zz]美团点评智能支付核心交易系统的可用性实践
- 恕我直言!千万别随便叫一个程序员为“码农”
- 【detectron】对输入样本如何产生anchor
- TCP协议——三次握手
- 【学习记录】QQZone项目 part1
- 【五一创作】Matlab 绘制风速、风向统计玫瑰花图【优化】
- 几万创业做什么好_适合小投资者轻资产的创业项目_加油优惠卡
热门文章
- 快速理解图像处理中注意力机制的应用
- mysql在什么情况下会变成全局查询_Linux下MYSQL数据语言,全局变量,查询
- 嵌入式数据库之SQLite 3
- 犹抱琵琶半遮面--MIMO信道中隐藏的秘密
- ajax异步获取数据后动态向表格中添加数据的页面
- 计算机开机长鸣报警,电脑开机报警,教您电脑开机一直长鸣报警怎么办
- 访问中国知网样式丢失
- java用监听捕捉点_使用Robot类创建自己的Java版屏幕捕捉程序
- datastage 函数_DataStage(ETL)技术总结 — 介绍篇 | 学步园
- vs中readfile的作用,readFile和readFileSync之间的区别