commit db66b68a307cd9f734909f5b16afb706838438f7 Author: FIFIFiSKA Date: Sun May 10 15:14:49 2026 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..48bd0f6 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,101 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahc-rs" +version = "0.1.0" +dependencies = [ + "ctor", + "gl", + "libc", +] + +[[package]] +name = "ctor" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "gl" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94edab108827d67608095e269cf862e60d920f144a5026d3dbcfd8b877fb404" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4ef4750 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "ahc-rs" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +libc = "0.2" +ctor = "0.2" +gl = "0.14" diff --git a/load.sh b/load.sh new file mode 100755 index 0000000..efefb95 --- /dev/null +++ b/load.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +# Путь к скомпилированной библиотеке +LIB_NAME="libahc_rs.so" +LIB_PATH="$(pwd)/target/i686-unknown-linux-gnu/release/$LIB_NAME" + +if [ ! -f "$LIB_PATH" ]; then + echo "[-] Ошибка: Файл $LIB_PATH не найден." + echo "[!] Сначала скомпилируйте проект: cargo build --release --target i686-unknown-linux-gnu" + exit 1 +fi + +# Поиск процесса игры +GAME_PID=$(pidof hl_linux) + +if [ -z "$GAME_PID" ]; then + echo "[+] Игра не запущена. Запуск через LD_PRELOAD..." + echo "[!] Убедитесь, что вы находитесь в папке с игрой или укажите полный путь." + + # Пример запуска (путь может отличаться в зависимости от установки Steam) + # LD_PRELOAD="$LIB_PATH" ./hl.sh -game cstrike + + echo "Введите команду для запуска CS 1.6 (например, ./hl.sh -game cstrike):" + read -r RUN_CMD + LD_PRELOAD="$LIB_PATH" eval "$RUN_CMD" +else + # Копируем библиотеку в /tmp с уникальным именем для форсированной перезагрузки + TIMESTAMP=$(date +%s) + TMP_LIB="/tmp/libahc_rs_$TIMESTAMP.so" + cp "$LIB_PATH" "$TMP_LIB" + chmod 777 "$TMP_LIB" + + echo "[+] Инъекция: $TMP_LIB" + + # Создаем временный файл команд для gdb + GDB_CMD=$(mktemp) + echo "attach $GAME_PID" > "$GDB_CMD" + echo "call (void*)dlopen(\"$TMP_LIB\", 2)" >> "$GDB_CMD" + echo "detach" >> "$GDB_CMD" + echo "quit" >> "$GDB_CMD" + + # Запускаем gdb + sudo gdb -batch -x "$GDB_CMD" + + rm "$GDB_CMD" + + if [ $? -eq 0 ]; then + echo "[+] Sucsess injected" + else + echo "[-] Ошибка при инъекции. Проверьте, установлен ли gdb и есть ли права sudo." + fi +fi diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..bbacb88 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,166 @@ +mod sdk; + +use sdk::*; +use std::ffi::{CString, c_char, c_void}; +use std::ptr; +use std::sync::atomic::{AtomicPtr, Ordering}; +use libc::{dlopen, dlsym, dlclose, RTLD_LAZY, RTLD_NOLOAD, mprotect, PROT_READ, PROT_WRITE}; +use std::fs::OpenOptions; +use std::io::Write; + +static ENGINE: AtomicPtr = AtomicPtr::new(ptr::null_mut()); +static CLIENT: AtomicPtr = AtomicPtr::new(ptr::null_mut()); +static PLAYER_MOVE: AtomicPtr = AtomicPtr::new(ptr::null_mut()); + +static mut ORIG_CLIENT: Option = None; +static mut FILL_RGBA_BOX: Option = None; + +struct Settings { + show_menu: bool, + bhop: bool, + chams: bool, +} + +static mut SETTINGS: Settings = Settings { + show_menu: false, + bhop: true, + chams: true, +}; + +fn log(msg: &str) { + if let Ok(mut file) = OpenOptions::new().create(true).append(true).open("/tmp/basturma.log") { + let _ = writeln!(file, "[DEBUG] {}", msg); + } +} + +#[ctor::ctor] +fn init() { + log("Library injected. Starting search thread..."); + std::thread::spawn(|| { + unsafe { + let mut hw = ptr::null_mut(); + // Список возможных имен движка в Linux + let libs = [ + b"hw.so\0".as_ptr() as *const c_char, + b"engine_i486.so\0".as_ptr() as *const c_char, + b"engine.so\0".as_ptr() as *const c_char, + b"./hw.so\0".as_ptr() as *const c_char, + b"hw_i386.so\0".as_ptr() as *const c_char, + ]; + + log("Waiting for engine library to be loaded..."); + for i in 0..500 { // Ждем до 50 секунд + for &lib in libs.iter() { + hw = dlopen(lib, RTLD_LAZY | RTLD_NOLOAD); + if !hw.is_null() { + let lib_name = std::ffi::CStr::from_ptr(lib).to_string_lossy(); + log(&format!("Success! Found engine: {}", lib_name)); + break; + } + } + if !hw.is_null() { break; } + std::thread::sleep(std::time::Duration::from_millis(100)); + } + + if hw.is_null() { + log("FATAL: Engine library not found after timeout!"); + return; + } + + setup(hw); + } + }); +} + +unsafe fn setup(hw: *mut c_void) { + log("Starting setup and symbol lookups..."); + + FILL_RGBA_BOX = std::mem::transmute(dlsym(hw, b"Draw_FillRGBA\0".as_ptr() as *const c_char)); + if FILL_RGBA_BOX.is_some() { log("Found Draw_FillRGBA"); } + + let engine_ptr = dlsym(hw, b"cl_enginefuncs\0".as_ptr() as *const c_char) as *mut Engine; + if !engine_ptr.is_null() { + ENGINE.store(engine_ptr, Ordering::SeqCst); + log("Found cl_enginefuncs"); + } else { + log("WARNING: cl_enginefuncs not found via dlsym"); + } + + let client_ptr = dlsym(hw, b"cl_funcs\0".as_ptr() as *const c_char) as *mut Client; + if !client_ptr.is_null() { + CLIENT.store(client_ptr, Ordering::SeqCst); + ORIG_CLIENT = Some(*client_ptr); + + let addr = client_ptr as usize; + let page_size = libc::sysconf(libc::_SC_PAGESIZE) as usize; + let page_start = addr & !(page_size - 1); + mprotect(page_start as *mut c_void, page_size, PROT_READ | PROT_WRITE); + + (*client_ptr).createmove = hooked_createmove; + (*client_ptr).hudredraw = hooked_hudredraw; + (*client_ptr).hudkeyevent = hooked_hudkeyevent; + log("Hooks applied: CreateMove, HUD_Redraw, Key_Event"); + } + + let pmove_ptr_ptr = dlsym(hw, b"pmove\0".as_ptr() as *const c_char) as *mut *mut PlayerMove; + if !pmove_ptr_ptr.is_null() { + PLAYER_MOVE.store(*pmove_ptr_ptr, Ordering::SeqCst); + log("Found pmove"); + } + + dlclose(hw); + log("Setup complete!"); +} + +extern "C" fn hooked_hudkeyevent(eventcode: i32, keynum: i32, binding: *const c_char) -> i32 { + unsafe { + if eventcode == 1 && keynum == 146 { // INSERT + SETTINGS.show_menu = !SETTINGS.show_menu; + return 0; + } + + if SETTINGS.show_menu && eventcode == 1 { + match keynum { + 135 => { SETTINGS.bhop = !SETTINGS.bhop; return 0; } // F1 + 136 => { SETTINGS.chams = !SETTINGS.chams; return 0; } // F2 + _ => {} + } + } + + if let Some(orig) = ORIG_CLIENT { + (orig.hudkeyevent)(eventcode, keynum, binding) + } else { 1 } + } +} + +extern "C" fn hooked_hudredraw(time: f32, intermission: i32) -> i32 { + unsafe { + if SETTINGS.show_menu { + if let Some(fill_box) = FILL_RGBA_BOX { + fill_box(100, 100, 200, 100, 0, 0, 0, 200); + let bhop_c = if SETTINGS.bhop { (0, 255, 0) } else { (255, 0, 0) }; + fill_box(110, 130, 15, 15, bhop_c.0, bhop_c.1, bhop_c.2, 255); + let chams_c = if SETTINGS.chams { (0, 255, 0) } else { (255, 0, 0) }; + fill_box(110, 160, 15, 15, chams_c.0, chams_c.1, chams_c.2, 255); + } + } + if let Some(orig) = ORIG_CLIENT { (orig.hudredraw)(time, intermission) } else { 0 } + } +} + +extern "C" fn hooked_createmove(frametime: f32, cmd: *mut UserCmd, active: i32) { + unsafe { + if let Some(orig) = ORIG_CLIENT { (orig.createmove)(frametime, cmd, active); } + let engine = ENGINE.load(Ordering::SeqCst); + if engine.is_null() { return; } + let lp = ((*engine).getlocalplayer)(); + if !lp.is_null() && SETTINGS.bhop { bhop(&mut *cmd); } + } +} + +unsafe fn bhop(cmd: &mut UserCmd) { + if (cmd.buttons & IN_JUMP) == 0 { return; } + let pmove = PLAYER_MOVE.load(Ordering::SeqCst); + if pmove.is_null() { return; } + if ((*pmove).flags & FL_ONGROUND) == 0 { cmd.buttons &= !IN_JUMP; } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/src/sdk.rs b/src/sdk.rs new file mode 100644 index 0000000..8a62954 --- /dev/null +++ b/src/sdk.rs @@ -0,0 +1,103 @@ +use std::ffi::c_char; + +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +pub struct Vector { + pub x: f32, + pub y: f32, + pub z: f32, +} + +pub const IN_ATTACK: u16 = 1 << 0; +pub const IN_JUMP: u16 = 1 << 1; + +#[repr(C)] +pub struct UserCmd { + pub _pad0: [u8; 3], + pub viewangles: Vector, + pub forwardmove: f32, + pub sidemove: f32, + pub upmove: f32, + pub _pad1: [u8; 1], + pub buttons: u16, + pub _pad2: [u8; 18], +} + +#[repr(C)] +pub struct EntityState { + pub _pad0: [u8; 12], + pub msgnum: i32, + pub _pad1: [u8; 72], + pub movetype: i32, + pub _pad2: [u8; 248], +} + +#[repr(C)] +pub struct Entity { + pub i: i32, + pub player: i32, + pub _pad0: [u8; 2 * 340], + pub curstate: EntityState, + pub _pad1: [u8; 1860], + pub origin: Vector, +} + +#[repr(C)] +pub struct EventAPI { + pub _pad0: [u8; 0x18], + pub localplayerviewheight: extern "C" fn(*mut Vector), + pub _pad1: [u8; 0x40], +} + +#[repr(C)] +pub struct Engine { + pub _pad0: [u8; 0x8C], + pub setviewangles: extern "C" fn(*const Vector), + pub getmaxclients: extern "C" fn() -> i32, + pub _pad1: [u8; 0xC], + pub con_printf: extern "C" fn(*const c_char, ...), + pub _pad2: [u8; 0x28], + pub getlocalplayer: extern "C" fn() -> *mut Entity, + pub _pad3: [u8; 0x4], + pub getentitybyindex: extern "C" fn(i: i32) -> *mut Entity, + pub _pad4: [u8; 0x78], + pub eventapi: *mut EventAPI, +} + +#[repr(C)] +pub struct RefParams { + pub vieworigin: Vector, + pub viewangles: Vector, + pub forward: Vector, + pub right: Vector, + pub up: Vector, + pub _pad0: [u8; 0x64], + pub punchangle: Vector, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct Client { + pub _pad0: [u8; 0xC], + pub hudredraw: extern "C" fn(f32, i32) -> i32, + pub _pad1: [u8; 0x18], // Пропускаем до HUD_Key_Event (индекс 10) + pub hudkeyevent: extern "C" fn(i32, i32, *const c_char) -> i32, + pub _pad2: [u8; 0xC], // До CreateMove (индекс 14) + pub createmove: extern "C" fn(f32, *mut UserCmd, i32), + pub is3rdperson: extern "C" fn() -> i32, + pub cameraoffset: extern "C" fn(*mut Vector), + pub _pad3: [u8; 0x8], + pub calcrefdef: extern "C" fn(*mut RefParams), +} + +pub const FL_ONGROUND: i32 = 1 << 9; + +#[repr(C)] +pub struct PlayerMove { + pub _pad0: [u8; 56], + pub origin: Vector, + pub _pad1: [u8; 24], + pub velocity: Vector, + pub _pad2: [u8; 80], + pub flags: i32, +}