硬编码(二)经典定长指令二
在CPU中,有一个EIP寄存器,这个寄存器存储着下一次CPU执行指令的地址,通过修改EIP的值,可以改变CPU执行指令的顺序
修改EIP有以下四种汇编指令:1.jmp 2.jcc 3.call 4.ret,接下来我们将学习这四种汇编指令的硬编码
0x70 - 0x7F
该范围硬编码表示汇编代码条件跳转指令JCC,其后跟一个字节立即数的偏移(有符号),共两个字节。 如果条件跳转判断成立,则会跳转到当前指令地址 + 当前指令长度 + Ib(一字节立即数),向后跳转的范围是0x0 - 0x7f,向前跳转的范围是0x80 - 0xFF。
定长指令 | 汇编代码 |
0x70 | JO |
0x71 | JNO |
0x72 | JB/JNAE/JC |
0x73 | JNB/JAE/JNC |
0x74 | JZ/JE |
0x75 | JNZ/JNE |
0x76 | JBE/JNA |
0x77 | JNBE/JA |
0x78 | JS |
0x79 | JNS |
0x7A | JP/JPE |
0x7B | JNP/JPO |
0x7C | JL/JNGE |
0x7D | JNL/JGE |
0x7E | JLE/JNG |
0x7F | JNLE/JG |
以0x70为例:0x70对应的汇编代码为JO,其中J为jmp,O为溢出标志位OF,这个代码的意思是:当溢出标志位为1时,发生跳转。
0x0F 0x80 - 0x0F 0x8F
条件跳转,后跟四个字节立即数的偏移(有符号),共五个字节。如果条件成立,跳转到当前指令地址 + 当前指令长度 + Id,向下跳的范围是0x0 - 0x7FFFFFFFF,向上跳的范围是:0x80000000 - 0xFFFFFFFF。
与前文0x70 - 0x7F相比,区别是立即数偏移的不同:一个是一字节,一个是四字节
定长指令 | 汇编代码 |
0x0F 0x80 | JO |
0x0F 0x81 | JNO |
0x0F 0x82 | JB/JNAE/JC |
0x0F 0x83 | JNB/JAE/JNC |
0x0F 0x84 | JZ/JE |
0x0F 0x85 | JNZ/JNE |
0x0F 0x86 | JBE/JNA |
0x0F 0x87 | JNBE/JA |
0x0F 0x88 | JS |
0x0F 0x89 | JNS |
0x0F 0x8A | JP/JPE |
0x0F 0x8B | JNP/JPO |
0x0F 0x8C | JL/JNGE |
0x0F 0x8D | JNL/JGE |
0x0F 0x8E | JLE/JNG |
0x0F 0x8F | JNLE/JG |
0xE0 - 0xE9
如下表格中的Jb等同Ib表示偏移量,其宽度根据后面b或者d决定
定长指令 | 汇编代码 | 宽度 | 作用 |
0xE0 | LOOPNE/LOOPNZ Ib (Jb) | 共2字节 | 先进行 ECX = ECX - 1 当 ZF = 0 && ECX!=0 时跳转到当前指令地址 + 当前指令长度 + Ib |
0XE1 | LOOPE/LOOPZ Ib (Jb) | 共2字节 | 先进行 ECX = ECX - 1 当 ZF = 1 && ECX != 0 时跳转到当前指令地址 + 当前指令长度 + Ib |
0XE2 | LOOP Ib (Jb) | 共2字节 | 先进行 ECX = ECX - 1 当 ECX!=0 时跳转到当前指令地址 + 当前指令长度 + Ib |
0XE3 | JrCXZ Ib (Jb) (在32位模式中rCX为ECX) | 共2字节 | 当 ECX = 0 时跳转到当前指令地址 + 当前指令长度 + Ib(自己控制步长) |
0xE8 | CALL Id (Jd) | 共5字节 | CALL指令的下一条指令地址入栈后,跳转到当前指令地址 + 当前指令长度 + Id |
0xE9 | JMP Id (Jd) | 共5字节 | 跳转到当前指令地址 + 当前指令长度 + Id |
段寄存器
在处理器十六位模式下中,寻址总线共有二十个,最多寻址2^20bit == 1MB。但只有十六位的寄存器,这意味着仅依靠十六位的寄存器最多只能寻址2^16bit == 64KB。为解决1MB的寻址范围,处理器便添加了段寄存器,通过段寄存器值 << 4 + 立即数进行寻址。但在处理器32位保护模式下,段寄存器用来做权限的控制
其他指令
定长指令 | 汇编代码 | 宽度 | 作用 |
0xEA | JMP Ap (Ap:六字节长度的直接地址) | 共7字节 | JMP CS:Id:将Ap中的高2字节赋值给CS段寄存器,表目标段,低4字节赋值EIP,跳转到目标段的段偏移处 |
0xEB | JMP Ib (Jb) | 共2字节 | 跳转到当前指令地址 + 当前指令长度 + Ib |
0xC3 | RET | 共1字节 | POP EIP,出栈四字节 |
0xC2 | RET Iw | 共3字节 | POP EIP后,进行 ESP = ESP + Iw |
0XCB | RETF (return far) | 共1字节 | 出栈8个字节,低4个字节赋值给EIP,高4个字节中低2位赋值给CS |
0xCA | RETF Iw | 共3字节 | 出栈8个字节,低4个字节赋值给EIP,高4个字节中低2位赋值给CS后,ESP = ESP + Iw |