mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-23 19:49:05 +08:00
feat, topmost window, exclude from capture
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
98f56fd506
commit
c23f377039
6
build.py
6
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'],
|
||||
}
|
||||
}
|
||||
|
@ -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)) {
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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 (<doména>:<port>).\nPokud chcete přistupovat k zařízení na jiném serveru, připojte adresu serveru (<id>@<adresa_serveru>?key=<hodnota_klíče>), například,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nPokud chcete přistupovat k zařízení na veřejném serveru, zadejte \"<id>@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();
|
||||
|
@ -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();
|
||||
|
@ -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 (<domain>:<port>) eingeben.\nWenn Sie auf ein Gerät auf einem anderen Server zugreifen möchten, fügen Sie bitte die Serveradresse (<id>@<server_address>?key=<key_value>) 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 \"<id>@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();
|
||||
|
@ -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();
|
||||
|
@ -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 (<domain>:<port>).\nIf you want to access a device on another server, please append the server address (<id>@<server_address>?key=<key_value>), for example,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nIf you want to access a device on a public server, please input \"<id>@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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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 (<dominio>:<puerto>).\nSi quieres acceder a un dispositivo en otro servidor, por favor añade la ip del servidor (<id>@<dirección_servidor>?key=<clave_valor>), por ejemplo,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nSi quieres acceder a un dispositivo en un servidor público, por favor, introduce \"<id>@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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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 (<domain>:<port>).\nJika anda ingin mengakses perangkat lain yang berbeda server, tambahkan alamat server setelah penulisan ID(<id>@<server_address>?key=<key_value>), sebagai contoh,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nJika anda ingin mengakses perangkat yang menggunakan server publik, masukkan \"<id>@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();
|
||||
|
@ -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 (<dominio>:<porta>).\nSe vuoi accedere as un dispositivo in un altro server, aggiungi l'indirizzo del server (<id>@<indirizzo_server >?key=<valore_chiave>), ad esempio\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nSe vuoi accedere as un dispositivo in un server pubblico, inserisci \"<id>@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();
|
||||
|
@ -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();
|
||||
|
@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("id_input_tip", "입력된 ID, IP, 도메인과 포트(<domain>:<port>)를 입력할 수 있습니다.\n다른 서버에 있는 장치에 접속하려면 서버 주소(<id>@<server_address>?key=<key_value>)를 추가하세요"),
|
||||
("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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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 (<domēns>:<ports>).\nJa vēlaties piekļūt ierīcei citā serverī, lūdzu, pievienojiet servera adresi (<id>@<servera_adrese>?key=<atslēgas_vērtība>), piemēram,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nJa vēlaties piekļūt ierīcei publiskajā serverī, lūdzu, ievadiet \"<id>@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();
|
||||
|
@ -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 (<domein>:<poort>) invoeren. Als je toegang wilt als apparaat op een andere server, voeg dan het serveradres toe (<id>@<server_adres>?key=<key_value>), bijvoorbeeld \n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.Als je toegang wilt als apparaat op een openbare server, voer dan \"<id>@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();
|
||||
|
@ -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 (<adres_domenowy>:<port>).\nJeżeli chcesz uzyskać dostęp do urządzenia na innym serwerze, dołącz adres serwera (<id>@<adres_serwera>?key=<wartość_klucza>, np. \n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nJeżeli chcesz uzyskać dostęp do urządzenia na serwerze publicznym, wpisz \"<id>@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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -570,6 +570,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("id_input_tip", "Можно ввести идентификатор, прямой IP-адрес или домен с портом (<домен>:<порт>).\nЕсли необходимо получить доступ к устройству на другом сервере, добавьте адрес сервера (<id>@<адрес_сервера>?key=<ключ_значение>), например:\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nЕсли необходимо получить доступ к устройству на общедоступном сервере, введите \"<id>@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();
|
||||
|
@ -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 (<doména>:<port>).\nAk chcete získať prístup k zariadeniu na inom serveri, doplňte adresu servera (<id>@<adresa_servera>?key=<hodnota_kľúča>), napríklad,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nAk chcete získať prístup k zariadeniu na verejnom serveri, zadajte \"<id>@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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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Якщо ви хочете отримати доступ до пристрою на іншому сервері, будь ласка, додайте адресу сервера (<id>@<адреса_сервера>?key=<значення_ключа>), наприклад,\n9123456234@192.168.16.1:21117?key=5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=.\nЯкщо ви хочете отримати доступ до пристрою на публічному сервері, будь ласка, введіть \"<id>@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", "Вийти з режиму конфіденційності"),
|
||||
|
@ -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();
|
||||
|
@ -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<String>
|
||||
{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());
|
||||
}
|
||||
|
@ -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((
|
||||
|
3
src/privacy_mode/win_exclude_from_capture.rs
Normal file
3
src/privacy_mode/win_exclude_from_capture.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub use super::win_topmost_window::PrivacyModeImpl;
|
||||
|
||||
pub(super) const PRIVACY_MODE_IMPL: &str = "privacy_mode_impl_exclude_from_capture";
|
@ -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<bool> {
|
||||
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<PrivacyModeState>,
|
||||
) -> 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<u16> = 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<u16> = 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<HWND> {
|
||||
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),
|
||||
|
375
src/privacy_mode/win_topmost_window.rs
Normal file
375
src/privacy_mode/win_topmost_window.rs
Normal file
@ -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<bool> {
|
||||
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<PrivacyModeState>,
|
||||
) -> 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<u16> = 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<u16> = 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<HWND> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user