繁体   English   中英

嵌入在C ++中的x86程序集中的Call / Ret

[英]Call/Ret in x86 assembly embedded in C++

这可能是微不足道的,但是由于某些原因我无法正常工作。 它应该是一个简单的函数,它将双字的最后一个字节更改为'AA'(10101010),但是当我调用该函数时什么也没有发生。 它只是返回我原来的双字。

__declspec(naked) long
    function(unsigned long inputDWord, unsigned long *outputDWord)
    {
      _asm{
        mov ebx, dword ptr[esp+4]

  push ebx
  call SET_AA
  pop ebx

  mov eax, dword ptr[esp+8]
  mov dword ptr[eax], ebx
       }
}

__declspec(naked) unsigned long 

SET_AA( unsigned long inputDWord )

{

       __asm{
          mov eax, [esp+4]
                mov al, 0xAA //10101010 didn't work either 
                ret
             }
}

您似乎对返回值和包含out变量感到困惑。

这里:

push ebx
call SET_AA
pop ebx

您的行为就像ebx是out变量。

和这里:

mov eax, [esp+4]
mov al, 0xAA //10101010 didn't work either 
ret

您只需将东西写入eax两次(使用参数一次,然后使用0xAA 覆盖它)。 传统上, eax是返回值寄存器。 您需要选择要使用的那个。

如果要使其成为out变量,则需要执行以下操作:

__declspec(naked) long function(unsigned long inputDWord, unsigned long *outputDWord) {
    _asm{
        mov ebx, dword ptr[esp+4]

        push ebx
        call SET_AA
        pop ebx

        mov eax, dword ptr[esp+8]
        mov dword ptr[eax], ebx
    }
}

__declspec(naked) void SET_AA( unsigned long inputDWord ) {
    __asm{
        mov [esp+4], 0xAA // put 0xAA into the variable passed on the stack
        ret
    }
}

如果要返回值,可以执行以下操作:

__declspec(naked) long function(unsigned long inputDWord, unsigned long *outputDWord) {
    _asm{
        mov ebx, dword ptr[esp+4]

        call SET_AA
        mov ebx, eax

        mov eax, dword ptr[esp+8]
        mov dword ptr[eax], ebx
    }
}

__declspec(naked) unsigned long SET_AA(/* input param not needed, we are just returning a value */) {
    __asm{
        mov eax, 0xAA // return 0xAA via the eax register
        ret
    }
}

我认为这更是您的意思。 重要的一件事:正如MSDN所说,

裸函数必须提供自己的序言和结尾

您的SET_AA()很好。 结果留在eax (您可以不使用序言/结尾,因为您是从_asm而不是C调用它的。)

__declspec(naked) unsigned long 
SET_AA(unsigned long inputDWord )
{
    __asm
    {
        mov eax, [esp+4]
        mov al, 0xAA
        ret               // final value is in eax
    }
}

function()应该返回void ,因为您需要*outputDWord的结果。 另外,您也可以使用inputDWord代替[esp + 4]:

__declspec(naked) void
function(unsigned long inputDWord, unsigned long *outputDWord)
{
    _asm
    {
    // you need a prolog/epilog to make C happy
    // here's the prolog:
    push ebp
    mov ebp, esp

    mov ebx, inputDWord    // the value you're going to change
    mov ecx, outputDWord   // address of where to put the result

    push ebx
    call SET_AA // puts the result in eax
    pop ebx

    // copy the result to the thing ecx points to (*outputDWord)
    mov [ecx], eax

    // epilog to keep C happy
    pop ebp
    ret
    }
}

您的函数SET_AA修改了EAX的值(仅在寄存器中),但是在call SET_AA ,您将在第一个函数中将另一个值移到EAX中,从而覆盖了SET_AA调用中的结果。 因此,以不覆盖EAX (如您的答案所示)的方式更改寄存器的使用可以解决您的问题。

我同意其他用户的意见。

  1. SET_AA将返回值存储在EAX寄存器中。 但是,不是返回它,而是返回传递它的参数( EBX )。
  2. 您的function没有RET指令。 您都无需手动实现返回给调用者的代码。

另外,我想指出的是,在function您将覆盖EBX寄存器的值,而没有将其保存(在堆栈中)并在最后进行恢复。

您没有指定为function假设的调用约定。 (由于您甚至不使用RETRET(8)指令,所以我无法猜测它必须是什么)。 但是,根据我所知道的大多数调用约定,覆盖EBX寄存器而不在最后恢复它是非法的。

可用于功能的寄存器(在大多数约定中)是EAXECXEDX 所有其他寄存器也可以随意使用,但是必须将其恢复。

除非您使用__cdecl作为调用对流,否则两个函数都将缺少堆栈清理,这将在返回展开堆栈帧以及未正确保留寄存器的事实时引起问题。

使用一些更加结构化,清晰和简洁的内容:

__declspec(naked) unsigned long __stdcall SET_AA(unsigned long inputDWord )
{
    __asm
    {
        mov eax, [esp+4]
        mov al, 0xAA
        retn 4
    }
}

__declspec(naked) void __fastcall function(unsigned long inputDWord, unsigned long *outputDWord)
{
    _asm
    {
    push ecx //push inputDWord
    call SET_AA // puts the result in eax

    // copy the result to the thing ecx points to (*outputDWord)
    mov [edx], eax
    retn//fastcall has no cleaup for the first 2 args
    }
}

为什么不只用C ++编写函数呢? 它很高兴提供位操作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM