跳转指令
- 控制转移类指令用于实现分支、循环、过程等程序结构
- 是仅次于传送指令的最常用指令
- 控制转移类指令通过改变IP(和CS)值,实现程序执行顺序的改变
举例:
jmp, loop,call,ret等
无条件跳转 jmp
不改变段(默认CS段),改变IP
- 段内转移、直接寻址
- 段内转移、间接寻址
改变段,改变IP
- 段间转移、直接寻址
- 段间转移、间接寻址
jmp
的参数 参考章节4_寻址方式
指令格式:
jmp [ptr / LABEL]
指令拆分:
100: EBFE jmp 100
地址: EB[偏移(一个字节)] jmp 跳转距离
FE=-2
跳转的地址=下条指令的地址+偏移
显示 转移对照表
- 如果不跟 修饰符(near/short/far)
- 那么编译器会自动判断
跳转指令 | 参数限制 | 跳转范围 | 机器码 | 作用 | 地址变化 |
---|---|---|---|---|---|
jmp short [寻址参数] | 立即数的话有限制:-128~+127 | -128~+127 | EB 偏移(1个字节) | 段内短转移 | ip<-ptr |
jmp near [寻址参数] | 立即数的话有限制:-32768~+32767 | -32768~+32767 | E9 偏移(2个字节) | 段内近转移 | ip<-ptr |
jmp word ptr [寻址参数] | 寻址参数 | 64KB | FF 标识编号 | 段内间接转移 | ip<-ptr |
jmp far ptr [标签] | 代码的标签 | 1MB | EA 偏移 段地址 | 段间直接寻址 | cs<-lable的断地址,ip<-lable的偏移地址 |
jmp far ptr [bx]/[bp+si+x] | [bx]/[bp+si+x] 寄存器 | 1MB | FF 偏移 段地址 | 段间间接寻址 | ip<-[mem],cs<-[men+2] |
@@
$
代表当前的偏移值
Call $+3
Pop ax
@f
:f
是front
的缩写,可以理解为代码向前执行的方向,@f
自动匹配向前的最近@@
的位置 @b
:b
是before
的缩写,@b
自动匹配向后的最近@@
的位置
使用@@
会导致汇编代码可读性差,所以不推荐使用
指令格式的应用
- 设置钩子
- 花指令/对抗
模拟函数
; 我们在这里设置
; cs为 代码运行段
; ds为 数据段
; ss为栈空间
assume cs:main_code,ds:this_data,ss:this_stack
this_stack segment stack
stack_buf db 256 dup(0)
this_stack ends
this_data segment
addr_jmp_src db 4 dup(0)
addr_jmp_dst db 4 dup(0)
str_true db 'true$'
str_false db 'false$'
this_data ends
main_code segment
; 类似main函数作为asm起始点
start:
;锁定栈
mov ax, seg this_stack
mov ss, ax
;锁定数据区
mov ax, seg this_data
mov ds,ax
; 设置要显示的符串
mov dx,offset str_true
; 准备跳回来的地址
mov bx, offset addr_jmp_src
mov word ptr [bx], offset show_back ; 填充 ip
mov word ptr [bx+2], seg main_code ; 填充 段地址
; 准备跳转的目标地址
mov bx, offset addr_jmp_dst
mov word ptr [bx], offset show_from_dx ; 填充 ip
mov word ptr [bx+2], seg show_code ; 填充 段地址
jmp far ptr [bx]; 跳转
;目标地址执行完成后跳转到这里
show_back:
; 安全退出
mov ah,4ch
int 21h
main_code ends
show_code segment
show_from_dx:
; 显示字符串
mov ah,09h
int 21h
jmp roll_back_to_src
roll_back_to_src:
; 回到调用这个代码快的地址
mov bx, offset addr_jmp_src
jmp far ptr [bx]; 跳转
show_code ends
end start
有条件跳转 jcc/jxx
JCC指令 | 中文含义 | 检查符号位 | 典型C应用 |
---|---|---|---|
JZ/JE | 若为0则跳转;若相等则跳转 | ZF=1 | if (i == j);if (i == 0); |
JNZ/JNE | 若不为0则跳转;若不相等则跳转 | ZF=0 | if (i != j);if (i != 0); |
JS | 若为负则跳转 | SF=1 | if (i < 0); |
JNS | 若为正则跳转 | SF=0 | if (i > 0); |
JP/JPE | 若1出现次数为偶数则跳转 | PF=1 | "1"的个数为偶数 |
JNP/JPO | 若1出现次数为奇数则跳转 | PF=0 | "1"的个数为奇数 |
JO | 若溢出则跳转 | OF=1 | 溢出 |
JNO | 若无溢出则跳转 | OF=0 | 无溢出 |
JC/JB/JNAE | 若进位则跳转;若低于则跳转;若不高于等于则跳转 | CF=1 | if (i < j); |
JNC/JNB/JAE | 若无进位则跳转;若不低于则跳转;若高于等于则跳转; | CF=0 | if (i >= j); |
JBE/JNA | 若低于等于则跳转;若不高于则跳转 | ZF=1或CF=1 | if (i <= j); |
JNBE/JA | 若不低于等于则跳转;若高于则跳转 | ZF=0或CF=0 | if (i > j); |
JL/JNGE | 若小于则跳转;若不大于等于则跳转 | SF != OF | if (si < sj); |
JNL/JGE | 若不小于则跳转;若大于等于则跳转 | SF = OF | if (si >= sj); |
JLE/JNG | 若小于等于则跳转;若不大于则跳转 | ZF != OF 或 ZF=1 | if (si <= sj); |
JNLE/JG | 若不小于等于则跳转;若大于则跳转 | SF=0F 且 ZF=0 | if(si>sj) |
关键点
- jb/ja用于无符号数
- jl/jg用有符号数
- 搭配cmp使用
loop
- LOOP指令为循环指令,其格式为
LOOP 标号
- 当cx不为0时就继续循环
- 如果标号为
0
则向下执行(CX默认为循环计数器)
mov ax,10
mov cx,5
loopa:
add ax,1
add ax,1
loop loopa ;首先`cx=cx-1`,如果cx==0 就结束循环
call/ret/retf
- call 和jmp 都是用来跳转程序运行点
- 但是 jmp是无条件跳转
- call 除了跳转 还可以配合 ret/retf 回到当初跳转的地方
call/ret
段内跳转:
Call 标号
相当于:
Push ip
Jmp xxx
Jmp xxx:相当于
push xxx
ret
ret:
用栈中数据改IP内容,近转移
相当于:
pop ip
call far ptr /retf
段间跳转
call far ptr 标号
相当于:
push cs
push ip
Jmp xxx
retf:
用栈中数据同时改CS,IP,远转移
相当于:
pop ip
pop cs
call 复杂跳转
; 我们在这里设置
; cs为 代码运行段
; ds为 数据段
; ss为栈空间
assume cs:main_code,ds:this_data,ss:this_stack
this_stack segment stack
stack_buf db 256 dup(0)
this_stack ends
this_data segment
str_true db 'true',10,13,'$'
str_false db 'false',10,13,'$'
this_data ends
main_code segment
; 类似main函数作为asm起始点
start:
;锁定栈
mov ax, seg this_stack
mov ss, ax
;锁定数据区
mov ax, seg this_data
mov ds,ax
; 模拟 for(;cl!=ch;cl++){echo false}
mov cl,0
mov ch,3
; ----------------if_for
call far ptr if_for;远掉,push cs,ip
; ----------------显示结果
call far ptr show_from_dx;远掉,push cs,ip
; 安全退出
mov ah,4ch
int 21h
main_code ends
fx_code segment
show_from_dx:
; 显示字符串
mov ah,09h
int 21h
jmp roll_back_to_src
if_for:
jmp if_for_main
if_for_true:
mov dx,offset str_true
jmp roll_back_to_src
if_for_false:
add cx,1
mov dx,offset str_false
; 显示字符串
; 虽然这里可使用近跳转
; 但是 show_from_dx 统一使用远回
; 所以我们这里也是用 远掉
call far ptr show_from_dx
jmp if_for_main
if_for_main:
cmp cl,ch
je if_for_true ;cl==ch 就结束
jmp if_for_false
roll_back_to_src:
; 回到调用这个代码快的地址
retf;远回,pop cs,ip
fx_code ends
end start