如何在 x86 程序集中按值传递结构

Posted

技术标签:

【中文标题】如何在 x86 程序集中按值传递结构【英文标题】:How to pass a struct by value in x86 assembly 【发布时间】:2019-06-07 16:22:11 【问题描述】:

我正在尝试从 masm 中的 windows api 调用一个函数。

这是签名:

BOOL WINAPI SetConsoleScreenBufferSize(
  _In_ HANDLE hConsoleOutput,
  _In_ COORD  dwSize
);

COORD 结构 dwSize 是按值传递的,但是当我尝试调用它时,函数失败了。

看起来像这样:

                .DATA
dwSize          COORD   <20, 20>
                .CODE
                INVOKE  SetConsoleScreenBufferSize,
                        hConsoleOutput,
                        dwSize

这会导致类型错误并且程序无法汇编。如果我传递对结构的引用,程序会汇编,但函数不起作用。我尝试过其他按值接受结构的函数,但没有成功。

【问题讨论】:

INVOKE 宏不知道如何按值传递结构,它是 C 语言约定。如何正确地做到这一点最好通过查看 C 编译器生成的反汇编来完成。应该产生像 MOV eax, dword ptr [dwSize] 这样的东西,现在你可以使用 eax 是第三个 INVOKE 参数。 【参考方案1】:

Hans 是正确的Invoke 不明白如何按值传递结构。 COORD 是 2 个 16 位值,恰好是 DWORD 的大小。在 COORD 的情况下,您可以将其转换为 DWORD 作为 Invoke 的参数。这应该有效:

                .DATA
dwSize          COORD   <20, 20>
                .CODE
                INVOKE  SetConsoleScreenBufferSize,
                        hConsoleOutput,
                        DWORD PTR [dwSize]

注意:重要的是要理解,由于 COORD 恰好是一个 DWORD 的大小,我们可以侥幸逃脱。对于没有大小可以直接压入堆栈的结构,您必须在堆栈上构建结构并使用CALL 指令而不是Invoke

【讨论】:

【参考方案2】:

COORD 只是将两个 16 位数字打包在一起并作为普通 32 位数字传递。

MSVC (x86) 转

COORD cord =  0x666, 0x42 ;
SetConsoleScreenBufferSize(0, cord);

进入

33db            xor     ebx,ebx

66c745986606    mov     word ptr [ebp-68h],666h    ; store cord.x
66c7459a4200    mov     word ptr [ebp-66h],42h     ; store cord.y

ff7598          push    dword ptr [ebp-68h]        ; push the whole struct
53              push    ebx                        ; push 0
ff1540104000    call    dword ptr [image00400000+0x1040 (00401040)] ; SetConsoleScreenBufferSize

push'ing 之后但在call 之前,堆栈以:

00000000 00420666 ...

对寄存器进行异或归零,然后将其推送为错过的优化与立即为零的push 0。首先存储到堆栈也只是因为源代码是在禁用优化的情况下编译的。

【讨论】:

那是禁用优化。打开优化会导致编译器将单个 32 位值 (00420666H) 加载到堆栈内存中。 @IInspectable 这可能取决于该值在编译时是否已知以及调用后您对 COORD 结构的操作。 它不依赖于 value 在编译时是否已知。重要的是结构布局,它在编译时已知的。

以上是关于如何在 x86 程序集中按值传递结构的主要内容,如果未能解决你的问题,请参考以下文章

按值传递和返回结构(带有数组成员)

按值传递的参数在 Visual Studio 2015 x64 调试器中显示不正确?

使用 JNA 在 Clojure 中按值获取和传递结构

为啥不允许将数组按值传递给 C 和 C++ 中的函数?

结构是“按值传递”吗?

我应该如何按值传递数组? Clone() 不气馁吗?