使用C++与程序集在运行时分配和创建新函数

Ell*_*ink 7 c++ gcc inline-assembly att dynamic-function

我一直在研究一个(C++)项目,它需要完全动态分配的函数,这意味着malloc/new和mprotect,然后手动修改缓冲区到汇编代码.因此我完全想知道,我的这个"缓冲区"需要什么,因为它是任何其他_cdecl函数的复制品.例如:

int ImAcDeclFunc(int a, int b)
{
     return a + b;
}
Run Code Online (Sandbox Code Playgroud)

如果我想从字面上创建这个函数的副本,但是完全动态,那需要什么(并记住它是带有内联汇编的C++ )?对于初学者,我想我必须做这样的事情(或类似的解决方案):

// My main....
byte * ImAcDeclFunc = new byte[memory];
mprotect(Align(ImAcDeclFunc), pageSize, PROT_EXEC | PROT_READ | PROT_WRITE);
Run Code Online (Sandbox Code Playgroud)

在此之后我将不得不找出汇编代码ImAcDeclFunc(int a, int b);.现在我在组装时仍然很糟糕,那么这个函数将如何用于AT&T语法?这是我的大胆尝试:

push %ebp
movl %%ebp, %%esp
movl 8(%ebp), %%eax
movl 12(%ebp), %%edx
addl edx, eax
pop ebp
ret
Run Code Online (Sandbox Code Playgroud)

现在如果这段代码是正确的(我非常怀疑,请纠正我)我只需要在十六进制中找到这个代码的值(例如,'jmp'是0xE9而'inc'是0xFE),并直接使用这些值C++?如果我继续以前的C++代码:

*ImAcDeclFunc = 'hex value for push'; // This is 'push' from the first line
*(uint)(ImAcDeclFunc + 1) = 'address to push'; // This is %ebp from the first line
*(ImAcDeclFunc + 5) = 'hex value for movl' // This is movl from the second line
// and so on...
Run Code Online (Sandbox Code Playgroud)

在我为整个代码/缓冲区完成此操作之后,这对于一个完全动态的_cdecl函数来说是否已足够(即我可以将其转换为函数指针并执行int result = ((int (*)(int, int))ImAcDeclFunc)(firstArg, secondArg)吗?).而且我对使用boost :: function或类似的东西不感兴趣,我需要函数完全动态,因此我的兴趣:)

注意:这个问题是我之前的问题的延续,但更具体.

Mik*_*wan 6

如果你这样做lala.c:

int ImAcDeclFunc(int a, int b)
{
    return a + b;
}

int main(void)
{
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

你可以编译它gcc -Wall lala.c -o lala.然后,您可以用objdump -Dslx lala >> lala.txt.反汇编可执行文件.你会发现ImAcDeclFunc组装到:

00000000004004c4 <ImAcDeclFunc>:
ImAcDeclFunc():
  4004c4:   55                      push   %rbp
  4004c5:   48 89 e5                mov    %rsp,%rbp
  4004c8:   89 7d fc                mov    %edi,-0x4(%rbp)
  4004cb:   89 75 f8                mov    %esi,-0x8(%rbp)
  4004ce:   8b 45 f8                mov    -0x8(%rbp),%eax
  4004d1:   8b 55 fc                mov    -0x4(%rbp),%edx
  4004d4:   8d 04 02                lea    (%rdx,%rax,1),%eax
  4004d7:   c9                      leaveq 
  4004d8:   c3                      retq   
Run Code Online (Sandbox Code Playgroud)

实际上这个功能相对容易复制到其他地方.在这种情况下,你完全正确地说你可以复制字节,它只会工作.

当您开始使用将相对偏移作为操作码的一部分的指令时,将出现问题.例如,相对跳转或相对呼叫.在这些情况下,您需要正确地重新定位指令,除非您碰巧能够将其复制到与最初相同的地址.

简而言之,要重新定位,您需要找到它最初所在的位置,并计算您将基于它的位置的差异,并重新定位每个相对指令的偏移量.这本身是可行的.您真正的困难是处理对其他函数的调用,特别是对库的函数调用.在这种情况下,您需要保证链接库,然后以您定位的可执行格式定义的方式调用它.这非常重要.如果您仍然感兴趣,我可以指出您应该阅读的方向.


在上面的简单案例中,您可以这样做:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <sys/mman.h>
#include <unistd.h>

int main(void)
{
    char func[] = {0x55, 0x48, 0x89, 0xe5, 0x89, 0x7d, 0xfc,
    0x89, 0x75, 0xf8, 0x8b, 0x45, 0xf8,
    0x8b, 0x55, 0xfc, 0x8d, 0x04, 0x02,
    0xc9, 0xc3};

    int (* func_copy)(int,int) = mmap(NULL, sizeof(func),
        PROT_WRITE | PROT_READ | PROT_EXEC,
        MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);

    memcpy(func_copy, func, sizeof(func));
    printf("1 + 2 = %d\n", func_copy(1,2));

    munmap(func_copy, sizeof(func));
    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

这在x86-64上工作正常.它打印:

1 + 2 = 3
Run Code Online (Sandbox Code Playgroud)

  • 如果您能为我提供一个可行的示例,我将非常感激。这对我来说就是纯金!关于亲戚的电话,据我所知是这样的;`targetAddress - currentAddress -/+ 任何偏移量`?关于“处理库调用”,如果我只调用成员函数,会有什么问题吗?由于我使用 GCC,它_完全_类似于 cdecl 调用,但带有一个附加指针(“this”指针)。或者,如果我随后使用成员函数中的 _stdcall 调用库函数,会产生问题吗?即dynamic_func-&gt;member_func-&gt;library_func? (2认同)