-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Superior version of Autorun
- Loading branch information
Showing
7 changed files
with
340 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
target/ | ||
Cargo.lock | ||
*.exe | ||
auto.bat | ||
*.dll |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
[package] | ||
name = "Autorun" | ||
version = "0.1.0" | ||
authors = ["Vurv78 <[email protected]>"] | ||
edition = "2018" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[lib] | ||
crate-type = ["dylib"] | ||
|
||
[dependencies] | ||
# detour-rs static detours needs nightly, which sucks since nightly breaks rls, I don't wanna bother trying to search up how to fix that. | ||
detour = { version = "0.7.1", default-features = false } | ||
|
||
libc = "0.2.85" # FFI Types | ||
winapi = {version = "0.3.9", features = ["consoleapi"]} | ||
|
||
# DLL Loading | ||
dlopen = "0.1.8" | ||
dlopen_derive = "0.1.4" | ||
|
||
dirs = "3.0.1" # To get your home directory. |
File renamed without changes.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
@echo off | ||
rustup target add i686-pc-windows-msvc | ||
cargo build --release --target=i686-pc-windows-msvc | ||
move %cd%\target\i686-pc-windows-msvc\release\Autorun.dll %cd%\Autorun_Win_32.dll | ||
pause |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
@echo off | ||
cargo build --release | ||
move %cd%\target\release\Autorun.dll %cd%\Autorun_Win_64.dll | ||
pause |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,303 @@ | ||
|
||
|
||
#![allow(non_snake_case)] | ||
|
||
// Winapi | ||
use winapi::shared::minwindef as WinStructs; // Types from the windows api | ||
use WinStructs::HINSTANCE; | ||
use winapi::um::consoleapi::AllocConsole; // Allocate Windows Console for io | ||
|
||
use std::fs::{self, File}; | ||
use std::path::{Path, PathBuf}; | ||
use std::io::{self, prelude::* }; // Read user input for commands | ||
|
||
// detours-rs | ||
use detour::GenericDetour; | ||
|
||
// dlopen-rs | ||
#[macro_use] extern crate dlopen_derive; | ||
use dlopen::wrapper::{Container, WrapperApi}; | ||
|
||
// Types | ||
use std::ffi::{CStr, CString}; | ||
use libc::c_void as CVoid; | ||
type CChar = i8; | ||
type LuaState = *mut CVoid; | ||
type CharBuf = *const CChar; // *const char | ||
type SizeT = usize; | ||
type CInt = i32; | ||
type GlobalDetour<T> = Option<GenericDetour<T>>; | ||
type LuaCFunction = extern "C" fn(LuaState) -> CInt; | ||
|
||
static mut JOIN_SERVER: GlobalDetour<LuaCFunction> = None; | ||
static mut LUAL_LOADBUFFERX: GlobalDetour< extern fn(LuaState, CharBuf, SizeT, CharBuf, CharBuf) -> CInt > = None; | ||
static mut CURRENT_LUA_STATE: Option<LuaState> = None; | ||
static mut CURRENT_SERVER_IP: Option<&'static str> = None; | ||
|
||
// Functions from lua_shared.dll. Will search for the dll relative to the GarrysMod folder, | ||
// Which rust finds it by looking at the program the dll is attached to (Gmod.) | ||
#[derive(WrapperApi)] | ||
struct LuaShared { | ||
// Fetch the function from the dll that lib.rs will output | ||
luaL_loadbufferx: extern fn(state: LuaState, code: CharBuf, size: SizeT, id: CharBuf, mode: CharBuf) -> CInt, | ||
luaL_loadbuffer: extern fn(state: LuaState, code: CharBuf, size: SizeT, id: CharBuf) -> CInt, | ||
luaL_loadstring: extern fn(state: LuaState, code: CharBuf) -> CInt, | ||
lua_pcall: extern fn(state: LuaState, nargs: CInt, nresults: CInt, msgh: CInt) -> CInt, | ||
lua_tolstring: extern fn(state: LuaState, ind: CInt, size: SizeT) -> CharBuf, | ||
lua_settop: extern fn(state: LuaState, ind: CInt), | ||
lua_getfield: extern fn(state: LuaState, idx: CInt, key: CharBuf) -> CInt, | ||
lua_tocfunction: extern fn(state: LuaState, idx: CInt) -> LuaCFunction | ||
} | ||
|
||
// #define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s)) | ||
|
||
|
||
|
||
// lua_pop implementation like the C macro. | ||
// TODO: Make this a rust macro, alongside lua_getglobal. | ||
fn lua_pop(ls: &Container<LuaShared>, state: LuaState, ind: CInt) { | ||
ls.lua_settop(state, -(ind)-1); | ||
} | ||
|
||
// Recursively creates folders based off of a directory from your HOME dir + the lua path made from the currently running file. | ||
// &str garry_dir = Not necessarily a directory, can be anything, but this is the id returned by loadbuffer, loadstring, etc. Ex: "lua/init/bruh.lua" | ||
// &str server_ip = The ip of the server. This will be used to create the folder structure of HOME/sautorun-rs/lua_dumps/IP/... | ||
// Returns Option<File> that was created at the final dir. | ||
fn get_autorun_file(garry_dir: &str, server_ip: &str) -> Option<File> { | ||
if garry_dir.len() > 500 { return None }; // If the server wants to try and attack your fs. | ||
let mut lua_run_path = PathBuf::from(garry_dir); | ||
|
||
let extension = match lua_run_path.extension() { | ||
Some(ext) => { | ||
match ext.to_str() { | ||
Some(ext) if ext=="lua" => "lua", // Using guards check if the extension is lua, else it will fall under _. | ||
_ => "txt" | ||
} | ||
} | ||
None => "txt" | ||
}; | ||
lua_run_path.set_extension(extension); | ||
|
||
let home = match dirs::home_dir() { | ||
Some(path) => { | ||
path.join("sautorun-rs").join("lua_dumps").join(server_ip.replace(":",".")).join(&lua_run_path) | ||
}, | ||
None => { | ||
println!("Couldn't get home directory, for whatever reason."); | ||
return None; // Abort get_autorun_file | ||
} | ||
}; | ||
|
||
match home.parent() { | ||
Some(dirs) => { | ||
match fs::create_dir_all(dirs) { | ||
Err(why) => { | ||
println!("Couldn't create sautorun-rs directories. [{}]", why); | ||
dbg!(dirs); | ||
None | ||
} | ||
Ok(_) => { | ||
match File::create(home) { | ||
Ok(file) => Some(file), | ||
Err(why) => { | ||
println!("Couldn't create sautorun-rs file. [{}]", why); | ||
None | ||
} | ||
} | ||
} | ||
} | ||
} | ||
None => None | ||
} | ||
} | ||
|
||
static mut LUA_SHARED: Option< Container<LuaShared> > = None; | ||
|
||
extern fn h_loadbufferx(state: LuaState, code: CharBuf, size: SizeT, identifier: CharBuf, mode: CharBuf) -> CInt { | ||
let raw_path = &rust_str(identifier)[1 ..]; | ||
let server_ip = unsafe { | ||
CURRENT_LUA_STATE = Some(state); // Hijack the lua state. | ||
CURRENT_SERVER_IP.unwrap_or("unknown_ip") | ||
}; | ||
if let Some(mut file) = get_autorun_file(raw_path, server_ip) { | ||
if let Err(why) = file.write_all( rust_str(code).as_bytes() ) { | ||
println!("Couldn't write to file made from lua path [{}]. {}", raw_path, why); | ||
} | ||
} | ||
if let Some(hook) = unsafe{ &LUAL_LOADBUFFERX } { | ||
return hook.call( state, code, size, identifier, mode ); // Call the original function and return the value. | ||
} | ||
println!("Failed to get LUAL_LOADBUFFERX hook"); | ||
0 | ||
} | ||
|
||
extern fn h_join_server(state: LuaState) -> CInt { | ||
unsafe { | ||
if let Some(lua_shared) = &LUA_SHARED { | ||
let ip = rust_str(lua_shared.lua_tolstring(state, 1, 0)); | ||
println!("Joining Server with IP {}!", ip); | ||
CURRENT_SERVER_IP = Some(ip); | ||
if let Some(hook) = &JOIN_SERVER { | ||
return hook.call(state); | ||
} | ||
} | ||
} | ||
println!("Failed to get JOIN_SERVER hook."); | ||
0 | ||
} | ||
|
||
// Turns a rust &str into a CString | ||
// Could alternatively use std::mem::forget to return *const i8 instead. | ||
fn c_string(s: &str) -> CString { | ||
match CString::new(s) { | ||
Ok(cstring) => cstring, | ||
Err(why) => { | ||
panic!("NulError. {}", why); | ||
} | ||
} | ||
} | ||
|
||
// rust_str(c_string("test").as_ptr()) -> "test" | ||
fn rust_str<'a>(s: CharBuf) -> &'a str { | ||
let cstr = unsafe{ CStr::from_ptr(s) }; | ||
cstr.to_str().unwrap_or("") | ||
} | ||
|
||
fn get_lua_error<'a>(err: i32) -> Option<&'a str> { | ||
match err { | ||
0 => None, // Ok | ||
1 => Some("Yield"), // Yield | ||
2 => Some("Error at runtime"), | ||
3 => Some("Syntax error"), | ||
4 => Some("Ran out of memory"), | ||
5 => Some("Errored during garbage collection"), | ||
6 => Some("Errored inside error message handler"), | ||
_ => unreachable!() | ||
} | ||
} | ||
|
||
// When this DLL is attached | ||
fn detour_funcs() -> Result<(), Box<dyn std::error::Error>> { | ||
let gmod_path = std::env::current_dir()?; // D:\SteamLibrary\steamapps\common\GarrysMod for example. | ||
let bin_path = Path::new(&gmod_path) | ||
.join("bin") | ||
.join("win64"); | ||
|
||
let lua_shared_path = Path::new( &bin_path ) | ||
.join("lua_shared.dll"); | ||
|
||
unsafe { | ||
let lua_shared = Container::<LuaShared>::load(lua_shared_path)?; | ||
// Setting the static vars | ||
|
||
let tour = GenericDetour::new( lua_shared.luaL_loadbufferx, h_loadbufferx )?; | ||
tour.enable()?; | ||
LUAL_LOADBUFFERX = Some(tour); | ||
|
||
LUA_SHARED = Some(lua_shared); | ||
}; | ||
Ok(()) | ||
} | ||
|
||
// "D:\gmod\garrysmod\lua\gmodcrash.lua" | ||
fn runLua(code: &str) { | ||
if let Some(state) = unsafe{ CURRENT_LUA_STATE } { | ||
if let Some(loadbufx_hook) = unsafe { &LUAL_LOADBUFFERX } { | ||
if let Some(ls) = unsafe { &LUA_SHARED } { | ||
loadbufx_hook.call( state, c_string(code).as_ptr(), std::mem::size_of_val(code), c_string("@RunString(Ex)").as_ptr(), c_string("bt").as_ptr() ); | ||
let result = ls.lua_pcall( state, 0, -1, 0 ); | ||
if let Some(err_type) = get_lua_error(result) { | ||
// TODO: Running while true do end causes a stack leak here. | ||
println!("{}: {}", err_type, rust_str(ls.lua_tolstring(state, -1, 0)) ); | ||
lua_pop(ls, state, 1); | ||
} else { | ||
println!("Code ran successfully."); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn handle_input() { | ||
// Loop forever in this thread, since it is separate from Gmod, and take in user input. | ||
loop { | ||
let mut buffer = String::new(); | ||
if let Ok(_) = io::stdin().read_line(&mut buffer) { | ||
if buffer.starts_with("lua_run") { | ||
// Does not work with unicode. Hope you don't somehow get unicode in the console | ||
let slice = &buffer[8 ..].trim_end(); | ||
runLua(slice); | ||
} else if buffer.starts_with("lua_openscript") { | ||
let slice = &buffer[15 ..].trim_end(); | ||
match fs::read_to_string( Path::new(slice) ) { | ||
Err(why) => { | ||
println!("Errored on lua_openscript. [{}]", why); | ||
} | ||
Ok(contents) => { | ||
runLua(&contents); | ||
} | ||
} | ||
} else if buffer.starts_with("init_file_steal") { | ||
// Run this in the menu state. Hopefully will automate this with CreateInterface or something. | ||
if let Some(state) = unsafe{ CURRENT_LUA_STATE } { | ||
if let Some(ls) = unsafe { &LUA_SHARED } { | ||
ls.lua_getfield(state, -10002, c_string("JoinServer").as_ptr() ); // lua_getglobal | ||
unsafe { | ||
match GenericDetour::new( ls.lua_tocfunction(state, -1), h_join_server ) { | ||
Ok(hook) => { | ||
hook.enable().expect("Couldn't enable JoinServer hook"); | ||
JOIN_SERVER = Some(hook); | ||
println!("Successfully hooked JoinServer."); | ||
} | ||
Err(why) => { | ||
println!("Couldn't hook JoinServer. {}", why); | ||
} | ||
} | ||
} | ||
lua_pop(ls, state, 1); | ||
} | ||
}else { | ||
println!("Run this command when you've caused menu state lua to run, hover over a ui button or something!"); | ||
} | ||
} else if buffer.starts_with("help") { | ||
println!("Commands list:"); | ||
println!("lua_run <code> | Runs lua code on the currently loaded lua state. Will print if any errors occur."); | ||
println!("lua_openscript <file_dir> | Runs a lua script located at file_dir, this dir being a full directory, not relative or anything."); | ||
println!("init_file_steal | Hooks JoinServer so that we can get the IP of servers you join. Do this before expecting any files to be dumped. (Will hopefully be automated at some point, only needs to be called once in the menu tho.)"); | ||
println!("help | Prints this out."); | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn entry_point() { | ||
assert!( unsafe { AllocConsole() }==1 ,"Couldn't allocate console."); | ||
println!("<--> Autorun-rs <-->"); | ||
println!("Type [help] for the list of commands."); | ||
if let Err(why) = detour_funcs() { | ||
println!("Failed to detour functions. {}", why); | ||
loop {} // Lock the main thread so you actually see the error, panic would just crash gmod. | ||
} else { | ||
println!("Successfully detoured functions."); | ||
} | ||
handle_input(); | ||
} | ||
|
||
// Windows Only. I'm not going to half-ass cross-operating system support. | ||
#[no_mangle] | ||
pub extern "stdcall" fn DllMain(_: HINSTANCE, reason: u32, _: *mut CVoid) -> WinStructs::BOOL { | ||
match reason { | ||
1 => { | ||
// DLL_PROCESS_ATTACH | ||
std::thread::spawn(entry_point); | ||
} | ||
0 => { | ||
// DLL_PROCESS_DETACH | ||
// Todo | ||
} | ||
_ => () | ||
} | ||
WinStructs::TRUE | ||
} | ||
|
||
// cargo build --release // --target=i686-pc-windows-msvc |