7

My question is dedicated mostly to profs and is about using C++ in "strange" way. In C++ there isn't really big difference between pointers to variables and pointers to functions. We can do something useless like this:

char* buff     = new char[32];
void (*func)() = (void (*)())buff;

But we allmost created a function that never existed, right? What if we go further and fill buff with x86 commands stord in a file? OS will never know that a function was created.

#include <iostream>
using namespace std;

// no stack push'ing or pop'ing, nothing to return
void func(void){cout << "Hello?";}

int main()
{
  char* x86_code = new char[6];

  x86_code[0]                 = 0x9A;          // call (far)
  *((__int32*)(x86_code + 1)) = (__int32)func; // load 32-bit address
  x86_code[5]                 = 0xC3;          // ret

  void (*x86_func)(void) = (void (*)(void))x86_code;
  x86_func();

  return 0;
}

Calling x86_func() makes a runtime error (violation reading location 0xFFFFFFFF). How does OS loads it's binaries or modules in RAM if not in this manner? Many thanks.

Ivars
  • 2,375
  • 7
  • 22
  • 31
  • 1
    Look for "shellcode" in the WEB. – Alex F Nov 17 '13 at 08:52
  • 2
    Don't call far. That is nearly guaranteed to be incorrect in 32bit code. Use an absolute near call (opcode 0xFF with the reg field in the ModRM byte set to 2) (or a normal near call, but be aware that it's relative). Also, you might have to make some system call to make that memory executable. – harold Nov 17 '13 at 08:56
  • 1
    Related: [Data Execution Prevention on Wikipedia](https://en.wikipedia.org/wiki/Data_execution_prevention) – user2802841 Nov 17 '13 at 10:19
  • Please also see [Why are data pointers and function pointers incompatible in C/C++](http://stackoverflow.com/q/12358843/597607) – Bo Persson Nov 17 '13 at 10:27
  • For specifically how to call functions in assembler, look at something called the _calling convention_ (en.wikipedia.org/wiki/X86_calling_conventions). It details how exactly the stack and registers must be set up. – Iwillnotexist Idonotexist Nov 23 '13 at 13:27

1 Answers1

4

Indeed you can fill an array with x86 machine code and attempt to execute it. It's called shellcode and managing to make an application or library execute such code when it wasn't intended to is called an "exploit".

Unfortunately it's not that easy, since modern hardware and OSes normally prevent you from executing code from areas that are writable, such as non-const char arrays. This is called W^X (write or execute permissions, but not both) But one can request POSIX-compliant OSes to disable such protection with the mprotect() function. Here's an example that works, because it enables read, write and execute permissions on the array of machine code bytes concerned:

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


typedef int(*FUNKY_POINTER)(void);


char shellcode[] = {
    0xb8, 0x2a, 0x00, 0x00, 0x00, //mov    $0x2a,%eax
    0xc3                          //retq
};



int main(void){
    uintptr_t pageSize        = 4096;
    uintptr_t shellcodeAddr   = (uintptr_t)shellcode;
    uintptr_t pageAlignedAddr = shellcodeAddr & ~(pageSize-1);
    FUNKY_POINTER shellcodeFn = (FUNKY_POINTER)shellcode;

    /* Magic */
    mprotect((void*)pageAlignedAddr,
             (shellcodeAddr - pageAlignedAddr) + sizeof(shellcode),
             PROT_EXEC|PROT_WRITE|PROT_READ);

    printf("The answer to the ultimate question of life, "
           "the universe and everything is %d\n",
           shellcodeFn());

    return 0;
}
Iwillnotexist Idonotexist
  • 13,297
  • 4
  • 43
  • 66
  • thank you! but seems like mprotect() doesn't exists on windows platforms. i used VirtualProtect [link](http://msdn.microsoft.com/en-us/library/windows/desktop/aa366898%28v=vs.85%29.aspx) from WinAPI and it allowed me to execute shellcode. this one relly helped me to understand low level things. – Ivars Nov 23 '13 at 07:39
  • but still i don't understand how to call functions or edit variables from shellcode. – Ivars Nov 23 '13 at 08:18
  • Ah now for that you should look at something called the _calling convention_. http://en.wikipedia.org/wiki/X86_calling_conventions – Iwillnotexist Idonotexist Nov 23 '13 at 13:16
  • what's the difference between a normal address and a page-aligned address? Doing this seems to work fine too: `mprotect((void*)pageAlignedAddr, sizeof(shellcode), PROT_EXEC|PROT_WRITE|PROT_READ);` https://github.com/pirate/C--DataStructures/blob/master/runtime_func_exec.cpp – Nick Sweeting Sep 27 '14 at 07:34
  • 1
    @NickSweeting That your code still works is luck. The reason for page-alignment is that the OS and hardware controls to prevent execution of shellcode operate at the granularity of a 4KB page; So `mprotect()` can only alter protection settings for blocks that start on addresses divisible by 4KB. Hence why I calculate the shellcode's offset from the block start address and _add_ that to the shellcode's size – Only that guarantees that the whole shellcode is marked executable, while your omission of that offset means that it is _possible_ that only a portion of the shellcode will be executable. – Iwillnotexist Idonotexist Sep 27 '14 at 14:57
  • 1
    @NickSweeting On examining your code, I see that you placed the shellcode in the first block of heap memory allocated by `malloc()`. There's an extremely high chance that will be aligned to a very large boundary, which is why it works right now, but `malloc()` isn't guaranteed to align the memory block on 4KB; For that you'd need `posix_memalign()` or suchlike. I still advise you to add the offset and give yourself peace of mind. – Iwillnotexist Idonotexist Sep 27 '14 at 15:02