前面,我们知道,在DOS操作系统中,有一个程序http://command.com,也就是DOS系统的shell.
DOS中运行一个程序时,command将程序从可执行文件加载进内存,并使其得以运行. 但是这样我们不能逐条指令地看到程序的执行过程.
为了观察程序的运行过程,可以使用Debug. Debug可以将程序加载入内存,设置CS:IP指向程序的入口,但Debug并不放弃对CPU的控制,这样,我们就可以使用Debug的相关命令来单步执行程序,查看每一条指令的执行结果.
具体方法如图1所示:
我们可以用R指令查看各个寄存器的设置情况,如图2所示:
现在程序已从1.exe中装入内存,接下来查看一下它的内容,但程序被装入内存的什么位置?
这里,需要讲解DOS系统中 .exe文件中的程序的加载过程,如图3所示:
我们的程序被装入内存什么地方?如何得知,从图3中我们得到如下信息:
① 程序加载后,ds中存放着程序所在内存区的段地址,这个内存区的偏移地址为0,则程序所在的内存区的地址为ds:0;
② 这个内存区的前256个字节存放的是PSP,DOS用来和程序进行通信。从256字节处向后的空间存放的是程序;
所以,从ds中可以得到PSP的段地址SA,PSP的偏移地址为0,则物理地址为SA × 16 + 0
因为PSP占256 (100H)字节,所以程序的物理地址是:
SA × 16 + 0 + 256 = SA × 16 + 0 + 16 × 16 = (SA + 16) × 16 + 0
可用段地址和偏移地址表示为:SA + 10H : 0
现在,我们看一下图2中的DS值,DS = 075A,则 PSP的地址为075A:0,程序的地址为076A:0 ( 075A+10H:0 ).
可以用U命令看一下其他指令,如图4所示:
可以看到,从076A:0000 ~ 076A:000E都是程序的机器码.
现在我们可以跟踪了,用T命令单步执行程序中的每一条指令,并观察每条指令的执行结果,到了int21,我们用P命令执行,这里不要考虑是为什么,只要记住这一点就行了,如图5所示:
注:使用Q命令退出Debug,将返回到command中,因为Debug是由command加载运行的. 在DOS中用“Debug 1.exe”运行Debug对1.exe进行跟踪时,程序加载的顺序是:command加载debug,Debug加载1.exe. 返回的顺序是:从1.exe中程序返回到Debug,从Debug返回到command.
实验:编程、编译、连接、跟踪
(1) 将下面的程序保存为t1.asm文件,将其生成可执行文件t1.exe.
assume cs:codesg
codesg segment
mov ax, 2000H
mov ss, ax
mov sp, 0
add sp, 10
pop ax
pop bx
push ax
push bx
pop ax
pop bx
mov ax, 4c00H
int 21H
codesg ends
end
(2) 用Debug跟踪t1.exe的执行过程,写出每一步执行后,相关寄存器中的内容和栈顶的内容.(以下答案仅供参考)
mov ax, 2000H ; ax = 2000H
mov ss, ax ; ss = 2000H
mov sp, 0 ; sp = 0
add sp, 10 ; sp = AH,栈项内容:01A3H
pop ax ; ax = 01A3H, ss:sp = 2000:CH, 栈顶内容:7206H
pop bx ; bx = 7206H, ss:sp = 2000:EH, 栈顶内容:0
push ax ; ss:sp = 2000:CH, 栈顶内容:01A3H
push bx ; ss:sp = 2000:AH, 栈顶内容:7206H
pop ax ; ax = 7206H,ss:sp = 2000:CH,栈顶内容:01A3H
pop bx ; bx = 01A3H,ss:sp = 2000:EH,栈顶内容:0
mov ax, 4c00H ; ax = 4c00H
int 21h
(3) PSP的头两个字节是CD 20,用Debug加载t1.exe,查看PSP的内容.