鱼C论坛

 找回密码
 立即注册
查看: 155|回复: 6

[已解决]问下大佬,这条指令为啥是8B00而不是008B?

[复制链接]
发表于 4 天前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x

000.jpg


不是说高地址对应高位字节数据,低地址对应地位字节数据么
最佳答案
4 天前
在 x86 平台上,指令编码(opcode + ModR/M + …)与 多字节数据的内存存放方式(大小端问题) 是两个不同的概念!

为何会有“8B 00”而不是“00 8B”?

以 mov ax, [bx] 类似的指令为例,它会被汇编器和链接器组合成对应的机器码字节流:

  • 第一个字节(较低地址)是 opcode,例如 8B。
  • 第二个字节(紧随其后地址)是 ModR/M、SIB 或者紧跟的立即数等字段,这里是 00。

CPU 取指时,会先从内存的最低地址抓取第一个字节 8B,再从紧邻的下一地址抓取第二个字节 00,拼成完整的机器指令“8B 00”并执行。


大小端与“指令读取”无必然联系

x86 上的大小端(Little-Endian)主要体现在“多字节数据(比如 16 位、32 位、64 位整数)的存储顺序”上:即“低位字节”放在内存的低地址,“高位字节”放在内存的高地址。

指令则是由一个个 1 字节 为最小单位的编码序列(opcode + 若干附加字节)组成;CPU 取指时,就按“内存地址递增”的顺序一次取 1 个字节来解码。

也就是说,指令并不是一个需要做‘大小端翻转’的多字节整数,它更像是一串顺次排列的字节序列,CPU 会按顺序抓取和解释每个字节,从而确定完整的指令含义。

换言之,8B 00 并不是说“8B 是高位字节,00 是低位字节”,而是说:

  • 第一个字节(内存低地址)= 0x8B (opcode)
  • 第二个字节(紧邻地址) = 0x00 (ModR/M 或其他)

如果写成 00 8B,CPU 取到的第一个字节就会变成 0x00,第二个字节才是 0x8B,那么这在指令解码上就完全变了味,要么不合法,要么含义不是你想要的 mov ax, [bx]。


指令编码与大小端的区别

大小端 (Endianness)

对于例如 0x1234(16 位)的数据,在 x86(小端)里会被拆成两个字节存储:

  1. 地址 + 0: 0x34  (低位字节)
  2. 地址 + 1: 0x12  (高位字节)
复制代码

这是存放多字节“数据”时的约定。

若读取这两个字节把它当作一个 16 位数,小端模式下 CPU 最终解释为 0x1234。

指令编码 (Instruction Encoding)
对于一条指令来说,每一字节都有特定含义:

  • 第一个字节常常是 opcode,
  • 第二个字节可能是 ModR/M、或者紧跟的立即数、或者段寄存器选择等字段,
  • 第三或第四字节可能是偏移量、立即数的剩余部分……

CPU 取指的过程是:地址从低到高依次读入这几字节,拼凑出“opcode + 附加字段”,从而解析出指令类型、操作数、地址模式等信息。

它跟大小端仅在“指令里如果有多字节立即数字段”时才会有交互关系(比如指令中的立即数 0x1234 也会被拆成 34 12 存在连续地址里),但并不影响 opcode 这一层的排布顺序。

为何有时看似“高位在前”又有时“低位在前”?

当你把内存里的那几个字节当作“机器指令”去看时,往往是从内存低地址到高地址按 人类阅读习惯 直接列出来,比如说:

  1. 2000h: 8B
  2. 2001h: 00
复制代码

于是我们说指令是 “8B 00”。

当你把内存里的那几个字节当作“一个多字节数”去看时,x86 小端就会是 “低字节在先,高字节在后”。

所以如果一个 16 位数是 0x1234,放在内存就是 [0x34, 0x12];读到寄存器里就恢复成 0x1234。

这两者本来就服从不同的解释规则,也就不会彼此矛盾。


指令流是一串字节,一个字节接一个字节地存在内存,从 低地址 到 高地址 依次排列。

CPU 取指时也是这样顺序读取,把第一个字节识别成 opcode、第二个字节识别成 ModR/M 或立即数等。

大小端处理的是“把一个多字节数据(如 16 位、32 位、64 位整数)如何分拆到若干字节,并存放到连续的内存地址中”的问题。

因此,“8B 00” 并不违背“小端存储高位在高地址、低位在低地址”的原则;

它只是说明指令的第一个字节(opcode)正好是 0x8B,紧跟的第二个字节是 0x00,两者在汇编→机器码的阶段就被这么顺序写入了内存。

若反过来写成 “00 8B”,它早已不是你想要的指令了。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 4 天前 | 显示全部楼层
关于字节序问题的解答

这个问题涉及 小端序(Little-Endian) 的存储特性。在 x86/x64 架构中:

  • 内存数据存储规则:低地址存低位字节,高地址存高位字节
  • 指令编码观察:当我们看到机器码 8B 00 时,实际表示的是 0x008B(小端序解析)


举个具体例子:
假设指令 MOV AX, [BX+SI] 的机器码是 8B 00

  1. 内存地址 | 内容
  2. 0x1000  | 8B  ← 操作码(opcode)
  3. 0x1001  | 00  ← ModR/M 字节(表示寻址方式)
复制代码


关键点:
1. 只有 多字节数据(如 int、指针)才需要考虑字节序
2. 操作码(如 8B)和 ModR/M 字节(如 00)是 独立单字节,不存在字节序问题
3. 如果是立即数(如 MOV AX, 0x8B00),则会存储为 00 8B

推荐用调试器(如 GDB/OD)观察内存实际存储:

  1. (gdb) x/2xb 0x1000  ← 查看两个字节
  2. 0x1000: 0x8b 0x00  ← 实际存储顺序
复制代码


理解这个特性对逆向工程和调试非常重要!(^_-)☆

以上回复来自 FishC AI 小助理,如未能正确解答您的问题,请继续追问。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 4 天前 | 显示全部楼层    本楼为最佳答案   
在 x86 平台上,指令编码(opcode + ModR/M + …)与 多字节数据的内存存放方式(大小端问题) 是两个不同的概念!

为何会有“8B 00”而不是“00 8B”?

以 mov ax, [bx] 类似的指令为例,它会被汇编器和链接器组合成对应的机器码字节流:

  • 第一个字节(较低地址)是 opcode,例如 8B。
  • 第二个字节(紧随其后地址)是 ModR/M、SIB 或者紧跟的立即数等字段,这里是 00。

CPU 取指时,会先从内存的最低地址抓取第一个字节 8B,再从紧邻的下一地址抓取第二个字节 00,拼成完整的机器指令“8B 00”并执行。


大小端与“指令读取”无必然联系

x86 上的大小端(Little-Endian)主要体现在“多字节数据(比如 16 位、32 位、64 位整数)的存储顺序”上:即“低位字节”放在内存的低地址,“高位字节”放在内存的高地址。

指令则是由一个个 1 字节 为最小单位的编码序列(opcode + 若干附加字节)组成;CPU 取指时,就按“内存地址递增”的顺序一次取 1 个字节来解码。

也就是说,指令并不是一个需要做‘大小端翻转’的多字节整数,它更像是一串顺次排列的字节序列,CPU 会按顺序抓取和解释每个字节,从而确定完整的指令含义。

换言之,8B 00 并不是说“8B 是高位字节,00 是低位字节”,而是说:

  • 第一个字节(内存低地址)= 0x8B (opcode)
  • 第二个字节(紧邻地址) = 0x00 (ModR/M 或其他)

如果写成 00 8B,CPU 取到的第一个字节就会变成 0x00,第二个字节才是 0x8B,那么这在指令解码上就完全变了味,要么不合法,要么含义不是你想要的 mov ax, [bx]。


指令编码与大小端的区别

大小端 (Endianness)

对于例如 0x1234(16 位)的数据,在 x86(小端)里会被拆成两个字节存储:

  1. 地址 + 0: 0x34  (低位字节)
  2. 地址 + 1: 0x12  (高位字节)
复制代码

这是存放多字节“数据”时的约定。

若读取这两个字节把它当作一个 16 位数,小端模式下 CPU 最终解释为 0x1234。

指令编码 (Instruction Encoding)
对于一条指令来说,每一字节都有特定含义:

  • 第一个字节常常是 opcode,
  • 第二个字节可能是 ModR/M、或者紧跟的立即数、或者段寄存器选择等字段,
  • 第三或第四字节可能是偏移量、立即数的剩余部分……

CPU 取指的过程是:地址从低到高依次读入这几字节,拼凑出“opcode + 附加字段”,从而解析出指令类型、操作数、地址模式等信息。

它跟大小端仅在“指令里如果有多字节立即数字段”时才会有交互关系(比如指令中的立即数 0x1234 也会被拆成 34 12 存在连续地址里),但并不影响 opcode 这一层的排布顺序。

为何有时看似“高位在前”又有时“低位在前”?

当你把内存里的那几个字节当作“机器指令”去看时,往往是从内存低地址到高地址按 人类阅读习惯 直接列出来,比如说:

  1. 2000h: 8B
  2. 2001h: 00
复制代码

于是我们说指令是 “8B 00”。

当你把内存里的那几个字节当作“一个多字节数”去看时,x86 小端就会是 “低字节在先,高字节在后”。

所以如果一个 16 位数是 0x1234,放在内存就是 [0x34, 0x12];读到寄存器里就恢复成 0x1234。

这两者本来就服从不同的解释规则,也就不会彼此矛盾。


指令流是一串字节,一个字节接一个字节地存在内存,从 低地址 到 高地址 依次排列。

CPU 取指时也是这样顺序读取,把第一个字节识别成 opcode、第二个字节识别成 ModR/M 或立即数等。

大小端处理的是“把一个多字节数据(如 16 位、32 位、64 位整数)如何分拆到若干字节,并存放到连续的内存地址中”的问题。

因此,“8B 00” 并不违背“小端存储高位在高地址、低位在低地址”的原则;

它只是说明指令的第一个字节(opcode)正好是 0x8B,紧跟的第二个字节是 0x00,两者在汇编→机器码的阶段就被这么顺序写入了内存。

若反过来写成 “00 8B”,它早已不是你想要的指令了。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 4 天前 | 显示全部楼层
不二如是 发表于 2025-4-15 18:55
在 x86 平台上,指令编码(opcode + ModR/M + …)与 多字节数据的内存存放方式(大小端问题) 是两个不同 ...

哇哦,大佬牛犇
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 4 天前 | 显示全部楼层
不二如是 发表于 2025-4-15 18:55
在 x86 平台上,指令编码(opcode + ModR/M + …)与 多字节数据的内存存放方式(大小端问题) 是两个不同 ...

大佬再问下,CPU是怎么知道自己在地址值20016中要读取的指令数据只有2个字节的,而不会少读或多读1个字节的数据?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 4 天前 | 显示全部楼层
竹逸 发表于 2025-4-15 20:08
大佬再问下,CPU是怎么知道自己在地址值20016中要读取的指令数据只有2个字节的,而不会少读或多读1个字节 ...

CPU 并不是事先“知道”在某个地址处正好只有 2 个字节构成一条完整指令,而是通过内建的指令解码逻辑来逐字节判断指令的边界和长度~

内建过程有点复杂,解释起来文字有点多,看这个例子能理解吗

假设在地址 20016 处的指令被编码为 2 个字节:

  • 第一字节(8B):确定了操作为 MOV 并指示需要一个附加的 ModR/M 字节来指定操作数。
  • 第二字节(00):作为 ModR/M 字节,完整地表达了操作数的寻址方式。

解码器读到第一个字节后,知道接下来的字节在指令格式中属于必需的部分,于是再读取第二个字节,构成完整的一条指令。

此后,指令指针自动跳转到下一个指令起始地址,而不会再将下一个字节和当前指令混在一起。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 4 天前 | 显示全部楼层
不二如是 发表于 2025-4-15 21:18
CPU 并不是事先“知道”在某个地址处正好只有 2 个字节构成一条完整指令,而是通过内建的指令解码逻辑来 ...

懂了,谢谢大佬的解答
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-19 10:54

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表