整理复习汇编语言的知识点,以前在学习《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 } .386 p .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 } .386 p .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指令跳转过去,实在美妙!
先给大家看一下,我是怎吗保存这些地址的吧,汇编代码如下所示,直接取出标号,放入数组中,也可以使用堆栈存储,随意。
.386 p .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 ' ,0 dh,0 ah,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) { 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 }; 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