参考资料:
本文大量内容抄袭看雪作者:waiWH的VMP系列
1、名称:X86指令内幕 —— 序
网址:X86指令内幕 —— 序_晓风残月的技术天地-CSDN博客
学习目的:
1、VMP代码自带反汇编引擎,需要一定的Opcode指令基础。
正文:
1、快速入门Opcode
Intel-64和IA-32架构指令编码是图2-1所示格式的子集.一条指令包括可选的指令前缀(顺序任意),主操作码(最多3字节),由ModR/M和SIB字节(可选) 组成的地址格式描述符(如果需要的话),偏移量(可选)以及立即数(可选).
总结:
1、根据上图所示我们知道一条汇编指令分为以下几个部分:
1、Legacy Prefix(可选)
2、REX prefix(用于64位模式,这里不做讲解)
3、Opcode(必须有)
4、ModRM(可选)
5、SIB(可选)
6、Displacement(可选)
7、Immediate(可选)
2、Opcode结构顺序是不能被打乱的
2、 指令前缀讲解(可选)
1、基础理论知识
指令前缀分为四组,每一组包含一些允许的前缀码.对于任何指令,前缀可以从这四组(组1,2,3,4)里的挑选,并且它们不区分次序.
• 组1
— 锁定和重复前缀:
• F0H - LOCK
• F2H - REPNE/REPNZ,仅用于串操作和I/O指令,也可被用作某些指令的强制性前缀
• F3H - REP或REPE/REPZ,仅用于串操作和I/O指令,也可被用作某些指令的强制性前缀
• 组2
— 段重载前缀:
• 2EH—CS 段重载(用于任意分支指令时保留)
• 36H—SS 段重载(用于任意分支指令时保留)
• 3EH—DS 段重载(用于任意分支指令时保留)
• 26H—ES 段重载(用于任意分支指令时保留)
• 64H—FS 段重载(用于任意分支指令时保留)
• 65H—GS 段重载(用于任意分支指令时保留)
— 分支提示:
• 2EH—分支不被接受(仅用于Jcc指令中)
• 3EH—分支被接受(仅用于Jcc指令中)
• 组3
• 66H—操作数大小重载前缀,也可被用作某些指令的强制性前缀.
• 组4
• 67H—地址尺寸重载前缀
LOCK前缀(F0H)在多处理器环境下强制执行独占共享内存操作.详见《Instruction Set Reference, A-M》第三章"LOCK – 断言LOCK#信号前缀".
重复前缀(F2H,F3H)将会重复操作字符串的每一个元素.只有MOVS,CMPS,SCAS,LODS,STOS,INS,OUTS等字符串操作或I/O指令才能使用这些前缀. 对Intel 64 或 IA-32 其他指令使用重复前缀和/或未定义的操作码是被保留的,将会引起不可预知的行为.
某些指令可能使用F2H,F3H作为强制性前缀来表示特定的功能.强制性前缀应当位于其他可选的前缀之后(例外的情形请查看第2.2.1节,”REX前缀”)
分支提示前缀(2EH,3EH)允许程序给处理器一个最有可能的执行分支提示.这些前缀只能用于条件指令(Jcc).在Intel 64 或 IA-32 其他指令中使用分支预测前缀或者未定义的操作码是被保留的,将引起不可预知的行为.
操作数大小重载前缀允许程序在16位和32位操作数大小间切换.它们中任一个都可以是默认值,而使用这个前缀则选择非默认值.
某些SSE2/SSE3/SSSE3/SSE4和使用3字节操作码的指令可能使用66H作为强制性前缀来表示特定的功能. 强制性前缀应当位于其他可选的前缀之后(例外的情形请查看第2.2.1节,”REX前缀”) . 66H前缀的其他用法是被保留的, 将引起不可预知的行为.
地址尺寸重载前缀(67H)允许程序在16位和32位地址间切换.它们中的任何一个都可以是默认值,使用这个前缀选择非默认值.当指令中的操作数不在内存中,使用这个前缀或未定义的操作码时,操作被保留,可能引起不可预知的行为.
3、 OPcode详解(必备)
主操作码,必须有。指令不定长1、2、3都有,例如8B C0 mov eax,eax 其中8B就是主操作码。例如90 NOP就是只有主操作码没有ModR/M和SIB字节的。
4、ModR/M 和 SIB 字节(可选)
1、基础知识(非常重要的字节)
许多涉及内存操作数的指令都有一个紧挨着主操作码的寻址格式说明字节(叫做ModR/M字节),ModR/M字节包含3个域信息:
• mod域与r/m域组成32个可能的值:8个寄存器和24个寻址模式.
• reg/opcode域确定寄存器号或者附加的3位操作码.reg/opcode域的用途由主操作码确定.
• r/m域确定一个寄存器为操作数或者和mod域一起编码寻址模式.有时候有些指令使用特定的mod域和r/m域组合来表示操作码信息.
某些ModR/M字节编码需要第二寻址字节(SIB).基址+索引或者比例+索引形式的32位寻址需要SIB字节.SIB字节包括下列域:
• scale 域指定比例因子.
• index域指定索引寄存器号.
• base 域指定基址寄存器号.
ModR/M和SIB编码详见第2.1.5节.
总结:
1、SIB就是对ModR/M的补充辅助。这些都是可选的
5、 针对前面内容进行实践
0、 参考书籍
1、查ModRM、SIB表使用的是:参考文档:Intel开发者手册 第二卷 指令集手册 第2章
2、查OPcode使用的是:参考文档:IntelCPU机器指令中文版手册
1、举例说明
26:C784C8 44332211 78563412 MOV DWORD PTR ES:[EAX+ECX*8+0x11223344],0x12345678
2、指令拆解
1、Legacy Prefix(可选) | 26H—ES 段重载(用于任意分支指令时保留) |
2、Opcode(必须有) | C7 |
3、ModRM(可选) | 84 |
4、SIB(可选) | C8 |
5、Displacement(可选) | 44332211 |
6、Immediate(可选) | 78563412 |
3、解析Opcode找到主操作码(查表)
得到的是 MOV R/M32,IMM32
4、分析ModRM(查表)
ModRM结构如下:
转换成二进制如下:
84=10 000 100
结构 | 描述 | 内容 |
ModRM.mod | 寻址模式 | 10 |
ModRM.reg | 寄存器 | 000 |
ModRM.r/m | 寄存器或则地址 | 100 |
注:
1. “[--][--]”记号表示ModR/M 后跟随有一个SIB字节.
2. “disp32”记号表示ModR/M(或者SIB,如果出现的话) 后跟随一个32位的偏移量,该偏移量被加至有效地址.
3. “disp8” 记号表示ModR/M(或者SIB,如果出现的话) 后跟随一个8位的偏移量,该偏移量将被符号扩展,然后被加至有效地址.
表2-3囊括了SIB 的256个可能值(十六进制形式) . 可以作为基的通用寄存器通过表的上部列出,也列出了相应的base域值. 表的主体的每行列出了索引(index SIB的3,4,5位)对应的寄存器及倍率因子(scaling factor SIBbyte的6,7位).
总结:
1、ModRM.mod 提供寻址模式: 11 = register(寄存器) 11 != memory(地址)
2、R/M = 100并且mod!=11 的时候表示存在SIB表
5、分析SIB(查表)
根据上文找到的地址发现是[--][--]”,表示有SIB表
SIB结构如下:
转换成2进制如下:
C8=11 001 000
结构 | 描述 | 内容 |
SIB.scale | 提供 index 寄存器乘数因子 scale | 11 |
SIB.index | 提供 index 寄存器寻址 | 001 |
SIB.base | 提供 base 寄存器寻址 | 000 |
注:
1. “[*]”记号表示:若MOD = 00B表示没有基,且带有一个32位的偏移量;否则表示disp8或disp32 + [EBP].即提供如下的寻址方式:
MOD 有效地址
00 [scaled index] + disp32
01 [scaled index] + disp8 + [EBP]
10 [scaled index] + disp32 + [EBP]
44332211 - Displacement:此为小端模式,即为 0x11223344
78563412 - Immediate:小端模式,即为 0x12345678
综上,得到:
MOV DWORD PTR ES:[EAX+ECX*8+0x11223344],0x12345678