From f08cb0412d32634475d75ac7e2a66a83dedad8b8 Mon Sep 17 00:00:00 2001 From: fufesou <13586388+fufesou@users.noreply.github.com> Date: Sun, 26 Jan 2025 19:39:38 +0800 Subject: [PATCH] fix: windows, dll, pre-loading attack (#10608) Signed-off-by: fufesou --- Cargo.lock | 2 +- libs/scrap/src/dxgi/mag.rs | 2 +- ...10.disable-loading-DLLs-from-app-dir.patch | 31 ++++++++ res/vcpkg/ffmpeg/portfile.cmake | 1 + src/core_main.rs | 5 +- src/flutter.rs | 41 +++++++++-- src/platform/windows.rs | 73 +++++++++++++++++-- 7 files changed, 138 insertions(+), 17 deletions(-) create mode 100644 res/vcpkg/ffmpeg/patch/0010.disable-loading-DLLs-from-app-dir.patch diff --git a/Cargo.lock b/Cargo.lock index de75588ad..4b7622595 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3077,7 +3077,7 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" version = "0.7.1" -source = "git+https://github.com/rustdesk-org/hwcodec#c4d6b1c5c4ddc7548868306004cf5d4eb614a36f" +source = "git+https://github.com/rustdesk-org/hwcodec#0ea7e709d3c48bb6446e33a9cc8fd0e0da5709b9" dependencies = [ "bindgen 0.59.2", "cc", diff --git a/libs/scrap/src/dxgi/mag.rs b/libs/scrap/src/dxgi/mag.rs index 923606a82..cc36b3c23 100644 --- a/libs/scrap/src/dxgi/mag.rs +++ b/libs/scrap/src/dxgi/mag.rs @@ -133,7 +133,7 @@ impl MagInterface { s.lib_handle = LoadLibraryExA( lib_file_name_c.as_ptr() as _, NULL, - LOAD_WITH_ALTERED_SEARCH_PATH, + LOAD_LIBRARY_SEARCH_SYSTEM32, ); if s.lib_handle.is_null() { return Err(Error::new( diff --git a/res/vcpkg/ffmpeg/patch/0010.disable-loading-DLLs-from-app-dir.patch b/res/vcpkg/ffmpeg/patch/0010.disable-loading-DLLs-from-app-dir.patch new file mode 100644 index 000000000..18da50b44 --- /dev/null +++ b/res/vcpkg/ffmpeg/patch/0010.disable-loading-DLLs-from-app-dir.patch @@ -0,0 +1,31 @@ +diff --git a/compat/w32dlfcn.h b/compat/w32dlfcn.h +index ac20e83..1e83aa6 100644 +--- a/compat/w32dlfcn.h ++++ b/compat/w32dlfcn.h +@@ -76,6 +76,7 @@ static inline HMODULE win32_dlopen(const char *name) + if (!name_w) + goto exit; + namelen = wcslen(name_w); ++ /* + // Try local directory first + path = get_module_filename(NULL); + if (!path) +@@ -91,6 +92,7 @@ static inline HMODULE win32_dlopen(const char *name) + path = new_path; + wcscpy(path + pathlen + 1, name_w); + module = LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); ++ */ + if (module == NULL) { + // Next try System32 directory + pathlen = GetSystemDirectoryW(path, pathsize); +@@ -131,7 +133,9 @@ exit: + return NULL; + module = LoadPackagedLibrary(name_w, 0); + #else +-#define LOAD_FLAGS (LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32) ++// #define LOAD_FLAGS (LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32) ++// Don't dynamic-link libraries from the application directory. ++ #define LOAD_FLAGS LOAD_LIBRARY_SEARCH_SYSTEM32 + /* filename may be be in CP_ACP */ + if (!name_w) + return LoadLibraryExA(name, NULL, LOAD_FLAGS); diff --git a/res/vcpkg/ffmpeg/portfile.cmake b/res/vcpkg/ffmpeg/portfile.cmake index 1d46535d5..9d09c5264 100644 --- a/res/vcpkg/ffmpeg/portfile.cmake +++ b/res/vcpkg/ffmpeg/portfile.cmake @@ -25,6 +25,7 @@ vcpkg_from_github( patch/0007-fix-linux-configure.patch patch/0008-remove-amf-loop-query.patch patch/0009-fix-nvenc-reconfigure-blur.patch + patch/0010.disable-loading-DLLs-from-app-dir.patch ) if(SOURCE_PATH MATCHES " ") diff --git a/src/core_main.rs b/src/core_main.rs index 23d7706d4..5264d5bfd 100644 --- a/src/core_main.rs +++ b/src/core_main.rs @@ -31,7 +31,10 @@ macro_rules! my_println{ pub fn core_main() -> Option> { crate::load_custom_client(); #[cfg(windows)] - crate::platform::windows::bootstrap(); + if !crate::platform::windows::bootstrap() { + // return None to terminate the process + return None; + } let mut args = Vec::new(); let mut flutter_args = Vec::new(); let mut i = 0; diff --git a/src/flutter.rs b/src/flutter.rs index fe0a77e39..255a00e0f 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -19,6 +19,7 @@ use serde_json::json; use std::{ collections::{HashMap, HashSet}, ffi::CString, + io::{Error as IoError, ErrorKind as IoErrorKind}, os::raw::{c_char, c_int, c_void}, str::FromStr, sync::{ @@ -50,7 +51,7 @@ lazy_static::lazy_static! { #[cfg(target_os = "windows")] lazy_static::lazy_static! { - pub static ref TEXTURE_RGBA_RENDERER_PLUGIN: Result = Library::open("texture_rgba_renderer_plugin.dll"); + pub static ref TEXTURE_RGBA_RENDERER_PLUGIN: Result = load_plugin_in_app_path("texture_rgba_renderer_plugin.dll"); } #[cfg(target_os = "linux")] @@ -65,7 +66,37 @@ lazy_static::lazy_static! { #[cfg(target_os = "windows")] lazy_static::lazy_static! { - pub static ref TEXTURE_GPU_RENDERER_PLUGIN: Result = Library::open("flutter_gpu_texture_renderer_plugin.dll"); + pub static ref TEXTURE_GPU_RENDERER_PLUGIN: Result = load_plugin_in_app_path("flutter_gpu_texture_renderer_plugin.dll"); +} + +// Move this function into `src/platform/windows.rs` if there're more calls to load plugins. +// Load dll with full path. +#[cfg(target_os = "windows")] +fn load_plugin_in_app_path(dll_name: &str) -> Result { + match std::env::current_exe() { + Ok(exe_file) => { + if let Some(cur_dir) = exe_file.parent() { + let full_path = cur_dir.join(dll_name); + if !full_path.exists() { + Err(LibError::OpeningLibraryError(IoError::new( + IoErrorKind::NotFound, + format!("{} not found", dll_name), + ))) + } else { + Library::open(full_path) + } + } else { + Err(LibError::OpeningLibraryError(IoError::new( + IoErrorKind::Other, + format!( + "Invalid exe parent for {}", + exe_file.to_string_lossy().as_ref() + ), + ))) + } + } + Err(e) => Err(LibError::OpeningLibraryError(e)), + } } /// FFI for rustdesk core's main entry. @@ -2076,11 +2107,7 @@ pub mod sessions { } pub(super) mod async_tasks { - use hbb_common::{ - bail, - tokio::{self, select}, - ResultType, - }; + use hbb_common::{bail, tokio, ResultType}; use std::{ collections::HashMap, sync::{ diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 92d02220e..6c0136128 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -18,12 +18,11 @@ use hbb_common::{ use std::{ collections::HashMap, ffi::{CString, OsString}, - fs, io, - io::prelude::*, + fs, + io::{self, prelude::*}, mem, os::windows::process::CommandExt, path::*, - process::{Command, Stdio}, ptr::null_mut, sync::{atomic::Ordering, Arc, Mutex}, time::{Duration, Instant}, @@ -32,11 +31,13 @@ use wallpaper; use winapi::{ ctypes::c_void, shared::{minwindef::*, ntdef::NULL, windef::*, winerror::*}, - um::sysinfoapi::{GetNativeSystemInfo, SYSTEM_INFO}, um::{ errhandlingapi::GetLastError, handleapi::CloseHandle, - libloaderapi::{GetProcAddress, LoadLibraryA}, + libloaderapi::{ + GetProcAddress, LoadLibraryExA, LoadLibraryExW, LOAD_LIBRARY_SEARCH_SYSTEM32, + LOAD_LIBRARY_SEARCH_USER_DIRS, + }, minwinbase::STILL_ACTIVE, processthreadsapi::{ GetCurrentProcess, GetCurrentProcessId, GetExitCodeProcess, OpenProcess, @@ -44,6 +45,7 @@ use winapi::{ }, securitybaseapi::GetTokenInformation, shellapi::ShellExecuteW, + sysinfoapi::{GetNativeSystemInfo, SYSTEM_INFO}, winbase::*, wingdi::*, winnt::{ @@ -1563,10 +1565,63 @@ pub fn is_win_10_or_greater() -> bool { unsafe { is_windows_10_or_greater() > 0 } } -pub fn bootstrap() { +pub fn bootstrap() -> bool { if let Ok(lic) = get_license_from_exe_name() { *config::EXE_RENDEZVOUS_SERVER.write().unwrap() = lic.host.clone(); } + + set_safe_load_dll() +} + +fn set_safe_load_dll() -> bool { + if !unsafe { set_default_dll_directories() } { + return false; + } + + // `SetDllDirectoryW` should never fail. + // https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setdlldirectoryw + if unsafe { SetDllDirectoryW(wide_string("").as_ptr()) == FALSE } { + eprintln!("SetDllDirectoryW failed: {}", io::Error::last_os_error()); + return false; + } + + true +} + +// https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-setdefaultdlldirectories +unsafe fn set_default_dll_directories() -> bool { + let module = LoadLibraryExW( + wide_string("Kernel32.dll").as_ptr(), + 0 as _, + LOAD_LIBRARY_SEARCH_SYSTEM32, + ); + if module.is_null() { + return false; + } + + match CString::new("SetDefaultDllDirectories") { + Err(e) => { + eprintln!("CString::new failed: {}", e); + return false; + } + Ok(func_name) => { + let func = GetProcAddress(module, func_name.as_ptr()); + if func.is_null() { + eprintln!("GetProcAddress failed: {}", io::Error::last_os_error()); + return false; + } + type SetDefaultDllDirectories = unsafe extern "system" fn(DWORD) -> BOOL; + let func: SetDefaultDllDirectories = std::mem::transmute(func); + if func(LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_USER_DIRS) == FALSE { + eprintln!( + "SetDefaultDllDirectories failed: {}", + io::Error::last_os_error() + ); + return false; + } + } + } + true } pub fn create_shortcut(id: &str) -> ResultType<()> { @@ -2530,7 +2585,11 @@ pub fn try_kill_rustdesk_main_window_process() -> ResultType<()> { fn nt_terminate_process(process_id: DWORD) -> ResultType<()> { type NtTerminateProcess = unsafe extern "system" fn(HANDLE, DWORD) -> DWORD; unsafe { - let h_module = LoadLibraryA(CString::new("ntdll.dll")?.as_ptr()); + let h_module = LoadLibraryExA( + CString::new("ntdll.dll")?.as_ptr(), + std::ptr::null_mut(), + LOAD_LIBRARY_SEARCH_SYSTEM32, + ); if !h_module.is_null() { let f_nt_terminate_process: NtTerminateProcess = std::mem::transmute(GetProcAddress( h_module,