【问题标题】:Restoring the stack causes an access violation恢复堆栈会导致访问冲突
【发布时间】:2012-01-12 15:18:59
【问题描述】:

我正在使用 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

    }
}

【问题讨论】:

    标签: assembly x86 stack inline-assembly calling-convention


    【解决方案1】:
            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”,这是代码生成器也发出的内在函数。它将调用您的函数而不是标准函数。这会很糟糕,标准的签名有很大的不同。选择另一个名称以避免这种几乎不可能调试的事故。

    【讨论】:

    • 好的,但是 MSVC 现在吐出“围绕变量 'szText' 的堆栈已损坏。”哦,我将函数重命名为 SetMemory。
    • 是的,这是您代码中的另一个错误。您正在编写 6 个字节,而不是 3 个字节。使用 mov [edi],dl,因此您复制 1 个字节而不是 4 个字节。请注意,用 C 语言编写此函数不仅可以使您的代码更可靠,而且速度也会快得多。否则,这看起来就像 memset() 的功能等价物。
    • 嗯,好的。我会尝试用 C 语言编写它。谢谢。
    【解决方案2】:

    使用ret 4 代替add esp, 4 / ret

    【讨论】:

      猜你喜欢
      • 2012-11-08
      • 2010-11-05
      • 1970-01-01
      • 1970-01-01
      • 2020-05-24
      • 2015-08-29
      • 2012-05-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多