empty session with firewall instead of suspend
This commit is contained in:
@@ -1,14 +1,23 @@
|
|||||||
use crate::util::{
|
use crate::util::{countdown::Countdown, system_info::SystemInfo};
|
||||||
consts::game::{EXE_ENHANCED, EXE_LEGACY},
|
use std::{
|
||||||
countdown::Countdown,
|
error::Error,
|
||||||
system_info::SystemInfo,
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use std::time::{Duration, Instant};
|
use windows::{
|
||||||
use windows::Win32::{
|
Win32::{
|
||||||
Foundation::{HANDLE, NTSTATUS},
|
NetworkManagement::WindowsFirewall::{
|
||||||
System::Threading::{OpenProcess, PROCESS_SUSPEND_RESUME},
|
INetFwPolicy2, INetFwRule, NET_FW_ACTION_BLOCK, NET_FW_IP_PROTOCOL_UDP,
|
||||||
|
NET_FW_RULE_DIR_IN, NET_FW_RULE_DIR_OUT, NetFwPolicy2, NetFwRule,
|
||||||
|
},
|
||||||
|
System::Com::{CLSCTX_INPROC_SERVER, CoCreateInstance},
|
||||||
|
},
|
||||||
|
core::BSTR,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const FILTER_NAME_EMPTY_SESSION_IN: &str = "[GTA Tools] Block inbound UDP traffic for all of GTA V";
|
||||||
|
const FILTER_NAME_EMPTY_SESSION_OUT: &str =
|
||||||
|
"[GTA Tools] Block outbound UDP traffic for all of GTA V";
|
||||||
|
|
||||||
const INTERVAL: Duration = Duration::from_secs(10);
|
const INTERVAL: Duration = Duration::from_secs(10);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -29,52 +38,51 @@ impl Default for EmptySession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl EmptySession {
|
impl EmptySession {
|
||||||
pub fn run_timers(&mut self, game_handle: &mut HANDLE) {
|
pub fn run_timers(&mut self) -> Result<(), Box<dyn Error>> {
|
||||||
if self.disabled {
|
if self.disabled {
|
||||||
self.countdown.count();
|
self.countdown.count();
|
||||||
} else {
|
} else {
|
||||||
self.countdown.reset();
|
self.countdown.reset();
|
||||||
}
|
}
|
||||||
if self.interval.elapsed() >= INTERVAL {
|
if self.interval.elapsed() >= INTERVAL {
|
||||||
deactivate(game_handle);
|
deactivate()?;
|
||||||
self.disabled = false;
|
self.disabled = false;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[link(name = "ntdll")]
|
pub fn activate(system_info: &mut SystemInfo) -> Result<bool, Box<dyn Error>> {
|
||||||
unsafe extern "system" {
|
let Some(exe_path) = system_info.get_game_exe_path() else {
|
||||||
unsafe fn NtSuspendProcess(ProcessHandle: HANDLE) -> NTSTATUS;
|
log::info!("wasn't able to find game exe");
|
||||||
unsafe fn NtResumeProcess(ProcessHandle: HANDLE) -> NTSTATUS;
|
return Ok(false);
|
||||||
}
|
|
||||||
|
|
||||||
fn get_gta_pid(system_info: &mut SystemInfo) -> Option<u32> {
|
|
||||||
system_info.refresh();
|
|
||||||
system_info
|
|
||||||
.processes()
|
|
||||||
.iter()
|
|
||||||
.find(|p| p.name() == EXE_ENHANCED || p.name() == EXE_LEGACY)
|
|
||||||
.map(|p| p.pid())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn activate(game_handle: &mut HANDLE, system_info: &mut SystemInfo) -> bool {
|
|
||||||
let Some(pid) = get_gta_pid(system_info) else {
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
match unsafe { OpenProcess(PROCESS_SUSPEND_RESUME, false, pid) } {
|
for (direction, filter_name) in [
|
||||||
Ok(handle) => *game_handle = handle,
|
(NET_FW_RULE_DIR_IN, FILTER_NAME_EMPTY_SESSION_IN),
|
||||||
Err(why) => {
|
(NET_FW_RULE_DIR_OUT, FILTER_NAME_EMPTY_SESSION_OUT),
|
||||||
log::error!("failed to suspend game for empty session:\n{why}");
|
] {
|
||||||
return false;
|
let policy: INetFwPolicy2 =
|
||||||
}
|
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }?;
|
||||||
|
let rules = unsafe { policy.Rules() }?;
|
||||||
|
unsafe { rules.Remove(&BSTR::from(filter_name)) }?;
|
||||||
|
let rule: INetFwRule = unsafe { CoCreateInstance(&NetFwRule, None, CLSCTX_INPROC_SERVER) }?;
|
||||||
|
unsafe { rule.SetName(&BSTR::from(filter_name)) }?;
|
||||||
|
unsafe { rule.SetApplicationName(&BSTR::from(exe_path.to_string_lossy().to_string())) }?;
|
||||||
|
unsafe { rule.SetDirection(direction) }?;
|
||||||
|
unsafe { rule.SetEnabled(true.into()) }?;
|
||||||
|
unsafe { rule.SetAction(NET_FW_ACTION_BLOCK) }?;
|
||||||
|
unsafe { rule.SetProtocol(NET_FW_IP_PROTOCOL_UDP.0) }?;
|
||||||
|
unsafe { rules.Add(&rule) }?;
|
||||||
}
|
}
|
||||||
unsafe { NtSuspendProcess(*game_handle) }.unwrap();
|
Ok(true)
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deactivate(game_handle: &mut HANDLE) {
|
pub fn deactivate() -> Result<(), Box<dyn Error>> {
|
||||||
if !game_handle.is_invalid() {
|
let policy: INetFwPolicy2 =
|
||||||
// ignoring the return because this function behaves very weirdly
|
unsafe { CoCreateInstance(&NetFwPolicy2, None, CLSCTX_INPROC_SERVER) }?;
|
||||||
let _ = unsafe { NtResumeProcess(*game_handle) };
|
let rules = unsafe { policy.Rules() }?;
|
||||||
|
for filter_name in [FILTER_NAME_EMPTY_SESSION_IN, FILTER_NAME_EMPTY_SESSION_OUT] {
|
||||||
|
unsafe { rules.Remove(&BSTR::from(filter_name)) }?;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ impl GameNetworking {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn block_exe(&mut self, system_info: &mut SystemInfo) -> Result<(), Box<dyn Error>> {
|
pub fn block_exe(&mut self, system_info: &mut SystemInfo) -> Result<(), Box<dyn Error>> {
|
||||||
let Some(exe_path) = get_game_exe_path(system_info) else {
|
let Some(exe_path) = system_info.get_game_exe_path() else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
Self::block_generic(Mode::EntireGame(exe_path.to_path_buf()))?;
|
Self::block_generic(Mode::EntireGame(exe_path.to_path_buf()))?;
|
||||||
|
|||||||
+6
-8
@@ -49,7 +49,6 @@ pub struct App {
|
|||||||
stage: Stage,
|
stage: Stage,
|
||||||
pub flags: Flags,
|
pub flags: Flags,
|
||||||
pub system_info: SystemInfo,
|
pub system_info: SystemInfo,
|
||||||
game_handle: windows::Win32::Foundation::HANDLE,
|
|
||||||
pub anti_afk: features::anti_afk::AntiAfk,
|
pub anti_afk: features::anti_afk::AntiAfk,
|
||||||
empty_session: features::empty_session::EmptySession,
|
empty_session: features::empty_session::EmptySession,
|
||||||
force_close: features::force_close::ForceClose,
|
force_close: features::force_close::ForceClose,
|
||||||
@@ -60,7 +59,7 @@ pub struct App {
|
|||||||
impl eframe::App for App {
|
impl eframe::App for App {
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
ctx.request_repaint_after(Duration::from_millis(100));
|
ctx.request_repaint_after(Duration::from_millis(100));
|
||||||
self.empty_session.run_timers(&mut self.game_handle);
|
self.empty_session.run_timers().unwrap();
|
||||||
egui::TopBottomPanel::bottom("bottom_panel")
|
egui::TopBottomPanel::bottom("bottom_panel")
|
||||||
.exact_height(25.0)
|
.exact_height(25.0)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
@@ -122,10 +121,7 @@ impl App {
|
|||||||
ui.add_enabled_ui(!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(
|
&& features::empty_session::activate(&mut self.system_info).unwrap()
|
||||||
&mut self.game_handle,
|
|
||||||
&mut self.system_info,
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
self.empty_session.interval = Instant::now();
|
self.empty_session.interval = Instant::now();
|
||||||
self.empty_session.disabled = true;
|
self.empty_session.disabled = true;
|
||||||
@@ -334,7 +330,9 @@ impl Drop for App {
|
|||||||
settings: self.settings.clone(),
|
settings: self.settings.clone(),
|
||||||
}
|
}
|
||||||
.set();
|
.set();
|
||||||
// make sure we are not suspending game
|
// make sure we are not network blocking game
|
||||||
features::empty_session::deactivate(&mut self.game_handle);
|
if let Err(why) = features::empty_session::deactivate() {
|
||||||
|
log::error!("couldn't deactivate empty session: {why}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user