实验8 分析一个奇怪的程序
E:\mywork\asm\p906.asm
C:\>edit p906.asm
assume cs:code
code segment
mov ax,4c00h
int 21h
start:
mov ax,0
s:
nop ; 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
code ends
end
C:\>
编译、连接、运行
C:\>debug p906.exe
-u
076A:0000 B8004C MOV AX,4C00
076A:0003 CD 21 INT 21
076A:0005 B80000 MOV AX,0000
076A:0008 90 NOP
076A:0009 90 NOP
076A:000A BF0800 MOV DI,0008
076A:000D BE2000 MOV SI,0020
076A:0010 2E CS:
076A:0011 B804 MOV AX,[SI]
076A:0013 2E CS:
076A:0014 8905 MOV [DI],AX
076A:0016 EBF0 JMP 0008
076A:0018 B80000 MOV AX,0000
076A:001B CD21 INT 21
076A:001D B80000 MOV AX,0000
076A:0020 EBF6 JMP 0018 ; 位移0f6h,取反加1为:10h
076A:0022 90 NOP ; (-0ah)11110110 00001001 00001010(0ah)
076A:0023 0000 ADD [BX,SI],AL
-q
C:\>
运行说明
在debug中默认会从第1条指令(MOV AX,4C00)运行,无法正常调试。
所以我们使用rip命令,将ip修改为5,从cs:0005处开始执行。调试过程如下图:
单步调试到JMP 0008时,查看内存中的代码如下图所示:
从上图可以看到,076A:0008处的机器指令EBF6,汇编指令是:JMP 0000,怎么回事?请看后面的详细分析。
C:\>p906
C:\>
命令行下执行,也可以正常退出,如下图所示:
运行分析
先说一下整个运行流程的关键节点(可能和你想象的不一样):
1、执行076A:0016处的汇编指令JMP 0008;
2、执行076A:0008处的机器指令EBF6;
3、执行076A:0000处的汇编指令MOV AX,4C00和INT 21,调用中断结束运行。
详细分析
1、复制cs:0020处的字内容到cs:0008,实际上就是修改了内存cs:0008处内容为EBF6
(cs:0020处jmp 18的机器码);
2、顺序执行到076A:0016指令:JMP 0008;
3、执行076A:0008处的内容,机器指令为:EBF6。
机器指令:EBF6,EB表示跳转,偏移量为F6。
此处为关键,有两种算法,看懂这一点,你就通了:
1)用公式算(P180),根据机器指令:EBF6,求标号处的地址
:
8位位移=标号处的地址-jmp指令后的第一个字节的地址
//推导:
标号处的地址=8位位移+jmp指令后的第一个字节的地址
//看仔细喽:
8位位移:就是偏移量F6
jmp指令后的第一个字节的地址:076A:0008+2=076A:000A,段地址省略,直接取000A
//计算:
标号处的地址=0F6H+000AH=0H
补码:11110110 0F6H
补码:00001010 06H
相加:00000000 0H(高位溢出的1直接舍弃)
计算方法查看:附录2 补码
标号处的地址
为0H,即执行CS:0000处的代码。
2)向前跳转反推(看不懂这种就用第1种公式去算)
偏移量为F6,即向前跳转,F6取反加1为0AH,即向前跳转0AH个位移量:
补码:11110110 0F6H
取反:00001001 09H
加一:00001010 0AH(0F6H的绝对值)
jmp指令后的第一个字节的地址:076A:0008+2=076A:000A
从076A:000A向前跳转0AH,即执行076A:0000处的指令。
跳转方法查看:附录3 补汇编语言(masm.exe)对jmp的相关处理。
4、从076A:0000执行汇编指令MOV AX,4C00,调用中断结束程序。
后记
前面使用rip命令来进行调试,原因是代码抄错了:)
正确:
end start
错误:
end
(全文完)