diff --git a/build.py b/build.py index 741c1c095..2a79fc1d6 100755 --- a/build.py +++ b/build.py @@ -57,9 +57,9 @@ def parse_rc_features(feature): }, 'PrivacyMode': { 'platform': ['windows'], - 'zip_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.2' - '/TempTopMostWindow_x64_pic_en.zip', - 'checksum_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.2/checksum_md5', + 'zip_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.3' + '/TempTopMostWindow_x64.zip', + 'checksum_url': 'https://github.com/fufesou/RustDeskTempTopMostWindow/releases/download/v0.3/checksum_md5', 'include': ['WindowInjection.dll'], } } diff --git a/libs/portable/src/main.rs b/libs/portable/src/main.rs index dc6f7bc84..6e3648c4e 100644 --- a/libs/portable/src/main.rs +++ b/libs/portable/src/main.rs @@ -94,11 +94,11 @@ mod windows { // Used for privacy mode(magnifier impl). pub const RUNTIME_BROKER_EXE: &'static str = "C:\\Windows\\System32\\RuntimeBroker.exe"; - pub const WIN_MAG_INJECTED_PROCESS_EXE: &'static str = "RuntimeBroker_rustdesk.exe"; + pub const WIN_TOPMOST_INJECTED_PROCESS_EXE: &'static str = "RuntimeBroker_rustdesk.exe"; pub(super) fn copy_runtime_broker(dir: &PathBuf) { let src = RUNTIME_BROKER_EXE; - let tgt = WIN_MAG_INJECTED_PROCESS_EXE; + let tgt = WIN_TOPMOST_INJECTED_PROCESS_EXE; let target_file = dir.join(tgt); if target_file.exists() { if let (Ok(src_file), Ok(tgt_file)) = (fs::read(src), fs::read(&target_file)) { diff --git a/src/lang/ar.rs b/src/lang/ar.rs index 95f0a8a04..e1de2ae9d 100644 --- a/src/lang/ar.rs +++ b/src/lang/ar.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/ca.rs b/src/lang/ca.rs index 37ac295aa..48bf4b7d0 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/cn.rs b/src/lang/cn.rs index a838701d5..ee4d2a06a 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", "模式 1 (不推荐)"), ("privacy_mode_impl_virtual_display_tip", "模式 2 (推荐)"), + ("privacy_mode_impl_exclude_from_capture_tip", "模式 3"), ("Enter privacy mode", "进入隐私模式"), ("Exit privacy mode", "退出隐私模式"), ].iter().cloned().collect(); diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 993f136f4..6ca08ea32 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", "Můžete zadat ID, přímou IP adresu nebo doménu s portem (:).\nPokud chcete přistupovat k zařízení na jiném serveru, připojte adresu serveru (@?key=), například,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nPokud chcete přistupovat k zařízení na veřejném serveru, zadejte \"@public\", klíč není pro veřejný server potřeba."), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/da.rs b/src/lang/da.rs index 7cbe8d99d..f5f79e9f9 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/de.rs b/src/lang/de.rs index 9017a744a..4437d9612 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", "Sie können eine ID, eine direkte IP oder eine Domäne mit einem Port (:) eingeben.\nWenn Sie auf ein Gerät auf einem anderen Server zugreifen möchten, fügen Sie bitte die Serveradresse (@?key=) hinzu, zum Beispiel\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nWenn Sie auf ein Gerät auf einem öffentlichen Server zugreifen wollen, geben Sie bitte \"@public\" ein. Der Schlüssel wird für öffentliche Server nicht benötigt."), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/el.rs b/src/lang/el.rs index 46577ce90..f27284e81 100644 --- a/src/lang/el.rs +++ b/src/lang/el.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/en.rs b/src/lang/en.rs index 3afb27bc6..9a6eebe33 100644 --- a/src/lang/en.rs +++ b/src/lang/en.rs @@ -205,5 +205,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", "You can input an ID, a direct IP, or a domain with a port (:).\nIf you want to access a device on another server, please append the server address (@?key=), for example,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nIf you want to access a device on a public server, please input \"@public\", the key is not needed for public server"), ("privacy_mode_impl_mag_tip", "Mode 1 (deprecated)"), ("privacy_mode_impl_virtual_display_tip", "Mode 2 (recommended)"), + ("privacy_mode_impl_exclude_from_capture_tip", "Mode 3"), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index 35230ccb1..00159bd0a 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/es.rs b/src/lang/es.rs index 5692ce9d3..8bf744ff2 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", "Puedes introducir una ID, una IP directa o un dominio con un puerto (:).\nSi quieres acceder a un dispositivo en otro servidor, por favor añade la ip del servidor (@?key=), por ejemplo,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nSi quieres acceder a un dispositivo en un servidor público, por favor, introduce \"@public\", la clave no es necesaria para un servidor público."), ("privacy_mode_impl_mag_tip", "Modo 1 (obsoleto)"), ("privacy_mode_impl_virtual_display_tip", "Modo 2 (recomendado)"), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", "Entrar al modo privado"), ("Exit privacy mode", "Salir del modo privado"), ].iter().cloned().collect(); diff --git a/src/lang/fa.rs b/src/lang/fa.rs index 0d8c8e356..3567e8d2e 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 7f840558e..a9297bd90 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/hu.rs b/src/lang/hu.rs index 298a07d6e..daa21390d 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/id.rs b/src/lang/id.rs index 7e81282fc..841aaedf9 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", "Anda bisa memasukkan ID, IP langsung, atau domain dengan port kostum yang sudah ditentukan (:).\nJika anda ingin mengakses perangkat lain yang berbeda server, tambahkan alamat server setelah penulisan ID(@?key=), sebagai contoh,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nJika anda ingin mengakses perangkat yang menggunakan server publik, masukkan \"@public\", server public tidak memerlukan key khusus"), ("privacy_mode_impl_mag_tip", "Mode 1 (deprecated)"), ("privacy_mode_impl_virtual_display_tip", "Mode 2 (direkomendasikan)"), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", "Masuk mode privasi"), ("Exit privacy mode", "Keluar mode privasi"), ].iter().cloned().collect(); diff --git a/src/lang/it.rs b/src/lang/it.rs index 99613c4d2..c3c86b52f 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", "Puoi inserire un ID, un IP diretto o un dominio con una porta (:).\nSe vuoi accedere as un dispositivo in un altro server, aggiungi l'indirizzo del server (@?key=), ad esempio\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nSe vuoi accedere as un dispositivo in un server pubblico, inserisci \"@public\", per il server pubblico la chiave non è necessaria"), ("privacy_mode_impl_mag_tip", "Modo 1 (deprecato)"), ("privacy_mode_impl_virtual_display_tip", "Modo 2 (consigliato)"), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", "Entra in modalità privacy"), ("Exit privacy mode", "Esci dalla modalità privacy"), ].iter().cloned().collect(); diff --git a/src/lang/ja.rs b/src/lang/ja.rs index a5c0dff2f..da0a9f8fa 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 8d8981575..c4c24fa70 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", "입력된 ID, IP, 도메인과 포트(:)를 입력할 수 있습니다.\n다른 서버에 있는 장치에 접속하려면 서버 주소(@?key=)를 추가하세요"), ("privacy_mode_impl_mag_tip", "모드 1(더 이상 사용되지 않음)"), ("privacy_mode_impl_virtual_display_tip", "모드 2(권장)"), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", "개인정보 보호 모드 사용"), ("Exit privacy mode", "개인정보 보호 모드 종료"), ].iter().cloned().collect(); diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 5d9ec3f3a..0abe19562 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/lt.rs b/src/lang/lt.rs index 69b210311..d445f3964 100644 --- a/src/lang/lt.rs +++ b/src/lang/lt.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/lv.rs b/src/lang/lv.rs index 63b941d00..e44f337a1 100644 --- a/src/lang/lv.rs +++ b/src/lang/lv.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", "Varat ievadīt ID, tiešo IP vai domēnu ar portu (:).\nJa vēlaties piekļūt ierīcei citā serverī, lūdzu, pievienojiet servera adresi (@?key=), piemēram,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nJa vēlaties piekļūt ierīcei publiskajā serverī, lūdzu, ievadiet \"@public\", publiskajam serverim atslēga nav nepieciešama"), ("privacy_mode_impl_mag_tip", "1. režīms (novecojis)"), ("privacy_mode_impl_virtual_display_tip", "2. režīms (ieteicams)"), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", "Ieiet privātuma režīmā"), ("Exit privacy mode", "Iziet no privātuma režīma"), ].iter().cloned().collect(); diff --git a/src/lang/nl.rs b/src/lang/nl.rs index 280f5e9a0..75abcdf34 100644 --- a/src/lang/nl.rs +++ b/src/lang/nl.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", "Je kunt een ID, een direct IP of een domein met een poort (:) invoeren. Als je toegang wilt als apparaat op een andere server, voeg dan het serveradres toe (@?key=), bijvoorbeeld \n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.Als je toegang wilt als apparaat op een openbare server, voer dan \"@public\" in, voor de openbare server is de sleutel niet nodig."), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/pl.rs b/src/lang/pl.rs index 13ed8942d..4481a5dfc 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", "Możesz wprowadzić identyfikator, bezpośredni adres IP lub domenę z portem (:).\nJeżeli chcesz uzyskać dostęp do urządzenia na innym serwerze, dołącz adres serwera (@?key=, np. \n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nJeżeli chcesz uzyskać dostęp do urządzenia na serwerze publicznym, wpisz \"@public\", klucz nie jest potrzebny dla serwera publicznego."), ("privacy_mode_impl_mag_tip", "Tryb 1 (przestarzały)"), ("privacy_mode_impl_virtual_display_tip", "Tryb 2 (zalecany)"), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", "Wejdź w tryb prywatności"), ("Exit privacy mode", "Wyjdź z trybu prywatności"), ].iter().cloned().collect(); diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 8ca5a0b84..25b024a0e 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index 708784c20..1c744f209 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/ro.rs b/src/lang/ro.rs index c1eac74f8..893053456 100644 --- a/src/lang/ro.rs +++ b/src/lang/ro.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/ru.rs b/src/lang/ru.rs index 9e184cb89..89a234d5a 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", "Можно ввести идентификатор, прямой IP-адрес или домен с портом (<домен>:<порт>).\nЕсли необходимо получить доступ к устройству на другом сервере, добавьте адрес сервера (@<адрес_сервера>?key=<ключ_значение>), например:\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nЕсли необходимо получить доступ к устройству на общедоступном сервере, введите \"@public\", ключ для публичного сервера не требуется."), ("privacy_mode_impl_mag_tip", "Режим 1 (устаревший)"), ("privacy_mode_impl_virtual_display_tip", "Режим 2 (рекомендуемый)"), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", "Включить режим конфиденциальности"), ("Exit privacy mode", "Отключить режим конфиденциальности"), ].iter().cloned().collect(); diff --git a/src/lang/sk.rs b/src/lang/sk.rs index 072b532e6..e6b4b9ff2 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", "Môžete zadať ID, priamu IP adresu alebo doménu s portom (:).\nAk chcete získať prístup k zariadeniu na inom serveri, doplňte adresu servera (@?key=), napríklad,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nAk chcete získať prístup k zariadeniu na verejnom serveri, zadajte \"@public\", kľúč nie je potrebný pre verejný server."), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/sl.rs b/src/lang/sl.rs index 382b8a89d..475394f5a 100755 --- a/src/lang/sl.rs +++ b/src/lang/sl.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/sq.rs b/src/lang/sq.rs index c47a154ca..d27e12199 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 832e81283..4c95bc0ab 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/sv.rs b/src/lang/sv.rs index b2e42a291..71552b873 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/template.rs b/src/lang/template.rs index 027baebad..d481815ce 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/th.rs b/src/lang/th.rs index b81b77b0a..f90e4d771 100644 --- a/src/lang/th.rs +++ b/src/lang/th.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/tr.rs b/src/lang/tr.rs index 3fc58963a..26214719f 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/tw.rs b/src/lang/tw.rs index 4ea5bc250..828a72831 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/lang/ua.rs b/src/lang/ua.rs index e1d301119..a8d5b5149 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -569,6 +569,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Enable blocking user input", "Блокувати введення для користувача"), ("id_input_tip", "Ви можете ввести ID, безпосередню IP, або ж домен з портом (<домен>:<порт>).\nЯкщо ви хочете отримати доступ до пристрою на іншому сервері, будь ласка, додайте адресу сервера (@<адреса_сервера>?key=<значення_ключа>), наприклад,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nЯкщо ви хочете отримати доступ до пристрою на публічному сервері, будь ласка, введіть \"@public\", ключ для публічного сервера не потрібен."), ("privacy_mode_impl_mag_tip", "Режим 1 (застарілий)"), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("privacy_mode_impl_virtual_display_tip", "Режим 2 (рекомендований)"), ("Enter privacy mode", "Увійти в режим конфіденційності"), ("Exit privacy mode", "Вийти з режиму конфіденційності"), diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 89b6a189f..b79939d9f 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("id_input_tip", ""), ("privacy_mode_impl_mag_tip", ""), ("privacy_mode_impl_virtual_display_tip", ""), + ("privacy_mode_impl_exclude_from_capture_tip", ""), ("Enter privacy mode", ""), ("Exit privacy mode", ""), ].iter().cloned().collect(); diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 02c5490fd..c0c7e4fc3 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -3,7 +3,7 @@ use crate::common::PORTABLE_APPNAME_RUNTIME_ENV_KEY; use crate::{ ipc, license::*, - privacy_mode::win_mag::{self, WIN_MAG_INJECTED_PROCESS_EXE}, + privacy_mode::win_topmost_window::{self, WIN_TOPMOST_INJECTED_PROCESS_EXE}, }; use hbb_common::{ allow_err, @@ -848,8 +848,8 @@ fn get_default_install_path() -> String { } pub fn check_update_broker_process() -> ResultType<()> { - let process_exe = win_mag::INJECTED_PROCESS_EXE; - let origin_process_exe = win_mag::ORIGIN_PROCESS_EXE; + let process_exe = win_topmost_window::INJECTED_PROCESS_EXE; + let origin_process_exe = win_topmost_window::ORIGIN_PROCESS_EXE; let exe_file = std::env::current_exe()?; let Some(cur_dir) = exe_file.parent() else { @@ -926,8 +926,8 @@ pub fn copy_exe_cmd(src_exe: &str, exe: &str, path: &str) -> ResultType {main_exe} copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\" ", - ORIGIN_PROCESS_EXE = win_mag::ORIGIN_PROCESS_EXE, - broker_exe = win_mag::INJECTED_PROCESS_EXE, + ORIGIN_PROCESS_EXE = win_topmost_window::ORIGIN_PROCESS_EXE, + broker_exe = win_topmost_window::INJECTED_PROCESS_EXE, )) } @@ -1157,7 +1157,7 @@ fn get_before_uninstall(kill_self: bool) -> String { reg delete HKEY_CLASSES_ROOT\\{ext} /f netsh advfirewall firewall delete rule name=\"{app_name} Service\" ", - broker_exe = WIN_MAG_INJECTED_PROCESS_EXE, + broker_exe = WIN_TOPMOST_INJECTED_PROCESS_EXE, ) } @@ -2164,7 +2164,7 @@ pub fn uninstall_service(show_new_window: bool) -> bool { taskkill /F /IM {app_name}.exe{filter} ", app_name = crate::get_app_name(), - broker_exe = WIN_MAG_INJECTED_PROCESS_EXE, + broker_exe = WIN_TOPMOST_INJECTED_PROCESS_EXE, ); if let Err(err) = run_cmds(cmds, false, "uninstall") { Config::set_option("stop-service".into(), "".into()); @@ -2279,7 +2279,10 @@ fn run_after_run_cmds(silent: bool) { pub fn try_kill_broker() { allow_err!(std::process::Command::new("cmd") .arg("/c") - .arg(&format!("taskkill /F /IM {}", WIN_MAG_INJECTED_PROCESS_EXE)) + .arg(&format!( + "taskkill /F /IM {}", + WIN_TOPMOST_INJECTED_PROCESS_EXE + )) .creation_flags(winapi::um::winbase::CREATE_NO_WINDOW) .spawn()); } diff --git a/src/privacy_mode.rs b/src/privacy_mode.rs index e8ec1fb53..811a96e3c 100644 --- a/src/privacy_mode.rs +++ b/src/privacy_mode.rs @@ -15,11 +15,14 @@ use std::{ sync::{Arc, Mutex}, }; +#[cfg(windows)] +pub mod win_exclude_from_capture; #[cfg(windows)] mod win_input; - #[cfg(windows)] pub mod win_mag; +#[cfg(windows)] +pub mod win_topmost_window; #[cfg(all(windows, feature = "virtual_display_driver"))] mod win_virtual_display; @@ -34,6 +37,9 @@ pub const NO_DISPLAYS: &'static str = "No displays"; #[cfg(windows)] pub const PRIVACY_MODE_IMPL_WIN_MAG: &str = win_mag::PRIVACY_MODE_IMPL; +#[cfg(windows)] +pub const PRIVACY_MODE_IMPL_WIN_EXCLUDE_FROM_CAPTURE: &str = + win_exclude_from_capture::PRIVACY_MODE_IMPL; #[cfg(all(windows, feature = "virtual_display_driver"))] pub const PRIVACY_MODE_IMPL_WIN_VIRTUAL_DISPLAY: &str = win_virtual_display::PRIVACY_MODE_IMPL; @@ -84,22 +90,23 @@ lazy_static::lazy_static! { pub static ref DEFAULT_PRIVACY_MODE_IMPL: String = { #[cfg(windows)] { - if display_service::is_privacy_mode_mag_supported() { - PRIVACY_MODE_IMPL_WIN_MAG - } else { - #[cfg(feature = "virtual_display_driver")] - { - if is_installed() { - PRIVACY_MODE_IMPL_WIN_VIRTUAL_DISPLAY - } else { - "" - } - } - #[cfg(not(feature = "virtual_display_driver"))] - { - "" - } - }.to_owned() + PRIVACY_MODE_IMPL_WIN_EXCLUDE_FROM_CAPTURE.to_owned() + // if display_service::is_privacy_mode_mag_supported() { + // PRIVACY_MODE_IMPL_WIN_MAG + // } else { + // #[cfg(feature = "virtual_display_driver")] + // { + // if is_installed() { + // PRIVACY_MODE_IMPL_WIN_VIRTUAL_DISPLAY + // } else { + // "" + // } + // } + // #[cfg(not(feature = "virtual_display_driver"))] + // { + // "" + // } + // }.to_owned() } #[cfg(not(windows))] { @@ -137,6 +144,10 @@ lazy_static::lazy_static! { Box::new(win_mag::PrivacyModeImpl::default()) }); + map.insert(win_exclude_from_capture::PRIVACY_MODE_IMPL, || { + Box::new(win_exclude_from_capture::PrivacyModeImpl::default()) + }); + #[cfg(feature = "virtual_display_driver")] map.insert(win_virtual_display::PRIVACY_MODE_IMPL, || { Box::new(win_virtual_display::PrivacyModeImpl::default()) @@ -272,6 +283,10 @@ pub fn get_supported_privacy_mode_impl() -> Vec<(&'static str, &'static str)> { if display_service::is_privacy_mode_mag_supported() { vec_impls.push((PRIVACY_MODE_IMPL_WIN_MAG, "privacy_mode_impl_mag_tip")); } + vec_impls.push(( + PRIVACY_MODE_IMPL_WIN_EXCLUDE_FROM_CAPTURE, + "privacy_mode_impl_exclude_from_capture_tip", + )); #[cfg(feature = "virtual_display_driver")] if is_installed() { vec_impls.push(( diff --git a/src/privacy_mode/win_exclude_from_capture.rs b/src/privacy_mode/win_exclude_from_capture.rs new file mode 100644 index 000000000..2ef752c5b --- /dev/null +++ b/src/privacy_mode/win_exclude_from_capture.rs @@ -0,0 +1,3 @@ +pub use super::win_topmost_window::PrivacyModeImpl; + +pub(super) const PRIVACY_MODE_IMPL: &str = "privacy_mode_impl_exclude_from_capture"; diff --git a/src/privacy_mode/win_mag.rs b/src/privacy_mode/win_mag.rs index 1df92a32d..a93dce350 100644 --- a/src/privacy_mode/win_mag.rs +++ b/src/privacy_mode/win_mag.rs @@ -1,370 +1,11 @@ -use super::{PrivacyMode, INVALID_PRIVACY_MODE_CONN_ID}; -use crate::{platform::windows::get_user_token, privacy_mode::PrivacyModeState}; -use hbb_common::{allow_err, bail, log, ResultType}; -use std::{ - ffi::CString, - time::{Duration, Instant}, -}; -use winapi::{ - shared::{ - minwindef::FALSE, - ntdef::{HANDLE, NULL}, - windef::HWND, - }, - um::{ - errhandlingapi::GetLastError, - handleapi::CloseHandle, - libloaderapi::{GetModuleHandleA, GetProcAddress}, - memoryapi::{VirtualAllocEx, WriteProcessMemory}, - processthreadsapi::{ - CreateProcessAsUserW, QueueUserAPC, ResumeThread, TerminateProcess, - PROCESS_INFORMATION, STARTUPINFOW, - }, - winbase::{WTSGetActiveConsoleSessionId, CREATE_SUSPENDED, DETACHED_PROCESS}, - winnt::{MEM_COMMIT, PAGE_READWRITE}, - winuser::*, - }, -}; +use super::win_topmost_window::PRIVACY_WINDOW_NAME; +use hbb_common::{bail, log, ResultType}; +use std::time::Instant; + +pub use super::win_topmost_window::PrivacyModeImpl; pub(super) const PRIVACY_MODE_IMPL: &str = "privacy_mode_impl_mag"; -pub const ORIGIN_PROCESS_EXE: &'static str = "C:\\Windows\\System32\\RuntimeBroker.exe"; -pub const WIN_MAG_INJECTED_PROCESS_EXE: &'static str = "RuntimeBroker_rustdesk.exe"; -pub const INJECTED_PROCESS_EXE: &'static str = WIN_MAG_INJECTED_PROCESS_EXE; -const PRIVACY_WINDOW_NAME: &'static str = "RustDeskPrivacyWindow"; - -struct WindowHandlers { - hthread: u64, - hprocess: u64, -} - -impl Drop for WindowHandlers { - fn drop(&mut self) { - self.reset(); - } -} - -impl WindowHandlers { - fn reset(&mut self) { - unsafe { - if self.hprocess != 0 { - let _res = TerminateProcess(self.hprocess as _, 0); - CloseHandle(self.hprocess as _); - } - self.hprocess = 0; - if self.hthread != 0 { - CloseHandle(self.hthread as _); - } - self.hthread = 0; - } - } - - fn is_default(&self) -> bool { - self.hthread == 0 && self.hprocess == 0 - } -} - -pub struct PrivacyModeImpl { - conn_id: i32, - handlers: WindowHandlers, -} - -impl Default for PrivacyModeImpl { - fn default() -> Self { - Self { - conn_id: INVALID_PRIVACY_MODE_CONN_ID, - handlers: WindowHandlers { - hthread: 0, - hprocess: 0, - }, - } - } -} - -impl PrivacyMode for PrivacyModeImpl { - fn init(&self) -> ResultType<()> { - Ok(()) - } - - fn clear(&mut self) { - allow_err!(self.turn_off_privacy(self.conn_id, None)); - } - - fn turn_on_privacy(&mut self, conn_id: i32) -> ResultType { - if self.check_on_conn_id(conn_id)? { - log::debug!("Privacy mode of conn {} is already on", conn_id); - return Ok(true); - } - - let exe_file = std::env::current_exe()?; - if let Some(cur_dir) = exe_file.parent() { - if !cur_dir.join("WindowInjection.dll").exists() { - return Ok(false); - } - } else { - bail!( - "Invalid exe parent for {}", - exe_file.to_string_lossy().as_ref() - ); - } - - if self.handlers.is_default() { - log::info!("turn_on_privacy, dll not found when started, try start"); - self.start()?; - std::thread::sleep(std::time::Duration::from_millis(1_000)); - } - - let hwnd = wait_find_privacy_hwnd(0)?; - if hwnd.is_null() { - bail!("No privacy window created"); - } - super::win_input::hook()?; - unsafe { - ShowWindow(hwnd as _, SW_SHOW); - } - self.conn_id = conn_id; - Ok(true) - } - - fn turn_off_privacy( - &mut self, - conn_id: i32, - state: Option, - ) -> ResultType<()> { - self.check_off_conn_id(conn_id)?; - super::win_input::unhook()?; - - unsafe { - let hwnd = wait_find_privacy_hwnd(0)?; - if !hwnd.is_null() { - ShowWindow(hwnd, SW_HIDE); - } - } - - if self.conn_id != INVALID_PRIVACY_MODE_CONN_ID { - if let Some(state) = state { - allow_err!(super::set_privacy_mode_state( - conn_id, - state, - PRIVACY_MODE_IMPL.to_string(), - 1_000 - )); - } - self.conn_id = INVALID_PRIVACY_MODE_CONN_ID.to_owned(); - } - - Ok(()) - } - - #[inline] - fn pre_conn_id(&self) -> i32 { - self.conn_id - } -} - -impl PrivacyModeImpl { - pub fn start(&mut self) -> ResultType<()> { - if self.handlers.hprocess != 0 { - return Ok(()); - } - - log::info!("Start privacy mode window broker, check_update_broker_process"); - if let Err(e) = crate::platform::windows::check_update_broker_process() { - log::warn!( - "Failed to check update broker process. Privacy mode may not work properly. {}", - e - ); - } - - let exe_file = std::env::current_exe()?; - let Some(cur_dir) = exe_file.parent() else { - bail!("Cannot get parent of current exe file"); - }; - - let dll_file = cur_dir.join("WindowInjection.dll"); - if !dll_file.exists() { - bail!( - "Failed to find required file {}", - dll_file.to_string_lossy().as_ref() - ); - } - - let hwnd = wait_find_privacy_hwnd(1_000)?; - if !hwnd.is_null() { - log::info!("Privacy window is ready"); - return Ok(()); - } - - // let cmdline = cur_dir.join("MiniBroker.exe").to_string_lossy().to_string(); - let cmdline = cur_dir - .join(INJECTED_PROCESS_EXE) - .to_string_lossy() - .to_string(); - - unsafe { - let cmd_utf16: Vec = cmdline.encode_utf16().chain(Some(0).into_iter()).collect(); - - let mut start_info = STARTUPINFOW { - cb: 0, - lpReserved: NULL as _, - lpDesktop: NULL as _, - lpTitle: NULL as _, - dwX: 0, - dwY: 0, - dwXSize: 0, - dwYSize: 0, - dwXCountChars: 0, - dwYCountChars: 0, - dwFillAttribute: 0, - dwFlags: 0, - wShowWindow: 0, - cbReserved2: 0, - lpReserved2: NULL as _, - hStdInput: NULL as _, - hStdOutput: NULL as _, - hStdError: NULL as _, - }; - let mut proc_info = PROCESS_INFORMATION { - hProcess: NULL as _, - hThread: NULL as _, - dwProcessId: 0, - dwThreadId: 0, - }; - - let session_id = WTSGetActiveConsoleSessionId(); - let token = get_user_token(session_id, true); - if token.is_null() { - bail!("Failed to get token of current user"); - } - - let create_res = CreateProcessAsUserW( - token, - NULL as _, - cmd_utf16.as_ptr() as _, - NULL as _, - NULL as _, - FALSE, - CREATE_SUSPENDED | DETACHED_PROCESS, - NULL, - NULL as _, - &mut start_info, - &mut proc_info, - ); - CloseHandle(token); - if 0 == create_res { - bail!( - "Failed to create privacy window process {}, code {}", - cmdline, - GetLastError() - ); - }; - - inject_dll( - proc_info.hProcess, - proc_info.hThread, - dll_file.to_string_lossy().as_ref(), - )?; - - if 0xffffffff == ResumeThread(proc_info.hThread) { - // CloseHandle - CloseHandle(proc_info.hThread); - CloseHandle(proc_info.hProcess); - - bail!( - "Failed to create privacy window process, {}", - GetLastError() - ); - } - - self.handlers.hthread = proc_info.hThread as _; - self.handlers.hprocess = proc_info.hProcess as _; - - let hwnd = wait_find_privacy_hwnd(1_000)?; - if hwnd.is_null() { - bail!("Failed to get hwnd after started"); - } - } - - Ok(()) - } - - #[inline] - pub fn stop(&mut self) { - self.handlers.reset(); - } -} - -impl Drop for PrivacyModeImpl { - fn drop(&mut self) { - if self.conn_id != INVALID_PRIVACY_MODE_CONN_ID { - allow_err!(self.turn_off_privacy(self.conn_id, None)); - } - } -} - -unsafe fn inject_dll<'a>(hproc: HANDLE, hthread: HANDLE, dll_file: &'a str) -> ResultType<()> { - let dll_file_utf16: Vec = dll_file.encode_utf16().chain(Some(0).into_iter()).collect(); - - let buf = VirtualAllocEx( - hproc, - NULL as _, - dll_file_utf16.len() * 2, - MEM_COMMIT, - PAGE_READWRITE, - ); - if buf.is_null() { - bail!("Failed VirtualAllocEx"); - } - - let mut written: usize = 0; - if 0 == WriteProcessMemory( - hproc, - buf, - dll_file_utf16.as_ptr() as _, - dll_file_utf16.len() * 2, - &mut written, - ) { - bail!("Failed WriteProcessMemory"); - } - - let kernel32_modulename = CString::new("kernel32")?; - let hmodule = GetModuleHandleA(kernel32_modulename.as_ptr() as _); - if hmodule.is_null() { - bail!("Failed GetModuleHandleA"); - } - - let load_librarya_name = CString::new("LoadLibraryW")?; - let load_librarya = GetProcAddress(hmodule, load_librarya_name.as_ptr() as _); - if load_librarya.is_null() { - bail!("Failed GetProcAddress of LoadLibraryW"); - } - - if 0 == QueueUserAPC(Some(std::mem::transmute(load_librarya)), hthread, buf as _) { - bail!("Failed QueueUserAPC"); - } - - Ok(()) -} - -fn wait_find_privacy_hwnd(msecs: u128) -> ResultType { - let tm_begin = Instant::now(); - let wndname = CString::new(PRIVACY_WINDOW_NAME)?; - loop { - unsafe { - let hwnd = FindWindowA(NULL as _, wndname.as_ptr() as _); - if !hwnd.is_null() { - return Ok(hwnd); - } - } - - if msecs == 0 || tm_begin.elapsed().as_millis() > msecs { - return Ok(NULL as _); - } - - std::thread::sleep(Duration::from_millis(100)); - } -} - pub fn create_capturer( privacy_mode_id: i32, origin: (i32, i32), diff --git a/src/privacy_mode/win_topmost_window.rs b/src/privacy_mode/win_topmost_window.rs new file mode 100644 index 000000000..6b94d58f1 --- /dev/null +++ b/src/privacy_mode/win_topmost_window.rs @@ -0,0 +1,375 @@ +use super::{PrivacyMode, INVALID_PRIVACY_MODE_CONN_ID}; +use crate::{platform::windows::get_user_token, privacy_mode::PrivacyModeState}; +use hbb_common::{allow_err, bail, log, ResultType}; +use std::{ + ffi::CString, + time::{Duration, Instant}, +}; +use winapi::{ + shared::{ + minwindef::FALSE, + ntdef::{HANDLE, NULL}, + windef::HWND, + }, + um::{ + errhandlingapi::GetLastError, + handleapi::CloseHandle, + libloaderapi::{GetModuleHandleA, GetProcAddress}, + memoryapi::{VirtualAllocEx, WriteProcessMemory}, + processthreadsapi::{ + CreateProcessAsUserW, QueueUserAPC, ResumeThread, TerminateProcess, + PROCESS_INFORMATION, STARTUPINFOW, + }, + winbase::{WTSGetActiveConsoleSessionId, CREATE_SUSPENDED, DETACHED_PROCESS}, + winnt::{MEM_COMMIT, PAGE_READWRITE}, + winuser::*, + }, +}; + +pub(super) const PRIVACY_MODE_IMPL: &str = "privacy_mode_impl_mag"; + +pub const ORIGIN_PROCESS_EXE: &'static str = "C:\\Windows\\System32\\RuntimeBroker.exe"; +pub const WIN_TOPMOST_INJECTED_PROCESS_EXE: &'static str = "RuntimeBroker_rustdesk.exe"; +pub const INJECTED_PROCESS_EXE: &'static str = WIN_TOPMOST_INJECTED_PROCESS_EXE; +pub(super) const PRIVACY_WINDOW_NAME: &'static str = "RustDeskPrivacyWindow"; + +struct WindowHandlers { + hthread: u64, + hprocess: u64, +} + +impl Drop for WindowHandlers { + fn drop(&mut self) { + self.reset(); + } +} + +impl WindowHandlers { + fn reset(&mut self) { + unsafe { + if self.hprocess != 0 { + let _res = TerminateProcess(self.hprocess as _, 0); + CloseHandle(self.hprocess as _); + } + self.hprocess = 0; + if self.hthread != 0 { + CloseHandle(self.hthread as _); + } + self.hthread = 0; + } + } + + fn is_default(&self) -> bool { + self.hthread == 0 && self.hprocess == 0 + } +} + +pub struct PrivacyModeImpl { + conn_id: i32, + handlers: WindowHandlers, + hwnd: u64, +} + +impl Default for PrivacyModeImpl { + fn default() -> Self { + Self { + conn_id: INVALID_PRIVACY_MODE_CONN_ID, + handlers: WindowHandlers { + hthread: 0, + hprocess: 0, + }, + hwnd: 0, + } + } +} + +impl PrivacyMode for PrivacyModeImpl { + fn init(&self) -> ResultType<()> { + Ok(()) + } + + fn clear(&mut self) { + allow_err!(self.turn_off_privacy(self.conn_id, None)); + } + + fn turn_on_privacy(&mut self, conn_id: i32) -> ResultType { + if self.check_on_conn_id(conn_id)? { + log::debug!("Privacy mode of conn {} is already on", conn_id); + return Ok(true); + } + + let exe_file = std::env::current_exe()?; + if let Some(cur_dir) = exe_file.parent() { + if !cur_dir.join("WindowInjection.dll").exists() { + return Ok(false); + } + } else { + bail!( + "Invalid exe parent for {}", + exe_file.to_string_lossy().as_ref() + ); + } + + if self.handlers.is_default() { + log::info!("turn_on_privacy, dll not found when started, try start"); + self.start()?; + std::thread::sleep(std::time::Duration::from_millis(1_000)); + } + + let hwnd = wait_find_privacy_hwnd(0)?; + if hwnd.is_null() { + bail!("No privacy window created"); + } + super::win_input::hook()?; + unsafe { + ShowWindow(hwnd as _, SW_SHOW); + } + self.conn_id = conn_id; + self.hwnd = hwnd as _; + Ok(true) + } + + fn turn_off_privacy( + &mut self, + conn_id: i32, + state: Option, + ) -> ResultType<()> { + self.check_off_conn_id(conn_id)?; + super::win_input::unhook()?; + + unsafe { + let hwnd = wait_find_privacy_hwnd(0)?; + if !hwnd.is_null() { + ShowWindow(hwnd, SW_HIDE); + } + } + + if self.conn_id != INVALID_PRIVACY_MODE_CONN_ID { + if let Some(state) = state { + allow_err!(super::set_privacy_mode_state( + conn_id, + state, + PRIVACY_MODE_IMPL.to_string(), + 1_000 + )); + } + self.conn_id = INVALID_PRIVACY_MODE_CONN_ID.to_owned(); + } + + Ok(()) + } + + #[inline] + fn pre_conn_id(&self) -> i32 { + self.conn_id + } +} + +impl PrivacyModeImpl { + #[inline] + pub fn get_hwnd(&self) -> u64 { + self.hwnd + } + + pub fn start(&mut self) -> ResultType<()> { + if self.handlers.hprocess != 0 { + return Ok(()); + } + + log::info!("Start privacy mode window broker, check_update_broker_process"); + if let Err(e) = crate::platform::windows::check_update_broker_process() { + log::warn!( + "Failed to check update broker process. Privacy mode may not work properly. {}", + e + ); + } + + let exe_file = std::env::current_exe()?; + let Some(cur_dir) = exe_file.parent() else { + bail!("Cannot get parent of current exe file"); + }; + + let dll_file = cur_dir.join("WindowInjection.dll"); + if !dll_file.exists() { + bail!( + "Failed to find required file {}", + dll_file.to_string_lossy().as_ref() + ); + } + + let hwnd = wait_find_privacy_hwnd(1_000)?; + if !hwnd.is_null() { + log::info!("Privacy window is ready"); + return Ok(()); + } + + // let cmdline = cur_dir.join("MiniBroker.exe").to_string_lossy().to_string(); + let cmdline = cur_dir + .join(INJECTED_PROCESS_EXE) + .to_string_lossy() + .to_string(); + + unsafe { + let cmd_utf16: Vec = cmdline.encode_utf16().chain(Some(0).into_iter()).collect(); + + let mut start_info = STARTUPINFOW { + cb: 0, + lpReserved: NULL as _, + lpDesktop: NULL as _, + lpTitle: NULL as _, + dwX: 0, + dwY: 0, + dwXSize: 0, + dwYSize: 0, + dwXCountChars: 0, + dwYCountChars: 0, + dwFillAttribute: 0, + dwFlags: 0, + wShowWindow: 0, + cbReserved2: 0, + lpReserved2: NULL as _, + hStdInput: NULL as _, + hStdOutput: NULL as _, + hStdError: NULL as _, + }; + let mut proc_info = PROCESS_INFORMATION { + hProcess: NULL as _, + hThread: NULL as _, + dwProcessId: 0, + dwThreadId: 0, + }; + + let session_id = WTSGetActiveConsoleSessionId(); + let token = get_user_token(session_id, true); + if token.is_null() { + bail!("Failed to get token of current user"); + } + + let create_res = CreateProcessAsUserW( + token, + NULL as _, + cmd_utf16.as_ptr() as _, + NULL as _, + NULL as _, + FALSE, + CREATE_SUSPENDED | DETACHED_PROCESS, + NULL, + NULL as _, + &mut start_info, + &mut proc_info, + ); + CloseHandle(token); + if 0 == create_res { + bail!( + "Failed to create privacy window process {}, code {}", + cmdline, + GetLastError() + ); + }; + + inject_dll( + proc_info.hProcess, + proc_info.hThread, + dll_file.to_string_lossy().as_ref(), + )?; + + if 0xffffffff == ResumeThread(proc_info.hThread) { + // CloseHandle + CloseHandle(proc_info.hThread); + CloseHandle(proc_info.hProcess); + + bail!( + "Failed to create privacy window process, {}", + GetLastError() + ); + } + + self.handlers.hthread = proc_info.hThread as _; + self.handlers.hprocess = proc_info.hProcess as _; + + let hwnd = wait_find_privacy_hwnd(1_000)?; + if hwnd.is_null() { + bail!("Failed to get hwnd after started"); + } + } + + Ok(()) + } + + #[inline] + pub fn stop(&mut self) { + self.handlers.reset(); + } +} + +impl Drop for PrivacyModeImpl { + fn drop(&mut self) { + if self.conn_id != INVALID_PRIVACY_MODE_CONN_ID { + allow_err!(self.turn_off_privacy(self.conn_id, None)); + } + } +} + +unsafe fn inject_dll<'a>(hproc: HANDLE, hthread: HANDLE, dll_file: &'a str) -> ResultType<()> { + let dll_file_utf16: Vec = dll_file.encode_utf16().chain(Some(0).into_iter()).collect(); + + let buf = VirtualAllocEx( + hproc, + NULL as _, + dll_file_utf16.len() * 2, + MEM_COMMIT, + PAGE_READWRITE, + ); + if buf.is_null() { + bail!("Failed VirtualAllocEx"); + } + + let mut written: usize = 0; + if 0 == WriteProcessMemory( + hproc, + buf, + dll_file_utf16.as_ptr() as _, + dll_file_utf16.len() * 2, + &mut written, + ) { + bail!("Failed WriteProcessMemory"); + } + + let kernel32_modulename = CString::new("kernel32")?; + let hmodule = GetModuleHandleA(kernel32_modulename.as_ptr() as _); + if hmodule.is_null() { + bail!("Failed GetModuleHandleA"); + } + + let load_librarya_name = CString::new("LoadLibraryW")?; + let load_librarya = GetProcAddress(hmodule, load_librarya_name.as_ptr() as _); + if load_librarya.is_null() { + bail!("Failed GetProcAddress of LoadLibraryW"); + } + + if 0 == QueueUserAPC(Some(std::mem::transmute(load_librarya)), hthread, buf as _) { + bail!("Failed QueueUserAPC"); + } + + Ok(()) +} + +pub(super) fn wait_find_privacy_hwnd(msecs: u128) -> ResultType { + let tm_begin = Instant::now(); + let wndname = CString::new(PRIVACY_WINDOW_NAME)?; + loop { + unsafe { + let hwnd = FindWindowA(NULL as _, wndname.as_ptr() as _); + if !hwnd.is_null() { + return Ok(hwnd); + } + } + + if msecs == 0 || tm_begin.elapsed().as_millis() > msecs { + return Ok(NULL as _); + } + + std::thread::sleep(Duration::from_millis(100)); + } +} +