use std::fs::File;
use std::io::Read;
use std::str;
use crate::emu;
use crate::structures;
use crate::winapi::winapi32;
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! write_u32_le {
($raw:expr, $off:expr, $val:expr) => {
$raw[$off + 0] = ($val & 0x000000ff) as u8;
$raw[$off + 1] = (($val & 0x0000ff00) >> 8) as u8;
$raw[$off + 2] = (($val & 0x00ff0000) >> 16) as u8;
$raw[$off + 3] = (($val & 0xff000000) >> 24) as u8;
};
}
pub const IMAGE_DOS_SIGNATURE: u16 = 0x5A4D;
pub const IMAGE_OS2_SIGNATURE: u16 = 0x544E;
pub const IMAGE_OS2_SIGNATURE_LE: u16 = 0x45AC;
pub const IMAGE_NT_SIGNATURE: u32 = 0x00004550;
pub const IMAGE_SIZEOF_FILE_HEADER: u8 = 20;
pub const IMAGE_NUMBEROF_DIRECTORY_ENTRIES: usize = 16;
pub const SECTION_HEADER_SZ: usize = 40;
pub const IMAGE_DIRECTORY_ENTRY_EXPORT: usize = 0;
pub const IMAGE_DIRECTORY_ENTRY_IMPORT: usize = 1;
pub const IMAGE_DIRECTORY_ENTRY_RESOURCE: usize = 2;
pub const IMAGE_DIRECTORY_ENTRY_EXCEPTION: usize = 3;
pub const IMAGE_DIRECTORY_ENTRY_SECURITY: usize = 4;
pub const IMAGE_DIRECTORY_ENTRY_BASERELOC: usize = 5;
pub const IMAGE_DIRECTORY_ENTRY_DEBUG: usize = 6;
pub const IMAGE_DIRECTORY_ENTRY_COPYRIGHT: usize = 7;
pub const IMAGE_DIRECTORY_ENTRY_GLOBALPTR: usize = 8;
pub const IMAGE_DIRECTORY_ENTRY_TLS: usize = 9;
pub const IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG: usize = 10;
pub const IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT: usize = 11;
pub const IMAGE_DIRECTORY_ENTRY_IAT: usize = 12;
pub const IMAGE_DIRECTORY_ENTRY_DELAY_LOAD: usize = 13;
pub const IMAGE_DIRECTORY_ENTRY_DOTNET_HDR: usize = 14;
pub const IMAGE_SIZEOF_SHORT_NAME: usize = 8;
pub const IMAGE_DEBUG_TYPE_UNKNOWN: u8 = 0;
pub const IMAGE_DEBUG_TYPE_COFF: u8 = 1;
pub const IMAGE_DEBUG_TYPE_CODEVIEW: u8 = 2;
pub const IMAGE_DEBUG_TYPE_FPO: u8 = 3;
pub const IMAGE_DEBUG_TYPE_MISC: u8 = 4;
#[derive(Debug)]
pub struct ImageDosHeader {
pub e_magic: u16, pub e_cblp: u16, pub e_cp: u16, pub e_crlc: u16, pub e_cparhdr: u16, pub e_minalloc: u16, pub e_maxalloc: u16, pub e_ss: u16, pub e_sp: u16, pub e_csum: u16, pub e_ip: u16, pub e_cs: u16, pub e_lfarlc: u16, pub e_ovno: u16, pub e_res: [u16; 4], pub e_oemid: u16, pub e_oeminfo: u16, pub e_res2: [u16; 10], pub e_lfanew: u32, }
impl ImageDosHeader {
pub fn size() -> usize {
64
}
pub fn load(raw: &[u8], off: usize) -> ImageDosHeader {
ImageDosHeader {
e_magic: read_u16_le!(raw, off),
e_cblp: read_u16_le!(raw, off + 2),
e_cp: read_u16_le!(raw, off + 4),
e_crlc: read_u16_le!(raw, off + 6),
e_cparhdr: read_u16_le!(raw, off + 8),
e_minalloc: read_u16_le!(raw, off + 10),
e_maxalloc: read_u16_le!(raw, off + 12),
e_ss: read_u16_le!(raw, off + 14),
e_sp: read_u16_le!(raw, off + 16),
e_csum: read_u16_le!(raw, off + 18),
e_ip: read_u16_le!(raw, off + 20),
e_cs: read_u16_le!(raw, off + 22),
e_lfarlc: read_u16_le!(raw, off + 24),
e_ovno: read_u16_le!(raw, off + 26),
e_res: [
read_u16_le!(raw, off + 28),
read_u16_le!(raw, off + 30),
read_u16_le!(raw, off + 32),
read_u16_le!(raw, off + 34),
],
e_oemid: read_u16_le!(raw, off + 36),
e_oeminfo: read_u16_le!(raw, off + 38),
e_res2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
e_lfanew: read_u32_le!(raw, off + 60),
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
}
#[derive(Debug)]
pub struct ImageNtHeaders {
pub signature: u32,
}
impl ImageNtHeaders {
pub fn load(raw: &[u8], off: usize) -> ImageNtHeaders {
ImageNtHeaders {
signature: read_u32_le!(raw, off),
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
}
#[derive(Debug)]
pub struct ImageFileHeader {
pub machine: u16,
pub number_of_sections: u16,
pub time_date_stamp: u32,
pub pointer_to_symbol_table: u32,
pub number_of_symbols: u32,
pub size_of_optional_header: u16,
pub characteristics: u16,
}
impl ImageFileHeader {
pub fn load(raw: &[u8], off: usize) -> ImageFileHeader {
ImageFileHeader {
machine: read_u16_le!(raw, off),
number_of_sections: read_u16_le!(raw, off + 2),
time_date_stamp: read_u32_le!(raw, off + 4),
pointer_to_symbol_table: read_u32_le!(raw, off + 8),
number_of_symbols: read_u32_le!(raw, off + 12),
size_of_optional_header: read_u16_le!(raw, off + 16),
characteristics: read_u16_le!(raw, off + 18),
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
}
#[derive(Debug)]
pub struct ImageDataDirectory {
pub virtual_address: u32,
pub size: u32,
}
impl ImageDataDirectory {
pub fn load(raw: &[u8], off: usize) -> ImageDataDirectory {
ImageDataDirectory {
virtual_address: read_u32_le!(raw, off),
size: read_u32_le!(raw, off + 4),
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
}
#[derive(Debug)]
pub struct ImageOptionalHeader {
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 base_of_data: u32,
pub image_base: u32,
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 reserved1: 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: u32,
pub size_of_stack_commit: u32,
pub size_of_heap_reserve: u32,
pub size_of_heap_commit: u32,
pub loader_flags: u32,
pub number_of_rva_and_sizes: u32,
pub data_directory: Vec<ImageDataDirectory>, }
impl ImageOptionalHeader {
pub fn load(raw: &Vec<u8>, off: usize) -> ImageOptionalHeader {
let mut dd: Vec<ImageDataDirectory> = Vec::new();
let mut pos = 96;
for i in 0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES {
dd.push(ImageDataDirectory::load(raw, off + pos));
pos += 8;
}
ImageOptionalHeader {
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),
base_of_data: read_u32_le!(raw, off + 24),
image_base: read_u32_le!(raw, off + 28),
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),
reserved1: 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_u32_le!(raw, off + 72),
size_of_stack_commit: read_u32_le!(raw, off + 76),
size_of_heap_reserve: read_u32_le!(raw, off + 80),
size_of_heap_commit: read_u32_le!(raw, off + 84),
loader_flags: read_u32_le!(raw, off + 88),
number_of_rva_and_sizes: read_u32_le!(raw, off + 92),
data_directory: dd,
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
}
#[derive(Debug)]
pub struct ImageSectionHeader {
pub name: [u8; IMAGE_SIZEOF_SHORT_NAME],
pub virtual_size: u32,
pub virtual_address: u32,
pub size_of_raw_data: u32,
pub pointer_to_raw_data: u32,
pub pointer_to_relocations: u32,
pub pointer_to_linenumbers: u32,
pub number_of_relocations: u16,
pub number_of_linenumbers: u16,
pub characteristics: u32,
}
impl ImageSectionHeader {
pub fn load(raw: &[u8], off: usize) -> ImageSectionHeader {
let mut name: [u8; IMAGE_SIZEOF_SHORT_NAME] = [0; IMAGE_SIZEOF_SHORT_NAME];
name[..(off + IMAGE_SIZEOF_SHORT_NAME - off)]
.copy_from_slice(&raw[off..(off + IMAGE_SIZEOF_SHORT_NAME)]);
let off2 = off + IMAGE_SIZEOF_SHORT_NAME;
ImageSectionHeader {
name,
virtual_size: read_u32_le!(raw, off2),
virtual_address: read_u32_le!(raw, off2 + 4),
size_of_raw_data: read_u32_le!(raw, off2 + 8),
pointer_to_raw_data: read_u32_le!(raw, off2 + 12),
pointer_to_relocations: read_u32_le!(raw, off2 + 16),
pointer_to_linenumbers: read_u32_le!(raw, off2 + 20),
number_of_relocations: read_u16_le!(raw, off2 + 24),
number_of_linenumbers: read_u16_le!(raw, off2 + 26),
characteristics: read_u32_le!(raw, off2 + 28),
}
}
pub fn get_name(&self) -> String {
let s = str::from_utf8(&self.name).unwrap_or("err");
s.to_string().replace("\x00", "")
}
pub fn set_name(&mut self, newname: &str) {
if newname.len() + 1 > IMAGE_SIZEOF_SHORT_NAME {
panic!("fixing a name bigger than IMAGE_SIZEOF_SHORT_NAME");
}
let mut vname: Vec<u8> = newname.as_bytes().to_vec();
vname.push(0);
for (i, &item) in vname.iter().enumerate() {
self.name[i] = item;
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
}
#[derive(Debug)]
pub struct ImageResourceDirectoryEntry {
pub name: u32,
pub offset_to_data: u32,
}
impl ImageResourceDirectoryEntry {
pub fn load(raw: &[u8], off: usize) -> ImageResourceDirectoryEntry {
ImageResourceDirectoryEntry {
name: read_u32_le!(raw, off),
offset_to_data: read_u32_le!(raw, off + 4),
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
}
#[derive(Debug)]
pub struct ImageResourceDirectory {
pub characteristics: u32,
pub time_date_stamp: u32,
pub major_version: u16,
pub minor_version: u16,
pub number_of_named_entries: u16,
pub number_of_id_entries: u16,
}
impl ImageResourceDirectory {
pub fn load(raw: &[u8], off: usize) -> ImageResourceDirectory {
ImageResourceDirectory {
characteristics: read_u32_le!(raw, off),
time_date_stamp: read_u32_le!(raw, off + 4),
major_version: read_u16_le!(raw, off + 8),
minor_version: read_u16_le!(raw, off + 10),
number_of_named_entries: read_u16_le!(raw, off + 12),
number_of_id_entries: read_u16_le!(raw, off + 14),
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
}
#[derive(Debug)]
pub struct ImageExportDirectory {
pub characteristics: u32,
pub time_date_stamp: u32,
pub major_version: u16,
pub minor_version: u16,
pub name: u32,
pub base: u32,
pub number_of_functions: u32,
pub number_of_names: u32,
pub address_of_functions: u32,
pub address_of_names: u32,
pub address_of_name_ordinals: u32,
}
impl ImageExportDirectory {
pub fn load(raw: &[u8], off: usize) -> ImageExportDirectory {
ImageExportDirectory {
characteristics: read_u32_le!(raw, off),
time_date_stamp: read_u32_le!(raw, off + 4),
major_version: read_u16_le!(raw, off + 8),
minor_version: read_u16_le!(raw, off + 10),
name: read_u32_le!(raw, off + 12),
base: read_u32_le!(raw, off + 16),
number_of_functions: read_u32_le!(raw, off + 20),
number_of_names: read_u32_le!(raw, off + 24),
address_of_functions: read_u32_le!(raw, off + 28),
address_of_names: read_u32_le!(raw, off + 22),
address_of_name_ordinals: read_u32_le!(raw, off + 26),
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
}
#[derive(Debug)]
pub struct TlsDirectory32 {
pub tls_data_start: u32,
pub tls_data_end: u32,
pub tls_index: u32, pub tls_callbacks: u32,
pub zero_fill_size: u32, pub characteristic: u32,
}
impl TlsDirectory32 {
pub fn load(raw: &[u8], off: usize) -> TlsDirectory32 {
TlsDirectory32 {
tls_data_start: read_u32_le!(raw, off),
tls_data_end: read_u32_le!(raw, off + 4),
tls_index: read_u32_le!(raw, off + 8),
tls_callbacks: read_u32_le!(raw, off + 12),
zero_fill_size: read_u32_le!(raw, off + 16),
characteristic: read_u32_le!(raw, off + 20),
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
pub fn size() -> usize {
24
}
}
#[derive(Debug)]
pub struct DelayLoadDirectory {
pub attributes: u32,
pub name_ptr: u32,
pub handle: u32,
pub address_table: u32,
pub name_table: u32,
pub bound_delay_import_table: u32,
pub unload_delay_import_table: u32,
pub tstamp: u32,
pub name: String,
}
impl DelayLoadDirectory {
pub fn size() -> usize {
32
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
pub fn load(raw: &[u8], off: usize) -> DelayLoadDirectory {
DelayLoadDirectory {
attributes: read_u32_le!(raw, off),
name_ptr: read_u32_le!(raw, off + 4),
handle: read_u32_le!(raw, off + 8),
address_table: read_u32_le!(raw, off + 12),
name_table: read_u32_le!(raw, off + 16),
bound_delay_import_table: read_u32_le!(raw, off + 20),
unload_delay_import_table: read_u32_le!(raw, off + 24),
tstamp: read_u32_le!(raw, off + 28),
name: String::new(),
}
}
}
#[derive(Debug)]
pub struct DelayLoadIAT {
name_ptr: u32,
iat_addr: u32,
bound_iat: u32,
}
impl DelayLoadIAT {
fn load(raw: &[u8], off: usize) -> DelayLoadIAT {
DelayLoadIAT {
name_ptr: read_u32_le!(raw, off),
iat_addr: read_u32_le!(raw, off + 4),
bound_iat: read_u32_le!(raw, off + 8),
}
}
}
#[derive(Debug)]
pub struct ImageImportDirectory {
pub address_of_import_lookup_table: u32,
pub time_date_stamp: u32,
pub forwarder_chain: u32,
pub address_of_names: u32,
pub address_of_import_table: u32,
}
impl ImageImportDirectory {
pub fn load(raw: &[u8], off: usize) -> ImageImportDirectory {
ImageImportDirectory {
address_of_import_lookup_table: read_u32_le!(raw, off),
time_date_stamp: read_u32_le!(raw, off + 4),
forwarder_chain: read_u32_le!(raw, off + 8),
address_of_names: read_u32_le!(raw, off + 12),
address_of_import_table: read_u32_le!(raw, off + 16),
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
}
#[derive(Debug)]
pub struct ImageImportDescriptor {
pub original_first_thunk: u32,
pub time_date_stamp: u32,
pub forwarder_chain: u32,
pub name_ptr: u32,
pub first_thunk: u32,
pub name: String,
}
impl ImageImportDescriptor {
pub fn load(raw: &[u8], off: usize) -> ImageImportDescriptor {
ImageImportDescriptor {
original_first_thunk: read_u32_le!(raw, off),
time_date_stamp: read_u32_le!(raw, off + 4),
forwarder_chain: read_u32_le!(raw, off + 8),
name_ptr: read_u32_le!(raw, off + 12),
first_thunk: read_u32_le!(raw, off + 16),
name: String::new(),
}
}
pub fn size() -> usize {
20
}
}
#[derive(Debug)]
pub struct ImportLookupTable {
pub bits: Vec<u32>,
}
#[derive(Debug)]
pub struct HintNameItem {
pub is_ordinal: bool,
pub func_name_addr: u32,
}
impl HintNameItem {
pub fn load(raw: &[u8], off: usize) -> HintNameItem {
let func_name_addr: u32;
if raw.len() <= off + 4 {
HintNameItem {
is_ordinal: false,
func_name_addr: 0,
}
} else {
HintNameItem {
is_ordinal: raw[off] & 0b10000000 == 0b10000000,
func_name_addr: read_u32_le!(raw, off), }
}
}
pub fn size() -> usize {
4
}
}
#[derive(Debug)]
pub struct ImportAddressTable {}
impl ImportLookupTable {
pub fn load(raw: &[u8], off: usize, nitems: usize) -> ImportLookupTable {
let bits: Vec<u32> = Vec::new();
ImportLookupTable { bits }
}
}
#[derive(Debug)]
pub struct TagImportDirectory {
pub dw_rva_function_name_list: u32,
pub dw_useless1: u32,
pub dw_useless2: u32,
pub dw_rva_module_name: u32,
pub dw_rva_function_address_list: u32,
}
impl TagImportDirectory {
pub fn load(raw: &[u8], off: usize) -> TagImportDirectory {
TagImportDirectory {
dw_rva_function_name_list: read_u32_le!(raw, off),
dw_useless1: read_u32_le!(raw, off + 4),
dw_useless2: read_u32_le!(raw, off + 8),
dw_rva_module_name: read_u32_le!(raw, off + 12),
dw_rva_function_address_list: read_u32_le!(raw, off + 16),
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
}
#[derive(Debug)]
pub struct ImageDebugDirectory {
pub characteristics: u32,
pub time_date_stamp: u32,
pub major_version: u16,
pub minor_version: u16,
pub types: u32,
pub size_of_data: u32,
pub address_of_raw_data: u32,
pub pointer_to_raw_data: u32,
}
impl ImageDebugDirectory {
pub fn load(raw: &[u8], off: usize) -> ImageDebugDirectory {
ImageDebugDirectory {
characteristics: read_u32_le!(raw, off),
time_date_stamp: read_u32_le!(raw, off + 4),
major_version: read_u16_le!(raw, off + 8),
minor_version: read_u16_le!(raw, off + 10),
types: read_u32_le!(raw, off + 12),
size_of_data: read_u32_le!(raw, off + 16),
address_of_raw_data: read_u32_le!(raw, off + 20),
pointer_to_raw_data: read_u32_le!(raw, off + 24),
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
}
#[derive(Debug)]
pub struct ImageBaseRelocation {
pub virtual_address: u32,
pub size_of_block: u32,
}
impl ImageBaseRelocation {
pub fn load(raw: &[u8], off: usize) -> ImageBaseRelocation {
ImageBaseRelocation {
virtual_address: read_u32_le!(raw, off),
size_of_block: read_u32_le!(raw, off + 4),
}
}
pub fn print(&self) {
log::trace!("{:#x?}", self);
}
}
pub struct Section {
pub name: String,
pub off: usize,
pub sz: usize,
}
impl Section {
pub fn new(off: usize, sz: usize) -> Section {
Section {
name: String::new(),
off,
sz,
}
}
}
pub struct PE32 {
pub filename: String,
pub raw: Vec<u8>,
pub dos: ImageDosHeader,
pub nt: ImageNtHeaders,
pub fh: ImageFileHeader,
pub opt: ImageOptionalHeader,
pub sect_hdr: Vec<ImageSectionHeader>,
pub delay_load_dir: Vec<DelayLoadDirectory>,
pub image_import_descriptor: Vec<ImageImportDescriptor>,
}
impl PE32 {
pub fn is_pe32(filename: &str) -> bool {
let mut fd = match File::open(filename) {
Ok(file) => file,
Err(_) => return false,
};
let file_size = match fd.metadata() {
Ok(metadata) => metadata.len(),
Err(_) => return false,
};
if file_size < ImageDosHeader::size() as u64 {
return false;
}
let mut raw = vec![0u8; ImageDosHeader::size()];
if fd.read_exact(&mut raw).is_err() {
return false;
}
let dos = ImageDosHeader::load(&raw, 0);
if dos.e_magic != 0x5a4d {
return false;
}
if dos.e_lfanew >= file_size as u32 {
return false;
}
true
}
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 read_string_200(raw: &[u8], off: usize) -> String {
let mut last = 0;
for (i, byte) in raw.iter().enumerate().skip(off).take(200) {
if *byte == 0 {
last = i;
break;
}
}
if last == 0 {
return String::new();
}
let s = str::from_utf8(raw.get(off..last).unwrap()).unwrap_or("noname");
s.to_string()
}
pub fn load_from_raw(filename: &str, raw: &[u8]) -> PE32 {
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 = ImageOptionalHeader::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 = PE32::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 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 = PE32::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 = PE32::vaddr_to_off(§, iid.name_ptr) as usize;
if off > raw.len() {
panic!("the name of pe32 iid is out of buffer");
}
let libname = PE32::read_string(&raw, off);
if libname.is_empty() {
import_off += ImageImportDescriptor::size();
continue;
}
iid.name = libname.to_string();
image_import_descriptor.push(iid);
import_off += ImageImportDescriptor::size();
}
} else {
}
} else {
}
PE32 {
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) -> PE32 {
let mut fd = File::open(filename).expect("pe32 binary not found");
let mut raw: Vec<u8> = Vec::new();
fd.read_to_end(&mut raw)
.expect("couldnt read the pe32 binary");
PE32::load_from_raw(filename, &raw)
}
pub fn size(&self) -> usize {
self.raw.len()
}
pub fn get_filename(&self) -> String {
self.filename.clone()
}
pub fn mem_size(&self) -> usize {
let mut max_va: u32 = 0;
let mut max_va_sz: usize = 0;
for i in 0..self.sect_hdr.len() {
let sect = &self.sect_hdr[i];
if sect.virtual_address > max_va {
max_va = sect.virtual_address;
if sect.size_of_raw_data > sect.virtual_size {
max_va_sz = sect.size_of_raw_data as usize;
} else {
max_va_sz = sect.virtual_size as usize;
}
}
}
(max_va as usize) + max_va_sz
}
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 {
return vaddr - sect.virtual_address + sect.pointer_to_raw_data;
}
}
0
}
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;
println!("name: {} off: {:x} sz: {:x}", name, off, sz);
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_section_ptr(&self, id: usize) -> &[u8] {
let off = self.sect_hdr[id].pointer_to_raw_data as usize;
let mut sz = self.sect_hdr[id].size_of_raw_data as usize; if off + sz >= self.raw.len() {
sz = self.raw.len() - off - 1;
}
if sz == 0 || off > self.raw.len() || off + sz > self.raw.len() {
return &[];
}
let section_ptr = &self.raw[off..off + sz];
section_ptr
}
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 = PE32::vaddr_to_off(&self.sect_hdr, entry_tls) as usize;
log::trace!("raw {:x} off {:x}", self.raw.len(), tls_off);
let tls = TlsDirectory32::load(&self.raw, tls_off);
tls.print();
let mut cb_off;
if tls.tls_callbacks < self.opt.image_base - 0xf000 + 0xa400 {
panic!("error loading tls callbacks");
}
cb_off = (tls.tls_callbacks - self.opt.image_base - 0xf000 + 0xa400) as usize;
log::trace!("cb_off {:x}", cb_off);
log::trace!("cb_off {:x} {:x}", cb_off, self.opt.image_base);
loop {
let callback: u64 = read_u32_le!(&self.raw, cb_off) as u64;
if callback == 0 {
break;
}
log::trace!("TLS Callback: 0x{:x}", callback);
callbacks.push(callback);
cb_off += 4;
}
callbacks
}
pub fn delay_load_binding(&mut self, emu: &mut emu::Emu, base_addr: u32) {
log::trace!("Delay load binding started for {} ...", self.filename);
for i in 0..self.delay_load_dir.len() {
let dld = &self.delay_load_dir[i];
if dld.name.is_empty() {
continue;
}
if winapi32::kernel32::load_library(emu, &dld.name) == 0 {
panic!(
"cannot found the library `{}` on {}",
&dld.name, emu.cfg.maps_folder
);
}
let mut off_name = PE32::vaddr_to_off(&self.sect_hdr, dld.name_table) as usize;
let mut off_addr =
PE32::vaddr_to_off(&self.sect_hdr, dld.bound_delay_import_table) as usize;
let mut rva = dld.bound_delay_import_table;
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 = PE32::vaddr_to_off(&self.sect_hdr, hint.func_name_addr) as usize;
if off2 == 0 {
off_name += HintNameItem::size();
off_addr += 4;
rva += 4;
continue;
}
let func_name = PE32::read_string(&self.raw, off2 + 2);
let real_addr = winapi32::kernel32::resolve_api_name(emu, &func_name);
if real_addr == 0 {
break;
}
write_u32_le!(self.raw, off_addr, real_addr as u32);
let patch_addr = base_addr as u64 + rva as u64;
if let Some(mem) = emu.maps.get_mem_by_addr_mut(patch_addr) {
mem.force_write_dword(patch_addr, real_addr as u32);
}
off_name += HintNameItem::size();
off_addr += 4;
rva += 4;
}
}
log::trace!("delay load bound!");
}
pub fn iat_binding(&mut self, emu: &mut emu::Emu, base_addr: u32) {
let dbg = false;
log::trace!(
"IAT binding started image_import_descriptor.len() = {} ...",
self.image_import_descriptor.len()
);
println!("-----> {}", self.image_import_descriptor.len());
for i in 0..self.image_import_descriptor.len() {
let iim_name = self.image_import_descriptor[i].name.clone();
let original_first_thunk = self.image_import_descriptor[i].original_first_thunk;
let first_thunk = self.image_import_descriptor[i].first_thunk;
if iim_name.is_empty() {
continue;
}
if winapi32::kernel32::load_library(emu, &iim_name) == 0 {
log::trace!("cannot found the library `{}` on maps32/", &iim_name);
continue;
} else if dbg {
log::trace!("library `{}` loaded", &iim_name);
}
let mut off_name =
PE32::vaddr_to_off(&self.sect_hdr, original_first_thunk) as usize;
let mut off_addr = PE32::vaddr_to_off(&self.sect_hdr, first_thunk) as usize;
let mut rva = first_thunk;
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 = PE32::vaddr_to_off(&self.sect_hdr, hint.func_name_addr) as usize;
if off2 == 0 {
off_name += HintNameItem::size();
off_addr += 4;
rva += 4;
continue;
}
let func_name = PE32::read_string(&self.raw, off2 + 2);
let real_addr =
winapi32::kernel32::resolve_api_name_in_module(emu, &iim_name, &func_name);
if dbg {
let real_addr1 = winapi32::kernel32::resolve_api_name(emu, &func_name);
if real_addr1 != real_addr {
log::trace!("--------------------------");
let (va, modm, func) = winapi32::kernel32::search_api_name(emu, &func_name);
log::trace!(
"inport: {}!{} ldr: {}!{}",
&iim_name,
&func_name,
modm,
func
);
log::trace!(
"0x{:x} {}!{} 0x{:x}-> 0x{:x}",
addr,
iim_name,
func_name,
off_addr,
real_addr
);
log::trace!(
"*********** prev:0x{:x} == new:0x{:x}",
real_addr1,
real_addr
);
println!("0x{:x} {} {}", va, modm, func);
log::trace!("--------------------------");
}
}
if real_addr == 0 {
break;
}
if dbg {
let old_addr = read_u32_le!(self.raw, off_addr);
}
write_u32_le!(self.raw, off_addr, real_addr as u32);
let patch_addr = base_addr as u64 + rva as u64;
if emu.maps.is_mapped(patch_addr) {
emu.maps.write_dword(patch_addr, real_addr as u32);
}
off_name += HintNameItem::size();
off_addr += 4;
rva += 4;
}
}
log::trace!("{} IAT Bound.", &self.filename);
}
pub fn import_addr_to_name(&self, paddr: u32) -> String {
let dbg = false;
if paddr == 0 {
return String::new();
}
for i in 0..self.image_import_descriptor.len() {
let iim = &self.image_import_descriptor[i];
if dbg {
log::trace!("import: {}", iim.name);
}
if iim.name.is_empty() {
continue;
}
let mut off_name =
PE32::vaddr_to_off(&self.sect_hdr, iim.original_first_thunk) as usize;
let mut off_addr = PE32::vaddr_to_off(&self.sect_hdr, iim.first_thunk) 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 = PE32::vaddr_to_off(&self.sect_hdr, hint.func_name_addr) as usize;
if off2 == 0 {
off_name += HintNameItem::size();
off_addr += 4;
continue;
}
if addr == paddr {
let func_name = PE32::read_string(&self.raw, off2 + 2);
return func_name;
}
off_name += HintNameItem::size();
off_addr += 4;
}
}
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::ImageResourceDataEntry32> {
if level >= 10 {
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;
for i in 0..entries {
let mut entry = structures::ImageResourceDirectoryEntry::new();
let off2 = off + i as usize * 8 + structures::ImageResourceDirectory::size() as usize;
entry.name_or_id = read_u32_le!(rsrc, off2);
entry.data_or_directory = read_u32_le!(rsrc, off2 + 4);
let matched: bool;
if entry.is_id() {
if level == 0 && type_id.is_some() && type_id.unwrap() == entry.get_name_or_id() {
println!("type_id matched");
matched = true;
} else if level == 1
&& name_id.is_some()
&& name_id.unwrap() == entry.get_name_or_id()
{
println!("name_id matched");
matched = true;
} else if level > 1 {
matched = true;
} else {
matched = false;
}
} else if level == 0
&& type_name.is_some()
&& type_name.unwrap() == self.get_resource_name(&entry)
{
println!("type_name matched");
matched = true;
} else if level == 1
&& name.is_some()
&& name.unwrap() == self.get_resource_name(&entry)
{
println!("name matched");
matched = true;
} else {
if level > 1 {
matched = true;
} else {
matched = false;
}
}
if matched {
if entry.is_directory() {
return self.locate_resource_data_entry(
rsrc,
off2,
level + 1,
type_id,
name_id,
type_name,
name,
);
} else {
let mut data_entry = structures::ImageResourceDataEntry32::new();
let off = PE32::vaddr_to_off(&self.sect_hdr, entry.get_offset()) as usize;
data_entry.offset_to_data = read_u32_le!(self.raw, off);
data_entry.size = read_u32_le!(self.raw, off + 4);
data_entry.code_page = read_u32_le!(self.raw, off + 8);
data_entry.reserved = read_u32_le!(self.raw, off + 12);
return Some(data_entry);
}
}
}
None
}
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 = PE32::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 off = PE32::vaddr_to_off(&self.sect_hdr, entry.get_name_or_id() as u32) as usize;
let length = u16::from_le_bytes([self.raw[off], self.raw[off + 1]]) as usize;
let string_start = off + 2;
let utf16_data: Vec<u16> = (0..length)
.map(|i| {
let idx = string_start + i * 2;
u16::from_le_bytes([self.raw[idx], self.raw[idx + 1]])
})
.collect();
String::from_utf16_lossy(&utf16_data)
}
}