use crate::memory::{read_memory_t, write_memory_t, MemoryError};
use crate::memory_hook::register_extractor::register::{calculate_storage_size, Register};
use crate::memory_hook::shellcode::ShellcodeBuilder;
use crate::memory_hook::TrampolineHook;
use windows::Win32::Foundation::HANDLE;
use windows::Win32::System::Memory::{
VirtualAllocEx, VirtualFreeEx, MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_READWRITE,
};
pub struct RegisterExtractor {
hook: TrampolineHook,
storage_address: usize,
extracted_regs: Vec<Register>,
is_x64: bool,
is_installed: bool,
}
impl RegisterExtractor {
pub fn builder() -> crate::memory_hook::register_extractor::builder::RegisterExtractorBuilder {
crate::memory_hook::register_extractor::builder::RegisterExtractorBuilder::new()
}
pub(crate) fn new(
handle: HANDLE,
target_address: usize,
bytes_to_overwrite: usize,
extracted_regs: Vec<Register>,
is_x64: bool,
) -> Self {
let storage_size = calculate_storage_size(&extracted_regs, is_x64);
let storage_address = unsafe {
VirtualAllocEx(
handle,
None,
storage_size,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE,
)
};
if storage_address.is_null() {
panic!("Failed to allocate storage memory for RegisterExtractor");
}
let detour_code =
Self::generate_extraction_shellcode(&extracted_regs, storage_address as usize, is_x64);
let mut hook = TrampolineHook::auto_new(handle, target_address, detour_code);
hook.set_architecture(is_x64);
hook.set_bytes_to_overwrite(bytes_to_overwrite);
Self {
hook,
storage_address: storage_address as usize,
extracted_regs,
is_x64,
is_installed: false,
}
}
fn generate_extraction_shellcode(
regs: &[Register],
storage_base: usize,
is_x64: bool,
) -> Vec<u8> {
let mut builder = if is_x64 {
ShellcodeBuilder::new_x64()
} else {
ShellcodeBuilder::new_x86()
};
if is_x64 {
let mut regs_to_save: Vec<u8> = Vec::new();
for ® in regs {
let reg_index = reg.register_index();
if !regs_to_save.contains(®_index) {
regs_to_save.push(reg_index);
}
}
if !regs_to_save.contains(&11) {
regs_to_save.push(11);
}
regs_to_save.sort();
for ®_idx in ®s_to_save {
builder.push_reg_x64(reg_idx);
}
for ® in regs {
let offset = reg.offset(is_x64);
let addr = storage_base + offset;
if reg.is_32bit_alias() {
Self::store_register_x64_32bit(&mut builder, reg, addr);
} else {
Self::store_register_x64(&mut builder, reg, addr);
}
}
for reg_idx in regs_to_save.iter().rev() {
builder.pop_reg_x64(*reg_idx);
}
} else {
builder.pushad();
for ® in regs {
let offset = reg.offset(is_x64);
let addr = storage_base + offset;
Self::store_register_x86(&mut builder, reg, addr);
}
builder.popad();
}
builder.build()
}
fn store_register_x64_32bit(builder: &mut ShellcodeBuilder, reg: Register, addr: usize) {
let reg_index = reg.register_index();
builder.mov_mem32_from_reg(addr, reg_index);
}
fn store_register_x64(builder: &mut ShellcodeBuilder, reg: Register, addr: usize) {
let reg_index = reg.register_index();
builder.mov_mem64_from_reg(addr, reg_index);
}
fn store_register_x86(builder: &mut ShellcodeBuilder, reg: Register, addr: usize) {
let reg_index = match reg {
Register::RAX => 0, Register::RCX => 1, Register::RDX => 2, Register::RBX => 3, Register::RSP => 4, Register::RBP => 5, Register::RSI => 6, Register::RDI => 7, _ => panic!("x86 only supports RAX-RDI (EAX-EDI)"),
};
builder.mov_mem32_from_reg(addr, reg_index);
}
pub fn install(&mut self) -> Result<(), MemoryError> {
self.hook.install()?;
self.is_installed = true;
Ok(())
}
pub fn uninstall(&mut self) -> Result<(), MemoryError> {
self.hook.uninstall()?;
self.is_installed = false;
Ok(())
}
pub fn read_register<T: Copy>(&self, reg: Register) -> Result<T, MemoryError> {
if !self.is_installed {
return Err(MemoryError::InvalidAddress(
"RegisterExtractor is not installed".to_string(),
));
}
let offset = reg.offset(self.is_x64);
let addr = self.storage_address + offset;
read_memory_t(self.hook.handle.0, addr)
}
pub fn read_chain<T: Copy>(&self, reg: Register, offsets: &[usize]) -> Result<T, MemoryError> {
if !self.is_installed {
return Err(MemoryError::InvalidAddress(
"RegisterExtractor is not installed".to_string(),
));
}
let base_ptr: usize = self.read_register::<usize>(reg)?;
if base_ptr == 0 {
return Err(MemoryError::InvalidAddress(
"Null pointer encountered in chain".to_string(),
));
}
let mut current_addr = base_ptr;
for (i, &offset) in offsets.iter().enumerate() {
if i == offsets.len() - 1 {
current_addr += offset;
return read_memory_t(self.hook.handle.0, current_addr);
} else {
current_addr += offset;
current_addr = read_memory_t::<usize>(self.hook.handle.0, current_addr)?;
if current_addr == 0 {
return Err(MemoryError::InvalidAddress(format!(
"Null pointer at chain level {}",
i
)));
}
}
}
unreachable!()
}
pub fn write_memory_t<T: Copy>(&self, address: usize, value: T) -> Result<(), MemoryError> {
write_memory_t(self.hook.handle.0, address, value)
}
pub fn get_storage_address(&self) -> usize {
self.storage_address
}
pub fn get_register_storage_address(&self, reg: Register) -> usize {
let offset = reg.offset(self.is_x64);
self.storage_address + offset
}
pub fn is_installed(&self) -> bool {
self.is_installed
}
pub fn get_extracted_registers(&self) -> &[Register] {
&self.extracted_regs
}
}
impl Drop for RegisterExtractor {
fn drop(&mut self) {
if self.is_installed {
let _ = self.uninstall();
}
if self.storage_address != 0 {
unsafe {
let _ = VirtualFreeEx(
self.hook.handle.0,
self.storage_address as *mut _,
0,
MEM_RELEASE,
);
}
}
}
}