0 前言
先给出源程序
assume cs:code
code segmentmov ax,4c00hint 21hstart:mov ax,0s:nopnopmov di,offset smov si,offset s2mov ax,cs:[si]mov cs:[di],axs0:jmp short ss1:mov ax,0int 21hmov ax,0s2:jmp short s1nopcode ends
end start
运行结果:本程序能够正常Return Operating System。
1 程序运行分析
给出程序代码中重要的偏移地址
assume cs:code
code segment0000 mov ax,4c00hint 21hstart:
0005 mov ax,00008 s:nopnopmov di,offset smov si,offset s2mov ax,cs:[si]mov cs:[di],ax0016 s0:jmp short s0018 s1:mov ax,0int 21hmov ax,00020 s2:jmp short s1nopcode ends
end start
关键的代码:
mov di,offset s
mov si,offset s2
mov ax,cs:[si]
mov cs:[di],ax
这部分程序,将s2中的jmp short s1
对应的2字节机器码,存放到了s中的2个nop
对应的内存单元中。
这里有一个关键点,短转移机器码存放的是位移量,不是偏移地址,因此,原来s2跳转到s1是偏移-10
个字节,存放到s的nop中,也将是偏移-10
个字节,而不是跳转到s1。
偏移-10
个字节,正好跳转到mov ax,4c00H
,程序得以正常return operating system。
这里的误区的,从表面看,可能误以为是将跳转到s1这条指令进行拷贝了,实际上不是这样。
那么,为什么是-10
而不是-8
?因为这条指令本身占2个字节,执行指令前,IP = 0008
,执行该指令,IP的变化为IP = IP + 2 - 10 = 0
,也就跳转到了mov ax,4c00H
了。
再关注一个细节,-10
是以补码形式存储的,查看指令jmp short s1
的机器码为EBF6
,F6
也就是十进制的-10
。
我们来看一下IP的十六进制数的变化,指令执行前,IP = 0008
,执行过程IP = 0008 + F6h + 2 = 0100h
。
咦?为什么不是0
,而是0100h
,回想一个知识,短转移的范围是-128~127,也就是00 - FFh
,再进行机器数加法的时候,IP是按照一个字节进行的加法,因此100h
将会丢失高位,变为00h
,则IP = 0000h
。
如果偏移地址更大,比如-10000
,对应D8F0
,这个时候,就进行字运算,超过FFFF
才会丢失。