Dos-Debug-Base-1

DOS 汇编 DEBUG 基本命令及其功能详解

注:本篇文档是转载,忘记转载链接了!!!

目前 Window 10 以上版本 已经不再使用 DEBUG 工具了,而是使用 WinDBG 工具,但是并不影响我们去了解它。

目的

掌握 DEBUG 的基本命令及其功能掌握 win7 win8 使用 DEBUG 功能

调试步骤

1. 使用 Debug,将程序段写入内存,逐条执行,观察每条指令后cpu 中相关寄存器内容的变化
2. 将指令写入内存单元中,计算 2 的 8 次方。
3. 查看内存中的内容,并试图修改
4. 用机器指令和汇编指令编程

具体内容

一、查看 CPU 和内存,用机器指令和汇编指令编程

1.预备知识:

Debug总结:它是DOS、Windows 都提供的实模式(8086 方式)程序的调试工具。使用它,可以查看CPU 各种寄存器中的内容、内存的情况和在机器码级跟踪程序的运行。

常用的Debug功能总结:

命令 含义 用法
A assemble 在内存中直接编写代码
C Compare 比较内存
D Dump 查看内存内容
E Enter 修改内存内容
F Full 填充内存
G Go 执行代码
H Hexarithmetic 以 16 进制进行数学运算
I Input 从某个端口输入一个 16 进制并显示
L load 把文件或者绝对扇区内容读入内存
M move 数据传递(不同于汇编的 move )
N name 为读写磁盘文件定义文件名
O output 把指定字节发送到制定端口
Q quit 结束 debug 程序
R Register 寄存器命令
S Search 按照 list 清单查找内存 range 范围
T track 追踪命令
U UNassemble 反汇编内存中的指令
W write 把调试过的信息写到磁盘上
? Help 获取帮助信息,即现实所有支持的命令

Debug的方法总结:

(1) Debug 之前需要进入到 DOS 方式:

a.重新启动计算机,进入 DOS 方式,此时进入的是实模式的 DOS。
b.在 Windows 中进入 DOS 方式,此时进入的是虚拟 8086 模式的 DOS。

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

a. 显示 CPU 内部所有寄存器内容和标志位状态;格式为:-R

首先输入 debug 进入 debug 模式,然后输入命令 -R 查看 CPU 内部所有寄存器内容和标志位状态如上图所示。

b. 显示和修改某个指定寄存器内容,格式为:-R 寄存器名 若要修改一个寄存器中的值,比如 AX 中的值,可用 R 命令后加寄存器名来进行,输入 "r ax" 后按 Enter 键,将出现 ":" 作为输入提示,在后面输入要写入的数据后按 Enter 键,即完成了对 AX 中内容的修改。若想看一下修改的结果,可再用 R 命令查看

这里我修改了寄存器AX的值,令其值为0110,再使用R命令查看各个寄存器中的内容,结果证明修改成功。

(3)用Debug的D命令查看内存中的内容

a. 格式:-d 段地址:偏移地址,Debug 将列出从指定内存单元开始的 128 个内存单元的内容。使用 D 命令,Debug 将输出 3 部分内容,如图所示。

通过查阅可知,中间是部分从指定地址开始的 128 个内存单元的内容,用十六进制的格式输出,每行的输出从 16 的整数倍的地址开始,最多输出 16 个单元的内容。左边是每行的起始地址。右边是每个内存单元中的数据对应的可显示的 ASCII 码字符。


b.格式:-d 段地址:起始偏移地址 结尾偏移地址,Debug 将列出指定范围的内存单元的 内容。

此处使用命令 -D 073F:0100 0136 ,将起始偏移地址定义为 0100,结尾偏移地址定义为 0136,查询到了指定范围的内存单元的内容。

(4)用 Debug 的 E 命令改写内存中的内容

a. 格式:-e 起始地址 数据 数据 数据,如要将 1000:0 开始的 10 个内存单元修改为 0~9,可以用 "-e 1000:0 0 1 2 3 4 5 6 7 8 9"

使用命令 -E 073F :0110 00 01 02 20 10 20 30,修改了指定位置的内存中的数据。

b. 格式:-e 起始地址,逐个单元相继地修改。 如:-e 1000:10 1000:0010 6D.0 61.1 72.2 6B.1c 输入 e 1000:10 ,Debug 显示起始地址 1000:0010,和 1000:0010 单元的原始内容:6D,然后光标停在 "." 的后面提示输入想要写入数据,输入数据 0,然后按空格键,即用输入的数据0改写了当前的内存单元。当前单元处理完成后,Debug 将接着显示下一个内存单元的原始内容,并提示读者进行修改,可以用同样的方法处理。改写完毕后,按 Enter 键,E 命令操作结束。

​ 可以用E命令向内存中写入字符,比如:用E命令从内存 1000:0 开始写入:数值 1、字符 “a”,数值 2,字符 “b”,数值 3,字符 “c”,可以用: “-e 1000:0 1 ,, a “2 ,, b”3,,c””, 修改的结果是,向 1000:0、1000:2、1000:4 单元中写入数值 1、2、3,向 1000:1、1000:3、1000: 5单元中写入字符 “a”、”b”、”c” 的 ASCII 码值:61H、62H、63H。

在这里我犯了一个错误,我直接将字母 A、B、C 输入,这里机器将它自动识别成为十六进制的符号了,而并没有将它的 ASCII 码值转换。

随后,我使用' '将字符转义输入,最后显示其 ASCII 码值:41H,42H,43H.


也可以用 E 命令向内存中写入字符串,比如:用 E 命令从内存1000:0开始写入:数值1、字符串 “a+b”、数值 2 、字符串 “c++”、字符3、字符串 “IBM”。 可以用:’-e 1000:0 1 “a+b” 2 “c++”” 3 “IBM” ‘。

从右边是每个内存单元中的数据对应的可显示的 ASCII 码字符中可证明,字符串最终以 ASCII 码的形式存储进内存单元中。

可以用 E 命令向内存中写入机器码 比如要从内存 1000:0 单元开始写入这样一段机器码:
1
2
3
4
5
6
;机器码 对应的汇编指令
b80100 mov ax,0001
b90200 mov cx,0002
01c8 add ax, cx
;可用:
-e 1000:0 b8 01 00 b9 02 00 01 c8

![10.png]

由结果可知,该机器码可以生效。

(5)用 U 命令查看写入的或内存中原有的机器码所对应的汇编指令

a. -U 段地址:偏移地址

该命令从指定地址开始,反汇编 32 个字节,若地址省略,则从上一个 U 命令的最后一条指令的下一个单元开始显示 32 个字节。

通过-U 命令查看反汇编指令如上。

b. -U 地址范围

该命令对指定范围的内存单元进行反汇编

在这里指定反汇编从 073F:010E 开始,通过与上图的对照可知,反汇编结果正确。

(6)使用 T 命令,可以执行 CS:IP 指向的指令,格式:-t,指令执行后,Debug 显示输出 CPU 中寄存器的状态。

通过命令 -t,执行该指令并显示输出 CPU 中寄存器的状态。

(7) 用 Debug 的 A 命令以汇编指令的形式在内存中写入机器指令。

a. 格式:-A 段地址:偏移地址 该命令从指定地址开始允许输入汇编语句,把它们汇编成机器代码相继存放在从指定地址开始的存储器中。

通过 -A 指令,允许从 073F:011F 开始输入汇编语句,通过 -U 指令证明,该语句汇编成机器代码相继存放在从指定地址开始的存储器中。

常用汇编程序指令总结:
| 指令 | 含义 |
| mov | mov A,B 即 B 值赋给 A |
| add | add ax,bx 语意是ax = ax + bx |
| sub | sub ax,bx 语意是ax = bx - ax |
| jmp | jmp 无条件跳转 |

2. 源代码:

(1):使用 Debug,将下面的程序段写入内存,逐条执行,观察每条指令执行后,CPU 中相关寄存器中内容的变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
机器码        汇编指令
b8 20 4e mov ax,4E20H ;将4E20H存入ax
05 16 14 add ax,1416H ;将ax中的值+1416H存入ax
bb 00 20 mov bx,2000H ;将2000H存入bx
01 d8 add ax,bx ;将ax+bx的值存入ax
89 c3 mov bx,ax ;将ax中的值存入bx
01 d8 add ax,bx ;将ax+bx的值存入ax
b8 1a 00 mov ax,001AH ;将001AH存入ax
bb 26 00 mov bx,0026H ;将0026H存入bx
00 d8 add al,bl ;将al+bl的值存入al
00 dc add ah,bl ;将ah+bl的值存入ah
00 c7 add bh,al ;将bh+al的值存入bh
b4 00 mov ah,0 ;将0存入ah
00 d8 add al,bl ;将al+bl的值存入al
04 9c add al,9CH ;将al+9CH的值存入al

(2):将下面3条指令写入从 2000:0 开始的内存单元中,利用这 3 条指令计算 2 的 8 次方。

1
2
3
mov ax,1   ;将1存入ax(从2000:0开始的内存单元)
add ax,ax ;将ax+ax的值存入ax
jmp 2000:0003 ; 跳转至地址2000:0003,即第二条指令

3. 实验代码、过程、相应结果的说明和分析:

1. 使用 Debug,将源程序段写入内存,逐条执行,观察每条指令执行后,CPU 中相关寄存器中内容的变化。

(1) 首先我进入了 debug,并将源代码写入内存,设置起始地址为 073F:0100,如下所示:

其中不可忽视的是 ip 地址,由 ip 地址变化可知,IP = IP + 所读指令的字节数。不同的指令对ip地址变化的影响程度不同。

(2) 输入指令 -t,执行 CS:IP 指向的指令,指令执行后,Debug 显示输出 CPU 中寄存器的状态。如下图所示,可见已经执行第一条语句 mov ax,4E20H,将 4E20H 存入 ax。并准备执行 add ax,1416H 语句。

(3) 输入指令 -t,执行 CS:IP 指向的指令,指令执行后,由 Debug 显示输出 CPU 中寄存器的状态可知,执行了add ax,1416H语句。可以计算得知:1416+4E20=6236,即将 ax 中的值 + 1416H 存入 ax。

(4) 执行 mov bx,2000H ,即将 2000H 存入 bx。如下图所示:

(5) 执行 add ax,bx ,即将ax+bx的值存入ax。通过计算可知ax 中的值是6236+2000=8236,且bx 中的值不变。

(6) 输入指令-t,执行 mov bx,ax ;将 ax 中的值存入 bx,且 ax 中的值不变。

(7) 同样,执行 add ax,bx ;将 ax+bx 的值存入 ax,这里 ax=8236+8236=046C

(8) 这几步,执行 mov ax,001AH (将001AH存入ax),mov bx,0026H (将0026H存入bx)。

(10) 这四步分别执行 add al,bl (将 al+bl 的值存入 al),此时 ax 为 0040,再执行 add ah,bl (将 ah+bl 的值存入 ah),00+26=26,此时 ax 为 2640,再通过 add bh,al (将 bh+al 的值存入 bh),40+00=40,此时 bx 为 4026,再通过 mov ah,0 (将0存入ah),ax 的状态为 0040。

(14) 继续通过 -t 命令,执行 add al,bl (将 al+bl 的值存入 al)最后执行 add al,9CH (将 al+9CH 的值存入 al),但是在此过程中发生了溢出,66+9CH=102,但是此时 AL 仅能显示两位,故最高位发生了溢出,ax 显示为 0002。

2.将源代码第二部分中的 3 条指令写入从 2000:0 开始的内存单元中,利用这 3 条指令计算 2 的 8 次方。

(1) 首先,我进入了 debug 模式,然后以 2000:0 为起始地址,使用 -a 命令,开始输入指令。输入结束后我用 -u 命令查看了写入的汇编指令。但是接着使用 -t 执行指令时却没有出现预想之中的结果,然后我使用 -r 命令查看了 CPU 寄存器的内容,发现 cs:ip 地址没有从 2000:0 开始。

(2) 接着我使用 -r 命令修改了 cs:ip,使其指向 2000:0。

(3) 然后我使用 -t 执行命令,先执行了 mov ax,1 ,后执行 add ax,ax,将 ax 中的值 +ax 中的值的结果保存在 ax 中, 然后发现执行 jmp 时发生了无条件的跳转,ip 又返回了 0003,然后接下来继续执行 add 命令。

(4) 每跳转一次后执行 add ax,ax 都相当于是一次翻倍的操作,执行 8 次后即可求出 2 的八次方。得到结果为 100H,即 256。如下所示:

3.查看内存中的内容

PC 机主板上的 ROM 中写有一个生产日期,在内存 FFF00HFFFFFH 的某几个单元中,请找到这个生产日期并试图改变它。(内存ffff:0005ffff:000C(共 8 个字节单元中)处)

这里使用 -d ffff:0005 查看了该指定地址开始的 128 个内存单元的内容,找到了 ASCII 码为 01/01/92 的字样,可知其生产日期为 1992年1月1日。尝试用 -e ffff:0005 改变内存值,但是实际并没有改变。

在 DOS 环境下 使用 wmic bios get description,结果显示命令不合法,说明在 DOSBOX 虚拟环境下是无法读取 bios 的配置信息的。我想这也从侧面证明了 DOSBOX 虚拟环境中的主板ROM信息并不是真正的 PC 机主板信息,而只是作为软件内置的参数而已。

用机器指令和汇编指令编程

1.Debug 命令的补充

(1) Debug 的 T 命令在执行修改寄存器 SS 的指令时,下一条指令也紧接着被执行。
通过资料查询,我找到了SS:SP的具体解释,其中SS 段寄存器和寄存器SP 用来指示栈,其中栈顶的段地址存放在SS 中,而相对于栈顶的偏移地址存放在SP 中。并且在任意时刻,SS:SP 都指向栈顶元素。

(2) 在 D 命令中使用段寄存器
格式:”d 段寄存器:偏移地址”,以段寄存器中的数据为段地址 SA,列出从 SA:偏移地址开始的内存区间中的数据

(3)在 E、A、U命令中使用段寄存器 在E、A、U这些可以带有内存单元地址的命令中,也可以同D命令一样,用段寄存器表示内存单元的段地址。

2.源代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mov ax,ffff ;将ffffH存入ax 中
mov ds,ax ;将ax作为数据段的段地址
mov ax,2200 ;将2200H覆盖ax中原有的数据
mov ss,ax ;将ax中的数据放入ss中作为栈段的地址
mov sp,0100 ;将0010H数据覆盖sp中原有的数据
mov ax,[0] ;将偏移量为0 的数据存入ax
add ax,[2] ;将ax 与偏移量为2 的内容相加并且存入ax
mov bx,[4] ;将偏移量为4 的内容放进bx
add bx,[6] ;将bx 与偏移量为6 的内容相加并将结果放入bx
push ax ;将ax 中的内容压入栈
push bx ;将bx 中的内容压入栈
pop ax ;将栈顶内容放入ax 中
pop bx ;将栈顶内容放入bx 中
push [4] ;将偏移量为4 的地址的内容压入栈
push [6] ;将偏移量为4 的地址的内容压入栈

mov ax,1000H ;将1000H存入ax
mov ds,ax ;将ax中的数据赋给ds
mov ds,[0] ;将偏移量为0的数据赋给ds
add ds,ax ;将ax 和ds中的数据相加并且将结果存入ds中
3.代码、过程、相应结果的说明和分析:

(1) 使用Debug,将上面的程序段写入内存,逐条执行,根据指令执行后的实际运行情况填空。

执行命令,首先将将 ffffH 赋值给 ax 中,然后将 ax 中的值覆盖 ds 中的值,再将 2200H 赋值再 ax 中,然后将 ax 中的值赋值给 ss 中,同时 sp 改为 0100,代表着开辟了一块栈空间。

将偏移地址为 0 的单元的内容放入 ax 中,此时ax 的值变为 c0ea,然后将 ax 与偏移地址为 2 的数据相加并将结果放在 ax 中,此时ax 中数据变为 C0FC,然后将偏移地址为 4 的单元的数据 放入 bx 中,由 -r 查看状态可知,这时 ds:0004 =30F0,因此 bx =30F0,然后将 bx 和偏移地址为6 的数据相加并将结果存在 bx 中,此时 ds:0006 =2F31,于是 bx =6021。然后将ax 中的数据压入栈,此时 sp =00FE ,然后将 bx =6021 压入栈中,此时 p =00FC。

接着出栈并将数据存在 ax 中,此时 sp 为 00FE,然后继续出栈,并将数据存放在 bx 中。然后将偏移地址为 4 的数据压入栈,此时 ds:0004 =30F0,而 sp =00FE,然后将偏移地址为6 的 内容压入栈,此时 ds:0006 =2F31,sp =00FC。

综上:填写空格如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mov ax,ffff
mov ds,ax
mov ax,2200
mov ss,ax
mov sp,0100
mov ax,[0] ;ax= C0EA
add ax,[2] ;ax= C0FC
mov bx,[4] ;bx= 30F0
add bx,[6] ;bx= 6021
push ax ;sp= 00FE ;修改的内存单元的地址是00FE 内容为C0FC
push bx ;sp= 00FC ;修改的内存单元的地址是00FC 内容为6021
pop ax ;sp= 00FE ;ax= 6021
pop bx ;sp= 0100 ;bx= C0FC
push [4] ;sp= 00FE ;修改的内存单元的地址是00FE 内容为30F0
push [6] ;sp= 00FC ;修改的内存单元的地址是00FC 内容为2F31

(2) 使用Debug,将程序段(二)写入内存,逐条执行,观察每条指令执行后,CPU 中相关寄存器中内容的变化。

如上图所示,程序报错,对于这种现象的解释是 DS 为数据段寄存器,无法用在算数运算指令中,不能与通用寄存器进行运算。

(3) 仔细观察下图中的实验过程,然后分析:为什么 2000:0~2000:f 中的内容会发生改 变?

a. 首先出现的第一个问题与在知识准备板块出现的问题一样:该图代码在执行完mov ss,ax后,下一步的指令应该是movsp,10,但实际上却是mov ax,3123。在用T命令单步执行mov ss,ax 前,ax=0000,ss=0b39,sp=ffee,而执行后 ss=2000,sp=0010。ss变为2000是正常的,这正是mov ss,ax的执行结果。而能够将sp设为0010的只有指令mov sp,10,看来,mov sp,10一定是得到了执行。 在用T命令执行mov ss,ax的时候,它的下一条指令mov sp,10也紧接着执行了。 整理一下我们分析的结果:在用T命令执行mov ss,ax的时候,它的下一条指令mov sp,10也紧接着执行了。一般情况下,用T命令执行一条指令后,会停止继续执行,显示出当前CPU各个寄存器的状态和下一步要执行的指令,但T命令执行mov ss,ax的时候,没有做到这一点。不单是mov ss,ax,对于如:mov ss,bx,mov ss,[0],pop ss等指令都会发生上面的情况,这些指令有哪些共性呢?它们都是修改栈段寄存器SS的指令。 结论:Debug的T命令在执行修改寄存器SS的指令时,下一条指令也紧接着被执行。

b. 针对2000:0~2000:f 中的内容会发生改变这一问题,我通过查询资料之后得知,之所以靠近栈顶的10 个字节中值有了变化,是因为对定义栈段时部分运行环境变量进行暂存,靠近栈顶的10 个字节中的暂存数据分别是SS、IP、CS 等的值,在用T 指令在进行调试时,会产生中断。而为了保护现场,CPU 先把标志寄存器入栈,再把CS IP 分别入栈。

总结心得

​ 我对于汇编语言的认识进一步加深了,也在这过程中有了自己的体会。首先,眼看千遍不如手过一遍,对于预备知识里的内容,我没有松懈,将里面的每一条命令都认真地写了一遍,并且记录了下来,这让我对于汇编 debug 的常用命令较为熟悉,印象也较为深刻,这对我后面的实验帮助非常大。通过实验,我也了解到查看、改变 CPU 寄存器的内容,查看、改写内存中的内容,将内存中的机器指令翻译成汇编指令,执行一条机器指令,以及反汇编的一些指令。

​ 其次,我发现实验后面出现的一些问题,往往都能在实验开始时的部分找到解答,因此,整个实验的每一部分都是不能轻视的。比如在实验过程中起初输入完命令之后,用T指令开始执行时,显示的结果却并未执行,这在最初困扰着我,之后再查看 CPU 寄存器的内容时发现原来 CS:IP 并没有指向指令起始的地址区域,因此在执行时并未执行所编写的区域。之后我想到可以通过修改 CS:IP 的值来指向指令的起始地址,也可以通过jmp指令跳转到对应的区域。包括后来在分析:2000:0~2000:f的过程中,突然发现指令自动执行了一步,然后就回想到最开始的预备知识部分做过相关的探究了,Debug的T命令在执行修改寄存器SS的指令时,下一条指令也紧接着被执行。

综上,通过实验,我认为多实践以及多读多想是十分关键的,而且许多知识之间都是相通的,只有在积累了足够多的知识以后,在处理解决问题以及学习新知识时才能更好的达到期望的目标。
Contents
  1. 1. DOS 汇编 DEBUG 基本命令及其功能详解
    1. 1.1. 目的
    2. 1.2. 调试步骤
    3. 1.3. 具体内容
      1. 1.3.1. 一、查看 CPU 和内存,用机器指令和汇编指令编程
        1. 1.3.1.1. 1.预备知识:
        2. 1.3.1.2. Debug的方法总结:
        3. 1.3.1.3. 2. 源代码:
      2. 1.3.2. (1):使用 Debug,将下面的程序段写入内存,逐条执行,观察每条指令执行后,CPU 中相关寄存器中内容的变化。
      3. 1.3.3. (2):将下面3条指令写入从 2000:0 开始的内存单元中,利用这 3 条指令计算 2 的 8 次方。
        1. 1.3.3.1. 3. 实验代码、过程、相应结果的说明和分析:
          1. 1.3.3.1.1. 1. 使用 Debug,将源程序段写入内存,逐条执行,观察每条指令执行后,CPU 中相关寄存器中内容的变化。
          2. 1.3.3.1.2. 2.将源代码第二部分中的 3 条指令写入从 2000:0 开始的内存单元中,利用这 3 条指令计算 2 的 8 次方。
          3. 1.3.3.1.3. 3.查看内存中的内容
        2. 1.3.3.2. 用机器指令和汇编指令编程
          1. 1.3.3.2.1. 1.Debug 命令的补充
          2. 1.3.3.2.2. 2.源代码:
          3. 1.3.3.2.3. 3.代码、过程、相应结果的说明和分析:
    4. 1.4. 总结心得
|