How to get c code to execute hex bytecode?

I want a simple C method to be able to run hexadecimal bytecode on a 64bit Linux machine. Here is the C program that I have:

char code[] = "\x48\x31\xc0"; #include <stdio.h> int main(int argc, char **argv) { int (*func) (); func = (int (*)()) code; (int)(*func)(); printf("%s\n","DONE"); } 

The code I'm trying to run ( "\x48\x31\xc0" ) I got by writing this simple build program (it shouldn't do anything)

 .text .globl _start _start: xorq %rax, %rax 

and then compiles and objdump-ing to get the bytecode.

However, when I run my program in C, I get a segmentation error. Any ideas?

+8
source share
6 answers

You need a page containing machine code in order to have permission to execute. X86-64 page tables have a separate bit for execution, separate from read permissions, unlike legacy 386 page tables.

Here is a simple example:

 #include <stdio.h> #include <string.h> #include <sys/mman.h> int main () { char code[] = { 0x8D, 0x04, 0x37, // lea eax,[rdi+rsi] // retval = a+b; 0xC3 // ret }; int (*sum) (int, int) = NULL; // copy code to executable buffer sum = mmap (0,sizeof(code),PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON,-1,0); memcpy (sum, code, sizeof(code)); // doesn't actually flush cache on x86 // but is still necessary so memcpy isn't optimized away as a dead store. __builtin___clear_cache(sum, sum + sizeof(sum)); // GNU C // run code int a = 2; int b = 3; int c = sum (a, b); printf ("%d + %d = %d\n", a, b, c); return 0; } 
+16
source

Your machine code may be right, but your CPU objects.

Modern processors manage memory in segments. During normal operation, the operating system loads a new program into the program-text segment and sets the stack in the data segment. The operating system reports that the CPU never runs code in the data segment. Your code is in code[] , in the data segment. So segfault.

+4
source

You need to include the assembly in the string using a special compiler directive so that it eventually ends up in the code segment. See this guide, for example: http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html

+3
source

This will require some effort.

Your code variable is stored in the .data section of your executable:

 $ readelf -p .data exploit String dump of section '.data': [ 10] H1Γ€ 

H1Γ€ is the value of your variable.

The .data section cannot be executed:

 $ readelf -S exploit There are 30 section headers, starting at offset 0x1150: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [...] [24] .data PROGBITS 0000000000601010 00001010 0000000000000014 0000000000000000 WA 0 0 8 

All 64-bit processors I am familiar with support for non-executable pages initially in pagetables. Most new 32-bit processors (those that support PAE) provide enough space in their pagetables for the operating system to emulate hardware non-executable pages. You need to run either the old OS or the ancient processor in order to get the .data section marked as executable.

Since these are just flags of the executable file, you must set the X flag through some other mechanism, but I do not know how to do this. And your OS may not even allow you to have pages that you can record and execute.

+2
source

You may need to install the page executable before you can call it. On MS-Windows, see VirtualProtect Feature.

URL: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366898%28v=vs.85%29.aspx

0
source

Two problems:

  • The exec permission on the page is because you used an array that will be the .data section of the noexec read + write .data .
  • Your machine code does not end with a ret statement, so even if it is still executed, execution will return to what will be further in memory, and not to return.

And by the way, the REX prefix is ​​completely redundant. "\x31\xc0" xor eax,eax has the same effect as xor rax,rax .


You need a page containing machine code in order to have permission to execute . X86-64 page tables have a separate bit for execution, separate from read permissions, unlike legacy 386 page tables.

The easiest way to put static arrays into read + exec memory is to compile with gcc -z execstack . (Makes the stack and other section executable).

Until recently (2018 or 2019), the standard .rodata (binutils ld ) placed the .rodata section in the same ELF segment as the .text , so they would both have read permission + exec. Thus, using const char code[] = "..."; was enough to execute manually the specified bytes as data.

But on my Arch Linux system with GNU ld (GNU Binutils) 2.31.1 this is no longer the case. readelf -a shows that the .rodata section .rodata moved into the ELF segment with .eh_frame_hdr and .eh_frame and has read permission only. .text goes to the segment with Read + Exec, and .data goes to the segment with Read + Write (along with .got and .got.plt ). ( What is the difference between section and segment in ELF file format )

 #include <stdio.h> // can be non-const if you use gcc -z execstack. static is also optional static const char code[] = { 0x8D, 0x04, 0x37, // lea eax,[rdi+rsi] // retval = a+b; 0xC3 // ret }; static const char ret0_code[] = "\x31\xc0\xc3"; // xor eax,eax ; ret // the compiler will append a 0 byte to terminate the C string, // but that fine. It after the ret. int main () { // void* cast is easier to type than a cast to function pointer, // and in C can be assigned to any other pointer type. (not C++) int (*sum) (int, int) = (void*)code; int (*ret0)(void) = (void*)ret0_code; // run code int c = sum (2, 3); return ret0(); } 

On older Linux systems: gcc -O3 shellcode.c &&./a.out (works due to const in global / static arrays)

On old and current gcc -O3 -z execstack shellcode.c &&./a.out Linux: gcc -O3 -z execstack shellcode.c &&./a.out (works due to -zexecstack no matter where your machine code). Also works with clang -z execstack . gcc allows -zexecstack to be -zexecstack without spaces, but clang does not.

They also work on Windows, where the data is for .rdata only in .rdata instead of .rodata .

The main generated by the compiler is as follows (from objdump -drwC -Mintel ). You can run it inside gdb and set breakpoints on code and ret0_code

 (I actually used gcc -no-pie -O3 -zexecstack shellcode.c hence the addresses near 401000 0000000000401020 <main>: 401020: 48 83 ec 08 sub rsp,0x8 # stack aligned by 16 before a call 401024: be 03 00 00 00 mov esi,0x3 401029: bf 02 00 00 00 mov edi,0x2 # 2 args 40102e: e8 d5 0f 00 00 call 402008 <code> # note the target address in the next page 401033: 48 83 c4 08 add rsp,0x8 401037: e9 c8 0f 00 00 jmp 402004 <ret0_code> # optimized tailcall 

Or use system calls to change page permissions

Instead of compiling with gcc -zexecstack you can instead use mmap(PROT_EXEC) to highlight new executable pages or mprotect(PROT_EXEC) to change existing pages to executable. (Including pages containing static data.) Typically, you also want at least PROT_READ and sometimes PROT_WRITE .

Using mprotect for a static array means that you are still executing code from a known place, possibly making it easier to set a breakpoint on it.

On Windows, you can use VirtualAlloc or VirtualProtect.

In GNU C, you also need to use __builtin___clear_cache(buf, buf + len) after writing bytes of machine code to the buffer, because the optimizer does not consider dereferencing a function pointer as reading bytes from this address. Removing dead storage can remove the byte storage of machine code bytes into the buffer if the compiler proves that the storage is not being read as data. https://codegolf.stackexchange.com/questions/160100/the-repetitive-byte-counter/160236#160236 and https://godbolt.org/g/pGXn3B has an example where gcc really does this optimization because gcc "knows about" malloc .

(And on non-x86 architectures, where the I-cache is not consistent with the D-cache, it will actually perform any necessary cache synchronization. On x86, it's just an optimization blocker at compile time.)

My change in @AntoineMathys answer added this. Currently, gcc does not know about mmap , so it does not optimize storage for the pointer returned by mmap .

But it is not needed after mprotect on a page containing read-only C variables.

 #include <stdio.h> #include <sys/mman.h> #include <stdint.h> // can be non-const if you want, we're using mprotect static const char code[] = { 0x8D, 0x04, 0x37, // lea eax,[rdi+rsi] // retval = a+b; 0xC3 // ret }; static const char ret0_code[] = "\x31\xc0\xc3"; int main () { // void* cast is easier to type than a cast to function pointer, // and in C can be assigned to any other pointer type. (not C++) int (*sum) (int, int) = (void*)code; int (*ret0)(void) = (void*)ret0_code; // hard-coding x86 4k page size for simplicity. // also assume that 'code' doesn't span a page boundary and that ret0_code is in the same page. uintptr_t page = (uintptr_t)code & -4095ULL; // round down mprotect((void*)page, 4096, PROT_READ|PROT_EXEC|PROT_WRITE); // +write in case the page holds any writeable C vars that would crash later code. // run code int c = sum (2, 3); return ret0(); } 

I used PROT_READ|PROT_EXEC|PROT_WRITE in this example so that it works no matter where your variable is located. If it was local on the stack and you missed PROT_WRITE , the call will fail after the stack is read only when you try to send a return address.

In addition, PROT_WRITE allows you to test the PROT_WRITE code, which changes automatically, for example, edit zeros in your machine code or other bytes that he avoided.

 $ gcc -O3 shellcode.c # without -z execstack $ ./a.out $ echo $? 0 $ strace ./a.out ... mprotect(0x55605aa3f000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC) = 0 exit_group(0) = ? +++ exited with 0 +++ 

If I comment out mprotect , segfault does this.

If I did something like ret0_code[2] = 0xc3; After that I will need __builtin___clear_cache(ret0_code+2, ret0_code+2) to make sure that the storage has not been optimized, but if I do not mprotect static arrays, then I will not need it after mprotect . This is necessary after mmap + memcpy or manual saving, because we want to execute the bytes that were written in C (with memcpy ).

0
source

Source: https://habr.com/ru/post/912148/


All Articles