
引言本文介绍是宏汇编语言基础以及MASM宏汇编编译器基本使用方法不同汇编语言的指令集都大差不差主要是学会指令的语法及使用会使用反汇编语言来分析目标程序就行不必过于纠结不用汇编之间的差异。以下是本人的学习记录如有问题请各位博友指出。1.汇编语言的介绍汇编语言就是机器语言0或1的人类可读版本。它用简短的助记符如MOV、ADD、JMP来代替二进制指令用标号来代替内存地址。比如同样是把数字5存入寄存器机器语言是10110000 00000101而汇编语言是MOV AL, 5汇编语言是基于苦涩、难懂的机器语言而改进的便于人理解的语言并且现在的很多硬件设备都是用汇编语言作为主要语言进行程序烧入的汇编语言是计算机语言史上的一座里程碑。那么问题来了为什么不直接用高级语言因为汇编语言离硬件最近——它和机器语言几乎是一一对应的关系。每一行汇编代码都直接对应一条CPU指令。这意味着你用汇编写的每一行代码都在精确指挥硬件如何工作。这种精确控制是任何高级语言都无法提供的。2.汇编语言的“三足鼎立”架构典型应用特点x86Intel语法Windows、传统PC目标在前源在后MOV eax, 1x86ATT语法Unix/Linux/GCC内联汇编源在前目标在后movl $1, %eaxARM手机、嵌入式、M系列Mac精简指令集移动时代王者本文介绍的是x86 Intel语法。3. 基础知识3.1 进制我们平时常用的进制有二进制、八进制、十进制、十六进制它们的表示如下表。名称表示例子二进制0、11010八进制0712十进制0910十六进制09、AFA注意十六进制的A~F对于10进制的10~15后面进制转换会用到。3.2 进制转换3.2.1 位权一个数从右向左每一位的权值都是“进制的不同次方”。例如十进制123从右往左1 2 3 ↓ ↓ ↓ 10² 10¹ 10⁰所以123 1×10² 2×10¹ 3×10⁰ 100 20 3 1233.2.2 任意进制转换为十进制核心公式N aₙ×bⁿ aₙ₋₁×bⁿ⁻¹ ... a₁×b¹ a₀×b⁰其中b目标数字的原进制a每一位上的数字从最右边开始位权从b⁰开始简单记忆从右往左编号 0、1、2、3……每一位乘以“进制的对应次方”最后全部相加。例如二进制转十进制110101₂从右向左标记1 1 0 1 0 1 5 4 3 2 1 0因此110101₂ 1×2⁵ 1×2⁴ 0×2³ 1×2² 0×2¹ 1×2⁰ 32 16 0 4 0 1 53所以110101₂ 53₁₀八进制转十进制753₈从右向左标记7 5 3 2 1 0计算753₈ 7×8² 5×8¹ 3×8⁰ 7×64 5×8 3 448 40 3 491所以753₈ 491₁₀十六进制转十进制3F2₁₆其中F 15从右向左标记3 F 2 2 1 0计算3F2₁₆ 3×16² 15×16¹ 2×16⁰ 3×256 15×16 2 768 240 2 1010所以3F2₁₆ 1010₁₀3.2.3 十进制转其他进制十进制转换为二进制、八进制、十六进制需要使用“除基取余法”。例如十进制转二进制例如45₁₀不断除以 245 ÷ 2 22 …… 1 22 ÷ 2 11 …… 0 11 ÷ 2 5 …… 1 5 ÷ 2 2 …… 1 2 ÷ 2 1 …… 0 1 ÷ 2 0 …… 1余数从下往上读取101101因此45₁₀ 101101₂十进制转八进制83₁₀不断除以 883 ÷ 8 10 …… 3 10 ÷ 8 1 …… 2 1 ÷ 8 0 …… 1从下往上读取123因此83₁₀ 123₈十进制转十六进制687₁₀不断除以 16687 ÷ 16 42 …… 15 42 ÷ 16 2 …… 10 2 ÷ 16 0 …… 2将余数转换成十六进制15 F 10 A从下往上读取2AF所以687₁₀ 2AF₁₆3.3 数据宽度及表示范围单位所占位数字节数十六进制范围字节byte810~FF字word1620~FFFF双字dword3240~FFFFFFFF八字节qword6480-FFFFFFFFFFFFFFFF3.4 大、小端字节序字节序Byte Order是指多字节数据在内存中的存储顺序主要有两种类型大端字节序Big-Endian和小段字节序Little-Endian。大端字节序大端字节序是指数据的高位字节存储在内存的低地址端而低位字节存储在内存的高地址端。大端字节序符合我们的阅读习惯从左至右且符号位放在第一位判断正负数的效率较高但逻辑运行相对低。小端字节序小端字节序是指数据的低位字节存储在内存的低地址端而高位字节存储在内存的高地址端。小端字节序在数据类型的转换时效率较高因为直接取低位数据值即可。大端字节序和小端字节序的存储顺序示意图如下。存储十六进制0x12345678应用场景计算机内部处理大多数现代计算机的内部处理使用小端字节序因为这种方式能更有效地利用计算机电路。网络传输在网络传输中通常使用大端字节序。如TCP/IP协议使用大端字节序因此在网络通信中需要考虑字节序的转换问题以确保发送方和接受方对多字节数据的解释一致。我们的计算机通常是小端字节序存储数据。可通过一下程序来判断字节自己计算机的存储方式。取值比较法#includewindows.h #includestdio.h int main(){ DWORD dwSmallNum0x01020304; if(*(BYTE*)dwSmallNum0x04) printf(Small Sequence.\r\n); else printf(Big Sequence.\r\n); return 0; }直接转换比较法#includestdio.h #includewinsock2.h #pragma comment(lib,win2_32) int main(){ DWORD dwSmallNum0x01020304; if(dwSmallNumhtonl(dwSmallNum)) printf(Big Sequence.\r\n); else printf(Small Sequence.\r\n); return 0; }3.5 编译器安装工欲善其事必先利其器本文用到的是32位的宏汇编编译器。工具链接百度网盘 请输入提取码百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固支持教育网加速支持手机端。注册使用百度网盘即可享受免费存储空间https://pan.baidu.com/s/1Nz-tZKrg5t_T1sJU8H-V1g?pwd8tiv将压缩包解压后分别将编译器文件夹绝对路径和文件夹中的bin文件夹绝对路径添加到环境变量中即可在cmd中正常使用。3.6 汇编语言程序格式操作码与操作数汇编语言程序本质上由一条条指令构成而一条指令通常由操作码和操作数地址码组成。操作码用于说明“要做什么操作”。比如MOV数据传送ADD加法SUB减法JMP无条件跳转CMP比较操作数用于说明“对谁进行操作”或者“操作的数据在哪里”。比如MOV AX BX│ │ ││ │ └── 源操作数│ └──────── 目标操作数└────────────── 操作码表示将BX中的数据传送到AX中。标识符标识符包括标号Label和名字Name都是由程序员自定义主要用于表示程序中某个存储位置的逻辑地址但使用场景不同。对比标号名字使用语句执行性语句说明性语句分隔方式通常用冒号:通常用空格或制表符主要表示指令在主存中的逻辑地址变量、段、子程序等的逻辑地址主要作用指明分支、循环等的目的地址标识变量、段、子程序等示例LOOP:DATA DB 10如LOOP: ADD AX, BX JMP LOOP 其中 LOOP 是标号。 可以理解为 LOOP: ↓ 指向 ADD AX,BX 所在的逻辑地址 执行JMP LOOP就是跳转到 LOOP 对应的地址。DATA DB 10 DATA → 名字 DB → 说明性语句中的操作 10 → 数据 DATA 表示这段数据所在的逻辑地址。标识符最多由31个字母、数字以及规定的特殊符号如_、$、?、组成且不能由数字开头也不能是汇编程序采用的保留字。保留字是编程语言本身需要使用的各种具有特殊含义的标识符也称关键字关键字详细可见下表。类型示例作用硬指令助记符MOV、ADD、SUB、JMP表示 CPU 执行的指令伪指令助记符DB、DW、SEGMENT、ENDS指示汇编器如何处理程序寄存器名AX、BX、CX、DX表示 CPU 寄存器其他特殊符号/关键字PROC、ENDP、ASSUME等具有特定语法含义执行性语句标号 硬指令助记符 目标操作数,源操作数 ;注释LOOP: MOV AX, BX ; 将BX的内容送入AX说明性语句名字 伪指令助记符 参数,参数,....... ;注释msg dbhello, assembly!,13,10,0 ;定义一个字节类型的字符串变量变量名为msgmessage的缩写。13相当于回车符\r10相当于换行符\n0相当于字符串结束符NULL终止符包含伪指令include将常用的常量定义、过程说明、共享的子程序库等内容进行声明相当于c语言中包含头文件的作用。include io32.inc ;包含I/O库文件该库包含dismsg,dispint,readmsg,exit等函数整个语句类似于从语言的# include stdio.h段的简化定义段是把程序按照不同用途划分出来的一块逻辑区域。一个汇编程序通常会划分为不同的段例如汇编程序 │ ├── 代码段CODE │ └── 存放程序指令 │ ├── 数据段DATA │ └── 存放已经初始化的数据 │ ├── 未初始化数据段DATA? │ └── 存放未初始化的数据 │ └── 堆栈段STACK └── 存放堆栈数据“段的简化定义”指的是使用.DATA、.CODE、.STACK等简化段定义伪指令来定义程序中的各个段不需要像完整段定义那样写SEGMENT和ENDS。数据段定义伪指令.DATA创建一个数据段定义可读可写的变量等代码段定义伪指令.CODE创建一个代码段保留需要执行的可执行性语句堆栈段定义伪指令.STACK创建一个堆栈段。更多详情可参考汇编语言教程 | 汇编语言从入门到精通 | w3schools 中文网3.7 编译器的简单使用这是本次的示例代码;eg01.asm in windows console for MASM include io32.inc ;包含I/O库文件该库包含dismsg,dispint,readmsg,exit等函数整个语句类似于从语言的# include stdio.h .data ;数据段开始标志 msg dbhello, assembly!,13,10,0 ;定义一个字节类型的字符串变量变量名为msgmessage的缩写。13相当于回车符\r10相当于换行符\n0相当于字符串结束符NULL终止符 .code ;代码段开始标志 start: ;程序起始执行位置 mov eax,offset msg ;使用offset这个操作数计算msg的偏移量并将值传递给eax call dispmsg ;调用dispmsg这个库函数来显示eax中的字符串 exit 0 ;主程序结束位置 end start ;汇编代码结束位置Notepad2.exe是MASM目录中的asm代码编辑器可使用该编辑器直接编写汇编代码。快速开发方法进入MASM目录可从cmd窗口切换也可直接双击WIN32.bat。生成可执行文件make32 文件名 //也就说汇编代码.asm的前面部分自定义编译先生成目标模块文件.objml /c /coff 汇编文件 #/c 只编译不链接 #/coff 生成 COFF 格式 .obj生成可执行文件.exelink32 /subsystem:console 目标文件 /libpath:bin #/subsystem:console指定子系统为控制台生成控制台程序 #/libpath:bin指定库文件搜索路径告诉链接器到 bin 目录寻找 .lib 库文件调试程序指令ml /c /coff /Fl /Zi 汇编文件 #/Fl 生成 汇编列表文件 .lst.lst 列表文件通常可以看到源代码、地址、机器码等汇编信息。 #/Zi 生成调试信息用于调试程序让调试器能够把机器指令和源代码对应起来便于单步执行、查看变量等。link32 /subsystem:console eg01.obj /debug /libpath:bin #/debug 调试信息在生成的程序中保留/生成调试信息便于调试3.8 指令集CPU 的指令集架构ISA可以按照指令设计思想分为两种典型类型CISCComplex Instruction Set Computer复杂指令集计算机RISCReduced Instruction Set Computer精简指令集计算机二者的主要区别在于CISC 强调让单条指令完成更多功能让软件轻松硬件受累RISC 强调使用简单、规整的指令通过多条指令组合完成复杂任务让硬件轻松软件多写几条。CISC架构代表厂商/产品特点x86/x86-64Intel、AMD、海光、兆芯性能强、兼容性好、软件生态丰富但功耗较高、硬件复杂x86的32位版本现已逐步被64位x86-64取代IA--32Intel早期32位处理器如Pentiumx86的32位版本现已逐步被64位x86-64取代RISC架构代表厂商/产品特点ARM苹果M系列、高通骁龙、华为鲲鹏、飞腾低功耗、高效能、移动设备霸主服务器领域也在快速扩展RISC-V阿里平头哥、国芯科技、SiFive完全开源、模块化、无版权费生态正在快速建设中MIPS龙芯早期简洁、优化方便但生态逐渐萎缩龙芯已转向自研LoongArchAlpha申威高性能主要用于超算和特定领域3.9 计算机的基本结构微型计算机系统的基本组成硬件物理设备软件程序和文档现代计算机基本组成结构汇编语言程序员将硬件抽象为寄存器、存储器地址和输入输出地址。3.10 寄存器3.10.1 通用寄存器通用寄存器General Purpose Registers是最常用的寄存器用于存放运算数据和临时结果。x86提供了8个常用的x86通用寄存器32 位16 位高 8 位低 8 位主要用途EAXAXAHAL累加器存放函数返回值、算术运算结果EBXBXBHBL基址寄存器常用于存放内存基地址ECXCXCHCL计数器常用于循环计数和移位EDXDXDHDL数据寄存器存放乘除法的高位结果ESISI——源变址寄存器字符串操作的源地址EDIDI——目的变址寄存器常用于数组和字符串操作EBPBP——基址指针常用于定位栈帧ESPSP——栈指针指向当前栈顶3.10.2 段寄存器段寄存器用于指定当前使用的内存段。常见的16位段寄存器有6 个段寄存器名称主要用途CSCode Segment代码段寄存器指向当前正在执行的代码DSData Segment数据段寄存器指向程序的数据SSStack Segment栈段寄存器指向栈空间ESExtra Segment附加段寄存器可用于额外的数据段FSExtra Segment附加段寄存器现代系统中具有特殊用途GSExtra Segment附加段寄存器现代系统中具有特殊用途3.10.3 指针与变址寄存器这些32位寄存器主要用于访问内存存放内存地址寄存器名称主要作用EIPInstruction Pointer指令指针寄存器指向下一条要执行的指令ESPStack Pointer栈指针寄存器指向当前栈顶EBPBase Pointer基址指针寄存器定位当前函数的栈帧ESISource Index源变址寄存器保存源数据的地址或索引EDIDestination Index目的变址寄存器保存目标数据的地址或索引3.10.4 标志寄存器标志寄存器EFLAGS是一个 32 位寄存器每个比特位代表一个状态标志Flag。你不能直接读写整个 EFLAGS但 CPU 会根据运算结果自动更新这些标志位条件跳转指令则根据标志位决定是否跳转。常见标志位标志位名称作用CFCarry Flag进位标志无符号运算产生进位或借位时置 1PFParity Flag奇偶标志结果低 8 位中 1 的个数为偶数时置 1AFAuxiliary Carry Flag辅助进位标志低 4 位向高 4 位产生进位或借位时置 1ZFZero Flag零标志运算结果为 0 时置 1SFSign Flag符号标志运算结果最高位为 1 时置 1OFOverflow Flag溢出标志有符号运算发生溢出时置 1DFDirection Flag方向标志控制字符串操作的地址增减方向IFInterrupt Enable Flag中断允许标志控制是否允许可屏蔽中断TFTrap Flag陷阱标志用于单步调试参考汇编语言 – 寄存器 | 菜鸟教程待续...