X86汇编语言的分支和控制跳转指令

2020-11-05 11:15:35 浏览数 (1)

在使用高级语言例如java,C ,python来编写代码时,我们使用最多的莫过于分支跳转控制语句,例如if..else, switch..case, for()等,本节我们看看这些分支跳转语句如何在X86汇编语言下呈现。

程序其实由一系列控制流组成,当代码运行时如果某个条件满足,它会执行既定代码,如果不满足或者另一个条件满足,它又会执行另一部分代码。在控制跳转指令中,最常用的就是jmp指令,它让控制流直接跳转到具体设定的位置去执行那里的代码。这种跳转由于无需判断先决条件,因此也叫无条件跳转。

问题在于程序在逻辑设计上通常需要满足固定条件的跳转,例如提到的if..else就属于这种类型。在汇编语言层面就需要使用标志位来进行跳转前的条件判断。在汇编语言层面可以实现多达三十多种的条件跳转方式。我们先看几个例子: jz addr #当标志寄存器上的ZF位设置成1时跳转到addr指定的地址 jnz addr #当标志位ZF为0时跳转到地址addr je addr #经常跟在cmp指令后,cmp指令用于比对两个操作数是否相等,如果相等就将ZF标志位设置成1,于是je就跳转到地址addr jne addr #使用cmp比对两个操作数,如果两个数值不相等则跳转到地址addr jg addr #使用cmp比对两个有符号的操作数,如果第一个操作数比第二个大,那么跳转到地址addr jng addr #使用cmp比对两个有符号的操作数,如果第一个不大于第二个则跳转到地址addr。 ja,jae 分别对应jg和jge,不同在于这两条指令比对的是无符号操作数 jl addr #使用cmp比对两个有符号操作数,如果第一个比第二个小,那么跳转到地址addr jle #使用cmp指令比对两个有符号操作数,如果第一个不小于第二个,那么跳转到地址addr jb, jbe作用于je,jle相同,不过比较的是无符号操作数 jo addr #如果该指令的上一条指令运行后时代标志位OF设置为1则跳转到地址addr js addr #如果上一条指令运行后使得标志位SF设置为1则跳转到地址addr jecxz addr #如果寄存器ECX的值被设置为0则跳转到地址addr

在程序设计时还需要经常使用for , while等循环语句,这些循环功能其实就是使用了上面描述的跳转指令来实现,只要判断某个条件是否成立,如果成立则通过jmp跳转回原来指令的起始位置就能实现对同一部分指令进行多次运行的效果。

在X86汇编语言中,还有一系列指令专门负责对数据进行批量操作。例如指令movsx, cmpsx, stosx, scasx 其中x对应字符b或w,如果对应b那么对应指令就是对批量的字节数据,也就是8位数值进行操作,如果对应w则对字数据,也就是16位数值进行批量操作。这些指令在执行时会改变寄存器ESI和EDI的值,ESI指向数据的源地址,EDI指向数据的目的地址,同时寄存器ECX用于计数。

假设在地址0x8000处存有一个含有10字节个元素的数值,代码想将它们复制到地址0x9000,那么对应的汇编指令类似下面:

代码语言:javascript复制
mov esi, 0x8000
mov edi, 0x9000
mov ecx, 10
rep movsb

rep指令告诉CPU重复执行指令movsb,每执行一次,寄存器ECX的值就减去1,直到它的值为0为止。movsb将esi处一字节的数据转移到edi对应的地址,然后分别把这两个寄存器的数值增加1。如果你对C语言熟悉的话,你会想到函数memcpy。

在上面代码的执行过程中还受到一个标志位DF的影响,如果DF的值位0,那么每次执行movsb后,esi,edi的值就会增加1,如果DF的值位1,那么esi,edi的值在每次指令movsb执行后就会减1.

在前面代码中,如果指令mvsb变成cmpsb,那么CPU会将edi指向内存地址所存储的数值减去esi所指向内存地址的数值,然后根据结果来设置标志位。cmpsb则比对esi,edi两处内存存储的数值直到两个数值相等或者ecx寄存器的数值减为0为止。scasb指令用于搜索esi指定位置的数值,然后与寄存器al中的数值进行比对,如果相等则这种相关标志位,然后停止或者是将ecx寄存器的值减到0后停止。

汇编语言是黑客技术中非常复杂,繁琐,反人性的一部分,绝大多数人都会见而远之,这也是很多人无法掌握底层技术原理的原因。这几节介绍的汇编语言仅仅是一点点皮毛,剩下的还需要渴望掌握黑客技术或是希望掌握底层原理的同学自己去学习和把握。

0 人点赞