这是一个如此小的程序,因此可以在不将其转换回 C 的情况下对其进行跟踪。让我们分解一下当它被调用时会发生什么asm(0xb6, 0xc6)。
push ebp
mov ebp, esp
这两行就是所谓的函数序言。我们首先保存调用函数堆栈帧(ebp正在跟踪),在第二个中,我们将函数堆栈帧设置为等于当前堆栈位置。
mov eax, DWORD PTR [ebp+0x8]
mov ebx, DWORD PTR [ebp+0xc]
上面几行将我们传递的参数加载到eax和ebx。由于 incdecl参数以相反的顺序通过堆栈传递,因此在 in 中的那些行之后,eax我们将具有0xb6并且ebx将等于0xc6。
mov eax, ebx
from 的值ebx存储在 中eax,因此我们从前一个片段中删除了第一行的需要。此外,由于这是eax此代码中最后一次使用 ,因此可以将其解释为返回值,因为这也是cdecl. 因此,在这种情况下,返回值将是0xc6.
mov esp,ebp
pop ebp
这只是将堆栈恢复为我们进入函数时的状态 - 也称为函数结尾。
ret
并返回给调用者。
经过分析,很明显这个函数返回了传递给它的第二个参数。
int second(int a, int b)
{
return b;
}
您可以将此代码编译为库并从 C 代码中使用它:
#include <stdio.h>
extern unsigned int _test (unsigned int, unsigned int);
int main(void)
{
printf("%x\n", _test(0xb6, 0xc6));
return 0;
}
并用gcc -m32 -o run asm.o call.o.
./运行
c6
您可以通过例如使用godbolt来验证它是否是这种情况,但由于将第一个值分配给eax然后用第二个值替换它的代码实际上不需要 - 它可以从一开始就使用第二个参数 - 它赢了'即使没有任何优化也不会生成。