在学习汇编语言的时候遇到下面一段代码,起初简单地认为这个程序是不能正常结束的。但是经过debug单步跟踪之后,发现程序是能正常结束的,且出现了一种奇怪的现象:当程序从code:0005处开始执行到code:0014后,继续jmp指令跳到code:0008。然而,继续往前执行一步时,程序直接跳转到了code:0处,而不是预先想象的跳到code:0018(即标号s1段处)。起初并不理解这是怎么回事,经过多次单步跟踪观察仍然感到怪异。仔细研读教科书[1]关于jmp指令的用法后,再结合机器码对程序进行分析,对jmp指令有了更多新的理解。 汇编代码 地址偏移(IP) 机器指令 assume cs:code (单位:h) code segment mov ax,4c00h 0000 B8004C int 21h 0003 CD21 start: mov ax,0 0005 B80000 s: nop 0008 90 nop 0009 90 mov di,offset s 000A BF0800 mov si,offset s2 000D BE2000 mov ax,cs:[si] CS:0020=F6EB mov cs:[di],ax CS:0008=9090 s0: jmp short s 0016 EBF0 s1: mov ax,0 0018 B80000 int 21h 001B CD21 mov ax,0 001D B80000 s2: jmp short s1 0020 EBF6 nop 0022 90 code ends end start 一、下面两条需要理解清楚 1、CPU指令执行的过程如下: <1>、从CS:IP指向的内存单元读取指令,并将其放入指令缓冲器。 <2>、IP=IP+当前被读取进入缓冲器的指令长度,即IP指向下一条指令。 <3>、执行缓冲器中的当前指令。转到步骤<1> 2、段内段转移jmp short ?指令的意义 <1>、IP是偏移量,即IP=标号处的地址-jmp指令后第一个字节的地址。 <2>、该指令的功能就是修改IP的值,执行该指令后IP=IP+8位偏移量 <3>、8位位移范围为-128~127,且偏移是用补码[2]的形式表示。 二、代码分析 1、第一步:程序从入口start(code:0005)处开始执行,当执行完code:0013~0014语句之后,标号s2处的语句jmp short s1的机器码被复制到code:0008中,IP为0016。此时,假定没有jmp short s指令,则程序会执行到s2:jmp short s1处,读jmp short s1入指令缓冲器后IP=0022;而s2到s1的转移为段内段转移,机器码格式为EB disp,且disp=标号s1-标号s2=(00018-0022)的补码=F6,所以指令jmp short s1的机器码应为EBF6。因此,EBF6被复制到code:0008~code:0009单元中。 2、第二步:将指令jmp short s(EBF0)读入指令缓冲器, IP=IP+0002=0018;标号s-标号s0=(0008-0018)的补码=F0,而s0到s的转移为段内段转移,机器码格式为EB disp(即EBF0) 3、第三步:指令jmp short s(EBF0)是一条修改IP的指令,执行指令EBF0后,IP=IP+(标号s-标号s0)=0008,指向code:0008单元。 4、第四步:读取code:0008单元的内容。由于s2处的指令jmp short s1(EBF6)被复制到code:0008~code:0009单元,所以读取该单元的内容后,IP=IP+0002=000A 5、第五步:指令jmp short s1(EBF6)是一条修改IP的指令,执行指令EBF6后,IP=IP+(标号s1-标号s2)=0000,指向code:0000单元。 6、第六步:程序转到code:0000处执行,即完成了正常结束。 三、后记 正确分析这段代码:需要理解CPU指令执行的过程以及jmp short ?中标号?的地址计算方法(通过IP修改来计算的)。另外,偏移的计算结果取补码也是一个关键。通过对这段看似不正常的代码进行分析之后,对汇编中的jmp指令有了更深层次的理解。
|