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,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
flag DWORD ?
.code
main PROC
; if(var1 >= 20 and var2 <= 100 and var3 == 50)
cmp dword ptr ds:[var1],20 ; 判断是否大于20
jl L1 ; 不大于则跳转
cmp dword ptr ds:[var2],100 ; 判断是否小于100
jg L1 ; 不小于则跳转
cmp dword ptr ds:[var3],50 ; 判断是否等于50
jne L1 ; 不等于则跳转
mov dword ptr ds:[flag],1 ; 说明等式成立 flag=1
jmp L2
L1: mov dword ptr ds:[flag],0
L2: cmp dword ptr ds:[flag],0
je lop_end ; 为0则跳转,不为0则继续执行
xor eax,eax ; 此处是执行if语句内部
xor ebx,ebx
xor ecx,ecx
jmp lop_end
lop_end:
nop ; 直接结束
invoke ExitProcess,0
main ENDP
END main
IF中OR语句的构造: OR语句的判断则是只要等式两边一边的结果返回为真,则整个表达式的后半部分直接跳过。
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int var1 = 20;
int var2 = 10;
int var3 = 50;
if (var1 > var2 || var2 <= var3)
{
printf("xor eax,eax");
}
else if(var3 == 50 || var2 > 10)
{
printf("xor ebx,ebx");
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
; if (var1 > var2 || var2 <= var3)
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; var1 > var2
jg L1
mov eax,dword ptr ds:[var2]
cmp eax,dword ptr ds:[var3] ; var2 <= var3
jg L2 ; 条件是 var2 > var3 则跳转
L1:
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
L2:
; else if(var3 == 50 || var2 > 10)
cmp dword ptr ds:[var3],50
je L3
cmp dword ptr ds:[var2],10 ; var2 > 10
jle lop_end
L3:
xor ebx,ebx ; printf("xor ebx,ebx")
jmp lop_end
lop_end:
nop
int 3
invoke ExitProcess,0
main ENDP
END main
IF中AND/OR混合构造:
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int var1 = 20;
int var2 = 10;
int var3 = 50;
if ((var1 >= 10 && var2 <= 20) || (var2 == 10 && var3 >= 40))
{
printf("xor eax,eax");
}
else
{
printf("xor ebx,ebx");
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
; if ((var1 >= 10 && var2 <= 20) && (var2 == 10 || var3 >= 40))
cmp dword ptr ds:[var1],10 ; var1 >= 10
jl L1
cmp dword ptr ds:[var2],20 ; var2 <= 20
jg L1
cmp dword ptr ds:[var2],10 ; var2 == 10
je L2
cmp dword ptr ds:[var3],40 ; var3 >= 40
jl L1
jmp L2
L1:
xor ebx,ebx ; else
jmp lop_end
L2:
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
IF语句嵌套调用: 在编写这样子的嵌套语句时,应该由外到内逐层解析,这样能更容易写出优美的表达式。
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int x = 100, y = 200, z = 300;
int var1 = 20,var2 = 10,var3 = 50;
if (var1 >= var2)
{
if ((x<y) && (z>y))
{
printf("xor eax,eax");
}
else
{
printf("xor ebx,ebx");
}
}
else if (var2 > var3)
{
printf("xor ecx,ecx");
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
x DWORD 100
y DWORD 200
z DWORD 300
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; if(var1 >= var2) ?
jl L1
mov eax,dword ptr ds:[x]
cmp eax,dword ptr ds:[y] ; if((x<y)) ?
jge L2
mov eax,dword ptr ds:[z] ; if((z>y)) ?
cmp eax,dword ptr ds:[y]
jle L2
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
L1:
mov eax,dword ptr ds:[var2]
cmp eax,dword ptr ds:[var3]
jle lop_end
xor ecx,ecx ; printf("xor ecx,ecx")
jmp lop_end
L2:
xor ebx,ebx ; printf("xor ebx,ebx")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
IF 判断平年闰年: 闰年时年份对400取余等于0的数,或者对4取余等于0并且对100取余不等于0的数.
#include <windows.h>
#include <stdio.h>
int main(int argc,char * argv[])
{
int year = 2017;
if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0))
{
printf("%d 闰年 \n", year);
}
{
printf("%d 平年 \n", year);
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
Year DWORD 2017
szFmtR BYTE '%d 是闰年',0dh,0ah,0
szFmtP BYTE '%d 是平年',0dh,0ah,0
.code
main PROC
mov eax,dword ptr ds:[Year] ; year = 2017;
cdq
mov ecx,400
idiv ecx ; year % 400 == 0
test edx,edx
je L1
mov eax,dword ptr ds:[Year]
and eax,080000003h ; year % 4
test eax,eax
jne L2
mov eax,dword ptr ds:[Year]
cdq
mov ecx,100
idiv ecx ; year % 100 != 0
test edx,edx ; 比较余数
je L2 ; 跳转则是平年
L1: mov eax,dword ptr ds:[Year]
invoke crt_printf,addr szFmtR,eax ; 是闰年
jmp lop_end
L2: mov eax,dword ptr ds:[Year]
invoke crt_printf,addr szFmtP,eax ; 是平年
jmp lop_end
lop_end:
int 3
main ENDP
END main
IF语句三层嵌套:
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int x = 100, y = 200, z = 300;
int var1 = 20,var2 = 10,var3 = 50;
int result = 1;
if ((var1 >= var2) && (var2 <= var3) || (var3 > var1))
{
if ((x % 2 == 0) || (y % 2 != 0))
{
if (result == 1)
printf("xor eax,eax");
}
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
x DWORD 100
y DWORD 200
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
result DWORD 1
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; and var1 >= var2
jl lop_end
mov eax,dword ptr ds:[var2]
cmp eax,dword ptr ds:[var3] ; and var2 <= var3
jle L1
mov eax,dword ptr ds:[var3]
cmp eax,dword ptr ds:[var1] ; or var3 > var1
jle lop_end
L1:
mov eax,dword ptr ds:[x]
and eax,080000001h ; eax = eax % 2 = 0
jns L2 ; eax = 0 则跳转
dec eax
or eax,0fffffffeh ; eax = eax % 2 != 0
inc eax
L2:
mov eax,dword ptr ds:[result]
test eax,eax ; if(result == 1)
jne L3
jmp lop_end
L3:
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
IF语句中的TEST: 这里有多种写法,第一种是比较好的写法,不需要增加太多编号,第二种是正常人的思维方式.
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int x = 100, y = 200, z = 300;
int var1 = 20,var2 = 10,var3 = 50;
int result = 1;
if (var1 >= var2 && var2 <= var3)
{
if (x == 100 || y == 200 || z == 300)
{
if (result == 1)
printf("xor eax,eax");
}
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
x DWORD 100
y DWORD 200
z DWORD 300
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
result DWORD 1
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; var1 >= var2
jl lop_end
mov eax,dword ptr ds:[var2]
cmp eax,dword ptr ds:[var3] ; var2 <= var3
jg lop_end
mov eax,dword ptr ds:[x]
cmp eax,100 ; x == 100
jne lop_end
mov eax,dword ptr ds:[y]
cmp eax,200 ; y == 200
jne lop_end
mov eax,dword ptr ds:[z]
cmp eax,300 ; z = 300
jne lop_end
mov eax,dword ptr ds:[result]
test eax,eax ; eax = 0 ?
jz lop_end
xor eax,eax
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
以下是人的逻辑方式.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
x DWORD 100
y DWORD 200
z DWORD 300
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
result DWORD 1
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; var1 >= var2
jge L1
jmp lop_end
L1:
mov eax,dword ptr ds:[var2] ; var2 <= var3
cmp eax,dword ptr ds:[var3]
jle L2
L2:
mov eax,dword ptr ds:[x]
cmp eax,100 ; x == 100 ?
je L3
mov eax,dword ptr ds:[y] ; y == 200 ?
cmp eax,200
je L3
mov eax,dword ptr ds:[y]
cmp eax,300 ; z == 300 ?
je L3
jmp lop_end
L3:
mov eax,dword ptr ds:[result] ; result == 1 ?
test eax,eax ; eax && eax != 0
jz lop_end
xor eax,eax
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
IF-ELSEIF-ELSE: 多层循环从何治,看我的,给我写。
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int var1 = 20,var2 = 10,var3 = 50;
if (var1 > 20)
printf("xor eax,eax");
else if (var2 > 10)
printf("xor ebx,ebx");
else if (var2 < var3)
printf("xor ecx,ecx");
else
printf("xor edx,edx");
return 0;
}
正常写法
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,20 ; var1 > 20
jg L1
mov eax,dword ptr ds:[var2]
cmp eax,10 ; var2 > 10
jg L2
cmp eax,dword ptr ds:[var3]
jl L3 ; var2 < var3
xor edx,edx ; printf("xor edx,edx")
jmp lop_end
L1:
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
L2:
xor ebx,ebx ; printf("xor ebx,ebx")
jmp lop_end
L3:
xor ecx,ecx ; printf("xor ecx,ecx")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
编译器是这样干的,我把他的思路写一下。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,20
jle L1
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
L1:
mov eax,dword ptr ds:[var2]
cmp eax,10
jle L2
xor ebx,ebx ; printf("xor ebx,ebx")
jmp lop_end
L2:
mov eax,dword ptr ds:[var2]
cmp eax,dword ptr ds:[var3]
jge L3
xor ecx,ecx ; printf("xor ecx,ecx")
jmp lop_end
L3:
xor edx,edx ; printf("xor edx,edx")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
编译器对于if-elseif-else是这样处理的.
int var1 = 20;
int var2 = 10;
int var3 = 50;
if (var1 > 20)
printf("xor eax,eax");
else if (var2 >= 20)
printf("xor ebx,ebx");
else if (var3 <= 20)
printf("xor ecx,ecx");
else
printf("xor edx,edx");
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,20 ; var1 > 20 ?
jle L1 ; 不大于则跳到L1继续判断
xor eax,eax
jmp lop_end ; 最后都要跳向结束
L1: mov eax,dword ptr ds:[var2]
cmp eax,20 ; var1 >= 20 ?
jl L2 ; 不大于则继续判断L2
xor ebx,ebx
jmp lop_end
L2: mov eax,dword ptr ds:[var3]
cmp eax,20 ; var3 <= 20 ?
jg L3 ; 大于则跳到L3
xor ecx,ecx
jmp lop_end
L3: xor edx,edx
jmp lop_end
lop_end:
xor esi,esi
invoke ExitProcess,0
main ENDP
END main
IF的前期脑残写法: 写的烂,没编译器生成的代码有趣,垃圾保存。
脑残1
if(var1 > var2) and (var2 < var3)
{
xor eax,eax
}else if(var1 > var3)
{
xor ebx,ebx
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; if(var1 > var2)
jg L1
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var3] ; else if(var1 > var3)
jg L3
L1:
mov eax,dword ptr ds:[var2] ; if(var2 < var3)
cmp eax,dword ptr ds:[var3]
jl L2
L2:
xor eax,eax
jmp lop
L3:
xor ebx,ebx
jmp lop
lop:
nop
invoke ExitProcess,0
main ENDP
END main
脑残2
if var1 == var2
{
if x > y
{
xchg x,y
}
else
{
x=10
y=20
}
}else
{
var1 = 0
var2 = 0
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
x BYTE 6
y BYTE 5
var1 DWORD 10
var2 DWORD 10
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; var1 == var2 ?
jne L1 ; 不等于跳转到L1
mov al,byte ptr ds:[x]
cmp al,byte ptr ds:[y] ; x > y ?
jg L2 ; 大于跳到L2
mov byte ptr ds:[x],0 ; 不大于则执行x=10 y=20
mov byte ptr ds:[y],0
jmp lop
L1:
mov dword ptr ds:[var1],0 ; var1 != var2 则执行
mov dword ptr ds:[var2],0
L2:
mov al,byte ptr ds:[x]
mov bl,byte ptr ds:[y]
xchg al,bl ; x y 数值交换
mov byte ptr ds:[x],al
mov byte ptr ds:[y],bl
jmp lop
lop:
nop
invoke ExitProcess,0
main ENDP
END main
if 双层嵌套结构: 包含有and,or运算符的连用处理.
int var1 = 20;
int var2 = 10;
int var3 = 50;
if (var1++ > 5 && var2++ >= 10)
{
var3 = var3 + 10;
var3 << 2;
if (var3 <= 100 or var3 <= 1000)
xor eax,eax
else
xor ebx,ebx
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
inc dword ptr ds:[var1] ; var1++
mov eax,dword ptr ds:[var1]
cmp eax,5 ; var1 > 5 ?
jg L1
jmp lop_end
L1:
inc dword ptr ds:[var2] ; var2++
mov eax,dword ptr ds:[var2] ; var2 >=10 ?
cmp eax,10
jge L2
jmp lop_end
L2:
mov eax,dword ptr ds:[var3] ; 获取 var3
add eax,10 ; var3 = var3 + 10
shl eax,2 ; var3 << 2
cmp eax,100
jle L3 ; var3 <= 100 ?
cmp eax,1000 ; eax or
jle L3 ; var3 <= 1000 ?
jmp L4 ; else
L3:
xor eax,eax
jmp lop_end
L4:
xor ebx,ebx
jmp lop_end
lop_end:
nop
invoke ExitProcess,0
main ENDP
END main
编译器对于此类嵌套出处理结果是这样的,由于and指令左面如果成立则继续执行右面的判断,如果不成立右面的直接掠过,这样的话就比较有趣了,如下是我根据汇编代码推测的一段片段,。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
tmp DWORD ?
flag DWORD ?
.code
main PROC
mov eax,dword ptr ds:[var1]
mov dword ptr ds:[tmp],eax ; 将var1原值备份到tmp
mov ecx,dword ptr ds:[var1]
add ecx,1 ; 递增var1并会写到变量中
mov dword ptr ds:[var1],ecx
cmp dword ptr ds:[tmp],5 ; 用原值与5做比较
jle L1 ; 如果 var1 < var2
mov dword ptr ds:[flag],1
jmp L2
L1: mov dword ptr ds:[flag],0 ; 判断的是and的第一个等式
L2: cmp dword ptr ds:[flag],0
je lop_end
mov eax,dword ptr ds:[var2]
mov dword ptr ds:[tmp],eax ; 备份var2
mov ecx,dword ptr ds:[var2]
add ecx,1 ; 递增运算++
mov dword ptr ds:[var2],ecx
cmp dword ptr ds:[tmp],10 ; 判断 var2>=10 ?
jl L3 ; 不大于则跳到L3
mov dword dword ptr ds:[flag],1 ; 大于则标志flag=1
jmp L4
L3: mov dword ptr ds:[flag],0
L4: cmp dword ptr ds:[flag],0
je lop_end ; 不跳转则执行内部if
mov eax,dword ptr ds:[var3]
add eax,10
mov dword ptr ds:[var3],eax ; 递增var3
mov eax,dword ptr ds:[var3]
shl eax,2
mov dword ptr ds:[var3],eax ; var3 = var3 << 2
cmp dword ptr ds:[var3],100 ; var3 <= 100
jle L5
cmp dword ptr ds:[var3],1000 ; var3<=1000
jg L6 ; 跳转到内层else
L5:
xor eax,eax
nop
jmp lop_end
L6:
xor ebx,ebx
nop
jmp lop_end
lop_end:
xor eax,eax
invoke ExitProcess,0
main ENDP
END main
IF中的自增自减处理: 执行自增自减运算需要找一个临时区域来存放自增后的数据,所以首先要开辟局部空间,多数情况下开辟空间可在栈上,例如使用sub esp,12
来分配栈空间,并初始化后即可使用,最后需要将该空间恢复.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.code
main PROC
push ebp
mov ebp,esp
sub esp,12 ; 开辟 3*4 =12 的空间
lea edi,dword ptr ss:[ebp-12] ; 指向栈中基址
mov ecx,3 ; 填充次数 12/4 = 3
mov eax,0cccccccch ; 填充物
rep stosd ; 初始化开始
mov dword ptr ss:[ebp-12],1
mov dword ptr ss:[ebp-8],2 ; 给每个地址赋值
mov dword ptr ss:[ebp-4],3
mov eax,dword ptr ss:[ebp-12] ; 取第一个数据1
mov ebx,dword ptr ss:[ebp-4] ; 取第二个数据3
add eax,ebx ; 执行递增
mov dword ptr ss:[ebp-8],eax ; 写回栈
add esp,12 ; 平栈
mov esp,ebp
pop ebp
invoke ExitProcess,0
main ENDP
END main
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int var1 = 20,var2 = 10,var3 = 50;
if (var1++ >= 20 && ++var2 > 10)
{
printf("xor eax,eax");
}
return 0;
}
以下代码中需要注意,当我们使用var1++
时程序是将++后的结果赋值到了栈中存放,并让var1变量递增,而判断则使用的是栈中的原值,相反++var1
则是在原值上直接进行操作,将操作结果赋值给原值后在进行判断.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
push ebp
mov ebp,esp
sub esp,8 ; 开辟 2*4 =8 的空间
lea edi,dword ptr ss:[ebp-8] ; 指向栈中基址
mov ecx,2 ; 填充次数 8/4 = 2
mov eax,0cccccccch ; 填充物
rep stosd ; 初始化开始
mov eax,dword ptr ds:[var1]
mov dword ptr ss:[ebp-8],eax ; 将var1存入临时变量中
add eax,1
mov dword ptr ds:[var1],eax ; 将相加后的结果写回到var1
cmp dword ptr ss:[ebp-8],20 ; 用原值与20对比
jl L1
mov dword ptr ss:[ebp-4],1 ; 局部变量存放标志=1
jmp L2
L1: mov dword ptr ss:[ebp-4],0
L2: cmp dword ptr ss:[ebp-4],0
je lop_end
mov eax,dword ptr ds:[var2] ; 继续执行 ++var2
add eax,1
mov dword ptr ds:[var2],eax
cmp dword ptr ds:[var2],10 ; var2 > 10
jle lop_end
xor eax,eax ; printf("xor eax,eax")
lop_end:
add esp,8 ; 平栈
mov esp,ebp
pop ebp
invoke ExitProcess,0
main ENDP
END main
IF嵌套中的移位1:
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int var1 = 20,var2 = 10,var3 = 50;
if (((var1 << 2) ^ (var2 << 3)) || ((var2 << 1) ^ (var3 << 3)))
{
if ((var1 >= var2) || (var2 <= var3) && (var3 == 50))
{
printf("xor eax,eax");
}
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
; ((var1 << 2) ^ (var2 << 3))
mov eax,dword ptr ds:[var1]
shl eax,2
mov ecx,dword ptr ds:[var2]
shl ecx,3
xor eax,ecx
je L1
; ((var2 << 1) ^ (var3 << 3))
mov eax,dword ptr ds:[var2]
shl eax,1
mov eax,dword ptr ds:[var3]
shl ecx,3
xor eax,ecx
je lop_end
; (var1 >= var2)
L1: mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2]
jge L2
; (var2 <= var3)
mov eax,dword ptr ds:[var2]
cmp eax,dword ptr ds:[var3]
jg lop_end
L2:
; (var3 == 50)
cmp dword ptr ds:[var3],50
jnz lop_end
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
第二种如果判断
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int var1 = 20,var2 = 10,var3 = 50;
if (((var1 << 2) % 2) || (var3 >> 1) % 3)
{
if (((var1 << 2) + 10) > 50)
{
printf("xor ebx,ebx");
}
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
; ((var1 << 2) % 2)
mov eax,dword ptr ds:[var1]
shl eax,2
and eax,080000001h ; var1 % 2
jns L2 ; 非负数则跳转
; (var3 >> 1) % 3 ; 为负数执行第二个表达式
L1: mov eax,dword ptr ds:[var3]
sar eax,1 ; var3 >> 1
cdq ; 扩展为8字节
mov ecx,3 ; 除以3
idiv ecx
test edx,edx ; 比较余数是否为0
je lop_end
; ((var1 << 2) + 10) > 50
L2: mov eax,dword ptr ds:[var1]
shl eax,2
add eax,10
cmp eax,50
jle lop_end
xor eax,eax ; printf("xor ebx,ebx")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
IF中的三目运算符:
#include <stdio.h>
#include <Windows.h>
int main(int argc,char *argv[])
{
int var1 = 20, var2 = 10, var3 = 50;
if ((var1 > var2 ? 1 : 0) && (var2 <= var3 ? 1 : 0))
{
printf("xor eax,eax");
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
flag DWORD ?
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; var1 > var2 ?
jle L1
mov dword ptr ds:[flag],1 ; 表达式1成立
jmp L2
L1: mov dword ptr ds:[flag],0
L2: cmp dword ptr ds:[flag],0
je lop_end
mov eax,dword ptr ds:[var2]
cmp eax,dword ptr ds:[var3] ; var2 <= var3
jg L3
mov dword ptr ds:[flag],1 ; 表达式2成立
jmp L4
L3: mov dword ptr ds:[flag],0
L4: cmp dword ptr ds:[flag],0
je lop_end
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
While /For 语句构建
While/FOr 循环框架: while循环,for循环的简单框架,后期会逐步提高难度,最终实现一个循环链表结构。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
count DWORD ?
.code
main PROC
mov dword ptr ds:[count],0 ; 设置while初始化
S1: cmp dword ptr ds:[count],10 ; 设置最大循环数
jge loop_end ; 判断是否循环结束
xor eax,eax ; 执行循环体
mov eax,dword ptr ds:[count] ; 取出循环条件
add eax,1 ; 递增
mov dword ptr ds:[count],eax ; 写回
jmp S1
loop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
再看一下他的好基友,do-while是如何构造的,相比于while,该语句是先执行在判断,从效率上来说这个效率要高于while.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
count DWORD ?
.code
main PROC
mov dword ptr ds:[count],0 ; 初始化循环次数
S1: xor eax,eax ; 执行循环体
mov eax,dword ptr ds:[count] ; 取出计数器
add eax,1 ; 递增
mov dword ptr ds:[count],eax ; 回写
cmp dword ptr ds:[count],10 ; 与10做对比
jl S1 ; 小于则继续循环
int 3
invoke ExitProcess,0
main ENDP
END main
最后看一个for语句的实现流程,该语句的构建方式相对于While来说略显复杂些,效率远不及While,反汇编后发现,编译器是这样构建的.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
count DWORD ?
.code
main PROC
mov dword ptr ds:[count],0 ; 设置 int x = 0;
jmp L2
L1: mov eax,dword ptr ds:[count] ; x = x++
add eax,1
mov dword ptr ds:[count],eax
L2: cmp dword ptr ds:[count],10 ; 比较 x < 10
jge lop_end
xor eax,eax ; 执行循环体
jmp L1
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
在Python中for循环是for x in range(2,10)
可以指定一个范围,我们接着尝试构建一下.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
start_count DWORD ?
end_count DWORD ?
.code
main PROC
mov dword ptr ds:[start_count],2 ; 指定开始循环编号
mov dword ptr ds:[end_count],5 ; 指定结束循环编号
mov ecx,dword ptr ds:[start_count]
L1: cmp dword ptr ds:[end_count],ecx
jle lop_end
xor eax,eax ; 循环体内部
add ecx,1 ; 每次递增
mov dword ptr ds:[start_count],ecx
jmp L1
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
While遍历数组: 以下案例主要通过仿写While循环结构并通过比例因子寻址,实现对一个DWORD数组的遍历.
#include <stdio.h>
#include <Windows.h>
int main(int argc,char *argv[])
{
int Array[10] = { 1,2,3,4,5,6,7,8,9,10 };
int count = 0;
while (count < sizeof(Array) / sizeof(int))
{
printf("value = %d \n", Array[count]);
count = count + 1;
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MyArray DWORD 1,2,3,4,5,6,7,8,9,10
count DWORD ?
szFmt BYTE 'value = %d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[count],0 ; 初始化循环
mov ecx,0 ; 设置循环计数(比例因子)
S1: cmp dword ptr ds:[count],lengthof MyArray ; 与数组总长度对比
jge lop_end ; 是否结束
lea esi,dword ptr ds:[MyArray] ; 获取数组基地址
mov ebx,dword ptr ds:[esi + ecx * 4] ; 比例因子寻址
invoke crt_printf,addr szFmt,ebx ; 调用系统crt
mov ecx,dword ptr ds:[count]
add ecx,1 ; 计次循环递增
mov dword ptr ds:[count],ecx
jmp S1
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
For循环尝试判断: 这次使用For循环,首先仿写For循环语句,然后在内部判断指定数值是否合格,合格输出.
#include <stdio.h>
#include <Windows.h>
int main(int argc,char *argv[])
{
int Array[10] = { 56,78,33,45,78,90,32,44,56,67 };
for (int x = 0; x < 10; x++)
{
if (Array[x] >= 50)
{
printf("out -> %d \n", Array[x]);
}
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MyArray DWORD 56,78,33,45,78,90,32,44,56,67
count DWORD ?
szFmt BYTE 'out -> %d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[count],0 ; int x = 0
jmp L1
L2: mov eax,dword ptr ds:[count]
add eax,1 ; x ++
mov dword ptr ds:[count],eax
L1:
cmp dword ptr ds:[count],10 ; x < 10
jge lop_end
mov eax,dword ptr ds:[count] ; 获取循环次数,当作因子
lea esi,dword ptr ds:[MyArray] ; 取数组基地址
mov ebx,dword ptr ds:[esi + eax * 4] ; 因子寻址
cmp ebx,50
jl L2 ; 如果小于50则跳转到下一次循环
invoke crt_printf,addr szFmt,ebx ; 调用系统crt
jmp L2
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
继续增加难度,求最大最小平均值的代码,尝试用汇编实现.
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
int Array[10] = { 56,78,33,45,78,90,32,44,56,67 };
int max_result = 0,min_result = 100,sum_result = 0,avg_result = 0;
for (int x = 0; x < 10; x++)
{
if (Array[x] >= max_result)
{
max_result = Array[x];
}
if (Array[x] <= min_result)
{
min_result = Array[x];
}
sum_result = sum_result + Array[x];
avg_result = sum_result / 10;
}
printf("max = %d min = %d sum = %d avg = %d \n", max_result,min_result,sum_result,avg_result);
system("pause");
return 0;
}
以下这段代码,写的有点小问题,但大体完善,先思考一下哪里的问题,后期我在发答案!
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MyArray DWORD 56,78,33,45,78,90,32,44,56,67
count DWORD ?
max_result DWORD 0
min_result DWORD 100
sum_result DWORD 0
avg_result DWORD 0
szFmt BYTE 'max = %d min = %d sum = %d avg = %d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[count],0 ; int x = 0
jmp L1
L2: mov eax,dword ptr ds:[count]
add eax,1 ; x ++
mov dword ptr ds:[count],eax
L1:
cmp dword ptr ds:[count],10 ; x < 10
jge lop_end
mov eax,dword ptr ds:[count]
lea esi,dword ptr ds:[MyArray]
mov ebx,dword ptr ds:[esi + eax * 4]
cmp ebx,dword ptr ds:[max_result] ; Array[x] >= max_result
jl L3
mov dword ptr ds:[max_result],ebx ; max_result = Array[x];
L3:
mov ebx,dword ptr ds:[esi + eax * 4]
cmp ebx,dword ptr ds:[min_result] ; Array[x] <= min_result
jg L4
L4:
mov ebx,dword ptr ds:[esi + eax * 4]
mov edx,dword ptr ds:[sum_result] ; sum_result + Array[x];
add ebx,edx
mov dword ptr ds:[sum_result],ebx ; sum_result
mov eax,dword ptr ds:[sum_result]
cdq
mov ecx,10
idiv ecx ; sum_result / 10;
mov dword ptr ds:[sum_result],eax ; avg_result
jmp L2
lop_end:
mov eax,dword ptr ds:[max_result]
mov ebx,dword ptr ds:[min_result]
mov ecx,dword ptr ds:[sum_result]
mov edx,dword ptr ds:[avg_result]
invoke crt_printf,addr szFmt,eax,ebx,ecx,edx
int 3
invoke ExitProcess,0
main ENDP
END main
问题显而易见,相信大家都看出来了,我就直接公布正确代码了
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MyArray DWORD 56,78,33,45,78,90,32,44,56,67
count DWORD ?
max_result DWORD 0
min_result DWORD 100
sum_result DWORD 0
avg_result DWORD 0
szFmt BYTE 'max = %d min= %d sum= %d avg = %d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[count],0 ; int x = 0
jmp L1
L2: mov eax,dword ptr ds:[count]
add eax,1 ; x ++
mov dword ptr ds:[count],eax
L1:
cmp dword ptr ds:[count],10 ; x < 10
jge lop_end
mov eax,dword ptr ds:[count]
lea esi,dword ptr ds:[MyArray]
mov ebx,dword ptr ds:[esi + eax * 4]
cmp ebx,dword ptr ds:[max_result] ; Array[x] >= max_result
jl L3
mov dword ptr ds:[max_result],ebx ; max_result = Array[x];
L3:
mov ebx,dword ptr ds:[esi + eax * 4]
cmp ebx,dword ptr ds:[min_result] ; Array[x] <= min_result
jg L4
mov dword ptr ds:[min_result],ebx
L4:
mov ebx,dword ptr ds:[esi + eax * 4] ; Array[x]
add dword ptr ds:[sum_result],ebx ; sum_result = sum_result + Array[x];
mov eax,dword ptr ds:[sum_result]
cdq ; 符号扩展
mov ecx,10 ; / 10
idiv ecx ; sum_result / 10;
mov dword ptr ds:[avg_result],eax ; avg_result
jmp L2
lop_end:
mov eax,dword ptr ds:[max_result]
mov ebx,dword ptr ds:[min_result]
mov ecx,dword ptr ds:[sum_result]
mov edx,dword ptr ds:[avg_result]
invoke crt_printf,addr szFmt,eax,ebx,ecx,edx
int 3
main ENDP
END main
Do-While 与跳出循环: 要说continue与break语句的唯一区别,就在于一个是跳转到了本次循环的结束位置,另一个则是条向了总循环结束位置.
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
int Array[10] = { 56,78,33,45,78,90,32,15,56,67 };
int index = 0;
do
{
if (Array[index] > 10 && Array[index + 1] <= 20)
{
printf("array[1] => %d array[2] => %d \n", Array[index], Array[index + 1]);
break;
}
index = index + 1;
} while (index < 10);
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MyArray DWORD 56,78,33,45,78,90,32,15,56,67
count DWORD ?
szFmt BYTE 'array[1] => %d array[2] => %d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[count],0 ; int index = 0;
L1:
mov eax,dword ptr ds:[count]
cmp dword ptr ds:[MyArray + eax * 4],10 ; Array[index] > 10
jle L2
mov eax,dword ptr ds:[count]
add eax,1
cmp dword ptr ds:[MyArray + eax * 4],20 ; Array[index + 1] <= 20
jg L2
mov esi,dword ptr ds:[MyArray + eax * 4 - 4] ; esi = Array[index]
mov edi,dword ptr ds:[MyArray + eax * 4] ; edi = Array[index+1]
invoke crt_printf,addr szFmt,esi,edi
jmp lop_end ; break
L2: mov eax,dword ptr ds:[count]
add eax,1 ; index = index + 1;
mov dword ptr ds:[count],eax
cmp dword ptr ds:[count],10 ; index < 10
jl L1
lop_end: ; break
int 3
main ENDP
END main
For循环多重IF判断: 在循环中我们首先判断两个数组中元素是否大于0,大于则执行加法运算,然后输出基数或偶数.
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
int SrcArray[10] = { 56,78,33,45,78,90,32,15,56,67 };
int DstArray[10] = { 59,77,89,23,11,45,67,88,93,27 };
int index = 0;
for (index = 0; index < 10; index++)
{
if (SrcArray[index] != 0 && DstArray[index] != 0)
{
int sum = SrcArray[index] + DstArray[index];
if (sum % 2 == 0)
printf("偶数: %d \n", sum);
else
printf("基数: %d \n", sum);
}
}
system("pause");
return 0;
}
思考了一会,花费了一些时间,但还是使用汇编完成了.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
SrcArray DWORD 56,78,33,45,78,90,32,15,56,67
DstArray DWORD 59,77,89,23,11,45,67,88,93,27
index DWORD 0
sum DWORD 0
szFmt1 BYTE '基数: %d ',0dh,0ah,0
szFmt2 BYTE '偶数: %d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[index],0 ; index = 0
jmp L1
L2: mov eax,dword ptr ds:[index]
add eax,1 ; index++
mov dword ptr ds:[index],eax
L1:
cmp dword ptr ds:[index],10 ; index < 10
jge lop_end
mov eax,dword ptr ds:[index];
cmp dword ptr ds:[SrcArray + eax * 4],0
je L2 ; SrcArray[index] != 0
mov eax,dword ptr ds:[index]
cmp dword ptr ds:[DstArray + eax * 4],0 ; DstArray[index] != 0
je L2
; ------------------------------------------
; 另类加法,通过一个SrcArray定位DstArray完成加法
mov eax,dword ptr ds:[index] ; 获取因子
lea esi,dword ptr ds:[SrcArray] ; 取数组首地址
mov ebx,dword ptr ds:[esi + eax * 4] ; 获取 SrcArray[index]
mov ecx,dword ptr ds:[esi + eax * 4 + 40] ; 获取 DstArray[index]
add ebx,ecx ; SrcArray[index] + DstArray[index]
mov dword ptr ds:[sum],ebx ; sum = SrcArray[index] + DstArray[index]
mov eax,dword ptr ds:[sum]
and eax,080000001h ; sum % 2 == 0
test eax,eax
jne L3
invoke crt_printf,addr szFmt2,dword ptr ds:[sum] ; 偶数输出
jmp L2
L3:
invoke crt_printf,addr szFmt1,dword ptr ds:[sum] ; 基数输出
jmp L2
lop_end:
int 3
main ENDP
END main
For语句嵌套(乘法口诀表): 首先我们来接触一下For循环的嵌套实现方法,以打印99表为例,尝试使用汇编实现.
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
for (int x = 1; x < 10; x++)
{
for (int y = 1; y <= x; y++)
{
int result = x*y;
printf("%d*%d=%-3d", y, x, result);
}
printf("\n");
}
system("pause");
return 0;
}
执行双层循环需要嵌套For语句,先来写一个简单的双层For循环的汇编版.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
x DWORD ?
y DWORD ?
szFmt BYTE '内层循环: %d 外层循环: %d ',0dh,0ah,0
szPr BYTE '----->',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[x],1 ; int x = 1
jmp L1
L2: mov eax,dword ptr ds:[x]
add eax,1 ; x++
mov dword ptr ds:[x],eax
L1:
cmp dword ptr ds:[x],10 ; x < 10
jge lop_end
mov dword ptr ds:[y],1 ; y = 1
jmp L3
L5: mov eax,dword ptr ds:[y]
add eax,1 ; y++
mov dword ptr ds:[y],eax
L3:
mov eax,dword ptr ds:[y]
cmp eax,dword ptr ds:[x] ; y <= x
jg L4
; 执行的是循环体内部
mov eax,dword ptr ds:[x]
mov ebx,dword ptr ds:[y]
invoke crt_printf,addr szFmt,eax,ebx
jmp L5
L4:
; 执行外层循环
invoke crt_printf,addr szPr
jmp L2
lop_end:
int 3
main ENDP
END main
最终实现只是相应的做一个替换即可.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
x DWORD ?
y DWORD ?
szFmt BYTE '%d * %d = %d ',0
szPr BYTE ' ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[x],1 ; int x = 1
jmp L1
L2: mov eax,dword ptr ds:[x]
add eax,1 ; x++
mov dword ptr ds:[x],eax
L1:
cmp dword ptr ds:[x],10 ; x < 10
jge lop_end
mov dword ptr ds:[y],1 ; y = 1
jmp L3
L5: mov eax,dword ptr ds:[y]
add eax,1 ; y++
mov dword ptr ds:[y],eax
L3:
mov eax,dword ptr ds:[y]
cmp eax,dword ptr ds:[x] ; y <= x
jg L4
; 执行的是循环体内部
mov eax,dword ptr ds:[x]
imul eax,dword ptr ds:[y]
invoke crt_printf,addr szFmt,dword ptr ds:[y],dword ptr ds:[x],eax
jmp L5
L4:
; 执行外层循环
invoke crt_printf,addr szPr
jmp L2
lop_end:
int 3
main ENDP
END main
For简单循环(水仙花数): 所谓水仙花数是指一个三位数,其各位数字立方和等于该数本身.
例如: 153是一个水仙花数,因为153=1的三次方+5的三次方+3的三次方.
分析: 利用for循环控制100-999个数,每个数分解出个位,十位,百位.
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
int x, y, z, n;
for (n = 100; n < 1000; n++)
{
x = n / 100;
y = n / 10 % 10;
z = n % 10;
if (x * 100 + y * 10 + z == x*x*x + y*y*y + z*z*z)
{
printf("水仙花: %-5d \n", n);
}
}
system("pause");
return 0;
}
尝试使用汇编实现计算逻辑.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
x DWORD ?
y DWORD ?
z DWORD ?
n DWORD ?
szFmt BYTE '水仙花: %-5d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[n],100 ; n = 100
jmp L1
L2: mov eax,dword ptr ds:[n]
add eax,1 ; n++
mov dword ptr ds:[n],eax
L1: mov eax,dword ptr ds:[n]
cmp eax,1000 ; n < 1000
jge lop_end
mov eax,dword ptr ds:[n]
cdq
mov ecx,100 ; x = n / 100;
idiv ecx
mov dword ptr ds:[x],eax
mov eax,dword ptr ds:[n]
cdq
mov ecx,10
idiv ecx ; y = n / 10;
cdq
mov ecx,10
idiv ecx ; y = y % 10;
mov dword ptr ds:[y],edx
mov eax,dword ptr ds:[n]
cdq
mov ecx,10
idiv ecx ; z = n % 10;
mov dword ptr ds:[z],edx
; 开始执行if()比较语句
imul eax,dword ptr ds:[x],100 ; x * 100
imul ecx,dword ptr ds:[y],10 ; y * 10
add eax,dword ptr ds:[z] ; + z
add ecx,eax
mov edx,dword ptr ds:[x]
imul edx,dword ptr ds:[x] ; x*x*x
imul edx,dword ptr ds:[x]
mov eax,dword ptr ds:[y]
imul eax,dword ptr ds:[y] ; y*y*y
imul eax,dword ptr ds:[y]
add edx,eax
mov eax,dword ptr ds:[z]
imul eax,dword ptr ds:[z] ; z*z*z
imul eax,dword ptr ds:[z]
add edx,eax
cmp ecx,edx ; (x * 100 + y * 10 + z) == (x*x*x + y*y*y + z*z*z)
jne L2
mov eax,dword ptr ds:[n]
invoke crt_printf,addr szFmt,eax
jmp L2
lop_end:
int 3
main ENDP
END main
For语句嵌套(冒泡排序): 冒泡排序实现思路从后向前,大的数下沉小的数上移,C代码如下,尝试使用汇编实现.
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
int Array[10] = { 34,78,65,77,89,43,23,55,67,8 };
int x, y, temporary, ArraySize=10;
for (x = 0; x < ArraySize - 1; x++)
{
for (y = ArraySize - 1; y > x; y--)
{
if (Array[y - 1] > Array[y])
{
temporary = Array[y - 1];
Array[y - 1] = Array[y];
Array[y] = temporary;
}
}
}
for (int x = 0; x < 10; x++)
{
printf("%d \n", Array[x]);
system("pause");
return 0;
}
未完待续
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
Array DWORD 34,78,65,77,89,43,23,55,67,8
x DWORD ?
y DWORD ?
Temporary DWORD ?
ArraySize DWORD ?
.code
main PROC
mov dword ptr ds:[x],0 ; x=0
mov dword ptr ds:[ArraySize],10 ; ArraySize=10
jmp L1
L2: mov eax,dword ptr ds:[x]
add eax,1 ; x++
mov dword ptr ds:[x],eax
L1: mov eax,dword ptr ds:[ArraySize]
sub eax,1 ; x < ArraySize - 1
cmp dword ptr ds:[x],eax
jge lop_end
; 内层循环体内容
L4: mov eax,dword ptr ds:[ArraySize]
sub eax,1 ; y = ArraySize - 1
mov dword ptr ds:[y],eax
jmp L3
mov eax,dword ptr ds:[y]
sub eax,1 ; y--
mov dword ptr ds:[y],eax
L3:
mov eax,dword ptr ds:[y]
cmp eax,dword ptr ds:[x] ; y > x
jle L2
mov ecx,dword ptr ds:[y]
mov eax,dword ptr ds:[Array + ecx * 4] ; y
sub ecx,1
mov ebx,dword ptr ds:[Array + ecx * 4] ; x
xchg eax,ebx
mov dword ptr ds:[Array + ecx * 4],eax
add ecx,1
mov dword ptr ds:[Array + ecx * 4],ebx
jmp L4
jmp L2
lop_end:
int 3
main ENDP
END main
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
Array DWORD 34,78,65,77,89,43,23,55,67,8
x DWORD ?
y DWORD ?
Temporary DWORD ?
ArraySize DWORD ?
szFmt BYTE '%d --> %d ',0dh,0ah,0
.code
main PROC
; 初始化的部分
mov dword ptr ds:[x],0 ; x=0
mov dword ptr ds:[ArraySize],10 ; ArraySize=10
; 外层循环体
jmp L1
L2: mov eax,dword ptr ds:[x]
add eax,1 ; x++
mov dword ptr ds:[x],eax
L1: mov eax,dword ptr ds:[ArraySize]
sub eax,1 ; ArraySize - 1
cmp dword ptr ds:[x],eax ; x < ArraySize
jge lop_end
; 内层循环体内容
mov eax,dword ptr ds:[ArraySize]
sub eax,1
mov dword ptr ds:[y],eax
jmp L3
L4: mov eax,dword ptr ds:[y]
sub eax,1 ; y--
mov dword ptr ds:[y],eax
L3: mov eax,dword ptr ds:[y]
cmp eax,dword ptr ds:[x]
jle L2
mov esi,dword ptr ds:[y]
mov ebx,dword ptr ds:[Array + esi * 4] ; Array[y]
mov edx,dword ptr ds:[Array + esi * 4 - 4] ; Array[y - 1]
cmp edx,ebx
jle L4
mov dword ptr ds:[Array + esi * 4],edx
mov dword ptr ds:[Array + esi * 4 - 4],ebx
; invoke crt_printf,addr szFmt,ebx,edx
jmp L4
jmp L2
lop_end:
int 3
main ENDP
END main
排序完成
完整代码已经写出来了,如下所示.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
Array DWORD 34,78,65,77,89,43,23,55,67,8
x DWORD ?
y DWORD ?
Temporary DWORD ?
ArraySize DWORD ?
szFmt BYTE '%d --> %d ',0dh,0ah,0
.code
main PROC
; 初始化的部分
mov dword ptr ds:[x],0 ; x=0
mov dword ptr ds:[ArraySize],10 ; ArraySize=10
; 外层循环体
jmp L1
L2: mov eax,dword ptr ds:[x]
add eax,1 ; x++
mov dword ptr ds:[x],eax
L1: mov eax,dword ptr ds:[ArraySize]
sub eax,1 ; ArraySize - 1
cmp dword ptr ds:[x],eax ; x < ArraySize
jge lop_end
; 内层循环体内容
mov eax,dword ptr ds:[ArraySize]
sub eax,1
mov dword ptr ds:[y],eax
jmp L3
L4: mov eax,dword ptr ds:[y]
sub eax,1 ; y--
mov dword ptr ds:[y],eax
L3: mov eax,dword ptr ds:[y]
cmp eax,dword ptr ds:[x] ; Array[y - 1] > Array[y]
jle L2
mov esi,dword ptr ds:[y]
mov ebx,dword ptr ds:[Array + esi * 4] ; Array[y]
mov edx,dword ptr ds:[Array + esi * 4 - 4] ; Array[y - 1]
cmp edx,ebx
jle L4
mov dword ptr ds:[Array + esi * 4],edx ; Array[y] = Array[y - 1]
mov dword ptr ds:[Array + esi * 4 - 4],ebx ; Array[y - 1] = Array[y]
; invoke crt_printf,addr szFmt,ebx,edx
jmp L4
jmp L2
lop_end:
nop
; 执行打印函数
mov dword ptr ds:[Temporary],0
jmp L5
L7: mov eax,dword ptr ds:[Temporary]
add eax,1
mov dword ptr ds:[Temporary],eax
L5:
mov eax,dword ptr ds:[Temporary]
cmp eax,10
jge L6
lea esi,dword ptr ds:[Array] ; 取数组基地址
mov esi,dword ptr ds:[Array + eax * 4] ; 比例因子寻址
invoke crt_printf,addr szFmt,esi,esi
jmp L7
L6:
int 3
main ENDP
END main
先看排序部分
接着是输出部分
While 三层嵌套: 在写三层嵌套时,你需要注意了,在内层循环时需要清空变量,不然会导致循环一次整个程序结束掉了,就像下面的这种写法,乍一看没啥问题,可是一旦运行就会发现,程序每次都只运行外层一次循环就意外终止了,经过反汇编调试发现,是粗心导致没有让内层循环及时的置空。
#include <windows.h>
#include <stdio.h>
int main(int argc,char * argv[])
{
int x=1, y=1, z=1;
while (x < 5)
{
while (y < 5)
{
while (z < 5)
{
z = z + 1;
}
y = y + 1;
}
x = x + 1;
}
return 0;
}
来一个案例看看,使用三层嵌套完成案例,有1,2,3,4个数字,能组成多少个互补相同且不重复的三位数,尝试使用汇编实现以下这个逻辑。
#include <windows.h>
#include <stdio.h>
int main(int argc,char * argv[])
{
int x=1, y=1, z=1;
while (x < 5)
{
while (y < 5)
{
while (z < 5)
{
if (x != z && x != y && y != z)
{
printf("%d,%d,%d \n", x, y, z);
}
z = z + 1;
}
z = 1;
y = y + 1;
}
y = 1;
x = x + 1;
}
return 0;
}
首先我们先来构建一个双层循环,然后再构建三层的.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
x DWORD ?
y DWORD ?
szFmt BYTE '外层循环: %d ---> 内层循环:%d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[x],1 ; x = 1
; 外层循环
L1: mov ecx,dword ptr ds:[x]
cmp ecx,5 ; x < 5
jge lop_end
; 内层循环
mov dword ptr ds:[y],1 ; y = 1
L2: mov ecx,dword ptr ds:[y] ; ecx = y
cmp ecx,5 ; y < 5
jge L3
; 循环过程执行
mov esi,dword ptr ds:[x]
mov edi,dword ptr ds:[y]
invoke crt_printf,addr szFmt,esi,edi
mov ecx,dword ptr ds:[y]
add ecx,1 ; y = y + 1
mov dword ptr ds:[y],ecx
jmp L2
L3: mov ecx,dword ptr ds:[x]
add ecx,1 ; x = x + 1
mov dword ptr ds:[x],ecx
jmp L1
lop_end:
int 3
main ENDP
END main
接着是构建一个三层循环体,三层循环体就像汉堡一样,前面初始化部分时面包,中间时肉,后面也是面包,放在一起,很有食欲。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
x DWORD ?
y DWORD ?
z DWORD ?
szFmt BYTE '外层循环: %d ---> 中间层循环: %d ---> 内层循环: %d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[x],1 ; x = 1
; 外层循环
L1: mov ecx,dword ptr ds:[x]
cmp ecx,5 ; x < 5
jge lop_end
; 中间循环
mov dword ptr ds:[y],1 ; y = 1
L2: mov ecx,dword ptr ds:[y] ; ecx = y
cmp ecx,5 ; y < 5
jge L3 ; 大于跳到最外层
; 内层循环
mov dword ptr ds:[z],1 ; z = 1
L5: mov ecx,dword ptr ds:[z]
cmp ecx,5 ; z < 5
jge L4 ; 大于跳到中间层
; 三层循环框架
mov eax,dword ptr ds:[x]
mov ebx,dword ptr ds:[y]
mov ecx,dword ptr ds:[z]
invoke crt_printf,addr szFmt,eax,ebx,ecx
mov ecx,dword ptr ds:[z]
add ecx,1 ; z = z + 1
mov dword ptr ds:[z],ecx
jmp L5
L4: mov ecx,dword ptr ds:[y]
add ecx,1 ; y = y + 1
mov dword ptr ds:[y],ecx
jmp L2
L3: mov ecx,dword ptr ds:[x]
add ecx,1 ; x = x + 1
mov dword ptr ds:[x],ecx
jmp L1
lop_end:
int 3
main ENDP
END main
中间的IF语句,就是汉堡包的佐料部分,肉质丝滑,入口即化,实在是美妙至极,如下时肉质部分。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
x DWORD ?
y DWORD ?
z DWORD ?
szFmt BYTE '%d,%d,%d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[x],1 ; x = 1
; 外层循环
L1: mov ecx,dword ptr ds:[x]
cmp ecx,5 ; x < 5
jge lop_end
; 中间循环
mov dword ptr ds:[y],1 ; y = 1
L2: mov ecx,dword ptr ds:[y] ; ecx = y
cmp ecx,5 ; y < 5
jge L3 ; 大于跳到最外层
; 内层循环
mov dword ptr ds:[z],1 ; z = 1
L5: mov ecx,dword ptr ds:[z]
cmp ecx,5 ; z < 5
jge L4 ; 大于跳到中间层
; 三层循环框架
;mov eax,dword ptr ds:[x]
;mov ebx,dword ptr ds:[y]
;mov ecx,dword ptr ds:[z]
;invoke crt_printf,addr szFmt,eax,ebx,ecx
; 开始在框架中搞事情
mov eax,dword ptr ds:[x]
cmp eax,dword ptr ds:[z]
je L6
mov eax,dword ptr ds:[x]
cmp eax,dword ptr ds:[y]
je L6
mov eax,dword ptr ds:[y]
cmp eax,dword ptr ds:[z]
je L6
invoke crt_printf,addr szFmt,dword ptr ds:[x],dword ptr ds:[y],dword ptr ds:[z]
L6: mov ecx,dword ptr ds:[z]
add ecx,1 ; z = z + 1
mov dword ptr ds:[z],ecx
jmp L5
L4: mov ecx,dword ptr ds:[y]
add ecx,1 ; y = y + 1
mov dword ptr ds:[y],ecx
jmp L2
L3: mov ecx,dword ptr ds:[x]
add ecx,1 ; x = x + 1
mov dword ptr ds:[x],ecx
jmp L1
lop_end:
int 3
main ENDP
END main
Switch与循环: Switch语句与IF语句类似,不同之处就在于Switch是将跳转地址保存在数组中,需要时去数组中通过比例因子寻找到指定的内存然后,使用一条Jmp指令跳转过去,实在美妙!
先给大家看一下,我是怎吗保存这些地址的吧,汇编代码如下所示,直接取出标号,放入数组中,也可以使用堆栈存储,随意。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MemArray DWORD ?
szFmt BYTE '%d,%d,%d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[MemArray],offset lop_end
mov dword ptr ds:[MemArray+4],offset L2
lop_end:
int 3
L2:
xor eax,eax
xor ebx,ebx
main ENDP
END main
尝试构建Switch语句。
#include <windows.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
int x = 0;
while (x < 6)
{
switch (x)
{
case 0:
printf("1"); break;
case 1:
printf("2"); break;
case 2:
printf("3"); break;
case 3:
printf("4"); break;
case 4:
printf("5"); break;
default:
printf("0"); break;
}
x = x + 1;
}
return 0;
}
理解了Switch的查表法,然后再配合汇编中的语法规范就可以巧妙地构造出Switch结构.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MemArray DWORD 0,0,0,0,0,0,0,0,0,0
Count DWORD ?
szFmt BYTE '%d ',0dh,0ah,0
.code
main PROC
; 将指定标号的地址取出,并复制到数组
mov dword ptr ds:[MemArray],offset S0
mov dword ptr ds:[MemArray + 4],offset S1
mov dword ptr ds:[MemArray + 8],offset S2
mov dword ptr ds:[MemArray + 12],offset S3
mov dword ptr ds:[MemArray + 16],offset S4
mov dword ptr ds:[MemArray + 20],offset S_END
mov dword ptr ds:[Count],0
L1: mov ecx,dword ptr ds:[Count]
cmp ecx,6
jg lop_end
; 通过循环次数寻找指令地址并跳转
mov ecx,dword ptr ds:[Count]
cmp ecx,6
jg S_END
jmp dword ptr ds:[MemArray + ecx * 4]
S0: mov eax,1
invoke crt_printf,addr szFmt,eax
jmp L2
S1: mov eax,2
invoke crt_printf,addr szFmt,eax
jmp L2
S2: mov eax,3
invoke crt_printf,addr szFmt,eax
jmp L2
S3: mov eax,4
invoke crt_printf,addr szFmt,eax
jmp L2
S4: mov eax,5
invoke crt_printf,addr szFmt,eax
jmp L2
S_END: mov eax,0
invoke crt_printf,addr szFmt,eax
jmp L2
L2: mov ecx,dword ptr ds:[Count]
add ecx,1 ; ecx ++
mov dword ptr ds:[Count],ecx
jmp L1
lop_end:
int 3
main ENDP
END main
Loop实现排序: 如果不自己构建排序循环,使用loop实现,则冒泡排序将变得香。
先来看一个汇编案例,我想说,观察下面的代码,你说 这是不是一个死循环呢?思考一下。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
Count DWORD 10
.code
main PROC
mov ecx,dword ptr ds:[Count]
dec ecx
L1: push ecx
invoke crt_printf,addr szFmt,ecx
pop ecx
loop L1
main ENDP
END main
不是,loop人家执行的时候,会自动的将ecx中的值减去1,所以他不是死循环,来实现一下这个需求。
#include <windows.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
int Array[10] = { 56,88,34,67,98,25,67,10,87,43 };
int x=10, y, temporary;
while (x - 1 > 0)
{
y = x;
while (y > 0)
{
if (Array[y] > Array[y - 1])
{
temporary = Array[y - 1];
Array[y - 1] = Array[y];
Array[y] = temporary;
}
y--;
}
x--;
}
for (int x = 0; x < 10; x++)
printf("%d ", Array[x]);
return 0;
}
然后使用loop实现双层夹心汉堡,口感同样一级棒.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MyArray DWORD 25,74,89,33,24,67,93,15,78,92
Count DWORD 10
PrCount DWORD ?
szFmt BYTE '%d ',0dh,0ah,0
.code
main PROC
; 数组排序
mov ecx,dword ptr ds:[Count] ; 获取到数组元素数
dec ecx ; 数组减1
L1: push ecx ; 入栈保存
lea esi,dword ptr ds:[MyArray] ; 得到数组基地址
L2: mov eax,dword ptr ds:[esi]
cmp eax,dword ptr ds:[esi + 4] ; 比较第一个数组与第二个
jg L3
xchg eax,[esi + 4] ; 交换数据
mov [esi],eax
L3: add esi,4
loop L2
pop ecx ; 弹出数据
loop L1
; for循环输出元素
mov dword ptr ds:[PrCount],0
jmp LL1
mov ecx,dword ptr ds:[PrCount]
add ecx,1
mov dword ptr ds:[PrCount],ecx
LL1:
mov ecx,dword ptr ds:[PrCount]
cmp ecx,10
jg lop_end
lea eax,dword ptr ds:[MyArray]
mov ebx,dword ptr ds:[eax + ecx * 4]
invoke crt_printf,addr szFmt,ebx
mov ecx,dword ptr ds:[PrCount]
add ecx,1
mov dword ptr ds:[PrCount],ecx
jmp LL1
lop_end:
int 3
main ENDP
END main
While 实现(二分法): 二分查找法也是常用查找结构,主要思想是对半分,如果中位数大于则说明元素在前半部分,如果小于则说明在后半部分,该排序唯一需要注意的是,数组必须是一个有序集合.
#include <windows.h>
#include <stdio.h>
int BinSearch(int value[], const int searchVal, int Count)
{
int first = 0;
int last = Count - 1;
while (first <= last)
{
int mid = (last + first) / 2; // 取中位数
if (value[mid] < searchVal) // 中位数小于searchVal
{ // 说明元素在后面
first = mid + 1;
}
else if (value[mid] > searchVal)
{ // 否则说明元素在前
last = mid - 1;
}
else
{ // 找到后返回中位数
return mid;
}
}
return -1;
}
int main(int argc, char *argv[])
{
// 二分查找法,必须针对的是有序数组
int Array[10] = { 1,2,3,4,5,6,7,8,9,10 };
// 查找数组Array中索引7所在的下标
int ret = BinSearch(Array, 7, 10);
printf("数组下标: %d \n", ret);
system("pause");
return 0;
}
接着是尝试使用汇编实现这个查找逻辑,完整版代码已经写好了
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MyArray DWORD 1,2,3,4,5,6,7,8,9,10
SearchVal DWORD 7
Count DWORD 10
first DWORD ?
last DWORD ?
mid DWORD ?
szFmt BYTE '%d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[first],0 ; first = 0;
mov edi,dword ptr ds:[SearchVal] ; 得到要查找的数
lea ebx,dword ptr ds:[MyArray] ; 得到数组基地址
; int last = Count - 1;
mov eax,dword ptr ds:[Count]
sub eax,1
mov dword ptr ds:[last],eax
; while(first <=last)
L1: mov ecx,dword ptr ds:[first]
cmp ecx,dword ptr ds:[last]
jg lop_end
; int mid = (last + first) / 2;
mov eax,dword ptr ds:[last]
add eax,dword ptr ds:[first]
shr eax,1
mov dword ptr ds:[mid],eax
; edx = value[mid]
mov esi,dword ptr ds:[mid]
shl esi,2
mov edx,[ebx + esi]
;invoke crt_printf,addr szFmt,edx
; if(edx < SearchVal(edi))
cmp edx,edi
jge L2
; first = mid + 1;
mov eax,dword ptr ds:[mid]
add eax,1
mov dword ptr ds:[first],eax
jmp L1
L2:
; else if (value[mid] > searchVal)
cmp edx,edi
jle L3
; last = mid - 1;
mov eax,dword ptr ds:[mid]
sub eax,1
mov dword ptr ds:[last],eax
jmp L1
L3: ; else
mov eax,dword ptr ds:[mid]
invoke crt_printf,addr szFmt,eax
jmp lop_end
jmp L1
lop_end:
mov eax,-1
int 3
main ENDP
END main
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!