#[macro_use]
extern crate litcrypt2;
use_litcrypt!();
use std::cell::UnsafeCell;
use std::{collections::HashMap, path::Path};
use std::{fs, ptr};
use std::mem::size_of;
use std::ffi::c_void;
use winapi::shared::ntdef::LARGE_INTEGER;
use windows::Win32::System::Memory::MEMORY_BASIC_INFORMATION;
use windows::Win32::System::SystemInformation::SYSTEM_INFO;
use windows::Win32::System::SystemServices::{IMAGE_BASE_RELOCATION, IMAGE_IMPORT_DESCRIPTOR};
use windows::Win32::System::Threading::GetCurrentProcess;
use windows::Win32::System::WindowsProgramming::{IMAGE_THUNK_DATA32, IMAGE_THUNK_DATA64};
use windows::Win32::{
Foundation::{HANDLE,UNICODE_STRING, OBJECT_ATTRIBUTE_FLAGS},
System::{Diagnostics::Debug::{IMAGE_OPTIONAL_HEADER32, IMAGE_SECTION_HEADER},IO::IO_STATUS_BLOCK}
};
use windows::Wdk::Foundation::OBJECT_ATTRIBUTES;
use dinvoke_data::{FILE_EXECUTE, FILE_NON_DIRECTORY_FILE, FILE_READ_ATTRIBUTES, FILE_READ_DATA, FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT, ImageFileHeader, ImageOptionalHeader64, MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, PAGE_EXECUTE_WRITECOPY, PAGE_GUARD, PAGE_NOACCESS, PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOPY, PVOID, PeManualMap, PeMetadata, PeSectionMap, RegionInfo, SEC_IMAGE, SECTION_ALL_ACCESS, SECTION_MEM_EXECUTE, SECTION_MEM_READ, SECTION_MEM_WRITE, SYNCHRONIZE};
use litcrypt2::lc;
/// Manually maps a PE from disk to the memory of the current process.
///
/// If the clean_headers parameters is set to true, the mapped pe's dos header will be removed during the
/// mapping process. Otherwise, the dos header will be kept untouched.
///
/// The third parameter determines whether TLS callbacks are executed (true) or not (false).
///
/// It will return either a pair (PeMetadata,usize) containing the mapped PE
/// metadata and its base address or a String with a descriptive error message.
///
/// # Examples
///
/// ```
/// let ntdll = manualmap::read_and_map_module(r"c:\windows\system32\ntdll.dll", true, false);
///
/// match ntdll {
/// Ok(x) => if x.1 != 0 {println!("The base address of ntdll.dll is 0x{:X}.", x.1);},
/// Err(e) => println!("{}", e),
/// }
/// ```
pub fn read_and_map_module (filepath: &str, clean_dos_header: bool, run_callbacks: bool) -> Result<(PeMetadata,usize), String>
{
let file_content = fs::read(filepath).expect(&lc!("[x] Error opening the specified file."));
let file_content_ptr = file_content.as_ptr() as *mut _;
let result = manually_map_module(file_content_ptr, clean_dos_header, run_callbacks)?;
unsafe
{
for i in 0..file_content.len() {
*(file_content_ptr.add(i)) = 0u8;
}
Ok(result)
}
}
/// Manually maps a PE into the current process.
///
/// If the clean_headers parameters is set to true, the mapped pe's dos header will be removed during the
/// mapping process. Otherwise, the dos header will be kept untouched.
///
/// The third parameter determines whether TLS callbacks are executed (true) or not (false).
///
/// It will return either a pair (PeMetadata,usize) containing the mapped PE
/// metadata and its base address or a String with a descriptive error message.
///
/// # Examples
///
/// ```
/// use std::fs;
///
/// let file_content = fs::read("c:\\windows\\system32\\ntdll.dll").expect("[x] Error opening the specified file.");
/// let file_content_ptr = file_content.as_ptr();
/// let result = manualmap::manually_map_module(file_content_ptr, true, true);
/// ```
pub fn manually_map_module (file_ptr: *const u8, clean_dos_headers: bool, run_callbacks: bool) -> Result<(PeMetadata,usize), String>
{
let pe_info = get_pe_metadata(file_ptr, false)?;
if (pe_info.is_32_bit && (size_of::<usize>() == 8)) || (!pe_info.is_32_bit && (size_of::<usize>() == 4)) {
return Err(lc!("[x] The module architecture does not match the process architecture."));
}
let dwsize;
if pe_info.is_32_bit {
dwsize = pe_info.opt_header_32.SizeOfImage as usize;
} else {
dwsize = pe_info.opt_header_64.size_of_image as usize;
}
unsafe
{
let handle = GetCurrentProcess();
let a = usize::default();
let base_address: *mut PVOID = std::mem::transmute(&a);
let zero_bits = 0 as usize;
let size: *mut usize = std::mem::transmute(&dwsize);
let ret = dinvoke::nt_allocate_virtual_memory(handle, base_address, zero_bits, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
let _r = dinvoke::close_handle(handle);
if ret != 0 {
return Err(lc!("[x] Error allocating memory."));
}
let image_ptr = *base_address;
map_module_to_memory(file_ptr, image_ptr, &pe_info)?;
relocate_module(&pe_info, image_ptr);
rewrite_module_iat(&pe_info, image_ptr)?;
if clean_dos_headers {
clean_dos_header(image_ptr);
}
set_module_section_permissions(&pe_info, image_ptr)?;
add_runtime_table(&pe_info, image_ptr);
if run_callbacks {
run_tls_callbacks(&pe_info, image_ptr);
}
Ok((pe_info,image_ptr as usize))
}
}
/// Returns a pair containing a pointer to the Exception data of an arbitrary module and the size of the
/// corresponding PE section (.pdata). In case that it fails to retrieve this information, it returns
/// null values.
pub fn get_runtime_table(image_ptr: *mut c_void) -> (*mut dinvoke_data::RuntimeFunction, u32)
{
unsafe
{
let mut size: u32 = 0;
let module_metadata = get_pe_metadata(image_ptr as *const u8, false);
if !module_metadata.is_ok() {
return (ptr::null_mut(), size);
}
let metadata = module_metadata.unwrap();
let mut runtime: *mut dinvoke_data::RuntimeFunction = ptr::null_mut();
for section in &metadata.sections
{
let s = std::str::from_utf8(§ion.Name).unwrap();
if s.contains(".pdata")
{
let base = image_ptr as usize;
let addr = base + section.VirtualAddress as usize;
runtime = std::mem::transmute(addr);
size = section.SizeOfRawData;
break;
}
}
return (runtime, size);
}
}
/// Retrieves PE headers information from the module base address.
///
/// It will return either a dinvoke_data::PeMetada struct containing the PE
/// metadata or a String with a descriptive error message.
///
/// # Examples
///
/// ```
/// use std::fs;
///
/// let file_content = fs::read("c:\\windows\\system32\\ntdll.dll").expect("[x] Error opening the specified file.");
/// let file_content_ptr = file_content.as_ptr();
/// let result = manualmap::get_pe_metadata(file_content_ptr, false);
/// ```
pub fn get_pe_metadata (module_ptr: *const u8, check_signature: bool) -> Result<PeMetadata,String>
{
let mut pe_metadata= PeMetadata::default();
unsafe
{
let e_lfanew = *((module_ptr as usize + 0x3C) as *const u32);
pe_metadata.pe = *((module_ptr as usize + e_lfanew as usize) as *const u32);
if pe_metadata.pe != 0x4550 && check_signature {
return Err(lc!("[x] Invalid PE signature."));
}
pe_metadata.image_file_header = *((module_ptr as usize + e_lfanew as usize + 0x4) as *mut ImageFileHeader);
let opt_header: *const u16 = (module_ptr as usize + e_lfanew as usize + 0x18) as *const u16;
let pe_arch = *(opt_header);
if pe_arch == 0x010B {
pe_metadata.is_32_bit = true;
let opt_header_content: *const IMAGE_OPTIONAL_HEADER32 = std::mem::transmute(opt_header);
pe_metadata.opt_header_32 = *opt_header_content;
}
else if pe_arch == 0x020B {
pe_metadata.is_32_bit = false;
let opt_header_content: *const ImageOptionalHeader64 = std::mem::transmute(opt_header);
pe_metadata.opt_header_64 = *opt_header_content;
}
else {
return Err(lc!("[x] Invalid magic value."));
}
let mut sections: Vec<IMAGE_SECTION_HEADER> = vec![];
for i in 0..pe_metadata.image_file_header.number_of_sections {
let section_ptr = (opt_header as usize + pe_metadata.image_file_header.size_of_optional_header as usize + (i * 0x28) as usize) as *const u8;
let section_ptr: *const IMAGE_SECTION_HEADER = std::mem::transmute(section_ptr);
sections.push(*section_ptr);
}
pe_metadata.sections = sections;
Ok(pe_metadata)
}
}
/// Maps a module to a valid memory space in the current process.
///
/// The parameters required are a vector with the module content, the base address where the module should be
/// mapped and the module's metadata.
pub fn map_module_to_memory(module_ptr: *const u8, image_ptr: *mut c_void, pe_info: &PeMetadata) -> Result<(),String>
{
if (pe_info.is_32_bit && (size_of::<usize>() == 8)) || (!pe_info.is_32_bit && (size_of::<usize>() == 4)) {
return Err(lc!("[x] The module architecture does not match the process architecture."));
}
let nsize;
if pe_info.is_32_bit {
nsize = pe_info.opt_header_32.SizeOfHeaders as usize;
} else {
nsize = pe_info.opt_header_64.size_of_headers as usize;
}
unsafe
{
let handle = GetCurrentProcess();
let base_address: *mut c_void = std::mem::transmute(image_ptr);
let buffer: *mut c_void = std::mem::transmute(module_ptr);
let written: usize = 0;
let bytes_written: *mut usize = std::mem::transmute(&written);
let ret = dinvoke::nt_write_virtual_memory(handle, base_address, buffer, nsize, bytes_written);
if ret != 0 {
let _r = dinvoke::close_handle(handle);
return Err(lc!("[x] Error writing PE headers to the allocated memory."));
}
for section in &pe_info.sections
{
let section_base_ptr = (image_ptr as usize + section.VirtualAddress as usize) as *mut u8;
let section_content_ptr = (module_ptr as usize + section.PointerToRawData as usize) as *mut u8;
let base_address: *mut c_void = std::mem::transmute(section_base_ptr);
let buffer: *mut c_void = std::mem::transmute(section_content_ptr);
let nsize = section.SizeOfRawData as usize;
let bytes_written: *mut usize = std::mem::transmute(&written);
let ret = dinvoke::nt_write_virtual_memory(handle, base_address, buffer, nsize, bytes_written);
let _r = dinvoke::close_handle(handle);
if ret != 0 || *bytes_written != nsize {
return Err(lc!("[x] Failed to write PE sections to the allocated memory."))
}
}
Ok(())
}
}
/// Relocates a module in memory.
///
/// The parameters required are the module's metadata information and a
/// pointer to the base address where the module is mapped in memory.
pub fn relocate_module(pe_info: &PeMetadata, image_ptr: *mut c_void)
{
unsafe
{
let module_memory_base: *mut usize = std::mem::transmute(image_ptr);
let image_data_directory;
let image_delta: isize;
if pe_info.is_32_bit {
image_data_directory = pe_info.opt_header_32.DataDirectory[5]; // BaseRelocationTable
image_delta = module_memory_base as isize - pe_info.opt_header_32.ImageBase as isize;
} else {
image_data_directory = pe_info.opt_header_64.datas_directory[5]; // BaseRelocationTable
image_delta = module_memory_base as isize - pe_info.opt_header_64.image_base as isize;
}
let mut reloc_table_ptr = (module_memory_base as usize + image_data_directory.VirtualAddress as usize) as *mut i32;
let mut next_reloc_table_block = -1;
while next_reloc_table_block != 0
{
let ibr: *mut IMAGE_BASE_RELOCATION = std::mem::transmute(reloc_table_ptr);
let image_base_relocation = *ibr;
let reloc_count: isize = (image_base_relocation.SizeOfBlock as isize - size_of::<IMAGE_BASE_RELOCATION>() as isize) / 2;
for i in 0..reloc_count
{
let reloc_entry_ptr = (reloc_table_ptr as usize + size_of::<IMAGE_BASE_RELOCATION>() as usize + (i * 2) as usize) as *mut u16;
let reloc_value = *reloc_entry_ptr;
let reloc_type = reloc_value >> 12;
let reloc_patch = reloc_value & 0xfff;
if reloc_type != 0
{
if reloc_type == 0x3 {
let patch_ptr = (module_memory_base as usize + image_base_relocation.VirtualAddress as usize + reloc_patch as usize) as *mut i32;
let original_ptr = *patch_ptr;
let patch = original_ptr + image_delta as i32;
*patch_ptr = patch;
} else {
let patch_ptr = (module_memory_base as usize + image_base_relocation.VirtualAddress as usize + reloc_patch as usize) as *mut isize;
let original_ptr = *patch_ptr;
let patch = original_ptr + image_delta as isize;
*patch_ptr = patch;
}
}
}
reloc_table_ptr = (reloc_table_ptr as usize + image_base_relocation.SizeOfBlock as usize) as *mut i32;
next_reloc_table_block = *reloc_table_ptr;
}
}
}
/// Rewrites the IAT of a manually mapped module.
///
/// The parameters required are the module's metadata information and a
/// pointer to the base address where the module is mapped in memory.
pub fn rewrite_module_iat(pe_info: &PeMetadata, image_ptr: *mut c_void) -> Result<(),String>
{
unsafe
{
let module_memory_base: *mut usize = std::mem::transmute(image_ptr);
let image_data_directory;
if pe_info.is_32_bit {
image_data_directory = pe_info.opt_header_32.DataDirectory[1]; // ImportTable
} else {
image_data_directory = pe_info.opt_header_64.datas_directory[1]; // ImportTable
}
if image_data_directory.VirtualAddress == 0 {
return Ok(()); // No import table
}
let import_table_ptr = (module_memory_base as usize + image_data_directory.VirtualAddress as usize) as *mut usize;
let info = os_info::get();
let version = info.version().to_string();
let mut api_set_dict: HashMap<String,String> = HashMap::new();
if version >= "10".to_string() {
api_set_dict = dinvoke::get_api_mapping();
}
let mut counter = 0;
let mut image_import_descriptor_ptr = (import_table_ptr as usize + size_of::<IMAGE_IMPORT_DESCRIPTOR>() as usize * counter) as *mut IMAGE_IMPORT_DESCRIPTOR;
let mut image_import_descriptor = *image_import_descriptor_ptr;
while image_import_descriptor.Name != 0
{
let mut dll_name = "".to_string();
let mut c: char = ' ';
let mut ptr = (module_memory_base as usize + image_import_descriptor.Name as usize) as *mut u8;
while c != '\0'
{
c = *ptr as char;
if c != '\0' {
dll_name.push(c);
ptr = ptr.add(1);
}
}
if dll_name == "" {
return Ok(());
} else {
let lookup_key = format!("{}{}",&dll_name[..dll_name.len() - 6], ".dll");
if (version >= 10.to_string() && (dll_name.starts_with("api-") || dll_name.starts_with("ext-"))) && api_set_dict.contains_key(&lookup_key)
{
let key = match api_set_dict.get(&lookup_key) {
Some(x) => x.to_string(),
None => "".to_string(),
};
if key.len() > 0
{
dll_name = key.to_string();
}
}
let mut module_handle = dinvoke::get_module_base_address(&dll_name) as usize;
if module_handle == 0
{
module_handle = dinvoke::load_library_a(&dll_name) as usize;
if module_handle == 0 {
return Err(lc!("[x] Unable to find the specified module: {}", dll_name));
}
}
if pe_info.is_32_bit
{
let mut i: isize = 0;
loop
{
let image_thunk_data = (module_memory_base as usize + image_import_descriptor.Anonymous.OriginalFirstThunk as usize
+ i as usize * size_of::<u32>() as usize) as *mut IMAGE_THUNK_DATA32;
let image_thunk_data = *image_thunk_data;
let ft_itd = (module_memory_base as usize + image_import_descriptor.FirstThunk as usize +
i as usize * size_of::<u32>() as usize) as *mut i32;
if image_thunk_data.u1.AddressOfData == 0 {
break;
}
if image_thunk_data.u1.AddressOfData < 0x80000000 {
let mut imp_by_name_ptr = (module_memory_base as usize + image_thunk_data.u1.AddressOfData as usize +
size_of::<u16>() as usize) as *mut u8;
let mut import_name: String = "".to_string();
let mut c: char = ' ';
while c != '\0'
{
c = *imp_by_name_ptr as char;
if c != '\0' {
import_name.push(c);
}
imp_by_name_ptr = imp_by_name_ptr.add(1);
}
let func_ptr = dinvoke::get_function_address(module_handle, &import_name);
*ft_itd = func_ptr as i32;
} else {
let f_ordinal = (image_thunk_data.u1.AddressOfData & 0xFFFF) as u32;
let func_ptr = dinvoke::get_function_address_by_ordinal(module_handle, f_ordinal);
let func_ptr = func_ptr as *mut i32;
*ft_itd = func_ptr as i32;
}
i = i + 1;
}
}
else
{
let mut i: isize = 0;
loop
{
let image_thunk_data = (module_memory_base as u64 + image_import_descriptor.Anonymous.OriginalFirstThunk as u64
+ i as u64 * size_of::<u64>() as u64) as *mut IMAGE_THUNK_DATA64;
let image_thunk_data = *image_thunk_data;
let ft_itd = (module_memory_base as u64 + image_import_descriptor.FirstThunk as u64 +
i as u64 * size_of::<u64>() as u64) as *mut isize;
if image_thunk_data.u1.AddressOfData == 0 {
break;
}
if image_thunk_data.u1.AddressOfData < 0x8000000000000000 {
let mut imp_by_name_ptr = (module_memory_base as u64 + image_thunk_data.u1.AddressOfData as u64 +
size_of::<u16>() as u64) as *mut u8;
let mut import_name: String = "".to_string();
let mut c: char = ' ';
while c != '\0'
{
c = *imp_by_name_ptr as char;
if c != '\0' {
import_name.push(c);
}
imp_by_name_ptr = imp_by_name_ptr.add(1);
}
let func_ptr = dinvoke::get_function_address(module_handle, &import_name) as *mut isize;
*ft_itd = func_ptr as isize;
} else {
let f_ordinal = (image_thunk_data.u1.AddressOfData & 0xFFFF) as u32;
let func_ptr = dinvoke::get_function_address_by_ordinal(module_handle, f_ordinal);
*ft_itd = func_ptr as isize;
}
i = i + 1;
}
}
}
counter = counter + 1;
image_import_descriptor_ptr = (import_table_ptr as usize + size_of::<IMAGE_IMPORT_DESCRIPTOR>() as usize * counter) as *mut IMAGE_IMPORT_DESCRIPTOR;
image_import_descriptor = *image_import_descriptor_ptr;
}
Ok(())
}
}
// This method is reponsible for cleaning IOCs that may reveal the pressence of a
// manually mapped PE in a private memory region. It will remove PE magic bytes,
// DOS header and DOS stub.
fn clean_dos_header (image_ptr: *mut c_void)
{
unsafe
{
let mut base_addr = image_ptr as *mut u8;
let pe_header = image_ptr as isize + 0x3C;
while (base_addr as isize) < pe_header {
*base_addr = 0;
base_addr = base_addr.add(1);
}
base_addr = base_addr.add(4);
let e_lfanew = *((image_ptr as usize + 0x3C) as *const u32);
let pe = image_ptr as isize + e_lfanew as isize;
while (base_addr as isize) < pe {
*base_addr = 0;
base_addr = base_addr.add(1);
}
let pe = pe as *mut u16;
*pe = 0;
}
}
pub fn add_runtime_table(pe_info: &PeMetadata, image_ptr: *mut c_void)
{
unsafe
{
for section in &pe_info.sections
{
let s = std::str::from_utf8(§ion.Name).unwrap();
if s.contains(&lc!(".pdata"))
{
let entry_count = (section.SizeOfRawData / 12) as i32; // 12 = size_of RUNTIME_FUNCTION
let func: dinvoke_data::RtlAddFunctionTable;
let _ret: Option<bool>;
let k32 = dinvoke::get_module_base_address(&lc!("kernel32.dll"));
let function_table_addr: usize = image_ptr as usize + section.VirtualAddress as usize;
dinvoke::dynamic_invoke!(k32,&lc!("RtlAddFunctionTable"),func,_ret,function_table_addr,entry_count,image_ptr as usize);
}
}
}
}
/// Sets correct module section permissions for a manually mapped module.
///
/// The parameters required are the module's metadata information and a
/// pointer to the base address where the module is mapped in memory.
pub fn set_module_section_permissions(pe_info: &PeMetadata, image_ptr: *mut c_void) -> Result<(),String>
{
unsafe
{
let base_of_code;
if pe_info.is_32_bit {
base_of_code = pe_info.opt_header_32.BaseOfCode as usize;
} else {
base_of_code = pe_info.opt_header_64.base_of_code as usize;
}
let handle = GetCurrentProcess();
let base_address: *mut PVOID = std::mem::transmute(&image_ptr);
let s: UnsafeCell<isize> = isize::default().into();
let size: *mut usize = std::mem::transmute(s.get());
*size = base_of_code;
let o = u32::default();
let old_protection: *mut u32 = std::mem::transmute(&o);
let _ret = dinvoke::nt_protect_virtual_memory(handle, base_address, size, PAGE_READONLY, old_protection);
for section in &pe_info.sections
{
let is_read = (section.Characteristics.0 & SECTION_MEM_READ) != 0;
let is_write = (section.Characteristics.0 & SECTION_MEM_WRITE) != 0;
let is_execute = (section.Characteristics.0 & SECTION_MEM_EXECUTE) != 0;
let new_protect: u32;
if is_read & !is_write & !is_execute {
new_protect = PAGE_READONLY;
} else if is_read & is_write & !is_execute {
new_protect = PAGE_READWRITE;
} else if is_read & is_write & is_execute {
new_protect = PAGE_EXECUTE_READWRITE;
} else if is_read & !is_write & is_execute {
new_protect = PAGE_EXECUTE_READ;
} else if !is_read & !is_write & is_execute {
new_protect = PAGE_EXECUTE;
} else {
return Err(lc!("[x] Unknown section permission."));
}
let address: *mut c_void = (image_ptr as usize + section.VirtualAddress as usize) as *mut c_void;
let base_address: *mut PVOID = std::mem::transmute(&address);
*size = section.Misc.VirtualSize as usize;
let o = u32::default();
let old_protection: *mut u32 = std::mem::transmute(&o);
let ret = dinvoke::nt_protect_virtual_memory(handle, base_address, size, new_protect, old_protection);
let _r = dinvoke::close_handle(handle);
if ret != 0 {
return Err(lc!("[x] Error changing section permission."));
}
}
Ok(())
}
}
/// Executes any registered TLS Callback function.
///
/// The parameters required are the module's metadata information and a
/// pointer to the base address where the module is mapped in memory.
pub fn run_tls_callbacks(pe_info: &PeMetadata, image_ptr: *mut c_void)
{
unsafe
{
let entry_point;
if pe_info.is_32_bit {
entry_point = image_ptr as isize + pe_info.opt_header_32.AddressOfEntryPoint as isize;
} else {
entry_point = image_ptr as isize + pe_info.opt_header_64.address_of_entry_point as isize;
}
if pe_info.opt_header_64.number_of_rva_and_sizes >= 10
{
let address: *mut u8 = (image_ptr as usize + pe_info.opt_header_64.datas_directory[9].VirtualAddress as usize) as *mut u8;
let address_of_tls_callback = address.add(24) as *mut usize;
let mut address_of_tls_callback_array: *mut usize = std::mem::transmute(*address_of_tls_callback);
while *address_of_tls_callback_array != 0 {
let tls_callback: extern "system" fn (isize, u32, PVOID) = std::mem::transmute(*address_of_tls_callback_array);
tls_callback(entry_point, 1, ptr::null_mut());
address_of_tls_callback_array = address_of_tls_callback_array.add(1);
}
}
}
}
/// Map a module to a memory section.
///
/// The parameter required is the file path of the module that should be mapped.
pub fn map_to_section(module_path: &str) -> Result<(PeManualMap,HANDLE),String>
{
unsafe
{
if !Path::new(&module_path).is_file() {
return Err(lc!("[x] Filepath not found."));
}
let module_path = format!("{}{}", "\\??\\", module_path);
let mut module_path_utf16: Vec<u16> = module_path.encode_utf16().collect();
module_path_utf16.push(0);
let o_name = UNICODE_STRING::default();
let object_name: *mut UNICODE_STRING = std::mem::transmute(&o_name);
dinvoke::rtl_init_unicode_string(object_name, module_path_utf16.as_ptr());
let mut object_attributes = OBJECT_ATTRIBUTES::default();
object_attributes.Length = size_of::<OBJECT_ATTRIBUTES>() as u32;
object_attributes.ObjectName = object_name;
object_attributes.Attributes = OBJECT_ATTRIBUTE_FLAGS(0x40); // Case Insensitive
let io: Vec<u8> = vec![0; size_of::<IO_STATUS_BLOCK>()];
let io: *mut IO_STATUS_BLOCK = std::mem::transmute(io.as_ptr());
let object_attributes: *mut OBJECT_ATTRIBUTES = std::mem::transmute(&object_attributes);
let h = HANDLE::default();
let hfile: *mut HANDLE = std::mem::transmute(&h);
let r = dinvoke::nt_open_file(
hfile,
FILE_READ_DATA | FILE_EXECUTE | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
object_attributes,
io,
FILE_SHARE_READ | FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE,
);
if r != 0 {
return Err(lc!("[x] Error opening file."));
}
let max_size: Vec<u8> =vec![0; size_of::<LARGE_INTEGER>()];
let max_size: *mut LARGE_INTEGER = std::mem::transmute(max_size.as_ptr());
let h = HANDLE::default();
let hsection: *mut HANDLE = std::mem::transmute(&h);
let r = dinvoke::nt_create_section(
hsection,
SECTION_ALL_ACCESS,
ptr::null_mut(),
max_size,
PAGE_READONLY,
SEC_IMAGE,
*hfile
);
if r != 0 {
return Err(lc!("[x] Error creating file section in memory."));
}
let offset: Vec<u8> =vec![0; size_of::<LARGE_INTEGER>()];
let offset: *mut LARGE_INTEGER = std::mem::transmute(offset.as_ptr());
let b = usize::default();
let base_address: *mut PVOID = std::mem::transmute(&b);
let v = usize::default();
let view_size: *mut usize = std::mem::transmute(&v);
let r = dinvoke::nt_map_view_of_section(
*hsection,
HANDLE { 0: -1 as _},
base_address,
0,
0,
offset,
view_size,
0x2,
0x0,
PAGE_READWRITE
);
if r != 0 {
return Err(lc!("[x] Error mapping file section."));
}
let base_address: *const u8 = std::mem::transmute(*base_address);
let sec_object: PeManualMap = PeManualMap { pe_info : get_pe_metadata(base_address, false).unwrap(),
base_address : base_address as usize, decoy_module: module_path};
let _r = dinvoke::close_handle(*hfile);
Ok((sec_object, *hsection))
}
}
pub fn map_to_allocated_memory (module_ptr: *const u8, image_ptr: *mut c_void, pe_info: &PeMetadata) -> Result<(), String>
{
map_module_to_memory(module_ptr, image_ptr, &pe_info)?;
relocate_module(&pe_info, image_ptr);
rewrite_module_iat(&pe_info, image_ptr)?;
clean_dos_header(image_ptr);
set_module_section_permissions(&pe_info, image_ptr)?;
add_runtime_table(&pe_info, image_ptr);
Ok(())
}
/// Manually map a file to a memory section, performing IAT fixing and relocations, leaving the module ready to be executed.
/// This tries to replicate LoadLibrary but allowing you to use a file handle instead of a file name or path.
pub fn map_file_to_section_from_handle(file_handle: HANDLE) -> Result<PeSectionMap,String>
{
unsafe
{
let max_size: Vec<u8> =vec![0; size_of::<LARGE_INTEGER>()];
let max_size: *mut LARGE_INTEGER = std::mem::transmute(max_size.as_ptr());
let section_handle = HANDLE::default();
let section_handle_ptr: *mut HANDLE = std::mem::transmute(§ion_handle);
let ret = dinvoke::nt_create_section(
section_handle_ptr,
SECTION_ALL_ACCESS,
ptr::null_mut(),
max_size,
PAGE_READONLY,
SEC_IMAGE,
file_handle
);
if ret != 0 {
return Err(lc!("[x] Error creating file section in memory."));
}
let offset: Vec<u8> =vec![0; size_of::<LARGE_INTEGER>()];
let offset: *mut LARGE_INTEGER = std::mem::transmute(offset.as_ptr());
let baddr = usize::default();
let base_address: *mut PVOID = std::mem::transmute(&baddr);
let size = usize::default();
let view_size: *mut usize = std::mem::transmute(&size);
let ret = dinvoke::nt_map_view_of_section(
*section_handle_ptr,
HANDLE { 0: -1 as _},
base_address,
0,
0,
offset,
view_size,
0x2,
0x0,
PAGE_READWRITE
);
if ret != 0 {
return Err(lc!("[x] Error mapping file section."));
}
let base_address: *const u8 = std::mem::transmute(*base_address);
let return_data: PeSectionMap = PeSectionMap { pe_info : get_pe_metadata(base_address, false).unwrap(),
base_address : base_address as usize, section_handle: (*section_handle_ptr).0 as usize};
let mut all_addresses = get_iat_addresses(&return_data.pe_info, base_address as _);
let reloc_addreses = get_reloc_addresses(&return_data.pe_info, base_address as _);
all_addresses.extend(reloc_addreses);
if page_align_sort_dedup(&mut all_addresses) == 0 {
dinvoke::nt_unmap_view_of_section(*section_handle_ptr, base_address as _);
dinvoke::close_handle(*section_handle_ptr);
return Err(lc!("[x] Error detected: page size == 0."));
}
let non_writable_regions = collect_non_writable_regions(&all_addresses);
let ret = protect_regions_for_patching(non_writable_regions);
if ret.is_err()
{
dinvoke::nt_unmap_view_of_section(*section_handle_ptr, base_address as _);
dinvoke::close_handle(*section_handle_ptr);
let error = format!("{}{:x}.", lc!("[x] Error changing memory protection. NTSTATUS: "), ret.err().unwrap());
return Err(error);
}
rewrite_module_iat(&return_data.pe_info, base_address as _)?;
relocate_module(&return_data.pe_info, base_address as _);
let ret = set_module_section_permissions(&return_data.pe_info, base_address as _);
if ret.is_err() {
dinvoke::nt_unmap_view_of_section(*section_handle_ptr, base_address as _);
dinvoke::close_handle(*section_handle_ptr);
let error = format!("{}{}.", lc!("[x] Call to 'set_module_section_permissions' failed. Error: "), ret.err().unwrap());
return Err(error);
}
add_runtime_table(&return_data.pe_info, base_address as _);
Ok(return_data)
}
}
fn get_iat_addresses(pe_info: &PeMetadata, image_ptr: *mut c_void) ->Vec<usize>
{
unsafe
{
let mut addresses = vec![];
let module_memory_base: *mut usize = std::mem::transmute(image_ptr);
let image_data_directory;
if pe_info.is_32_bit {
image_data_directory = pe_info.opt_header_32.DataDirectory[1]; // ImportTable
} else {
image_data_directory = pe_info.opt_header_64.datas_directory[1]; // ImportTable
}
if image_data_directory.VirtualAddress == 0 {
return addresses; // No import table
}
let import_table_ptr = (module_memory_base as usize + image_data_directory.VirtualAddress as usize) as *mut usize;
let mut counter = 0;
let mut image_import_descriptor_ptr = (import_table_ptr as usize + size_of::<IMAGE_IMPORT_DESCRIPTOR>() as usize * counter) as *mut IMAGE_IMPORT_DESCRIPTOR;
let mut image_import_descriptor = *image_import_descriptor_ptr;
while image_import_descriptor.Name != 0
{
let mut dll_name = "".to_string();
let mut c: char = ' ';
let mut ptr = (module_memory_base as usize + image_import_descriptor.Name as usize) as *mut u8;
while c != '\0'
{
c = *ptr as char;
if c != '\0' {
dll_name.push(c);
ptr = ptr.add(1);
}
}
if dll_name == "" {
return addresses;
} else {
if pe_info.is_32_bit {
let mut i: isize = 0;
loop
{
let image_thunk_data = (module_memory_base as usize + image_import_descriptor.Anonymous.OriginalFirstThunk as usize
+ i as usize * size_of::<u32>() as usize) as *mut IMAGE_THUNK_DATA32;
let image_thunk_data = *image_thunk_data;
let ft_itd = (module_memory_base as usize + image_import_descriptor.FirstThunk as usize +
i as usize * size_of::<u32>() as usize) as *mut i32;
addresses.push(ft_itd as usize);
if image_thunk_data.u1.AddressOfData == 0 {
break;
}
i = i + 1;
}
} else {
let mut i: isize = 0;
loop
{
let image_thunk_data = (module_memory_base as u64 + image_import_descriptor.Anonymous.OriginalFirstThunk as u64
+ i as u64 * size_of::<u64>() as u64) as *mut IMAGE_THUNK_DATA64;
let image_thunk_data = *image_thunk_data;
let ft_itd = (module_memory_base as u64 + image_import_descriptor.FirstThunk as u64 +
i as u64 * size_of::<u64>() as u64) as *mut isize;
addresses.push(ft_itd as usize);
if image_thunk_data.u1.AddressOfData == 0 {
break;
}
i = i + 1;
}
}
}
counter = counter + 1;
image_import_descriptor_ptr = (import_table_ptr as usize + size_of::<IMAGE_IMPORT_DESCRIPTOR>() as usize * counter) as *mut IMAGE_IMPORT_DESCRIPTOR;
image_import_descriptor = *image_import_descriptor_ptr;
}
addresses
}
}
fn get_reloc_addresses(pe_info: &PeMetadata, image_ptr: *mut c_void) -> Vec<usize>
{
unsafe
{
let mut addresses = vec![];
let module_memory_base: *mut usize = std::mem::transmute(image_ptr);
let image_data_directory;
if pe_info.is_32_bit {
image_data_directory = pe_info.opt_header_32.DataDirectory[5]; // BaseRelocationTable
} else {
image_data_directory = pe_info.opt_header_64.datas_directory[5]; // BaseRelocationTable
}
let mut reloc_table_ptr = (module_memory_base as usize + image_data_directory.VirtualAddress as usize) as *mut i32;
let mut next_reloc_table_block = -1;
while next_reloc_table_block != 0
{
let ibr: *mut IMAGE_BASE_RELOCATION = std::mem::transmute(reloc_table_ptr);
let image_base_relocation = *ibr;
let reloc_count: isize = (image_base_relocation.SizeOfBlock as isize - size_of::<IMAGE_BASE_RELOCATION>() as isize) / 2;
for i in 0..reloc_count
{
let reloc_entry_ptr = (reloc_table_ptr as usize + size_of::<IMAGE_BASE_RELOCATION>() as usize + (i * 2) as usize) as *mut u16;
let reloc_value = *reloc_entry_ptr;
let reloc_type = reloc_value >> 12;
let reloc_patch = reloc_value & 0xfff;
if reloc_type != 0 {
let patch_ptr = (module_memory_base as usize + image_base_relocation.VirtualAddress as usize + reloc_patch as usize) as usize;
addresses.push(patch_ptr);
}
}
reloc_table_ptr = (reloc_table_ptr as usize + image_base_relocation.SizeOfBlock as usize) as *mut i32;
next_reloc_table_block = *reloc_table_ptr;
}
addresses
}
}
fn page_align_sort_dedup(addrs: &mut Vec<usize>) -> usize
{
let mut si: SYSTEM_INFO = unsafe { std::mem::zeroed() };
dinvoke::get_system_info(&mut si);
let page_size = si.dwPageSize as usize;
if page_size == 0 {
return 0;
}
for a in addrs.iter_mut() {
*a = (*a / page_size) * page_size;
}
// sort + dedup
addrs.sort_unstable();
addrs.dedup();
page_size
}
#[inline]
fn protection_allows_write(protection: u32) -> bool {
(protection & PAGE_READWRITE) != 0
|| (protection & PAGE_WRITECOPY) != 0
|| (protection & PAGE_EXECUTE_READWRITE) != 0
|| (protection & PAGE_EXECUTE_WRITECOPY) != 0
}
// Returns non writable regions so the memory protection can be changed before IAT fixing + relocations
fn collect_non_writable_regions(pages: &[usize]) -> Vec<RegionInfo>
{
let mut out: Vec<RegionInfo> = Vec::new();
// Parameter 'pages' should be already sorted
let mut last_region_base: usize = 0;
for &page_base in pages
{
let mut mbi: MEMORY_BASIC_INFORMATION = unsafe { std::mem::zeroed() };
let ret = dinvoke::virtual_query(page_base as _, &mut mbi, size_of::<MEMORY_BASIC_INFORMATION>());
if ret == 0 {
continue;
}
// Only commited memory
if mbi.State.0 != MEM_COMMIT {
continue;
}
// We don't touch these pages
if (mbi.Protect.0 & PAGE_NOACCESS) != 0 || (mbi.Protect.0 & PAGE_GUARD) != 0 {
continue;
}
let region_base = mbi.BaseAddress as usize;
let region_size = mbi.RegionSize;
let region_protect = mbi.Protect;
// We avoid duplicated info
if region_base == last_region_base {
continue;
}
// Already writable regions are ignored
if protection_allows_write(region_protect.0) {
last_region_base = region_base;
continue;
}
out.push(RegionInfo {
base_address: region_base,
region_size: region_size,
memory_protection: region_protect.0,
});
last_region_base = region_base;
}
out
}
#[inline]
fn is_executable_protect(protection: u32) -> bool {
(protection & 0xF0) != 0
}
fn protect_regions_for_patching(regions: Vec<RegionInfo>) -> Result<(), i32>
{
unsafe
{
for r in regions.iter()
{
let new_protect = if is_executable_protect(r.memory_protection) {
PAGE_EXECUTE_READWRITE
} else {
PAGE_READWRITE
};
let handle = GetCurrentProcess();
let base_address: *mut PVOID = std::mem::transmute(&r.base_address);
let s: UnsafeCell<isize> = isize::default().into();
let size: *mut usize = std::mem::transmute(s.get());
*size = r.region_size;
let o = u32::default();
let old_protection: *mut u32 = std::mem::transmute(&o);
let ret = dinvoke::nt_protect_virtual_memory(handle, base_address, size, new_protect, old_protection);
if ret != 0 {
return Err(ret);
}
}
Ok(())
}
}