Skip to content

Commit

Permalink
refactor: iterative DFS instead of recursion for Mach-O exports
Browse files Browse the repository at this point in the history
  • Loading branch information
latonis committed Jun 27, 2024
1 parent c45c8c7 commit f9ea488
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 97 deletions.
149 changes: 91 additions & 58 deletions lib/src/modules/macho/parser.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::collections::HashMap;

use crate::modules::protos;
use bstr::{BStr, ByteSlice};
use itertools::Itertools;
Expand Down Expand Up @@ -917,67 +919,94 @@ impl<'a> MachOFile<'a> {

fn parse_export_node(
&mut self,
) -> impl FnMut(&'a [u8], u64, &BStr) -> IResult<&'a [u8], String> + '_
{
move |data: &'a [u8], offset: u64, prefix: &BStr| {
let (remainder, length) = uleb128(&data[offset as usize..])?;
let mut remaining_data = remainder;

if length != 0 {
let (remainder, flags) = uleb128(remaining_data)?;
match flags {
EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => {
let (remainder, _stub_offset) = uleb128(remainder)?;

let (remainder, _resolver_offset) =
uleb128(remainder)?;
remaining_data = remainder;
}
EXPORT_SYMBOL_FLAGS_REEXPORT => {
let (remainder, _ordinal) = uleb128(remainder)?;
) -> impl FnMut(&'a [u8], u64) -> IResult<&'a [u8], usize> + '_ {
move |data: &'a [u8], offset: u64| {
let mut stack = Vec::<ExportNode>::new();
let mut visited = HashMap::<usize, bool>::new();
stack.push(ExportNode {
offset: offset as usize,
prefix: "".to_string(),
});

while !data.is_empty()
&& (offset as usize) < data.len()
&& !stack.is_empty()
{
if let Some(export_node) = stack.pop() {
let (remainder, length) =
uleb128(&data[export_node.offset..])?;
let mut remaining_data = remainder;
if !visited.contains_key(&export_node.offset) {
visited.insert(export_node.offset, true);
if length != 0 {
let (remainder, flags) = uleb128(remaining_data)?;
match flags {
EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER => {
let (remainder, _stub_offset) =
uleb128(remainder)?;

let (remainder, _resolver_offset) =
uleb128(remainder)?;
remaining_data = remainder;
}
EXPORT_SYMBOL_FLAGS_REEXPORT => {
let (remainder, _ordinal) =
uleb128(remainder)?;

let (remainder, _label) = map(
tuple((
take_till(|b| b == b'\x00'),
tag(b"\x00"),
)),
|(s, _)| s,
)(
remainder
)?;

remaining_data = remainder;
}
EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION => {
let (remainder, _offset) =
uleb128(remainder)?;
remaining_data = remainder;
}
_ => {}
}
}

let (remainder, _label) = map(
tuple((take_till(|b| b == b'\x00'), tag(b"\x00"))),
|(s, _)| s,
)(
remainder
)?;
let (remainder, edges) = u8(remaining_data)?;
let mut edge_remainder = remainder;
for _ in 0..edges {
let (remainder, strr) = map(
tuple((
take_till(|b| b == b'\x00'),
tag(b"\x00"),
)),
|(s, _)| s,
)(
edge_remainder
)?;
let edge_label = BStr::new(strr);
let (remainder, edge_offset) = uleb128(remainder)?;
if let Ok(edge_label_str) = edge_label.to_str() {
stack.push(ExportNode {
offset: edge_offset as usize,
prefix: format!(
"{}{}",
export_node.prefix, edge_label_str
),
});
}
edge_remainder = remainder;
}

remaining_data = remainder;
}
EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION => {
let (remainder, _offset) = uleb128(remainder)?;
remaining_data = remainder;
if length != 0 {
self.exports.push(export_node.prefix)
}
}
_ => {}
}
}

let (remainder, edges) = u8(remaining_data)?;
let mut edge_remainder = remainder;

for _ in 0..edges {
let (remainder, strr) = map(
tuple((take_till(|b| b == b'\x00'), tag(b"\x00"))),
|(s, _)| s,
)(edge_remainder)?;
let edge_label = BStr::new(strr);
let (remainder, edge_offset) = uleb128(remainder)?;
let (_, _) = self.parse_export_node()(
data,
edge_offset,
BStr::new(&bstr::concat([prefix, edge_label])),
)?;
edge_remainder = remainder;
}

if length != 0 {
if let Ok(prefix) = prefix.to_str() {
self.exports.push(prefix.to_string())
}
}

Ok((data, prefix.to_str().unwrap().to_string()))
Ok((data, 0))
}
}

Expand All @@ -988,8 +1017,7 @@ impl<'a> MachOFile<'a> {
) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], Vec<String>> + '_ {
move |data: &'a [u8]| {
let exports = Vec::<String>::new();
let (remainder, _) =
self.parse_export_node()(data, 0, BStr::new(""))?;
let (remainder, _) = self.parse_export_node()(data, 0)?;

Ok((remainder, exports))
}
Expand Down Expand Up @@ -1394,6 +1422,11 @@ struct MinVersion {
sdk: u32,
}

struct ExportNode {
offset: usize,
prefix: String,
}

/// Parser that reads a 32-bits or 64-bits
fn uint(
endianness: Endianness,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,39 +456,39 @@ min_version:
version: "10.13.0"
sdk: "10.13.0"
exports:
- "__Z18AllocateStringCopyRPcRK8wxString"
- "__Z18ACCT_GetFirstIndexPK15CAPF_DataSourceRl"
- "__Z19CBB_GetResmanEventsP15CAPF_DataSourcePFvP20HarmonyResourceEventPvES3_P15HarmonyNBTicket"
- "__Z19CBB_SetEFControlBarP15CAPF_DataSourceP15CAPF_PluginInfoPv"
- "__Z19CB_ImportControlBarP15CAPF_DataSourceP15CAPF_PluginInfoPv"
- "__Z19ACCT_FreeStringListPP17HarmonyAttributesi"
- "__Z19ACCT_ClearJobLogXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv"
- "__Z19ACCT_GetEventsExXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv"
- "__Z16CreateControlBarP8stCBInfoP20HarmonySessionHandle"
- "__Z16ACCT_ClearJobLogPK15CAPF_DataSource"
- "__Z16out_HarmonyAttrsRNSt3__113basic_ostreamIcNS_11char_traitsIcEEEEiP17HarmonyAttributes"
- "__Z14ACCT_GetJobLogPK15CAPF_DataSourcePPP17HarmonyAttributesRl"
- "__Z10InitializeP15CAPF_PluginInfoP14PLUGINMEMBLOCK"
- "__Z17ACCT_GetJobLogXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv"
- "__Z21CBB_GetControlBarDataP15CAPF_DataSourceP15CAPF_PluginInfoPv"
- "__Z21ACCT_GetJobLogPortionPK15CAPF_DataSourceRlS2_PPP17HarmonyAttributesS2_"
- "__Z21ACCT_GetFirstIndexXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv"
- "__Z21APF_ds2HarmonyHandlesPK15CAPF_DataSourcePP29harmonyDataSourceSpecificDataPP19HarmonyServerHandle"
- "__Z21APF_harmonyGetSessionP29harmonyDataSourceSpecificData22APF_harmonySessionType"
- "__Z20CBB_DeleteControlBarP15CAPF_DataSourceRK8wxString"
- "__Z20ACCT_GetJobLogLengthPK15CAPF_DataSourceRl"
- "__Z22CBB_FreeControlBarDataP15CAPF_DataSourceP15CAPF_PluginInfoPv"
- "__Z22ACCT_FreeStringListXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv"
- "__Z24CBB_GetControlbarSupportP15CAPF_DataSourceP15CAPF_PluginInfoPv"
- "__Z24ACCT_GetJobLogPortionXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv"
- "__Z23ACCT_GetJobLogLengthXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv"
- "__Z23APF_ChkTicketOrTimedOutP15CAPF_DataSourceR15HarmonyNBTicketR13HarmonyResult"
- "__Z23GetFeatureSessionHandlePK15CAPF_DataSource22APF_harmonySessionTypePP20HarmonySessionHandle"
- "__Z25APF_H_GetLocalizedStringsP29harmonyDataSourceSpecificDataPPciS2_i"
- "__Z26APF_H_GetLocalizedStringNBP15CAPF_DataSourceR8wxStringS2_iS2_"
- "__Z36APF_H_GetLocalizedStringNB_AlternateP15CAPF_DataSourceR8wxStringS2_iS2_"
- "__ZlsRNSt3__113basic_ostreamIcNS_11char_traitsIcEEEEP17HarmonyAttributes"
- "_APF_Plugin_Initialize"
- "_APF_Plugin_DisconnectingDataSource"
- "_APF_Plugin_Unload"
- "_APF_Plugin_Terminate"
- "_APF_Plugin_Unload"
- "_APF_Plugin_DisconnectingDataSource"
- "_APF_Plugin_Initialize"
- "__ZlsRNSt3__113basic_ostreamIcNS_11char_traitsIcEEEEP17HarmonyAttributes"
- "__Z36APF_H_GetLocalizedStringNB_AlternateP15CAPF_DataSourceR8wxStringS2_iS2_"
- "__Z26APF_H_GetLocalizedStringNBP15CAPF_DataSourceR8wxStringS2_iS2_"
- "__Z25APF_H_GetLocalizedStringsP29harmonyDataSourceSpecificDataPPciS2_i"
- "__Z23GetFeatureSessionHandlePK15CAPF_DataSource22APF_harmonySessionTypePP20HarmonySessionHandle"
- "__Z23APF_ChkTicketOrTimedOutP15CAPF_DataSourceR15HarmonyNBTicketR13HarmonyResult"
- "__Z23ACCT_GetJobLogLengthXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv"
- "__Z24ACCT_GetJobLogPortionXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv"
- "__Z24CBB_GetControlbarSupportP15CAPF_DataSourceP15CAPF_PluginInfoPv"
- "__Z22ACCT_FreeStringListXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv"
- "__Z22CBB_FreeControlBarDataP15CAPF_DataSourceP15CAPF_PluginInfoPv"
- "__Z20ACCT_GetJobLogLengthPK15CAPF_DataSourceRl"
- "__Z20CBB_DeleteControlBarP15CAPF_DataSourceRK8wxString"
- "__Z21APF_harmonyGetSessionP29harmonyDataSourceSpecificData22APF_harmonySessionType"
- "__Z21APF_ds2HarmonyHandlesPK15CAPF_DataSourcePP29harmonyDataSourceSpecificDataPP19HarmonyServerHandle"
- "__Z21ACCT_GetFirstIndexXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv"
- "__Z21ACCT_GetJobLogPortionPK15CAPF_DataSourceRlS2_PPP17HarmonyAttributesS2_"
- "__Z21CBB_GetControlBarDataP15CAPF_DataSourceP15CAPF_PluginInfoPv"
- "__Z17ACCT_GetJobLogXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv"
- "__Z10InitializeP15CAPF_PluginInfoP14PLUGINMEMBLOCK"
- "__Z14ACCT_GetJobLogPK15CAPF_DataSourcePPP17HarmonyAttributesRl"
- "__Z16out_HarmonyAttrsRNSt3__113basic_ostreamIcNS_11char_traitsIcEEEEiP17HarmonyAttributes"
- "__Z16ACCT_ClearJobLogPK15CAPF_DataSource"
- "__Z16CreateControlBarP8stCBInfoP20HarmonySessionHandle"
- "__Z19ACCT_GetEventsExXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv"
- "__Z19ACCT_ClearJobLogXMLP15CAPF_PluginInfoP14PLUGINMEMBLOCKP9VERSIONEXPv"
- "__Z19ACCT_FreeStringListPP17HarmonyAttributesi"
- "__Z19CB_ImportControlBarP15CAPF_DataSourceP15CAPF_PluginInfoPv"
- "__Z19CBB_SetEFControlBarP15CAPF_DataSourceP15CAPF_PluginInfoPv"
- "__Z19CBB_GetResmanEventsP15CAPF_DataSourcePFvP20HarmonyResourceEventPvES3_P15HarmonyNBTicket"
- "__Z18ACCT_GetFirstIndexPK15CAPF_DataSourceRl"
- "__Z18AllocateStringCopyRPcRK8wxString"
8 changes: 4 additions & 4 deletions lib/src/modules/macho/tests/testdata/tiny_universal.out
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,9 @@ file:
version: "10.9.0"
sdk: "10.10.0"
exports:
- "__mh_execute_header"
- "_factorial"
- "_main"
- "_factorial"
- "__mh_execute_header"
- magic: 0xcffaedfe
cputype: 0x1000007
cpusubtype: 0x80000003
Expand Down Expand Up @@ -395,6 +395,6 @@ file:
version: "10.9.0"
sdk: "10.10.0"
exports:
- "__mh_execute_header"
- "_main"
- "_factorial"
- "_main"
- "__mh_execute_header"

0 comments on commit f9ea488

Please sign in to comment.