x64dbg 表达式系统完全指南:从命令栏计算器到源码级求值原理

x64dbg 表达式系统完全指南:从命令栏计算器到源码级求值原理 逆向工程调试器开发工具应用安全【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址https://gitcode.com/gh_mirrors/x6/x64dbg点击查看免费下载x64dbg 内置了一套功能强大的表达式求值引擎它不仅是命令栏与计算器Help - Calculator的算力底座更是条件断点、条件追踪、脚本自动化等高级功能的通用输入语言。本文以官方文档 Expressions.md 为主体结合 Values.md、Expression-functions.md 以及 src/dbg/expressionparser.cpp 等核心源码完整讲解表达式语法、运算符优先级、快速赋值、值类型、表达式函数库与底层解析原理帮助你写出正确、高效、可复用的调试表达式。表达式在 x64dbg 中的角色x64dbg 允许在几乎所有接受参数的命令中使用表达式既支持纯数值计算也支持使用类 C 语法对寄存器、标志、变量和内存进行赋值。这意味着你可以在命令栏直接输入12或[401000]4进行即时计算通过Help - Calculator菜单打开计算器窗口可视地试验各类表达式在条件断点中编写eip401000 [esp]0这样的复杂条件在脚本或TraceSetCondition等追踪指令中复用同一套表达式语法。从源码结构看表达式求值由 src/dbg/expressionparser.cpp 中的ExpressionParser类完成入口为Calculate而单个值数字、寄存器、内存、变量的解析则集中在 src/dbg/value.cpp 的valfromstring/valfromstring_noexpr系列函数中二者共同构成完整的求值链路。数值格式默认十六进制十进制需加点x64dbg 表达式最需要牢记的一条规则是表达式中的所有数字默认按十六进制解释需要使用十进制时必须在数字前加一个点例如.123。具体规则如下与 Values.md 一致写法含义示例123十六进制0x1231230x1230x123/x123显式十六进制前缀结果相同0x123、x123等价.123十进制123即十六进制0x7B.1237B该行为在源码中有明确体现src/dbg/value.cpp 中的ishexnumber()明确接受0x或x前缀而valfromstring_noexpr_isvar()src/dbg/value.cpp会先尝试按十进制判断isdecnumber再按十六进制判断ishexnumber由于普通无前缀数字总能通过十六进制检查因此默认按十六进制解析。对应的解析函数convertNumber()分别以基数 10 和 16 调用strtoullsrc/dbg/value.cpp。运算符13 级优先级与结合性表达式支持下列运算符按优先级从高到低依次求值。下表是文档 Expressions.md 的完整清单同时补充了源码中定义的结合性见 src/dbg/expressionparser.cpp 的Token::precedence()与Token::associativity()优先级类别运算符说明1括号/方括号(12)、[16]优先于其他所有运算方括号同时表示内存解引用2一元运算-1、~1、!0一元负号、按位取反、逻辑非右结合3乘除2*3、23、6/3、5%3乘法、乘法高位、除法、取模4加减13、5-2加法、减法5移位/旋转12、101、12、12左移、右移、循环左移、循环右移6大小比较410、36、12、67结果为 1真或 0假7相等比较11、2!6结果为 1真或 0假8按位与122—9按位异或2^1—10按位或2|8—11逻辑与03结果为 1真或 0假12逻辑或0||3结果为 1真或 0假13逻辑蕴含0-1a-b等价于!a || b结果为 1真或 0假几个值得注意的细节反引号是乘法高位运算符23返回两数相乘结果的高位部分。源码 src/dbg/expressionparser.cpp 中该运算在 64 位下调用_umul128/_mul12832 位下则用 64 位中间积右移 32 位实现。与是循环移位分别对应_rotl64/_rotr6464 位与_rotl/_rotr32 位旋转位数按位宽取模src/dbg/expressionparser.cpp。逻辑蕴含-的实现为!op1 || op2src/dbg/expressionparser.cpp。运算符按有符号/无符号两套路径求值ExpressionParser::Calculate接受signedcalc参数分别路由到signedOperation/unsignedOperationsrc/dbg/expressionparser.cpp。括号不匹配时解析器不会直接报错而是自动补齐缺失的右括号fixClosingBracketssrc/dbg/expressionparser.cpp。快速赋值类 C 语法直接改内存、寄存器、变量与标志表达式不仅用于计算还允许直接修改调试目标的状态语法与 C 语言高度一致Expressions.md 的 Quick-Assigning 一节a?b其中?可以是任意非逻辑运算符a可以是任意寄存器、标志、变量或内存位置b是任意合法表达式。例如eax4、[401000]123、myvar2、zf1。a/a--自增/自减a同样可以是寄存器、标志、变量或内存位置。赋值运算符在源码中是独立的 Token 类型OperatorAssign*系列、-、*、/、%、、、、、、^、|、以及前缀/后缀自增自减见 src/dbg/expressionparser.h 的 Token 枚举。其优先级统一为 14最低结合性为右结合与 C 语言一致。赋值目标必须是可写的值。源码handleAssignment()src/dbg/expressionparser.cpp的逻辑是若目标不是变量、寄存器、标志或内存位置即本身是一个常量值则拒绝赋值如果目标是一个尚不存在的名称则会自动新建一个用户变量varnew(variable, resultv, VAR_USER)。这也是文档 Variables.md 中myvar 1234这类写法的底层原理——等号左边既可以是寄存器也可以直接声明新变量。表达式中的值类型从变量到模块数据表达式能识别的值类型非常丰富完整列表见 Values.md理解它们是把表达式用于实战的前提。变量变量可以带$前缀myvar与$myvar等价只能存储一个 DWORDx64 下为 QWORD。变量分三类USER用户通过var/mov命令或赋值表达式创建无访问限制SYSTEMx64dbg 内部创建可读写但不可删除READONLYx64dbg 内部创建只读。常用保留变量Variables.md 的 Reserved Variables 一节包括$res/$result、$res1~$res4、$pid、$hp/$hProcess、$lastalloc、$breakpointcondition、$breakpointcounter、$breakpointlogcondition等。寄存器除浮点寄存器XMM/YMM/ZMM、K0、ST(0) 等外所有整数寄存器如RAX、EAX、AL都可用作变量。浮点寄存器不能用于标量赋值只能通过movdqu、vmovdqu、kmovd命令访问见 movdqu、vmovdqu、kmovd或以字符串格式化中的浮点类型输出。两个特殊说明x87 控制字标志的名称为_x87CW_UMx64dbg 额外提供架构无关寄存器CAX、CBX、CCX、CDX、CSP、CBP、CSI、CDI、CIP在 32 位平台映射到 EAX 等 32 位寄存器在 64 位平台映射到 RAX 等 64 位寄存器。例如CIP在 x86 下是EIP在 x64 下是RIP方便编写跨架构通用的脚本与条件。标志调试标志可作为整数输入写法为下划线加标志名_cf、_pf、_af、_zf、_sf、_tf、_if、_df、_of、_rf、_vm、_ac、_vif、_vip、_id。对应的读写逻辑在 src/dbg/value.cpp 的setflag/getregister等函数中实现。内存位置使用方括号语法读写内存语法含义[addr]从addr读取一个 DWORDx64 为 QWORDn:[addr]从addr读取n个字节seg:[addr]按段基址从addr读取 DWORD/QWORDbyte:[addr]读取 1 字节word:[addr]读取 2 字节dword:[addr]读取 4 字节qword:[addr]读取 8 字节仅 x64其中n在 x32 下最大为 4、x64 下最大为 8超出会报错seg可选gs、es、cs、fs、ds、ss但只有fs和gs实际生效。源码中fs/gs的段偏移分别对应 TEB 定位src/dbg/value.cpp读取大小前缀n:、byte:、word:、dword:、qword:的解析见 src/dbg/value.cpp。注意解引用无效地址会导致错误这在条件断点或脚本中可能引发问题。此时应改用ReadByte(addr)等表达式函数见下文Byte/Word/Dword/Qword/Ptr一节它们在地址无效时返回 0 而不是报错。标签与符号用户自定义标签和符号是合法的表达式直接解析为该标签/符号的地址。模块数据表达式支持直接引用模块符号、导出函数、基址与偏移这是逆向中最常用的能力之一Values.md 的 Module Data 一节DLL 导出直接输入导出名即可自动解析为实际地址也可显式指定模块写法为module.dll:api或module:api.与:等价ntdll.dll:ZwContinue ntdll:memcmp ntdll.memcmp // 与上面等价 ntdll:1D // 序号 0x1D :myexport // 当前 CPU 视图中选中模块的导出 myexport转发导出默认会解析到最终地址若想阻止转发把:换成?kernel32:EnterCriticalSection // 解析为 ntdll:RtlEnterCriticalSection kernel32?EnterCriticalSection // 解析为 kernel32 中的原始导出模块基址直接写module、module:0、module:base、module:imagebase或module:header即可获得已加载模块的基址。RVA 与文件偏移// 文件偏移 0x400 ntdll.dll:#400 :#400 // RVA 0x1000 ntdll.dll:$1000 :$1000其中module rva与module:$rva等价module:#offset将文件偏移转换为 VA模块名为空时如:$123使用 CPU 视图中当前选中的模块。模块入口点module:entry、module:oep或module:ep返回模块入口地址注意若存在名为entry/oep/ep的导出则返回导出地址。表达式函数内置函数库全览函数可以出现在表达式中的任何位置其返回值可作为其他运算的操作数。完整文档见 Expression-functions.md。按类别整理如下。字符串函数说明utf8(addr[, count])从addr读取以 NUL 结尾的 UTF-8 字符串count可截断长度utf16(addr[, count])读取 UTF-16 字符串ansi(addr[, count])读取 ANSI 字符串strstr(str1, str2)查找子串如strstr(utf8(addr), abc)stristr(str1, str2)不区分大小写的子串查找streq(str1, str2)字符串比较如streq(utf8(addr), abc)strieq(str1, str2)不区分大小写的字符串比较strlen(str)字符串长度ansi/utf8/utf16的返回值可作为其他接受str参数的函数的输入地址不可读时返回空字符串若希望读取失败直接报错可使用.strict变体如utf8.strict(rax)。此外字符串值不是数字因此不能用作追踪条件等要求数值结果的场景。GUI 交互disasm.sel()/dis.sel()反汇编视图选中的地址dump.sel()转储视图选中的地址stack.sel()栈视图选中的地址。源码信息src.disp(addr)addr相对最后一条源码行的位移src.line(addr)addr对应的源码行号。模块函数说明mod.party(addr)模块类型编号0为用户模块1为系统模块mod.base(addr)模块基址mod.size(addr)模块大小mod.hash(addr)模块哈希mod.entry(addr)模块入口地址mod.system(addr)是否为系统模块mod.user(addr)是否为用户模块mod.main()主模块被调试程序基址若是 DLL 则加载前返回 0mod.rva(addr)addr在模块内的 RVA不在模块内返回 0mod.offset(addr)addr在模块内的文件偏移不在模块内返回 0mod.isexport(addr)addr是否为模块的导出函数mod.fromname(str)按名称取模块基址未找到返回 0如mod.fromname(ntdll.dll)进程信息peb()PEB 地址teb()TEB 地址tid()当前线程 IDkusd()/KUSD()/KUSER_SHARED_DATA()KUSER_SHARED_DATA的地址0x7FFE0000。通用工具bswap(value)字节交换如bswap(44332211)0x11223344ternary(condition, val1, val2)条件非零返回val1否则返回val2GetTickCount()Windows API每 1ms 递增的计时器rdtsc()RDTSC指令结果CPU 计时器32 位环境仅返回 32 位结果isdebuggerfocused()x64dbg 是否有焦点是则返回 1isdebuggeefocused()被调试程序是否有焦点是则返回 1。内存mem.valid(addr)addr是否为有效内存地址mem.base(addr)addr所在内存页基址随内存映射视图模式变化mem.size(addr)addr所在内存页大小随内存映射视图模式变化mem.iscode(addr)addr所在页是否可执行mem.decodepointer(ptr)等价于调用DecodePointerAPI仅 Vista 及以上可用。反汇编函数说明dis.len(addr)指令长度dis.iscond(addr)是否为条件分支dis.isbranch(addr)是否为分支jcc/calldis.isret(addr)是否为retdis.iscall(addr)是否为calldis.ismem(addr)是否含内存操作数dis.isnop(addr)是否等价于 NOPdis.isunusual(addr)是否为 unusual 指令dis.branchdest(addr)分支目标地址按回车跳转的地址dis.branchexec(addr)该分支是否会执行dis.imm(addr)指令的立即数dis.brtrue(addr)分支目标地址dis.brfalse(addr)条件分支未跳转时下一条指令地址dis.next(addr)下一条指令地址dis.prev(addr)上一条指令地址dis.iscallsystem(addr)该call是否进入系统模块dis.mnemonic(addr)返回助记符字符串如streq(dis.mnemonic(cip), cpuid)dis.text(addr)返回指令文本字符串可用于条件如strstr(dis.text(rip), rbx)注意文本可能与 GUI 显示略有差异dis.match(addr, str)指令是否匹配正则str如dis.match(rip, test., 0x1)追踪tr.enabled(addr)addr处的 trace coverage 是否启用tr.hitcount(addr)trace coverage 在addr处的命中次数tr.isrecording()/tr.runtraceenabled()trace recording 是否启用。Byte/Word/Dword/Qword/Ptr函数说明ReadByte(addr)/Byte(addr)/byte(addr)读取 1 字节如byte(eax)读取[eax]处一字节ReadWord(addr)/Word(addr)/word(addr)读取 2 字节ReadDword(addr)/Dword(addr)/dword(addr)读取 4 字节ReadQword(addr)/Qword(addr)/qword(addr)读取 8 字节仅 x64ReadPtr(addr)/ReadPointer(addr)/ptr(addr)/Pointer(addr)/pointer(addr)读取一个指针4/8 字节这些函数在addr无效时返回 0不会像方括号解引用那样报错是条件断点与脚本中安全读取内存的首选。函数边界、引用、参数与异常函数func.start()/func.end()返回addr所属函数的起点/终点不在函数内则返回 0引用ref.count()返回当前引用视图条目数ref.addr(index)返回第index条引用的地址失败返回 0参数假定返回地址在栈上即正处于函数内部arg(index)/arg.get(index)获取第index个参数从 0 起arg.set(index, value)设置第index个参数异常ex.firstchance()是否首次机会异常、ex.addr()异常地址、ex.code()异常代码、ex.flags()异常标志、ex.infocount()异常参数个数、ex.info(index)第index个异常参数访问违例或内存断点时ex.info(1)为被访问的内存地址。这些函数常与异常断点配合构造高级条件。系统调用syscall.name(number)返回系统调用号对应的名称字符串syscall.id(name)返回系统调用名称对应的编号。底层原理词法分析、调度场算法与 RPN 求值理解表达式系统的内部工作方式有助于判断什么写法合法、什么写法会产生歧义。核心实现集中在 src/dbg/expressionparser.cpp预处理fixClosingBrackets()统计括号数量缺失的右括号会被自动补全src/dbg/expressionparser.cpp。词法分析tokenize将表达式切分为数据Data、带引号数据QuotedData、数字字面量NumericLiteral、函数名Function与各类运算符 Tokensrc/dbg/expressionparser.cpp。方括号[...]内部的内容整体作为一个 Token 保留通过stateMemory状态跟踪嵌套深度带引号的字符串则通过stateQuote跟踪这保证了[4010004]、text不会被误切分。调度场算法shuntingYard采用 Dijkstra 调度场算法将中缀表达式转为逆波兰RPN后缀序列mPrefixTokenssrc/dbg/expressionparser.cpp同时统计每个函数的实参个数argCount。RPN 求值Calculate用操作数栈依次执行运算遇到函数 Token 时先通过ExpressionFunctions::GetType校验参数个数与类型ValueTypeNumber/ValueTypeString/ValueTypeAny及可选参数再调用ExpressionFunctions::Call执行src/dbg/expressionparser.cpp。参数个数或类型不匹配时返回明确错误信息。值得注意的一个性能优化当表达式是纯数字字面量时Calculate会直接返回 Token 中缓存的数值跳过整个求值管线src/dbg/expressionparser.cpp 的注释明确说明这是为了优化追踪场景的性能。表达式函数的分发中心是 src/dbg/expressionfunctions.h 中的ExpressionFunctions类它维护一张std::unordered_mapString, Function支持注册、别名RegisterAlias、注销与按名调用。实战组合条件断点、脚本与插件扩展把上述语法组合起来就能写出强大的实战表达式。以下场景均以仓库内的官方文档为依据条件断点ConditionalBreakpoint.md断点条件就是一个表达式常用寄存器、内存与函数组合eip ntdll.dll:ZwContinue [esp] 0 byte:[eip] 0xCC streq(dis.mnemonic(cip), ret) cip mod.main()配合保留变量$breakpointcondition与$breakpointcounter后者在条件求值前已被设置为断点命中次数可以写出计数与暂停控制逻辑Variables.md。脚本自动化脚本命令中可直接使用表达式并通过var/mov与赋值表达式读写变量scriptcmd.mdmov myvar, mod.base(ntdll.dll) myvar 0x1000 [myvar] 0条件追踪TraceSetCondition等追踪命令接受表达式例如用dis.match(rip, call)或byte:(eip)0xCC判断是否命中目标指令。注意字符串类函数如utf8(rax)不返回数字不能直接用作追踪条件。插件扩展插件可以通过_plugin_registerexprfunction注册自己的表达式函数让用户在命令栏、条件断点和脚本中直接调用文档见 registerexprfunction.rst相关 API 还包括 _plugin_registerexprfunctionex 与 _plugin_unregisterexprfunction。注册后的函数与内置函数走完全相同的解析、类型检查与调用管线src/dbg/expressionfunctions.h。小结x64dbg 的表达式系统是一套完整的小型编程语言默认十六进制的数值体系、13 级优先级运算符、类 C 赋值语法、丰富的值类型变量/寄存器/标志/内存/标签/模块数据以及超过 80 个内置表达式函数覆盖字符串、模块、反汇编、内存、异常、系统调用等调试核心领域。从源码看它由ExpressionParser词法分析 调度场 RPN 求值与valfromstring系列单值解析两层构成并通过ExpressionFunctions支持插件扩展。掌握这套语法是高效使用条件断点、条件追踪与脚本自动化的关键。赞分享逆向工程调试器开发工具应用安全【免费下载链接】x64dbgAn open-source user mode debugger for Windows. Optimized for reverse engineering and malware analysis.项目地址https://gitcode.com/gh_mirrors/x6/x64dbg点击查看免费下载相关推荐x64dbg 的 xor 命令详解按位异或运算、^ 快速赋值与表达式运算原理x64dbg 的 xor 命令详解按位异或运算、 ^ 快速赋值与表达式运算原理 xor 是 x64dbg 调试器“通用运算General Purpose逆向工程调试器开发工具应用安全OI-wiki 表达式求值完全指南从表达式树到逆波兰表达式与运算符优先级解析OI wiki 表达式求值完全指南从表达式树到逆波兰表达式与运算符优先级解析 导读 本文基于 OI wiki 的 expression.md https://文档知识库教育教程x64dbg 帮助Help菜单完全指南从表达式计算器到崩溃转储生成x64dbg 帮助Help菜单完全指南从表达式计算器到崩溃转储生成 导读 x64dbg 是面向 Windows 的开源用户态调试器专为逆向工程与恶意软件逆向工程调试器开发工具应用安全上一篇CefSharp搜索功能完整指南快速实现网页内容查找下一篇SqueezeDet视频检测实战如何构建实时交通目标识别系统创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考