硬编码:机器指令
说明:一共有六部分,第一部分前缀指令,只管自己,Opcode是这六部分的核心,Opcod决定ModR/M,ModR/M决定SIB,这三部分是这整条指令的核心,后面两部分属于“配角”,(偏移量和立即数)由核心决定
一.前缀指令(“:”)
分组:
定义 | 说明 | 指令 |
---|---|---|
LOCK和REPEAT前缀指令 | LOCK(锁地址总线,多核情况下某时刻只允许一个核读这条指令) | F0 |
REPNT/REPNZ(重复执行后面的指令) | F2 | |
REP/REPZ(重复执行后面的指令) | F3 | |
段前缀指令 | 改变默认,明确告诉使用哪个段寄存器(双向 16位->32位 32位->16位) | CS(2E) SS(36) DS(3E) ES(26) FS(64) GS(65) |
地址宽度前缀指令 | 改变默认,明确告诉使用那种寻址方式(双向 16位->32位 32位->16位) | 67 |
操作数宽度 | 改变默认,改变操作数的宽度双向 16位->32位 32位->16位 | 66 |
使用; 最多四个,一组一个,四个字节,最少0个,没有顺序
二.定长指令和变长指令
关键表格
Zz表示法 技巧:E和G–>变长
1. 定长指令
Opecode确定后指令长度确定,Opcode 后没有ModR/M,上图中圈出来的是比较常用的定长指令
(1)经典的定长指令_修改ERX
- PUSH/POP 50系列
- INC/DEC 40系列
- Mov Rb,Ib 0xb0-0xb7系列
- Mov ERX,Id 0xb8-0xbF系列
- XCHG (交换两个寄存器的值) 0x90-0x97系列
前八列:
50 <–> push eax
51 <–> push ecx
52 <–> push edx
53 <–> push ebx
54 <–> push esp
55 <–> push ebp
56 <–> push esi
57 <–> push edi
后八列:
58 <–> pop eax
59 <–> pop ecx
5A <–> pop edx
5B <–> pop ebx
5C <–> pop esp
5D <–> pop ebp
5E <–> pop esi
5F <–> pop edi
寄存器编号
(2)经典的定长指令_修改EIP
0x70-0x7F
条件跳转:Opcode后面跟一个立即数的偏移,因此指令共两个字节
立即数是有符号的:最高位为0(7F)向下跳,最高位为1(80)向上跳
<1>条件跳转,后跟一个字节立即数的偏移(有符号),共两个字节
<2>如果条件成立,跳转到 当前指令+当前指令长度+Ib
<3> 最大值,向前跳7f,向后跳80
硬编码 | 汇编指令 |
---|---|
0x70 | JO |
0x71 | JN0 |
0x72 | JB/JNAE/JC |
0x73 | JNB/JAE/JNC |
0x74 | JN/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 |
0x0F 0X80 -0X0F 0X8F
0F80~0F8F远距离JCC跳转:
后面跟一个四字节的立即数,指令长共6字节。立即数正负以7FFFFFFF、80000000为界。0F800F8F和707F对应列的指令一样只不过立即数字节数不同。
<1>条件跳转,后跟四个字节立即数的偏移(有符号),共五个字节
<2>如果条件成立,跳转到 当前指令+当前指令长度+Id
<3> 最大值,向前跳7FFFFFFFF,向后跳80000000
硬编码 | 汇编指令 |
---|---|
0x0F 0X80 | JO |
0x0F 0X81 | JN0 |
0x0F 0X82 | JB/JNAE/JC |
0x0F 0X83 | JNB/JAE/JNC |
0x0F 0X84 | JN/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 | LOOPNE/LOOPNZ Ib(jb) ECX=ECX-1 当ZF=0&&ECX!=0时跳转到当前指令地址+当前指令长度+Ib | 2 |
0xE1 | LOOPE/LOOPZ Ib(jb) ECX=ECX-1 当ZF=1&&ECX!=0时跳转到当前指令地址+当前指令长度+Ib | 2 |
0xE2 | LOOP Ib(jb) ECX=ECX-1 当ECX!=0时跳转到当前指令地址+当前指令长度+Ib | 2 |
0xE3 | JrCXZ Ib(jb) (在32位模式中,rCX为ECX) 当ECX=0时跳转到当前指令地址+当前指令长度+Ib(自己控制步长) | 2 |
0xE8 | CALL Id(jd) ECX=ECX-1 CALL指令的下一跳指令地址入栈后,跳转到当前指令地址+当前指令长度+Id | 5 |
0xE9 | JMP Id(jd) 跳转到当前指令地址+当前指令长度+Id | 5 |
E0 <–> loopne/loopnz Ib(dec ecx) (ZF=0 && ECX != 0)
E1 <–> loope/loopz Ib(dec ecx) (ZF=1 && ECX != 0)
E2 <–> loop Ib(dec ecx) (满足ECX != 0就跳转)
E3 <–> jecxz/jrcxz Ib (满足ECX=0跳转)
注意: Ib即为byte类型立即数(Immediate data),Iw则是Immediate data word,Id即为Immediate data dword,Ap即六字节长度的直接地址
E8 <–> call Id
E9 <–> jmp Id
EA <–> jmp Ap,jmp CS:Id (**前四个字节为跳转地址,后两个字节为段选择子.**即高两字节赋给CS,低四字节赋给EIP)
EB <–> jmp Ib
FF <–> call dword ptr [edx]
E8call为直接call,call后面的地址即为要跳转的地址,FFcall为间接call,后面跟的内存那只能够存放着即将要跳转的地址。比如用对象指针访问一个普通成员函数和一个虚函数,其call的硬编码都不同
其他指令
定义 | 说明 | 字节数 |
---|---|---|
0xEA | JMP Ap(Ap:六字节长度的而直接地址)JMP CS:Id 将AP中的高两位赋值给CS,低4位直接赋值给EIP,即跳转 | 7 |
0xEB | JMP Ib(Jb)跳转到当前指令地址+当前指令长度+Ib | |
0xC3 | RET EIP出栈 | 1 |
0xC2 | RETF lw EIP出栈后,ESP=ESP+iw | 3 |
0xCB | RETF(return far) 出栈8个字节,低于4个字节赋值给EIP,高4个字节中低2位赋值给CS | 1 |
0xCA | RETF lw 出栈8个字节,低于4个字节赋值给EIP,高4个字节中低2位赋值给CS后,ESP=ESP+Iw | 3 |
2.变长指令
Opecode确定后指令长度不确定,Opcode 后紧跟ModR/M
(1)ModRM
当指令中出现内存操作对象的时候,就需要在操作码后面附加一个字节来进行补充说明,这个字节被称为ModR/M,该字节的8个位被分为三部分
其中,Reg/Opcode(第3、4、5位,共3个位)描述指令中的G部分,即寄存器查看下表
Mod(第6、7位)和R/M(第0、1、2位)共同描述指令中的E部分,即寄存器/内存查看下表
(2)RegOpcode (一个字节8位)
Reg/Opcode(第3、4、5位,共3个位)除描述寄存器外还可描述Opcode,也就是描述执行什么命令
80 81 82 83这四个指令
(3)SIB(一个字节8位)
R/M为100,后面会跟着SIB,SIB的作用是解决Mod和R/M无法把偏移量和立即数的格式描述清楚时,类似下图不确定的格式,
SIB的格式