Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/emulator/ps2/pcsx2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ impl State {
self.addr_base = if let Some(debug_symbol) =
// Recent PCSX2 releases include a debug symbol that can be used to easily retrieve the address of the emulated RAM
// Info: https://github.com/PCSX2/pcsx2/blob/0b6dccae5184b12e2dfc515a424d001cd38acb7c/pcsx2/Memory.cpp#L79
pe::symbols(game, main_module_range.0).find(|symbol| {
symbol
.get_name::<6>(game)
.is_ok_and(|name| name.matches(b"EEmem"))
}) {
pe::symbols(game, main_module_range.0)
.find(|symbol| {
symbol
.get_name::<6>(game)
.is_ok_and(|name| name.matches(b"EEmem"))
}) {
debug_symbol.address
} else {
// For older versions we rely on regular sigscanning.
Expand Down
2 changes: 1 addition & 1 deletion src/emulator/ps2/retroarch.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{file_format::pe, signature::Signature, Address, PointerSize, Process};

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
pub struct State {
core_base: Address,
}
Expand Down
153 changes: 77 additions & 76 deletions src/file_format/pe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ fn read_coff_header(process: &Process, module_address: Address) -> Option<(COFFH
}

/// A symbol exported into the current module.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Symbol {
/// The address associated with the current symbol
pub address: Address,
Expand Down Expand Up @@ -375,98 +376,98 @@ pub struct FileVersion {
pub build_part: u16,
}

/// Reads the numeric file version (major.minor.build.private) from the VERSIONINFO
/// resource of the PE module starting at the specified memory address.
/// Returns `None` if the module has no version resource or parsing fails.
pub fn file_version_info(
process: &Process,
module_address: impl Into<Address>,
) -> Option<FileVersion> {
#[repr(C)]
#[derive(Debug, Copy, Clone, Zeroable, Pod, Default)]
struct VsFixedFileInfo {
signature: u32, // must be 0xFEEF04BD
struct_version: u32,
file_version: FileVersion,
}

fn read_dir_header(process: &Process, addr: Address) -> Option<u16> {
// [6] = NumberOfNamedEntries, [7] = NumberOfIdEntries
process.read::<[u16; 8]>(addr)
.map(|val| val[6] + val[7])
.ok()
}

#[repr(C)]
#[derive(Pod, Zeroable, Copy, Clone, Debug, Default)]
struct DataEntry {
id: u32,
offset: u32,
}
impl FileVersion {
/// Reads the numeric file version (major.minor.build.private) from the VERSIONINFO
/// resource of the PE module starting at the specified memory address.
/// Returns `None` if the module has no version resource or parsing fails.
pub fn read(process: &Process, module_address: impl Into<Address>) -> Option<Self> {
#[repr(C)]
#[derive(Debug, Copy, Clone, Zeroable, Pod, Default)]
struct VsFixedFileInfo {
signature: u32, // must be 0xFEEF04BD
struct_version: u32,
file_version: FileVersion,
}

impl DataEntry {
fn is_rt_version(&self) -> bool {
self.id == 0x10
fn read_dir_header(process: &Process, addr: Address) -> Option<u16> {
// [6] = NumberOfNamedEntries, [7] = NumberOfIdEntries
process
.read::<[u16; 8]>(addr)
.map(|val| val[6] + val[7])
.ok()
}

fn is_directory(&self) -> bool {
(self.offset & 0x80000000) != 0
#[repr(C)]
#[derive(Pod, Zeroable, Copy, Clone, Debug, Default)]
struct DataEntry {
id: u32,
offset: u32,
}

fn get_offset(&self) -> u32 {
self.offset & 0x7FFFFFFF
impl DataEntry {
fn is_rt_version(&self) -> bool {
self.id == 0x10
}

fn is_directory(&self) -> bool {
(self.offset & 0x80000000) != 0
}

fn get_offset(&self) -> u32 {
self.offset & 0x7FFFFFFF
}
}
}

let address: Address = module_address.into();
let address: Address = module_address.into();

let coff_header_address = read_coff_header(process, address)
.filter(|(coff, _)| {
(coff.size_of_optional_header as usize) >= mem::size_of::<OptionalCOFFHeader>()
})
.map(|(_, address)| address)?;
let coff_header_address = read_coff_header(process, address)
.filter(|(coff, _)| {
(coff.size_of_optional_header as usize) >= mem::size_of::<OptionalCOFFHeader>()
})
.map(|(_, address)| address)?;

let optional_header_address = coff_header_address + mem::size_of::<COFFHeader>() as u64;
let optional_header_address = coff_header_address + mem::size_of::<COFFHeader>() as u64;

let optional_header_magic = process.read::<u16>(optional_header_address).ok()?;
let is_64_bit = match optional_header_magic {
0x10B => false, // PE32
0x20B => true, // PE32+
_ => return None, // Invalid data
};
let optional_header_magic = process.read::<u16>(optional_header_address).ok()?;
let is_64_bit = match optional_header_magic {
0x10B => false, // PE32
0x20B => true, // PE32+
_ => return None, // Invalid data
};

let res_dd_offset = if is_64_bit { 0x80 } else { 0x70 };
let res_dd_addr = optional_header_address + res_dd_offset;
let res_dd_offset = if is_64_bit { 0x80 } else { 0x70 };
let res_dd_addr = optional_header_address + res_dd_offset;

let data_directory = process
.read::<[u32; 2]>(res_dd_addr)
.ok()
.filter(|[a, b]| *a != 0 && *b != 0)
.map(|[a, _]| a)?;
let data_directory = process
.read::<[u32; 2]>(res_dd_addr)
.ok()
.filter(|[a, b]| *a != 0 && *b != 0)
.map(|[a, _]| a)?;

let res_base = address + data_directory;
let res_base = address + data_directory;

// Level 1 (resource type = RT_VERSION = 0x10)
let type_dir = (0..read_dir_header(process, res_base)?)
.filter_map(|i| process.read::<DataEntry>(res_base + 0x10 + i * 8).ok())
.find(|entry| entry.is_directory() && entry.is_rt_version())
.map(|entry| res_base + entry.get_offset())?;
// Level 1 (resource type = RT_VERSION = 0x10)
let type_dir = (0..read_dir_header(process, res_base)?)
.filter_map(|i| process.read::<DataEntry>(res_base + 0x10 + i * 8).ok())
.find(|entry| entry.is_directory() && entry.is_rt_version())
.map(|entry| res_base + entry.get_offset())?;

let lang_dir = (0..read_dir_header(process, type_dir)?)
.filter_map(|i| process.read::<DataEntry>(type_dir + 0x10 + i * 8).ok())
.find(|entry| entry.is_directory())
.map(|entry| res_base + entry.get_offset())?;
let lang_dir = (0..read_dir_header(process, type_dir)?)
.filter_map(|i| process.read::<DataEntry>(type_dir + 0x10 + i * 8).ok())
.find(|entry| entry.is_directory())
.map(|entry| res_base + entry.get_offset())?;

let data_entry = (0..read_dir_header(process, lang_dir)?)
.filter_map(|i| process.read::<DataEntry>(lang_dir + 0x10 + i * 8).ok())
.find(|entry| !entry.is_directory())
.map(|entry| res_base + entry.get_offset())?;
let data_entry = (0..read_dir_header(process, lang_dir)?)
.filter_map(|i| process.read::<DataEntry>(lang_dir + 0x10 + i * 8).ok())
.find(|entry| !entry.is_directory())
.map(|entry| res_base + entry.get_offset())?;

let vs_version_va = address + process.read::<u32>(data_entry).ok()?;
let vs_version_va = address + process.read::<u32>(data_entry).ok()?;

process
.read::<VsFixedFileInfo>(vs_version_va + 0x28)
.ok()
.filter(|val| val.signature == 0xFEEF04BD)
.map(|val| val.file_version)
process
.read::<VsFixedFileInfo>(vs_version_va + 0x28)
.ok()
.filter(|val| val.signature == 0xFEEF04BD)
.map(|val| val.file_version)
}
}
2 changes: 1 addition & 1 deletion src/game_engine/unity/il2cpp/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impl Version {
return Some(Self::Base);
}

let file_version = pe::file_version_info(process, unity_module)?;
let file_version = pe::FileVersion::read(process, unity_module)?;

return Some(
if file_version.major_version > 2023
Expand Down
2 changes: 1 addition & 1 deletion src/game_engine/unity/mono/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl Version {
// We can read Unity’s FileVersionInfo directly from the PE header
// and infer the version from its major/minor numbers.
if binary_format == BinaryFormat::PE {
let file_version = pe::file_version_info(process, unity_module.0)?;
let file_version = pe::FileVersion::read(process, unity_module.0)?;
return Some(
if file_version.major_version > 2021
|| (file_version.major_version == 2021 && file_version.minor_version >= 2)
Expand Down
Loading