ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

x64dbg 中 vmovdqu/vmovups/vmovupd 命令详解:YMM/ZMM 寄存器与内存的数据搬运指南

x64dbg 中 vmovdqu/vmovups/vmovupd 命令详解:YMM/ZMM 寄存器与内存的数据搬运指南 x64dbg 中 vmovdqu/vmovups/vmovupd 命令详解YMM/ZMM 寄存器与内存的数据搬运指南【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg本指南围绕 x64dbg 调试器的通用指令类命令vmovdqu/vmovups/vmovupd展开讲解如何在调试会话中直接读写 YMMAVX32 字节与 ZMMAVX-51264 字节寄存器、如何在寄存器与内存地址之间搬运 256/512 位数据并说明其与 XMM 级命令movdqu的差异。读完本文你将掌握该命令的全部语法形态、参数取值规则、底层实现机制含 AVX/AVX-512 上下文读写调用链以及实测用例中的典型用法。命令概述一条命令三种助记符vmovdqu、vmovups、vmovupd在 x64dbg 的命令系统中是同一命令的三个别名全部注册到同一个命令处理函数cbInstrVmovdqu。命令注册位置在 src/dbg/x64dbg.cppdbgcmdnew(movdqu,movups,movupd, cbInstrMovdqu, true); //move from and to XMM register dbgcmdnew(vmovups,vmovupd,vmovdqu, cbInstrVmovdqu, true); //move from and to YMM/ZMM register对应地movdqu/movups/movupd三个别名则统一由cbInstrMovdqu处理负责 XMM16 字节寄存器。也就是说前缀v决定命令操作的位宽层级而后缀dqu/ups/upd只是对齐语法的三种历史写法在本命令中行为完全一致均可直接混用。该命令的本质是数据搬运把源操作数arg2的值赋给目标操作数arg1功能上等价于汇编指令vmovdqu在调试器内部的执行模型。源与目标操作数可以是 YMM/ZMM 寄存器也可以是内存地址当操作数为内存时仅支持[addr]这种方括号语法addr是一个内存地址表达式。命令别名寄存器层级操作数宽度movdqu/movups/movupdXMM16 字节vmovdqu/vmovups/vmovupdYMM32 字节vmovdqu/vmovups/vmovupdZMM64 字节注意与其他多数命令不同本命令的操作数大小是按寄存器层级固定的——YMM 固定 32 字节、ZMM 固定 64 字节而不是随地址表达式推断。要访问 XMM 寄存器请使用 movdqu 命令要访问 ZMM 寄存器则要求宿主机 CPU 与 x64dbg 版本均支持 AVX-512。参数与返回值argumentsarg1目标操作数destination operand。可以是ymm0~ymm31、zmm0~zmm31这类寄存器名也可以是[addr]形式的内存地址表达式。arg2源操作数source operand。同样可以是 YMM/ZMM 寄存器或[addr]形式的内存地址表达式。命令执行后目标操作数会被设置为源操作数的值若目标为内存地址则内存中对应宽度32 或 64 字节的数据被改写。result本命令不会设置任何结果变量如$result等因此不能直接在后续脚本中读取其返回值来判断成败判断是否成功需依赖命令返回的 true/false例如通过脚本条件或插件 API 检查。支持的六种操作数组合从源码实现 src/dbg/commands/cmd-general-purpose.cpp 可以看到命令按内存→寄存器、寄存器→内存、寄存器→寄存器三种方向 × YMM/ZMM 两种层级共分六条分支路径处理1. 内存 → YMM 寄存器读内存到寄存器vmovdqu ymm0, [addr]从地址addr读取 32 字节写入ymm0。源码对应分支cmd-general-purpose.cpp校验寄存器编号registerindex ArchValue(8, 32)x86 下最多 ymm7x64 下最多 ymm31通过valfromstring解析地址表达式用MemRead(address, newValue, sizeof(newValue))读取 32 字节调用GetAVXContext(hActiveThread, context)取得当前活动线程的 AVX 上下文写入后以SetAVXContext回写最后GuiUpdateAllViews()刷新反汇编/内存等视图。2. YMM 寄存器 → 内存写寄存器到内存vmovdqu [addr], ymm0将ymm0的 32 字节写入地址addr。源码分支cmd-general-purpose.cpp先校验MemIsValidReadPtr(address)保证目标地址可访问再通过MemWrite(address, context.YmmRegisters[registerindex], sizeof(ZmmRegister_t))写入。3. YMM 寄存器 → YMM 寄存器寄存器间复制vmovdqu ymm0, ymm1将ymm1的 32 字节复制到ymm0。源码分支cmd-general-purpose.cpp通过一次GetAVXContext 数组赋值context.YmmRegisters[registerindex[1]] context.YmmRegisters[registerindex[0]]SetAVXContext完成。4. 内存 → ZMM 寄存器读内存到寄存器需 AVX-512vmovdqu zmm0, [addr]从地址addr读取 64 字节写入zmm0。源码分支cmd-general-purpose.cpp与 YMM 版本对称区别在于使用ZmmRegister_t newValue64 字节与GetAVX512Context/SetAVX512Context读写 AVX-512 上下文。5. ZMM 寄存器 → 内存写寄存器到内存需 AVX-512vmovdqu [addr], zmm0将zmm0的 64 字节写入地址addr。源码分支cmd-general-purpose.cpp。6. ZMM 寄存器 → ZMM 寄存器寄存器间复制需 AVX-512vmovdqu zmm0, zmm1将zmm1的 64 字节复制到zmm0。源码分支cmd-general-purpose.cpp。命令的完整用法提示当语法不匹配时输出为Usage: vmovdqu zmm0 (or ymm0), [address] / vmovdqu [address], zmm0 (or ymm0) / vmovdqu zmm0 (or ymm0), zmm1 (or ymm1)寄存器编号与架构相关的有效范围寄存器编号的合法性随架构不同而变化源码中统一使用ArchValue(8, 32)进行编译期选择x86 取 8、x64 取 32x8632 位仅支持ymm0~ymm7、zmm0~zmm7AVX/AVX-512 在该架构下最多 8 个向量寄存器x6464 位支持ymm0~ymm31、zmm0~zmm31。超出范围的寄存器名会被判定为InvalidSrc源无效或InvalidDest目标无效并输出对应错误信息Invalid src %s Invalid dest %s底层数据模型XMM/YMM/ZMM 的嵌套结构命令的位宽差异源于 x64dbg 的寄存器数据模型。在 src/dbg/TitanEngine/TitanEngine.h 中XMM、YMM、ZMM 被定义为嵌套结构typedef struct DECLSPEC_ALIGN(16) _XmmRegister_t { ULONGLONG Low; LONGLONG High; } XmmRegister_t; // 16 字节 typedef struct { XmmRegister_t Low; // XMM/SSE part XmmRegister_t High; // AVX part } YmmRegister_t; // 32 字节 typedef struct { YmmRegister_t Low; // AVX part YmmRegister_t High; // AVX-512 part } ZmmRegister_t; // 64 字节即YMM 的高 128 位是 XMM 的扩展AVX 部分ZMM 的高 256 位是 YMM 的扩展AVX-512 部分。这解释了为什么命令对 ZMM 的操作宽度是 64 字节、对 YMM 是 32 字节——它们正好对应上述结构体的大小。上下文结构同样按架构区分TITAN_ENGINE_CONTEXT_t在 x64 下包含YmmRegisters[16]x86 下为YmmRegisters[8]TITAN_ENGINE_CONTEXT_AVX512_t在 x64 下包含ZmmRegisters[32]外加 8 个Opmask掩码寄存器x86 下为ZmmRegisters[8]。这些结构体是 TitanEngine 导出 APIGetAVXContext/SetAVXContext/GetAVX512Context/SetAVX512Context的载体cbInstrVmovdqu正是通过这些 API 读写当前活动线程hActiveThread的扩展寄存器上下文。内存操作的解析细节与失败处理当操作数为内存时命令要求严格的[addr]方括号语法这与 x64dbg 中其他宽操作数命令的约定一致。解析流程为通过首尾字符检查确认方括号包裹剥离方括号后用valfromstring解析地址表达式支持 x64dbg 表达式语法可包含寄存器、算术、函数调用等读方向用MemRead读取若目标地址不可读则输出Failed to read (all) memory...并返回 false写方向先用MemIsValidReadPtr校验地址有效性失败则进入InvalidDest随后用MemWrite写入失败则输出Failed to write to %p每次成功操作后调用GuiUpdateAllViews()让反汇编、内存转储等 GUI 视图立即刷新保证调试器界面所见即所得。相关命令与测试佐证与 movdquXMM 级的分工vmovdqu系列与 movdqu 系列的分工非常明确movdqu只处理 16 字节的 XMM 寄存器xmm0~xmm15x64 下 x86 架构取 8vmovdqu处理 32/64 字节的 YMM/ZMM。两条命令的实现位于同一文件 src/dbg/commands/cmd-general-purpose.cpp函数声明集中在 cmd-general-purpose.h便于对照阅读。测试用例中的典型用法仓库测试 src/tests/issue3803/plugin.cpp 展示了vmovdqu的真实使用场景——插件通过新桥接 API 写入_ZMM0后用命令把寄存器值搬运到内存进行校验if(!_plugin_testassert(DbgValSetBuffer(_ZMM0, zmm.data(), zmm.size()), DbgValSetBuffer failed for _ZMM0)) ... if(!_plugin_testassert(DbgCmdExecDirect(vmovdqu [$result], zmm0), vmovdqu [$result], zmm0 failed)) ... if(!_plugin_testassert(memcmp(written.data(), zmm.data(), sizeof(XMMREGISTER)) 0, ZMM0 low lane mismatch))测试说明src/tests/issue3803/README.md特别强调ZMM 校验通过vmovdqu检查低 128 位low lane这样即使宿主机没有 AVX-512 硬件、只保留 AVX 子集测试依然可靠。这从侧面印证了两个关键事实vmovdqu [addr], zmm0写内存时实际写入的是 ZMM 寄存器的完整 64 字节低 128 位即 XMM 部分必然包含其中在不支持 AVX-512 的机器上ZMM 上下文无法完整读写因此该命令的 ZMM 形态以及movdqu的 XMM 形态需要以兼容方式使用。使用限制与注意事项AVX-512 硬件要求访问 ZMM 寄存器要求宿主机 CPU 与 x64dbg 构建版本均支持 AVX-512否则GetAVX512Context会失败并输出Failed to read register context...。操作数宽度固定vmovdqu的 YMM 形态总是 32 字节、ZMM 形态总是 64 字节不会根据地址表达式自动推断宽度需要更小粒度的搬运请使用movdqu16 字节。仅支持[addr]语法内存操作数必须写成方括号形式且addr需能被地址表达式解析器接受如[401000]、[var:addr]、[rsp0x10]等。不设置结果变量无法通过$result判断命令成败需依赖命令返回值或后续内存/寄存器检查。寄存器范围受限x86 下仅ymm0~ymm7/zmm0~zmm7x64 下最多ymm31/zmm31超出即报Invalid src/dest。【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址: https://gitcode.com/gh_mirrors/x6/x64dbg创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进