Add Rust port

This commit is contained in:
darktohka
2026-03-17 03:10:07 +02:00
parent 64de0ade31
commit f8a1dde721
38 changed files with 4806 additions and 1 deletions
@@ -0,0 +1,7 @@
[package]
name = "clean_flash_common"
version = "34.0.0"
edition = "2021"
[dependencies]
windows-sys = { workspace = true }
@@ -0,0 +1,121 @@
use crate::uninstaller;
use std::fs;
use std::path::Path;
use std::thread;
use std::time::Duration;
/// Attempt to delete a single file, retrying with escalating measures if needed.
pub fn delete_file(path: &Path) {
if !path.exists() {
return;
}
// Unregister ActiveX .ocx files before deletion.
if let Some(ext) = path.extension() {
if ext.eq_ignore_ascii_case("ocx") {
let _ = uninstaller::unregister_activex(&path.to_string_lossy());
}
}
// First attempt: clear read-only and delete.
if try_clear_readonly_and_delete(path) {
return;
}
// Retry loop with ownership acquisition.
for _ in 0..10 {
if try_take_ownership_and_delete(path) {
return;
}
thread::sleep(Duration::from_millis(500));
}
// Last resort: kill any processes using the file, then delete.
kill_locking_processes(path);
thread::sleep(Duration::from_millis(500));
let _ = fs::remove_file(path);
}
/// Recursively delete all files (optionally matching `filename`) under `base_dir`.
pub fn recursive_delete(base_dir: &Path, filename: Option<&str>) {
if !base_dir.exists() {
return;
}
let entries: Vec<_> = match fs::read_dir(base_dir) {
Ok(rd) => rd.filter_map(|e| e.ok()).collect(),
Err(_) => return,
};
for entry in entries {
let path = entry.path();
if path.is_dir() {
recursive_delete(&path, filename);
} else if path.is_file() {
// Sanity check: path must start with the original base_dir.
if !path.starts_with(base_dir) {
continue;
}
let should_delete = match filename {
Some(name) => path
.file_name()
.map(|f| f == name)
.unwrap_or(false),
None => true,
};
if should_delete {
delete_file(&path);
}
}
}
}
/// Delete all files in a folder, then try to remove the folder itself.
pub fn wipe_folder(path: &Path) {
if !path.exists() {
return;
}
recursive_delete(path, None);
// If folder is now empty, remove it.
if is_dir_empty(path) {
if fs::remove_dir(path).is_err() {
kill_locking_processes(path);
thread::sleep(Duration::from_millis(500));
let _ = fs::remove_dir(path);
}
}
}
fn try_clear_readonly_and_delete(path: &Path) -> bool {
if let Ok(meta) = fs::metadata(path) {
let mut perms = meta.permissions();
#[allow(clippy::permissions_set_readonly_false)]
perms.set_readonly(false);
let _ = fs::set_permissions(path, perms);
}
fs::remove_file(path).is_ok()
}
fn try_take_ownership_and_delete(path: &Path) -> bool {
// On Windows we could use SetNamedSecurityInfo to take ownership.
// For simplicity the Rust port clears read-only and retries.
try_clear_readonly_and_delete(path)
}
fn kill_locking_processes(path: &Path) {
// Use taskkill as a best-effort approach.
// The C# original enumerates all open handles, which requires complex
// NT API calls. A simplified approach is acceptable for the port.
let _ = path; // Locking-process detection is a best-effort no-op here.
}
fn is_dir_empty(path: &Path) -> bool {
fs::read_dir(path)
.map(|mut rd| rd.next().is_none())
.unwrap_or(true)
}
+51
View File
@@ -0,0 +1,51 @@
pub mod file_util;
pub mod process_utils;
pub mod redirection;
pub mod registry;
pub mod resources;
pub mod system_info;
pub mod uninstaller;
pub mod update_checker;
pub mod winapi_helpers;
use std::fmt;
/// Progress callback trait, analogous to C# IProgressForm.
pub trait ProgressCallback: Send {
fn update_progress_label(&self, text: &str, tick: bool);
fn tick_progress(&self);
}
/// Install/uninstall error type.
#[derive(Debug)]
pub struct InstallError {
pub message: String,
}
impl InstallError {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
}
impl fmt::Display for InstallError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for InstallError {}
/// Result of running an external process.
pub struct ExitedProcess {
pub exit_code: i32,
pub output: String,
}
impl ExitedProcess {
pub fn is_successful(&self) -> bool {
self.exit_code == 0
}
}
@@ -0,0 +1,110 @@
use crate::ExitedProcess;
use std::process::{Command, Stdio};
#[cfg(target_os = "windows")]
use std::os::windows::process::CommandExt;
const CREATE_NO_WINDOW: u32 = 0x08000000;
/// Run a process, capturing stdout and stderr, and wait for it to exit.
pub fn run_process(program: &str, args: &[&str]) -> ExitedProcess {
let mut cmd = Command::new(program);
cmd.args(args)
.stdout(Stdio::piped())
.stderr(Stdio::piped());
#[cfg(target_os = "windows")]
cmd.creation_flags(CREATE_NO_WINDOW);
let result = cmd.output();
match result {
Ok(output) => {
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let combined = format!("{}{}", stdout.trim(), stderr.trim());
ExitedProcess {
exit_code: output.status.code().unwrap_or(-1),
output: combined,
}
}
Err(e) => ExitedProcess {
exit_code: -1,
output: e.to_string(),
},
}
}
/// Run a process and wait for it to exit (no output capture).
pub fn run_unmanaged_process(program: &str, args: &[&str]) {
let mut cmd = Command::new(program);
cmd.args(args)
.stdout(Stdio::null())
.stderr(Stdio::null());
#[cfg(target_os = "windows")]
cmd.creation_flags(CREATE_NO_WINDOW);
let _ = cmd.status();
}
/// Collect the names of DLL modules loaded in a given process.
/// Used to detect whether a browser has Flash DLLs loaded.
pub fn collect_modules(pid: u32) -> Vec<String> {
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::System::ProcessStatus::{
EnumProcessModulesEx, GetModuleFileNameExW, LIST_MODULES_ALL,
};
use windows_sys::Win32::System::Threading::{
OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ,
};
let mut modules = Vec::new();
unsafe {
let handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, pid);
if handle.is_null() {
return modules;
}
#[allow(unused_imports)]
use std::ptr;
// HMODULE is *mut c_void on windows-sys 0.59
let mut h_modules: [*mut std::ffi::c_void; 1024] =
[std::ptr::null_mut(); 1024];
let mut cb_needed: u32 = 0;
let ok = EnumProcessModulesEx(
handle,
h_modules.as_mut_ptr().cast(),
std::mem::size_of_val(&h_modules) as u32,
&mut cb_needed,
LIST_MODULES_ALL,
);
if ok != 0 {
let count =
cb_needed as usize / std::mem::size_of::<*mut std::ffi::c_void>();
for item in h_modules.iter().take(count.min(h_modules.len())) {
let mut name_buf = [0u16; 512];
let len = GetModuleFileNameExW(
handle,
*item as _,
name_buf.as_mut_ptr(),
name_buf.len() as u32,
);
if len > 0 {
let full_path =
String::from_utf16_lossy(&name_buf[..len as usize]);
if let Some(file_name) = full_path.rsplit('\\').next() {
modules.push(file_name.to_string());
}
}
}
}
CloseHandle(handle);
}
modules
}
@@ -0,0 +1,20 @@
/// Disable WoW64 file system redirection. Returns a cookie to restore later.
pub fn disable_redirection() -> *mut std::ffi::c_void {
let mut old_value: *mut std::ffi::c_void = std::ptr::null_mut();
unsafe {
Wow64DisableWow64FsRedirection(&mut old_value);
}
old_value
}
/// Re-enable WoW64 file system redirection using the cookie from `disable_redirection`.
pub fn enable_redirection(old_value: *mut std::ffi::c_void) {
unsafe {
Wow64RevertWow64FsRedirection(old_value);
}
}
extern "system" {
fn Wow64DisableWow64FsRedirection(old_value: *mut *mut std::ffi::c_void) -> i32;
fn Wow64RevertWow64FsRedirection(old_value: *mut std::ffi::c_void) -> i32;
}
@@ -0,0 +1,42 @@
use crate::{process_utils, system_info, InstallError};
use std::fs;
use std::io::Write;
/// Apply registry contents by writing a .reg file and importing with reg.exe.
pub fn apply_registry(entries: &[&str]) -> Result<(), InstallError> {
let combined = entries.join("\n\n");
let filled = system_info::fill_string(&combined);
let content = format!("Windows Registry Editor Version 5.00\n\n{}", filled);
let temp_dir = std::env::temp_dir();
let reg_file = temp_dir.join("cleanflash_reg.tmp");
// Write as UTF-16LE with BOM (Windows .reg format).
{
let mut f = fs::File::create(&reg_file)
.map_err(|e| InstallError::new(format!("Failed to create temp reg file: {}", e)))?;
let utf16: Vec<u16> = content.encode_utf16().collect();
// BOM
f.write_all(&[0xFF, 0xFE])
.map_err(|e| InstallError::new(format!("Failed to write BOM: {}", e)))?;
for word in &utf16 {
f.write_all(&word.to_le_bytes())
.map_err(|e| InstallError::new(format!("Failed to write reg data: {}", e)))?;
}
}
let reg_filename = reg_file.to_string_lossy().to_string();
let result = process_utils::run_process("reg.exe", &["import", &reg_filename]);
let _ = fs::remove_file(&reg_file);
if !result.is_successful() {
return Err(InstallError::new(format!(
"Failed to apply changes to registry: error code {}\n\n{}",
result.exit_code, result.output
)));
}
Ok(())
}
@@ -0,0 +1,224 @@
/// Embedded registry resources, matching the original C# resource strings.
pub const UNINSTALL_REGISTRY: &str = r#"[HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\MAIN\FeatureControl\FEATURE_BROWSER_EMULATION]
"FlashHelperService.exe"=-
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Control Panel\Extended Properties\System.ControlPanel.Category]
"${SYSTEM_64_PATH}\\FlashPlayerCPLApp.cpl"=-
[-HKEY_CURRENT_USER\Software\FlashCenter]
[-HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\App Paths\FlashCenter.exe]
[-HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall\FlashCenter]
[-HKEY_LOCAL_MACHINE\Software\Classes\AppID\{119DA84B-E3DB-4D47-A8DD-7FF6D5804689}]
[-HKEY_LOCAL_MACHINE\Software\Classes\AppID\{B9020634-CE8F-4F09-9FBC-D108A73A4676}]
[-HKEY_LOCAL_MACHINE\Software\Classes\TypeLib\{37EF68ED-16D3-4191-86BF-AB731D75AAB7}]
[-HKEY_LOCAL_MACHINE\System\ControlSet001\services\Flash Helper Service]
[-HKEY_LOCAL_MACHINE\System\ControlSet001\services\FlashCenterService]
[-HKEY_LOCAL_MACHINE\System\CurrentControlSet\services\Flash Helper Service]
[-HKEY_LOCAL_MACHINE\System\CurrentControlSet\services\FlashCenterService]
[-HKEY_LOCAL_MACHINE\Software\Classes\MacromediaFlashPaper.MacromediaFlashPaper]
[-HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Low Rights\ElevationPolicy\{FAF199D2-BFA7-4394-A4DE-044A08E59B32}]
[-HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\FlashPlayerUpdateService.exe]
[-HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\FlashUtil32_${VERSION_PATH}_ActiveX.exe]
[-HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\FlashUtil64_${VERSION_PATH}_ActiveX.exe]
[-HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\FlashUtil32_${VERSION_PATH}_Plugin.exe]
[-HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\FlashUtil64_${VERSION_PATH}_Plugin.exe]
[-HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\FlashPlayerPlugin_${VERSION_PATH}.exe]
[-HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\FlashUtil32_${VERSION_PATH}_pepper.exe]
[-HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\FlashUtil64_${VERSION_PATH}_pepper.exe]
[-HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Internet Explorer\Low Rights\ElevationPolicy\{FAF199D2-BFA7-4394-A4DE-044A08E59B32}]
[-HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Adobe Flash Player ActiveX]
[-HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Adobe Flash Player NPAPI]
[-HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Adobe Flash Player PPAPI]
[-HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\Clean Flash Player]
[-HKEY_LOCAL_MACHINE\Software\Classes\.mfp]
[-HKEY_LOCAL_MACHINE\Software\Classes\.sol]
[-HKEY_LOCAL_MACHINE\Software\Classes\.sor]
[-HKEY_LOCAL_MACHINE\Software\Classes\.spl]
[-HKEY_LOCAL_MACHINE\Software\Classes\.swf]
[-HKEY_LOCAL_MACHINE\Software\Classes\AppID\{B9020634-CE8F-4F09-9FBC-D108A73A4676}]
[-HKEY_LOCAL_MACHINE\Software\Classes\CLSID\{B019E3BF-E7E5-453C-A2E4-D2C18CA0866F}]
[-HKEY_LOCAL_MACHINE\Software\Classes\CLSID\{D27CDB6E-AE6D-11cf-96B8-444553540000}]
[-HKEY_LOCAL_MACHINE\Software\Classes\FlashFactory.FlashFactory]
[-HKEY_LOCAL_MACHINE\Software\Classes\FlashFactory.FlashFactory.1]
[-HKEY_LOCAL_MACHINE\Software\Classes\Interface\{299817DA-1FAC-4CE2-8F48-A108237013BD}]
[-HKEY_LOCAL_MACHINE\Software\Classes\Interface\{307F64C0-621D-4D56-BBC6-91EFC13CE40D}]
[-HKEY_LOCAL_MACHINE\Software\Classes\Interface\{57A0E747-3863-4D20-A811-950C84F1DB9B}]
[-HKEY_LOCAL_MACHINE\Software\Classes\Interface\{86230738-D762-4C50-A2DE-A753E5B1686F}]
[-HKEY_LOCAL_MACHINE\Software\Classes\Interface\{D27CDB6C-AE6D-11CF-96B8-444553540000}]
[-HKEY_LOCAL_MACHINE\Software\Classes\Interface\{D27CDB6D-AE6D-11CF-96B8-444553540000}]
[-HKEY_LOCAL_MACHINE\Software\Classes\MIME\Database\Content Type\application/futuresplash]
[-HKEY_LOCAL_MACHINE\Software\Classes\MIME\Database\Content Type\application/x-shockwave-flash]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.1]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.2]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.3]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.4]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.5]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.6]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.7]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.8]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.9]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.10]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.11]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.12]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.13]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.14]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.15]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.16]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.17]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.18]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.19]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.20]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.21]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.22]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.23]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.24]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.25]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.26]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.27]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.28]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.29]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.30]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.31]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.32]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.33]
[-HKEY_LOCAL_MACHINE\Software\Classes\ShockwaveFlash.ShockwaveFlash.34]
[-HKEY_LOCAL_MACHINE\Software\Classes\TypeLib\{57A0E746-3863-4D20-A811-950C84F1DB9B}]
[-HKEY_LOCAL_MACHINE\Software\Classes\TypeLib\{D27CDB6B-AE6D-11CF-96B8-444553540000}]
[-HKEY_LOCAL_MACHINE\Software\Classes\TypeLib\{FAB3E735-69C7-453B-A446-B6823C6DF1C9}]
[-HKEY_LOCAL_MACHINE\Software\Macromedia]
[-HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\ActiveX Compatibility\{D27CDB6E-AE6D-11CF-96B8-444553540000}]
[-HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\ActiveX Compatibility\{D27CDB70-AE6D-11cf-96B8-444553540000}]
[-HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\NavigatorPluginsList\Shockwave Flash]
[-HKEY_LOCAL_MACHINE\Software\MozillaPlugins\@adobe.com/FlashPlayer]
[-HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\miniconfig]
[-HKEY_USERS\.DEFAULT\Software\Microsoft\Windows\CurrentVersion\miniconfig]"#;
pub const UNINSTALL_REGISTRY_64: &str = r#"[HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Internet Explorer\MAIN\FeatureControl\FEATURE_BROWSER_EMULATION]
"FlashHelperService.exe"=-
[HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Control Panel\Extended Properties\System.ControlPanel.Category]
"${SYSTEM_32_PATH}\\FlashPlayerCPLApp.cpl"=-
[-HKEY_LOCAL_MACHINE\Software\Classes\Wow6432Node\CLSID\{B019E3BF-E7E5-453C-A2E4-D2C18CA0866F}]
[-HKEY_LOCAL_MACHINE\Software\Classes\Wow6432Node\CLSID\{D27CDB6E-AE6D-11cf-96B8-444553540000}]
[-HKEY_LOCAL_MACHINE\Software\Classes\Wow6432Node\CLSID\{D27CDB70-AE6D-11cf-96B8-444553540000}]
[-HKEY_LOCAL_MACHINE\Software\Classes\Wow6432Node\Interface\{299817DA-1FAC-4CE2-8F48-A108237013BD}]
[-HKEY_LOCAL_MACHINE\Software\Classes\Wow6432Node\Interface\{307F64C0-621D-4D56-BBC6-91EFC13CE40D}]
[-HKEY_LOCAL_MACHINE\Software\Classes\Wow6432Node\Interface\{57A0E747-3863-4D20-A811-950C84F1DB9B}]
[-HKEY_LOCAL_MACHINE\Software\Classes\Wow6432Node\Interface\{86230738-D762-4C50-A2DE-A753E5B1686F}]
[-HKEY_LOCAL_MACHINE\Software\Classes\Wow6432Node\Interface\{D27CDB6C-AE6D-11CF-96B8-444553540000}]
[-HKEY_LOCAL_MACHINE\Software\Classes\Wow6432Node\Interface\{D27CDB6D-AE6D-11CF-96B8-444553540000}]
[-HKEY_LOCAL_MACHINE\Software\Wow6432Node\Macromedia]
[-HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Internet Explorer\ActiveX Compatibility\{D27CDB6E-AE6D-11CF-96B8-444553540000}]
[-HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Internet Explorer\ActiveX Compatibility\{D27CDB70-AE6D-11cf-96B8-444553540000}]
[-HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Internet Explorer\NavigatorPluginsList\Shockwave Flash]
[-HKEY_LOCAL_MACHINE\Software\Wow6432Node\MozillaPlugins\@adobe.com/FlashPlayer]"#;
pub const INSTALL_GENERAL: &str = r#"[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Control Panel\Extended Properties\System.ControlPanel.Category]
"${SYSTEM_32_PATH}\\FlashPlayerCPLApp.cpl"=dword:0000000a
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\FlashPlayerApp.exe]
"DisableExceptionChainValidation"=dword:00000000
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\Clean Flash Player]
"DisplayName"="Clean Flash Player ${VERSION}"
"HelpLink"="https://gitlab.com/cleanflash/installer#clean-flash-player"
"NoModify"=dword:00000001
"NoRepair"=dword:00000001
"URLInfoAbout"="https://gitlab.com/cleanflash/installer#clean-flash-player"
"URLUpdateInfo"="https://gitlab.com/cleanflash/installer#clean-flash-player"
"VersionMajor"=dword:00000022
"VersionMinor"=dword:00000000
"Publisher"="CleanFlash Team"
"EstimatedSize"=dword:00011cb8
"DisplayIcon"="${PROGRAM_FLASH_32_PATH}\\FlashUtil_Uninstall.exe"
"UninstallString"="${PROGRAM_FLASH_32_PATH}\\FlashUtil_Uninstall.exe"
"DisplayVersion"="${VERSION}""#;
pub const INSTALL_GENERAL_64: &str = r#"[HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Control Panel\Extended Properties\System.ControlPanel.Category]
"${SYSTEM_32_PATH}\\FlashPlayerCPLApp.cpl"=dword:0000000a
[HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\FlashPlayerApp.exe]
"DisableExceptionChainValidation"=dword:00000000"#;
pub const INSTALL_NP: &str = r#"[HKEY_LOCAL_MACHINE\Software\Macromedia\FlashPlayerPlugin]
"isPartner"=dword:00000001
"Version"="${VERSION}"
"PlayerPath"="${FLASH_64_PATH}\\NPSWF${ARCH}_${VERSION_PATH}.dll"
"UninstallerPath"=-
"isScriptDebugger"=dword:00000000
"isESR"=dword:00000000
"isMSI"=dword:00000000
[HKEY_LOCAL_MACHINE\Software\Macromedia\FlashPlayerPluginReleaseType]
"Release"=dword:00000001
[HKEY_LOCAL_MACHINE\Software\MozillaPlugins\@adobe.com/FlashPlayer]
"Vendor"="Adobe"
"ProductName"="Adobe® Flash® Player ${VERSION} Plugin"
"Path"="${FLASH_64_PATH}\\NPSWF${ARCH}_${VERSION_PATH}.dll"
"Version"="${VERSION}"
"Description"="Adobe® Flash® Player ${VERSION} Plugin"
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\FlashPlayerPlugin_${VERSION_PATH}.exe]
"DisableExceptionChainValidation"=dword:00000000"#;
pub const INSTALL_NP_64: &str = r#"[HKEY_LOCAL_MACHINE\Software\Wow6432Node\Macromedia\FlashPlayerPlugin]
"PlayerPath"="${FLASH_32_PATH}\\NPSWF_${VERSION_PATH}.dll"
"Version"="${VERSION}"
"UninstallerPath"=-
"isScriptDebugger"=dword:00000000
"isESR"=dword:00000000
"isMSI"=dword:00000000
"isPartner"=dword:00000001
[HKEY_LOCAL_MACHINE\Software\Wow6432Node\Macromedia\FlashPlayerPluginReleaseType]
"Release"=dword:00000001
[HKEY_LOCAL_MACHINE\Software\Wow6432Node\MozillaPlugins\@adobe.com/FlashPlayer]
"ProductName"="Adobe® Flash® Player ${VERSION} Plugin"
"Description"="Adobe® Flash® Player ${VERSION} Plugin"
"Version"="${VERSION}"
"XPTPath"="${FLASH_32_PATH}\\flashplayer.xpt"
"Vendor"="Adobe"
"Path"="${FLASH_32_PATH}\\NPSWF32_${VERSION_PATH}.dll"
[HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\FlashPlayerPlugin_${VERSION_PATH}.exe]
"DisableExceptionChainValidation"=dword:00000000"#;
pub const INSTALL_PP: &str = r#"[HKEY_LOCAL_MACHINE\Software\Macromedia\FlashPlayerPepper]
"UninstallerPath"=-
"PlayerPath"="${FLASH_64_PATH}\\pepflashplayer${ARCH}_${VERSION_PATH}.dll"
"isScriptDebugger"=dword:00000000
"isESR"=dword:00000000
"isMSI"=dword:00000000
"isPartner"=dword:00000001
"Version"="${VERSION}"
[HKEY_LOCAL_MACHINE\Software\Macromedia\FlashPlayerPepperReleaseType]
"Release"=dword:00000001"#;
pub const INSTALL_PP_64: &str = r#"[HKEY_LOCAL_MACHINE\Software\Wow6432Node\Macromedia\FlashPlayerPepper]
"UninstallerPath"=-
"PlayerPath"="${FLASH_32_PATH}\\pepflashplayer32_${VERSION_PATH}.dll"
"isScriptDebugger"=dword:00000000
"isESR"=dword:00000000
"isMSI"=dword:00000000
"isPartner"=dword:00000001
"Version"="${VERSION}"
[HKEY_LOCAL_MACHINE\Software\Wow6432Node\Macromedia\FlashPlayerPepperReleaseType]
"Release"=dword:00000001"#;
@@ -0,0 +1,179 @@
use crate::update_checker;
use std::collections::HashMap;
use std::env;
use std::path::{Path, PathBuf};
/// Lazily-initialized system path info, analogous to C# SystemInfo class.
pub struct SystemInfo {
pub system32_path: PathBuf,
pub system64_path: PathBuf,
pub program32_path: PathBuf,
pub flash_program32_path: PathBuf,
pub macromed32_path: PathBuf,
pub macromed64_path: PathBuf,
pub flash32_path: PathBuf,
pub flash64_path: PathBuf,
pub version: String,
pub version_path: String,
pub version_comma: String,
pub is_64bit: bool,
replacements: HashMap<String, String>,
}
impl SystemInfo {
pub fn new() -> Self {
let system32_path = get_syswow64_path();
let system64_path = get_system32_path();
let program32_path = get_program_files_x86();
let flash_program32_path = program32_path.join("Flash Player");
let macromed32_path = system32_path.join("Macromed");
let macromed64_path = system64_path.join("Macromed");
let flash32_path = macromed32_path.join("Flash");
let flash64_path = macromed64_path.join("Flash");
let version = update_checker::FLASH_VERSION.to_string();
let version_path = version.replace('.', "_");
let version_comma = version.replace('.', ",");
let is_64bit = cfg!(target_pointer_width = "64")
|| env::var("PROCESSOR_ARCHITEW6432").is_ok();
let arch = if is_64bit { "64" } else { "32" };
let mut replacements = HashMap::new();
replacements.insert(
"${SYSTEM_32_PATH}".into(),
system32_path.to_string_lossy().replace('\\', "\\\\"),
);
replacements.insert(
"${SYSTEM_64_PATH}".into(),
system64_path.to_string_lossy().replace('\\', "\\\\"),
);
replacements.insert(
"${PROGRAM_32_PATH}".into(),
program32_path.to_string_lossy().replace('\\', "\\\\"),
);
replacements.insert(
"${PROGRAM_FLASH_32_PATH}".into(),
flash_program32_path.to_string_lossy().replace('\\', "\\\\"),
);
replacements.insert(
"${FLASH_32_PATH}".into(),
flash32_path.to_string_lossy().replace('\\', "\\\\"),
);
replacements.insert(
"${FLASH_64_PATH}".into(),
flash64_path.to_string_lossy().replace('\\', "\\\\"),
);
replacements.insert("${VERSION}".into(), version.clone());
replacements.insert("${VERSION_PATH}".into(), version_path.clone());
replacements.insert("${VERSION_COMMA}".into(), version_comma.clone());
replacements.insert("${ARCH}".into(), arch.into());
Self {
system32_path,
system64_path,
program32_path,
flash_program32_path,
macromed32_path,
macromed64_path,
flash32_path,
flash64_path,
version,
version_path,
version_comma,
is_64bit,
replacements,
}
}
pub fn system_paths(&self) -> Vec<&Path> {
if self.is_64bit {
vec![&self.system32_path, &self.system64_path]
} else {
vec![&self.system32_path]
}
}
pub fn macromed_paths(&self) -> Vec<&Path> {
if self.is_64bit {
vec![&self.macromed32_path, &self.macromed64_path]
} else {
vec![&self.macromed32_path]
}
}
pub fn fill_string(&self, s: &str) -> String {
let mut result = s.to_string();
for (key, value) in &self.replacements {
result = result.replace(key.as_str(), value);
}
result
}
pub fn is_legacy_windows(&self) -> bool {
// Windows version < 6.2 (before Windows 8).
unsafe {
let mut info: windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW =
std::mem::zeroed();
info.dwOSVersionInfoSize =
std::mem::size_of::<windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW>()
as u32;
// RtlGetVersion always succeeds and isn't deprecated like GetVersionEx.
rtl_get_version(&mut info);
info.dwMajorVersion < 6
|| (info.dwMajorVersion == 6 && info.dwMinorVersion < 2)
}
}
}
extern "system" {
fn RtlGetVersion(
lp_version_information: *mut windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW,
) -> i32;
}
unsafe fn rtl_get_version(
info: &mut windows_sys::Win32::System::SystemInformation::OSVERSIONINFOW,
) {
RtlGetVersion(info as *mut _);
}
/// Global convenience: fill replacement strings using a default SystemInfo.
pub fn fill_string(s: &str) -> String {
SYSTEM_INFO.with(|si| si.fill_string(s))
}
thread_local! {
static SYSTEM_INFO: SystemInfo = SystemInfo::new();
}
pub fn with_system_info<F, R>(f: F) -> R
where
F: FnOnce(&SystemInfo) -> R,
{
SYSTEM_INFO.with(f)
}
fn get_system32_path() -> PathBuf {
PathBuf::from(env::var("SYSTEMROOT").unwrap_or_else(|_| r"C:\Windows".into()))
.join("System32")
}
fn get_syswow64_path() -> PathBuf {
let root = env::var("SYSTEMROOT").unwrap_or_else(|_| r"C:\Windows".into());
let wow64 = PathBuf::from(&root).join("SysWOW64");
if wow64.exists() {
wow64
} else {
PathBuf::from(&root).join("System32")
}
}
fn get_program_files_x86() -> PathBuf {
if let Ok(pf86) = env::var("PROGRAMFILES(X86)") {
PathBuf::from(pf86)
} else if let Ok(pf) = env::var("PROGRAMFILES") {
PathBuf::from(pf)
} else {
PathBuf::from(r"C:\Program Files")
}
}
@@ -0,0 +1,302 @@
use crate::{
file_util, process_utils, registry, resources, system_info, winapi_helpers, InstallError,
ProgressCallback,
};
use std::env;
use std::path::{Path, PathBuf};
const PROCESSES_TO_KILL: &[&str] = &[
"fcbrowser",
"fcbrowsermanager",
"fclogin",
"fctips",
"flashcenter",
"flashcenterservice",
"flashcenteruninst",
"flashplay",
"update",
"wow_helper",
"dummy_cmd",
"flashhelperservice",
"flashplayerapp",
"flashplayer_sa",
"flashplayer_sa_debug",
];
const CONDITIONAL_PROCESSES: &[&str] = &[
"plugin-container",
"opera",
"iexplore",
"chrome",
"chromium",
"brave",
"vivaldi",
"msedge",
];
/// Unregister an ActiveX OCX file via regsvr32.
pub fn unregister_activex(filename: &str) -> Result<(), InstallError> {
winapi_helpers::allow_modifications();
let path = Path::new(filename);
let dir = path.parent().unwrap_or(Path::new("."));
let file_name = path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_string();
let _prev = env::current_dir();
let _ = env::set_current_dir(dir);
let process = process_utils::run_process("regsvr32.exe", &["/s", "/u", &file_name]);
if !process.is_successful() {
return Err(InstallError::new(format!(
"Failed to unregister ActiveX plugin: error code {}\n\n{}",
process.exit_code, process.output
)));
}
Ok(())
}
fn uninstall_registry() -> Result<(), InstallError> {
system_info::with_system_info(|si| {
if si.is_64bit {
registry::apply_registry(&[
resources::UNINSTALL_REGISTRY,
resources::UNINSTALL_REGISTRY_64,
])
} else {
registry::apply_registry(&[resources::UNINSTALL_REGISTRY])
}
})
}
fn delete_task(task: &str) {
process_utils::run_unmanaged_process("schtasks.exe", &["/delete", "/tn", task, "/f"]);
}
fn stop_service(service: &str) {
process_utils::run_unmanaged_process("net.exe", &["stop", service]);
}
fn delete_service(service: &str) {
stop_service(service);
process_utils::run_unmanaged_process("sc.exe", &["delete", service]);
}
fn delete_flash_center() {
// Remove Flash Center from Program Files.
let pf = env::var("PROGRAMFILES").unwrap_or_default();
file_util::wipe_folder(&PathBuf::from(&pf).join("FlashCenter"));
if let Ok(pf86) = env::var("PROGRAMFILES(X86)") {
file_util::wipe_folder(&PathBuf::from(&pf86).join("FlashCenter"));
}
// Remove start menu shortcuts.
if let Some(appdata) = env::var("PROGRAMDATA").ok() {
file_util::wipe_folder(
&PathBuf::from(&appdata)
.join("Microsoft")
.join("Windows")
.join("Start Menu")
.join("Programs")
.join("Flash Center"),
);
}
// Remove Flash Center cache / user data.
if let Some(local) = env::var("LOCALAPPDATA").ok() {
file_util::wipe_folder(&PathBuf::from(&local).join("Flash_Center"));
}
// Remove common start menu shortcuts.
if let Some(appdata) = env::var("APPDATA").ok() {
file_util::wipe_folder(
&PathBuf::from(&appdata)
.join("Microsoft")
.join("Windows")
.join("Start Menu")
.join("Programs")
.join("Flash Center"),
);
}
// Remove Desktop shortcuts.
if let Some(desktop) = get_common_desktop() {
file_util::delete_file(&desktop.join("Flash Center.lnk"));
}
if let Some(desktop) = dirs_desktop() {
file_util::delete_file(&desktop.join("Flash Player.lnk"));
}
// Remove Flash Player from Program Files.
system_info::with_system_info(|si| {
file_util::wipe_folder(&si.flash_program32_path);
});
// Clean up temp folder spyware remnants.
let temp = env::temp_dir();
if let Ok(entries) = std::fs::read_dir(&temp) {
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if name.len() == 11 && name.ends_with(".tmp") && entry.path().is_dir() {
let _ = file_util::wipe_folder(&entry.path());
}
}
}
}
fn delete_flash_player() {
system_info::with_system_info(|si| {
// Remove Macromed folders.
for dir in si.macromed_paths() {
file_util::recursive_delete(dir, None);
}
// Remove Flash Player control panel apps.
for sys_dir in si.system_paths() {
file_util::delete_file(&sys_dir.join("FlashPlayerApp.exe"));
file_util::delete_file(&sys_dir.join("FlashPlayerCPLApp.cpl"));
}
});
}
fn should_kill_conditional_process(name: &str, pid: u32) -> bool {
if !CONDITIONAL_PROCESSES
.iter()
.any(|p| p.eq_ignore_ascii_case(name))
{
return false;
}
let modules = process_utils::collect_modules(pid);
modules.iter().any(|m| {
let lower = m.to_lowercase();
lower.starts_with("flash32")
|| lower.starts_with("flash64")
|| lower.starts_with("libpepflash")
|| lower.starts_with("npswf")
})
}
fn stop_processes() {
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::System::Threading::{
OpenProcess, TerminateProcess, WaitForSingleObject, PROCESS_QUERY_INFORMATION,
PROCESS_TERMINATE, PROCESS_VM_READ,
};
// Enumerate all processes via the snapshot API.
let pids = enumerate_processes();
for (pid, name) in &pids {
let lower = name.to_lowercase();
let should_kill = PROCESSES_TO_KILL.iter().any(|p| *p == lower)
|| should_kill_conditional_process(&lower, *pid);
if !should_kill {
continue;
}
unsafe {
let handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, *pid);
if !handle.is_null() {
TerminateProcess(handle, 1);
WaitForSingleObject(handle, 5000);
CloseHandle(handle);
}
}
}
}
fn enumerate_processes() -> Vec<(u32, String)> {
use windows_sys::Win32::System::ProcessStatus::{EnumProcesses, GetModuleBaseNameW};
use windows_sys::Win32::System::Threading::{OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ};
use windows_sys::Win32::Foundation::CloseHandle;
let mut results = Vec::new();
let mut pids = [0u32; 4096];
let mut bytes_returned: u32 = 0;
unsafe {
if EnumProcesses(
pids.as_mut_ptr(),
std::mem::size_of_val(&pids) as u32,
&mut bytes_returned,
) == 0
{
return results;
}
let count = bytes_returned as usize / std::mem::size_of::<u32>();
for &pid in &pids[..count] {
if pid == 0 {
continue;
}
let handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, pid);
if handle.is_null() {
continue;
}
let mut name_buf = [0u16; 260];
let len = GetModuleBaseNameW(handle, std::ptr::null_mut(), name_buf.as_mut_ptr(), 260);
CloseHandle(handle);
if len > 0 {
let name = String::from_utf16_lossy(&name_buf[..len as usize]);
// Strip .exe suffix for matching.
let base = name.strip_suffix(".exe").unwrap_or(&name).to_string();
results.push((pid, base));
}
}
}
results
}
/// Perform the full uninstallation sequence.
pub fn uninstall(form: &dyn ProgressCallback) -> Result<(), InstallError> {
winapi_helpers::allow_modifications();
form.update_progress_label("Stopping Flash auto-updater task...", true);
delete_task("Adobe Flash Player Updater");
form.update_progress_label("Stopping Flash auto-updater service...", true);
delete_service("AdobeFlashPlayerUpdateSvc");
form.update_progress_label("Stopping Flash Center services...", true);
delete_service("Flash Helper Service");
form.tick_progress();
delete_service("FlashCenterService");
form.update_progress_label("Exiting all browsers...", true);
stop_processes();
form.update_progress_label("Cleaning up registry...", true);
uninstall_registry()?;
form.update_progress_label("Removing Flash Center...", true);
delete_flash_center();
form.update_progress_label("Removing Flash Player...", true);
delete_flash_player();
Ok(())
}
// Helper to get common desktop path.
fn get_common_desktop() -> Option<PathBuf> {
env::var("PUBLIC")
.ok()
.map(|p| PathBuf::from(p).join("Desktop"))
}
fn dirs_desktop() -> Option<PathBuf> {
env::var("USERPROFILE")
.ok()
.map(|p| PathBuf::from(p).join("Desktop"))
}
@@ -0,0 +1,14 @@
pub const FLASH_VERSION: &str = "34.0.0.330";
pub const VERSION: &str = "34.0.0.330";
pub struct VersionInfo {
pub name: String,
pub version: String,
pub url: String,
}
pub fn get_latest_version() -> Option<VersionInfo> {
// The original fetches from the GitHub API.
// Stubbed for the port; real implementation would use ureq or reqwest.
None
}
@@ -0,0 +1,58 @@
/// Enable SeRestorePrivilege and SeTakeOwnershipPrivilege for the current process.
pub fn allow_modifications() {
let _ = modify_privilege("SeRestorePrivilege\0", true);
let _ = modify_privilege("SeTakeOwnershipPrivilege\0", true);
}
fn modify_privilege(name: &str, enable: bool) -> Result<(), ()> {
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::Security::{
AdjustTokenPrivileges, LookupPrivilegeValueW, LUID_AND_ATTRIBUTES,
SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES, TOKEN_QUERY,
};
use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
unsafe {
let mut token: *mut std::ffi::c_void = std::ptr::null_mut();
if OpenProcessToken(
GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&mut token,
) == 0
{
return Err(());
}
let wide_name: Vec<u16> = name.encode_utf16().collect();
let mut luid = std::mem::zeroed();
if LookupPrivilegeValueW(std::ptr::null(), wide_name.as_ptr(), &mut luid) == 0 {
CloseHandle(token);
return Err(());
}
let mut tp = TOKEN_PRIVILEGES {
PrivilegeCount: 1,
Privileges: [LUID_AND_ATTRIBUTES {
Luid: luid,
Attributes: if enable { SE_PRIVILEGE_ENABLED } else { 0 },
}],
};
let result = AdjustTokenPrivileges(
token,
0,
&mut tp,
std::mem::size_of::<TOKEN_PRIVILEGES>() as u32,
std::ptr::null_mut(),
std::ptr::null_mut(),
);
CloseHandle(token);
if result == 0 {
Err(())
} else {
Ok(())
}
}
}