Skip to content

Commit

Permalink
2021 Edition, Stable Rust (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vurv78 authored Nov 24, 2021
1 parent 7721045 commit 940ca63
Show file tree
Hide file tree
Showing 12 changed files with 277 additions and 151 deletions.
26 changes: 17 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
[package]
name = "autorun"
version = "0.5.1"
version = "0.6.0"
authors = ["Vurv78 <[email protected]>"]
edition = "2018"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
crate-type = ["dylib"]
crate-type = ["cdylib"]

[dependencies]
rglua = { git = "https://github.com/Vurv78/rglua" }

detour = "0.8.1"
detour = { version = "0.8.1", default-features = false }

# Global Mutable Variables
once_cell = "1.8.0"
atomic = "0.5.0"

# Misc
home = "0.5.3"
anyhow = "1.0.42"
anyhow = "1.0.44"
thiserror = "1.0.30"

# Logging
chrono = "0.4.19"
log = "0.4.14"
simplelog = "0.10.0"
chrono = { version = "0.4.19", optional = true }
log = { version = "0.4.14", optional = true }
simplelog = { version = "0.11.0", optional = true }

regex = "1.5.4"
regex = "1.5.4"

[features]
default = ["runner"]
runner = []

# Disabled by default for now as this breaks autorun.
logging = ["chrono", "log", "simplelog"]
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ This file runs before every single lua script run on your client from addons and
local script = sautorun.CODE
if script:find("while true do end") then
sautorun.log("Found an evil script!")
return true -- Exit from here & don't run the script
-- Run our modified script that will replace all ``while true do end`` with ``while false do end``. 😎

return string.Replace(script, "while true do end", "while false do end")

-- OR: return true to not run the script at all.
end
```
__autorun.lua__
Expand All @@ -73,10 +77,12 @@ sautorun.log( "Connected to server " .. sautorun.IP, DEBUG )
```

## Logging
Autorun automatically writes logs to a log file whenever you boot up a game for your security and for easy debugging.
Check the sautorun-rs/logs directory for crash dumps & logs if you use something like [Safety](https://github.com/Vurv78/Safety) to log HTTP requests, etc.
Autorun features logging under the ``logging`` feature. You need to build it yourself to enable this.
It will be re-enabled in the future when it is fixed.

> Autorun automatically writes logs to a log file whenever you boot up a game for your security and for easy debugging.
> Check the sautorun-rs/logs directory for crash dumps & logs if you use something like [Safety](https://github.com/Vurv78/Safety) to log HTTP requests, etc.
## Building
1. [Setup Rust & Cargo](https://www.rust-lang.org/learn/get-started)
2. Use ``build_win_32.bat`` or ``build_win_64.bat``.
**This requires Nightly** (in order to use ``thiscall`` and ``static_detour!``)
2. Use ``build_win_32.bat`` or ``build_win_64.bat``.
13 changes: 10 additions & 3 deletions build_win_32.bat
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
@echo off
rustup target add i686-pc-windows-msvc
cargo build --target=i686-pc-windows-msvc
move %cd%\target\i686-pc-windows-msvc\debug\Autorun.dll %cd%\gmsv_autorun_win32.dll
set file_name=autorun.dll

set target=i686-pc-windows-msvc
set target_dir=%cd%\target\%target%\release
set out=%cd%\gmsv_autorun_win32.dll

rustup target add %target%
cargo build --release --target=%target%

move %target_dir%\%file_name% %out%
pause
12 changes: 10 additions & 2 deletions build_win_64.bat
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
@echo off
cargo build
move %cd%\target\debug\Autorun.dll %cd%\gmsv_autorun_win64.dll
set file_name=autorun.dll

set target=x86_64-pc-windows-msvc
set target_dir=%cd%\target\%target%\release
set out=%cd%\gmsv_autorun_win64.dll

rustup target add %target%
cargo build --release --target=%target%

move %target_dir%\%file_name% %out%
pause
22 changes: 22 additions & 0 deletions src/detours/lazy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#[macro_export]
macro_rules! lazy_detour {
// Lazy Path
( $vis:vis static $name:ident : $t:ty = ($target:expr, $tour:expr) ; $($rest:tt)* ) => {
$vis static $name: once_cell::sync::Lazy< detour::GenericDetour< $t > > = once_cell::sync::Lazy::new(|| unsafe {
match detour::GenericDetour::new( $target, $tour ) {
Ok(b) => {
b.enable().expect( concat!("Failed to enable detour '", stringify!($name), "'") );
b
},
Err(why) => panic!( concat!("Failed to create hook '", stringify!($name), "' {}"), why)
}
});
lazy_detour!( $($rest)* );
};
// OnceCell Path
( $vis:vis static $name:ident : $t:ty ; $($rest:tt)* ) => {
$vis static $name: once_cell::sync::OnceCell<detour::GenericDetour<$t>> = once_cell::sync::OnceCell::new();
lazy_detour!( $($rest)* );
};
() => ();
}
122 changes: 70 additions & 52 deletions src/detours/mod.rs
Original file line number Diff line number Diff line change
@@ -1,41 +1,50 @@
use std::{fs, io::prelude::*, sync::atomic::Ordering};

use crate::sys::{
util::{self, getAutorunHandle, getClientState, setClientState},
util::{getAutorunHandle, setClientState},
runlua::runLuaEnv, statics::*
};

use rglua::{
lua_shared::{self, *},
types::*,
rstring,
interface::IPanel
rstring
};

use detour::static_detour;

const LUA_BOOL: i32 = rglua::globals::Lua::Type::Bool as i32;
const LUA_STRING: i32 = rglua::globals::Lua::Type::String as i32;

static_detour! {
pub static luaL_newstate_h: extern "C" fn() -> LuaState;
pub static luaL_loadbufferx_h: extern "C" fn(LuaState, *const i8, SizeT, *const i8, *const i8) -> CInt;
pub static joinserver_h: extern "C" fn(LuaState) -> CInt;
pub static paint_traverse_h: extern "thiscall" fn(&'static IPanel, usize, bool, bool);
#[macro_use]
pub mod lazy;

// Make our own static detours because detours.rs is lame and locked theirs behind nightly. :)
lazy_detour! {
pub static LUAL_NEWSTATE_H: extern "C" fn() -> LuaState = (*lua_shared::luaL_newstate, luaL_newstate);
pub static LUAL_LOADBUFFERX_H: extern "C" fn(LuaState, *const i8, SizeT, *const i8, *const i8) -> CInt = (*lua_shared::luaL_loadbufferx, luaL_loadbufferx);
pub static JOINSERVER_H: extern "C" fn(LuaState) -> CInt;
}

#[cfg(feature = "runner")]
use rglua::interface::IPanel;

#[cfg(feature = "runner")]
lazy_detour! {
static PAINT_TRAVERSE_H: extern "fastcall" fn(&'static IPanel, usize, bool, bool);
}

fn luaL_newstate() -> LuaState {
let state = luaL_newstate_h.call();
extern "C" fn luaL_newstate() -> LuaState {
let state = LUAL_NEWSTATE_H.call();
debug!("Got client state through luaL_newstate");
setClientState(state);
state
}

fn luaL_loadbufferx(state: LuaState, mut code: *const i8, mut size: SizeT, identifier: *const i8, mode: *const i8) -> CInt {
extern "C" fn luaL_loadbufferx(state: LuaState, mut code: *const i8, mut size: SizeT, identifier: *const i8, mode: *const i8) -> CInt {
use crate::sys::util::initMenuState;
if MENU_STATE.get().is_none() {
initMenuState(state)
.expect("Couldn't initialize menu state");
if let Err(why) = initMenuState(state) {
error!("Couldn't initialize menu state. {}", why);
}
}

// Todo: Check if you're in menu state (Not by checking MENU_DLL because that can be modified by lua) and if so, don't dump files.
Expand All @@ -44,18 +53,16 @@ fn luaL_loadbufferx(state: LuaState, mut code: *const i8, mut size: SizeT, ident
let server_ip = CURRENT_SERVER_IP.load( Ordering::Relaxed );

let mut do_run = true;
if raw_path == "lua/includes/init.lua" {
if HAS_AUTORAN.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed).is_ok() {
// This will only run once when HAS_AUTORAN is false, setting it to true.
// Will be reset by JoinServer.
if let Ok(script) = fs::read_to_string(&*AUTORUN_SCRIPT_PATH) {
// Try to run here
if let Err(why) = runLuaEnv(&script, identifier, code, server_ip, true) {
error!("{}", why);
}
} else {
error!( "Couldn't read your autorun script file at {}/{}", SAUTORUN_DIR.display(), AUTORUN_SCRIPT_PATH.display() );
if raw_path == "lua/includes/init.lua" && HAS_AUTORAN.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed).is_ok() {
// This will only run once when HAS_AUTORAN is false, setting it to true.
// Will be reset by JoinServer.
if let Ok(script) = fs::read_to_string(&*AUTORUN_SCRIPT_PATH) {
// Try to run here
if let Err(why) = runLuaEnv(&script, identifier, code, server_ip, true) {
error!("{}", why);
}
} else {
error!( "Couldn't read your autorun script file at [{}]", AUTORUN_SCRIPT_PATH.display() );
}
}

Expand Down Expand Up @@ -91,87 +98,98 @@ fn luaL_loadbufferx(state: LuaState, mut code: *const i8, mut size: SizeT, ident

if do_run {
// Call the original function and return the value.
return luaL_loadbufferx_h.call( state, code, size, identifier, mode );
return LUAL_LOADBUFFERX_H.call( state, code, size, identifier, mode );
}
0
}

// Since the first lua state will always be the menu state, just keep a variable for whether joinserver has been hooked or not,
// If not, then hook it.
pub fn joinserver(state: LuaState) -> CInt {
let ip = rstring!( lua_tolstring(state, 1, 0) );
pub extern "C" fn joinserver(state: LuaState) -> CInt {
let ip = rstring!(lua_tolstring(state, 1, 0));
info!("Joining Server with IP {}!", ip);

CURRENT_SERVER_IP.store(ip, Ordering::Relaxed); // Set the IP so we know where to write files in loadbufferx.
HAS_AUTORAN.store(false, Ordering::Relaxed);

joinserver_h.call(state)
JOINSERVER_H.get().unwrap().call(state)
}

fn paint_traverse(this: &'static IPanel, panel_id: usize, force_repaint: bool, force_allow: bool) {
paint_traverse_h.call(this, panel_id, force_repaint, force_allow);
#[cfg(feature = "runner")]
extern "fastcall" fn paint_traverse(this: &'static IPanel, panel_id: usize, force_repaint: bool, force_allow: bool) {
use crate::sys::util::{self, getClientState};

PAINT_TRAVERSE_H.get().unwrap().call(this, panel_id, force_repaint, force_allow);

let script_queue = &mut *LUA_SCRIPTS
.lock()
.unwrap();

if script_queue.len() > 0 {
if !script_queue.is_empty() {
let (realm, script) = script_queue.remove(0);

let state = match realm {
REALM_MENU => MENU_STATE.get().unwrap().load(Ordering::Acquire), // Code will never get into the queue without a menu state already existing.
REALM_CLIENT => getClientState()
};

if state == std::ptr::null_mut() { return; }
if state.is_null() { return; }

match util::lua_dostring(state, &script) {
Err(why) => {
error!("{}", why);
},
Ok(_) => {
info!("Code [#{}] ran successfully.", script.len())
info!("Script of len #{} ran successfully.", script.len())
}
}
}
}

pub unsafe fn init() -> Result<(), detour::Error> {
luaL_loadbufferx_h
.initialize(*lua_shared::luaL_loadbufferx, luaL_loadbufferx)?
.enable()?;

luaL_newstate_h
.initialize(*lua_shared::luaL_newstate, luaL_newstate)?
.enable()?;

#[cfg(feature = "runner")]
unsafe fn init_paint_traverse() -> Result<(), detour::Error> {
use rglua::interface::*;

let vgui_interface = get_from_interface( "VGUI_Panel009", get_interface_handle("vgui2.dll").unwrap() )
.unwrap() as *mut IPanel;

let panel_interface = vgui_interface.as_ref().unwrap();

type PaintTraverseFn = extern "thiscall" fn(&'static IPanel, usize, bool, bool);
type PaintTraverseFn = extern "fastcall" fn(&'static IPanel, usize, bool, bool);
// Get painttraverse raw function object to detour.
let painttraverse: PaintTraverseFn = std::mem::transmute(
(panel_interface.vtable as *mut *mut CVoid)
.offset(41)
.read()
);

paint_traverse_h
.initialize( painttraverse, paint_traverse )?
.enable()?;
let detour = detour::GenericDetour::new(painttraverse, paint_traverse)?;

PAINT_TRAVERSE_H.set(detour);
PAINT_TRAVERSE_H.get().unwrap().enable()?;

Ok(())
}

pub unsafe fn init() -> Result<(), detour::Error> {
use once_cell::sync::Lazy;

Lazy::force(&LUAL_LOADBUFFERX_H);
Lazy::force(&LUAL_NEWSTATE_H);

#[cfg(feature = "runner")]
init_paint_traverse()?;

Ok(())
}

pub unsafe fn cleanup() -> Result<(), detour::Error>{
luaL_loadbufferx_h.disable()?;
luaL_newstate_h.disable()?;
joinserver_h.disable()?;
paint_traverse_h.disable()?;
LUAL_LOADBUFFERX_H.disable()?;
LUAL_NEWSTATE_H.disable()?;
JOINSERVER_H.get().unwrap().disable()?;

#[cfg(feature = "runner")]
PAINT_TRAVERSE_H.get().unwrap().disable()?;

Ok(())
}
9 changes: 3 additions & 6 deletions src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ pub(crate) fn try_process_input() -> anyhow::Result<()> {
let mut buffer = String::new();

std::io::stdin().read_line(&mut buffer)?;
let (word, rest) = buffer.split_once(' ').unwrap_or( (&buffer.trim_end(), "") );
let (word, rest) = buffer.split_once(' ').unwrap_or( (buffer.trim_end(), "") );
let rest_trim = rest.trim_end();

debug!("Command used: [{}], rest [{}]", word, rest);

match word {
"lua_run_cl" => if let Err(why) = runLua(REALM_CLIENT, rest.to_owned()) {
error!("{}", why);
Expand All @@ -29,9 +27,8 @@ pub(crate) fn try_process_input() -> anyhow::Result<()> {

"lua_openscript_menu" => match std::fs::read_to_string( Path::new( rest ) ) {
Err(why) => error!("Errored on lua_openscript. [{}]", why),
Ok(contents) => match runLua( REALM_MENU, contents ) {
Err(why) => error!("Errored on lua_openscript. {}", why),
_ => ()
Ok(contents) => if let Err(why) = runLua( REALM_MENU, contents ) {
error!("Errored on lua_openscript. {}", why);
}
},

Expand Down
Loading

0 comments on commit 940ca63

Please sign in to comment.