Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Mac for Unity / Mono #53

Closed
wants to merge 8 commits into from
Closed
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ libm = { version = "0.2.7", optional = true }
wasi = { version = "0.11.0+wasi-snapshot-preview1", default-features = false }

[features]
std = ["alloc"]
alloc = []
derive = ["asr-derive"]
flags = ["bitflags"]
Expand Down
123 changes: 123 additions & 0 deletions src/file_format/macho.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//! Support for parsing MachO files

use crate::{Address, PointerSize, Process};

use core::mem;

// Magic mach-o header constants from:
// https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html
const MH_MAGIC_32: u32 = 0xfeedface;
const MH_CIGAM_32: u32 = 0xcefaedfe;
const MH_MAGIC_64: u32 = 0xfeedfacf;
const MH_CIGAM_64: u32 = 0xcffaedfe;

struct MachOFormatOffsets {
number_of_commands: usize,
load_commands: usize,
command_size: usize,
symbol_table_offset: usize,
number_of_symbols: usize,
string_table_offset: usize,
nlist_value: usize,
size_of_nlist_item: usize,
}

impl MachOFormatOffsets {
const fn new() -> Self {
// offsets taken from:
// - https://github.com/hackf5/unityspy/blob/master/src/HackF5.UnitySpy/Offsets/MachOFormatOffsets.cs
// - https://opensource.apple.com/source/xnu/xnu-4570.71.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html
MachOFormatOffsets {
number_of_commands: 0x10,
load_commands: 0x20,
command_size: 0x04,
symbol_table_offset: 0x08,
number_of_symbols: 0x0c,
string_table_offset: 0x10,
nlist_value: 0x08,
size_of_nlist_item: 0x10,
}
}
}

/// Scans the range for a page that begins with MachO Magic
pub fn scan_macho_page(process: &Process, range: (Address, u64)) -> Option<Address> {
const PAGE_SIZE: u64 = 0x1000;
let (addr, len) = range;
// negation mod PAGE_SIZE
let distance_to_page = (PAGE_SIZE - (addr.value() % PAGE_SIZE)) % PAGE_SIZE;
// round up to the next multiple of PAGE_SIZE
let first_page = addr + distance_to_page;
for i in 0..((len - distance_to_page) / PAGE_SIZE) {
let a = first_page + (i * PAGE_SIZE);
match process.read::<u32>(a) {

Check warning on line 53 in src/file_format/macho.rs

View workflow job for this annotation

GitHub Actions / Check clippy lints

you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
Ok(MH_MAGIC_64 | MH_CIGAM_64 | MH_MAGIC_32 | MH_CIGAM_32) => {

Check failure on line 54 in src/file_format/macho.rs

View workflow job for this annotation

GitHub Actions / Check formatting

Diff in /home/runner/work/asr/asr/src/file_format/macho.rs
return Some(a);
}
_ => ()
}
}
None
}

/// Determines whether a MachO header at the address is 64-bit or 32-bit
pub fn pointer_size(process: &Process, address: Address) -> Option<PointerSize> {
let magic: u32 = process.read(address).ok()?;
match magic {

Check failure on line 66 in src/file_format/macho.rs

View workflow job for this annotation

GitHub Actions / Check formatting

Diff in /home/runner/work/asr/asr/src/file_format/macho.rs
MH_MAGIC_64 | MH_CIGAM_64 => Some(PointerSize::Bit64),
MH_MAGIC_32 | MH_CIGAM_32 => Some(PointerSize::Bit32),
_ => None
}
}

/// Finds the address of a function from a MachO module range and file contents.

Check failure on line 73 in src/file_format/macho.rs

View workflow job for this annotation

GitHub Actions / Check formatting

Diff in /home/runner/work/asr/asr/src/file_format/macho.rs
pub fn get_function_address(process: &Process, range: (Address, u64), macho_bytes: &[u8], function_name: &[u8]) -> Option<Address> {
let function_offset: u32 = get_function_offset(&macho_bytes, function_name)?;

Check warning on line 75 in src/file_format/macho.rs

View workflow job for this annotation

GitHub Actions / Check clippy lints

this expression creates a reference which is immediately dereferenced by the compiler
let function_address = scan_macho_page(process, range)? + function_offset;
let actual: [u8; 0x100] = process.read(function_address).ok()?;
let expected: [u8; 0x100] = slice_read(&macho_bytes, function_offset as usize).ok()?;

Check warning on line 78 in src/file_format/macho.rs

View workflow job for this annotation

GitHub Actions / Check clippy lints

this expression creates a reference which is immediately dereferenced by the compiler

Check failure on line 78 in src/file_format/macho.rs

View workflow job for this annotation

GitHub Actions / Check formatting

Diff in /home/runner/work/asr/asr/src/file_format/macho.rs
if actual != expected { return None; }
Some(function_address)
}

/// Finds the offset of a function in the bytes of a MachO file.
pub fn get_function_offset(macho_bytes: &[u8], function_name: &[u8]) -> Option<u32> {
let macho_offsets = MachOFormatOffsets::new();
let number_of_commands: u32 = slice_read(macho_bytes, macho_offsets.number_of_commands).ok()?;
let function_name_len = function_name.len();

let mut offset_to_next_command: usize = macho_offsets.load_commands as usize;

Check warning on line 89 in src/file_format/macho.rs

View workflow job for this annotation

GitHub Actions / Check clippy lints

casting to the same type is unnecessary (`usize` -> `usize`)
for _i in 0..number_of_commands {
// Check if load command is LC_SYMTAB

Check failure on line 91 in src/file_format/macho.rs

View workflow job for this annotation

GitHub Actions / Check formatting

Diff in /home/runner/work/asr/asr/src/file_format/macho.rs
let next_command: i32 = slice_read(macho_bytes, offset_to_next_command).ok()?;
if next_command == 2 {
let symbol_table_offset: u32 = slice_read(macho_bytes, offset_to_next_command + macho_offsets.symbol_table_offset).ok()?;
let number_of_symbols: u32 = slice_read(macho_bytes, offset_to_next_command + macho_offsets.number_of_symbols).ok()?;
let string_table_offset: u32 = slice_read(macho_bytes, offset_to_next_command + macho_offsets.string_table_offset).ok()?;

for j in 0..(number_of_symbols as usize) {
let symbol_name_offset: u32 = slice_read(macho_bytes, symbol_table_offset as usize + (j * macho_offsets.size_of_nlist_item)).ok()?;
let string_offset = string_table_offset as usize + symbol_name_offset as usize;
let symbol_name: &[u8] = &macho_bytes[string_offset..(string_offset + function_name_len + 1)];

if symbol_name[function_name_len] == 0 && symbol_name.starts_with(function_name) {
return Some(slice_read(macho_bytes, symbol_table_offset as usize + (j * macho_offsets.size_of_nlist_item) + macho_offsets.nlist_value).ok()?);

Check warning on line 104 in src/file_format/macho.rs

View workflow job for this annotation

GitHub Actions / Check clippy lints

question mark operator is useless here
}
}

break;

Check failure on line 108 in src/file_format/macho.rs

View workflow job for this annotation

GitHub Actions / Check formatting

Diff in /home/runner/work/asr/asr/src/file_format/macho.rs
} else {
let command_size: u32 = slice_read(macho_bytes, offset_to_next_command + macho_offsets.command_size).ok()?;
offset_to_next_command += command_size as usize;
}
}
None
}

Check failure on line 116 in src/file_format/macho.rs

View workflow job for this annotation

GitHub Actions / Check formatting

Diff in /home/runner/work/asr/asr/src/file_format/macho.rs
/// Reads a value of the type specified from the slice at the address
/// given.
pub fn slice_read<T: bytemuck::CheckedBitPattern>(slice: &[u8], address: usize) -> Result<T, bytemuck::checked::CheckedCastError> {
let size = mem::size_of::<T>();
let slice_src = &slice[address..(address + size)];
bytemuck::checked::try_from_bytes(slice_src).cloned()
}
1 change: 1 addition & 0 deletions src/file_format/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Support for parsing various file formats.

Check failure on line 1 in src/file_format/mod.rs

View workflow job for this annotation

GitHub Actions / Check formatting

Diff in /home/runner/work/asr/asr/src/file_format/mod.rs

pub mod elf;
pub mod pe;
pub mod macho;
Loading