CTF之旅:从汇编语言(王爽)在一次开始

学习汇编语言(王爽)的总结与摘抄:一个感想,王爽老师的这本书真的一个字都减不下去。

汇编语言组成:
汇编指令,机器码的主机符,有对应的机器码
伪代码,没有对应的机器码,由编译器执行,计算机不执行
其他符号,如±*/,编译器识别,无对应机器码

指令和数据在磁盘或者内存上都是以二进制信息存储,那计算机如何区分指令和数据呢?(通过不同的总线来区别)

存储器被划分为若干个存储单元,存储单元从零开始编号,一个存储单元可以存1字节(8位)

CPU要进行数据读写,必须有以下三类信息:
存储单元的地址(地址信息),通过地址总线传输
器件的选择,读或写的命令(控制信息),通过控制总线传输
读或写的数据(数据信息)通过数据总线传输

机器码(汇编指令)驱动计算机进行数据的读写。

8088cpu 数据总线宽度为8,8086CPU总线宽度为16,读写内存次数不一样

随机存储器(RAM)可读可写,带电存储
只读存储器(ROM)只读不可写,关机后内容不丢失。

所有物理存储器被看做是一个有若干存储单元的逻辑存储器,每个物理存储器在逻辑存储器占有一个地址段,即一段地址空间。CPU在这段地址空间多谢数据,实际就是在相对应的物理存储其中读写数据。但是当CPU向ROM中写数据时是没有结果的,因为ROM只读。
所以在硬件编程时,必须知道系统中内存地址空间的分配情况(即逻辑存储器的地址分配情况)。当我们想要在某类存储器中读写数据时,必须知道他的第一个单元的地址和最后一个单元的地址。

地址总线,数据总线和控制总线属于外部总线,实现CPU和主板器件的联系
CPU中还有内部总线,实现CPU内部各器件的联系
在CPU中
运算器进行信息处理
寄存器进行信息存储
控制器控制各器件工作
内部总线连接各种器件,在他们之间进行数据传送

程序员通过改变各种寄存器的内容实现对CPU的控制
8086cpu所有寄存器都是16位的。
通用寄存器:即AX、BX、CX、DX用来存放一般性的数据的4个寄存器
为保证和上一代8位寄存器兼容,通用寄存器都可以分为两个可以独立使用的8位寄存器使用
AX可分为AH和AL:AH高8位(815位),AL低8位(07位)。其他三个类似。

由于兼容性考虑,8086CPU可以一次性处理一下两种尺寸的数据
字节:记为byte,8个bit组成,可以存在8位寄存器中
字:记为word,一个字有两个字节组成,即16bit,这两个字节分别被称为高位字节和高位字节。
个内存单元可存放8bit位数据

在写汇编指令或者一个寄存器名称时,不区分大小写。
当运算操作的是AX时,数据超出16位时,寄存器存储最后面的16位,
当运算操作的是AL或者AH时,数据超出8位时,寄存器存储最后面的8位,前面的不保存在AX中。
在进行数据传送或者运算时,注意位数要一样。
mov ax,18H
mov al,18H
mov ah,18H
正确,但
mov ax,ah
mov bh,ax
mov al,20000(最大可存数据为十进制255)
mov bh,100H
错误

16位结构的CPU是指:
运算器一次最多可以处理16位的数据
寄存器的最大宽度位16位
寄存器和运算器之间的通路为16位

8086CPU一共有20位地址总线,可以传20位地址,达到1MB得寻址能力,但8086CPU又是16位架构,在内部一次性传输处理暂时存储的地址为16位。所以8086CPU采用一种在内部两个16位地址合成的方法来形容一个20位的物理地址。

如图:当8086CPU要读写内存时:
CPU中相关部件提供两个16位的地址,一个是段地址,一个是偏移地址
段地址和偏移地址通过内部总线送入一个称为地址加法器的部件
地址加法器将两个16位地址合成一个20位的物理地址
地址加法器通过内部总线将20位物理地址送入输入输出控制电路
输入输出控制电路将20位物理地址送上地址总线
20位物理地址被地址总线传送到存储器

地址加法器采用物理地址=段地址x16+偏移地址的方法合成物理地址。段地址x16即左移四位,最后四位补零。
如:
段地址为1230H,偏移地址为00c8H,通过地址加法器时运算过程为1230Hx16=12300H
12300H+00c8H=123c8H。
一个X进制的数据左移以为,相当于乘以X。

注意:段地址中的“段”,并不是指内存被划分为了一个一个的段,每个段都有一个段地址,这种说法是错误的。正确的是内存并没有分段,段的划分来自于CPU,由于我们使用段地址x16+偏移地址=物理地址的方式给出内存单元,使得我们可以使用分段的方式来管理内存。在编程时我们可以根据需要将一组内存单元定义为一个段。
段地址x16必然是16的倍数,所以一个段的其实地址也一定是16的倍数
偏移地址为16位,16位地址的寻址能力为64KB,所以一个段的长度最大为64kB

CPU可以用不同的段地址加偏移地址形成同一个物理地址
若CPU要访问21F60H单元,则他给出的段地址SA和偏移地址EA满足SAx16+EA=21F60H即可。
若给定一个段地址,仅通过变化偏移地址来寻址,最多可定位64KB个内存单元
因为偏移地址是16位的,变化范围为0~FFFFH,一个内存单元地址为1B,所以最多寻址64KB个内存单元,也就是65536个内存单元。

数据在21F60H内存单元中,一般这样表述:数据存在内存2000:1F60单元中,或者,数据存在内存的2000H段中的1F60H单元中。

段寄存器共有四个CS、DS、SS、ES。
CS和IP是8086CPU两个最关键的寄存器,CS为代码段寄存器,IP为指令指针寄存器。
在8086中,任意时刻,CPU将CS:IP指向的内容当做指令运行。比如,设CS中为M,IP中为N,则8086CPU从Mx16+N单元开始,读取一条指令并执行。

8086CPU工作过程
从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器
IP=IP+所读指令的长度,从而指向下一条指令
执行指令,转到开始,重复执行。

在8086CPU加电启动或者复位后(即CPU刚开始工作时)CS=FFFFH,IP=0000H,即FFFF0H单元中的指令是8086pc机开机后执行的第一条指令。

CPU将CS:IP指向的内存单元看做指令。若内存中的一段信息曾被CPU执行过,那它所在的内存单元必定被CS:IP指向过。

程序员可以通过改变CS、IP的内容来控制CPU。
mov指令可以修改AX的值,但不能修改CS、IP的值,这类指令被称为传送指令。
可以修改CS、IP的值得指令被称为转移指令,比如jmp指令。
jmp 段地址:偏移地址,即可同时修改CS、IP的值。比如:
jmp 2AE3:3 执行后 CS=2AE3H,IP=0003H,CPU将从2AE33H处读取指令
jmp 3:0B16 执行后 CS=0003H,IP=0B16H,CPU将从00B46H处读指令。
若想仅修改IP内容,可用jmp 某一合法寄存器 指令完成。如:
jmp ax 指令执行前:ax=1000H,CS=2000H,IP=0003H
指令执行后:ax=1000H,CS=2000H,IP=1000H
jmp bx 执行指令前:bx=0B16H,CS=2000H,IP=0003H
指令执行后:bx=0B16H,CS=2000H,IP=0B16H

windows debug命令:

进入debug 重启计算机,进入DOS,此时进入的实模式的DOS
在windows下进入DOS,此时进入的是虚拟8086模式的CPU。
虚拟模式进入方法,在运行窗口输入command指令。alt+enter变为全屏,然后运行debug

CPU中用16位寄存器存放一个字,但在内存存储中,内存单元是按照字节存放的,即一个单元存放一个字节,一个字要用两个地址连续的内存单元存放,这个字低字节放在地地址单元中,高字节存放高地址单元中。(内存中向下是高字节)
将起始地址为N的字单元称为N地址字单元。

8086C中,内存地址由段地址和偏移地址组成,DS寄存器通常存放要访问数据的段地址。

mov指令可完成三种数据传送:
将数据直接传入一个寄存器中,mov bx,1000H
将一个寄存器的内容送入到另一个寄存器 mov ds,bx
将一个内存单元的内容送入一个寄存器 mov al,[0]

在第三种情况时,方括号中的数字表示内存单元的偏移地址,8086CPU会自动从
DS段寄存器中读取数据作为内存单元的段地址。
在8086CPU中,不支持直接传数据到DS段寄存器中,只能用一个一般的寄存器传入到DS段寄存器中。

mov a1,[0] 将一个字节的内存单元值传入AL
mov [0],a1 将AL值传入一个字节的内存单元
mov ax,[0] 将两个连续的字节内存单元(即一个字)传入AX
mov [0],ax 将AX的值传入连续两个字节的内存单元(即传入一个字)

mov 段寄存器,寄存器 mov 寄存器,段寄存器 二者都成立。
mov 段寄存器,内存单元 mov 内存单元,段寄存器 二者都成立。
mov 内存单元,CS 成立 mov CS,任意 都不成立,因为mov不能改变CS的值。

add 除了无法对段寄存器进行操作,其他操作同mov。即 sub 段寄存器,任意 sub 任意,段寄存器 sub 任意,cs 都错误。