Hex-Rays 不能正确显示字符串
这里的问题不是字符串,而是 Hex-Rays 没有猜到sub_4022E0. 请注意,反汇编列表将值移动到调用之前ecx和edx之前,而该调用的反编译仅显示一个参数?Hex-Rays 认为只有一个参数——甚至可能只有一个堆栈参数——而实际上有两个寄存器参数而没有堆栈参数(即 32 位 MSVC __fastcall)。
解决此问题的简单方法是通过反编译器进入该函数(将光标放在该函数上sub_4022E0并按 Enter),然后在反编译后,点击ESC返回调用函数并按下F5以重新反编译。这可能会导致 Hex-Rays 正确猜测原型,此时它将显示函数的两个参数。
或者,如果没有,您可以Y将光标放在 上sub_4022E0,然后将原型更改为void __fastcall sub_4022E0(void *, const char *)。
编辑:我想我可能会添加一些关于 Hex-Rays 内部的细节来阐明这种行为的来源。
IDA/Hex-Rays 的运作方式与 Ghidra 不同。分析 Ghidra 中的二进制文件涉及对每个函数进行反编译,您可以在状态栏中看到它反复更新“反编译 FUN_123456”。这样做时,它执行分析以确定函数期望在其整个生命周期中访问哪些寄存器和堆栈位置(即函数的原型/调用约定)。它从调用图的叶子开始直到根——也就是说,它从不调用其他函数的函数开始,然后分析只调用那些函数的函数,依此类推。因此,在分析任何给定函数时,对于每个函数调用,它已经知道哪些寄存器和堆栈位置应该被视为这些调用的参数。
在普通的交互操作中,IDA 和 Hex-Rays 的工作方式与 Ghidra 不同。当您将二进制文件加载到 IDA 中时,它只会根据反汇编列表执行正常的自动分析。这意味着当您第一次反编译一个函数时,Hex-Rays 不知道被调用函数的原型,除非由于类型库或损坏的导入符号而提供了该信息。因此,Hex-Rays 必须猜测哪些寄存器和堆栈位置应该被视为被调用函数的参数——而且,正如您的问题所表明的那样,它并不总是正确地做到这一点。这是所谓的“呼叫分析”的一部分,如果您曾经收到过“呼叫分析失败”,您可能对它很熟悉。
在反编译一个函数时,Hex-Rays 还会猜测被分析函数的原型,这与我在上面描述 Ghidra 的方式类似。“从外部”(在调用站点)猜测的原型,如前一段所述,以及“来自内部”,如最后一句所述,存储在类型信息的全局缓存中。对于任何给定的函数,“从内部”收集的函数原型总是优先于“从外部”收集的函数原型,因为它们更可靠。
所以,在这个问题中发生的事情是:
- 用户反编译了一个函数,该函数调用了以前从未见过的其他函数。
- 因为 Hex-Rays 没有这些函数的原型信息,它猜测了它们的原型(错误地)。
- 结果,函数调用的反编译显示了一个参数而不是两个。
- 我建议用户按下
ENTER该函数,以便对其进行反编译并让 Hex-Rays 获得有关其原型的更好信息。然后将原型信息存储在全局类型缓存中。 - 回溯到调用函数并刷新后,Hex-Rays 现在不必“从外部”猜测参数。它从全局类型缓存中检索原型,因此它知道函数调用在
ecx和 中使用了两个参数edx。它使用该信息正确反编译函数调用的参数。 - 我的替代建议是直接提供被调用函数的原型。用户提供的类型信息优先于全局类型缓存中任何形式的猜测类型信息。
如果您想模拟 Ghidra 的自动分析功能,您可以使用File->Produce file->Create .c file. 这将“从叶子到根”反编译函数,并在此过程中一次性使用整个二进制文件的最佳信息填充全局类型缓存。但是请注意,这个过程并不是特别快——毕竟,缺乏这种分析是 IDA 的自动分析比 Ghidra 更快的一个主要部分。
尝试取消选中 Edit-> Plugins -> Hex-Rays Decompiler -> Print Only constant string literals
