-
-
Save caiorss/4baf7dab5728d0e8f5a475967c0846b5 to your computer and use it in GitHub Desktop.
Loading machine code with mmap at runtime
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// =======>>> Load and execute machine code at runtime with mmap <<<====== | |
#include <iostream> | |
#include <string> | |
#include <fstream> | |
#include <sstream> | |
#include <cstring> | |
#include <cstdlib> | |
#include <vector> | |
#include <sys/mman.h> | |
using byte = uint8_t; | |
/** Load machine code into process virtual memory */ | |
void* load_machine_code(byte* buffer, std::size_t size); | |
void* load_machine_code(std::vector<byte> const& data); | |
std::vector<byte> | |
hexstr_to_bytes(std::string const& hexcode); | |
template<typename T> | |
inline T load_mcode(std::vector<byte> const& data) | |
{ | |
return reinterpret_cast<T>( load_machine_code(data) ); | |
} | |
int main(int argc, char** argv) | |
{ | |
std::puts("\n ====== Load add_numbers from machine code ================"); | |
// 'extern' means that the variable will be resolved by the Linker | |
extern std::string add_numbers_str; | |
// Type alias for function pointer | |
using add_numbers_t = int (*) (int, int); | |
auto add_numbers_code = hexstr_to_bytes(add_numbers_str); | |
// Load machine code for function add_numbers() at runtime. | |
auto add_numbers = load_mcode<add_numbers_t>(add_numbers_code); | |
std::cout << " [RESULT] add_numbers(20, 100) = " << add_numbers(20, 100) << '\n'; | |
std::cout << " [RESULT] add_numbers(206, 782) = " << add_numbers(206, 782) << '\n'; | |
std::puts("\n ======== Load factorial() from machine code ============="); | |
extern std::vector<byte> factorial_code; | |
// Function pointer alias | |
using factorial_t = int (*) (int); | |
auto factorial_func = load_mcode<factorial_t>(factorial_code); | |
std::cout << " [RESULT] factorial(5) = " << factorial_func(5) << '\n'; | |
std::cout << " [RESULT] factorial(6) = " << factorial_func(6) << '\n'; | |
std::cout << " [RESULT] factorial(7) = " << factorial_func(7) << '\n'; | |
std::fprintf(stdout, " [TRACE] Process terminated gracefully Ok. \n"); | |
return 0; | |
} | |
//===========================================================// | |
// I P L E M E N T A T I O N S // | |
//===========================================================// | |
void* load_machine_code(const byte* buffer, std::size_t size) | |
{ | |
// Create anonymous mapping (not backed by file), a memory that will | |
// be later marked as executable. | |
void* pmap = ::mmap( nullptr | |
, size | |
, PROT_READ | PROT_WRITE | |
, MAP_PRIVATE | MAP_ANONYMOUS | |
, -1, 0 ); | |
if(pmap == MAP_FAILED){ throw std::runtime_error("Failed create memory map"); } | |
// Copy machine code to memory map | |
std::memcpy( pmap, buffer, size); | |
// Mark memory as executable, but not writable. | |
if( mprotect(pmap, size, PROT_READ | PROT_EXEC ) == -1 ) | |
throw std::runtime_error("Faile to change memory protection flags"); | |
return pmap; | |
} | |
std::vector<byte> | |
hexstr_to_bytes(std::string const& hexcode) | |
{ | |
std::vector<byte> bytes; | |
bytes.reserve(hexcode.size()); | |
char* token = strtok((char*) hexcode.data(), "\\x"); | |
while( token != nullptr ){ | |
char byte = (char) strtol(token, nullptr, 16); | |
// ss << byte; | |
bytes.push_back(byte); | |
token = strtok(nullptr, "\\x"); | |
} | |
return bytes; | |
} | |
void* load_machine_code(std::vector<byte> const& data) | |
{ | |
return load_machine_code(&data[0], data.size()); | |
} | |
// int add_numners(int x, int y) | |
//--------------------------------------------------- | |
// 0000000000401152 <add_numbers>: | |
// 401152: 55 push %rbp | |
// 401153: 48 89 e5 mov %rsp,%rbp | |
// 401156: 48 89 f8 mov %rdi,%rax | |
// 401159: 48 01 f0 add %rsi,%rax | |
// 40115c: 5d pop %rbp | |
// 40115d: c3 retq | |
// | |
// Machine encoded as ASCII string | |
std::string add_numbers_str = "\\x55\\x48\\x89\\xe5\\x48\\x89\\xf8\\x48\\x01\\xf0\\x5d\\xc3"; | |
std::vector<byte> factorial_code = { | |
0x55 // push rbp | |
, 0x48, 0x89, 0xe5 // mov rbp,rsp | |
, 0x89, 0x7d, 0xec // mov DWORD PTR [rbp-0x14],edi | |
, 0xc7, 0x45, 0xfc, 0x01, 0x00, 0x00, 0x00 // mov DWORD PTR [rbp-0x4],0x1 | |
, 0xc7, 0x45, 0xf8, 0x01, 0x00, 0x00, 0x00 // mov DWORD PTR [rbp-0x8],0x1 | |
, 0xeb, 0x0e // jmp 40115b <factorial+0x25> | |
, 0x8b, 0x45, 0xfc // mov eax,DWORD PTR [rbp-0x4] | |
, 0x0f, 0xaf, 0x45, 0xf8 // imul eax,DWORD PTR [rbp-0x8] | |
, 0x89, 0x45, 0xfc // mov DWORD PTR [rbp-0x4],eax | |
, 0x83, 0x45, 0xf8, 0x01 // add DWORD PTR [rbp-0x8],0x1 | |
, 0x8b, 0x45, 0xf8 // mov eax,DWORD PTR [rbp-0x8] | |
, 0x3b, 0x45, 0xec // cmp eax,DWORD PTR [rbp-0x14] | |
, 0x7c, 0xea // jl 40114d <factorial+0x17> | |
, 0x8b, 0x45, 0xfc // mov eax,DWORD PTR [rbp-0x4] | |
, 0x5d // pop rbp | |
, 0xc3 // ret | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment