1、无条件跳转指令 JMP#
EIP(指针寄存器)指向下一条将要执行的命令,执行完毕后,EIP 指向再下一条。EIP(指针寄存器)中的值会在每条指令执行后自动更新,指向下一条将要执行的指令的地址。汇编语言中的一些跳转指令(例如 JMP、JE、JNE 等)可以修 EIP 的值,从而实现跳转到指定地址处执行。
JMP A指令当中,A 是程序作者期望的无条件转向的一个内存地址。

jmp short 指令
JMP SHORT指令是一个两字节的短程跳转指令,只能向前或者往回跳转。第一个字节 EB 是跳转操作码 (OPCODE) 。跳转的方向由第二个字节的值确定。
这种跳转是有距离限制的。

显示 jmp short 指令的机器码
上图中,EB 是 jmp 的操作码,这条指令向前跳转到指令结束后的 5 个字节,计算目标地址可以采用下图的方式,指令的起始地址➕指令本身的长度(2 字节)➕ 第二个字节跳转的距离(5 字节)。

计算跳转位置
short 的跳转距离:最大的正向跳转距离是 0x7f。
注:ida 显示机器码
菜单栏 Options-General 设置,将 Number of opcode bytes (graph) 的值改为 12(10),默认位 0。

注:数据库快照功能
逆向其间会对程序做些修改,会打断已识别的函数,使用数据库快照能够返回之前某个时候的状态,当遇到问题不知如何修复时,可以使用这个功能。

给快照命名

加载快照,View-Database snapshot manager

可以看到所有的快照清单以及创建的日期

实验 2 将跳转的第二个字节从 05 改成 7f
使用 ida patch byte 功能将 05 改成 7f






接下来将 0x7f 改为 0x80。

改为 0x80 后,程序往回跳转,之前的 0x7f 是最远的向前跳转,那么,0x80 是最远的往后跳转。

由于 python 不会从数值上判断向前还是往后跳转,必须输入一个 dword 表示 - 0x80,也就是0xffffff80,计算的结果和0xffffffff进行按位与运算,超过 32 位的比特位会被清零,所以得到0x4012a6这个值。
如果把 0xff 作为第二个字节的跳转距离,这是一个最小跳转距离 - 1,由上图可知,最终跳到 0x401325,也就是跳转了 1 个字节。
如果再往回多跳转一个字节,指令的第二个字节改为 fe,最终会跳回起点位置,从而形成一个无限循环。

如果将第二个字节改为 fd,从指令的结尾往回跳转 3 个字节,最终跳到 0x401323。


使用短跳转只能在当前地址的附近跳转,无法跳转到所有的地址。所以程序会使用长跳转。

上图中显示了一些长跳转,图中的 loc 表示那一条指令是一般指令。

上图是一个长跳转,0x4026ae 到 0x4029b3 的距离超过了短跳转的作用范围。

跳转范围计算公式位:终点指令起始地址 - 起点指令起始地址 - 5
5 是跳转指令的长度,结果是 0x300,也就是长跳转指令操作码 E9 后面的 dword。

2、 有条件跳转指令#
CMP A,B #对A和B进行比较,根据比较结果进行不同的操作

条件跳转指令
上图中,CMP 对 eax、ebx 进行比较(寄存器相减),如果它们相等,那么结果就是 0,就触发了标志寄存器(EFLAGS)中 Z 标记或者 zero 标记,JZ 指令检测到这个标记然后进行跳转。如果触发 Z 标记,就会执行上图中的绿色箭头路径,如果没有触发,那么就执行青色箭头路径。
| 16 进制 | ASM | 描述 | |
|---|---|---|---|
| 74 or 0F84 | JE | 等于则跳转 | |
| 75 or 0F85 | JNE | 不等于则跳转 | |
| 77 or 0F87 | JA | 大于则跳转 | |
| 7C | JL | 小于则跳转 | |
| 7D | JGE | 不小于则跳转 | |
| 7E | JLE | 小于等于则跳转 | |
| 7F | JG | 大于等于则跳转 | |
| 80 | JO | 溢出则跳转 | |
| 81 | JNO | 不溢出则跳转 | |
| 82 | JB/JNAE | 低于则跳转 | |
| 83 | JNB/JAE | 不低于则跳转 | |
| 86 | JNA/JBE | 不大于等于则跳转 | |
| 88 | JS | 符号为负则跳转 | |
| 89 | JNS | 符号为正则跳转 | |
| 8C | JL/JNGE | 小于则跳转 | |
| 8D | JGE/JNL | 不小于则跳转 | |
| 8E | JLE/JNG | 小于等于则跳转 | |
| 8F | JG/JNLE | 大于等于则跳转 |
除了 JMP 和 NOP 之外,剩余的都是根据比较结果执行的条件跳转指令。
3、CALL 和 RET 指令#
- CALL 指令用来调用一个函数。
- RET 指令用来返回调用这个函数的指令的下面一条指令处。
下图中有一个 CALL 调用指令,程序将跳转到0x4013d8去执行这个函数。 (在0x4013d8这个地址前面的sub_表示这里是一个函数) CALL 指令会将返回的地址0x40123d保存到栈上。

call 指令实例
双击这个函数,可以跳转到如下位置,函数具体内容如下:

0x4013d8 函数具体内容
如上图所示,函数执行完毕后执行 ret 指令,跳转到栈上保存的返回地址 0x4012d 处,就是调用这个函数指令的下一条。

返回地址