From cc10ab59ea8719e99090a351458afe7ca845ad1f Mon Sep 17 00:00:00 2001 From: Ethan Green Date: Mon, 7 Oct 2024 11:32:34 -0400 Subject: [PATCH 1/2] implement native module creation --- crates/lovely-core/src/sys.rs | 146 +++++++++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 4 deletions(-) diff --git a/crates/lovely-core/src/sys.rs b/crates/lovely-core/src/sys.rs index 8a5a58d..d222a7b 100644 --- a/crates/lovely-core/src/sys.rs +++ b/crates/lovely-core/src/sys.rs @@ -1,9 +1,9 @@ -use std::{ - collections::VecDeque, ffi::{c_void, CString}, ptr, slice -}; +use std::ptr; +use std::slice; +use std::collections::VecDeque; +use std::ffi::{c_void, CString}; use itertools::Itertools; -use libc::FILE; use libloading::{Library, Symbol}; use log::info; use once_cell::sync::Lazy; @@ -68,6 +68,144 @@ pub static lua_typename: Lazy isize>> = Lazy::new(|| unsafe { LUA_LIB.get(b"lua_isstring").unwrap() }); +pub static lual_register: Lazy> = + Lazy::new(|| unsafe { LUA_LIB.get(b"luaL_register").unwrap() }); + +pub static lua_pushstring: Lazy> = + Lazy::new(|| unsafe { LUA_LIB.get(b"lua_pushstring").unwrap() }); + +pub static lua_pushnumber: Lazy> = + Lazy::new(|| unsafe { LUA_LIB.get(b"lua_pushnumber").unwrap() }); + +pub static lua_settable: Lazy> = + Lazy::new(|| unsafe { LUA_LIB.get(b"lua_settable").unwrap() }); + +/// A trait which allows the implementing value to generically push its value onto the Lua stack. +pub trait Pushable { + /// Push this value onto the Lua stack. + /// + /// # Safety + /// Directly interacts with native Lua state. + unsafe fn push(&self, state: *mut LuaState); +} + +impl Pushable for String { + unsafe fn push(&self, state: *mut LuaState) { + let value = format!("{self}\0"); + lua_pushstring(state, value.as_ptr() as _); + } +} + +impl Pushable for &str { + unsafe fn push(&self, state: *mut LuaState) { + let value = format!("{self}\0"); + lua_pushstring(state, value.as_ptr() as _); + } +} + +impl Pushable for isize { + unsafe fn push(&self, state: *mut LuaState) { + lua_pushnumber(state, *self as _); + } +} + +/// A lua FFI entry. Used specifically by lual_register to register +/// a module and its associated functions into the lua runtime. +pub struct LuaReg { + name: String, + func: unsafe extern "C" fn(*mut LuaState) -> isize, +} + +pub struct LuaVar { + name: String, + val: P, +} + +pub struct LuaModule { + reg: Vec, + var: Vec>, +} + +impl LuaModule

{ + pub fn new() -> Self { + LuaModule { + reg: vec![], + var: vec![], + } + } + + /// Register a native C FFI function to this Lua module. + pub fn add_reg(self, name: &'static str, func: unsafe extern "C" fn(*mut LuaState) -> isize) -> Self { + let name = format!("{name}\0"); + let mut reg = self.reg; + reg.push(LuaReg { + name, + func, + }); + + Self { + reg, + var: self.var, + } + } + + /// Add a variable to this Lua module. + pub fn add_var(self, name: &'static str, val: P) -> Self { + let name = format!("{name}\0"); + let mut var = self.var; + var.push(LuaVar { + name, + val, + }); + + LuaModule { + reg: self.reg, + var, + } + } + + /// Commit this Lua module to native Lua state. + /// + /// # Safety + /// Directly interacts and mutates native Lua state. + pub unsafe fn commit(self, state: *mut LuaState) { + // Convert self.reg into a raw array of name:func pairs, represented as native c_void pointers. + let native_reg: Vec<*const c_void> = self + .reg + .iter() + .map(|reg| { + let name = ®.name; + let func = reg.func; + + vec![name.as_ptr() as *const c_void, func as *const c_void] + }) + .flatten() + .chain(vec![0 as _, 0 as _]) + .collect(); + + // Register the name:func table within the Lua runtime. + let reg_ptr = native_reg.as_ptr() as *const c_void; + lual_register(state, b"lovely\0".as_ptr() as _, reg_ptr); + + // Now we register variables onto the lovely global table. + let top = lua_gettop(state); + lua_getfield(state, LUA_GLOBALSINDEX, b"lovely\0".as_ptr() as _); + + for lua_var in self.var.iter() { + // Push the var name and value onto the stack. + lua_pushstring(state, lua_var.name.as_ptr() as _); + lua_var.val.push(state); + + // Set the table key:val from what we previously pushed onto the stack. + lua_settable(state, -3); + } + + // Reset the stack. + lua_settop(state, top); + } +} + + /// Load the provided buffer as a lua module with the specified name. /// # Safety /// Makes a lot of FFI calls, mutates internal C lua state. From 49b950f902944de3aad0e0aa432a4c24f425cc07 Mon Sep 17 00:00:00 2001 From: Ethan Green Date: Mon, 7 Oct 2024 11:32:51 -0400 Subject: [PATCH 2/2] replace lovely.lua with native module setup --- crates/lovely-core/lovely.lua | 8 -------- crates/lovely-core/src/lib.rs | 22 +++++++++------------- 2 files changed, 9 insertions(+), 21 deletions(-) delete mode 100644 crates/lovely-core/lovely.lua diff --git a/crates/lovely-core/lovely.lua b/crates/lovely-core/lovely.lua deleted file mode 100644 index 0bde9c2..0000000 --- a/crates/lovely-core/lovely.lua +++ /dev/null @@ -1,8 +0,0 @@ --- This file contains template values filled during JIT. You can access these values --- within your code with `local lovely = require("lovely")`. - -return { - repo = "https://github.com/ethangreen-dev/lovely-injector", - version = "lovely_template:version", - mod_dir = "lovely_template:mod_dir", -} diff --git a/crates/lovely-core/src/lib.rs b/crates/lovely-core/src/lib.rs index 639cfb9..1f1e185 100644 --- a/crates/lovely-core/src/lib.rs +++ b/crates/lovely-core/src/lib.rs @@ -16,7 +16,7 @@ use getargs::{Arg, Options}; use itertools::Itertools; use patch::{Patch, PatchFile, Priority}; use sha2::{Digest, Sha256}; -use sys::LuaState; +use sys::{LuaModule, LuaState}; pub mod chunk_vec_cursor; pub mod log; @@ -372,18 +372,14 @@ impl PatchTable { /// # Safety /// Unsafe due to internal unchecked usages of raw lua state. pub unsafe fn inject_metadata(&self, state: *mut LuaState) { - let table = vec![ - ("mod_dir", self.mod_dir.to_str().unwrap().replace('\\', "/")), - ("version", env!("CARGO_PKG_VERSION").to_string()), - ]; - - let mut code = include_str!("../lovely.lua").to_string(); - for (field, value) in table { - let field = format!("lovely_template:{field}"); - code = code.replace(&field, &value); - } - - sys::load_module(state, "lovely", &code, self.loadbuffer.as_ref().unwrap()) + let mod_dir = self.mod_dir.to_str().unwrap().replace('\\', "/"); + let repo = "https://github.com/ethangreen-dev/lovely-injector"; + + LuaModule::new() + .add_var("repo", repo) + .add_var("version", env!("CARGO_PKG_VERSION")) + .add_var("mod_dir", &mod_dir) + .commit(state); } /// Apply one or more patches onto the target's buffer.