-
Notifications
You must be signed in to change notification settings - Fork 4
#40 - Implement safeCall to spoof the stack #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
yogwoggf
wants to merge
40
commits into
master
Choose a base branch
from
NONE-stack-spoof
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 22 commits
Commits
Show all changes
40 commits
Select commit
Hold shift + click to select a range
5d12e19
Implement frame walking (supports FR2 delta/Lua frames)
yogwoggf afd87b1
Add Debug to TValue and dummy frame marking
yogwoggf 9207e7d
Add initial implementation of safe call
yogwoggf 0eb14d2
Implement frame walking (supports FR2 delta/Lua frames)
yogwoggf e7b7452
Add Debug to TValue and dummy frame marking
yogwoggf c28bcc2
Add initial implementation of safe call
yogwoggf e2b605a
Merge branch 'NONE-stack-spoof' of https://github.com/thevurv/Autorun…
yogwoggf 24945a1
Fix how errors are returned
yogwoggf 07f17dc
Merge branch 'master' into NONE-stack-spoof
yogwoggf 7005bee
Remove debug frames and tighten the safe_call self-check
yogwoggf b271053
Add forwarding pcall to forward errors to GMod
yogwoggf 93cbddd
Merge remote-tracking branch 'origin/master' into NONE-stack-spoof
yogwoggf 9851ea1
Merge master
yogwoggf 7f37321
Fix a Windows-specific loading bug
yogwoggf bbc3646
Merge master
yogwoggf d421d62
Merge branch 'master' into NONE-stack-spoof
yogwoggf b9b9510
Merge branch 'master' into NONE-stack-spoof
yogwoggf d568816
Highly experimental frame stitching implementation
yogwoggf 25b2618
Clean up funcname hook, add proper erroring to reduce chance of detec…
yogwoggf 6054dc0
Refactor errors to match GMod's behavior 1:1
yogwoggf d22d6af
Add some default protections to std
yogwoggf ab506cd
Block TCO from removing an Autorun call frame
yogwoggf 2427363
Wrap assert too
yogwoggf 2f49193
Fix the funcname stitch constant
yogwoggf 1187bba
Account for new closure wrapper
yogwoggf 2b57726
Fix closure wrapper leaking to functions
yogwoggf 4a5b8de
Wrap pcall in a safe call to handle certain situations where the fram…
yogwoggf 1906519
Improve code quality
yogwoggf 5409c4f
Remove unnecessary hook enabling
yogwoggf a2e9acf
Use helper
yogwoggf 9cd20c1
Remove debug code
yogwoggf 056db2a
Add linux signature
yogwoggf 2f63e63
Merge master
yogwoggf 6162b9b
Fix bad merge
yogwoggf 4677541
Add docs
yogwoggf 64e1cbd
Clean up code
yogwoggf 2a4b75a
Improve code
yogwoggf 288ce2b
Remove unused fields on frames and simplify code
yogwoggf 2cadb62
Add get_proto to GCfuncL
yogwoggf eebcd72
Fix major oversight relating to error forwarding
yogwoggf File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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
This file contains hidden or 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,7 @@ | ||
| pub mod lj_debug_funcname; | ||
|
|
||
| pub fn install_auth_hooks() -> anyhow::Result<()> { | ||
| lj_debug_funcname::init()?; | ||
|
|
||
| Ok(()) | ||
| } |
118 changes: 118 additions & 0 deletions
118
packages/autorun-env/src/functions/auth/hooks/lj_debug_funcname.rs
This file contains hidden or 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,118 @@ | ||
| use anyhow::Context; | ||
| use autorun_luajit::{Frame, LJState, TValue}; | ||
|
|
||
| #[cfg(target_os = "windows")] | ||
| pub const TARGET_MODULE: &str = "lua_shared.dll"; | ||
|
|
||
| #[cfg(target_os = "linux")] | ||
| pub const TARGET_MODULE: &str = "lua_shared_client.so"; | ||
|
|
||
| pub const LJ_DEBUG_FUNCNAME_SIG: &str = | ||
yogwoggf marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| "48 89 5c 24 08 48 89 74 24 10 57 48 83 ec 20 48 8b 41 38 49 8b f0 48 83 c0 08 48 8b d9 48 3b d0"; | ||
|
|
||
| pub const STITCHED_AUTORUN_FRAMES: usize = 2; | ||
| pub const MINIMUM_STACK_FRAMES: usize = 4; | ||
|
|
||
| type LjDebugFuncnameFn = unsafe extern "C" fn(state: *mut LJState, frame: *mut TValue, name: *const *const u8) -> *const u8; | ||
|
|
||
| static LJ_DEBUG_FUNCNAME_HOOK: std::sync::OnceLock<retour::GenericDetour<LjDebugFuncnameFn>> = std::sync::OnceLock::new(); | ||
|
|
||
| /// Hook for `lj_debug_funcname` to stitch across Autorun frames, | ||
| /// enabling LuaJIT to properly identify function names in the call stack. | ||
| /// | ||
| /// This is particularly useful for avoiding detection by anti-cheat systems that monitor the call stack for unauthorized code. | ||
| /// Detection can occur if a function name is missing and replaced with `?` when Autorun is in use, as opposed to | ||
| /// a proper function name when Autorun is not present. | ||
| /// | ||
| /// This hook attempts to find the correct frame for `lj_debug_funcname` to use by walking the stack, locating the original frame, | ||
| /// and stitching across the two Autorun frames that are typically present. | ||
| /// | ||
| /// It only activates in the situation where there are at least `MINIMUM_STACK_FRAMES` frames in the stack, and | ||
| /// the original function name was not found (i.e., the first call to the original `lj_debug_funcname` returned null). | ||
| /// | ||
| /// This helps ensure that legitimate function names are returned in any legitimate scenarios, while still providing the stitching functionality | ||
| /// for Autorun-involved calls. Ideally, we could be more precise about when to stitch, but somehow we would need to identify | ||
| /// which frames belong to Autorun specifically, which is non-trivial at this point because we don't have reliable metadata about the frames. | ||
| /// | ||
| /// We also cannot set a flag or anything because we forward errors which longjmps back to LuaJIT code, which means we can not control the ending state | ||
| extern "C" fn lj_debug_funcname_hook(state: *mut LJState, frame: *mut TValue, name: *const *const u8) -> *const u8 { | ||
| let first_ret = unsafe { LJ_DEBUG_FUNCNAME_HOOK.get().unwrap().call(state, frame, name) }; | ||
| if first_ret != std::ptr::null() { | ||
| // Never attempt to stitch if we already have a valid name. | ||
| return first_ret; | ||
| } | ||
|
|
||
| let frames = Frame::walk_stack(state); | ||
| if frames.len() < MINIMUM_STACK_FRAMES { | ||
| return first_ret; | ||
| } | ||
|
|
||
| let mut matched_frame_index: Option<usize> = None; | ||
|
|
||
| for (i, f) in frames.iter().enumerate() { | ||
| if f.tvalue.eq(&frame) { | ||
| matched_frame_index = Some(i); | ||
| } | ||
| } | ||
|
|
||
| let mut target_frame = frame; | ||
| if let Some(matched_index) = matched_frame_index { | ||
| let new_index = matched_index + STITCHED_AUTORUN_FRAMES; | ||
| target_frame = frames.get(new_index).map_or(frame, |f| f.tvalue); | ||
| } | ||
|
|
||
| let ret = unsafe { LJ_DEBUG_FUNCNAME_HOOK.get().unwrap().call(state, target_frame, name) }; | ||
| ret | ||
| } | ||
|
|
||
| pub fn enable() -> anyhow::Result<()> { | ||
| let hook = LJ_DEBUG_FUNCNAME_HOOK | ||
| .get() | ||
| .context("lj_debug_funcname hook is not initialized")?; | ||
| unsafe { | ||
| hook.enable().context("Failed to enable lj_debug_funcname hook")?; | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| pub fn disable() -> anyhow::Result<()> { | ||
| let hook = LJ_DEBUG_FUNCNAME_HOOK | ||
| .get() | ||
| .context("lj_debug_funcname hook is not initialized")?; | ||
| unsafe { | ||
| hook.disable().context("Failed to disable lj_debug_funcname hook")?; | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| pub fn init() -> anyhow::Result<()> { | ||
| if LJ_DEBUG_FUNCNAME_HOOK.get().is_some() { | ||
| return Ok(()); | ||
| } | ||
|
|
||
| let lj_debug_funcname_addr = autorun_scan::scan(autorun_scan::sig_byte_string(LJ_DEBUG_FUNCNAME_SIG), Some(TARGET_MODULE)) | ||
| .context("Failed to find lj_debug_funcname signature")?; | ||
| let lj_debug_funcname_addr = lj_debug_funcname_addr.context("lj_debug_funcname address not found")?; | ||
|
|
||
| autorun_log::info!("Found lj_debug_funcname at address: {:x}", lj_debug_funcname_addr); | ||
yogwoggf marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| unsafe { | ||
| let hook = retour::GenericDetour::<LjDebugFuncnameFn>::new( | ||
| std::mem::transmute(lj_debug_funcname_addr as *const ()), | ||
| lj_debug_funcname_hook, | ||
| ) | ||
| .context("Failed to create lj_debug_funcname detour")?; | ||
|
|
||
| unsafe { | ||
| hook.enable().context("Failed to enable lj_debug_funcname hook")?; | ||
| } | ||
|
|
||
| LJ_DEBUG_FUNCNAME_HOOK | ||
| .set(hook) | ||
| .map_err(|_| anyhow::anyhow!("Failed to set LJ_DEBUG_FUNCNAME_HOOK"))?; | ||
| autorun_log::info!("lj_debug_funcname hook installed successfully"); | ||
yogwoggf marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
This file contains hidden or 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
This file contains hidden or 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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.