第9章 转移指令的原理
可以修改IP,或同时修改CS和IP的指令统称为转移指令。概括的讲,转移指令就是可以控制CPU执行内存中某处代码的指令。
根据转移行为分为两类
- 段内转移,只修改IP。如:jmp ax
- 段间转移,同时修改CS和IP。如:jmp 1000:0
同时,根据转移指令对IP的修改范围不同,段内转移有分为短转移和近转移
- 短转移IP的修改范围是-128~127
- 近转移IP的修改范围是-32768~32767
0x01 操作符 offset
操作符的作用是:取得标号的偏移地址。
如下述程序:
1 assume cs:codesg 2 3 codesg segment 4 start: mov ax,offset start 5 s: mov ax,offset s 6 codesg ends 7 8 end start
其中,offset操作符的意思是取得标号start和s的偏移地址。所以,mov ax,offset start 与mov ax,0的效果相同。
0x02 jmp指令
无条件转移指令,可以只修改IP,也可以同时修改CS和IP。
1. 依据位移进行转移的jmp指令
格式:
jmp short 标号(转到标号处执行指令)
解释:上述指令实现的是段内短转移,它对IP的修改范围为-128~127。向前转移时可以最多越过128字节,向后转移可以最多越过127个字节。
指令执行完之后,CS:IP应该指向标号处的指令。
程序1.1
1 assume cs:codesg 2 codesg segment 3 start: mov ax,0 4 jmp short s 5 add ax,1 6 s: inc ax 7 codesg ends 8 end start
当指令执行到jmp short s,将会跳过吓一跳指令即add ax,1 转到执行inc ax。所以最终ax只是加了一个1。
程序1.2
1 assume cs:codesg 2 codesg segment 3 start: mov ax,0 4 mov bx,0 5 jmp short s 6 add ax,1 7 s: inc ax 8 codesg ends 9 end start
在debug中分别运行1.1和1.2 程序之后
程序1.1

程序1.2

观察到不同的程序中,JMP对应的机器码都是EB03。第一个偏移地址是0008,第二个偏移地址是000B。
所以,在执行JMP指令时,其机器码中并不包含转移的目的地址,而包含的是转移的位移。
该位移是编译器根据汇编指令中的“标号”计算出来的。
CPU执行指令的过程如下:
(1) 从CS:IP指向内存单元读取指令,读取的指令进入指令缓存器;
(2) (IP) = (IP) + 所读取指令的长度,从而指向下一条指令;
(3) 执行指令。转到(1),重复这个过程。
按照这个步骤,看一下程序1.2中jmp short s指令的读取和执行过程
(1) (CS)=0772H,(IP)=0006H.CS:IP执行EB 03(jmp对应的机器码)
(2) 读取指令码EB 03进入指令缓存器
(3) (IP)=(IP)+所读取指令的长度=(IP) + 2 = 0008H,CS:IP指向add ax,1
(4) CPU执行指令缓存器中的指令EB 03
(5) 指令EB 03 执行后,(IP)=000BH,CS:IP 指向inc ax
如果按照其他机器码的话,CPU执行该指令后,IP指向下一条指令,即此时(IP)=0008H。但是jmp指令后,IP变为000BH,比原来的多了3。这说明,该指令会修改(IP)的大小。
具体怎么修改,可以看一下下图:

也就是说,"jpm short 标号"指令的功能为:(IP) = (IP) + 8位位移。(等号右边的地址为该条指令执行完之后,下一条指令的偏移地址,1.2中(IP)=0008H)
注释:这里的8位位移值得是8个二进制位,所以其对应的范围是-128~127。
具体的计算方法为:标号处的地址-jmp指令后下一条指令所对应的地址
如程序1.2中,标号处的地址是0772:000B,jmp指令后下一条指令所对应的地址是0772:0008
2.转移的目的地址在指令中的jmp指令
格式:"jmp far ptr 标号"
实现的是段间转移,又成为远转移。
(CS)=标号所在段的段地址;(IP)=标号在段中的偏移地址
程序2.1
1 assume cs:code 2 code segment 3 start: mov ax,0 4 mov bx,0 5 jmp far ptr s 6 db 256 dup (0) 7 s: add ax,1 8 inc ax 9 code ends 10 end start
编译连接后,deubg打开程序
对应的机器码是:

jmp far ptr s 所对应的机器码是: EA 0B 01 72 07
其中,"0B 01 72 07" 是目的地址在指令中的存储顺序,高地址的"72 07"是转移的段地址:0772H,低地址的"0B 01"是偏移地址:010BH
查看一下该目的地址对应的指令

3. 转移地址在寄存器中的jmp指令
格式: jmp 16位 reg
功能:(IP)=(16位 reg)
通过代码理解下
程序3.1
1 assume cs:code 2 code segment 3 start: mov ax,1 4 mov bx,ds 5 add ax,bx 6 jmp ax 7 mov ax,4c00h 8 int 21h 9 code ends 10 end start
debug后,看一下指令对应的机器码

4.转移地址在内存中的jmp指令
(1) jmp word ptr 内存单元地址(段内转移)
功能:从内存单元地址处开始存放着一个字,是转移的目的偏移地址。
内存单元地址可用寻址方式的任一格式给出
情形一:
mov ax,0001H
mov ds:[0],ax
jmp word ptr ds:[0]
结果,(IP)=0001H
情形二:
mov ax,0123H
mov [bx],ax
jmp word ptr [bx]
执行后,(IP)=0123H
(2) jmp dword ptr 内存单元地址(段间转移)
功能:从内存单元地址处开始存放着两个字,高地址处的字是转移的目的段地址,低地址处是转移的目的偏移地址
(CS)=(内存单元地址+2)
(IP)=(内存单元地址)
如:
mov ax,0123H
mov ds:[0],ax
mov word ptr ds:[2],0
jmp dword ptr ds:[0]
执行后,(CS)=0,(IP)=0123H,CS:IP指向0000:0123
0x02 jcxz 指令
jcxz 指令为有条件转移指令,属于段内转移中的短转移。
格式: jcxz 标号(如果(cx)=0,转移到标号处执行。)
操作:当(CX)=0时,(IP)=(IP)+8位位移
当(CX)≠0时,程序向下执行
程序4.1
1 assume cs:code 2 code segment 3 start: 4 mov bx,0 5 s: mov ch,0 6 mov cl,[bx] 7 jcxz ok 8 inc bx 9 jmp short s 10 ok: mov dx,bx 11 mov ax,4c00h 12 int 21h 13 code ends 14 end start
该程序目的是,实现在ds对应的内存地址中查找第一个值为0的字节,找到后,将它的偏移地址存储在dx中。
编译链接,在debug中打开
先查看下,ds对应的内存中内容

即内存中ds:0004中的内容为00
截至运行知道,查找到第一个值为0的字节

dx中的内容,即为对应的偏移地址
0x03 loop指令
loop 指令为循环指令,所有的循环指令都是短转移。
对应的机器码中包含转移的位移,而不是目的地址。
对IP的修改范围:-128-127
格式:loop 标号 ((cx)=(cx)-1,如果(cx)≠0,转移到标号处执行。)
操作:
(1) (cx)=(cx)-1;
(2) 如果(cx)≠0,(IP)=(IP)+8位位移
如果(cx)=0,什么也不做(程序向下执行)
loop 标号 的功能相当于:
(cx)--;
if ((cx)≠0) jmp short 标号;
0x04 其他
1. 根据位移进行转移的意义
更具位移进行转移的话,使用起来更加灵活。比如说下面的程序
s: add ax,ax
loop s
假如现在使用标识号的地址进行定位的。想要准确的跳转到s指令处,就需要对环境进行严格的限定,比如说,代码段的地址等
而使用位移进行转移的话,不需要这些额外的要求。
2.编译器对转移位移超界的检测
根据位移进行转移的指令,它们的转移范围受到转移位移的限制,如果在源程序中出现了转移范围超界的问题,在编译的时候,编译器将报错。

浙公网安备 33010602011771号