;分析下面的程序,在运行前仔细思考:这个程序可以正确的返回马?运行后在思考:为什么是这样的结果?
assume cs:codesg codesg segment mov ax,4c00h int 21h start: mov ax,0 s: nop nop mov di,offset s mov si,offset s2 mov ax,cs:[si] mov cs:[di],ax s0: jmp short s s1: mov ax,0 int 21h mov ax,0 s2: jmp short s1 nop codesg ends end start
程序执行流程:
start: mov ax,0开始执行,到mov cs:[di],ax,就把s2:处jmp short s1拷贝到s处,dubug 命令jmp short s1,共占两个字节,恰好占据原本2个nop的内存空间(nop指令占据一个字节)
jmp short s1 在s2处,可以计算一下需要往上编译10个字节(3(jmp short s1)+3*2(mov ax,0)+2(int 21H)),debug可以看到机器码为EBF6,F6是补码,转换一下即10,也即是向上偏移10个字节,这样的话s:处的2个nop的代码修改成EBF6。
程序接着往下运行,跑到s0:jmp short s,这样又s:处,执行EBF6,即往上偏移10字节,计算一下就知道,IP这个时候指向0,跑到codesg段起始位置,即执行mov ax,4c00H;int 21H,也就是返回操作。
;AX=0000 BX=0000 CX=0023 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
;DS=0BA7 ES=0BA7 SS=0BB7 CS=0BB7 IP=0005 NV UP EI PL NZ NA PO NC
;0BB7:0005 B80000 MOV AX,0000
assume cs:codeseg
codeseg segment
mov ax, 4c00h ;IP = 0000 E8004C
int 21h ;IP = 0003 CD21
start:
mov ax, 0 ;程序入口,IP = 0005 B80000
s: nop ;IP = 0008 80nop ;IP = 0009 90
mov di, offset s ;IP = 000A 机器码为BF0800 mov di, 0008
mov si, offset s2 ;IP = 000D 机器码为BE20000 mov si, 0020
mov ax, cs:[si] ;IP = 0010 机器码为8B04 将CS:0020内容传给AX
mov cs:[di], ax ;IP = 0013
;这4条指令是将s2处指令(EBF6)传到CS:0008处,正好覆盖了两个NOP字节
;CPU执行到这里,CS:0008 0009的内容是s2的偏移地址,为EBF6.继续往下执行
s0: jmp short s ;IP = 0016 机器码为EBF0 (IP位移) = 0008H - 0018H = -16D(-10H)
;跳到CS:0008(s处)执行.由于CS:0008处内容为EBF6,故跳转后就执行EBF6(s2处)
;EBF6执行后,IP = 000A.但EBF6作用为将IP向前移10个字节,即跳转到mov ax,4c00h处
s1: mov ax, 0 ;IP = 0018
int 21h ;IP = 000B 机器码为 CD21,占2个字节
mov ax, 0 ;IP = 000C
s2: jmp short s1 ; IP = 0020 (IP位移) = 0018H - 0022H = -10D 机器码为EBF6
codeseg endsnop ; IP = 0022
end start ;end伪指令通知汇编程序的结束,start标号说明程序从标号start处开始
参考资料:
http://blog.chinaunix.net/uid-10537496-id-462605.html
http://blog.csdn.net/superway117/article/details/1362863