Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7117b65cf5 | |||
| e6404f781c | |||
| 7e46f9a710 | |||
| 5b9646ff75 | |||
| 3e51a44e30 | |||
| e04660b93b | |||
| 808420e601 | |||
| 58145f97a0 |
@ -1,4 +1,4 @@
|
||||
# RLidentity v2.0.0
|
||||
# RLidentity v2.0.1
|
||||
|
||||
> **Be anyone, Win everything.**
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "rlidentitygui",
|
||||
"name": "RLIdentity",
|
||||
"private": true,
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
40
src-tauri/Cargo.lock
generated
40
src-tauri/Cargo.lock
generated
@ -2,6 +2,26 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "RLIdentity"
|
||||
version = "2.0.1"
|
||||
dependencies = [
|
||||
"dirs 5.0.1",
|
||||
"hex",
|
||||
"reqwest 0.12.28",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sysinfo",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-opener",
|
||||
"tauri-plugin-process",
|
||||
"tauri-plugin-updater",
|
||||
"tokio",
|
||||
"window-vibrancy 0.5.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
@ -3452,26 +3472,6 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rlidentitygui"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"dirs 5.0.1",
|
||||
"hex",
|
||||
"reqwest 0.12.28",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sysinfo",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-opener",
|
||||
"tauri-plugin-process",
|
||||
"tauri-plugin-updater",
|
||||
"tokio",
|
||||
"window-vibrancy 0.5.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "rlidentitygui"
|
||||
version = "2.0.0"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
name = "RLIdentity"
|
||||
version = "2.0.1"
|
||||
description = "check out https://rlidentity.me for more info!"
|
||||
authors = ["bits", "danni :3"]
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@ -14,6 +14,6 @@
|
||||
"opener:default",
|
||||
"updater:default",
|
||||
"process:allow-restart",
|
||||
"default"
|
||||
"allow-main-commands"
|
||||
]
|
||||
}
|
||||
16
src-tauri/permissions/allow-main-commands.json
Normal file
16
src-tauri/permissions/allow-main-commands.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"identifier": "allow-main-commands",
|
||||
"description": "Allows RLIdentity core commands",
|
||||
"commands": {
|
||||
"allow": [
|
||||
"minimize_to_tray",
|
||||
"save_config",
|
||||
"inject_dll",
|
||||
"validate_key",
|
||||
"check_status",
|
||||
"get_hwid",
|
||||
"get_app_version",
|
||||
"download_assets"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -13,5 +13,6 @@ commands.allow = [
|
||||
"inject_dll",
|
||||
"validate_key",
|
||||
"check_status",
|
||||
"get_hwid"
|
||||
"get_hwid",
|
||||
"download_assets"
|
||||
]
|
||||
|
||||
@ -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,43 @@ pub struct Status {
|
||||
pub is_injected: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct UserInfo {
|
||||
pub userId: Option<String>,
|
||||
pub discordId: Option<String>,
|
||||
pub epicId: Option<String>,
|
||||
#[serde(rename = "userId")]
|
||||
pub user_id: Option<String>,
|
||||
#[serde(rename = "discordId")]
|
||||
pub discord_id: Option<String>,
|
||||
#[serde(rename = "epicId")]
|
||||
pub epic_id: Option<String>,
|
||||
pub username: Option<String>,
|
||||
pub globalName: Option<String>,
|
||||
#[serde(rename = "globalName")]
|
||||
pub global_name: Option<String>,
|
||||
pub logins: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct KeyValidationResponse {
|
||||
pub status: String,
|
||||
pub user: Option<UserInfo>,
|
||||
}
|
||||
|
||||
// global state for optimization
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct SaveConfigPayload {
|
||||
#[serde(rename = "spoofedName")]
|
||||
name: String,
|
||||
platform: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct AssetManifest {
|
||||
injector_hash: String,
|
||||
dll_hash: String,
|
||||
}
|
||||
|
||||
struct AppState {
|
||||
client: Client,
|
||||
app_data: PathBuf,
|
||||
sys: Mutex<System>,
|
||||
}
|
||||
|
||||
// --- helpers ---
|
||||
@ -53,9 +71,8 @@ async fn get_last_epic_id(base_path: &Path) -> String {
|
||||
|
||||
// --- commands ---
|
||||
|
||||
#[tauri::command]
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
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,12 +85,10 @@ fn get_hwid() -> String {
|
||||
}
|
||||
}
|
||||
}
|
||||
"UNKNOWN-HWID".to_string()
|
||||
"00000000-0000-0000-0000-000000000000".to_string()
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[tauri::command]
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
async fn validate_key(
|
||||
key: String,
|
||||
hwid: String,
|
||||
@ -87,98 +102,104 @@ 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::<KeyValidationResponse>()
|
||||
.await
|
||||
.map_err(|e| format!("api schema mismatch: {}", e))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
async fn inject_dll(state: State<'_, AppState>) -> Result<String, String> {
|
||||
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() {
|
||||
let is_running = {
|
||||
let mut sys = state.sys.lock().unwrap();
|
||||
sys.refresh_processes_specifics(ProcessRefreshKind::new());
|
||||
let x = sys.processes_by_exact_name("RocketLeague.exe").next().is_some(); x
|
||||
};
|
||||
|
||||
if !is_running {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
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
|
||||
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/src/branch/dll/injector.exe",
|
||||
"B447D618886EEDE9F6A331A5605BFC40FADEB3F508916D8B19916467EC8E0E69"
|
||||
),
|
||||
(
|
||||
"RLIdentity.dll",
|
||||
"https://git.rlidentity.me/bits/RLidentity/src/branch/main/RLIdentity.dll",
|
||||
"69108E3E1084EA9AE6AC97D4F19D68356213FEF3A29193D63CE3A6069D333CD8"
|
||||
),
|
||||
("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]
|
||||
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(rename_all = "snake_case")]
|
||||
async fn check_status(state: State<'_, AppState>) -> Result<Status, String> {
|
||||
let is_running = {
|
||||
let mut sys = state.sys.lock().unwrap();
|
||||
sys.refresh_processes_specifics(ProcessRefreshKind::new());
|
||||
let x = sys.processes_by_exact_name("RocketLeague.exe").next().is_some(); x
|
||||
};
|
||||
|
||||
Ok(Status { is_running, is_injected: false })
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
async fn save_config(name: String, platform: String, state: State<'_, AppState>) -> Result<(), String> {
|
||||
let config_path = state.app_data.join("config.json");
|
||||
let payload = SaveConfigPayload { name, platform };
|
||||
|
||||
if !state.app_data.exists() {
|
||||
fs::create_dir_all(&state.app_data).await.map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
let json = serde_json::to_string(&payload).map_err(|e| e.to_string())?;
|
||||
fs::write(config_path, json).await.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
fn get_app_version(app: tauri::AppHandle) -> String {
|
||||
app.package_info().version.to_string()
|
||||
}
|
||||
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
fn minimize_to_tray(window: WebviewWindow) {
|
||||
let _ = window.hide();
|
||||
}
|
||||
@ -190,20 +211,23 @@ 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,
|
||||
validate_key,
|
||||
check_status,
|
||||
get_hwid,
|
||||
download_assets
|
||||
download_assets,
|
||||
save_config,
|
||||
get_app_version
|
||||
])
|
||||
.setup(|app| {
|
||||
let window = app.get_webview_window("main").unwrap();
|
||||
@ -211,23 +235,26 @@ pub fn run() {
|
||||
#[cfg(target_os = "windows")]
|
||||
apply_acrylic(&window, Some((18, 18, 18, 125))).ok();
|
||||
|
||||
let handle = app.handle().clone();
|
||||
let tray_menu = tauri::menu::Menu::with_items(app, &[
|
||||
&tauri::menu::MenuItem::with_id(app, "quit", "Quit", true, None::<&str>)?,
|
||||
])?;
|
||||
let monitor_handle = app.handle().clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let mut last_seen_running = false;
|
||||
loop {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
|
||||
|
||||
let _ = tauri::tray::TrayIconBuilder::new()
|
||||
.icon(app.default_window_icon().unwrap().clone())
|
||||
.menu(&tray_menu)
|
||||
.on_menu_event(move |_app, event| {
|
||||
if event.id().as_ref() == "quit" { handle.exit(0); }
|
||||
})
|
||||
.on_tray_icon_event(|tray, event| {
|
||||
if let tauri::tray::TrayIconEvent::Click { button: tauri::tray::MouseButton::Left, .. } = event {
|
||||
let _ = tray.app_handle().get_webview_window("main").unwrap().show();
|
||||
let state = monitor_handle.state::<AppState>();
|
||||
let is_running = {
|
||||
let mut sys = state.sys.lock().unwrap();
|
||||
sys.refresh_processes_specifics(ProcessRefreshKind::new());
|
||||
let x = sys.processes_by_exact_name("RocketLeague.exe").next().is_some(); x
|
||||
};
|
||||
|
||||
if is_running && !last_seen_running {
|
||||
println!("auto-detect: rocket league started. injecting...");
|
||||
let _ = inject_dll(state).await;
|
||||
}
|
||||
})
|
||||
.build(app)?;
|
||||
last_seen_running = is_running;
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
||||
35
src/App.tsx
35
src/App.tsx
@ -12,11 +12,11 @@ type Status =
|
||||
| string;
|
||||
|
||||
interface UserInfo {
|
||||
userId: string | null;
|
||||
discordId: string | null;
|
||||
epicId: string | null;
|
||||
user_id: string | null;
|
||||
discord_id: string | null;
|
||||
epic_id: string | null;
|
||||
username: string | null;
|
||||
globalName: string | null;
|
||||
global_name: string | null;
|
||||
logins?: number;
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ export default function App() {
|
||||
const initialPlatform = useMemo(() => localStorage.getItem(LS_KEYS.platform) ?? "Epic", []);
|
||||
const initialAutoInject = useMemo(() => localStorage.getItem(LS_KEYS.autoInject) === "true", []);
|
||||
const initialTheme = useMemo(() => (localStorage.getItem(LS_KEYS.theme) ?? "phantom") as ThemeId, []);
|
||||
|
||||
const [version, setVersion] = useState('');
|
||||
const [apiKey, setApiKey] = useState(initialApiKey);
|
||||
const [spoofedUsername, setSpoofedUsername] = useState(initialSpoofed);
|
||||
const [isAuthorized, setIsAuthorized] = useState(false);
|
||||
@ -138,14 +138,29 @@ export default function App() {
|
||||
useEffect(() => {
|
||||
if (initialApiKey) authorize(initialApiKey);
|
||||
syncAssetsAndCheckUpdates();
|
||||
loadAppVersion();
|
||||
}, []);
|
||||
|
||||
async function loadAppVersion() {
|
||||
if (!isTauriRuntime()) return;
|
||||
try {
|
||||
const app = await import("@tauri-apps/api/app");
|
||||
setVersion(await app.getVersion());
|
||||
} catch (e) {
|
||||
console.error("Failed to get app version:", e);
|
||||
setVersion("");
|
||||
}
|
||||
}
|
||||
|
||||
async function syncAssetsAndCheckUpdates() {
|
||||
if (!isTauriRuntime()) return;
|
||||
try {
|
||||
setStatus("syncing assets...");
|
||||
await tryInvoke("download_assets");
|
||||
setStatus("ready");
|
||||
} catch (e) {
|
||||
console.error("Failed to sync assets:", e);
|
||||
setStatus("Sync Error: " + String(e));
|
||||
}
|
||||
checkForUpdates();
|
||||
}
|
||||
@ -240,7 +255,7 @@ export default function App() {
|
||||
async function inject() {
|
||||
setStatus("injecting...");
|
||||
try {
|
||||
const res = await tryInvoke<string>("inject_dll", { discordId: userData?.discordId });
|
||||
const res = await tryInvoke<string>("inject_dll");
|
||||
setLastLog(res || "Successfully Injected!");
|
||||
setStatus("Successfully Injected!");
|
||||
window.setTimeout(() => setStatus("ready"), 1400);
|
||||
@ -301,7 +316,9 @@ export default function App() {
|
||||
/>
|
||||
<div className="titlebar-text" data-tauri-drag-region>
|
||||
<div className="app-name neon-text-soft">
|
||||
RLidentity <span style={{ fontSize: "10px", opacity: 0.6, marginLeft: "4px" }}>v2.0.0</span>
|
||||
RLidentity <span style={{ fontSize: "10px", opacity: 0.6, marginLeft: "4px" }}>
|
||||
v{version || "2.0.1"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="app-slogan">{isRevoked ? "License Revoked" : "Authorize to continue"}</div>
|
||||
</div>
|
||||
@ -387,9 +404,9 @@ export default function App() {
|
||||
<main className="panel-wrap">
|
||||
<header className="welcome-section">
|
||||
<h2 className="welcome-text">
|
||||
Welcome, <span className="neon-text-soft">{userData?.globalName || userData?.username || "User"}</span>
|
||||
Welcome, <span className="neon-text-soft">{userData?.global_name || userData?.username || "User"}</span>
|
||||
</h2>
|
||||
<div className="user-id-badge">User #{userData?.userId || "0"}</div>
|
||||
<div className="user-id-badge">User #{userData?.user_id || "0"}</div>
|
||||
</header>
|
||||
|
||||
<section className="glass-card neon-ring">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user