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 处,就是调用这个函数指令的下一条。
返回地址