Pregunta

I have a code that allocates memory, copies some buffer to that allocated memory and then it jumps to that memory address.

the problem is that I cant jump to the memory address. Im using gcc and __asm__ but I cant call that memory address.

I want to do something like:

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

And then I want to do this in ASM:

MOV EAX, dest
CALL EAX.

I've tried something like:

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

But it does not work. How can I do it?

¿Fue útil?

Solución

There is usually no need to use asm for this, you can simply go through a function pointer and let the compiler take care of the details.

You do need to use __builtin___clear_cache(buf, buf+len) after copy machine code to a buffer before you dereference a function-pointer to it, otherwise it can be optimized away as a dead store.. x86 has coherent instruction caches so it doesn't compile to any extra instructions, but you still need it so the optimizer knows what's going on.

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

Also, you don't actually need to copy a string, you can just mprotect or VirtualProtect the page it's in to make it executable. But if you want to make sure it does stop at the first 0 byte to test your shellcode, then sure copy it.


If you nevertheless insist on inline asm, you should know that gcc inline asm is a complex thing. Also, if you expect the function to return, you should really make sure it follows the calling convention, in particular it preserves the registers it should.

AT&T syntax is op src, dst so your mov was actually a store to the global symbol dest.

That said, here is the answer to the question as worded:

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

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

call *%0 = the %0 refers to the first substitued argument, the * is standard gas syntax for indirect call

"=a" (ret) = output argument in eax register should be assigned to variable ret after the block

"0" (dest) = input argument in the same place as output argument 0 (which is eax) should be loaded from dest before the block

"ecx", "edx" = tell the compiler these registers may be altered by the asm block, as per normal calling convention.

"memory" = tell the compiler the asm block might make unspecified modifications to memory, so don't cache anything


Note that in x86-64 System V (Linux / OS X), it's not safe to make a function call from inline asm like this. There's no way to declare a clobber on the red zone below RSP.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top