我最近一直在尝试通过使用缓冲区和不同汇编运算符的 RAW 十六进制等效项在 C++ 中实现动态函数。为了说明一个简单的跳转:
byte * buffer = new buffer[5];
*buffer = '0xE9'; // Hex for jump
*(uint*)(buffer + 1) = 'address destination';
我在组装方面没有经验,但我知道足以创建非常简单的功能。现在我正在原始内存中创建 cdecl 函数。问题是,我不知道我想用sub
. 我们以这个函数为例:
int MyTest(int x, int y) { return x + y; }
long TheTest(int x, int y)
{
return MyTest(x, 5);
}
08048a20 <_Z6TheTestii>:
_Z6TheTestii():
8048a20: 55 push %ebp
8048a21: 89 e5 mov %esp,%ebp
8048a23: 83 ec 18 sub $0x18,%esp
8048a26: c7 44 24 04 05 00 00 movl $0x5,0x4(%esp)
8048a2d: 00
8048a2e: 8b 45 08 mov 0x8(%ebp),%eax
8048a31: 89 04 24 mov %eax,(%esp)
8048a34: e8 c2 ff ff ff call 80489fb <_Z6MyTestii>
8048a39: c9 leave
8048a3a: c3 ret
如您所见,首先是 C++ 代码,下面是“TheTest”函数的 ASM。可以立即注意到堆栈被推送了 24 (0x18) 个字节(如前所述,我没有使用汇编的经验,因此我可能不会使用正确的术语和/或完全正确)。这对我来说没有任何意义。当只使用 2 个不同的整数时,为什么需要 24 个字节?使用了变量“x”,它是 4 个字节,而值“5”也使用了 4 个字节(记住它是 cdecl,因此调用函数负责处理函数参数的内存)不能弥补 24... .
现在这里有一个额外的例子,它让我对汇编输出感到非常好奇:
int NewTest(int x, char val) { return x + val; }
long TheTest(int x, int y)
{
return NewTest(x, (char)6);
}
08048a3d <_Z6TheTestiiii>:
_Z6TheTestiiii():
8048a3d: 55 push %ebp
8048a3e: 89 e5 mov %esp,%ebp
8048a40: 83 ec 08 sub $0x8,%esp
8048a43: c7 44 24 04 06 00 00 movl $0x6,0x4(%esp)
8048a4a: 00
8048a4b: 8b 45 08 mov 0x8(%ebp),%eax
8048a4e: 89 04 24 mov %eax,(%esp)
8048a51: e8 ca ff ff ff call 8048a20 <_Z7NewTestic>
8048a56: c9 leave
8048a57: c3 ret
这里唯一的区别(除了值)是我使用'char'(1字节)而不是整数。如果我们再看一下汇编代码,这只会将堆栈指针推入 8 个字节。这与前面的示例相差16个字节。作为一个彻头彻尾的 C++ 人,我不知道发生了什么。如果有人能在这个问题上启发我,我将不胜感激!
注意:我在这里发帖而不是阅读 ASM 书籍的原因是因为我需要为这个功能使用汇编。所以我不想为了 40 行代码读一整本书……
编辑:我也不关心平台依赖性,我只关心 Linux 32bit :)