use std::ffi::c_void;
use windows::{
core::PCSTR,
Win32::{
Foundation::CloseHandle,
System::{
Diagnostics::Debug::{ReadProcessMemory, WriteProcessMemory},
LibraryLoader::{GetModuleHandleA, GetProcAddress},
Memory::{VirtualAllocEx, VirtualFreeEx, MEM_COMMIT, MEM_RELEASE, PAGE_EXECUTE_READWRITE, PAGE_READWRITE},
Threading::{CreateRemoteThread, GetExitCodeThread, WaitForSingleObject},
},
},
};
use super::helpers::{get_module_base_address, open_process_full};
use super::shellcode;
use super::DllInjectorError;
pub fn get_exported_function_address(
pid: u32,
module_name: &str,
function_name: &str,
) -> Result<usize, DllInjectorError> {
unsafe {
let handle = open_process_full(pid)?;
let module_addr = get_module_base_address(pid, module_name)?;
let h_kernel32 = GetModuleHandleA(PCSTR::from_raw("kernel32.dll\0".as_ptr()))
.map_err(|e| DllInjectorError::GetProcAddressFailed(e.to_string()))?;
let get_proc_address_fn = match GetProcAddress(h_kernel32, PCSTR::from_raw("GetProcAddress\0".as_ptr())) {
Some(addr) => addr,
None => {
let _ = CloseHandle(handle);
return Err(DllInjectorError::GetProcAddressFailed(
"GetProcAddress not found".to_string()
));
}
};
let func_name_cstr = format!("{}\0", function_name);
let func_name_bytes = func_name_cstr.as_bytes();
let func_name_addr = VirtualAllocEx(handle, None, func_name_bytes.len(), MEM_COMMIT, PAGE_READWRITE);
if func_name_addr.is_null() {
let _ = CloseHandle(handle);
return Err(DllInjectorError::AllocationFailed);
}
let mut bytes_written = 0;
if WriteProcessMemory(handle, func_name_addr, func_name_bytes.as_ptr() as *const c_void, func_name_bytes.len(), Some(&mut bytes_written)).is_err() {
let _ = VirtualFreeEx(handle, func_name_addr, 0, MEM_RELEASE);
let _ = CloseHandle(handle);
return Err(DllInjectorError::WriteFailed("Failed to write function name".to_string()));
}
let result_size = std::mem::size_of::<usize>();
let result_addr = VirtualAllocEx(handle, None, result_size, MEM_COMMIT, PAGE_READWRITE);
if result_addr.is_null() {
let _ = VirtualFreeEx(handle, func_name_addr, 0, MEM_RELEASE);
let _ = CloseHandle(handle);
return Err(DllInjectorError::AllocationFailed);
}
#[cfg(target_arch = "x86_64")]
let shellcode = shellcode::get_proc_addr::generate_get_proc_addr_shellcode_x64(
get_proc_address_fn as usize,
module_addr,
func_name_addr as usize,
result_addr as usize,
);
#[cfg(target_arch = "x86")]
let shellcode = shellcode::get_proc_addr::generate_get_proc_addr_shellcode_x86(
get_proc_address_fn as usize,
module_addr,
func_name_addr as usize,
result_addr as usize,
);
let shellcode_addr = VirtualAllocEx(handle, None, shellcode.len(), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if shellcode_addr.is_null() {
let _ = VirtualFreeEx(handle, result_addr, 0, MEM_RELEASE);
let _ = VirtualFreeEx(handle, func_name_addr, 0, MEM_RELEASE);
let _ = CloseHandle(handle);
return Err(DllInjectorError::AllocationFailed);
}
let mut bytes_written = 0;
if WriteProcessMemory(handle, shellcode_addr, shellcode.as_ptr() as *const c_void, shellcode.len(), Some(&mut bytes_written)).is_err() {
let _ = VirtualFreeEx(handle, shellcode_addr, 0, MEM_RELEASE);
let _ = VirtualFreeEx(handle, result_addr, 0, MEM_RELEASE);
let _ = VirtualFreeEx(handle, func_name_addr, 0, MEM_RELEASE);
let _ = CloseHandle(handle);
return Err(DllInjectorError::WriteFailed("Failed to write shellcode".to_string()));
}
let thread_result = CreateRemoteThread(handle, None, 0, Some(std::mem::transmute(shellcode_addr)), None, 0, None);
match thread_result {
Ok(thread) => {
WaitForSingleObject(thread, u32::MAX);
let mut func_address: usize = 0;
let mut bytes_read = 0;
let read_result = ReadProcessMemory(
handle,
result_addr,
&mut func_address as *mut usize as *mut c_void,
result_size,
Some(&mut bytes_read),
);
let _ = VirtualFreeEx(handle, shellcode_addr, 0, MEM_RELEASE);
let _ = VirtualFreeEx(handle, result_addr, 0, MEM_RELEASE);
let _ = VirtualFreeEx(handle, func_name_addr, 0, MEM_RELEASE);
let _ = CloseHandle(thread);
let _ = CloseHandle(handle);
if read_result.is_err() || bytes_read != result_size {
return Err(DllInjectorError::Other("Failed to read result".to_string()));
}
if func_address == 0 {
return Err(DllInjectorError::GetProcAddressFailed(format!(
"Function '{}' not found in '{}'", function_name, module_name
)));
}
Ok(func_address)
}
Err(e) => {
let _ = VirtualFreeEx(handle, shellcode_addr, 0, MEM_RELEASE);
let _ = VirtualFreeEx(handle, result_addr, 0, MEM_RELEASE);
let _ = VirtualFreeEx(handle, func_name_addr, 0, MEM_RELEASE);
let _ = CloseHandle(handle);
Err(DllInjectorError::CreateThreadFailed(e.to_string()))
}
}
}
}
pub fn call_function_with_raw_bytes(
pid: u32,
function_address: usize,
param_data: Option<&[u8]>,
) -> Result<u32, DllInjectorError> {
unsafe {
let handle = open_process_full(pid)?;
let param_addr = if let Some(data) = param_data {
let param_size = data.len();
let param_addr_raw = VirtualAllocEx(handle, None, param_size, MEM_COMMIT, PAGE_READWRITE);
if param_addr_raw.is_null() {
let _ = CloseHandle(handle);
return Err(DllInjectorError::AllocationFailed);
}
let mut bytes_written = 0;
if WriteProcessMemory(handle, param_addr_raw, data.as_ptr() as *const c_void, param_size, Some(&mut bytes_written)).is_err() {
let _ = VirtualFreeEx(handle, param_addr_raw, 0, MEM_RELEASE);
let _ = CloseHandle(handle);
return Err(DllInjectorError::WriteFailed("Failed to write parameter".to_string()));
}
Some((param_addr_raw as usize, param_size))
} else {
None
};
let param_addr_val = param_addr.as_ref().map(|(addr, _)| *addr).unwrap_or(0);
let param_size_val = param_addr.as_ref().map(|(_, size)| *size).unwrap_or(0);
#[cfg(target_arch = "x86_64")]
let shellcode = shellcode::call_function::generate_call_function_shellcode_x64(
function_address,
param_addr_val,
param_size_val,
);
#[cfg(target_arch = "x86")]
let shellcode = shellcode::call_function::generate_call_function_shellcode_x86(
function_address,
param_addr_val,
param_size_val,
);
let shellcode_addr = VirtualAllocEx(handle, None, shellcode.len(), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if shellcode_addr.is_null() {
if let Some((addr, _)) = param_addr {
let _ = VirtualFreeEx(handle, addr as *mut c_void, 0, MEM_RELEASE);
}
let _ = CloseHandle(handle);
return Err(DllInjectorError::AllocationFailed);
}
let mut bytes_written = 0;
if WriteProcessMemory(handle, shellcode_addr, shellcode.as_ptr() as *const c_void, shellcode.len(), Some(&mut bytes_written)).is_err() {
let _ = VirtualFreeEx(handle, shellcode_addr, 0, MEM_RELEASE);
if let Some((addr, _)) = param_addr {
let _ = VirtualFreeEx(handle, addr as *mut c_void, 0, MEM_RELEASE);
}
let _ = CloseHandle(handle);
return Err(DllInjectorError::WriteFailed("Failed to write shellcode".to_string()));
}
let thread_result = CreateRemoteThread(handle, None, 0, Some(std::mem::transmute(shellcode_addr)), None, 0, None);
match thread_result {
Ok(thread) => {
WaitForSingleObject(thread, u32::MAX);
let mut exit_code: u32 = 0;
let _ = GetExitCodeThread(thread, &mut exit_code);
let _ = VirtualFreeEx(handle, shellcode_addr, 0, MEM_RELEASE);
if let Some((addr, _)) = param_addr {
let _ = VirtualFreeEx(handle, addr as *mut c_void, 0, MEM_RELEASE);
}
let _ = CloseHandle(thread);
let _ = CloseHandle(handle);
Ok(exit_code)
}
Err(e) => {
let _ = VirtualFreeEx(handle, shellcode_addr, 0, MEM_RELEASE);
if let Some((addr, _)) = param_addr {
let _ = VirtualFreeEx(handle, addr as *mut c_void, 0, MEM_RELEASE);
}
let _ = CloseHandle(handle);
Err(DllInjectorError::CreateThreadFailed(e.to_string()))
}
}
}
}
pub fn call_function_no_params(
pid: u32,
function_address: usize,
) -> Result<u32, DllInjectorError> {
unsafe {
let handle = open_process_full(pid)?;
#[cfg(target_arch = "x86_64")]
let shellcode = shellcode::call_function::generate_call_function_no_params_shellcode_x64(function_address);
#[cfg(target_arch = "x86")]
let shellcode = shellcode::call_function::generate_call_function_no_params_shellcode_x86(function_address);
let shellcode_addr = VirtualAllocEx(handle, None, shellcode.len(), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if shellcode_addr.is_null() {
let _ = CloseHandle(handle);
return Err(DllInjectorError::AllocationFailed);
}
let mut bytes_written = 0;
if WriteProcessMemory(handle, shellcode_addr, shellcode.as_ptr() as *const c_void, shellcode.len(), Some(&mut bytes_written)).is_err() {
let _ = VirtualFreeEx(handle, shellcode_addr, 0, MEM_RELEASE);
let _ = CloseHandle(handle);
return Err(DllInjectorError::WriteFailed("Failed to write shellcode".to_string()));
}
let thread_result = CreateRemoteThread(handle, None, 0, Some(std::mem::transmute(shellcode_addr)), None, 0, None);
match thread_result {
Ok(thread) => {
WaitForSingleObject(thread, u32::MAX);
let mut exit_code: u32 = 0;
let _ = GetExitCodeThread(thread, &mut exit_code);
let _ = VirtualFreeEx(handle, shellcode_addr, 0, MEM_RELEASE);
let _ = CloseHandle(thread);
let _ = CloseHandle(handle);
Ok(exit_code)
}
Err(e) => {
let _ = VirtualFreeEx(handle, shellcode_addr, 0, MEM_RELEASE);
let _ = CloseHandle(handle);
Err(DllInjectorError::CreateThreadFailed(e.to_string()))
}
}
}
}