Skip to content
This repository was archived by the owner on Nov 12, 2025. It is now read-only.

Commit 940ca63

Browse files
authored
2021 Edition, Stable Rust (#21)
1 parent 7721045 commit 940ca63

File tree

12 files changed

+277
-151
lines changed

12 files changed

+277
-151
lines changed

Cargo.toml

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,38 @@
11
[package]
22
name = "autorun"
3-
version = "0.5.1"
3+
version = "0.6.0"
44
authors = ["Vurv78 <[email protected]>"]
5-
edition = "2018"
5+
edition = "2021"
66

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

99
[lib]
10-
crate-type = ["dylib"]
10+
crate-type = ["cdylib"]
1111

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

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

1717
# Global Mutable Variables
1818
once_cell = "1.8.0"
1919
atomic = "0.5.0"
2020

2121
# Misc
2222
home = "0.5.3"
23-
anyhow = "1.0.42"
23+
anyhow = "1.0.44"
24+
thiserror = "1.0.30"
2425

2526
# Logging
26-
chrono = "0.4.19"
27-
log = "0.4.14"
28-
simplelog = "0.10.0"
27+
chrono = { version = "0.4.19", optional = true }
28+
log = { version = "0.4.14", optional = true }
29+
simplelog = { version = "0.11.0", optional = true }
2930

30-
regex = "1.5.4"
31+
regex = "1.5.4"
32+
33+
[features]
34+
default = ["runner"]
35+
runner = []
36+
37+
# Disabled by default for now as this breaks autorun.
38+
logging = ["chrono", "log", "simplelog"]

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,11 @@ This file runs before every single lua script run on your client from addons and
6262
local script = sautorun.CODE
6363
if script:find("while true do end") then
6464
sautorun.log("Found an evil script!")
65-
return true -- Exit from here & don't run the script
65+
-- Run our modified script that will replace all ``while true do end`` with ``while false do end``. 😎
66+
67+
return string.Replace(script, "while true do end", "while false do end")
68+
69+
-- OR: return true to not run the script at all.
6670
end
6771
```
6872
__autorun.lua__
@@ -73,10 +77,12 @@ sautorun.log( "Connected to server " .. sautorun.IP, DEBUG )
7377
```
7478

7579
## Logging
76-
Autorun automatically writes logs to a log file whenever you boot up a game for your security and for easy debugging.
77-
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.
80+
Autorun features logging under the ``logging`` feature. You need to build it yourself to enable this.
81+
It will be re-enabled in the future when it is fixed.
82+
83+
> Autorun automatically writes logs to a log file whenever you boot up a game for your security and for easy debugging.
84+
> 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.
7885
7986
## Building
8087
1. [Setup Rust & Cargo](https://www.rust-lang.org/learn/get-started)
81-
2. Use ``build_win_32.bat`` or ``build_win_64.bat``.
82-
**This requires Nightly** (in order to use ``thiscall`` and ``static_detour!``)
88+
2. Use ``build_win_32.bat`` or ``build_win_64.bat``.

build_win_32.bat

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
@echo off
2-
rustup target add i686-pc-windows-msvc
3-
cargo build --target=i686-pc-windows-msvc
4-
move %cd%\target\i686-pc-windows-msvc\debug\Autorun.dll %cd%\gmsv_autorun_win32.dll
2+
set file_name=autorun.dll
3+
4+
set target=i686-pc-windows-msvc
5+
set target_dir=%cd%\target\%target%\release
6+
set out=%cd%\gmsv_autorun_win32.dll
7+
8+
rustup target add %target%
9+
cargo build --release --target=%target%
10+
11+
move %target_dir%\%file_name% %out%
512
pause

build_win_64.bat

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
@echo off
2-
cargo build
3-
move %cd%\target\debug\Autorun.dll %cd%\gmsv_autorun_win64.dll
2+
set file_name=autorun.dll
3+
4+
set target=x86_64-pc-windows-msvc
5+
set target_dir=%cd%\target\%target%\release
6+
set out=%cd%\gmsv_autorun_win64.dll
7+
8+
rustup target add %target%
9+
cargo build --release --target=%target%
10+
11+
move %target_dir%\%file_name% %out%
412
pause

src/detours/lazy.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#[macro_export]
2+
macro_rules! lazy_detour {
3+
// Lazy Path
4+
( $vis:vis static $name:ident : $t:ty = ($target:expr, $tour:expr) ; $($rest:tt)* ) => {
5+
$vis static $name: once_cell::sync::Lazy< detour::GenericDetour< $t > > = once_cell::sync::Lazy::new(|| unsafe {
6+
match detour::GenericDetour::new( $target, $tour ) {
7+
Ok(b) => {
8+
b.enable().expect( concat!("Failed to enable detour '", stringify!($name), "'") );
9+
b
10+
},
11+
Err(why) => panic!( concat!("Failed to create hook '", stringify!($name), "' {}"), why)
12+
}
13+
});
14+
lazy_detour!( $($rest)* );
15+
};
16+
// OnceCell Path
17+
( $vis:vis static $name:ident : $t:ty ; $($rest:tt)* ) => {
18+
$vis static $name: once_cell::sync::OnceCell<detour::GenericDetour<$t>> = once_cell::sync::OnceCell::new();
19+
lazy_detour!( $($rest)* );
20+
};
21+
() => ();
22+
}

src/detours/mod.rs

Lines changed: 70 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,50 @@
11
use std::{fs, io::prelude::*, sync::atomic::Ordering};
22

33
use crate::sys::{
4-
util::{self, getAutorunHandle, getClientState, setClientState},
4+
util::{getAutorunHandle, setClientState},
55
runlua::runLuaEnv, statics::*
66
};
77

88
use rglua::{
99
lua_shared::{self, *},
1010
types::*,
11-
rstring,
12-
interface::IPanel
11+
rstring
1312
};
1413

15-
use detour::static_detour;
16-
1714
const LUA_BOOL: i32 = rglua::globals::Lua::Type::Bool as i32;
1815
const LUA_STRING: i32 = rglua::globals::Lua::Type::String as i32;
1916

20-
static_detour! {
21-
pub static luaL_newstate_h: extern "C" fn() -> LuaState;
22-
pub static luaL_loadbufferx_h: extern "C" fn(LuaState, *const i8, SizeT, *const i8, *const i8) -> CInt;
23-
pub static joinserver_h: extern "C" fn(LuaState) -> CInt;
24-
pub static paint_traverse_h: extern "thiscall" fn(&'static IPanel, usize, bool, bool);
17+
#[macro_use]
18+
pub mod lazy;
19+
20+
// Make our own static detours because detours.rs is lame and locked theirs behind nightly. :)
21+
lazy_detour! {
22+
pub static LUAL_NEWSTATE_H: extern "C" fn() -> LuaState = (*lua_shared::luaL_newstate, luaL_newstate);
23+
pub static LUAL_LOADBUFFERX_H: extern "C" fn(LuaState, *const i8, SizeT, *const i8, *const i8) -> CInt = (*lua_shared::luaL_loadbufferx, luaL_loadbufferx);
24+
pub static JOINSERVER_H: extern "C" fn(LuaState) -> CInt;
25+
}
26+
27+
#[cfg(feature = "runner")]
28+
use rglua::interface::IPanel;
29+
30+
#[cfg(feature = "runner")]
31+
lazy_detour! {
32+
static PAINT_TRAVERSE_H: extern "fastcall" fn(&'static IPanel, usize, bool, bool);
2533
}
2634

27-
fn luaL_newstate() -> LuaState {
28-
let state = luaL_newstate_h.call();
35+
extern "C" fn luaL_newstate() -> LuaState {
36+
let state = LUAL_NEWSTATE_H.call();
2937
debug!("Got client state through luaL_newstate");
3038
setClientState(state);
3139
state
3240
}
3341

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

4150
// 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.
@@ -44,18 +53,16 @@ fn luaL_loadbufferx(state: LuaState, mut code: *const i8, mut size: SizeT, ident
4453
let server_ip = CURRENT_SERVER_IP.load( Ordering::Relaxed );
4554

4655
let mut do_run = true;
47-
if raw_path == "lua/includes/init.lua" {
48-
if HAS_AUTORAN.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed).is_ok() {
49-
// This will only run once when HAS_AUTORAN is false, setting it to true.
50-
// Will be reset by JoinServer.
51-
if let Ok(script) = fs::read_to_string(&*AUTORUN_SCRIPT_PATH) {
52-
// Try to run here
53-
if let Err(why) = runLuaEnv(&script, identifier, code, server_ip, true) {
54-
error!("{}", why);
55-
}
56-
} else {
57-
error!( "Couldn't read your autorun script file at {}/{}", SAUTORUN_DIR.display(), AUTORUN_SCRIPT_PATH.display() );
56+
if raw_path == "lua/includes/init.lua" && HAS_AUTORAN.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed).is_ok() {
57+
// This will only run once when HAS_AUTORAN is false, setting it to true.
58+
// Will be reset by JoinServer.
59+
if let Ok(script) = fs::read_to_string(&*AUTORUN_SCRIPT_PATH) {
60+
// Try to run here
61+
if let Err(why) = runLuaEnv(&script, identifier, code, server_ip, true) {
62+
error!("{}", why);
5863
}
64+
} else {
65+
error!( "Couldn't read your autorun script file at [{}]", AUTORUN_SCRIPT_PATH.display() );
5966
}
6067
}
6168

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

9299
if do_run {
93100
// Call the original function and return the value.
94-
return luaL_loadbufferx_h.call( state, code, size, identifier, mode );
101+
return LUAL_LOADBUFFERX_H.call( state, code, size, identifier, mode );
95102
}
96103
0
97104
}
98105

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

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

108-
joinserver_h.call(state)
115+
JOINSERVER_H.get().unwrap().call(state)
109116
}
110117

111-
fn paint_traverse(this: &'static IPanel, panel_id: usize, force_repaint: bool, force_allow: bool) {
112-
paint_traverse_h.call(this, panel_id, force_repaint, force_allow);
118+
#[cfg(feature = "runner")]
119+
extern "fastcall" fn paint_traverse(this: &'static IPanel, panel_id: usize, force_repaint: bool, force_allow: bool) {
120+
use crate::sys::util::{self, getClientState};
121+
122+
PAINT_TRAVERSE_H.get().unwrap().call(this, panel_id, force_repaint, force_allow);
113123

114124
let script_queue = &mut *LUA_SCRIPTS
115125
.lock()
116126
.unwrap();
117127

118-
if script_queue.len() > 0 {
128+
if !script_queue.is_empty() {
119129
let (realm, script) = script_queue.remove(0);
120130

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

126-
if state == std::ptr::null_mut() { return; }
136+
if state.is_null() { return; }
127137

128138
match util::lua_dostring(state, &script) {
129139
Err(why) => {
130140
error!("{}", why);
131141
},
132142
Ok(_) => {
133-
info!("Code [#{}] ran successfully.", script.len())
143+
info!("Script of len #{} ran successfully.", script.len())
134144
}
135145
}
136146
}
137147
}
138148

139-
pub unsafe fn init() -> Result<(), detour::Error> {
140-
luaL_loadbufferx_h
141-
.initialize(*lua_shared::luaL_loadbufferx, luaL_loadbufferx)?
142-
.enable()?;
143-
144-
luaL_newstate_h
145-
.initialize(*lua_shared::luaL_newstate, luaL_newstate)?
146-
.enable()?;
147-
149+
#[cfg(feature = "runner")]
150+
unsafe fn init_paint_traverse() -> Result<(), detour::Error> {
148151
use rglua::interface::*;
149152

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

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

155-
type PaintTraverseFn = extern "thiscall" fn(&'static IPanel, usize, bool, bool);
158+
type PaintTraverseFn = extern "fastcall" fn(&'static IPanel, usize, bool, bool);
156159
// Get painttraverse raw function object to detour.
157160
let painttraverse: PaintTraverseFn = std::mem::transmute(
158161
(panel_interface.vtable as *mut *mut CVoid)
159162
.offset(41)
160163
.read()
161164
);
162165

163-
paint_traverse_h
164-
.initialize( painttraverse, paint_traverse )?
165-
.enable()?;
166+
let detour = detour::GenericDetour::new(painttraverse, paint_traverse)?;
167+
168+
PAINT_TRAVERSE_H.set(detour);
169+
PAINT_TRAVERSE_H.get().unwrap().enable()?;
170+
171+
Ok(())
172+
}
173+
174+
pub unsafe fn init() -> Result<(), detour::Error> {
175+
use once_cell::sync::Lazy;
176+
177+
Lazy::force(&LUAL_LOADBUFFERX_H);
178+
Lazy::force(&LUAL_NEWSTATE_H);
179+
180+
#[cfg(feature = "runner")]
181+
init_paint_traverse()?;
166182

167183
Ok(())
168184
}
169185

170186
pub unsafe fn cleanup() -> Result<(), detour::Error>{
171-
luaL_loadbufferx_h.disable()?;
172-
luaL_newstate_h.disable()?;
173-
joinserver_h.disable()?;
174-
paint_traverse_h.disable()?;
187+
LUAL_LOADBUFFERX_H.disable()?;
188+
LUAL_NEWSTATE_H.disable()?;
189+
JOINSERVER_H.get().unwrap().disable()?;
190+
191+
#[cfg(feature = "runner")]
192+
PAINT_TRAVERSE_H.get().unwrap().disable()?;
175193

176194
Ok(())
177195
}

src/input.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,9 @@ pub(crate) fn try_process_input() -> anyhow::Result<()> {
66
let mut buffer = String::new();
77

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

12-
debug!("Command used: [{}], rest [{}]", word, rest);
13-
1412
match word {
1513
"lua_run_cl" => if let Err(why) = runLua(REALM_CLIENT, rest.to_owned()) {
1614
error!("{}", why);
@@ -29,9 +27,8 @@ pub(crate) fn try_process_input() -> anyhow::Result<()> {
2927

3028
"lua_openscript_menu" => match std::fs::read_to_string( Path::new( rest ) ) {
3129
Err(why) => error!("Errored on lua_openscript. [{}]", why),
32-
Ok(contents) => match runLua( REALM_MENU, contents ) {
33-
Err(why) => error!("Errored on lua_openscript. {}", why),
34-
_ => ()
30+
Ok(contents) => if let Err(why) = runLua( REALM_MENU, contents ) {
31+
error!("Errored on lua_openscript. {}", why);
3532
}
3633
},
3734

0 commit comments

Comments
 (0)