汇编学习

文章目录

  • assembly
    • 由机器语言到汇编语言
      • 机器语言与机器指令
      • 汇编语言与汇编指令
      • 汇编优势
      • 汇编编写程序的过程
    • 计算机的组成
      • 指令和数据的表示
      • 计算机中的总线
    • 内存的读写与地址空间
      • 内存地址空间
      • 将各类存储器看作一个逻辑存储器——统一编址
    • 搭建汇编实验环境
    • 寄存器
      • “字”在寄存器中的存储
    • 学习汇编指令
      • 写出汇编指令执行的结果(1)
      • 写出汇编指令执行的结果(2)
    • 确定物理地址
      • 问题
      • 解决办法
      • 用不同的段地址和偏移地址形成同一个物理地址
    • Debug的使用
      • Debug能做什么?
      • 用R命令查看、改变CPU寄存器的内容
      • 用D命令查看内存中的内容
      • 用E命令改变内存中的内容
      • 用U命令将内存中的机器指令翻译成汇编指令
      • 用A命令以汇编指令的格式在内存中写入机器指令
      • 用T命令执行机器指令
      • 用Q命令退出Debug
    • 两个关键的寄存器
    • jmp指令
      • 修改CS、IP的指令
      • 转移指令 jmp
        • 同时修改CS、IP的内容
        • 仅修改IP的内容
    • DS与数据段
    • 栈及栈操作的实现
      • 栈结构
      • push 指令和pop指令的执行过程
    • 关于“段”的总结
    • 由源程序到程序运行
      • 由写出源程序到执行可执行文件的过程
    • [...]和(...)
      • [...]的规定与(...)的约定
        • [...]——(汇编语法规定)表示一个内存单元
        • (...)——(为学习方便做出的约定)表示一个内存单元或寄存器中的内容
    • Loop指令
      • 功能:
      • CPU 执行loop指令时要进行的操作
      • 要求
      • 计算 2^5
    • 段前缀的使用
      • 引入段前缀:一个“异常”现象及对策
    • 在代码段中使用数据
      • 解决办法
    • 将数据、代码、栈放入不同段

写在前面的话:

终于是开了这个新坑了,很早就想系统学习汇编了,好处有很多。可以为我转型 pwn 和 re 打下基础,而且 web 方向到后面必须得能从底层分析别人的源码。还有就是顺带应付一下期末考试,选修了汇编却什么也没学(悲

assembly

由机器语言到汇编语言

机器语言与机器指令

: 机器语言是机器指令的集合。
: 机器指令是一台机器可以正确执行的命令。
: 机器指令由一串二进制数表示,例 01010000

汇编语言与汇编指令

  • 汇编语言的主体是汇编指令。
  • 汇编指令和机器指令的差别在于指令的表示方法上
  • 汇编指令是机器指令便于记忆的书写格式。
  • 汇编指令是机器指令的助记符。

操作:将寄存器BX的内容送到AX中

  1. 机器指令: 1000100111011000
  2. 汇编指令:MOV AX, BX

汇编优势

  1. 效率
    1. 运行效率:开发软件核心部件,快速执行和实时响应
    2. 开发效率:做合适的事,开发效率无敌
  2. 底层:计算机以及外围设备的驱动程序
    1. 操作系统内核
    2. 嵌入式系统:家电,物联网

汇编编写程序的过程

汇编学习_第1张图片

汇编语言发展至今,有以下3类指令组成。

  1. 汇编指令:机器码的助记符,有对应的机器码。
  2. 伪指令:没有对应的机器码,由编译器执行,计算机并不执行
  3. 其他符号:如+、-、*、/等,由编译器识别,没有对应的机器码。

汇编学习_第2张图片

计算机的组成

CPU 是计算机的核心部件,它控制整个计算机的运作并进行运算。要想让一个CPU工作,就必须向它提供指令和数据。指令和数据在存储器(内存)中存放。离开了内存,性能再好的CPU也无法工作。

指令和数据的表示

计算机中的数据和指令,存储在内存或磁盘上。
数据和指令,都是二进制信息。
二进制信息1000100111011000是数据,还是指令?

; 1000100111011000 ─> 89D8H (数据)
; 1000100111011000 ─> MOV AX,BX (程序)

:数据如何表示?

; 1000100111011000B (二进制)
; 89D8H (十六进制)
; 104730O(八进制)
; 35288D(十进制)

:数据量:B、KB、MB、GB、TB…

计算机中的总线

在计算机中专门有连接CPU和其他芯片的导线,通常称为总线。 物理上:一根根导线的集合;
逻辑上划分为

  1. 地址总线

    1. CPU是通过地址总线来指
      定存储单元的。
    2. 地址总线宽度,决定了
      可寻址的存储单元大小。
    3. N 根地址总线(宽度为
      N),对应寻址空间 2^N

    汇编学习_第3张图片

  2. 数据总线 (地址总线找到对应内存块1后,通过数据总线传输数据)

    1. CPU与内存或其它器件之间的数据传送是通过
      数据总线来进行的。
    2. 数据总线的宽度决定了CPU和外界的数据传送
      速度。

    例:向内存中写入数据89D8H时的数据传送,8088(8 位数据总线),8086(16 位数据总线) 的区别

    汇编学习_第4张图片

  3. 控制总线

    1. CPU通过控制总线对外部
      器件进行控制。
    2. 控制总线是一些不同控制
      线的集合
    3. 控制总线宽度决定了CPU
      对外部器件的控制能力

内存的读写与地址空间

学习汇编就是直接与内存打交道

CPU要想进行数据的读写,必须和外部器件进行三类信息的交互:

  1. 存储单元的地址(地址信息,通过地址总线)

  2. 器件的选择,读或写命令(控制信息,通过控制总线)

  3. 读或写的数据(数据信息,通过数据总线)

eg:
机器码: 101000000000001100000000
16进制:A00300
汇编指令:MOV AL,[3]
含义:从3号单元读取数据送入寄存器AL

汇编学习_第5张图片

可以看到地址线找到内存的 3 号地址块,控制线说我只要读取即可,然后数据线从 3 号内存块把 08 拿了过去

内存地址空间

什么是内存地址空间?
CPU地址总线宽度为N,寻址空间为 2^N B。8086CPU的地址总线宽度为20,那么可以寻址1MB个内存单元,其内存地址空间为1MB。

将各类存储器看作一个逻辑存储器——统一编址

  • 所有的物理存储器被看作一个由若干存储
    单元组成的逻辑存储器
  • 每个物理存储器在这个逻辑存储器中占有
    一个地址段,即一段地址空间;
  • CPU在这段地址空间中读写数据,实际上
    就是在相对应的物理存储器中读写数据。

搭建汇编实验环境

2023.12.30,其实不用自己搭建环境啊,用 vscode 的插件就行了,还更快更方便,跑代码也不用编译连接啥的了,安装过程看这篇文章一分钟,在VSCode中使用MASM/TASM搭建汇编环境_masm6.11-CSDN博客 )
汇编学习_第6张图片

汇编学习_第7张图片
当然要自己搭建的话继续看我下面步骤也行

汇编有很多版本,80x86,Linux 汇编,ARM 汇编。这里我们选择高龄的 8086 汇编,于当今的技术而言,是一个理想的教学模型。未来的工作可能基于 80x86, Linux 汇编、ARM 汇编,在 8086 汇编基础上再做拓展即可。

作为汇编语言的基础阶段,还就是经典的 DOS 环境即可,MASM 汇编,LINK连接,DEBUG 调试。问题又来了。在 Win xp 环境下,有 MS DOS 方式,尚可以运行 DOS 程序,但在 Win8 中,DOS 命令不支持了。一个简便的方案,先下载 8086 汇编工作环境吧。其中提供了一个 DOS 模拟器(DOSBox,大多用于模拟 DOS 环境玩一些经典 DOS 游戏),以及一组用于 8086 汇编程序设计的实用命令。解压后的文件夹如下:

image-20231224185906900

链接:https://pan.baidu.com/s/14qWK2NKAPF1fjyso2uoypg?pwd=84i7
提取码:84i7

DOSBox0.74-win32-installer.exe 是 DOS 模拟器的安装文件;MASM 文件夹中是汇编程序设计中用到的命令;MASM 文件夹 里的 EX 文件夹中提供了几个汇编程序作为示例。

然后按照 pdf 里的步骤来搭建就行了

汇编学习_第8张图片

寄存器

是 CPU 内部的信息存储单元。

8086CPU有14个寄存器:

  • 通用寄存器:AX、BX、CX、DX
  • 变址寄存器:SI、DI
  • 指针寄存器:SP、BP
  • 指令指针寄存器: IP
  • 段寄存器:CS、SS、DS、ES
  • 标志寄存器:PSW

共性

​ 8086 CPU 所有的寄存器都是 16 位的。可以存放两个字节

以通用寄存器 AX 为例子

一个16位寄存器存储一个16位的数据最大值?
; 2^16-1
: 例:在AX中存储18D
; 18D
--- 12H
--- 10010B
:再例:在AX中存储20000D
; 20000D
--- 4E20H
--- 0100111000100000B

汇编学习_第9张图片

问题:8086上一代CPU中的寄存器都是8位
的,如何保证程序的兼容性?

通用寄存器均可以分为两个独立的
8位寄存器使用
; AX可以分为AH和AL
; BX可以分为BH和BL
; CX可以分为CH和CL
; DX可以分为DH和DL

汇编学习_第10张图片

“字”在寄存器中的存储

8086是16位CPU, 8086的字长(word size)为16bit

学习汇编指令

汇编学习_第11张图片

汇编指令不区分大小写

写出汇编指令执行的结果(1)

程序段中的指令 指令执行后AX中的数据 指令执行后BX中的数据
mov ax, 4E20H 4E20H 0000H
add ax, 1406H 6226H 0000H
mov bx, 2000H 6226H 2000H
add ax, bx 8226H 2000H
mov bx, ax 8226H 8226H
add ax, bx 044CH 8226H

设原AX、BX中的值均为0000H

写出汇编指令执行的结果(2)

程序段中的指令 指令执行后AX中的数据 指令执行后BX中的数据
mov ax, 001AH 001AH 0000H
mov bx, 0026H 001AH 0026H
add al, bl 0040H 0026H
add ah, bl 2640H 0026H
add bh, al 2640H 4026H
mov ah, 0 0040H 4026H
add al, 85H 00C5H 4026H
add al, 93H 0058H 4026H
设原AX、BX中的值均为0000H

最后一步 c5 + 93 本来 = 0158H,但是因为 aL 只是 ax 的 8 位低位寄存器,所以 1 溢出舍去。故结果为 0058 H

确定物理地址

CPU访问内存单元时要给出内存单元的地址。

所有的内存单元构成的存储空间是一个一维的线性空间。

每一个内存单元在这个空间中都有唯一的地址,这个唯
一的地址称为物理地址。

问题

8086有20位地址总线,可传送20位地址,寻址能力
为1M。

8086是16位结构的CPU 运算器一次最多可以处理16位的数据,寄存器
的最大宽度为16位。

怎么做才能不浪费 4 位寻址地址。

解决办法

8086CPU的解决方法

用两个16位地址(段地址、偏移地址)合成一个20位的物理地址。地址加法器合成物理地址的方法,物理地址=**段地址×16+偏移地址 ** (*16 二进制下左移四位,16进制下左移一位)

汇编学习_第12张图片

基地址和偏移地址经过地址加法器运算即可扩展为 20 位地址

汇编学习_第13张图片

用不同的段地址和偏移地址形成同一个物理地址

汇编学习_第14张图片

在8086PC机中存储单元地址的表示方法

例:数据在21F60H内存单元中,段地址是2000H,说法

(a)数据存在内存2000:1F60单元中;
(b)数据存在内存的2000H段中的1F60H单元中。

段地址很重要!——用专门的寄存器存放段地址。

4个段寄存器:
CS - 代码段寄存器 DS - 数据段寄存器
SS - 栈段寄存器 ES - 附加段寄存器

Debug的使用

Debug是DOS系统中的著名的调试程序,也可以运行在windows系统实模式下。使用Debug程序,可以查看CPU各种寄存器中的内容、内存的情况,并且在机器指令级跟踪程序的运行!

Debug能做什么?

: 用R命令查看、改变CPU寄存器的内容
: 用D命令查看内存中的内容
: 用E命令改变内存中的内容
: 用U命令将内存中的机器指令翻译成汇编指令
: 用A命令以汇编指令的格式在内存中写入机器指令
: 用T命令执行机器指令
: ....

打开 DOSBOX ,输入 C: 然后输入 debug 即可进入。

用R命令查看、改变CPU寄存器的内容

: R - 查看寄存器内容
: R 寄存器名 回车 写值- 改变指定寄存器内容

image-20231225164408847

image-20231225164636250

用D命令查看内存中的内容

: D - 列出预设地址内存处的
128个字节的内容
: D 段地址:偏移地址 - 列出内
存中指定地址处的内容
: D 段地址:偏移地址 结尾偏移
地址 - 列出内存中指定地址范
围内的内容

汇编学习_第15张图片

汇编学习_第16张图片

image-20231225174449325

用E命令改变内存中的内容

 E 段地址:偏移地址 数据1 数据2 ...
: E 段地址:偏移地址
; 逐个询问式修改
; 空格 - 接受,继续
; 回车 - 结束

用U命令将内存中的机器指令翻译成汇编指令

:有汇编指令
mov ax, 0123H
mov bx, 0003H
mov ax, bx
add ax, bx
:对应的机器码为
B8 23 01
BB 03 00
89 D8
01 D8
: e 地址 数据 - 写入
: d 地址 - 查看
: u 地址 - 查看代码

image-20231225175812214

image-20231225175902587

用A命令以汇编指令的格式在内存中写入机器指令

:有汇编指令
mov ax, 0123H
mov bx, 0003H
mov ax, bx
add ax, bx
:对应的机器码为
B8 23 01
BB 03 00
89 D8
01 D8
: a 地址 回车
: - 写入汇编指令
: d 地址 - 查看数据
: u 地址 - 查看代码

用T命令执行机器指令

: t + 回车 - 执行CS:IP处的指令
mov ax, 0123H
mov bx, 0003H
mov ax, bx
add ax, bx

我们 r 查看 ,命令在 073f:0000 处,CS的确是指向 073f ,但是IP 现在指向 0100 ,所以 r ip 修改一下命令寄存器内容,让它 = 0000

汇编学习_第17张图片

然后 t 回车即可执行 mov ax,0123,可以看到 ax 寄存器的值变了

image-20231225181324862

再 t + 回车执行下一条代码 mov bx,003

image-20231225181416372

用Q命令退出Debug

: q - 退出Debug

两个关键的寄存器

CS:代码段寄存器
IP: 指令指针寄存器
CS:IP:CPU将内存中CS:IP
指向的内容当作指令执行。

8086PC工作过程的简要描述:

  1. 从CS:IP指向内存单元读取指令,读取的指令进入指令缓冲器;
  2. IP = IP + 所读取指令的长度,从而指向下一条指令;
  3. 执行指令。 转到步骤 1,重复这个过程。

jmp指令

修改CS、IP的指令

事实:执行何处的指令,取决于CS:IP
应用:可以通过改变CS、IP中的内容,来控制CPU要执行的目标指令
: 问题:如何在(程序)代码里改变CS、IP的值?

  • 方法1:Debug 中的 R 命令可以改变寄存器的值——rcs, rip,Debug是调试手段,并非程序方式!(可以在 debug 里面调试)

  • 方法2:用指令修改 (大错特错)

    mov cs, 2000H
    mov ip, 0000H
    

    8086CPU不提供对CS和IP修改的指令!

  • 方法3:转移指令 jmp

转移指令 jmp

同时修改CS、IP的内容

​ jmp 段地址:偏移地址
​ jmp 2AE3:3
​ jmp 3:0B16
​ 功能:用指令中给出的段地址修改CS,偏移地址修改IP。

仅修改IP的内容

jmp 某一合法寄存器
jmp ax (类似于 mov IP, ax,当然 move 是不合法的,只是举个例子)
jmp bx
功能:用寄存器中的值修改IP。

-a 073f:0100
jmp 073f:2000
-a 073f:2000
mov ax,1000

汇编学习_第18张图片

DS与数据段

用DS存放数据段的段地址,用相关指令(mov、add、sub…)访问数据段中的具体单元,单元地址由[address]指出

将123B0H~123BAH的内存单元定义为数据段,累加数据段中的前3个字型数据

汇编学习_第19张图片

给出00000H-0001F的数据,请写出下面代码的执行结果:

汇编学习_第20张图片

image-20231229234347942

mov ax,[0000] 2662 0000
代码 AX BX
mov bx,[0001] 2662 e626
mov ax,bx e626 e626
mov ax,[0000] 2662 e626
mov bx,[0002] 2662 d6e6
add ax, bx fd48 d6e6
add ax,[0004] 2c14 d6e6
mov ax,0 0000 d6e6
mov al,[0002] 00E6 d6e6
mov bx, 0 00e6 0000
mov bl, [000C] 00e6 0026
add al,bl 000c 0026

汇编学习_第21张图片

解释一下因为 8086 16 位,所以偏移 2 个字节读取下一个数 ([0002],[0004])

栈及栈操作的实现

栈结构

(2024.1.1,师傅们新年快乐,情不自禁更新一下)
栈是一种只能在一端进行插入或删除操作的数据结构。

栈有两个基本的操作:入栈和出栈。

 入栈:将一个新的元素放到栈顶;
 出栈:从栈顶取出一个元素。

栈顶的元素总是最后入栈,需要出栈时,又最先被从栈中取出。栈的操作规则:LIFO(Last In First Out,后进先出)PUSH(入栈)和 POP(出栈)指令

push ax:将ax中的数据送入栈中
pop ax:从栈顶取出数据送入ax
(8086 以字为单位对栈进行操作)

8086CPU中,有两个与栈相关的寄存器:

栈段寄存器SS - 存放栈顶的段地址
栈顶指针寄存器SP - 存放栈顶的偏移地址
——任意时刻,SS:SP指向栈顶元素。

push 指令和pop指令的执行过程

  • push ax
    (1)SP=SP–2;
    (2)将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶。
  • pop ax
    (1)将SS:SP指向的内存单元处的数据送入ax中;
    (2)SP = SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。

关于“段”的总结

目前已学习

  • 代码段 cs

    将段地址放在 CS中,将段中第一条指令的
    偏移地址放在IP中
    . CPU将执行我们定义的代码段中的指令;
    
  • 数据段 ds

    将段地址放在Ds中
    用mov、add、sub等访问内存单元的指令
    时,CPU将我们定义的数据段中的内容当
    作数据段来访问;
    
  • 栈段 ss

    将段地址放在SS中,将栈顶单元的偏移地
    置放在 SP 中
    . CPU在需要进行栈操作(push、pop)时,就
    将我们定义的栈段当作栈空间来用。
    

    由源程序到程序运行

由写出源程序到执行可执行文件的过程

汇编学习_第22张图片

源文件 .asm -> 目标文件.obj -> 目标文件 -> .obj -> 可执行文件 .exe

[…]和(…)

[…]的规定与(…)的约定

[…]——(汇编语法规定)表示一个内存单元

汇编学习_第23张图片

(…)——(为学习方便做出的约定)表示一个内存单元或寄存器中的内容

汇编学习_第24张图片

举个例子:

mov ax,[bx] 相当于 (ax)=((ds) *16 +(bx))

汇编学习_第25张图片

Loop指令

功能:

实现循环(计数型循环),指令的格式 loop 标号

CPU 执行loop指令时要进行的操作

​ ① (cx)=(cx)-1;
​ ② 判断cx中的值
​ 不为零则转至标号处执行程序,如果为零则向下执行。

要求

cx 中要提前存放循环次数,因为(cx)影响着loop指令的执行结果,要定义一个标号

计算 2^5

assume cs:code
code segment
	mov ax,1
	mov cx,5
s:	add ax,ax
	loop s

	mov ax,4c00h
	int 21h
code ends
end

image-20240107161311932

段前缀的使用

引入段前缀:一个“异常”现象及对策

Debug中,mov al, [0]的功能是 ——将DS:0存储单元的值传给AL,编译好的程序中,mov al, [0]变成了将常量0传给AL

汇编学习_第26张图片

对策:在[idata]前显式地写上段寄存器,mov al,ds:[0]

在代码段中使用数据

汇编学习_第27张图片

数据放在代码段就可以通过代码段寄存器拿到数据了

mov bx,0
mov ax,cs:[bx]

但是这样有问题?

汇编学习_第28张图片

解决问题的关键:让数据从CS:0000开始,让代码从CS:0010开始!

解决办法

汇编学习_第29张图片

效果:程序加载后,CS:IP指向要执行的第一条指令在start处!

将数据、代码、栈放入不同段

assume cs:code,ds:data,ss:stack
data segment
dw 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H
data ends
stack segment
dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends
code segment
start:
mov ax,start
mov ss,ax
mov sp,20h
mov data
mov ds,ax
code ends
end start

可以发现代码段里,除了代码段寄存器 cs,数据段 ds 和 堆栈段 ss 寄存器(包括其 sp )都要初始化其地址

mov ax,start
mov ss,ax
mov sp,20h
mov data
mov ds,ax

你可能感兴趣的:(开发日子,期末复习,汇编,学习)