Last active
May 14, 2021 14:16
-
-
Save paxbun/a25f75a9fc5b8c3a262a86f09965bab2 to your computer and use it in GitHub Desktop.
Run dynamically generated machine code on runtime
This file contains hidden or 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
| #ifdef _WIN32 | |
| # include <Windows.h> | |
| #else | |
| # include <sys/mman.h> | |
| # include <unistd.h> | |
| #endif | |
| #include <array> | |
| #include <cstddef> | |
| #include <cstring> | |
| #include <iostream> | |
| #include <stdexcept> | |
| #include <vector> | |
| template <typename From, typename To> | |
| To transmute(From from) | |
| { | |
| union | |
| { | |
| From from; | |
| To to; | |
| } u; | |
| u.from = from; | |
| return u.to; | |
| } | |
| void print(uint32_t n) | |
| { | |
| for (uint32_t i = 0; i < n; ++i) std::cout << i << ": Hello, world!" << std::endl; | |
| } | |
| struct Program | |
| { | |
| private: | |
| void (*ptr)(); | |
| size_t offset; | |
| public: | |
| Program(void (*callback)(uint32_t), uint32_t input); | |
| ~Program(); | |
| inline void operator()() | |
| { | |
| if (ptr) | |
| ptr(); | |
| } | |
| }; | |
| int main() | |
| { | |
| uint32_t input; | |
| std::cout << "Number of times to repeat: "; | |
| std::cin >> input; | |
| try | |
| { | |
| Program program { print, input }; | |
| program(); | |
| } | |
| catch (const std::runtime_error&) | |
| { | |
| std::perror("Changing protection attribute failed"); | |
| return 1; | |
| } | |
| } | |
| Program::Program(void (*callback)(uint32_t), uint32_t input) : ptr { nullptr }, offset { 0 } | |
| { | |
| auto param = transmute<uint32_t, std::array<uint8_t, 4>>(input); | |
| auto func = transmute<void (*)(uint32_t), std::array<uint8_t, 8>>(callback); | |
| uint8_t source[] = { | |
| 0x55, // push rbp | |
| 0x48, 0x89, 0xe5, // mov rbp, rsp | |
| 0x48, 0x83, 0xec, 0x10, // sub rsp, 0x10 | |
| #ifdef _WIN32 | |
| 0xb9, param[0], param[1], param[2], param[3], // mov ecx, input | |
| #else | |
| 0xbf, param[0], param[1], param[2], param[3], // mov edi, input | |
| #endif | |
| 0x48, 0xb8, func[0], func[1], func[2], | |
| func[3], func[4], func[5], func[6], func[7], // movabs rax, print | |
| 0xff, 0xd0, // call rax | |
| 0xc9, // leave | |
| 0xc3, // ret | |
| }; | |
| constexpr size_t program_size = sizeof source; | |
| #ifdef _WIN32 | |
| uint8_t* program = new uint8_t[program_size]; | |
| memcpy(program, source, program_size); | |
| DWORD before; | |
| if (!VirtualProtect(program, program_size, PAGE_EXECUTE_READWRITE, &before)) | |
| { | |
| delete[] program; | |
| throw std::runtime_error { "" }; | |
| } | |
| ptr = (void (*)())program; | |
| offset = 0; | |
| #else | |
| size_t page_size = getpagesize(); | |
| uint8_t* program = new uint8_t[page_size + program_size]; | |
| offset = page_size - ((size_t)program) % page_size; | |
| program += offset; | |
| memcpy(program, source, program_size); | |
| if (mprotect(program, program_size, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) | |
| { | |
| delete[](program - offset); | |
| throw std::runtime_error { "" }; | |
| } | |
| ptr = (void (*)())program; | |
| #endif | |
| } | |
| Program::~Program() | |
| { | |
| delete[]((uint8_t*)ptr - offset); | |
| ptr = nullptr; | |
| offset = 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment