![]() | 第3章 中断调用与子程序 | -39- | |||||||||||||||||||
-t | 不要敲入"P" | ||||||||||||||||||||
AX=0161 | BX=0000 | CX=0029 | DX=0000 | SP=FFFC | BP=0000 | SI=0000 | DI=0000 | ||||||||||||||
DS=0A3E | ES=0A3E | SS=0A3E | CS=0A3E | IP=010D | NV UP EI PL NZ NA PO NC | ||||||||||||||||
0A3E:010D | 50 | PUSH AX | |||||||||||||||||||
并非是所有的CALL、INT指令都需要使用"P"命令跳过,如果确实需要仔细分析一个子程序或检查操作系统中是否有"异形"(病毒)程序侵入,那么"T"命令的缺点也就成了优势了。这就是为什么我们在跟踪CALL 010D指令时用了T命令,因为我们想看到CALL指令的实际动作。 不难看出,CALL指令执行完后,CPU确实转移到10D处执行指令,同时我们也看到,SP寄存器被减了2,从FFFEH变成FFFCH,也就是说,有一个16位数据进入了堆栈。这个数据是什么呢? |
|||||||||||||||||||||
-dfffc | 返回地址 | ||||||||||||||||||||
0A3E:FFF0 | 07 01 00 00 | .>~...p.......p. | |||||||||||||||||||
用D命令显示出堆栈的内容,可以看到这个数据是0107①,它恰恰是指令"CMP AL,1B"的偏移地址。 通过这一点我们不仅可以比较出CALL与JMP的区别,而且也会发现CALL和INT具有一些相似之处,它也会在堆栈中保存返回地址。在这种情况下,最后一条RET指令的作用也就很清楚了:它会将保存在堆栈中的返回地址弹到IP寄存器中,使得CPU能从子程序正常返回到主程序。是不是这样呢?我们先把这个子程序执行完: |
|||||||||||||||||||||
-g=10d 128 | "128"是断点位置 | ||||||||||||||||||||
01100001 | 此数字是程序所显示出来的 | ||||||||||||||||||||
AX=0161 | BX=0001 | CX=0000 | DX=6131 | SP=FFFC | BP=0000 | SI=0000 | DI=0000 | ||||||||||||||
DS=0A3E | ES=0A3E | SS=0A3E | CS=0A3E | IP=0128 | NV UP EI PL ZR NA PE CY | ||||||||||||||||
0A3E:0128 | C3 | RET | |||||||||||||||||||
这里用了G命令一个十分重要的功能──设置断点。如果大家在键入程序时保证没有错误,那么就不必再"单步"执行这个子程序,我们现在急于看到"RET"指令执行的过程,所以我们想使CPU连续执行这一段程序,并且在0128处停下来,因此我们使用"G"命令在0128处设了断点。 注意在"="后面的第一个地址是CPU开始执行指令的起始地址,空格后的地址才是设断点的位置。我们之所以强调用"G"命令执行程序时要使用等号明确指定起始地址就是为了和断点相区分。有关断点的更多说明将留在后面介绍。现在只需记住断点设在哪里,CPU执行到那个位置时就会停下来并显示出所有寄存器的情况,并等待用户输入新的命令。 |
|||||||||||||||||||||
① 注意内存地址的编排是,低地址在先,高地址在后。因此数据存储于内存中也是高字节在后。 | |||||||||||||||||||||
Copyright © 2004-2015 Reanimator | www.cookmoon.org |