diff --git a/.gitignore b/.gitignore index 7f2a1ddf34..ca14776861 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk -temp/ +tmp/ # # macOS GitIgnore diff --git a/src/argument.rs b/src/argument.rs deleted file mode 100644 index 95cbb465cf..0000000000 --- a/src/argument.rs +++ /dev/null @@ -1,46 +0,0 @@ -use std::path::{Path,PathBuf}; -use clap::{Parser}; - -#[derive(Debug)] -pub struct Arguments { - pub results_path: String, - pub base_path: String -} - -#[derive(Parser)] -#[command(version, author = "Thomas A.", about = "Extracts library symbols from Apple's framework")] -struct RawArguments { - /// The normal root directory in macOS, iOS, etc. - /// If no argument is provided, the root path will be used. - #[arg(long, value_name = "PATH")] - standard_path: Option, - /// Where the symbols will be saved at. - #[arg(value_name = "RESULT FOLDER")] - results_path: String -} - -impl Arguments { - pub fn new() -> Arguments { - let raw_arguments = RawArguments::parse(); - Self::into_arguments(raw_arguments) - } - - fn into_arguments(raw_arguments: RawArguments) -> Arguments { - let base_path = raw_arguments.standard_path.unwrap_or(String::from("/")); - - Arguments { - results_path: raw_arguments.results_path, - base_path - } - } - - pub fn path_from_base>(&self, location: P) -> PathBuf { - let base_path = Path::new(self.base_path.as_str()); - base_path.join(location) - } - - pub fn path_from_results>(&self, location: P) -> PathBuf { - let results_path = Path::new(self.results_path.as_str()); - results_path.join(location) - } -} \ No newline at end of file diff --git a/src/arguments.rs b/src/arguments.rs new file mode 100644 index 0000000000..5d863fb781 --- /dev/null +++ b/src/arguments.rs @@ -0,0 +1 @@ +pub mod cli; \ No newline at end of file diff --git a/src/arguments/cli.rs b/src/arguments/cli.rs new file mode 100644 index 0000000000..1098d8359f --- /dev/null +++ b/src/arguments/cli.rs @@ -0,0 +1,65 @@ +use std::path::{Path,PathBuf}; +use clap::{Parser}; + +#[derive(Debug)] +pub struct CliArguments { + pub results_path: PathBuf, + pub base_path: PathBuf, + pub cryptexes_os_path: Option, +} + +#[derive(Parser)] +#[command(version, author = "Thomas A.", about = "Extracts library symbols from Apple's framework/libraries")] +struct RawArguments { + /// The normal root directory in macOS, iOS, etc. + /// If no argument is provided, the root path will be used. + #[arg(long, value_name = "PATH")] + standard_path: Option, + /// Path to cryptexes OS directory. + /// If no argument is provided, the program will first check + /// if "/System/Cryptexes/OS" exists, if it doesn't exist, the + /// option will be ignored. + #[arg(long, value_name = "PATH")] + cryptexes_os_path: Option, + /// Where the symbols will be saved at. + #[arg(value_name = "RESULT FOLDER")] + results_path: String, +} + +impl CliArguments { + pub fn new() -> CliArguments { + let raw_arguments = RawArguments::parse(); + Self::into_arguments(raw_arguments) + } + + fn into_arguments(raw_arguments: RawArguments) -> CliArguments { + let base_path = raw_arguments.standard_path.unwrap_or_else(|| { + println!("Standard path not provided. Falling back to root directory ('/')"); + String::from("/") + }); + + let cryptexes_os_path = raw_arguments.cryptexes_os_path.or_else(|| { + let temp = String::from("/System/Cryptexes/OS"); + let temp_path = Path::new(temp.as_str()); + + println!("Cryptexes OS path not provided. Checking if path '{}' exists", temp); + if temp_path.exists() { + println!("Found '{}' path", temp); + Some(temp) + } else { + println!("Unable to find '{}' path", temp); + None + } + }); + + let results_path = PathBuf::from(raw_arguments.results_path); + let base_path = PathBuf::from(&base_path); + let cryptexes_os_path = cryptexes_os_path.map(|path| { PathBuf::from(path) }); + + CliArguments { + results_path, + base_path, + cryptexes_os_path, + } + } +} \ No newline at end of file diff --git a/src/clean.rs b/src/clean.rs index fa618f1f73..c1337bcb8b 100644 --- a/src/clean.rs +++ b/src/clean.rs @@ -2,18 +2,14 @@ use std::fs::remove_dir_all; use crate::location::ResultsLocation; -pub struct Cleanup {} - -impl Cleanup { - pub fn remove_saved_symbols(results_locations: &ResultsLocation) { - if let Ok(_) = remove_dir_all(&results_locations.unique_version_path) { - println!("Deleted {:?}", results_locations.unique_version_path); - } +pub fn remove_saved_symbols(results_locations: &ResultsLocation) { + if let Ok(_) = remove_dir_all(&results_locations.os_version_path) { + println!("Deleted {:?}", results_locations.os_version_path); } +} - pub fn remove_temp(results_locations: &ResultsLocation) { - if let Ok(_) = remove_dir_all(&results_locations.temp_path) { - println!("Cleaned up temp data"); - } +pub fn remove_temp(results_locations: &ResultsLocation) { + if let Ok(_) = remove_dir_all(&results_locations.temp_path) { + println!("Cleaned up temp data"); } } \ No newline at end of file diff --git a/src/location.rs b/src/location.rs index eee415e6dc..5d535d5990 100644 --- a/src/location.rs +++ b/src/location.rs @@ -1,9 +1,11 @@ +pub mod system; + use std::fs::{read_dir, ReadDir}; use std::iter::Enumerate; use std::path::{Path,PathBuf}; -use crate::argument::Arguments; -use crate::program::SystemVersionDefaults; +use crate::arguments::cli::{CliArguments}; +use crate::program::{SystemVersionDefaults}; pub(crate) fn walk_directory>(location: P, valid_file: fn(&PathBuf) -> bool) -> Vec { let mut current_directory: Vec> = Vec::new(); @@ -40,53 +42,25 @@ pub(crate) fn walk_directory>(location: P, valid_file: fn(&PathBu } -#[derive(Debug)] -pub struct BaseLocation { - pub system_version_path: PathBuf, - pub dyld_shardcache_macos_path: PathBuf, - pub dyld_shardcache_iphoneos_path: PathBuf, -} - -impl BaseLocation { - pub fn new(arguments: &Arguments) -> BaseLocation { - let system_version_path = Path::new("System/Library/CoreServices/SystemVersion"); - let dyld_shardcache_macos = Path::new("System/Library/dyld"); - let dyld_shardcache_iphoneos = Path::new("System/Library/Caches/com.apple.dyld"); - - BaseLocation { - system_version_path: arguments.path_from_base(system_version_path), - dyld_shardcache_macos_path: arguments.path_from_base(dyld_shardcache_macos), - dyld_shardcache_iphoneos_path: arguments.path_from_base(dyld_shardcache_iphoneos), - } - } -} - #[derive(Debug)] pub struct ResultsLocation { - pub shared_cache_path: PathBuf, - pub unique_version_path: PathBuf, + pub os_version_path: PathBuf, pub temp_path: PathBuf, - pub temp_shared_cache_path: PathBuf } impl ResultsLocation { - pub fn new(arguments: &Arguments, system_version: &SystemVersionDefaults) -> ResultsLocation { - const SHARED_CACHE_DIR: &str = "shared_cache"; - const TEMP_DIR: &str = "temp"; + pub fn new(arguments: &CliArguments, system_version: SystemVersionDefaults) -> ResultsLocation { + const TEMP_DIR: &str = "tmp"; let version_folder = format!("{} ({})", system_version.product_version, system_version.product_build_version); let system_version = &system_version.product_name; - let unique_version_path = arguments.path_from_results(Path::new(system_version.as_str())).join(version_folder); - let shared_cache_path = unique_version_path.join(SHARED_CACHE_DIR); + + let os_version_path = arguments.results_path.as_path().join(system_version).join(version_folder); + let temp_path = os_version_path.join(TEMP_DIR); - let temp_path = unique_version_path.join(TEMP_DIR); - let temp_shared_cache_path = temp_path.join(SHARED_CACHE_DIR); - - ResultsLocation { - shared_cache_path, - unique_version_path, + Self { + os_version_path, temp_path, - temp_shared_cache_path } } } \ No newline at end of file diff --git a/src/location/system.rs b/src/location/system.rs new file mode 100644 index 0000000000..99896402e7 --- /dev/null +++ b/src/location/system.rs @@ -0,0 +1,174 @@ +use std::{path::{PathBuf, Path}, fs::{read_dir, ReadDir}, collections::{VecDeque}}; + +use crate::{arguments::cli::CliArguments, program::DyldSharedCacheExtractor}; + +use super::ResultsLocation; + +const SYSTEM_FOLDER_BLACKLIST: [&str; 5] = ["Cryptexes","Library","Developer","Applications","Volumes"]; + +// where we searched for the library files (either directly on the filesystem +// or extracted from a sharedcache container). +pub const STANDARD_DIR: &str = "standard"; +pub const SHAREDCACHE_DIR: &str = "sharedcache"; +#[derive(Debug)] +pub enum SymbolsLocationType { + Standard, + SharedCache, + Unknown +} + +// The filesystem for where we will start the search. +const ROOT_DIR: &str = "root"; +const CRYPTEXES_DIR: &str = "cryptexes"; +#[derive(Debug)] +pub enum VolumeType { + Root, + Cryptexes, + Unknown +} + +#[derive(Debug)] +pub struct SystemPathParser { + pub save_path: PathBuf, + pub symbol_folders: Vec, +} + +impl SystemPathParser { + pub fn new(arguments: &CliArguments, results_location: &ResultsLocation) -> Vec { + let mut parser_paths: Vec = Vec::new(); + + parser_paths.append(&mut Self::new_from_filesystem(ROOT_DIR,results_location,&arguments.base_path)); + if let Some(cryptexes_os_path) = &arguments.cryptexes_os_path { + parser_paths.append(&mut Self::new_from_filesystem(CRYPTEXES_DIR,results_location,cryptexes_os_path)); + } + + parser_paths + } + + pub fn breakdown_save_path(&self) -> (SymbolsLocationType,VolumeType) { + let mut components = self.save_path.components(); + + let symbol_location = if let Some(symbol_location_path) = components.next() { + let symbol_location_path = symbol_location_path.as_os_str(); + if symbol_location_path == STANDARD_DIR { + SymbolsLocationType::Standard + } else if symbol_location_path == SHAREDCACHE_DIR { + SymbolsLocationType::SharedCache + } else { + SymbolsLocationType::Unknown + } + } else { SymbolsLocationType::Unknown }; + + let volume = if let Some(volume_path) = components.next() { + let volume_path = volume_path.as_os_str(); + if volume_path == ROOT_DIR { + VolumeType::Root + } else if volume_path == CRYPTEXES_DIR { + VolumeType::Cryptexes + } else { + VolumeType::Unknown + } + } else { VolumeType::Unknown }; + + (symbol_location,volume) + } + + fn new_from_filesystem>(filesystem_identifier: &str, results_location: &ResultsLocation, filesystem_path: &P) -> Vec { + let mut parser_paths: Vec = Vec::new(); + let save_path = Path::new(STANDARD_DIR).join(filesystem_identifier); + let mut paths_to_sharedcache = Self::create_system_path_parser(&mut parser_paths,filesystem_path,save_path); + + while !paths_to_sharedcache.is_empty() { + if let Some(sharedcache_folder) = paths_to_sharedcache.pop_front() { + + if let Ok(relative_path) = sharedcache_folder.strip_prefix(&filesystem_path) { + let system_identifier = + if relative_path.starts_with("System/DriverKit") {"driverkit"} + else if relative_path.starts_with("System/Library") {"library"} + else { panic!("Unexpected filepath {:?}", relative_path) }; + + let save_path = Path::new(SHAREDCACHE_DIR).join(filesystem_identifier).join(system_identifier); + let sharedcache_extracted_path = results_location.temp_path.join(save_path.as_os_str()); + let dyld_shared_cache_extractor = DyldSharedCacheExtractor::new(&sharedcache_extracted_path, &sharedcache_folder); + + for extracted_path in dyld_shared_cache_extractor.extracted_paths { + let save_path = save_path.join(extracted_path.as_path().file_name().unwrap()); + let unexpected_sharedcache_locations = Self::create_system_path_parser(&mut parser_paths,&extracted_path,save_path); + assert!(unexpected_sharedcache_locations.is_empty(),"Unexpected sharedcache files"); + } + + } + + } + } + + parser_paths + } + + fn create_system_path_parser>(parser_paths: &mut Vec, filesystem_path: &P, save_path: PathBuf) -> VecDeque{ + let mut paths_to_sharedcache: VecDeque = VecDeque::new(); + + let mut paths_to_analyse: VecDeque = VecDeque::new(); + let mut symbol_folders: Vec = Vec::new(); + paths_to_analyse.push_back(PathBuf::from(filesystem_path.as_ref())); + while !paths_to_analyse.is_empty() { + if let Some(path) = paths_to_analyse.pop_front() { + Self::append_path_if_exists(&mut symbol_folders,&path,"usr/lib"); + + let system_path = path.join("System"); + if system_path.exists() { + Self::append_path_if_exists(&mut symbol_folders,&system_path,"Library/Frameworks"); + Self::append_path_if_exists(&mut symbol_folders,&system_path,"Library/PrivateFrameworks"); + + let system_folders = read_dir(&system_path).unwrap(); + Self::analyse_system_subfolders(system_folders,&mut paths_to_analyse); + + if let Some(sharedcache_folder) = Self::determine_sharedcache_folder(&system_path) { + paths_to_sharedcache.push_back(sharedcache_folder) + }; + } + } + } + + + parser_paths.push(SystemPathParser { save_path, symbol_folders }); + + return paths_to_sharedcache; + } + + fn append_path_if_exists>(list_of_path: &mut Vec, path: &P, path_to_append: &str) { + let evaluate_path = path.as_ref().join(path_to_append); + if evaluate_path.exists() { list_of_path.push(evaluate_path) } + } + + fn determine_sharedcache_folder>(path: &P) -> Option { + let mut dyld_shardcache_path: Option = None; + let dyld_shardcache_macos = path.as_ref().join(Path::new("Library/dyld")); + let dyld_shardcache_iphoneos = path.as_ref().join(Path::new("Library/Caches/com.apple.dyld")); + + let mut found_paths: u8 = 0; + found_paths += if dyld_shardcache_macos.exists() {1} else {0}; + found_paths += if dyld_shardcache_iphoneos.exists() {1} else {0}; + assert!(found_paths <= 1, "Both {dyld_shardcache_macos:?} and {dyld_shardcache_iphoneos:?} exist"); + + if dyld_shardcache_macos.exists() { + dyld_shardcache_path = Some(dyld_shardcache_macos) + } else if dyld_shardcache_iphoneos.exists() { + dyld_shardcache_path = Some(dyld_shardcache_iphoneos) + } + + return dyld_shardcache_path; + } + + fn analyse_system_subfolders(system_folders: ReadDir, paths_to_sharedcache: &mut VecDeque) { + for system_folder in system_folders { + let system_folder = system_folder.unwrap(); + let folder_name = system_folder.file_name(); + let file_type = system_folder.file_type().unwrap(); + + if !SYSTEM_FOLDER_BLACKLIST.contains(&folder_name.to_str().unwrap()) && file_type.is_dir() { + paths_to_sharedcache.push_back(system_folder.path()); + } + } + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index b5265f25b8..171691564d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,48 +1,96 @@ -mod argument; +mod arguments; mod clean; mod location; mod program; mod symbols; -use std::path::Path; +use std::{path::{Path, PathBuf}, fs::{File, create_dir_all}, io::Write}; -fn analyse_system_path,Q: AsRef>(path: &P, whoami_username: &program::WhoAmIUserName, result_location: &Q, unique_folder :Option<&str>) { - let parse_filesystem_symbols_list = symbols::ParseBaseFilesystem::new(path); - for parse_filesystem_symbols in parse_filesystem_symbols_list { - parse_filesystem_symbols.traverse(result_location, unique_folder, path, &whoami_username); +use location::system::SystemPathParser; + +#[derive(Debug)] +struct GlobalMainVariables { + arguments: arguments::cli::CliArguments, + system_path_parser: Vec, + results_location: location::ResultsLocation, + whoami_username: program::WhoAmIUserName, +} + +impl GlobalMainVariables { + pub fn new() -> GlobalMainVariables { + let arguments = arguments::cli::CliArguments::new(); + let system_version_path = arguments.base_path.join(Path::new("System/Library/CoreServices/SystemVersion.plist")); + + assert!(system_version_path.exists(), "Unable to find SystemVersion.plist"); + + let system_version = program::SystemVersionDefaults::new(system_version_path.to_str().unwrap()); + let whoami_username = program::WhoAmIUserName::new(); + let results_location = location::ResultsLocation::new(&arguments, system_version); + + clean::remove_saved_symbols(&results_location); + + let system_path_parser = location::system::SystemPathParser::new(&arguments, &results_location); + + GlobalMainVariables { + arguments, + system_path_parser, + results_location, + whoami_username, + } + } + + pub fn parse_for_symbols(&self) { + for path_to_parse in &self.system_path_parser { + let filesystem_path = Self::determine_filesystem_path(&self, path_to_parse); + for symbol_folder in &path_to_parse.symbol_folders { + let macho_executables = location::walk_directory(symbol_folder, symbols::macho::is_file_macho); + for macho_executable in macho_executables { + let relative_path = macho_executable.strip_prefix(&filesystem_path).unwrap().parent().unwrap(); + let macho_executable_file_name = format!{"{}.symboldir",macho_executable.file_name().unwrap().to_str().unwrap()}; + let macho_executable_dir = self.results_location.os_version_path.join(&path_to_parse.save_path).join(relative_path).join(macho_executable_file_name); + create_dir_all(&macho_executable_dir).unwrap(); + + let nm_program = program::NmLibrarySymbols::new(&macho_executable); + let mut nm_output_file = File::create(macho_executable_dir.join("nm.txt")).unwrap(); + nm_output_file.write_all(nm_program.raw_output.as_bytes()).expect("Unable to save `nm` output"); + + let otool_program = program::OtoolLibrarySymbols::new(&macho_executable, &self.whoami_username); + let mut otool_output_file = File::create(macho_executable_dir.join("otool.txt")).unwrap(); + otool_output_file.write_all(otool_program.raw_output.as_bytes()).expect("Unable to save `otool` output"); + } + } + } + } + + fn determine_filesystem_path(&self, path_to_parse: &SystemPathParser) -> PathBuf{ + let (symbol_location,volume) = path_to_parse.breakdown_save_path(); + + let arguments = &self.arguments; + let results_location = &self.results_location; + let starting_path = match symbol_location { + location::system::SymbolsLocationType::Standard => { + match volume { + location::system::VolumeType::Root => { arguments.base_path.clone() } + location::system::VolumeType::Cryptexes => { arguments.cryptexes_os_path.clone().unwrap() } + location::system::VolumeType::Unknown => { panic!("Unknown volumn location detected"); } + } + } + location::system::SymbolsLocationType::SharedCache => { + results_location.temp_path.join(&path_to_parse.save_path) + } + location::system::SymbolsLocationType::Unknown => { panic!("Unknown symbol location detected"); } + }; + + starting_path + } + + pub fn clean(&self) { + clean::remove_temp(&self.results_location); } } fn main() { - let arguments = argument::Arguments::new(); - let base_locations = location::BaseLocation::new(&arguments); - - let system_version = program::SystemVersionDefaults::new(base_locations.system_version_path.to_str().unwrap()); - let results_location = location::ResultsLocation::new(&arguments, &system_version); - let whoami_username = program::WhoAmIUserName::new(); - - clean::Cleanup::remove_saved_symbols(&results_location); - - let dyld_shared_cache_extractor = program::DyldSharedCacheExtractor::new(&base_locations, &results_location); - - for path in dyld_shared_cache_extractor.extracted_paths.iter() { - let shared_cache_folder = path.as_path().file_name().expect("Unable to obtain shared cache folder name").to_str(); - analyse_system_path(path,&whoami_username,&results_location.shared_cache_path,shared_cache_folder); - } - clean::Cleanup::remove_temp(&results_location); - - { - let path = Path::new(arguments.base_path.as_str()); - let unique_folder = Some("standard"); - analyse_system_path(&path,&whoami_username,&results_location.unique_version_path,unique_folder); - clean::Cleanup::remove_temp(&results_location); - } - - - println!{"{:#?}",arguments} - println!{"{:#?}",system_version} - println!{"{:#?}",base_locations} - println!{"{:#?}",results_location} - println!{"{:#?}",dyld_shared_cache_extractor} - println!("{:#?}",whoami_username) -} + let execution: GlobalMainVariables = GlobalMainVariables::new(); + execution.parse_for_symbols(); + execution.clean(); +} \ No newline at end of file diff --git a/src/program.rs b/src/program.rs index 1c223dbe62..2a42a428f2 100644 --- a/src/program.rs +++ b/src/program.rs @@ -1,6 +1,6 @@ -use std::{process::{Command, Output}, fs::{read_dir, ReadDir, File}, path::{Path, PathBuf}, io::Read}; +use std::{process::{Command, Output}, fs::{read_dir, ReadDir}, path::{Path, PathBuf}}; -use crate::location::{BaseLocation, ResultsLocation}; +use crate::{symbols::macho::is_file_dyld_sharedcache}; fn parse_stdout(output: Output) -> Vec { let raw_string = String::from_utf8(output.stdout).expect("Unable to save output"); @@ -53,36 +53,31 @@ pub struct DyldSharedCacheExtractor { } impl DyldSharedCacheExtractor { - pub fn new(base_location: &BaseLocation, results_location: &ResultsLocation) -> DyldSharedCacheExtractor { + pub fn new, Q: AsRef>(sharedcache_extracted_path: &Q, dyld_sharedcache_folder: &P) -> DyldSharedCacheExtractor { let mut instance = DyldSharedCacheExtractor { extracted_paths: Vec::new() }; - - if let Ok(read_dir) = read_dir(&base_location.dyld_shardcache_macos_path) { - println!("Inspecting {:?} for shared cache", base_location.dyld_shardcache_macos_path); - instance.extract_shared_library(results_location,read_dir); - } - if let Ok(read_dir) = read_dir(&base_location.dyld_shardcache_iphoneos_path) { - println!("Inspecting {:?} for shared cache", base_location.dyld_shardcache_iphoneos_path); - instance.extract_shared_library(results_location,read_dir); + if let Ok(sharedcache_readdir) = read_dir(dyld_sharedcache_folder) { + println!("Inspecting {:?} directory for shared cache", dyld_sharedcache_folder.as_ref()); + instance.extract_shared_library(sharedcache_extracted_path,sharedcache_readdir); } instance } - fn extract_shared_library(&mut self, results_location: &ResultsLocation, read_dir: ReadDir) { - for dir_entry in read_dir { - let dir_entry = dir_entry.unwrap(); - let file_path = dir_entry.path(); + fn extract_shared_library>(&mut self, sharedcache_extracted_path: &P, sharedcache_readdir: ReadDir) { + for sharedcache_direntry in sharedcache_readdir { + let sharedcache_direntry = sharedcache_direntry.unwrap(); + let sharedcache_file_path = sharedcache_direntry.path(); - if self.is_shared_cache_file(file_path.as_path()) { - let filename = file_path.as_path().file_name().expect("Unable to get file name"); - let temp_path = results_location.temp_shared_cache_path.join(filename); - DyldSharedCacheExtractor::launch_program(file_path.as_path(), &temp_path); + if is_file_dyld_sharedcache(sharedcache_file_path.as_path()) { + let file_name = sharedcache_file_path.file_name().unwrap(); + let temp_path = sharedcache_extracted_path.as_ref().join(&format!("{}.dir",file_name.to_str().unwrap())); // If the path doesn't exist after `dyld-shared-cache-extractor` finishes executing, it means that // the application was not able to extract anything from it. + DyldSharedCacheExtractor::launch_program(sharedcache_file_path.as_path(), &temp_path); if temp_path.is_dir() { self.extracted_paths.push(temp_path); } @@ -90,19 +85,6 @@ impl DyldSharedCacheExtractor { } } - fn is_shared_cache_file(&self, file_path: &Path) -> bool { - if file_path.is_file() { - if let Ok(mut file) = File::open(file_path) { - let mut magic: [u8; 5] = [0; 5]; - if let Ok(_) = file.read(&mut magic) { - return [b'd',b'y',b'l',b'd',b'_'] == magic; - } - } - } - - return false - } - fn launch_program(shared_cache_path: &Path, temp_path: &Path) { let _ = Command::new("dyld-shared-cache-extractor") .args([shared_cache_path, temp_path]) diff --git a/src/symbols.rs b/src/symbols.rs index a03bb48a49..66a9759abe 100644 --- a/src/symbols.rs +++ b/src/symbols.rs @@ -1,110 +1 @@ -use std::{path::{PathBuf, Path}, fs::{File, self}, io::{Read, Write}}; - -use crate::{location, program}; - -const SYSTEM_LIBRARY_FRAMEWORK_PATH: &str = "System/Library/Frameworks"; -const SYSTEM_LIBRARY_PRIVATEFRAMEWORK_PATH: &str = "System/Library/PrivateFrameworks"; -const USR_LIB_PATH: &str = "usr/lib"; -const SYSTEM_IOSSUPPORT: &str = "System/iOSSupport"; - -const MACHO_32BIT_MH_MAGIC: u32 = 0xfeedface; -const MACHO_32BIT_MH_CIGAM: u32 = 0xcefaedfe; -const MACHO_64BIT_MH_MAGIC: u32 = 0xfeedfacf; -const MACHO_64BIT_MH_CIGAM: u32 = 0xcffaedfe; -const MACHO_FAT_MAGIC: u32 = 0xcafebabe; -const MACHO_FAT_CIGAM: u32 = 0xbebafeca; - -fn is_file_macho(path: &PathBuf) -> bool { - if !path.is_file() { - return false - } - - let macho_filetype: u32; - if let Ok(mut file) = File::open(path) { - let mut temp_buf: [u8; 4] = [0; 4]; - if let Ok(size) = file.read(&mut temp_buf) { - if size != 4 { return false; } - } - macho_filetype = u32::from_le_bytes(temp_buf); - } else { - return false; - } - - let macho_32bit: bool; - let macho_64bit: bool; - let macho_fat: bool; - - macho_32bit = macho_filetype == MACHO_32BIT_MH_MAGIC - || macho_filetype == MACHO_32BIT_MH_CIGAM; - macho_64bit = macho_filetype == MACHO_64BIT_MH_MAGIC - || macho_filetype == MACHO_64BIT_MH_CIGAM; - macho_fat = macho_filetype == MACHO_FAT_MAGIC - || macho_filetype == MACHO_FAT_CIGAM; - - macho_32bit || macho_64bit || macho_fat -} - -pub struct ParseBaseFilesystem { - framework_path: PathBuf, - privateframework_path: PathBuf, - usr_lib_path: PathBuf -} - -const NM_TEXTFILE_NAME: &str = "nm.txt"; -const OTOOL_TEXTFILE_NAME: &str = "otool.txt"; - -impl ParseBaseFilesystem { - pub fn new>(location: &P) -> Vec { - let mut base_filesystems: Vec = Vec::new(); - base_filesystems.push(ParseBaseFilesystem::build_parse_base_filesystem(location)); - - let iossupport_path = location.as_ref().join(SYSTEM_IOSSUPPORT); - if iossupport_path.is_dir() { - println!("Found iOSSupport Directory"); - base_filesystems.push(ParseBaseFilesystem::build_parse_base_filesystem(iossupport_path)); - } - - base_filesystems - } - - fn build_parse_base_filesystem>(location: P) -> ParseBaseFilesystem { - let framework_path = location.as_ref().join(SYSTEM_LIBRARY_FRAMEWORK_PATH); - let privateframework_path = location.as_ref().join(SYSTEM_LIBRARY_PRIVATEFRAMEWORK_PATH); - let usr_lib_path = location.as_ref().join(USR_LIB_PATH); - - ParseBaseFilesystem { - framework_path, - privateframework_path, - usr_lib_path - } - } - - pub fn traverse, Q: AsRef>(&self, result_location: P, unique_folder: Option<&str>, base_location: Q, whoami: &program::WhoAmIUserName) { - let mut macho_paths: Vec = Vec::new(); - macho_paths.append(&mut location::walk_directory(&self.framework_path, is_file_macho)); - macho_paths.append(&mut location::walk_directory(&self.privateframework_path, is_file_macho)); - macho_paths.append(&mut location::walk_directory(&self.usr_lib_path, is_file_macho)); - - let macho_paths = macho_paths; - for macho_path in macho_paths.iter() { - let relative_location = macho_path.strip_prefix(base_location.as_ref()).unwrap(); - let result_dir: PathBuf = if let Some(unique_folder) = unique_folder { - result_location.as_ref().join(unique_folder).join(relative_location) - } else { - result_location.as_ref().join(relative_location) - }; - - println!("Parsing {:?}", macho_path); - fs::create_dir_all(&result_dir).expect("Unable to create directory"); - - let mut nm_textfile = File::create(result_dir.as_path().join(NM_TEXTFILE_NAME)).expect("Unable to create file"); - let nm = program::NmLibrarySymbols::new(&macho_path); - nm_textfile.write(nm.raw_output.as_bytes()).expect("Unable to save log information into file"); - - let mut otool_textfile = File::create(result_dir.as_path().join(OTOOL_TEXTFILE_NAME)).expect("Unable to create file"); - let otool = program::OtoolLibrarySymbols::new(&macho_path,whoami); - otool_textfile.write(otool.raw_output.as_bytes()).expect("Unable to save log information into file"); - - } - } -} \ No newline at end of file +pub mod macho; diff --git a/src/symbols/macho.rs b/src/symbols/macho.rs new file mode 100644 index 0000000000..668ae0fffc --- /dev/null +++ b/src/symbols/macho.rs @@ -0,0 +1,54 @@ +use std::{path::Path, fs::File, io::Read}; + +const MACHO_32BIT_MH_MAGIC: u32 = 0xfeedface; +const MACHO_32BIT_MH_CIGAM: u32 = 0xcefaedfe; +const MACHO_64BIT_MH_MAGIC: u32 = 0xfeedfacf; +const MACHO_64BIT_MH_CIGAM: u32 = 0xcffaedfe; +const MACHO_FAT_MAGIC: u32 = 0xcafebabe; +const MACHO_FAT_CIGAM: u32 = 0xbebafeca; + +pub(crate) fn is_file_macho>(path: &P) -> bool { + let path = path.as_ref(); + + if !path.is_file() { + return false + } + + let macho_filetype: u32; + if let Ok(mut file) = File::open(path) { + let mut temp_buf: [u8; 4] = [0; 4]; + if let Ok(size) = file.read(&mut temp_buf) { + if size != 4 { return false; } + } + macho_filetype = u32::from_le_bytes(temp_buf); + } else { + return false; + } + + let macho_32bit: bool; + let macho_64bit: bool; + let macho_fat: bool; + + macho_32bit = macho_filetype == MACHO_32BIT_MH_MAGIC + || macho_filetype == MACHO_32BIT_MH_CIGAM; + macho_64bit = macho_filetype == MACHO_64BIT_MH_MAGIC + || macho_filetype == MACHO_64BIT_MH_CIGAM; + macho_fat = macho_filetype == MACHO_FAT_MAGIC + || macho_filetype == MACHO_FAT_CIGAM; + + macho_32bit || macho_64bit || macho_fat +} + +pub(crate) fn is_file_dyld_sharedcache(file_path: &Path) -> bool { + if file_path.is_file() { + if let Ok(mut file) = File::open(file_path) { + let mut magic: [u8; 5] = [0; 5]; + if let Ok(_) = file.read(&mut magic) { + // The magic header for sharedcache is "dyld_" + return [b'd',b'y',b'l',b'd',b'_'] == magic; + } + } + } + + return false +}