1

Say I want to convert a certain function into hex

void func(char* string) {
    puts(string);
}
    1139:   55                      push   %rbp
    113a:   48 89 e5                mov    %rsp,%rbp
    113d:   48 83 ec 10             sub    $0x10,%rsp
    1141:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
    1145:   48 8b 45 f8             mov    -0x8(%rbp),%rax
    1149:   48 89 c7                mov    %rax,%rdi
    114c:   e8 df fe ff ff          callq  1030 <puts@plt>
    1151:   90                      nop
    1152:   c9                      leaveq 
    1153:   c3                      retq   

This is what I got on x86_64: \x55\x48\x89\xe5\x48\x83\xec\x10\x48\x89\x7d\xf8\x48\x8b\x45\xf8\x48\x89\xc7\xe8\xdf\xfe\xff\xff\x90\xc9\xc3

encrypt it and use it in this program. A decryptor at the start to decrypt these instructions at run time so it can't be analyzed statically.

Converting the above function into hex and creating a function pointer for it doesn't run and ends with SIGSEGV at push %rbp.

My aim is to make this code print Hi.

int main() {
    char* decrypted = decrypt(hexcode);
    void (*func)(char*) = (void)(*)(char)) decrypted;
    func("HI");
}

My questions are:

  1. How do I convert a function into hex properly.
  2. How do I then run this hex code from main as shown above?
Steve Friedl
  • 3,929
  • 1
  • 23
  • 30
user282909
  • 115
  • 1
  • 7
  • Is `decrypted("HI");` a typo? You went through all the trouble to cast it to `func` and didn't bother to use your newfound pointer. – foreverska Dec 11 '19 at 22:39
  • Do you really mean "convert to hex", or did you really mean simply the binary machine code of the compiled function? The hexadecimal representation is simply a presentation on the binary data for convenience and readability - it is not actually stored as hex digits. Either way there are a number of issues with what you are trying to do; you assume that the compiled machine code is position independent, and you are trying to execute _data_ which modern operating systems on processors with an MMU will normally prevent you from doing for security reasons. – Clifford Dec 11 '19 at 22:52
  • @foreverska yep that was a typo. It;s corrected now. – user282909 Dec 11 '19 at 22:53
  • @Clifford yeah I basically mean binary machine code. Say a shellcode for example how would I convert func() into a `shellcode` so I can exec it at runtime without exposing the instructions. – user282909 Dec 11 '19 at 22:55
  • Use an assembler to generate machine code. To run it make sure you place it into executable memory. – Jester Dec 11 '19 at 23:02
  • 2
    @user282909 as an aside the idea is interesting but will not survive serious static analysis. I assume the "key" lives in the binary so it will annoy the RE but not stop them. Even if you found an impenetrable keystore the function does exist in the clear when being executed so it can be lifted. But cool question all the same. – foreverska Dec 11 '19 at 23:02
  • @Jester updated the question. That's essentially what I did, but does the plt address change when merging this binary into another? – user282909 Dec 11 '19 at 23:32
  • You should not try to call libc functions. If you want to print something, go for direct system calls. The fault at `push rbp` is due to the memory not being executable, which I told you about. In the meantime @mevets posted an answer about how you can fix that. – Jester Dec 12 '19 at 00:25

3 Answers3

1

If you want to execute a binary blob; then you need to do something like this:

void *p = mmap(0, blob_size, PROT_WRITE, MAP_ANON, NOFD, 0);
read(blob_file, p, blob_size);
mprotect(p, blob_size, PROT_EXEC);
void (*UndefinedBehaviour)(char *x) = p;
UndefinedBehaviour("HI");

The allocates some memory, copies a blob into it, changes the memory to be PROT_EXEC, then invokes the blob at its beginning. You need to add some error checking, and depending upon what sort of system you are on, it may be running malware monitors to prevent you from doing this.

mevets
  • 10,070
  • 1
  • 21
  • 33
0

Answer for 1. : It is near impossible to do it automatically, because there is no simple way for determining the length of function code - it depends to machine CPU, compiler optimizations etc. Only way is "manual" analysis of disassembled binary.

VillageTech
  • 1,968
  • 8
  • 18
  • Ok I kinda didn't explain that part enough I guess. prog1.c has the func() and then I compile it position independently diassemble it and extract the binary for the instructions in func. After that; I take that binary and throw it in a new program (prog2.c) called like how it is in main. – user282909 Dec 11 '19 at 22:58
0

You can't for those instructions because they're not fully position-independent and self-contained.

e8 df fe ff ff is a call rel32 (with a little-endian relative displacement as the call target). It only works if that displacement reaches the puts@plt stub, and that only happens in the executable you're disassembling, where this code appears at a fixed distance from the PLT. (So the executable itself is position-independent when relocated as a whole, but taking the machine code for one function and trying to run it from some other address will break.)


In theory you could fixup the call target using a function pointer to puts in some code that included this machine code in an array, but if you're trying to make shellcode you can't depend on the "target" process helping you that way.

Instead you should use system calls directly via the syscall instruction, for example Linux syscall with RAX=1=__NR_write is write. (Not via their libc wrapper functions like write(), that would have exactly the same problem as puts).


Then you can refer to How to get c code to execute hex bytecode? for how to put machine code in a C array, make sure that's in an executable page (e.g. gcc -z execstack or mprotect or mmap), and cast that to a function pointer + call it like you're doing here.

ends with SIGSEGV at push %rbp

Yup, code-fetch from a page without EXEC permission will do that. gcc -z execstack is an easy way to fix that, or mmap like other answers suggest, at which point execution will get as far as the call -289 and fault or run bad instructions.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847