Skip to content

Instantly share code, notes, and snippets.

@Youka
Last active January 30, 2023 07:33
Show Gist options
  • Save Youka/0c44171e868377e8d496 to your computer and use it in GitHub Desktop.
Save Youka/0c44171e868377e8d496 to your computer and use it in GitHub Desktop.
Run machine code with LuaJIT (Windows x86)
-- Load foreign-function-interface handle
local ffi = require("ffi")
-- Shortcut FFI C namespace
local C = ffi.C
-- Load Windows kernel32 DLL
local kernel32 = ffi.load("kernel32")
-- Add C definitions to FFI for usage descriptions of components
ffi.cdef([[
// Redefinitions for WinAPI conventions
typedef void VOID;
typedef VOID* LPVOID;
typedef uintptr_t ULONG_PTR;
typedef ULONG_PTR SIZE_T;
typedef unsigned long DWORD;
typedef int BOOL;
// Property flags for functions below
enum{
MEM_COMMIT = 0x1000,
MEM_RESERVE = 0x2000
};
enum{
PAGE_EXECUTE_READWRITE = 0x40
};
enum{
MEM_RELEASE = 0x8000
};
// Function headers for correct Lua->C->Lua conversions
LPVOID __stdcall VirtualAlloc(LPVOID, SIZE_T, DWORD, DWORD);
BOOL __stdcall VirtualFree(LPVOID, SIZE_T, DWORD);
]])
-- Metatable for C function wrapping
local func_mt = {
__call = function(obj, ...)
return obj[1](...)
end,
__tostring = function(obj)
return tostring(obj[1])
end
}
-- Converts machine code to function
local function mem2func(header, code)
-- Check arguments
if type(header) ~= "string" or type(code) ~= "table" then
error("Expected function header as string and machine code as table!", 2)
end
-- Get code size
local code_n = #code
-- Allocate memory with executable rights
local exec_memory = kernel32.VirtualAlloc(nil, code_n, C.MEM_COMMIT + C.MEM_RESERVE, C.PAGE_EXECUTE_READWRITE)
if not exec_memory then
error("Couldn't allocate memory with execution rights!", 2)
end
ffi.gc(exec_memory, function(memory) kernel32.VirtualFree(memory, 0, C.MEM_RELEASE) end)
-- Copy code into executable memory
ffi.copy(exec_memory, ffi.new("char[?]", code_n, code), code_n)
-- Return executable memory as function (packed in table with memory to keep her safe from garbage collection)
return setmetatable(
{ffi.cast(header, exec_memory), exec_memory},
func_mt
)
end
-- Test
local func = mem2func("int (*)(int, int)", {0x8b, 0x44, 0x24, 0x08, 0x03, 0x44, 0x24, 0x04, 0xc3})
--[[
0: 8b 44 24 08 mov 0x8(%esp),%eax
4: 03 44 24 04 add 0x4(%esp),%eax
8: c3 ret
]]
print(func(-1, 4)) -- 3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment