use crate::emu;
use crate::pe::pe32::{
DelayLoadDirectory, HintNameItem, ImageDataDirectory, ImageDosHeader, ImageExportDirectory,
ImageFileHeader, ImageImportDescriptor, ImageImportDirectory, ImageNtHeaders,
ImageSectionHeader, IMAGE_DIRECTORY_ENTRY_BASERELOC, IMAGE_DIRECTORY_ENTRY_DELAY_LOAD, IMAGE_DIRECTORY_ENTRY_EXPORT,
IMAGE_DIRECTORY_ENTRY_IAT, IMAGE_DIRECTORY_ENTRY_IMPORT, IMAGE_DIRECTORY_ENTRY_TLS,
IMAGE_NUMBEROF_DIRECTORY_ENTRIES, PE32, SECTION_HEADER_SZ,
};
use crate::structures;
use crate::winapi::winapi64;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::str;
macro_rules! read_u8 {
($raw:expr, $off:expr) => {
$raw[$off]
};
}
macro_rules! read_u16_le {
($raw:expr, $off:expr) => {
(($raw[$off + 1] as u16) << 8) | ($raw[$off] as u16)
};
}
macro_rules! read_u32_le {
($raw:expr, $off:expr) => {
(($raw[$off + 3] as u32) << 24)
| (($raw[$off + 2] as u32) << 16)
| (($raw[$off + 1] as u32) << 8)
| ($raw[$off] as u32)
};
}
macro_rules! read_u64_le {
($raw:expr, $off:expr) => {
(($raw[$off + 7] as u64) << 56)
| (($raw[$off + 6] as u64) << 48)
| (($raw[$off + 5] as u64) << 40)
| (($raw[$off + 4] as u64) << 32)
| (($raw[$off + 3] as u64) << 24)
| (($raw[$off + 2] as u64) << 16)
| (($raw[$off + 1] as u64) << 8)
| ($raw[$off] as u64)
};
}
macro_rules! write_u64_le {
($raw:expr, $off:expr, $val:expr) => {
$raw[$off + 0] = ($val & 0x00000000_000000ff) as u8;
$raw[$off + 1] = (($val & 0x00000000_0000ff00) >> 8) as u8;
$raw[$off + 2] = (($val & 0x00000000_00ff0000) >> 16) as u8;
$raw[$off + 3] = (($val & 0x00000000_ff000000) >> 24) as u8;
$raw[$off + 4] = (($val & 0x000000ff_00000000) >> 32) as u8;
$raw[$off + 5] = (($val & 0x0000ff00_00000000) >> 40) as u8;
$raw[$off + 6] = (($val & 0x00ff0000_00000000) >> 48) as u8;
$raw[$off + 7] = (($val & 0xff000000_00000000) >> 56) as u8;
};
}
const IMAGE_FILE_DLL: u16 = 0x2000;
#[derive(Debug)]
pub struct ImageOptionalHeader64 {
pub magic: u16,
pub major_linker_version: u8,
pub minor_linker_version: u8,
pub size_of_code: u32,
pub size_of_initialized_data: u32,
pub size_of_uninitialized_data: u32,
pub address_of_entry_point: u32,
pub base_of_code: u32,
pub image_base: u64,
pub section_alignment: u32,
pub file_alignment: u32,
pub major_operating_system_version: u16,
pub minor_operating_system_version: u16,
pub major_image_version: u16,
pub minor_image_version: u16,
pub major_subsystem_version: u16,
pub minor_subsystem_version: u16,
pub win32_version_value: u32,
pub size_of_image: u32,
pub size_of_headers: u32,
pub checksum: u32,
pub subsystem: u16,
pub dll_characteristics: u16,
pub size_of_stack_reserve: u64,
pub size_of_stack_commit: u64,
pub size_of_heap_reserve: u64,
pub size_of_heap_commit: u64,
pub loader_flags: u32,
pub number_of_rva_and_sizes: u32,
pub data_directory: Vec<ImageDataDirectory>, }
impl ImageOptionalHeader64 {
pub fn load(raw: &[u8], off: usize) -> ImageOptionalHeader64 {
let mut dd: Vec<ImageDataDirectory> = Vec::new();
let mut pos = 112; for i in 0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES {
let idd = ImageDataDirectory::load(raw, off + pos);
dd.push(idd);
pos += 8;
}
ImageOptionalHeader64 {
magic: read_u16_le!(raw, off),
major_linker_version: read_u8!(raw, off + 2),
minor_linker_version: read_u8!(raw, off + 3),
size_of_code: read_u32_le!(raw, off + 4),
size_of_initialized_data: read_u32_le!(raw, off + 8),
size_of_uninitialized_data: read_u32_le!(raw, off + 12),
address_of_entry_point: read_u32_le!(raw, off + 16),
base_of_code: read_u32_le!(raw, off + 20),
image_base: read_u64_le!(raw, off + 24),
section_alignment: read_u32_le!(raw, off + 32),
file_alignment: read_u32_le!(raw, off + 36),
major_operating_system_version: read_u16_le!(raw, off + 40),
minor_operating_system_version: read_u16_le!(raw, off + 42),
major_image_version: read_u16_le!(raw, off + 44),
minor_image_version: read_u16_le!(raw, off + 46),
major_subsystem_version: read_u16_le!(raw, off + 48),
minor_subsystem_version: read_u16_le!(raw, off + 50),
win32_version_value: read_u32_le!(raw, off + 52),
size_of_image: read_u32_le!(raw, off + 56),
size_of_headers: read_u32_le!(raw, off + 60),
checksum: read_u32_le!(raw, off + 64),
subsystem: read_u16_le!(raw, off + 68),
dll_characteristics: read_u16_le!(raw, off + 70),
size_of_stack_reserve: read_u64_le!(raw, off + 72),
size_of_stack_commit: read_u64_le!(raw, off + 80),
size_of_heap_reserve: read_u64_le!(raw, off + 88),
size_of_heap_commit: read_u64_le!(raw, off + 94),
loader_flags: read_u32_le!(raw, off + 102),
number_of_rva_and_sizes: read_u32_le!(raw, off + 106),
data_directory: dd,
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
}
#[derive(Debug)]
pub struct TlsDirectory64 {
tls_data_start: u64,
tls_data_end: u64,
tls_index: u64, tls_callbacks: u64,
zero_fill_size: u32, characteristic: u32,
}
impl TlsDirectory64 {
pub fn load(raw: &[u8], off: usize) -> TlsDirectory64 {
TlsDirectory64 {
tls_data_start: read_u64_le!(raw, off),
tls_data_end: read_u64_le!(raw, off + 8),
tls_index: read_u64_le!(raw, off + 16),
tls_callbacks: read_u64_le!(raw, off + 24),
zero_fill_size: read_u32_le!(raw, off + 32),
characteristic: read_u32_le!(raw, off + 36),
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
}
#[derive(Debug)]
pub struct DelayLoadIAT {
name_ptr: u32,
iat_addr: u64,
bound_iat: u64,
}
impl DelayLoadIAT {
fn load(raw: &[u8], off: usize) -> DelayLoadIAT {
DelayLoadIAT {
name_ptr: read_u32_le!(raw, off),
iat_addr: read_u64_le!(raw, off + 4),
bound_iat: read_u64_le!(raw, off + 8),
}
}
}
pub struct PE64 {
pub filename: String,
pub raw: Vec<u8>,
pub dos: ImageDosHeader,
pub nt: ImageNtHeaders,
pub fh: ImageFileHeader,
pub opt: ImageOptionalHeader64,
pub sect_hdr: Vec<ImageSectionHeader>,
pub delay_load_dir: Vec<DelayLoadDirectory>,
pub image_import_descriptor: Vec<ImageImportDescriptor>,
}
impl PE64 {
pub fn is_pe64(filename: &str) -> bool {
let mut fd = File::open(filename).expect("file not found");
let mut raw = vec![0u8; ImageDosHeader::size()];
fd.read_exact(&mut raw).expect("couldnt read the file");
let dos = ImageDosHeader::load(&raw, 0);
if dos.e_magic != 0x5a4d {
return false;
}
if dos.e_lfanew >= fd.metadata().unwrap().len() as u32 {
return false;
}
true
}
pub fn load_from_raw(filename: &str, raw: &[u8]) -> PE64 {
let dos = ImageDosHeader::load(&raw, 0);
let nt = ImageNtHeaders::load(&raw, dos.e_lfanew as usize);
let fh = ImageFileHeader::load(&raw, dos.e_lfanew as usize + 4);
let opt = ImageOptionalHeader64::load(&raw.to_vec(), dos.e_lfanew as usize + 24);
let dos = ImageDosHeader::load(&raw, 0);
let nt = ImageNtHeaders::load(&raw, dos.e_lfanew as usize);
let fh = ImageFileHeader::load(&raw, dos.e_lfanew as usize + 4);
let opt = ImageOptionalHeader64::load(&raw.to_vec(), dos.e_lfanew as usize + 24);
let mut sect: Vec<ImageSectionHeader> = Vec::new();
let mut off = dos.e_lfanew as usize + 24 + fh.size_of_optional_header as usize;
for i in 0..fh.number_of_sections {
let s = ImageSectionHeader::load(&raw, off);
sect.push(s);
off += SECTION_HEADER_SZ;
}
let importd: ImageImportDirectory;
let exportd: ImageExportDirectory;
let import_va = opt.data_directory[IMAGE_DIRECTORY_ENTRY_IMPORT].virtual_address;
let export_va = opt.data_directory[IMAGE_DIRECTORY_ENTRY_EXPORT].virtual_address;
let delay_load_va = opt.data_directory[IMAGE_DIRECTORY_ENTRY_DELAY_LOAD].virtual_address;
let mut import_off: usize;
let mut delay_load_off: usize;
let mut image_import_descriptor: Vec<ImageImportDescriptor> = Vec::new();
let mut delay_load_dir: Vec<DelayLoadDirectory> = Vec::new();
if delay_load_va > 0 {
delay_load_off = PE64::vaddr_to_off(§, delay_load_va) as usize;
if delay_load_off > 0 {
loop {
let mut delay_load = DelayLoadDirectory::load(&raw, delay_load_off);
if delay_load.handle == 0 || delay_load.name_ptr == 0 {
break;
}
let off = PE64::vaddr_to_off(§, delay_load.name_ptr) as usize;
if off > raw.len() {
panic!("the delay_load.name of pe64 is out of buffer");
}
let libname = PE32::read_string(&raw, off);
delay_load.name = libname.to_string();
delay_load_dir.push(delay_load);
delay_load_off += DelayLoadDirectory::size();
}
}
}
if import_va > 0 {
import_off = PE64::vaddr_to_off(§, import_va) as usize;
if import_off > 0 {
loop {
let mut iid = ImageImportDescriptor::load(&raw, import_off);
if iid.name_ptr == 0 {
break;
}
let off = PE64::vaddr_to_off(§, iid.name_ptr) as usize;
if off > raw.len() {
panic!("the name of pe64 iid is out of buffer");
}
let libname = PE32::read_string(&raw, off);
iid.name = libname.to_string();
image_import_descriptor.push(iid);
import_off += ImageImportDescriptor::size();
}
} else {
}
} else {
}
PE64 {
filename: filename.to_string(),
raw: raw.to_vec(),
dos,
fh,
nt,
opt,
sect_hdr: sect,
delay_load_dir,
image_import_descriptor, }
}
pub fn load(filename: &str) -> PE64 {
let mut fd = File::open(filename).expect("pe64 binary not found");
let mut raw: Vec<u8> = Vec::new();
fd.read_to_end(&mut raw)
.expect("couldnt read the pe64 binary");
PE64::load_from_raw(filename, &raw)
}
pub fn size(&self) -> u64 {
self.raw.len() as u64
}
pub fn mem_size(&self) -> usize {
let mut sz = 0;
for i in 0..self.sect_hdr.len() {
let sect = &self.sect_hdr[i];
if sect.virtual_size > sect.size_of_raw_data {
sz += sect.virtual_size as usize;
} else {
sz += sect.size_of_raw_data as usize;
}
}
sz
}
pub fn is_dll(&self) -> bool {
self.fh.characteristics & IMAGE_FILE_DLL != 0
}
pub fn get_raw(&self) -> &[u8] {
&self.raw[0..self.raw.len()]
}
pub fn get_headers(&self) -> &[u8] {
&self.raw[0..self.opt.size_of_headers as usize]
}
pub fn clear(&mut self) {
self.raw.clear();
self.sect_hdr.clear();
}
pub fn vaddr_to_off(sections: &Vec<ImageSectionHeader>, vaddr: u32) -> u32 {
for sect in sections {
if vaddr >= sect.virtual_address && vaddr < sect.virtual_address + sect.virtual_size {
let offset_within_section = vaddr - sect.virtual_address;
if offset_within_section >= sect.size_of_raw_data {
log::warn!("Virtual address 0x{:x} maps to uninitialized data in section '{}' (offset {} >= raw_size {})",
vaddr, sect.get_name(), offset_within_section, sect.size_of_raw_data);
return 0; }
let file_offset = sect.pointer_to_raw_data + offset_within_section;
return file_offset;
}
}
0
}
pub fn read_string(raw: &[u8], off: usize) -> String {
if off >= raw.len() {
return String::new();
}
let end = raw[off..]
.iter()
.position(|&b| b == 0)
.map(|pos| off + pos)
.unwrap_or(raw.len());
match std::str::from_utf8(&raw[off..end]) {
Ok(s) => s.to_string(),
Err(_) => "".to_string(),
}
}
pub fn num_of_sections(&self) -> usize {
self.sect_hdr.len()
}
pub fn get_section_ptr_by_name(&self, name: &str) -> Option<&[u8]> {
for i in 0..self.sect_hdr.len() {
if self.sect_hdr[i].get_name() == name {
let off = self.sect_hdr[i].pointer_to_raw_data as usize;
let sz = self.sect_hdr[i].virtual_size as usize;
let section_ptr = &self.raw[off..off + sz];
return Some(section_ptr);
}
}
None
}
pub fn get_section(&self, id: usize) -> &ImageSectionHeader {
&self.sect_hdr[id]
}
pub fn get_pe_off(&self) -> u32 {
self.dos.e_lfanew
}
pub fn get_section_ptr(&self, id: usize) -> &[u8] {
if id > self.sect_hdr.len() {
panic!("/!\\ warning: invalid section id {}", id);
}
let off = self.sect_hdr[id].pointer_to_raw_data as usize;
let sz = self.sect_hdr[id].size_of_raw_data as usize; if off + sz > self.raw.len() {
log::trace!(
"/!\\ warning: id:{} name:{} raw sz:{} off:{} sz:{} off+sz:{}",
id,
self.sect_hdr[id].get_name(),
self.raw.len(),
off,
sz,
off + sz
);
if off > self.raw.len() {
return &[];
}
return &self.raw[off..];
}
&self.raw[off..off + sz]
}
pub fn get_section_vaddr(&self, id: usize) -> u32 {
self.sect_hdr[id].virtual_address
}
pub fn get_tls_callbacks(&self, vaddr: u32) -> Vec<u64> {
let mut callbacks: Vec<u64> = Vec::new();
if self.opt.data_directory.len() < IMAGE_DIRECTORY_ENTRY_TLS {
log::trace!("/!\\ alert there is .tls section but not tls directory entry");
return callbacks;
}
let entry_tls = self.opt.data_directory[IMAGE_DIRECTORY_ENTRY_TLS].virtual_address;
let iat = self.opt.data_directory[IMAGE_DIRECTORY_ENTRY_IAT].virtual_address;
let align = self.opt.file_alignment;
let tls_off = PE64::vaddr_to_off(&self.sect_hdr, entry_tls) as usize;
let tls = TlsDirectory64::load(&self.raw, tls_off);
tls.print();
let mut cb_off = PE64::vaddr_to_off(&self.sect_hdr, (tls.tls_callbacks & 0xffff) as u32);
loop {
let callback: u64 = read_u64_le!(&self.raw, cb_off as usize);
if callback == 0 {
break;
}
log::trace!("0x{:x} TLS Callback: 0x{:x}", cb_off, callback);
callbacks.push(callback);
cb_off += 8;
}
callbacks
}
pub fn delay_load_binding(&mut self, emu: &mut emu::Emu, base_addr: u64) {
log::trace!("Delay load binding started ...");
let mut resolved_cache: HashMap<String, u64> = HashMap::new();
for i in 0..self.delay_load_dir.len() {
let dld = &self.delay_load_dir[i];
if dld.name.is_empty() {
continue;
}
let mut off_name = PE64::vaddr_to_off(&self.sect_hdr, dld.name_table) as usize;
let mut off_addr = PE64::vaddr_to_off(&self.sect_hdr, dld.address_table) as usize;
loop {
if self.raw.len() <= off_name + 4 || self.raw.len() <= off_addr + 4 {
break;
}
let hint = HintNameItem::load(&self.raw, off_name);
let addr = read_u32_le!(self.raw, off_addr); let off2 = PE64::vaddr_to_off(&self.sect_hdr, hint.func_name_addr) as usize;
if off2 == 0 {
off_name += HintNameItem::size();
off_addr += 8;
continue;
}
let func_name = PE64::read_string(&self.raw, off2 + 2);
let cache_key = format!("{}!{}", dld.name.to_lowercase(), func_name.to_lowercase());
let real_addr = if let Some(cached) = resolved_cache.get(&cache_key) {
*cached
} else {
let resolved =
winapi64::kernel32::resolve_api_name_in_module(emu, &dld.name, &func_name);
resolved_cache.insert(cache_key, resolved);
resolved
};
if real_addr == 0 {
break;
}
write_u64_le!(self.raw, off_addr, real_addr);
let patch_addr = base_addr + addr as u64;
if let Some(mem) = emu.maps.get_mem_by_addr_mut(patch_addr) {
mem.force_write_qword(patch_addr, real_addr);
}
off_name += HintNameItem::size();
off_addr += 8;
}
}
log::trace!("delay load bound!");
}
pub fn get_dependencies(&mut self, emu: &mut emu::Emu) -> Vec<String> {
let mut dependencies: Vec<String> = Vec::new();
for i in 0..self.image_import_descriptor.len() {
let iim = &self.image_import_descriptor[i];
if iim.name.is_empty() {
continue;
}
let mut libname = iim.name.clone();
if iim.name.starts_with("api-ms-win-") {
libname = "kernelbase".to_string();
}
dependencies.push(libname);
}
dependencies
}
pub fn iat_binding(&mut self, emu: &mut emu::Emu, base_addr: u64) {
if emu.cfg.verbose >= 1 {
log::trace!(
"IAT binding started image_import_descriptor.len() = {} ...",
self.image_import_descriptor.len()
);
}
let mut resolved_cache: HashMap<String, u64> = HashMap::new();
for i in 0..self.image_import_descriptor.len() {
let iim = &self.image_import_descriptor[i];
if iim.name.is_empty() {
continue;
}
let import_dll = iim.name.clone();
let original_first_thunk = iim.original_first_thunk;
let first_thunk = iim.first_thunk;
if winapi64::kernel32::load_library(emu, &import_dll) == 0 {
if emu.cfg.verbose >= 1 {
log::trace!(
"cannot find/import library `{}` (IAT binding will skip it)",
&import_dll
);
}
continue;
}
if original_first_thunk == 0 {
self.iat_binding_alternative(
emu,
base_addr,
first_thunk,
&import_dll,
&mut resolved_cache,
);
} else {
self.iat_binding_original(
emu,
base_addr,
original_first_thunk,
first_thunk,
&import_dll,
&mut resolved_cache,
);
}
}
log::trace!("IAT Bound.");
}
pub fn iat_binding_alternative(
&mut self,
emu: &mut emu::Emu,
base_addr: u64,
first_thunk: u32,
import_dll: &str,
resolved_cache: &mut HashMap<String, u64>,
) {
let mut rva = first_thunk;
loop {
let off = PE64::vaddr_to_off(&self.sect_hdr, rva) as usize;
if self.raw.len() <= off + 8 {
break;
}
let func_name_addr_or_ordinal = read_u64_le!(self.raw, off);
if func_name_addr_or_ordinal == 0 {
break;
}
let is_ordinal = (func_name_addr_or_ordinal & 0x80000000_00000000) != 0;
if is_ordinal {
let ordinal = (func_name_addr_or_ordinal & 0xFFFF) as u16;
println!("---- ordinal: {}", ordinal);
unimplemented!("third variation of iat binding not implemented");
} else {
let func_name_addr =
(func_name_addr_or_ordinal & 0x7fff_ffff_ffff_ffff) as u32;
let off_name = PE64::vaddr_to_off(&self.sect_hdr, func_name_addr) as usize;
let api_name = PE64::read_string(&self.raw, off_name + 2);
let cache_key =
format!("{}!{}", import_dll.to_lowercase(), api_name.to_lowercase());
let real_addr = if let Some(cached) = resolved_cache.get(&cache_key) {
*cached
} else {
let resolved =
winapi64::kernel32::resolve_api_name_in_module(emu, import_dll, &api_name);
resolved_cache.insert(cache_key, resolved);
resolved
};
if real_addr > 0 {
write_u64_le!(self.raw, off, real_addr);
let patch_addr = base_addr + rva as u64;
if let Some(mem) = emu.maps.get_mem_by_addr_mut(patch_addr) {
mem.force_write_qword(patch_addr, real_addr);
}
} else if emu.cfg.verbose >= 1 {
log::trace!(
"unresolved import {}!{} (IAT rva 0x{:x})",
import_dll,
api_name,
rva
);
}
}
rva += 8;
}
}
pub fn iat_binding_original(
&mut self,
emu: &mut emu::Emu,
base_addr: u64,
original_first_thunk: u32,
first_thunk: u32,
import_dll: &str,
resolved_cache: &mut HashMap<String, u64>,
) {
let mut off_name = PE64::vaddr_to_off(&self.sect_hdr, original_first_thunk) as usize;
let mut off_addr = PE64::vaddr_to_off(&self.sect_hdr, first_thunk) as usize;
let mut rva = first_thunk;
loop {
if self.raw.len() <= off_name + 8 || self.raw.len() <= off_addr + 8 {
break;
}
let thunk_data = read_u64_le!(self.raw, off_name);
if thunk_data == 0 {
break;
}
let is_ordinal = (thunk_data & 0x80000000_00000000) != 0;
if is_ordinal {
let _ordinal = (thunk_data & 0xFFFF) as u16;
off_name += 8;
off_addr += 8;
rva += 8;
continue;
}
let func_name_addr = (thunk_data & 0x7fff_ffff_ffff_ffff) as u32;
let off2 = PE64::vaddr_to_off(&self.sect_hdr, func_name_addr) as usize;
if off2 == 0 {
off_name += 8;
off_addr += 8;
rva += 8;
continue;
}
let func_name = PE64::read_string(&self.raw, off2 + 2);
let cache_key =
format!("{}!{}", import_dll.to_lowercase(), func_name.to_lowercase());
let real_addr = if let Some(cached) = resolved_cache.get(&cache_key) {
*cached
} else {
let resolved =
winapi64::kernel32::resolve_api_name_in_module(emu, import_dll, &func_name);
resolved_cache.insert(cache_key, resolved);
resolved
};
if real_addr != 0 {
write_u64_le!(self.raw, off_addr, real_addr);
let patch_addr = base_addr + rva as u64;
if let Some(mem) = emu.maps.get_mem_by_addr_mut(patch_addr) {
mem.force_write_qword(patch_addr, real_addr);
}
} else if emu.cfg.verbose >= 1 {
log::trace!(
"unresolved import {}!{} (IAT rva 0x{:x})",
import_dll,
func_name,
rva
);
}
off_name += 8;
off_addr += 8;
rva += 8;
}
}
pub fn apply_relocations(&mut self, emu: &mut emu::Emu, base_addr: u64) {
if self.opt.data_directory.len() <= IMAGE_DIRECTORY_ENTRY_BASERELOC {
return;
}
let reloc_dir = &self.opt.data_directory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
let reloc_va = reloc_dir.virtual_address;
let reloc_sz = reloc_dir.size;
if reloc_va == 0 || reloc_sz == 0 {
return;
}
let delta = base_addr.wrapping_sub(self.opt.image_base);
if delta == 0 {
return;
}
let mut off = PE64::vaddr_to_off(&self.sect_hdr, reloc_va) as usize;
if off == 0 {
return;
}
let end_off = off + reloc_sz as usize;
if emu.cfg.verbose >= 1 {
log::info!("Applying base relocations...");
}
while off < end_off && off + 8 <= self.raw.len() {
let page_va = read_u32_le!(self.raw, off);
let block_sz = read_u32_le!(self.raw, off + 4);
if page_va == 0 && block_sz == 0 {
break;
}
if block_sz < 8 {
break; }
let entries_count = (block_sz - 8) / 2;
let mut entry_off = off + 8;
for _ in 0..entries_count {
if entry_off + 2 > self.raw.len() {
break;
}
let entry = read_u16_le!(self.raw, entry_off);
let reloc_type = entry >> 12;
let reloc_offset = entry & 0x0FFF;
if reloc_type == 10 {
let target_rva = page_va + reloc_offset as u32;
let target_off = PE64::vaddr_to_off(&self.sect_hdr, target_rva) as usize;
if target_off > 0 && target_off + 8 <= self.raw.len() {
let original_val = read_u64_le!(self.raw, target_off);
let new_val = original_val.wrapping_add(delta);
write_u64_le!(self.raw, target_off, new_val);
let patch_addr = base_addr + target_rva as u64;
if let Some(mem) = emu.maps.get_mem_by_addr_mut(patch_addr) {
mem.force_write_qword(patch_addr, new_val);
}
}
}
entry_off += 2;
}
off += block_sz as usize;
}
if emu.cfg.verbose >= 1 {
log::info!("Base Relocations applied successfully.");
}
}
pub fn import_addr_to_name(&self, paddr: u64) -> String {
if paddr == 0 {
return String::new();
}
for i in 0..self.image_import_descriptor.len() {
let iim = &self.image_import_descriptor[i];
if iim.name.is_empty() {
continue;
}
let thunk_names_rva = if iim.original_first_thunk != 0 {
iim.original_first_thunk
} else {
iim.first_thunk
};
let mut off_name = PE64::vaddr_to_off(&self.sect_hdr, thunk_names_rva) as usize;
let mut off_addr = PE64::vaddr_to_off(&self.sect_hdr, iim.first_thunk) as usize;
loop {
if self.raw.len() <= off_name + 8 || self.raw.len() <= off_addr + 8 {
break;
}
let thunk_data = read_u64_le!(self.raw, off_name);
if thunk_data == 0 {
break;
}
let addr = read_u64_le!(self.raw, off_addr);
let is_ordinal = (thunk_data & 0x80000000_00000000) != 0;
if !is_ordinal {
let func_name_addr = (thunk_data & 0x7fff_ffff_ffff_ffff) as u32;
let off2 = PE64::vaddr_to_off(&self.sect_hdr, func_name_addr) as usize;
if off2 != 0 && addr == paddr {
let func_name = PE64::read_string(&self.raw, off2 + 2);
return func_name;
}
}
off_name += 8;
off_addr += 8;
}
}
String::new()
}
pub fn import_addr_to_dll_and_name(&self, paddr: u64) -> String {
if paddr == 0 {
return String::new();
}
for iim in &self.image_import_descriptor {
if iim.name.is_empty() {
continue;
}
let thunk_names_rva = if iim.original_first_thunk != 0 {
iim.original_first_thunk
} else {
iim.first_thunk
};
let mut off_name = PE64::vaddr_to_off(&self.sect_hdr, thunk_names_rva) as usize;
let mut off_addr = PE64::vaddr_to_off(&self.sect_hdr, iim.first_thunk) as usize;
loop {
if self.raw.len() <= off_name + 8 || self.raw.len() <= off_addr + 8 {
break;
}
let thunk_data = read_u64_le!(self.raw, off_name);
if thunk_data == 0 {
break;
}
let addr = read_u64_le!(self.raw, off_addr);
let is_ordinal = (thunk_data & 0x80000000_00000000) != 0;
if !is_ordinal {
let func_name_addr = (thunk_data & 0x7fff_ffff_ffff_ffff) as u32;
let off2 = PE64::vaddr_to_off(&self.sect_hdr, func_name_addr) as usize;
if off2 != 0 && addr == paddr {
let func_name = PE64::read_string(&self.raw, off2 + 2);
return format!("{}!{}", iim.name, func_name);
}
}
off_name += 8;
off_addr += 8;
}
}
String::new()
}
pub fn locate_resource_data_entry(
&self,
rsrc: &[u8],
off: usize,
level: u32,
type_id: Option<u32>,
name_id: Option<u32>,
type_name: Option<&str>,
name: Option<&str>,
) -> Option<structures::ImageResourceDataEntry64> {
if level >= 10 {
log::warn!("Resource directory recursion limit reached");
return None;
}
if off + 16 > rsrc.len() {
log::warn!(
"Resource directory at offset {} is out of bounds (rsrc size: {})",
off,
rsrc.len()
);
return None;
}
let mut dir = structures::ImageResourceDirectory::new();
dir.characteristics = read_u32_le!(rsrc, off);
dir.time_date_stamp = read_u32_le!(rsrc, off + 4);
dir.major_version = read_u16_le!(rsrc, off + 8);
dir.minor_version = read_u16_le!(rsrc, off + 10);
dir.number_of_named_entries = read_u16_le!(rsrc, off + 12);
dir.number_of_id_entries = read_u16_le!(rsrc, off + 14);
let entries = dir.number_of_named_entries + dir.number_of_id_entries;
log::debug!(
"Resource directory level {}: {} named entries, {} ID entries",
level,
dir.number_of_named_entries,
dir.number_of_id_entries
);
for i in 0..entries {
let entry_off = off + (i as usize * 8) + 16;
if entry_off + 8 > rsrc.len() {
log::warn!(
"Resource directory entry {} at offset {} is out of bounds",
i,
entry_off
);
continue;
}
let mut entry = structures::ImageResourceDirectoryEntry::new();
entry.name_or_id = read_u32_le!(rsrc, entry_off);
entry.data_or_directory = read_u32_le!(rsrc, entry_off + 4);
log::debug!(
"Entry {}: name_or_id=0x{:x}, data_or_directory=0x{:x}, is_id={}, is_directory={}",
i,
entry.name_or_id,
entry.data_or_directory,
entry.is_id(),
entry.is_directory()
);
let matched: bool;
if entry.is_id() {
let entry_id = entry.get_name_or_id();
if level == 0 && type_id.is_some() && type_id.unwrap() == entry_id {
log::debug!("type_id {} matched at level {}", entry_id, level);
matched = true;
} else if level == 1 && name_id.is_some() && name_id.unwrap() == entry_id {
log::debug!("name_id {} matched at level {}", entry_id, level);
matched = true;
} else if level == 2 {
log::debug!("language_id {} at level {}", entry_id, level);
matched = true;
} else {
matched = false;
}
} else {
let name_offset = entry.get_name_or_id() & 0x7FFFFFFF;
let rsrc_section = self.get_section_ptr_by_name(".rsrc");
if rsrc_section.is_none() {
log::warn!("No .rsrc section found");
continue;
}
if name_offset as usize >= rsrc.len() {
log::warn!(
"Resource name offset 0x{:x} is out of bounds (rsrc size: {})",
name_offset,
rsrc.len()
);
continue;
}
let resource_name = self.read_resource_name_from_rsrc(rsrc, name_offset as usize);
if level == 0 && type_name.is_some() && type_name.unwrap() == resource_name {
log::debug!("type_name '{}' matched at level {}", resource_name, level);
matched = true;
} else if level == 1 && name.is_some() && name.unwrap() == resource_name {
log::debug!("name '{}' matched at level {}", resource_name, level);
matched = true;
} else {
matched = false;
}
}
if matched {
if entry.is_directory() {
let next_dir_offset = entry.get_offset() & 0x7FFFFFFF; log::debug!("Following directory at offset 0x{:x}", next_dir_offset);
return self.locate_resource_data_entry(
rsrc,
next_dir_offset as usize,
level + 1,
type_id,
name_id,
type_name,
name,
);
} else {
let data_entry_offset = entry.get_offset();
if data_entry_offset as usize + 16 > rsrc.len() {
log::warn!(
"Resource data entry at offset 0x{:x} is out of bounds",
data_entry_offset
);
return None;
}
log::debug!(
"Found resource data entry at offset 0x{:x}",
data_entry_offset
);
let mut data_entry = structures::ImageResourceDataEntry64::new();
data_entry.offset_to_data =
read_u32_le!(rsrc, data_entry_offset as usize) as u64;
data_entry.size = read_u32_le!(rsrc, data_entry_offset as usize + 4) as u64;
data_entry.code_page =
read_u32_le!(rsrc, data_entry_offset as usize + 8) as u64;
data_entry.reserved =
read_u32_le!(rsrc, data_entry_offset as usize + 12) as u64;
return Some(data_entry);
}
}
}
None
}
pub fn read_resource_name_from_rsrc(&self, rsrc: &[u8], offset: usize) -> String {
if offset + 1 >= rsrc.len() {
log::warn!(
"Cannot read resource name length at offset {}: out of bounds",
offset
);
return String::new();
}
let length = u16::from_le_bytes([rsrc[offset], rsrc[offset + 1]]) as usize;
let string_start = offset + 2;
let required_bytes = string_start + (length * 2);
if required_bytes > rsrc.len() {
log::warn!(
"Cannot read resource name: need {} bytes but only {} available in rsrc section",
required_bytes,
rsrc.len()
);
return String::new();
}
let utf16_data: Vec<u16> = (0..length)
.map(|i| {
let idx = string_start + i * 2;
u16::from_le_bytes([rsrc[idx], rsrc[idx + 1]])
})
.collect();
String::from_utf16_lossy(&utf16_data)
}
pub fn get_resource(
&self,
type_id: Option<u32>,
name_id: Option<u32>,
type_name: Option<&str>,
name: Option<&str>,
) -> Option<(u64, usize)> {
let rsrc = self.get_section_ptr_by_name(".rsrc");
if rsrc.is_none() {
return None;
}
let rsrc = rsrc.unwrap();
let data_entry =
self.locate_resource_data_entry(rsrc, 0, 0, type_id, name_id, type_name, name);
if data_entry.is_none() {
return None;
}
let data_entry = data_entry.unwrap();
let data_off = PE64::vaddr_to_off(&self.sect_hdr, data_entry.offset_to_data as u32)
as usize
- self.opt.image_base as usize;
return Some((data_off as u64, data_entry.size as usize));
}
pub fn get_resource_name(&self, entry: &structures::ImageResourceDirectoryEntry) -> String {
let rsrc = self.get_section_ptr_by_name(".rsrc");
if rsrc.is_none() {
return String::new();
}
let rsrc = rsrc.unwrap();
let name_offset = (entry.get_name_or_id() & 0x7FFFFFFF) as usize;
self.read_resource_name_from_rsrc(rsrc, name_offset)
}
}