[英]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
(如您的答案所示)的方式更改寄存器的使用可以解决您的问题。
我同意其他用户的意见。
SET_AA
将返回值存储在EAX
寄存器中。 但是,不是返回它,而是返回传递它的参数( EBX
)。 function
没有RET
指令。 您都无需手动实现返回给调用者的代码。 另外,我想指出的是,在function
您将覆盖EBX
寄存器的值,而没有将其保存(在堆栈中)并在最后进行恢复。
您没有指定为function
假设的调用约定。 (由于您甚至不使用RET
或RET(8)
指令,所以我无法猜测它必须是什么)。 但是,根据我所知道的大多数调用约定,覆盖EBX
寄存器而不在最后恢复它是非法的。
可用于功能的寄存器(在大多数约定中)是EAX
, ECX
, EDX
。 所有其他寄存器也可以随意使用,但是必须将其恢复。
除非您使用__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.