【问题标题】:Call/Ret in x86 assembly embedded in C++在 C++ 中嵌入的 x86 程序集中调用/Ret
【发布时间】:2010-05-16 06:16:41
【问题描述】:

这可能是微不足道的,但由于某种原因我无法正常工作。它应该是一个简单的函数,将 dword 的最后一个字节更改为“AA”(10101010),但是当我调用该函数时没有任何反应。它只是返回我原来的 dword。

__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
             }
}

【问题讨论】:

  • 我的错误。我尝试了 10101010,感到沮丧,然后输入了 0xAA(我以为我以前见过?)。不幸的是,两者都没有工作..
  • (不,不,你很好。我将 'AA' 视为字符串文字,而不是读取您放在后面的二进制文件。)但是(从 16 位时代开始处理器,因为我试图读取程序集),看起来你在返回时抨击 eax 中的值而不是使用它(即你用指向 [esp+8] 的指针覆盖它)而且你似乎没有用 ebx 做任何事情,即使你按下并弹出它。不过,我可能完全错了(就像我说的,已经有一段时间了)。

标签: c++ assembly embedded x86


【解决方案1】:

您似乎对返回值和输出变量感到困惑。

这里:

push ebx
call SET_AA
pop ebx

你的行为就像ebx 是一个输出变量。

这里:

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

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

如果您希望它成为一个输出变量,您需要执行以下操作:

__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
    }
}

【讨论】:

    【解决方案2】:

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

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

    您的SET_AA()很好。它将结果留在eax。 (你可以不用 prolog/epilog,因为你是从 _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
        }
    }
    

    【讨论】:

      【解决方案3】:

      您的函数SET_AA 修改了EAX 中的值(并且仅在寄存器中),但是在您call SET_AA 之后,您在第一个函数中将另一个值移动到EAX,从而覆盖了@ 987654325@电话。因此,以不覆盖EAX(如您的回答中所示)的方式更改您对寄存器的使用可以解决您的问题。

      【讨论】:

        【解决方案4】:

        我同意其他用户的说法。

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

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

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

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

        【讨论】:

        • 确实指定了调用约定:naked。而且我不知道你从哪里想出“非法”。除了偶尔出现的illegal operation之外,没有什么是“非法”的。
        • "naked" 是 NOT 调用约定。这是编译器不生成标准函数 prolog/epilog 代码的指令。约定是调用者和被调用者之间的“协议”。并且提供的代码不符合任何有效的协议。实际上这段代码不返回它肯定会导致未定义的行为。
        【解决方案5】:

        为什么不直接用 C++ 编写函数呢?它愉快地提供了位操作。

        【讨论】:

        • 想必这是一个简化的例子来说明问题。
        • 如果 OP 有更广泛的问题,......那么我的答案也完全可以扩展。他在汇编程序中可能需要哪些 C++ 中不可用的操作?
        • 我确信他只是在自学组装,这是一个非常合理的项目,这是一个非常合理的起点。
        【解决方案6】:

        除非您使用 __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
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2022-01-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-08
          • 1970-01-01
          • 1970-01-01
          • 2017-08-20
          相关资源
          最近更新 更多