You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++中动态获取内存中指令的操作数(x64进程)

解析x64指令长度与操作数的实操方法

要解决读取外部x64进程指令、确定操作数数量(比如MOV的双操作数)及指令长度的问题,核心是拆解x64的变长指令结构——x86/x64指令长度从1到15字节不等,必须逐段解析才能确定总长度和操作数。

一、先搞懂x64指令的核心结构

x64指令由以下按顺序排列的可选/必选部分组成:

  • 前缀字节(最多4个):含REX前缀(0x40-0x4F,扩展寄存器/操作数大小)、段前缀、操作数/地址大小前缀
  • 主操作码(1-3字节):MOV的基础操作码比如0x8B(内存/寄存器→寄存器)、0x89(寄存器→内存/寄存器)、0xA1(内存→EAX/RAX)等
  • ModR/M字节(可选):多数双操作数指令需要它来指定操作数的寻址模式(寄存器/内存)
  • SIB字节(可选):当ModR/M的R/M字段指定复杂内存寻址时出现,用于细化寻址方式
  • 位移量(0/1/2/4/8字节):内存寻址时的偏移值,长度由ModR/M的Mod字段决定
  • 立即数(0/1/2/4/8字节):指令中直接给出的数值,长度由操作码和REX前缀决定

二、MOV指令的操作数判定

MOV在x64中几乎都是双操作数(源→目的),少数看似单操作数的变种实际隐含了操作数:

  • 比如0xA1 m32:表面是单操作数,但实际是MOV EAX, m32,隐含EAX作为目的操作数,本质还是双操作数
  • 通过操作码+ModR/M确认:ModR/M的Reg字段对应一个操作数,R/M字段对应另一个(寄存器或内存地址),以此确定双操作数结构

三、确定指令长度的分步流程

不能一次性读取固定字节数,必须按需逐字节解析:

  1. 读取前缀字节
    逐个读取字节,判断是否为前缀(REX、段前缀、操作数/地址大小前缀),最多读4个,直到遇到非前缀的操作码字节。
  2. 读取完整操作码
    操作码可能是1-3字节:
    • 单字节:如0x8B、0x89
    • 双字节:以0x0F开头(如MOVZX的0x0F 0xB7)
    • 三字节:以0x0F 0x38或0x0F 0x3A开头
      读取到完整操作码后,判断是否需要ModR/M字节。
  3. 解析ModR/M与SIB字节
    若操作码要求ModR/M(多数MOV变种都需要),读取1字节的ModR/M:
    • 若ModR/M的R/M字段为0x04(32位)或0x05(64位),则需额外读取1字节的SIB。
  4. 读取位移量
    根据ModR/M的Mod字段确定位移长度:
    • Mod=00(无位移,仅当R/M指向内存):无需读取
    • Mod=01:1字节位移
    • Mod=10:8字节位移(x64地址模式下)
    • Mod=11:寄存器模式,无位移
      读取对应长度的位移字节。
  5. 读取立即数
    根据操作码和REX前缀确定立即数长度:
    • 比如带REX.W前缀的0x48 0xB8(MOV RAX, imm64),需要读取8字节立即数。

四、实操建议

  • 优先针对MOV的常见变种做适配:
    • MOV r64, r64:0x48 0x89 + ModR/M(1字节),总长度3字节
    • MOV r64, m64:0x48 0x8B + ModR/M + 可选SIB + 位移,长度3-10字节
    • MOV m64, r64:0x48 0x89 + ModR/M + 可选SIB + 位移,长度3-10字节
    • MOV r64, imm64:0x48 0xB8 + 8字节立即数,总长度10字节
  • 参考Intel指令集手册建立操作码与结构的映射,避免硬编码遗漏变种
  • 读取外部进程内存时,每次读取后要校验地址有效性,防止访问违规

内容的提问来源于stack exchange,提问作者user24684540

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 17:28:35