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()))
}
}
}
}