Files
xenia-rs/crates/xenia-xex/src/pe.rs
MechaCat02 c694bb3f43 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>
2026-04-16 23:14:56 +02:00

69 lines
2.0 KiB
Rust

//! 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)
}