__asm__ gcc调用内存地址

Alb*_*rto 0 c c++ assembly gcc inline-assembly

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

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

我想做类似的事情:

address=VirtualAlloc(NULL,len+1, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
dest=strncpy(address, buf, len);
Run Code Online (Sandbox Code Playgroud)

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

MOV EAX, dest
CALL EAX.
Run Code Online (Sandbox Code Playgroud)

我已经尝试过类似的东西:

  __asm__("movl %eax, dest\n\t"
 "call %eax\n\t");
Run Code Online (Sandbox Code Playgroud)

但这行不通。我该怎么做?

Jes*_*ter 5

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

您确实需要__builtin___clear_cache(buf, buf+len)在将机器代码复制到缓冲区之后使用,然后再取消对其的功能指针引用,否则可以将其优化为无效存储。。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;
}
Run Code Online (Sandbox Code Playgroud)

用GCC9.1编译-O2 -m32

func(char*, int):
    jmp     [DWORD PTR [esp+4]]    # tailcall
Run Code Online (Sandbox Code Playgroud)

此外,你实际上并不需要复制一个字符串,你可以mprotect或者VirtualProtect它在页面使其可执行。但是,如果您要确保它确实在0测试您的shellcode 的第一个字节处停止,那么请确保将其复制。


如果您仍然坚持使用内联汇编,那么您应该知道gcc内联汇编是一件复杂的事情。另外,如果希望函数返回,则应确保它遵循调用约定,特别是它保留应有的寄存器。

因此,AT&T语法实际上是op src, dstmov存储全局符号的地方dest

就是说,这是该问题的措词:

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

说明:https : //gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

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

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

"0" (dest)=输入参数应与输出参数0eax)位于同一位置,应从dest块之前加载

"ecx", "edx" =根据正常的调用约定,告诉编译器这些寄存器可以由asm块更改。

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


请注意,在x86-64系统V(Linux / OS X)中,从这样的内联asm进行函数调用并不安全。无法在RSP下方的红色区域中声明破坏者。