remove conditional elevation

This commit is contained in:
2026-06-28 16:14:49 +01:00
parent 054c86e2d2
commit 1f296c3880
6 changed files with 103 additions and 176 deletions
-2
View File
@@ -28,12 +28,10 @@ serde_json = "1.0.143"
strum = { version = "0.27.2", features = ["derive"] } strum = { version = "0.27.2", features = ["derive"] }
windows = { version = "0.62.0", features = [ windows = { version = "0.62.0", features = [
"Win32_NetworkManagement_WindowsFirewall", "Win32_NetworkManagement_WindowsFirewall",
"Win32_Security",
"Win32_System_Com", "Win32_System_Com",
"Win32_System_Diagnostics_ToolHelp", "Win32_System_Diagnostics_ToolHelp",
"Win32_System_Threading", "Win32_System_Threading",
"Win32_UI_Input_KeyboardAndMouse", "Win32_UI_Input_KeyboardAndMouse",
"Win32_UI_Shell",
"Win32_UI_WindowsAndMessaging", "Win32_UI_WindowsAndMessaging",
] } ] }
winreg = "0.55.0" winreg = "0.55.0"
+11
View File
@@ -6,6 +6,17 @@ fn main() {
.set("LegalCopyright", "futile <git@futile.eu>") .set("LegalCopyright", "futile <git@futile.eu>")
.set_language(0x0009) .set_language(0x0009)
.set_icon("assets/icon.ico") .set_icon("assets/icon.ico")
.set_manifest(
r#"<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>"#,
)
.compile() .compile()
.unwrap(); .unwrap();
embed_latest_git_hash(); embed_latest_git_hash();
+87 -110
View File
@@ -29,7 +29,6 @@ enum Stage {
#[derive(Debug)] #[derive(Debug)]
pub struct Flags { pub struct Flags {
pub elevated: bool,
pub debug: bool, pub debug: bool,
closing: bool, closing: bool,
} }
@@ -37,7 +36,6 @@ pub struct Flags {
impl Default for Flags { impl Default for Flags {
fn default() -> Self { fn default() -> Self {
Self { Self {
elevated: win::is_elevated(),
debug: false, debug: false,
closing: false, closing: false,
} }
@@ -67,15 +65,6 @@ impl eframe::App for App {
.show(ctx, |ui| { .show(ctx, |ui| {
ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| { ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| {
ui.build_menu(&mut self.stage); ui.build_menu(&mut self.stage);
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
let button = ui
.add_enabled(!self.flags.elevated, egui::Button::new("Elevate"))
.on_hover_text("Relaunch ourselves as administrator.")
.on_disabled_hover_text("We are already running elevated.");
if button.clicked() {
win::elevate(win::ElevationExitMethod::Gentle(&mut self.flags.closing));
}
});
}); });
}); });
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
@@ -120,7 +109,7 @@ impl App {
fn show_session_section(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) { fn show_session_section(&mut self, _ctx: &egui::Context, ui: &mut egui::Ui) {
ui.header("Session"); ui.header("Session");
ui.add_enabled_ui(self.flags.elevated && !self.empty_session.disabled, |ui| { ui.add_enabled_ui(!self.empty_session.disabled, |ui| {
ui.horizontal(|ui| { ui.horizontal(|ui| {
if ui.button("Empty current session").clicked() if ui.button("Empty current session").clicked()
&& features::empty_session::activate(&mut self.system_info, &self.firewall) && features::empty_session::activate(&mut self.system_info, &self.firewall)
@@ -156,77 +145,70 @@ impl App {
egui::Frame::new() egui::Frame::new()
.outer_margin(egui::vec2(0.0, -2.0)) .outer_margin(egui::vec2(0.0, -2.0))
.show(ui, |ui| { .show(ui, |ui| {
ui.add_enabled_ui(self.flags.elevated, |ui| { let label = ui.horizontal(|ui| {
let label = ui.horizontal(|ui| { let label = match self.settings.block_method {
let label = match self.settings.block_method { BlockMethod::EntireGame => ui.label("Game's network access"),
BlockMethod::EntireGame => ui.label("Game's network access"), BlockMethod::SaveServer => ui.label("Rockstar save server access"),
BlockMethod::SaveServer => ui.label("Rockstar save server access"), };
}; ui.add_space(1.0);
ui.add_space(1.0); ui.create_indicator_dot(match self.game_networking.blocked {
ui.create_indicator_dot(match self.game_networking.blocked { BlockedStatus::Executable
BlockedStatus::Executable if self.settings.block_method == BlockMethod::EntireGame =>
if self.settings.block_method == BlockMethod::EntireGame => {
{ colours::RED
colours::RED }
} BlockedStatus::Server
BlockedStatus::Server if self.settings.block_method == BlockMethod::SaveServer =>
if self.settings.block_method == BlockMethod::SaveServer => {
{ colours::RED
colours::RED }
} _ => colours::GREEN,
_ => colours::GREEN,
});
label
}); });
ui.allocate_ui_with_layout( label
egui::vec2(label.inner.rect.width(), 0.0), });
egui::Layout::top_down(egui::Align::Min), ui.allocate_ui_with_layout(
|ui| { egui::vec2(label.inner.rect.width(), 0.0),
ui.columns(2, |columns| { egui::Layout::top_down(egui::Align::Min),
columns[0].vertical_centered_justified(|ui| { |ui| {
if ui.button("Block").clicked() { ui.columns(2, |columns| {
match self.settings.block_method { columns[0].vertical_centered_justified(|ui| {
BlockMethod::EntireGame => { if ui.button("Block").clicked() {
self.game_networking match self.settings.block_method {
.block_exe( BlockMethod::EntireGame => {
&mut self.system_info, self.game_networking
&self.firewall, .block_exe(&mut self.system_info, &self.firewall)
) .unwrap();
.unwrap(); }
} BlockMethod::SaveServer => {
BlockMethod::SaveServer => { self.game_networking
self.game_networking .block_save_server(
.block_save_server( &self.settings.save_server_ip,
&self.settings.save_server_ip, &self.firewall,
&self.firewall, )
) .unwrap();
.unwrap();
}
} }
} }
}); }
columns[1].vertical_centered_justified(|ui| {
if ui.button("Unblock").clicked() {
match self.settings.block_method {
BlockMethod::EntireGame => {
self.game_networking
.unblock_exe(&self.firewall)
.unwrap();
}
BlockMethod::SaveServer => {
self.game_networking
.unblock_save_server(&self.firewall)
.unwrap();
}
}
}
});
}); });
}, columns[1].vertical_centered_justified(|ui| {
); if ui.button("Unblock").clicked() {
}) match self.settings.block_method {
.response BlockMethod::EntireGame => {
.on_disabled_hover_text("This requires administrator.\nUse the Elevate button."); self.game_networking
.unblock_exe(&self.firewall)
.unwrap();
}
BlockMethod::SaveServer => {
self.game_networking
.unblock_save_server(&self.firewall)
.unwrap();
}
}
}
});
});
},
);
}); });
} }
@@ -238,7 +220,6 @@ impl App {
fn show_settings_stage(&mut self, ctx: &egui::Context, ui: &mut egui::Ui) { fn show_settings_stage(&mut self, ctx: &egui::Context, ui: &mut egui::Ui) {
ui.collapsing("General", |ui| { ui.collapsing("General", |ui| {
ui.checkbox(&mut self.settings.start_elevated, "Always start elevated");
ui.horizontal(|ui| { ui.horizontal(|ui| {
let selection = self.settings.theme; let selection = self.settings.theme;
egui::ComboBox::from_id_salt("Theme") egui::ComboBox::from_id_salt("Theme")
@@ -263,39 +244,35 @@ impl App {
}); });
}); });
ui.collapsing("Network", |ui| { ui.collapsing("Network", |ui| {
ui.add_enabled_ui(self.flags.elevated, |ui| { ui.horizontal(|ui| {
egui::ComboBox::from_id_salt("Block method")
.selected_text(self.settings.block_method.to_string())
.show_ui(ui, |ui| {
ui.build_menu(&mut self.settings.block_method);
});
ui.label("Block method");
if let Err(why) = self
.game_networking
.ensure_block_exclusivity(self.settings.block_method, &self.firewall)
{
log::warn!("Couldn't ensure block exclusivity: {why}");
}
});
if self.settings.block_method == BlockMethod::SaveServer {
ui.horizontal(|ui| { ui.horizontal(|ui| {
egui::ComboBox::from_id_salt("Block method") ui.add(
.selected_text(self.settings.block_method.to_string()) egui::TextEdit::singleline(&mut self.settings.save_server_ip)
.show_ui(ui, |ui| { .char_limit(15)
ui.build_menu(&mut self.settings.block_method); .desired_width(92.0),
}); );
ui.label("Block method"); ui.label("Save server IP");
if let Err(why) = self if self.settings.save_server_ip != Settings::default().save_server_ip {
.game_networking if ui.button("").clicked() {
.ensure_block_exclusivity(self.settings.block_method, &self.firewall) self.settings.save_server_ip = String::from(ROCKSTAR_SAVE_SERVER);
{ }
log::warn!("Couldn't ensure block exclusivity: {why}");
} }
}); });
if self.settings.block_method == BlockMethod::SaveServer { };
ui.horizontal(|ui| {
ui.add(
egui::TextEdit::singleline(&mut self.settings.save_server_ip)
.char_limit(15)
.desired_width(92.0),
);
ui.label("Save server IP");
if self.settings.save_server_ip != Settings::default().save_server_ip {
if ui.button("").clicked() {
self.settings.save_server_ip = String::from(ROCKSTAR_SAVE_SERVER);
}
}
});
}
})
.response
.on_disabled_hover_text("This requires administrator.\nUse the Elevate button.");
}); });
ui.collapsing("Miscellaneous", |ui| { ui.collapsing("Miscellaneous", |ui| {
if ui.button("Open storage path").clicked() { if ui.button("Open storage path").clicked() {
+1 -5
View File
@@ -3,7 +3,7 @@ use crate::{
app::{App, WINDOW_SIZE}, app::{App, WINDOW_SIZE},
tools, tools,
}, },
util::{persistent_state::PersistentState, win}, util::persistent_state::PersistentState,
}; };
use eframe::egui; use eframe::egui;
use windows::Win32::System::Com::{COINIT_APARTMENTTHREADED, CoInitializeEx}; use windows::Win32::System::Com::{COINIT_APARTMENTTHREADED, CoInitializeEx};
@@ -21,10 +21,6 @@ fn app_creator(
if let Some(persistent_state) = PersistentState::get() { if let Some(persistent_state) = PersistentState::get() {
persistent_state.apply_to(&mut app); persistent_state.apply_to(&mut app);
} }
// check if we're elevated. if not, and the user wants an elevated launch - relaunch elevated
if !app.flags.elevated && app.settings.start_elevated {
win::elevate(win::ElevationExitMethod::Forced);
}
// refresh system info because it initializes with nothing // refresh system info because it initializes with nothing
app.system_info.refresh(); app.system_info.refresh();
// enable image loading support in egui // enable image loading support in egui
-2
View File
@@ -55,7 +55,6 @@ pub enum BlockMethod {
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Settings { pub struct Settings {
pub start_elevated: bool,
pub theme: Theme, pub theme: Theme,
pub launch_version: LaunchVersion, pub launch_version: LaunchVersion,
pub save_server_ip: String, pub save_server_ip: String,
@@ -65,7 +64,6 @@ pub struct Settings {
impl Default for Settings { impl Default for Settings {
fn default() -> Self { fn default() -> Self {
Self { Self {
start_elevated: false,
theme: Theme::default(), theme: Theme::default(),
launch_version: LaunchVersion::default(), launch_version: LaunchVersion::default(),
block_method: BlockMethod::default(), block_method: BlockMethod::default(),
+4 -57
View File
@@ -1,25 +1,10 @@
use windows::{ use windows::Win32::UI::{
Win32::{ Input::KeyboardAndMouse::{GetAsyncKeyState, VIRTUAL_KEY},
Foundation::{CloseHandle, HANDLE}, WindowsAndMessaging::{
Security::{GetTokenInformation, TOKEN_ELEVATION, TOKEN_QUERY, TokenElevation}, CURSOR_SHOWING, CURSORINFO, GetCursorInfo, GetForegroundWindow, GetWindowTextW,
System::Threading::{GetCurrentProcess, OpenProcessToken},
UI::{
Input::KeyboardAndMouse::{GetAsyncKeyState, VIRTUAL_KEY},
Shell::ShellExecuteW,
WindowsAndMessaging::{
CURSOR_SHOWING, CURSORINFO, GetCursorInfo, GetForegroundWindow, GetWindowTextW,
SW_NORMAL,
},
},
}, },
core::{HSTRING, PCWSTR},
}; };
pub enum ElevationExitMethod<'a> {
Gentle(&'a mut bool),
Forced,
}
pub fn is_cursor_visible() -> bool { pub fn is_cursor_visible() -> bool {
let mut ci = CURSORINFO { let mut ci = CURSORINFO {
cbSize: u32::try_from(std::mem::size_of::<CURSORINFO>()).unwrap(), cbSize: u32::try_from(std::mem::size_of::<CURSORINFO>()).unwrap(),
@@ -42,44 +27,6 @@ pub fn is_any_key_pressed(keys: &[VIRTUAL_KEY]) -> bool {
.any(|&key| unsafe { GetAsyncKeyState(i32::from(key.0)) } & i16::MIN != 0) .any(|&key| unsafe { GetAsyncKeyState(i32::from(key.0)) } & i16::MIN != 0)
} }
pub fn elevate(closing: ElevationExitMethod) {
let exe = std::env::current_exe().unwrap();
unsafe {
ShellExecuteW(
None,
&HSTRING::from("runas"),
&HSTRING::from(exe.as_path()),
PCWSTR::null(),
PCWSTR::null(),
SW_NORMAL,
);
}
match closing {
ElevationExitMethod::Gentle(closing) => *closing = true,
ElevationExitMethod::Forced => std::process::exit(0),
}
}
pub fn is_elevated() -> bool {
let mut token: HANDLE = HANDLE::default();
if unsafe { OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &raw mut token) }.is_err() {
return false;
}
let mut elevation = TOKEN_ELEVATION::default();
let mut size = u32::try_from(std::mem::size_of::<TOKEN_ELEVATION>()).unwrap();
let result = unsafe {
GetTokenInformation(
token,
TokenElevation,
Some((&raw mut elevation).cast()),
size,
&raw mut size,
)
};
unsafe { CloseHandle(token) }.unwrap();
result.is_ok() && elevation.TokenIsElevated != 0
}
pub fn is_system_theme_light() -> bool { pub fn is_system_theme_light() -> bool {
use winreg::RegKey; use winreg::RegKey;
let hkcu = RegKey::predef(winreg::enums::HKEY_CURRENT_USER); let hkcu = RegKey::predef(winreg::enums::HKEY_CURRENT_USER);