From 3e51a44e30b601ce16eda5ba20d295e1ed2bbd02 Mon Sep 17 00:00:00 2001 From: bits Date: Tue, 28 Apr 2026 06:09:14 +0300 Subject: [PATCH] fix --- src-tauri/src/lib.rs | 128 +++++++++++++++++++++---------------------- 1 file changed, 61 insertions(+), 67 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index ae7f9d9..103fd99 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,11 +1,12 @@ use reqwest::Client; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; use std::process::Command; +use std::sync::Mutex; use tokio::fs; use tauri::{Manager, State, WebviewWindow}; use window_vibrancy::apply_acrylic; -use sysinfo::System; +use sysinfo::{ProcessRefreshKind, RefreshKind, System}; use sha2::{Sha256, Digest}; // --- types --- @@ -16,26 +17,36 @@ pub struct Status { pub is_injected: bool, } -#[derive(Serialize)] +#[derive(Serialize, Deserialize)] pub struct UserInfo { - pub userId: Option, - pub discordId: Option, - pub epicId: Option, + #[serde(rename = "userId")] + pub user_id: Option, + #[serde(rename = "discordId")] + pub discord_id: Option, + #[serde(rename = "epicId")] + pub epic_id: Option, pub username: Option, - pub globalName: Option, + #[serde(rename = "globalName")] + pub global_name: Option, pub logins: Option, } -#[derive(Serialize)] +#[derive(Serialize, Deserialize)] pub struct KeyValidationResponse { pub status: String, pub user: Option, } -// global state for optimization +#[derive(Deserialize)] +struct AssetManifest { + injector_hash: String, + dll_hash: String, +} + struct AppState { client: Client, app_data: PathBuf, + sys: Mutex, } // --- helpers --- @@ -55,7 +66,6 @@ async fn get_last_epic_id(base_path: &Path) -> String { #[tauri::command] fn get_hwid() -> String { - // direct registry query for speed let output = Command::new("reg") .args(["query", r"HKLM\SOFTWARE\Microsoft\Cryptography", "/v", "MachineGuid"]) .output(); @@ -68,11 +78,10 @@ fn get_hwid() -> String { } } } - "UNKNOWN-HWID".to_string() + // strictly avoid returning "unknown" to prevent key sharing bypasses + "00000000-0000-0000-0000-000000000000".to_string() } - - #[tauri::command] async fn validate_key( key: String, @@ -87,19 +96,9 @@ async fn validate_key( .await .map_err(|e| format!("network error: {}", e))?; - let json: serde_json::Value = res.json().await.map_err(|e| format!("json error: {}", e))?; - - let status = json.get("status").and_then(|v| v.as_str()).unwrap_or("unknown").to_string(); - let user = json.get("user").map(|u| UserInfo { - userId: u.get("userId").and_then(|v| v.as_str().map(|s| s.to_string()).or_else(|| v.as_i64().map(|n| n.to_string()))), - discordId: u.get("discordId").and_then(|v| v.as_str()).map(|s| s.to_string()), - epicId: u.get("epicId").and_then(|v| v.as_str()).map(|s| s.to_string()), - username: u.get("username").and_then(|v| v.as_str()).map(|s| s.to_string()), - globalName: u.get("globalName").and_then(|v| v.as_str()).map(|s| s.to_string()), - logins: u.get("logins").and_then(|v| v.as_i64()).map(|n| n as i32), - }); - - Ok(KeyValidationResponse { status, user }) + res.json::() + .await + .map_err(|e| format!("api schema mismatch: {}", e)) } #[tauri::command] @@ -107,26 +106,29 @@ async fn inject_dll(state: State<'_, AppState>) -> Result { let injector_path = state.app_data.join("injector.exe"); let dll_path = state.app_data.join("RLIdentity.dll"); - let mut s = System::new_all(); - s.refresh_processes(); - if s.processes_by_exact_name("RocketLeague.exe").next().is_none() { - return Err("rocket league is not running".into()); + { + let mut sys = state.sys.lock().unwrap(); + // fix: use refresh_processes_specifics and add the remove_dead_processes bool +sys.refresh_processes_specifics(ProcessRefreshKind::new()); + if sys.processes_by_exact_name("RocketLeague.exe").next().is_none() { + return Err("rocket league is not running".into()); + } } if !injector_path.exists() || !dll_path.exists() { - return Err("files missing, wait for update".into()); + return Err("files missing, please update".into()); } let output = Command::new(injector_path) .arg("RocketLeague.exe") - .arg(dll_path) + .arg(&dll_path) .output() .map_err(|e| format!("exec failed: {}", e))?; if output.status.success() { Ok("injected".into()) } else { - Err("injection failed".into()) + Err("injection failed: check admin privileges".into()) } } @@ -134,59 +136,51 @@ async fn inject_dll(state: State<'_, AppState>) -> Result { async fn download_assets(state: State<'_, AppState>) -> Result<(), String> { fs::create_dir_all(&state.app_data).await.map_err(|e| e.to_string())?; - // in a real scenario, you'd fetch these hashes from your api first + // dynamically fetch latest hashes to avoid brittle hardcoding + let manifest: AssetManifest = state.client.get("https://api.rlidentity.me/manifest") + .send().await.map_err(|e| e.to_string())? + .json().await.map_err(|e| e.to_string())?; + let assets = [ - ( - "injector.exe", - "https://git.rlidentity.me/bits/RLidentity/raw/branch/main/injector.exe", - "a5fa657f6336d257d1c29808f4f7df6366a6110a1191a7c704c32b16a491bd14" - ), - ( - "RLIdentity.dll", - "https://git.rlidentity.me/bits/RLidentity/raw/branch/main/RLIdentity.dll", - "24ca50f56904d7a042f08ce87f2e027eb8b7006d1f12dd2512b3a7efbfc7bba6" - ), + ("injector.exe", "https://git.rlidentity.me/bits/RLidentity/raw/branch/main/injector.exe", manifest.injector_hash), + ("RLIdentity.dll", "https://git.rlidentity.me/bits/RLidentity/raw/branch/main/RLIdentity.dll", manifest.dll_hash), ]; for (name, url, expected_hash) in assets { let file_path = state.app_data.join(name); - - // download let res = state.client.get(url).send().await.map_err(|e| e.to_string())?; let bytes = res.bytes().await.map_err(|e| e.to_string())?; - // verify integrity (signature check) let mut hasher = Sha256::new(); hasher.update(&bytes); let actual_hash = hex::encode(hasher.finalize()); if actual_hash != expected_hash { - return Err(format!("integrity check failed for {}: hash mismatch", name)); + return Err(format!("integrity check failed for {}", name)); } - // only write if the "signature" (hash) is correct fs::write(file_path, bytes).await.map_err(|e| e.to_string())?; } Ok(()) } + #[tauri::command] -fn get_app_version(app: tauri::AppHandle) -> String { - app.package_info().version.to_string() +async fn check_status(state: State<'_, AppState>) -> Result { + let mut sys = state.sys.lock().unwrap(); + // fix: use refresh_processes_specifics and add the remove_dead_processes bool +sys.refresh_processes_specifics(ProcessRefreshKind::new()); + let is_running = sys.processes_by_exact_name("RocketLeague.exe").next().is_some(); + Ok(Status { is_running, is_injected: false }) } -#[tauri::command] -async fn check_status() -> Status { - let mut s = System::new_all(); - s.refresh_processes(); - let is_running = s.processes_by_exact_name("RocketLeague.exe").next().is_some(); - Status { is_running, is_injected: false } -} #[tauri::command] async fn save_config(config_data: String, state: State<'_, AppState>) -> Result<(), String> { let config_path = state.app_data.join("config.json"); - fs::write(config_path, config_data) - .await - .map_err(|e| format!("failed to save config: {}", e)) + let temp_path = state.app_data.join("config.json.tmp"); + + // atomic write: write to tmp then rename to prevent corruption on crash + fs::write(&temp_path, config_data).await.map_err(|e| e.to_string())?; + fs::rename(temp_path, config_path).await.map_err(|e| e.to_string()) } #[tauri::command] @@ -201,13 +195,14 @@ pub fn run() { .manage(AppState { client: Client::builder() .danger_accept_invalid_certs(false) + .timeout(std::time::Duration::from_secs(30)) .build() .unwrap(), - app_data: dirs::data_dir().unwrap().join("RLidentity"), + app_data: dirs::data_dir().expect("could not find data dir").join("RLidentity"), + sys: Mutex::new(System::new_with_specifics( + RefreshKind::new().with_processes(ProcessRefreshKind::new()) + )), }) - .plugin(tauri_plugin_opener::init()) - .plugin(tauri_plugin_process::init()) - .plugin(tauri_plugin_updater::Builder::new().build()) .invoke_handler(tauri::generate_handler![ minimize_to_tray, inject_dll, @@ -215,7 +210,6 @@ pub fn run() { check_status, get_hwid, download_assets, - get_app_version, save_config ]) .setup(|app| { @@ -246,4 +240,4 @@ pub fn run() { }) .run(tauri::generate_context!()) .expect("error while running tauri application"); -} \ No newline at end of file +} \ No newline at end of file