32位汇编代码编写环境:Visual Studio(笔者用的版本为2017);先来说一下在Visual Studio 2017中编写汇编代码的准备操作:
①创建空项目
②设置项目属性:平台工具集设置为Visual Studio 2015(v140),因为一些库再2015版本后取消了;
③为该项目生成自定义依赖项;
勾选masm文件
④设置汇编代码入口点为main
⑤为了方便阅读代码,可以下载插件
此处我们下载Asm Dude插件,帮助我们更好地编写代码(该插件可以高亮汇编代码)
此时就可以在项目中添加汇编源文件,此时的源文件的后缀名为.asm;
添加完成后就可以开始编写代码了。
代码基本框架
①指定启用的指令集;
在汇编语言编程中,指令集是处理器所支持的一系列指令的集合。在MASM(Microsoft Macro Assembler)中,用于指定处理器的指令集有:
①.8086 描述:启用Intel 8086处理器指令集。 特点:仅支持8086及其兼容处理器的指令。 ②.186 描述:启用Intel 80186处理器指令集。 特点:支持8086的指令集以及80186的新指令。 ③.286 描述:启用Intel 80286处理器指令集。 特点:支持实模式和保护模式,增加了新指令。 ④.386 描述:启用Intel 80386处理器指令集。 特点:支持32位操作,增加了更多的寄存器和指令。 ⑤.486 描述:启用Intel 80486处理器指令集。 特点:增加了一些新的指令和性能优化。 ⑥.586 描述:启用Intel Pentium(80586)处理器指令集。 特点:支持更高级的指令和优化。
②指定程序使用的内存模型和调用约定
指定程序使用的内存模型和调用约定时可以使用.model
指令;
内存模型
在DOS下的16位汇编程序中,内存模型有不同的选择(如tiny, small, compact, medium, large, huge),但在32位或64位模式下,这通常设为 flat
(扁平模型),这意味着整个程序和数据都在一个线性地址空间中。
调用约定
调用约定(Calling Conventions)定义了函数调用时参数的传递方式、返回值的处理方式以及栈的清理;不同的调用约定在参数传递顺序、寄存器使用、栈管理等方面有所不同。以下是几种常见的调用约定:
①cdecl(C Declaration):主要用于C语言程序中;参数从右到左压入栈中,调用者(caller)负责清理栈。这意味着调用者在函数调用结束后要调整栈指针以清理传递的参数。 ②stdcall:主要用于Windows API和一些其他情况下,调用约定规定被调用者(callee)负责清理栈。 ③fastcall:通过寄存器传递函数参数,从而提高函数调用的效率,相比于将所有参数压入栈中,使用寄存器传递参数可以减少内存访问次数,从而提升性能。 ④thiscall:主要用于C++类的成员函数调用;在thiscall调用约定下,this指针(即当前对象的指针)通过寄存器传递,而其他参数通常通过栈传递。 ⑤vectorcall:主要用于优化处理器支持SIMD(Single Instruction, Multiple Data)指令集的情况下向量和浮点数参数的函数调用,这种调用约定旨在通过使用寄存器传递参数来提高性能,特别是在处理大量浮点数和向量数据时。
③可以设置其他可选参数
此处我们可以使用option
指令设置汇编器的各种选项,如如代码是否区分大小写;若此时要设置代码不区分大小写则可以按照如下方法进行编写:
option casemap:none
最简单的x86汇编程序:
汇编代码设置了汇编器的一些选项,定义了程序的内存模型和函数的调用约定,并且包含了数据段和代码段的定义,以及一个简单的过程 main
:
.586
.model flat,stdcall
option casemap:none
.data ;数据段
.code ;代码段
main proc ;入口点
mov eax,64h
mov ebx,65h
main endp
end
.586
:这个指令告诉汇编器使用 Intel 80386 处理器及其支持的指令集。.586
指令启用了对这些指令的支持。
.model flat, stdcall
:这是一个模型指令,用于定义程序的内存模型和函数的调用约定。
-
flat
是指定了程序使用的内存模型。在这里,flat
内存模型表示程序使用的是平坦内存模型,即所有的内存地址都是在一个统一的地址空间中。 -
stdcall
是一个调用约定标识符,指示函数的调用约定。在这种调用约定下,函数的参数从右到左依次入栈,调用者清理栈。
option casemap:none
:这个选项指令设置了汇编器的一些选项;casemap:none
设置了符号不进行大小写转换,这意味着汇编器不会将符号强制转换为特定的大小写格式,符号将保持其原始的大小写形式。
.data
:是一个伪操作符,用于标记数据段的开始;在数据段中,程序可以定义变量和常量。
.code
:是一个伪操作符,用于标记代码段的开始;在代码段中,程序包含了执行的指令。
main proc
:是定义一个过程(procedure)的开始;这个过程是程序的入口点。(上述准备工作中已经在项目属性中将main
作为代码入口点)
main endp
:main endp
表示过程 main
的结束。
mov eax,64h
和 mov ebx,65h
:这两条指令将立即数 64h
和 65h
分别加载到寄存器 eax
和 ebx
中。
end
: 用于标记程序的结束。
点击本地Windows调试器运行该代码;
由于在程序中我们没有设置输出,则可以在Visual Studio的寄存器窗口查看代码运行情况;
根据寄存器中的情况可以看出代码正常运行:64h、65h分别被写入EAX
和EBX
两个寄存器中。