c++ - __asm__ gcc 调用内存地址

标签 c++ c gcc assembly inline-assembly

我有一个分配内存的代码,将一些缓冲区复制到分配的内存,然后跳转到该内存地址。

问题是我无法跳转到内存地址。我正在使用 gcc 和 __asm__ 但我无法调用该内存地址。

我想做这样的事情:

address=VirtualAlloc(NULL,len+1, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
dest=strncpy(address, buf, len);

然后我想在 ASM 中执行此操作:

MOV EAX, dest
CALL EAX.

我试过类似的方法:

  __asm__("movl %eax, dest\n\t"
 "call %eax\n\t");

但它不起作用。 我该怎么做?

最佳答案

通常不需要为此使用 asm,您可以简单地通过一个函数指针,让编译器处理细节。

在将机器代码复制到缓冲区之后,在取消引用指向它的函数指针之前,您确实需要使用 __builtin___clear_cache(buf, buf+len),否则它可能是 optimized away as a dead store. . x86 具有连贯的指令缓存,因此它不会编译成任何额外的指令,但您仍然需要它,以便优化器知道发生了什么。

static inline
int func(char *dest, int len) {
    __builtin___clear_cache(dest, dest+len); // no instructions on x86 but still needed
    int ret = ((int (*)(void))dest)();   // cast to function pointer and deref
    return ret;
}

compiles with GCC9.1 -O2 -m32 to

func(char*, int):
    jmp     [DWORD PTR [esp+4]]    # tailcall

此外,您实际上并不需要复制一个字符串,您可以只mprotectVirtualProtect 它所在的页面即 cocoa 执行。但是如果你想确保它确实在第一个 0 字节处停止以测试你的 shellcode,那么一定要复制它。


如果你仍然坚持内联汇编,你应该知道 gcc 内联汇编是一个复杂的东西。此外,如果您希望函数返回,您应该真正确保它遵循调用约定,特别是它保留了它应该保留的寄存器。

AT&T 语法是 op src, dst 所以你的 mov 实际上是全局符号 dest 的存储。

也就是说,这里是问题的答案:

int ret;
__asm__ __volatile__ ("call *%0" : "=a" (ret) : "0" (dest) : "ecx", "edx", "memory");

解释:https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

call *%0 = %0 指的是第一个替换参数,* 是标准的 gas 间接调用的语法

"=a"(ret) = eax 寄存器中的输出参数应分配给 block 后的变量 ret

"0"(dest) = 输入参数与输出参数 0(即 eax)应从中加载dest block 之前

"ecx", "edx" = 告诉编译器这些寄存器可能会被 asm block 改变,按照正常的调用约定。

"memory" = 告诉编译器 asm block 可能会对内存进行未指定的修改,因此不要缓存任何内容


请注意,在 x86-64 System V (Linux/OS X) 中,像这样从内联 asm 进行函数调用是不安全的。无法在 RSP 下面的红色区域声明 clobber。

关于c++ - __asm__ gcc 调用内存地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22367792/

相关文章:

c - 链表、指针和节点

c - -fstack-usage 对于叶函数是否错误?

python - 错误: command 'gcc' failed with exit status 1 while installing crf++

C++ 重载 getter 两次,一次返回指针,一次返回 const 引用,失败

c++ - 为什么要向这个宏传递三个参数?

c++ - 在 C/C++ 中使用 SDL2_gfx 绘制实心圆

c - 可变参数宏:粘贴 token 的扩展

c++ - glfw在创建之前/创建时实例化窗口位置

c - C中从文件指针获取文件名

c - C 练习文件中的 Fscanf