I would like to compile small double(...)
functions as x64 shellcode. I already have a working program to generate the encoded assembly for simple math operations like a + b / a
, where a
and b
are double
parameters for the function.
The generated code is loaded into a executable mmap
buffer and can be called later.
I have the following problem: I would like to call math.h
functions like sin
, exp
etc. inside my generated shellcode. Since all call
opcodes somehow use 32-bit adresses or relative jumps, i cannot easily use these instructions. Therefore I tried to implement my own call
-instruction this way:
lea rax, [rip+0x0] ;load instruction pointer into rax
add rax, <offset-to-return-to>
push rax
moveabs rax, <adress-of-function> ;load the function pointer to rax
jmp rax
This is my code to generate these instructions:
//lea rax, [rip+0x0]
insertAll(code,std::list<uint8_t>{ 0x48, 0x8D, 0x05, 0x00, 0x00, 0x00, 0x00 });
//add rax, <offset-to-return-to>
insertAll(code,std::list<uint8_t>{ 0x48, 0x83, 0xC0, <offset-to-return-to>});
//push rax
code.push_back(0x50);
//moveabs rax, address-of-function
uint8_t reg = 0; //rax
uint8_t rexWPrefix = 0x48 + (reg > 8);
uint8_t opCode = 0xB8 + (reg % 8);
insertAll(code,std::list<uint8_t>{ rexWPrefix, opCode });
code.insert(code.end(),reinterpret_cast<uint8_t*>(&fabs),reinterpret_cast<uint8_t*>(&fabs) + 8);
//jmp rax
insertAll(code,std::list<uint8_t>{ 0xFF, 0xE0 });
Unfortunately, calling functions this way does not work, the program crashes with a SIGSEGV
error. Is there something wrong with my assembly code or encoding of the instruction? What value should <offset-to-return-to>
have, to let the function return to the right position?
Disclaimer: I know, that this is not the best way to handle dynamic generation of code... I could just compile a dynamic library and load it with dlsym
. This is just a fun way to learn about assembly/shellcodes and should not be taken too seriously :)
I found three errors. The
jmp
instruction is not absolute, but afaikRIP
-relative. I usedpush + ret
as an alternative becauseret
jumps to an absolute address lying on the stack.Additionally, I did not know that it is mandatory for the caller to reserve 4*8 bytes shadow space on the stack. The details can be found here.
Finally, the code to insert the function pointer into the instruction code was wrong. I accidentally inserted the first 8 bytes of the function code instead of the value of the pointer:
This is the finished working code: