5

I have the following assembly function (shown with objdump already)

0000000000000000 <add>:
   0:   b8 06 00 00 00          mov    $0x6,%eax
   5:   c3                      retq  

Now in C I made the following code:

#include <stdio.h>

typedef int (*funcp) (int x);

unsigned char foo[] = {0xb8,0x06,0x00,0x00,0x00,0xc3};

int main(void)
{
  int i;
  funcp f = (funcp)foo;
  i = (*f);
  
  printf("exit = %d\n", i);
  return 0;
}

In the global variable foo I typed the memory address of my function in assembly and tried to execute it but it does not return 6 as expected. How can I execute functions for their memory addresses? furthermore, where can i research more on the subject?

obs: sometimes I got the Segmentation fault (core dumped) error

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
SbasicDY
  • 51
  • 2
  • 1
    For a start, you have to use `()` to call a function. – user253751 Nov 17 '20 at 14:21
  • Why not simply call it by name? – Eugene Sh. Nov 17 '20 at 14:22
  • `i = (*f)` -> `i = (*f)(0)` – Jabberwocky Nov 17 '20 at 14:29
  • Shouln't it be `typedef int (*funcp) ();`? Your function in assembly takes no parameters. – Jabberwocky Nov 17 '20 at 14:31
  • True, this function does not receive any parameters, but even changing to () the value of variable i is still wrong. I tried (0) and got segmetantion fault. Anyway I would like it to work for functions of n parameters – SbasicDY Nov 17 '20 at 14:34
  • @SbasicDY what is your platform? On my platform (Windows) it almost works, but I cannot execute code that is actually data. On your platform yopu may have a similar issue. – Jabberwocky Nov 17 '20 at 14:36
  • 5
    The data section is usually not executable, meaning your code will run into a segmentation fault. – fuz Nov 17 '20 at 14:39
  • I am running through WSL 1 (Windows Subsystem for Linux) I compile and run with gcc as follows: gcc -o out test.c ./out – SbasicDY Nov 17 '20 at 14:40
  • [This](https://stackoverflow.com/questions/11252707/how-to-make-a-c-program-that-can-run-x86-hex-codes/11253633#11253633) and [this](https://stackoverflow.com/questions/11447957/c-or-asm-how-to-execute-c-code-stored-in-memory-copied-from-labels) might be interesting to read – Jabberwocky Nov 17 '20 at 14:43
  • @fuz, I also tried to compile it as follows to avoid this: gcc -Wa, - execstack -Wall -c temp1.c – SbasicDY Nov 17 '20 at 14:44
  • @SbasicDY It's `gcc -Wa, -z execstack ...` – Jabberwocky Nov 17 '20 at 14:46
  • Using -z I get the followiing error: gcc -Wa,-z execstack -c temp1.c gcc: error: execstack: No such file or directory – SbasicDY Nov 17 '20 at 14:49
  • 1
    `gcc -z execstack temp1.c` works for me. I get `6` as output. – Jabberwocky Nov 17 '20 at 14:51
  • @SbasicDY That does not make the data segment executable, only the stack. – fuz Nov 17 '20 at 14:54
  • @fuz You are right and TBH I didn't expect it to work unless I'd make `foo` a local variable, but it works anyway. – Jabberwocky Nov 17 '20 at 14:57
  • if you make it a const it may pull it into .text and make that then in an executable space, but the real issue esp, if down the road you want to do self-modifying code, you have to solve the executable space problem and not just make this example work. – old_timer Nov 17 '20 at 15:07
  • 3
    @fuz: `-z execstack` on Linux made *all* readable pages executable until very recent kernel versions. Linux's ELF program loader set the read-implies-exec process "personality" flag when seeing the executable GNU stack metadata. [Linux default behavior against \`.data\` section](https://stackoverflow.com/q/64833715) - `PT_GNU_STACK == RWX` no longer implies exec-all; I just updated my Arch Linux and `gcc -zexecstack` doesn't make .data (or `.rodata`) executable. See also [Unexpected exec permission from mmap...](//stackoverflow.com/q/58260465). – Peter Cordes Nov 17 '20 at 15:54
  • @fuz: But since that change, there are now several outdated answers on the multiple duplicates of this question which suggest using `gcc -zexecstack`. Unless there's a handy way to set the READ_IMPLIES_EXEC personality flag directly (including for new kernel where GNU-stack doesn't do it), only the `mprotect` or `mmap`+`memcpy` answers are now viable for testing shellcode-like arrays this way. – Peter Cordes Nov 17 '20 at 16:01
  • @PeterCordes I had hoped something like `-Wl,-N` would do the trick, but the manual does not indicate such. Alternatively, perhaps it is possible via some `__attribute__` to place the variable in the text section? – fuz Nov 17 '20 at 16:37
  • 1
    @fuz: Yes, `__attribute__((section(".text")))` works. I get *Assembler messages: /tmp/ccNetmKQ.s:28: Warning: ignoring changed section attributes for .text* but it does run. https://godbolt.org/z/draGeh. On my desktop, `-Wl,-N` makes it fail to link: `/usr/bin/ld: cannot find -lgcc_s`. *Both* of those things (array in text section, and read/write text section) would be necessary for a non-const executable array. – Peter Cordes Nov 17 '20 at 16:41

1 Answers1

0

The NX flag might be your 'friend' here. Parts of memory which are never meant to be executed as binary machine code can be marked as No-eXecute. See https://en.wikipedia.org/wiki/NX_bit . So, depending on architecture, operating system and settings, and even BIOS settings.

So this feature might be on or off. If NX is used on the data-section of your program, it will not run. You will need to mmap() a piece of memory with PROT_EXEC set, copy the data in, then run it.

For the following, I changed the binary to be an amd64 code (+1 func). When using the mmap() copy, it works. When directly calling foo, it fails (on my machine with NX active)

(code without err-check, freeing of mem, etc)

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

typedef int (*funcp) (int x);

unsigned char foo[] = {0x8d,0x47,0x01,0xc3};
//unsigned char foo[] = {0xc3,0xb8,0x06,0x00,0x00,0x00,0xc3};

int main(void)
{
  int i;

  void *mem2;

  mem2 = mmap(0,4096,PROT_WRITE|PROT_READ|PROT_EXEC,MAP_PRIVATE|MAP_ANONYMOUS|MAP_EXECUTABLE,-1,0);

  memcpy(mem2,foo,sizeof(foo));


  funcp f = (funcp)mem2;
  i = f(42);

  printf("exit = %d\n", i);
  return 0;
}
Blindleistung
  • 672
  • 8
  • 21
  • 1
    That's not fully safe in GNU C without `__builtin___clear_cache` to let the compiler know the store into that buffer isn't "dead", i.e. that it will be read as code. (No actual cache invalidation has to happen on x86, with its coherent I-cache, but there is a possible compile-time optimization problem.) See [How to get c code to execute hex machine code?](https://stackoverflow.com/q/9960721) – Peter Cordes Nov 19 '20 at 00:01