Initial commit: xenia-rs workspace for Xbox 360 RE
Rust reimplementation of the xenia Xbox 360 emulator targeting reverse- engineering and preservation, initially scoped to Project Sylpheed. Includes: - XEX2 loader (LZX decompression, AES decryption, PE parsing) - XISO / XGD2 disc image VFS - PPC interpreter with 200+ opcodes and VMX128 decoding - Static analyzer: functions, cross-references, labels, asm + SQLite output - HLE kernel covering the xboxkrnl/xam subset used by Sylpheed init - Debugger with in-memory and SQLite-backed execution tracing - `xenia-rs` CLI with extract/dis/exec commands that produce cumulative, superset SQLite databases and opt-in instruction/import/branch traces Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
68
crates/xenia-xex/src/pe.rs
Normal file
68
crates/xenia-xex/src/pe.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
//! Minimal PE parser for Xbox 360 executables.
|
||||
//! PE headers are little-endian even on the big-endian Xbox 360.
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize, Debug, Clone)]
|
||||
pub struct PeSection {
|
||||
pub name: String,
|
||||
pub virtual_address: u32,
|
||||
pub virtual_size: u32,
|
||||
pub raw_offset: u32,
|
||||
pub raw_size: u32,
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
impl PeSection {
|
||||
pub fn is_code(&self) -> bool {
|
||||
self.flags & 0x20000000 != 0 // IMAGE_SCN_MEM_EXECUTE
|
||||
}
|
||||
}
|
||||
|
||||
fn le_u16(data: &[u8], off: usize) -> u16 {
|
||||
u16::from_le_bytes([data[off], data[off + 1]])
|
||||
}
|
||||
|
||||
fn le_u32(data: &[u8], off: usize) -> u32 {
|
||||
u32::from_le_bytes([data[off], data[off + 1], data[off + 2], data[off + 3]])
|
||||
}
|
||||
|
||||
pub fn parse_sections(pe: &[u8]) -> anyhow::Result<Vec<PeSection>> {
|
||||
anyhow::ensure!(pe.len() >= 64, "PE too small");
|
||||
anyhow::ensure!(pe[0] == b'M' && pe[1] == b'Z', "not a PE (bad MZ)");
|
||||
|
||||
let e_lfanew = le_u32(pe, 0x3C) as usize;
|
||||
anyhow::ensure!(e_lfanew + 4 <= pe.len(), "e_lfanew out of bounds");
|
||||
|
||||
let nt_sig = le_u32(pe, e_lfanew);
|
||||
anyhow::ensure!(nt_sig == 0x00004550, "bad PE signature: 0x{nt_sig:08X}");
|
||||
|
||||
let file_header_off = e_lfanew + 4;
|
||||
let num_sections = le_u16(pe, file_header_off + 2) as usize;
|
||||
let opt_header_size = le_u16(pe, file_header_off + 16) as usize;
|
||||
|
||||
let section_table_off = file_header_off + 20 + opt_header_size;
|
||||
|
||||
let mut sections = Vec::new();
|
||||
for i in 0..num_sections {
|
||||
let s = section_table_off + i * 40;
|
||||
if s + 40 > pe.len() { break; }
|
||||
|
||||
let name_bytes = &pe[s..s + 8];
|
||||
let name = std::str::from_utf8(name_bytes)
|
||||
.unwrap_or("???")
|
||||
.trim_end_matches('\0')
|
||||
.to_string();
|
||||
|
||||
sections.push(PeSection {
|
||||
name,
|
||||
virtual_size: le_u32(pe, s + 8),
|
||||
virtual_address: le_u32(pe, s + 12),
|
||||
raw_size: le_u32(pe, s + 16),
|
||||
raw_offset: le_u32(pe, s + 20),
|
||||
flags: le_u32(pe, s + 36),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(sections)
|
||||
}
|
||||
Reference in New Issue
Block a user