汇编语言学习 上

简介: 本文是介绍汇编语言的基本组成和基本齐全的指令,以及帮助理解这些的背景知识包括寄存器,汇编语言基本组成部分,数据传送指令,寻址指令,加减法指令,堆栈,过程,条件处理,整数运算

 本文是介绍汇编语言的基本组成和基本齐全的指令,以及帮助理解这些的背景知识

包括寄存器汇编语言基本组成部分数据传送指令,寻址指令,加减法指令堆栈过程条件处理整数运算

帮助读者在很短的时间内对汇编语言有一个总体的认知,学会查看汇编代码和掌握学习编写汇编程序的能力,深入学习汇编语言的相关知识可以查阅《汇编语言 基于x86处理器》

下一篇文章汇编语言学习笔记 下

image.gif编辑



汇编语言

image.gif编辑

MASM是Microsoft Macro Assembler的缩写,Microsoft宏汇编器

汇编语言的特点

1.与机器语言很接近

2.需要了解计算机架构和操作系统,直接访问计算机硬件

3.学习汇编语言的基本语法,不需要深入了解操作系统和计算机架构,最好使用过一门编程语言进行编程,逻辑是相通的

4.汇编器将汇编语言转换成机器语言

5.链接器将汇编器生成的单个文件组合成一个可执行文件

6.汇编语言不可移植

汇编语言的使用领域

1.C或C++开发者需要了解汇编,很多编程错误在高级语言难以识别,需要深入到程序内部,理解内存,地址和指令是如何在底层工作的

2.编写嵌入式程序,例如显卡,声卡,打印机,硬盘驱动器

3.高级语言嵌入汇编,可以对程序进行优化

4.帮助理解计算机硬件,操作系统,应用程序之间的交互关系

5.进行逆向分析时候,分析反汇编器生成的汇编代码

汇编语言与机器语言,C++,Java,Python

汇编语言与机器语言

汇编语言和机器语言是一一对应的关系(one-to-one)

一句汇编语言对应一句机器语言

机器语言用数字编写,汇编语言使用助记符ADD,MOV,PUSH

汇编语言与C++,Java,Python

Java,Java,Python与汇编语言是一对多的关系(one-to-many)

高级语言是可以移植的,汇编语言不可移植

基本微机设计

CPU包含了有限数量的寄存器,一个高频时钟,一个控制单元CU,一个算数逻辑单元ALU

CPU通过主板上CPU插座的引脚和计算机其他部分相连

大部分引脚连接的是数据总线,控制总线和地址总线

总线

总线是一组并行线

分为四类总线:

数据类,I/O类,控制类,地址类

时钟

机器指令的时间单位是时钟周期

一个时钟周期是一个完整脉冲所需要的时间

一个机器指令最少需要一个时钟周期

内存

image.gif编辑

ROM   永久烧录在芯片上,不能擦除

image.gif编辑

EPROM  只能用强紫外线照射来擦除,可以重新编程

image.gif编辑

DRAM 最常用的一种电脑内存。它通常使用一个晶体管和一个电容器来代表一个比特。价格便宜,每毫秒需要刷新,避免丢失内容。

image.gif编辑

SRAM 静态随机存取存储器,所谓的“静态”,是指这种存储器只要保持通电,里面储存的数据就可以恒常保持,不需要刷新。CPU的cache就是SRAM构成的。

image.gif编辑

VRAM 影像随机接达记忆器,作为影像绘图卡、显卡所使用的DRAM,就是广义上说的显存。

image.gif编辑

CMOS RAM 在系统主板,保存系统设置信息。由电池供电。计算机关闭后,CMOS RAM中的内容仍然能保留。

image.gif编辑

虚拟内存

虚拟内存是计算机系统内存管理的一种技术,他使计算机认为拥有连续的可用的内存。通常有多个物理内存碎片,还有部分在外部磁盘存储器上。大多数操作系统都使用了虚拟内存。虚拟内存可看作是一个存放在磁盘上的N个连续的字节大小的单元组成的数组。每个字节都有一个虚拟地址,作为到数组的索引。

寄存器

四类寄存器

通用寄存器

段寄存器

程序与控制寄存器

指令指针寄存器

32位机

通用寄存器

EAX (针对操作数和结果数据的)累加器
EBX (DS段的数据指针)基址寄存器
ECX (字符串和操作循环的)计数器
EDX (I/O)指针数据寄存器
ESI (字符串操作源指针)源变址寄存器
EDI   (字符串操作目标指针)目的变址寄存器
EBP   (SS段中栈内数据指针)扩展基址指针寄存器
ESP (SS段中栈指针)栈指针寄存器

通用型的寄存器,用于传送和暂存数据

ESP   PUSH,POP,CALL,RET可以直接用来操作ESP

EBP  函数调用存储ESP,函数返回把值返回ESP

段寄存器

CS 代码段寄存器
SS 栈段寄存器
DS 数据段寄存器
ES 附加数据段寄存器
FS 数据段寄存器
GS 数据段寄存器

每个16位

程序状态与控制寄存器

EFLAGS 标志寄存器

image.gif编辑

32位,每个位置记录一种状态

指令指针寄存器

EIP 指令指针寄存器

CPU从中读取一条指令的地址,指令传送到缓存区后,EIP地址自动增加

64位机

十六位机中E改为R,增加了R8~R15八个通用寄存器

通用寄存器

RAX (针对操作数和结果数据的)累加器
RBX (DS段的数据指针)基址寄存器
RCX (字符串和操作循环的)计数器
RDX (I/O)指针数据寄存器
RSI (字符串操作源指针)源变址寄存器
RDI   (字符串操作目标指针)目的变址寄存器
RBP   (SS段中栈内数据指针)扩展基址指针寄存器
RSP (SS段中栈指针)栈指针寄存器
R8~R15

补充

AL是AX寄存器的低八位

AH是AX寄存器的高八位

AX是EAX的低16位

汇编语言基本组成部分

整数常量

由三部分组成

可选前置符号(+/-) 一个或多个数字 可选基数字符(指明基数)

可选基数字符是指明进制类型,可选是因为没有基数的情况默认为十进制

基数值

十六进制 h
八进制 q/o
十进制 d
二进制 b
实数 r

例如:

26  ;十进制

3Ah ;十六进制

17o ;八进制

image.gif编辑

如图反汇编器生成的汇编代码中h基数结尾的表示16进制整数

整数常量表达式

算术运算符

运算符 名称 优先级
() 圆括号 1
+,- 一元加减 2
*,/ 乘除 3
MOD 取模 4
+,- 一元加减 2

浮点数常量

real number literial

一个可选符号 一个整数 小数点 表示小数部分的一个整数 一个可选的指数

例如

5.

+2.0

26.E5

-78.2E+05

x86指令集是针对整数处理的,浮点数用的少

字符常量

用单引号和双引号包含的一个字符

"A"

'a'

使用ASCLL编码

"A"在内存中存放的形式65,41h

字符串常量

用单引号和双引号包含的一个字符串

"Hello world"

"Peter"

'Good'

可以用单引号嵌套双引号,也可以双引号嵌套单引号

"That's good"

'Say"Hello"'

保留字

有特殊意义并且只能在正确的上下文使用

    • 指令助记符 例如ADD
    • 寄存器名称
    • 伪指令
    • 属性  例如BYTE,DWORD
    • 运算符
    • 预定义符号

    标识符

    不区分大小写

    第一个字符必须是字母,下划线,@,?,$

    后面的字符可以是数字

    伪指令

    作用

    定义宏,变量,子程序

    由汇编器识别和运行,不在运行时执行

    var dword 66  ;伪指令,为一个双字变量保留空间

    mov eax,var ;指令,含义后面会讲

    定义段

    定义程序的段

    例如

    .data   ;数据段,定义变量

    .code    ;code段包含了可执行的指令

    .stack 100h    ;定义了运行时的堆栈和大小

    指令

    标号

    是一种标识符,是指令和数据的位置标记

    数据标号

    var DWORD 89   ;定义了一个变量var

    代码标号

    target:

           mov     ebp, esp

           sub     esp, 68h

           push    ebx

           push    esi

    L1:mov     ebp, esp

    代码标号用于跳转和循环和目标

    指令助记符

    是标记一个指令的单词

    例如ADD,MOV,JMP

    注释

    单行注释   使用;

    ;注释内容

    块注释   使用COMMENT伪指令和一个用户定义的符号开始,相同的符号结束

    COMMENT @

        注释内容

    @

    汇编语言内部数据类型

    BYTE    8 位无符号整数,B 代表字节

    SBYTE    8 位有符号整数,S 代表有符号

    WORD    16 位无符号整数

    SWORD    16 位有符号整数

    DWORD    32 位无符号整数,D 代表双(字)

    SDWORD    32 位有符号整数,SD 代表有符号双(字)

    FWORD    48 位整数(保护模式中的远指针)

    QWORD    64 位整数,Q 代表四(字)

    TBYTE    80 位(10 字节)整数,T 代表 10 字节

    REAL4    32 位(4 字节)IEEE 短实数

    REAL8    64 位(8 字节)IEEE 长实数

    REAL10    80 位(10 字节)IEEE 扩展实数

    数据定义语句例:count BYTE 10

    数据传送指令,寻址指令,加减法指令

    数据传送指令

    MOV指令

    将源操作数复制到目的操作数

    MOV 目的操作数,源操作数

    mov     ebp, esp

    逻辑相当于高级语言中的ebp=esp

    XCHG指令

    XCHG指令交换两个操作数的内容

    XCHG eax,ebx

    偏移量操作数

    array BYTE 10h,20h,30h,40h,50h

    mov eax,array          ;eax=10h

    mov eax,[array+1]    ;eax=20h

    mov eax,[array+2]    ;eax=30h

    NOP指令

    是一个空操作

    占用一个字节,不做任何操作,用于将代码对齐到有效的地址边界

    加法指令和减法指令

    INC指令

    表示寄存器或内存操作数自减1

    INC reg/men

    DEC指令

    表示寄存器或内存操作数自减1

    DEC reg/men

    ADD指令

    将源操作数加在目的操作数上(可以是寄存器或者变量)

    ADD eax,var1

    SUB指令

    将目的操作数减去源操作数,存在目的操作数中(可以是寄存器或者变量)

    SUB eax,var1

    NEG指令

    取操作数的补码,存在操作数中

    NEG eax

    NEG var

    与数据有关的运算符和伪指令

    OFFSET运算符

    返回一个变量与其所对在段起始地址之间的距离

    ALIGN伪指令

    将一个变量对齐到字节边界,字边界,双字边界或者段落边界

    ALIGH bound

    bound可以取值为1,2,4,8,16

    定义后,变量地址将是bound的整数倍

    PTR运算符

    重写操作数默认的大小类型,必须和一个标准汇编数据类型一起使用

    TYPE运算符

    返回变量单个元素的大小

    var1 BYTE ?

    TYPE var1  ;值为1

    var2 WORD ?

    TYPE var2  ;值为2

    LENGTHOF运算符

    返回数组中元素的个数

    SIZEOF运算符

    返回数组占用的字节数

    JMP和LOOP指令

    JMP指令

    无条件跳转到一个地址,这个地址可以使用代码标号

    使用JMP可以创建循环

    top:

           ...

           jmp top

    是一个死循环

    LOOP指令

    ECX计数器循环

    ECX是计数器

    第一步,ECX的数值减1

    第二步,ECX与0比较,如果等于0循环终止

    mov eax,0
        mov ecx,5
    L:
        inc eax
        loop L1

    image.gif

    这个汇编程序会循环5次

    汇编语言程序范例

    ;Addtwo.asm  -add two 32-bit integers
    .386
    .model flat,stdcall
    .stack 4096
    ExitProcess PROTO,dwExitCode:DWORD
    .code
    main PROC
        mov eax 3
        add eax 2
        INVOKE ExitProcess,0
    main ENDP
    END main

    image.gif

    .386伪指令表示这是一个32位程序,访问32位寄存器和地址

    .model flat,stdcall表示内存模式是flat,子程序的调用规范是stdcall

    .stack 4096        运行时堆栈保留了4096字节的存储空间

    Exit是一个标准的Windows服务

    main PROC表示要执行的第一个指令的位置

    main ENDP表示一个过程的结束

    END main表示汇编程序的结束

    堆栈

    堆栈数据结构

    堆栈数据结构就是我们数据结构课中学到的栈

    image.gif编辑

    栈顶添加新元素,删除元素在栈顶删除

    后进去先出FILO

    本文要讲的是运行时堆栈

    运行时堆栈

    运行时堆栈是内存数组

    ESP寄存器

    ESP寄存器(extended stack pointer,扩展堆栈指针)

    可以理解为抽象数据结构栈中指向栈顶的指针

    存放某个位置的32位偏移量

    基本上不会被程序员修改

    用CALL,RET,PUSH,POP指令间接修改

    入栈操作

    32位入栈操作是把栈顶指针减4,再将数值复制到栈顶指针指向的位置

    image.gif编辑

    运行时堆栈在内存中是向下生长的,从高地址向低地址扩展

    出栈操作

    从堆栈删除元素,站定指针减小

    PUSH指令

    首先减少ESP的值,将操作数复制到堆栈

    POP指令

    将ESP指向的堆栈元素复制到一个操作数当中,增加ESP的值

    PUSHFD指令

    将32位EFLAGS寄存器的内容压入堆栈

    POPFD指令

    将栈顶元素弹出到32位EFLAGS寄存器

    PUSHAD指令

    按照EAX,ECX,EDX,EBX,ESP,EBP,ESI,EBI的顺序

    将所有32位通用寄存器压入堆栈

    POPAD指令

    按照与PUSHAD相反的顺序将其弹出堆栈

    过程

    有过高级语言编程经验的话,我们知道为了增加一个程序的可读性,便于阅读,调试和合作,应该将程序封装成函数和方法

    image.gif编辑

    这些函数和方法就是一个子程序

    在汇编语言中这些子程序叫做过程

    image.gif编辑

    过程是可以嵌套调用的

    定义主过程

    main PROC
    .
    .
    main ENDP

    image.gif

    其他过程

    pro PROC
        .
        .
        ret
    pro ENDP

    image.gif

    全局标号

    当跳转和循环时候

    jmp destination

    destination必须和JMP指令位于同一过程中

    可以使用全局标号解决这一问题,在名字后面加双冒号::

    CALL指令

    调用一个过程

    将返回地址压入堆栈,将被调用的过程地址复制到指令地址寄存器EIP

    RET指令

    结束一个过程调用

    从堆栈将返回地址复制到指令地址寄存器EIP

    向过程传递寄存器参数

    在调用过程之前,将数值传递给寄存器

    寄存器中保留传递的参数

    条件处理

    布尔操作和比较指令

    AND指令,OR指令,XOR指令,NOT指令

    分别将目的操作数与源操作数进行与,或,异或,非操作

    将结果存放在目的操作数当中

    TEST指令

    进行与操作,更新CPU标志位

    CMP指令

    比较两个数字

    隐含减法操作,但是不修改任何操作数

    比较两个无符号数]

    标志位在EFLAGS寄存器上

    image.gif编辑

    ZF是零标志位,CF是进位标志位,OF是溢出标志位

    CMP结果 ZF CF
    目的操作数<源操作数 0 1
    目的操作数>源操作数 0 0
    目的操作数=源操作数 1 0

    比较两个有符号数

    CMP结果 标志位结果
    目的操作数<源操作数 SF不等于OF
    目的操作数>源操作数 SF=OF
    目的操作数=源操作数 ZF=1

    我们可以看出当两个数字相等的时候,ZF是1

    条件跳转

    Jcond指令

    JZ 为零跳转 ZF=1 JNO 无溢出跳转 OF=0

    JNZ 非零跳转 ZF=0 JS 有符号跳转 SF=1

    JC 进位跳转 CF=1 JNS 无符号跳转 SF=0

    JNC 无进位跳转 CF=0 JP 偶校验跳转 PF=1

    JO 溢出跳转 OF=1 JNP 奇校验跳转 PF=0

    cmp eax,0
        jz L1        ;当ZF是1时候跳转
        .
        .
    L1:
        .
        .

    image.gif

    基于相等性的跳转,基于无符号数的跳转指令,基于有符号数比较的跳转

    cmp leftOp,reghtOp

    基于相等性的跳转

    JE 相等跳转(leftOp = reghtOp)

    JNE 不相等跳转(leftOp ≠ reghtOp)

    JCXZ CX = 0 跳转

    JECXZ ECX = 0 跳转

    JRCXZ RCX = 0 跳转(64位模式)

    基于无符号数的跳转指令

    JA    大于跳转(若leftOp > reghtOp)

    JNBE    不小于或不等于跳转(同JA)

    JAE    大于或等于跳转(若leftOp ≥ rightOp)

    JNB    不小于跳转(同JAE)

    JB    小于跳转(若leftOp < reght)

    JNAE    不大于或不等于跳转(同JB)

    JBE    小于或等于跳转(若leftOp ≤ rightOp)

    JNA    不大于跳转(同JBE)

    基于有符号数比较的跳转

    JG    大于跳转(若leftOp > reghtOp)

    JNLE    不小于或不等于跳转(同JG)

    JGE    大于或等于跳转(若leftOp ≥ rightOp)

    JNL    不小于跳转(同JGE)

    JL    小于跳转(若leftOp < reght)

    JNGE    不大于或不等于跳转(同JL)

    JLE    小于或等于跳转(若leftOp ≤ rightOp)

    JNG    不大于跳转(同JLE)

    整数运算

    移位指令

    算术移位和逻辑移位

    逻辑移位是对无符号数进行的,左移和右移都是补零

    算术移位是对有符号数进行的,空出来的符号位用原数据的符号位填充

    image.gif编辑

    SHL指令

    逻辑左移指令,将目的操作数顺序左移1位或CL寄存器中指定的位数。左移一位时,操作数的最高位移入进位标志位CF,最低位补零。

    SHR指令

    逻辑右移指令,SHR指令将目的操作数顺序右移1位或CL寄存器指定的位数。逻辑右移1位时,目的操作数的最低位移到进位标志位CF,最高位补零。

    SAL指令

    算术左移指令,最高位移入CF,最低位补0。

    SAR指令

    算术右移指令

    ROL指令

    所有位都向左移,最高位复制到CF和空出来的最低位

    ROR指令

    所有位都向右移,最低位复制到CF和空出来的最低位

    RCL指令

    带进位循环左移,CF移动到空出来的最低位,最高位移动到CF

    RCR指令

    带进位循环右移,最低位移动到CF,CF移动到空出来的最高位

    SHLD指令

    双精度左移

    SHLD dest,source,count

    源操作数的最高位移动到目的操作数的最低位,目的操作数都左移,目的操作数的最高位移动到CF

    image.gif编辑

    SHRD指令

    双精度右移

    SHLD dest,source,count

    源操作数最低位移动到目的操作数空出来的最高位,目的操作数最低位移动到CF,其余位右移

    image.gif编辑

    乘法指令

    MUL指令(32位模式)

    32位机中有三种乘法,一种乘法示例:

    只有一个操作数,实现一个8位操作数和AL寄存器的乘法,存入AX

    AL是AX寄存器的低八位

    AH是AX寄存器的高八位

    AX是EAX的低16位

    范例

    mov al 5h
    mov bl 10h
    mul bl  ;AX=0050  CF=0

    image.gif

    当AX的高半部分是0的时候,CF是0

    三种类型

    被乘数 乘数 乘积
    AL reg/mem8 AX
    AX reg/mem16 DX:AX
    EAX reg/mem32 EDX:EAX

    MUL指令(64位模式)

    新增的类型

    被乘数 乘数 乘积
    RAX reg/mem64 RDX:RDX

    IMUL指令(32位模式)

    有符号数乘法

    X86支持三种类型的IMUL指令:

    单操作数,双操作数,三操作数

    单操作数格式与MUL一样

    三种类型

    被乘数 乘数 乘积
    AL reg/mem8 AX
    AX reg/mem16 DX:AX
    EAX reg/mem32 EDX:EAX

    双操作数格式

    将两个操作数的乘积放在第一个操作数当中,第一个操作数必须是寄存器

    三操作数格式

    第一个操作数必须是一个16位寄存器

    第二个操作数是一个16位置寄存器或者内存操作数

    第三个操作数是一个8位或者16位的立即数

    或者

    第一个操作数必须是一个32位寄存器

    第二个操作数是一个32位置寄存器或者内存操作数

    第三个操作数是一个16位或者32位的立即数

    IMUL指令(64位模式)

    同MUL一样添加了64位数相乘的情况

    DIV指令(32位模式)

    被除数 除数 余数
    AX reg/mem8 AL AH
    DX:AX reg/mem16 AX DX
    EDX:EAX reg/mem32 EAX EDX

    DIV指令(64位模式)

    被除数 除数 余数
    RDX:RAX reg/mem64 RAX RDX

    IDIV指令指令

    有符号数除法

    篇幅有点长,后续放在下一篇文章汇编语言学习笔记 下

    点击继续阅读

    image.gif编辑


    相关文章
    王道408计组汇编语言部分学习总结
    用于实现分支结构、循环结构的指令: cmp、 test、 jmp、 jxxx 用于实现函数调用的指令: push、pop、call、 ret 用于实现数据转移的指令: mov
    436 0
    |
    3月前
    |
    存储 算法 NoSQL
    天呐!汇编语言竟如此神奇,从零到精通的学习指南带你开启计算机世界神秘大门!
    【8月更文挑战第31天】汇编语言是一种底层编程语言,直接与硬件交互,对于理解计算机体系结构和底层原理至关重要。尽管现代软件开发中较少使用,但学习汇编语言有助于深入了解计算机如何执行指令、管理内存和处理数据,从而优化程序性能,进行底层系统开发和调试。不同处理器有不同指令集,如 x86 和 ARM,掌握这些指令集及寄存器、内存地址等基本概念是学习汇编语言的基础。通过简单示例开始,逐步掌握复杂指令和调试工具,可以大大提高编程技能和解决问题的能力。
    69 1
    |
    1月前
    |
    存储 Go
    汇编语言的学习
    本文详细介绍了在DOSBox中学习汇编语言的基础知识,包括寄存器的使用、数据和指令的选取、栈的操作、段地址的计算以及如何编写和执行简单的汇编程序。
    19 0
    汇编语言的学习
    |
    5月前
    |
    程序员 索引
    逆向学习汇编篇:内存管理与寻址方式
    逆向学习汇编篇:内存管理与寻址方式
    52 0
    |
    6月前
    |
    编译器
    【Bug】8086汇编学习
    随笔 与汇编的灵活与底层所伴随的,是使用者极大的心智负担。 模块之间的隔离性很难控制。甚至多个子程序间的标号也不能相同,故子程序无法独立开发后直接集成。
    146 5
    |
    6月前
    汇编指令学习(寻址方式)
    汇编指令学习(寻址方式)
    59 0
    |
    6月前
    汇编指令学习(LOOP)
    汇编指令学习(LOOP)
    81 0
    |
    6月前
    汇编指令学习(CALL,JMP,RET)
    汇编指令学习(CALL,JMP,RET)
    93 0
    |
    6月前
    |
    JavaScript
    汇编指令学习(JMP、JE、JS、JP,JO,JB)
    汇编指令学习(JMP、JE、JS、JP,JO,JB)
    435 0
    |
    6月前
    汇编指令学习(CMP,TEST)
    汇编指令学习(CMP,TEST)
    138 0