From fc1af7b0d9c59790c76dc91dee84c4bae4b61a7e Mon Sep 17 00:00:00 2001 From: fufesou Date: Wed, 1 Jun 2022 23:57:58 +0800 Subject: [PATCH] privacy_mode_win_magnifier: fix UAC prompt window Signed-off-by: fufesou --- src/server/video_service.rs | 44 ++++++++++++++++-- src/ui/win_privacy.rs | 89 +++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 3 deletions(-) diff --git a/src/server/video_service.rs b/src/server/video_service.rs index b890e7b3d..fa9fabb52 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -19,6 +19,8 @@ // https://slhck.info/video/2017/03/01/rate-control.html use super::*; +#[cfg(windows)] +use crate::ui::win_privacy::win_event_hook; use hbb_common::tokio::sync::{ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, Mutex as TokioMutex, @@ -301,6 +303,24 @@ pub fn test_create_capturer(privacy_mode_id: i32, timeout_millis: u64) -> bool { false } +#[cfg(windows)] +fn check_uac_switch(privacy_mode_id: i32, captuerer_privacy_mode_id: i32) -> ResultType<()> { + if captuerer_privacy_mode_id != 0 { + if privacy_mode_id != captuerer_privacy_mode_id { + if !win_event_hook::is_process_consent_running()? { + bail!("consent.exe is running"); + } + } + //if win_event_hook::is_desktop_switched() { + if win_event_hook::is_process_consent_running()? { + // win_event_hook::reset_desktop_switch(); + bail!("consent.exe is running"); + } + //} + } + Ok(()) +} + fn run(sp: GenericService) -> ResultType<()> { #[cfg(windows)] ensure_close_virtual_device()?; @@ -322,11 +342,24 @@ fn run(sp: GenericService) -> ResultType<()> { ); let privacy_mode_id = *PRIVACY_MODE_CONN_ID.lock().unwrap(); + #[cfg(not(windows))] + let captuerer_privacy_mode_id = privacy_mode_id; + #[cfg(windows)] + let mut captuerer_privacy_mode_id = privacy_mode_id; + #[cfg(windows)] + if win_event_hook::is_process_consent_running()? { + captuerer_privacy_mode_id = 0; + } log::debug!( - "Try create capturer with privacy mode id {}", - privacy_mode_id, + "Try create capturer with captuerer privacy mode id {}", + captuerer_privacy_mode_id, ); - let mut c = create_capturer(privacy_mode_id, display)?; + if privacy_mode_id != captuerer_privacy_mode_id { + log::info!("In privacy mode, but show UAC prompt window for now"); + } else { + log::info!("In privacy mode, the peer side cannot watch the screen"); + } + let mut c = create_capturer(captuerer_privacy_mode_id, display)?; let q = get_image_quality(); let (bitrate, rc_min_quantizer, rc_max_quantizer, speed) = get_quality(width, height, q); @@ -373,6 +406,9 @@ fn run(sp: GenericService) -> ResultType<()> { #[cfg(windows)] log::info!("gdi: {}", c.is_gdi()); while sp.ok() { + #[cfg(windows)] + check_uac_switch(privacy_mode_id, captuerer_privacy_mode_id)?; + if *SWITCH.lock().unwrap() { bail!("SWITCH"); } @@ -480,6 +516,8 @@ fn run(sp: GenericService) -> ResultType<()> { let wait_begin = Instant::now(); while wait_begin.elapsed().as_millis() < timeout_millis as _ { check_privacy_mode_changed(&sp, privacy_mode_id)?; + #[cfg(windows)] + check_uac_switch(privacy_mode_id, captuerer_privacy_mode_id)?; frame_controller.try_wait_next(&mut fetched_conn_ids, 300); // break if all connections have received current frame if fetched_conn_ids.len() >= frame_controller.send_conn_ids.len() { diff --git a/src/ui/win_privacy.rs b/src/ui/win_privacy.rs index 11d0ff91b..b0919e2c8 100644 --- a/src/ui/win_privacy.rs +++ b/src/ui/win_privacy.rs @@ -552,6 +552,95 @@ pub(super) mod privacy_hook { } } +pub mod win_event_hook { + use hbb_common::{bail, lazy_static, ResultType}; + use std::sync::Mutex; + use winapi::{ + shared::{ + minwindef::DWORD, + ntdef::{LONG, NULL}, + windef::{HWINEVENTHOOK, HWND}, + winerror::RPC_E_CHANGED_MODE, + }, + um::{ + combaseapi::{CoInitializeEx, CoUninitialize}, + objbase::COINIT_MULTITHREADED, + winuser::{ + SetWinEventHook, UnhookWinEvent, EVENT_SYSTEM_DESKTOPSWITCH, WINEVENT_OUTOFCONTEXT, + WINEVENT_SKIPOWNPROCESS, + }, + }, + }; + + lazy_static::lazy_static! { + static ref DESKTOP_SWITCH: Mutex = Mutex::new(false); + } + + pub fn is_desktop_switched() -> bool { + *DESKTOP_SWITCH.lock().unwrap() + } + + pub fn reset_desktop_switch() { + *DESKTOP_SWITCH.lock().unwrap() = false; + } + + pub struct WinEventHook { + hook: HWINEVENTHOOK, + } + + impl WinEventHook { + fn create() -> ResultType { + unsafe { + if RPC_E_CHANGED_MODE == CoInitializeEx(NULL, COINIT_MULTITHREADED) { + bail!("Failed CoInitializeEx with RPC_E_CHANGED_MODE"); + } + + let hook = SetWinEventHook( + EVENT_SYSTEM_DESKTOPSWITCH, + EVENT_SYSTEM_DESKTOPSWITCH, + NULL as _, + Some(hook_win_event), + 0, + 0, + WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS, + ); + Ok(Self { hook }) + } + } + } + + impl Drop for WinEventHook { + fn drop(&mut self) { + unsafe { + UnhookWinEvent(self.hook); + CoUninitialize(); + } + } + } + + #[no_mangle] + pub extern "system" fn hook_win_event( + _hook: HWINEVENTHOOK, + event: DWORD, + _hwnd: HWND, + _id_object: LONG, + _id_child: LONG, + _dw_event_thread: DWORD, + _dwms_event_time: DWORD, + ) { + if event == EVENT_SYSTEM_DESKTOPSWITCH { + *DESKTOP_SWITCH.lock().unwrap() = true; + } + } + + pub fn is_process_consent_running() -> ResultType { + let output = std::process::Command::new("cmd") + .args(&["/C", "tasklist | findstr consent.exe"]) + .output()?; + Ok(output.status.success() && !output.stdout.is_empty()) + } +} + mod test { #[test] fn privacy_hook() {