如何查看 MASM 指令的结果,例如 PROC、.SETFRAME。 .PUSHREG

How to see result of MASM directives such as PROC, .SETFRAME. .PUSHREG

使用 MASM 编写 x64 汇编代码,我们可以使用这些指令来提供帧展开信息。例如,从 .SETFRAME 定义:

These directives do not generate code; they only generate .xdata and .pdata.

由于这些指令不产生任何代码,我无法在反汇编中看到它们的效果 window。因此,当我使用或不使用这些指令编写汇编函数时,我看不出有什么区别。如何查看这些指令的结果 - 使用 dumpbin 或其他方式?

如何编写可以测试这种展开能力的代码?例如,我故意编写导致异常的汇编代码。我想看看在使用或不使用这些指令编写函数时异常处理行为的区别。

在我的例子中,调用者是用 C++ 编写的,可以使用 try-catch、SSE 等 - 任何与这种情况相关的东西。

最好的办法可能是让您的 asm 函数调用另一个 C++ 函数,并让您的 C++ 函数抛出 C++ 异常。 (不像来自 asm 的非法内存会出错并可能导致 SEH 处理程序。正常的 C++ 异常比 Windows SEH 更简单)。

因此,创建一个 C++ 异常需要通过您的 asm 函数展开堆栈的情况;如果它有效,那么你的堆栈展开元数据指令是正确的。具体来说,try{}catch 在 C++ 调用者中,throw 在 C++ 函数中 call 来自 asm.

我认为那个 thrower 可以是 extern "C" 这样您就可以从 asm 调用它而无需名称修改。或者通过函数指针调用它,或者只查看 MSVC 编译器输出并将损坏的名称复制到 asm.


顺便说一句,此元数据的非Windows(例如GNU/Linux)等价物是创建.eh_frame部分的DWARF .cfi指令。

我不知道 Windows 的等效细节,但我知道它们使用类似的元数据,可以在不依赖 RBP 帧指针的情况下展开堆栈。这让编译器可以优化代码,不会在函数 prologues/epilogues 中浪费 push rbp / mov rbp,rspleave 上的指令,并释放 RBP 用作通用寄存器. (在 32 位代码中更有用,其中除了堆栈指针之外还有 7 个而不是 6 个寄存器比 15 和 14 大得多。)

想法是给定一个 RIP,您可以查找从 RSP 到堆栈上 return 地址的偏移量,以及任何调用保留寄存器的位置。因此,您可以恢复它们并使用该 return 地址继续展开到父级。

回答您的问题:

How can I see the result of these directives - using dumpbin or something else?

您可以使用 dumpbin /UNWINDINFO out.exe 查看因使用 .SETFRAME 而对 .pdata 添加的内容。

输出将如下所示:

00000054 00001530 00001541 000C2070
Unwind version: 1
Unwind flags: None
Size of prologue: 0x04
Count of codes: 2
Frame register: rbp
Frame offset: 0x0
Unwind codes:
  04: SET_FPREG, register=rbp, offset=0x00
  01: PUSH_NONVOL, register=rbp

对输出的一些解释:

  1. 在输出中找到的第二个十六进制数是函数地址00001530
  2. 展开代码表示函数序言中发生的事情。在这个例子中发生的是:
    • RBP 入栈
    • RBP用作帧指针

其他功能可能如下所示:

000000D8 000016D0 0000178A 000C20E4
Unwind version: 1
Unwind flags: EHANDLER UHANDLER
Size of prologue: 0x05
Count of codes: 2
Unwind codes:
  05: ALLOC_SMALL, size=0x20
  01: PUSH_NONVOL, register=rbx
Handler: 000A2A50

这里的一个主要区别是这个函数有一个异常处理程序。这由 Unwind flags: EHANDLER UHANDLERHandler: 000A2A50.

表示