恢复堆栈会导致访问冲突

发布于 2024-12-19 22:42:43 字数 1630 浏览 1 评论 0原文

我正在使用 ASM 编写本质上相当于 memset() 的内容。我编写的代码可以工作,但当我尝试恢复堆栈时,它会因访问冲突而崩溃。我已经使用内联汇编将代码放入 MSVC 中,因此我可以对其进行调试。

当函数返回时就会出现问题。但是,当我取出 add esp, 4 行时,代码执行正确,但在 main() 函数返回后,MSVC 表示变量周围的堆栈已损坏。

我不愿意在没有 add esp, 4 的情况下继续,因为我知道这会在以后引起问题。

我该如何解决这个问题?

int main(int argc, char **argv)
{
    char szText[3];


    /*__asm
    {
        push 3
        mov edx, 65
        lea ecx, szText
        call memset
    }*/

    memset((void*)&szText, 'A', 3);

    return 42;
}

void __declspec(naked) __fastcall memset(void *pDest, int iValue, int iSize)
{
    __asm
    {
        ; Assume the pointer to the memory is stored in ECX
        ; Assume the value is stored in EDX
        ; Assume the size of the block is stored on the stack

            mov eax, esi        ; Put ESI somewhere it won't be touched (I think)

            mov esi, ecx        ; Move the address of the memory into ESI
            xor ecx, ecx        ; Zero ECX

            mov ecx, [esp+4]    ; Get the size of the block into ECX. ECX is our loop counter

        memset_count:
            cmp ecx, 0          ; If we are at the end of the block,
            jz memset_return    ; Jump to return

            mov [esi], edx      ; Move our value into the memory
            inc esi             ; Otherwise, increment out position in the memory
            dec ecx             ; Decrement out counter
            jmp memset_count    ; Start again

        memset_return:
            mov esi, eax        ; Restore ESI
            add esp, 4          ; Clean up the stack
            ret

    }
}

I am writing essentially the equivalent of memset() using ASM. The code I have written works, except that when I try to restore the stack, it crashes with an access violation. I have put the code into MSVC using inline assembly, so I can debug it.

The problem occurs when the function returns. However, when I take the add esp, 4 line out, the code executes correctly, but after the main() function has returned, MSVC says that the stack around a variable was corrupted.

I am reluctant to continue without the add esp, 4 as I know this will cause problems later.

How would I go about fixing this?

int main(int argc, char **argv)
{
    char szText[3];


    /*__asm
    {
        push 3
        mov edx, 65
        lea ecx, szText
        call memset
    }*/

    memset((void*)&szText, 'A', 3);

    return 42;
}

void __declspec(naked) __fastcall memset(void *pDest, int iValue, int iSize)
{
    __asm
    {
        ; Assume the pointer to the memory is stored in ECX
        ; Assume the value is stored in EDX
        ; Assume the size of the block is stored on the stack

            mov eax, esi        ; Put ESI somewhere it won't be touched (I think)

            mov esi, ecx        ; Move the address of the memory into ESI
            xor ecx, ecx        ; Zero ECX

            mov ecx, [esp+4]    ; Get the size of the block into ECX. ECX is our loop counter

        memset_count:
            cmp ecx, 0          ; If we are at the end of the block,
            jz memset_return    ; Jump to return

            mov [esi], edx      ; Move our value into the memory
            inc esi             ; Otherwise, increment out position in the memory
            dec ecx             ; Decrement out counter
            jmp memset_count    ; Start again

        memset_return:
            mov esi, eax        ; Restore ESI
            add esp, 4          ; Clean up the stack
            ret

    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

冷弦 2024-12-26 22:42:43
        memset_return:
            mov esi, eax        ; Restore ESI
            add esp, 4          ; Clean up the stack
            ret

那是错误的,你没有在函数体中从 ESP 中减去 4。实际上,您将跳过返回地址,并且 RET 从堆栈中弹出参数并跳转到其值。轰隆隆。修复:

        memset_return:
            mov esi, eax        ; Restore ESI
            ret 4

您还必须编写函数原型,以便编译器知道该函数的调用约定是非标准的。如果它不知道,它将在调用站点生成错误的代码。将其粘贴在 main 方法之前:

 void  __fastcall memset(void *pDest, int iValue, int iSize);

并避免将其命名为“memset”,这是一个内部函数,也是由代码生成器发出的。它将调用您的函数而不是标准函数。这会很糟糕,标准的有一个非常不同的签名。选择另一个名称以避免这种几乎不可能调试的事故。

        memset_return:
            mov esi, eax        ; Restore ESI
            add esp, 4          ; Clean up the stack
            ret

That's wrong, you didn't subtract 4 from ESP in the function body. You'll actually skip the return address and RET pops the argument from the stack and jumps to its value. Kaboom. Fix:

        memset_return:
            mov esi, eax        ; Restore ESI
            ret 4

You will also have to write a function prototype so that the compiler knows that the calling convention for the function is non-standard. It will generate the wrong code at the call site if it doesn't know. Paste this before the main method:

 void  __fastcall memset(void *pDest, int iValue, int iSize);

And avoid naming it "memset", that's an intrinsic function that's also emitted by the code generator. It will call your function instead of the standard one. Which will go badly, the standard one has a very different signature. Pick another name to avoid this almost impossible to debug mishap.

半步萧音过轻尘 2024-12-26 22:42:43

使用 ret 4 而不是 add esp, 4 / ret

Use ret 4 instead of add esp, 4 / ret

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文