详解JMP

来源:互联网 发布:令狐冲 知乎 编辑:程序博客网 时间:2024/05/21 12:45

jmp指令初一看感觉挺简单,无条件跳转指令。通常我们通过百度和查阅资料可以了解到以下知识

格式: JMP OPRD
 ----

 功能: JMP指令将无条件地控制程序转移到目的地址去执行.当目的地址仍在同一个代码段内,称为段内转移;当目标地址不在同一个代码段内,则称为段间转移.这两种情况都        将产生不同的指令代码,以便能正确地生成目的地址,在段内转移时,指令只要能提供目的地址的段内偏移量即够了;而在段间转移时,指令应能提供目的地址的段地址及段内偏移地址值. 


说明: 1. 其中OPRD为转移的目的地址.程序转移到目的地址所指向的指令继续往下执行.
 ----
       2. 本组指令对标志位无影响.

       3. <1> 段内直接转移指令: JMP NEAR 标号
              即: JMP NEAR  标号;  (IP)<--disp16+(IP)
                  JMP SHORT 标号;  (IP)<--disp8+(IP)

          <2> 段内间接转移指令: JMP OPRD
              例如: JMP BP               ; 转向(SS):(BP)
                    JMP JNEAR[BX]        ; 转向(CS):(BX)+JNEAR
                    JMP WORD PTR[BX][DI] ; 转向(CS):(BX)+(DI)

          <3> 段间直接转移指令: JMP FAR 标号
              由于标号之前用FAR说明为远的属性,因而只能是一条段间转移指令.执行该指令时,将把标号所在的段的值送CS,将标号在所属段内的偏移量送IP,从而形成新的转移地址CS:IP

          <4> 段间间接转移指令:JMP OPRD其中的OPRD为存储器双字操作数.段间间接转移只能通过存储器操作数来实现.
              例如:指令JMP DWORD PTR[BX],其操作数是一个双字类型的存储器操作数,它指向数据段DS,段内偏移为(BX).从这个DS:BX开始的前两个字节中,存放了目标地址的段内偏移值,后两个字节中,存放了目标地址所在的新的段的段基址,分别将它们送至IP及CS,便形成了新的转移地址.

以上站在一个开发者的角度上来讲,应该知道这些足够了,可是在做逆向分析或inline hook的时候,我们发现在这些还不够,貌似jmp没有那么简单,先看看一张截图:

发现了点什么了没?jmp的机器码为E9,oppd = 跳转到的地址-当前地址-5 byte

上图中oppd = A30B0000,当前地址=00C21118,跳转后的地址为:00C21CC0

发现 00C21CC0 - 00C21118 - 5 = BA3,并不等于A30B0000哇。可是别忘记了机器码是按照字节排列的,所以A30B0000实际上是 00000BA3

知道了这个规律之后,我们可以轻松的知道,如果oppd为负(oppd采用补码,即高位为1时,也就是oppd最后倒数第二位>9),那么会向下跳,最高位为0的时候会向上跳。

在编程应用中,发现很多人喜欢写成这样的结构体就见怪不怪啦

typedef struct _JMPCODE

{

 BYTE E9;

 ULONGJMPADDR;

}JMPCODE,*PJMPCODE

可是为什么要这样用那个公式计算出oppd呢,原来在设计jmp指令的时候,用的是偏移,跳到的地址 = 当前地址+oppd+5,那么就很好理解了,jmp指令,其对应的机器码中并没有转移的目的地址,而是相对于IP的转移位移,而这个转移位移在编译时就已经由编译器编译完成。至于说为什么有个5字节,因为jmp指令的长度有2个字节,3个字节和5个字节的说法,在这里的段间跳转显然是 jmp指令长度为5,所以转移位移实际上应该是应该是目的地址和jmp下一条指令的位移差咯!



0 0
原创粉丝点击