├── _config.yml ├── fix_swift ├── llvm │ └── Config │ │ ├── abi-breaking.h │ │ └── llvm-config.h └── swift │ └── Runtime │ └── CMakeConfig.h ├── docs └── _config.yml ├── media ├── swift.png └── vmmap.png ├── compiled ├── dsdump_beta.zip └── dsdump_compiled.zip ├── dsdump ├── binaries │ ├── some_app │ └── arm_hello.out ├── libswiftDemangling.a ├── XRMachOLibrary+PID_Info.h ├── Bind Symbols │ ├── XRMachOLibrary+Opcode.h │ ├── XRBindSymbol.h │ ├── XRBindSymbol.m │ └── XRMachOLibrary+Opcode.mm ├── XRMachOLibrary+Disassemble.h ├── XRMachOLibrary+Swift.h ├── TaskPath.h ├── XRMachOLibrary_cplus.h ├── Ripped off │ ├── objc-layout.h │ ├── objc-typeencoding.mm │ └── objc_.h ├── XRSymbolEntry.m ├── Protocols.h ├── Properties.h ├── XRMachOLibrary+SymbolDumper.h ├── XRMachOLibrary+ObjectiveC.h ├── Methods.h ├── XRSymbolEntry.h ├── FAT │ ├── XRMachOLibrary+FAT.h │ └── XRMachOLibrary+FAT.mm ├── payload.cpp ├── XRMachOLibrary+FileManagement.m ├── statusbar.h ├── TestSwift.swift ├── objc_.mm ├── miscellaneous.h ├── statusbar.c ├── progressbar.h ├── XRMachOLibraryCplusHelpers.h ├── Methods.mm ├── XRMachOLibrary.h ├── dsdump.1 ├── XRMachOLibrary+Disassemble.mm ├── dyld_process_info_internal.h ├── Protocols.mm ├── miscellaneous.mm ├── TaskPath.mm ├── progressbar.c ├── payload.hpp ├── main.m ├── XRMachOLibrary+SymbolDumper.mm ├── DSXRLibrary+PID_Info.m ├── Properties.mm └── XRMachOLibrary+ObjectiveC.mm ├── .gitmodules ├── dsdump.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── lolgrep.xcuserdatad │ │ └── WorkspaceSettings.xcsettings ├── xcuserdata │ ├── derekselander.xcuserdatad │ │ └── xcschemes │ │ │ └── xcschememanagement.plist │ └── lolgrep.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── xcshareddata │ └── xcschemes │ ├── Generate Build.xcscheme │ └── dsdump.xcscheme ├── dsdump.rb ├── .gitignore └── README.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /fix_swift/llvm/Config/abi-breaking.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fix_swift/llvm/Config/llvm-config.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fix_swift/swift/Runtime/CMakeConfig.h: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /media/swift.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DerekSelander/dsdump/HEAD/media/swift.png -------------------------------------------------------------------------------- /media/vmmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DerekSelander/dsdump/HEAD/media/vmmap.png -------------------------------------------------------------------------------- /compiled/dsdump_beta.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DerekSelander/dsdump/HEAD/compiled/dsdump_beta.zip -------------------------------------------------------------------------------- /dsdump/binaries/some_app: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DerekSelander/dsdump/HEAD/dsdump/binaries/some_app -------------------------------------------------------------------------------- /compiled/dsdump_compiled.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DerekSelander/dsdump/HEAD/compiled/dsdump_compiled.zip -------------------------------------------------------------------------------- /dsdump/libswiftDemangling.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DerekSelander/dsdump/HEAD/dsdump/libswiftDemangling.a -------------------------------------------------------------------------------- /dsdump/binaries/arm_hello.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DerekSelander/dsdump/HEAD/dsdump/binaries/arm_hello.out -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "capstone"] 2 | path = capstone 3 | url = https://derekselander@github.com/aquynh/capstone.git 4 | -------------------------------------------------------------------------------- /dsdump.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /dsdump.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /dsdump.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /dsdump/XRMachOLibrary+PID_Info.h: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary+PID_Info.h 3 | // xref 4 | // 5 | // Created by Derek Selander on 5/3/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import "XRMachOLibrary.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface XRMachOLibrary (PID_Info) 14 | 15 | @end 16 | 17 | NS_ASSUME_NONNULL_END 18 | -------------------------------------------------------------------------------- /dsdump/Bind Symbols/XRMachOLibrary+Opcode.h: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary+Opcode.h 3 | // xref 4 | // 5 | // Created by Derek Selander on 4/21/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import "XRMachOLibrary.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | 14 | @interface XRMachOLibrary (Opcode) 15 | - (void)parseDYLDExports; 16 | - (void)parseDYLDOpcodes; 17 | 18 | @end 19 | 20 | 21 | NS_ASSUME_NONNULL_END 22 | -------------------------------------------------------------------------------- /dsdump.rb: -------------------------------------------------------------------------------- 1 | class dsdump < Formula 2 | desc "An improved nm + Objective-C & Swift class-dump" 3 | homepage "https://github.com/DerekSelander/dsdump" 4 | version "0.1.0" 5 | sha256 "83eebd025b43b58a486235e1bec70a3239995be409605e3ff19bdae07adff917" 6 | 7 | 8 | url "https://github.com/DerekSelander/dsdump/blob/master/compiled/dsdump.zip", :using => :curl 9 | 10 | 11 | def install 12 | bin.install "selander/dsdump" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /dsdump/XRMachOLibrary+Disassemble.h: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary+Disassemble.h 3 | // dsdump 4 | // 5 | // Created by Derek Selander on 9/27/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | #import "XRMachOLibrary.h" 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface XRMachOLibrary (Disassemble) 17 | 18 | - (uintptr_t *)resolveMetadataFromCode:(uintptr_t)address; 19 | 20 | @end 21 | 22 | NS_ASSUME_NONNULL_END 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /xref.xcodeproj/xcuserdata/derekselander.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist 2 | /xref.xcodeproj/project.xcworkspace/xcuserdata/derekselander.xcuserdatad/UserInterfaceState.xcuserstate 3 | /xref.xcodeproj/xcuserdata/lolgrep.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist 4 | /xref.xcodeproj/project.xcworkspace/xcuserdata/lolgrep.xcuserdatad/UserInterfaceState.xcuserstate 5 | *.xcuserstate 6 | *.xcbkptlist 7 | Breakpoints_v2.xcbkptlist 8 | /.DS_Store 9 | /dsdump/.DS_Store 10 | .DS_Store 11 | xcuserdata 12 | swift-source 13 | -------------------------------------------------------------------------------- /dsdump/XRMachOLibrary+Swift.h: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary+Swift.h 3 | // xref 4 | // 5 | // Created by Derek Selander on 5/18/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import "XRMachOLibrary.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface XRMachOLibrary (Swift) 14 | 15 | - (void)dumpSwiftTypes; 16 | - (BOOL)preparseSwiftTypes; 17 | - (void)dumpSwiftProtocols; 18 | - (void)preparseSwiftProtocols; 19 | @end 20 | 21 | #define FAST_IS_SWIFT_LEGACY 1 // < 5 22 | #define FAST_IS_SWIFT_STABLE 2 // 5.X 23 | 24 | NS_ASSUME_NONNULL_END 25 | -------------------------------------------------------------------------------- /dsdump/TaskPath.h: -------------------------------------------------------------------------------- 1 | // 2 | // TaskPath.hpp 3 | // xref 4 | // 5 | // Created by Derek Selander on 5/7/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #ifndef TaskPath_hpp 10 | #define TaskPath_hpp 11 | 12 | #include 13 | #include 14 | 15 | //typedef enum _LibraryTaskError { 16 | // LibraryTaskErrorDyldVersion = 0, 17 | //} LibraryTaskError; 18 | 19 | // BOOL FindLibraryInTask(pid_t task, char *search_string, LibraryTaskError* err) ; 20 | void DumpProcessesContainingLibrary(const char *lib_name, uuid_t uuid); 21 | 22 | #endif /* TaskPath_hpp */ 23 | -------------------------------------------------------------------------------- /dsdump.xcodeproj/xcuserdata/derekselander.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | xref.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | A66A9CBA22316CF3006602AB 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /dsdump/XRMachOLibrary_cplus.h: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary_cplus.h 3 | // xref 4 | // 5 | // Created by Derek Selander on 5/21/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #ifndef XRMachOLibrary_cplus_h 10 | #define XRMachOLibrary_cplus_h 11 | 12 | #import "XRMachOLibrary.h" 13 | #import 14 | 15 | // Dealing with the c++ 16 | @interface XRMachOLibrary (Opcode_Private) 17 | @property (nonatomic, assign) std::unordered_map exports; 18 | @end 19 | 20 | @implementation XRMachOLibrary (Opcode_Private) 21 | @dynamic exports; // Defined in XRMachOLibrary.mm 22 | @end 23 | 24 | #endif /* XRMachOLibrary_cplus_h */ 25 | -------------------------------------------------------------------------------- /dsdump/Ripped off/objc-layout.h: -------------------------------------------------------------------------------- 1 | // 2 | // objc-layout.h 3 | // dsdump 4 | // 5 | // Created by Derek Selander on 4/30/20. 6 | // Copyright © 2020 Selander. All rights reserved. 7 | // 8 | 9 | #ifndef objc_layout_h 10 | #define objc_layout_h 11 | 12 | char *scan_ivar_type_for_layout(char *type, long offset, long bits_size, unsigned char *bits, long *next_offset); 13 | char *scan_basic_ivar_type(char *type, long *size, long *alignment, bool *is_reference); 14 | 15 | char *skip_ivar_type_name(char *type); 16 | char *skip_ivar_struct_name(char *type); 17 | 18 | char *scan_ivar_type_for_layout(char *type, long offset, long bits_size, unsigned char *bits, long *next_offset); 19 | #endif /* objc_layout_h */ 20 | -------------------------------------------------------------------------------- /dsdump.xcodeproj/project.xcworkspace/xcuserdata/lolgrep.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildLocationStyle 6 | UseAppPreferences 7 | CustomBuildLocationType 8 | RelativeToDerivedData 9 | DerivedDataLocationStyle 10 | Default 11 | EnabledFullIndexStoreVisibility 12 | 13 | IssueFilterStyle 14 | ShowActiveSchemeOnly 15 | LiveSourceIssuesEnabled 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /dsdump/XRSymbolEntry.m: -------------------------------------------------------------------------------- 1 | // 2 | // XRSymbolEntry.m 3 | // xref 4 | // 5 | // Created by Derek Selander on 5/22/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import "XRSymbolEntry.h" 10 | #import "XRMachOLibrary.h" 11 | 12 | @implementation XRSymbolEntry 13 | 14 | -(instancetype)initWithSymbol:(struct nlist_64 *)symbol machoLibrary:(XRMachOLibrary*)lib { 15 | if (self = [super init]) { 16 | _name = (const char*)&lib.str_symbols[symbol->n_un.n_strx]; 17 | if (strlen(_name) == 0) { 18 | _name = NULL; 19 | } 20 | _address = symbol->n_value; 21 | } 22 | return self; 23 | } 24 | 25 | - (NSString *)debugDescription { 26 | return [NSString stringWithFormat:@"%p, name: %s addr:%p", self, _name, (void*)_address]; 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /dsdump/Protocols.h: -------------------------------------------------------------------------------- 1 | // 2 | // Protocols.hpp 3 | // dsdump 4 | // 5 | // Created by Derek Selander on 4/29/20. 6 | // Copyright © 2020 Selander. All rights reserved. 7 | // 8 | 9 | #ifndef Protocols_hpp 10 | #define Protocols_hpp 11 | 12 | #include 13 | #import "string.h" 14 | #import "XRMachOLibrary+Swift.h" 15 | #import "XRSymbolEntry.h" 16 | #import "objc_.h" 17 | #import "XRMachOLibrary+ObjectiveC.h" 18 | #import "XRMachOLibraryCplusHelpers.h" 19 | #import "XRMachOLibrary+Disassemble.h" 20 | #import "XRMachOLibraryCplusHelpers.h" 21 | 22 | #import "XRMachOLibrary+ObjectiveC.h" 23 | #import "XRMachOLibrary+SymbolDumper.h" 24 | #import "objc_.h" 25 | #import "Methods.h" 26 | #import "Properties.h" 27 | 28 | #endif /* Protocols_hpp */ 29 | 30 | void dumpObjectiveCProtocols(void); 31 | BOOL listProtocolsForObjectiveCClass(swift_class* cls); 32 | -------------------------------------------------------------------------------- /dsdump/Bind Symbols/XRBindSymbol.h: -------------------------------------------------------------------------------- 1 | // 2 | // DSXRObjCClass.h 3 | // xref 4 | // 5 | // Created by Derek Selander on 4/22/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | /// Used for dyld opcode bind mapping 14 | @interface XRBindSymbol : NSObject 15 | 16 | @property (nonatomic, assign) NSNumber *address; 17 | @property (nonatomic, assign) int symidx; 18 | @property (nonatomic, copy) NSString *name; 19 | @property (nonatomic, readonly) NSString *shortName; 20 | @property (nonatomic, assign) uint64_t addend; 21 | @property (nonatomic, assign) uint64_t libOrdinal; 22 | 23 | - (instancetype)initWithAddress:(NSNumber *)address symbol:(NSString *)symbol libord:(uint64_t)ordinal addend:(uint64_t)addend; 24 | 25 | @end 26 | 27 | 28 | NS_ASSUME_NONNULL_END 29 | -------------------------------------------------------------------------------- /dsdump/Properties.h: -------------------------------------------------------------------------------- 1 | // 2 | // Properties.hpp 3 | // dsdump 4 | // 5 | // Created by Derek Selander on 4/28/20. 6 | // Copyright © 2020 Selander. All rights reserved. 7 | // 8 | 9 | #ifndef Properties_hpp 10 | #define Properties_hpp 11 | 12 | #include 13 | #import "string.h" 14 | #import "XRMachOLibrary+Swift.h" 15 | #import "XRSymbolEntry.h" 16 | #import "objc_.h" 17 | #import "XRMachOLibrary+ObjectiveC.h" 18 | #import "XRMachOLibraryCplusHelpers.h" 19 | #import "XRMachOLibrary+Disassemble.h" 20 | #import "XRMachOLibraryCplusHelpers.h" 21 | 22 | #import "XRMachOLibrary+ObjectiveC.h" 23 | #import "XRMachOLibrary+SymbolDumper.h" 24 | #import "objc_.h" 25 | 26 | 27 | void dumpObjCPropertiesWithResolvedAddress(protocol_t* prtl); 28 | void dumpObjCPropertiesWithResolvedAddress(swift_class* cls); 29 | void dumpObjCPropertiesWithResolvedAddress(property_list_t* propertiesList) ; 30 | 31 | #endif /* Properties_hpp */ 32 | -------------------------------------------------------------------------------- /dsdump/XRMachOLibrary+SymbolDumper.h: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary+SymbolDumper.h 3 | // xref 4 | // 5 | // Created by Derek Selander on 4/22/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | #import "XRMachOLibrary.h" 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | OS_ALWAYS_INLINE 18 | char* demangleCPP(char* mangledSym); 19 | 20 | void print_symbol(XRMachOLibrary *object, struct nlist_64 * _Nonnull sym, uintptr_t * _Nullable override_addr); 21 | 22 | #ifdef __cplusplus 23 | } // extern c 24 | #endif 25 | 26 | 27 | @interface XRMachOLibrary (SymbolDumper) 28 | 29 | - (void)dumpSymbols; 30 | - (void)dumpExternalSymbols; 31 | - (XRBindSymbol *)objCSuperClassFromSymbol:(struct nlist_64 * _Nonnull)sym; 32 | 33 | @end 34 | 35 | 36 | 37 | NS_ASSUME_NONNULL_END 38 | 39 | 40 | 41 | #define OBJC_CLASS_LENGTH (strlen("_OBJC_CLASS_$_")) 42 | 43 | 44 | -------------------------------------------------------------------------------- /dsdump/XRMachOLibrary+ObjectiveC.h: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary+ObjectiveC.h 3 | // xref 4 | // 5 | // Created by Derek Selander on 4/29/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import "XRMachOLibrary.h" 10 | 11 | #define FILE_OFFSET_UNKNOWN ((intptr_t)-1) 12 | 13 | NS_ASSUME_NONNULL_BEGIN 14 | 15 | 16 | 17 | 18 | typedef struct { 19 | uint16_t mod_off : 16; 20 | uint16_t mod_len : 16; 21 | uint16_t cls_off : 16; 22 | // uint16_t cls_len : 10; 23 | BOOL success : 1; 24 | } d_offsets; 25 | 26 | // https://github.com/RetVal/objc-runtime/blob/master/runtime/objc-runtime-new.h#L478 27 | #define FAST_IS_SWIFT_LEGACY 1 28 | #define FAST_IS_SWIFT_STABLE 2 29 | 30 | BOOL demangleSwiftName(const char *name, d_offsets *f); 31 | 32 | @interface XRMachOLibrary (ObjectiveC) 33 | 34 | - (void)dumpObjectiveCClasses; 35 | - (void)dumpObjectiveCCategories; 36 | 37 | 38 | 39 | @end 40 | 41 | NS_ASSUME_NONNULL_END 42 | -------------------------------------------------------------------------------- /dsdump/Methods.h: -------------------------------------------------------------------------------- 1 | // 2 | // Methods.hpp 3 | // dsdump 4 | // 5 | // Created by Derek Selander on 4/29/20. 6 | // Copyright © 2020 Selander. All rights reserved. 7 | // 8 | 9 | #ifndef Methods_hpp 10 | #define Methods_hpp 11 | 12 | #import 13 | #import "string.h" 14 | #import "XRMachOLibrary+Swift.h" 15 | #import "XRSymbolEntry.h" 16 | #import "objc_.h" 17 | #import "XRMachOLibrary+ObjectiveC.h" 18 | #import "XRMachOLibraryCplusHelpers.h" 19 | #import "XRMachOLibrary+Disassemble.h" 20 | #import "XRMachOLibraryCplusHelpers.h" 21 | 22 | #import "XRMachOLibrary+ObjectiveC.h" 23 | #import "XRMachOLibrary+SymbolDumper.h" 24 | #import "objc_.h" 25 | 26 | #endif /* Methods_hpp */ 27 | 28 | /// iOS 14 adds a __TEXT.__objc_methlist for int32_t offsets to methods, doesn't apply to protocols 29 | void dumpObjectiveCMethods(method_list_t* methodList, const char *name, bool isMeta, bool isProtocol = false, const char * overrideColor = NULL); 30 | 31 | const char* translate_method_type_to_string(char *typeString); 32 | -------------------------------------------------------------------------------- /dsdump/XRSymbolEntry.h: -------------------------------------------------------------------------------- 1 | // 2 | // XRSymbolEntry.h 3 | // xref 4 | // 5 | // Created by Derek Selander on 5/22/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | @class XRMachOLibrary; 14 | 15 | @protocol TestProto 16 | @property (nonatomic, assign) uint64_t address; 17 | 18 | @end 19 | 20 | // Used to reference symbols by address, originates from the symbol table 21 | @interface XRSymbolEntry : NSObject 22 | 23 | @property (nonatomic, assign) const char* name; 24 | @property (nonatomic, assign) uint64_t address; 25 | @property (nonatomic, assign) uint64_t flags; 26 | @property (nonatomic, assign) uint64_t other; 27 | @property (nonatomic, assign) const char* importName; 28 | @property (nonatomic, assign) BOOL visited; 29 | 30 | - (instancetype)initWithSymbol:(struct nlist_64 *)symbol machoLibrary:(XRMachOLibrary*)lib; 31 | 32 | @end 33 | 34 | NS_ASSUME_NONNULL_END 35 | -------------------------------------------------------------------------------- /dsdump/Bind Symbols/XRBindSymbol.m: -------------------------------------------------------------------------------- 1 | // 2 | // DSXRObjCClass.m 3 | // xref 4 | // 5 | // Created by Derek Selander on 4/22/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import "XRBindSymbol.h" 10 | #import "XRMachOLibrary+SymbolDumper.h" 11 | 12 | @implementation XRBindSymbol 13 | 14 | - (instancetype)initWithAddress:(NSNumber *)address symbol:(NSString *)symbol libord:(uint64_t)ordinal addend:(uint64_t)addend { 15 | if (self = [super init]) { 16 | self.name = symbol; 17 | self.address = address; 18 | self.libOrdinal = ordinal; 19 | self.addend = addend; 20 | } 21 | return self; 22 | } 23 | 24 | - (NSString *)shortName { 25 | NSInteger index = [self.name rangeOfString:@"_$_"].location; 26 | if (index != NSNotFound) { 27 | return [self.name substringFromIndex:index + 3]; 28 | } 29 | return self.name; 30 | } 31 | 32 | - (NSString *)description { 33 | return [NSString stringWithFormat:@"%p %@: <%p>", self.address.pointerValue, _name, self]; 34 | } 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /dsdump/FAT/XRMachOLibrary+FAT.h: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary+FAT.h 3 | // xref 4 | // 5 | // Created by Derek Selander on 5/1/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import "XRMachOLibrary.h" 10 | 11 | NS_ASSUME_NONNULL_BEGIN 12 | 13 | @interface XRMachOLibrary (FAT) 14 | 15 | /// Architecture can't be found 16 | #define FAT_OFFSET_BAD_NAME -1 17 | 18 | /// Convenience for dealing with archs and endianness 19 | #define FIX_ENDIAN(name) (((fat->nfat_arch) < (htonl(fat->nfat_arch))) ? (name) : (htonl(name))) 20 | 21 | /// Used to display info if no arch is present and FAT 22 | -(NSString*)printAllArchitectures; 23 | 24 | /// Get offset for arch, returns FAT_OFFSET_BAD_NAME if can't find it 25 | - (intptr_t)offsetForArchitecture:(NSString *)architecture size:(size_t* _Nullable )size; 26 | 27 | /// Default arch name 28 | - (NSString *)defaultArchitectureName; 29 | 30 | /// Get default offset 31 | - (intptr_t)offsetForDefaultArchitecture; 32 | 33 | /// Return name for cpu/subcpu type 34 | -(NSString *)nameForCPU:(cpu_type_t)cputype subtype:(cpu_subtype_t)subtype; 35 | @end 36 | 37 | NS_ASSUME_NONNULL_END 38 | -------------------------------------------------------------------------------- /dsdump.xcodeproj/xcuserdata/lolgrep.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Generate Build.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 2 11 | 12 | Generate Manpage.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 6 16 | 17 | dsdump.xcscheme_^#shared#^_ 18 | 19 | orderHint 20 | 0 21 | 22 | 23 | SuppressBuildableAutocreation 24 | 25 | 3F50D7802315765B0017A20A 26 | 27 | primary 28 | 29 | 30 | 3FDE7B992314BF6800715685 31 | 32 | primary 33 | 34 | 35 | A66A9CBA22316CF3006602AB 36 | 37 | primary 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /dsdump/payload.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // payload.cpp 3 | // dsdump 4 | // 5 | // Created by Derek Selander on 6/10/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #include "payload.hpp" 10 | 11 | 12 | // Likely from LLVM cmake 13 | namespace llvm { 14 | int DisableABIBreakingChecks = 0; 15 | }; 16 | 17 | // Holds the executable data 18 | namespace payload { 19 | uint8_t *data; 20 | uintptr_t size; 21 | std::vector sections; 22 | uintptr_t offset; 23 | std::map sectionsDict; 24 | std::unordered_set filters; 25 | 26 | uintptr_t Offset2Virtual(uintptr_t f) { 27 | auto r = ARM64E_POINTER(f); 28 | for (auto &sec : payload::sections) { 29 | if (sec->offset <= (r) && (r) < (sec->offset + sec->size)) { 30 | return r + sec->addr - sec->offset; 31 | } 32 | } 33 | 34 | printf( "WARNING: couldn't find offset 0x%lx in binary!\n", r); 35 | return 0; 36 | } 37 | 38 | uintptr_t Virtual2Offset(uintptr_t f) { 39 | auto r = ARM64E_POINTER(f); 40 | for (auto i = 0; i < payload::sections.size(); i++) { 41 | struct section_64 *sec = payload::sections[i]; 42 | if (sec->offset <= (r) && (r) < (sec->offset + sec->size)) { 43 | return r + sec->addr - sec->offset; 44 | } 45 | } 46 | 47 | printf( "WARNING: couldn't find offset 0x%lx in binary!\n", r); 48 | return 0; 49 | } 50 | 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /dsdump/XRMachOLibrary+FileManagement.m: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary+FileManagement.m 3 | // xref 4 | // 5 | // Created by Derek Selander on 4/10/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import "XRMachOLibrary.h" 10 | #import 11 | 12 | //#define PAYLOAD_FILE_SIZE(c) (sizeof(long) + (sizeof(cs_insn) * (c)) + (sizeof(cs_detail) * (c))) 13 | 14 | 15 | @implementation XRMachOLibrary (FileManagement) 16 | 17 | 18 | //////////////////////////////////////////////////////////////// 19 | // Public methods 20 | //////////////////////////////////////////////////////////////// 21 | 22 | 23 | - (NSString *)translateUUID { 24 | char uuid_output[37]; 25 | for (int cur = 0, i = 0; i < 16; i++) { 26 | char c = self.uuid_cmd->uuid[i]; 27 | char lower = c & 0xf; 28 | char upper = (c & 0xf0) >> 4; 29 | 30 | if (upper < 10) { 31 | upper += 48; 32 | } else { 33 | upper += 55; 34 | } 35 | 36 | if (lower < 10) { 37 | lower += 48; 38 | } else { 39 | lower += 55; 40 | } 41 | uuid_output[cur++] = upper; 42 | uuid_output[cur++] = lower; 43 | if (i == 3 || i == 5 || i == 7 || i == 9) { 44 | uuid_output[cur++] = '-'; 45 | } 46 | uuid_output[cur] = '\x00'; 47 | } 48 | 49 | return [NSString stringWithUTF8String:uuid_output]; 50 | } 51 | 52 | - (NSString *)analysisSavePath { 53 | return [NSString stringWithFormat:@"/tmp/%@.%@", [self.path lastPathComponent], [self translateUUID]]; 54 | } 55 | 56 | 57 | @end 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /dsdump/statusbar.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \author Trevor Fountain 4 | * \author Johannes Buchner 5 | * \author Erik Garrison 6 | * \date 2010-2014 7 | * \copyright BSD 3-Clause 8 | * 9 | * statusbar -- a C class (by convention) for displaying indefinite progress 10 | * on the command line (to stderr). 11 | */ 12 | 13 | #ifndef STATUSBAR_H 14 | #define STATUSBAR_H 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | /** 26 | * Statusbar data structure (do not modify or create directly) 27 | */ 28 | typedef struct _statusbar_t 29 | { 30 | unsigned int start_time; 31 | const char *label; 32 | int format_index; 33 | int format_length; 34 | char *format; 35 | int last_printed; 36 | } statusbar; 37 | 38 | /// Create a new statusbar with the specified label and format string 39 | statusbar *statusbar_new_with_format(const char *label, const char *format); 40 | 41 | /// Create a new statusbar with the specified label 42 | statusbar *statusbar_new(const char *label); 43 | 44 | /// Free an existing progress bar. Don't call this directly; call *statusbar_finish* instead. 45 | void statusbar_free(statusbar *bar); 46 | 47 | /// Increment the given statusbar. 48 | void statusbar_inc(statusbar *bar); 49 | 50 | /// Finalize (and free!) a statusbar. Call this when you're done. 51 | void statusbar_finish(statusbar *bar); 52 | 53 | /// Draw a statusbar to the screen. Don't call this directly, 54 | /// as it's called internally by *statusbar_inc*. 55 | void statusbar_draw(statusbar *bar); 56 | 57 | #ifdef __cplusplus 58 | } 59 | #endif 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /dsdump/TestSwift.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestSwift.swift 3 | // dsdump 4 | // 5 | // Created by Derek Selander on 8/26/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Cocoa 11 | // swiftc /Users/lolgrep/code/dsdump/dsdump/TestSwift.swift -sdk `xcrun --show-sdk-path -sdk iphoneos` -target arm64e-apple-ios99.99.99.99 -o /tmp/TestSwift 12 | protocol AProtocol { 13 | func yay() 14 | // func hey() 15 | } 16 | 17 | enum FFF { 18 | case yo 19 | } 20 | 21 | public class SomeVC : NSViewController, AProtocol { 22 | public var someview : NSView! 23 | let h : SomeVC? = nil 24 | let jj : Int32 = 3 25 | var someStr : NSString = "yay some string" 26 | override public func viewDidLoad() { 27 | super.viewDidLoad() 28 | self.someview = NSView() 29 | 30 | } 31 | 32 | override init(nibName nibNameOrNil: NSNib.Name?, bundle nibBundleOrNil: Bundle?) { 33 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 34 | } 35 | 36 | required init?(coder: NSCoder) { 37 | fatalError("init(coder:) has not been implemented") 38 | } 39 | 40 | public func yay() { 41 | 42 | } 43 | } 44 | 45 | 46 | /* 47 | private class PrivateClass { 48 | 49 | } 50 | 51 | public enum WootYeah { 52 | case some 53 | case test 54 | } 55 | 56 | @_cdecl("do_stuff") 57 | public func meh() { 58 | let a = SomeTest() 59 | print("\(a)") 60 | 61 | } 62 | 63 | public class SomeTest { 64 | // var blah : String! 65 | var numa : Int = 0 66 | var numb : Int = 2 67 | var numc : Int = 4 68 | 69 | func somefunc() {} 70 | public func yay() { 71 | print("do stuff") 72 | 73 | } 74 | func hey() { } 75 | 76 | } 77 | 78 | extension SomeTest: AProtocol { 79 | 80 | func extensionTest() { 81 | print("\(#function)") 82 | } 83 | } 84 | */ 85 | -------------------------------------------------------------------------------- /dsdump/objc_.mm: -------------------------------------------------------------------------------- 1 | // 2 | // objc_.cpp 3 | // dsdump 4 | // 5 | // Created by Derek Selander on 7/4/20. 6 | // Copyright © 2020 Selander. All rights reserved. 7 | // 8 | #import 9 | #import "objc_.h" 10 | 11 | static bool useRelativeMethUsage(void) { 12 | static dispatch_once_t onceToken; 13 | static bool useRelativeMeth = false; 14 | dispatch_once(&onceToken, ^{ 15 | auto section = payload::sectionsDict["__TEXT.__objc_methlist"]; 16 | if (section) { 17 | useRelativeMeth = true; 18 | } 19 | }); 20 | return useRelativeMeth; 21 | } 22 | 23 | payload::LoadToDiskTranslator* method_t::getName() { 24 | 25 | if (useRelativeMethUsage()) { 26 | auto typeOffsets = payload::DiskWrapper::Cast(this->disk()); 27 | auto resolvedAddress = ((intptr_t)typeOffsets[0].disk() + *typeOffsets[0].disk()); 28 | 29 | auto cur = payload::CastToDisk(resolvedAddress); 30 | return cur->disk()->name; 31 | } 32 | return this->name; 33 | } 34 | payload::LoadToDiskTranslator* method_t::getTypes() { 35 | if (useRelativeMethUsage()) { 36 | auto typeOffsets = payload::DiskWrapper::Cast(this->disk()); 37 | auto resolvedAddress = ((intptr_t)typeOffsets[1].disk() + *typeOffsets[1].disk()); 38 | auto cur = payload::CastToDisk(resolvedAddress); 39 | return cur; 40 | } 41 | return this->types; 42 | } 43 | payload::LoadToDiskTranslator* method_t::getImp() { 44 | if (useRelativeMethUsage()) { 45 | auto typeOffsets = payload::DiskWrapper::Cast(this->disk()); 46 | 47 | auto resolvedAddress = ((intptr_t)typeOffsets[2].disk() + *typeOffsets[2].disk()); 48 | 49 | auto impPointer = reinterpret_cast*>(resolvedAddress); 50 | return impPointer; 51 | } 52 | return this->imp; 53 | } 54 | 55 | method_t* method_list::GetMethod(int i, bool isProtocol) { 56 | auto startAddress = &this->first_method; 57 | if (!isProtocol && useRelativeMethUsage()) { 58 | 59 | // TODO This is for 64 bit only, probably would never get to 32 bit... : ] 60 | auto addr = reinterpret_cast(startAddress) + (i * (sizeof(int32_t) * 3)); 61 | return reinterpret_cast(addr); 62 | } 63 | return &startAddress[i]; 64 | } 65 | -------------------------------------------------------------------------------- /dsdump/miscellaneous.h: -------------------------------------------------------------------------------- 1 | // 2 | // miscellaneous.h 3 | // xref 4 | // 5 | // Created by Derek Selander on 3/7/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | 10 | #ifndef MISCELLANEOUS_H 11 | #define MISCELLANEOUS_H 12 | 13 | #import 14 | #import 15 | #import 16 | 17 | #ifdef __cplusplus 18 | 19 | extern "C" { 20 | #endif 21 | 22 | /// Usage deets 23 | void print_manpage(void); 24 | void print_usage(void); 25 | 26 | __attribute__((weak)) unsigned char __manpage_deets; 27 | /******************************************************************************** 28 | /// Globals for exploring multiple references 29 | ********************************************************************************/ 30 | 31 | typedef NS_OPTIONS(NSUInteger, DSCOLOR) { 32 | DSCOLOR_CYAN, 33 | DSCOLOR_YELLOW, 34 | DSCOLOR_MAGENTA, 35 | DSCOLOR_RED, 36 | DSCOLOR_PURPLE, 37 | DSCOLOR_BLUE, 38 | DSCOLOR_GRAY, 39 | DSCOLOR_GREEN, 40 | DSCOLOR_DARK_GREEN, 41 | DSCOLOR_BOLD, 42 | DSCOLOR_CYAN_UNDERLINE, 43 | DSCOLOR_PURPLE_BOLD, 44 | DSCOLOR_CYAN_LIGHT, 45 | DSCOLOR_YELLOW_LIGHT, 46 | DSCOLOR_STRONG_RED, 47 | DSCOLOR_LIGHT_BLUE 48 | }; 49 | char const* dcolor(DSCOLOR c); 50 | 51 | /// Ends the color option if the DSCOLOR env var is set 52 | char const* color_end(void); 53 | 54 | /// Different levels to logging, opted for a "codesign -vvvv" style 55 | #define VERBOSE_NONE 0 56 | #define VERBOSE_1 1 57 | #define VERBOSE_2 2 58 | #define VERBOSE_3 3 59 | #define VERBOSE_4 4 60 | #define VERBOSE_5 5 61 | 62 | typedef struct { 63 | int verbose; 64 | int undefined; 65 | int objectiveC_mode; 66 | int swift_mode; 67 | int symbol_mode; 68 | int all_symbols; 69 | int defined; 70 | int color; 71 | int use_regex; 72 | int external; 73 | int analyze; 74 | uintptr_t file_offset; 75 | uintptr_t virtual_address; 76 | int virtual_address_count; 77 | int library; 78 | char * arch; 79 | int debug; 80 | int help; 81 | int opcodes; 82 | int demangle_mode; 83 | } xref_options_t; 84 | 85 | extern xref_options_t xref_options; 86 | 87 | 88 | /// Read unsigned leb128 coding 89 | const uint8_t *r_uleb128_decode(uint8_t *data, int *datalen, uint64_t *v); 90 | 91 | /// Read signed leb128 coding 92 | const uintptr_t r_sleb128_decode(uint8_t *byte, uintptr_t* shift, uint64_t *v); 93 | 94 | /// Used to check for class filtering 95 | BOOL ContainsFilteredWords(const char *word); 96 | 97 | /// Used to input filters for checking for filtering 98 | void AddFilter(char * filter); 99 | 100 | /// Only print when DEBUG env var is set 101 | void warn_debug(const char *format, ...); 102 | 103 | #ifdef __cplusplus 104 | } 105 | #endif 106 | 107 | /// sizeof pointer, 64 bit only 108 | #define PTR_SIZE sizeof(void*) 109 | 110 | /// Only print if DEBUG flag is set 111 | #define DEBUG_PRINT(fmt, args...) if (xref_options.opcodes) { printf(fmt, ## args); } 112 | 113 | #endif // MISCELLANEOUS_H 114 | -------------------------------------------------------------------------------- /dsdump/statusbar.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \author Trevor Fountain 4 | * \author Johannes Buchner 5 | * \author Erik Garrison 6 | * \date 2010-2014 7 | * \copyright BSD 3-Clause 8 | * 9 | * statusbar -- a C class (by convention) for displaying progress 10 | * on the command line (to stderr). 11 | */ 12 | #include "statusbar.h" 13 | 14 | statusbar *statusbar_new_with_format(const char *label, const char *format) 15 | { 16 | statusbar *new = malloc(sizeof(statusbar)); 17 | if(new == NULL) { 18 | return NULL; 19 | } 20 | 21 | new->label = label; 22 | new->start_time = (unsigned int)time(0); 23 | new->format_length = (unsigned int)strlen(format); 24 | new->format = malloc( sizeof(char) * (new->format_length + 1) ); 25 | if(new->format == NULL) { 26 | free(new); 27 | return NULL; 28 | } 29 | 30 | strncpy(new->format, format, new->format_length); 31 | new->format_index = 0; 32 | new->last_printed = 0; 33 | 34 | return new; 35 | } 36 | 37 | statusbar *statusbar_new(const char *label) 38 | { 39 | return statusbar_new_with_format(label, "-\\|/"); 40 | } 41 | 42 | void statusbar_free(statusbar *bar) 43 | { 44 | // We malloc'd a string, so let's be sure to free it... 45 | free(bar->format); 46 | // ...before we free the struct itself. 47 | free(bar); 48 | 49 | return; 50 | } 51 | 52 | void statusbar_inc(statusbar *bar) 53 | { 54 | bar->format_index++; 55 | if (bar->format_index >= bar->format_length) { 56 | bar->format_index = 0; 57 | } 58 | statusbar_draw(bar); 59 | 60 | return; 61 | } 62 | 63 | void statusbar_draw(statusbar *bar) 64 | { 65 | // Erase the last draw. If anything else has been printed to stderr, 66 | // things are going to look mighty interesting... 67 | for(int i=0; i < bar->last_printed; i++) { 68 | fprintf(stderr,"\b"); 69 | } 70 | 71 | fprintf( 72 | stderr, 73 | "%s: %c%n", 74 | bar->label, 75 | bar->format[bar->format_index], 76 | &(bar->last_printed) 77 | ); 78 | 79 | return; 80 | } 81 | 82 | void statusbar_finish(statusbar *bar) 83 | { 84 | // Draw one more time, with the actual time to completion. 85 | unsigned int offset = (unsigned int)(time(0) - (bar->start_time)); 86 | 87 | // Convert the time to display into HHH:MM:SS 88 | unsigned int h = offset/3600; 89 | offset -= h*3600; 90 | unsigned int m = offset/60; 91 | offset -= m*60; 92 | unsigned int s = offset; 93 | 94 | // Erase the last draw 95 | for(int i=0; i < bar->last_printed; i++) { 96 | fprintf(stderr,"\b"); 97 | } 98 | 99 | // Calculate number of spaces for right-justified time to completion 100 | fprintf(stderr,"%s: %3d:%02d:%02d%n",bar->label,h,m,s,&(bar->last_printed)); 101 | for(int i=0; i < bar->last_printed; i++) { 102 | fprintf(stderr,"\b"); 103 | } 104 | 105 | // Print right-justified 106 | fprintf(stderr,"%s: ",bar->label); 107 | for(int i=0; i < (80 - (bar->last_printed)); i++) { 108 | fprintf(stderr," "); 109 | } 110 | fprintf(stderr,"%3d:%02d:%02d\n",h,m,s); 111 | 112 | // We've finished with this statusbar, so go ahead and free it. 113 | statusbar_free(bar); 114 | 115 | return; 116 | } 117 | -------------------------------------------------------------------------------- /dsdump.xcodeproj/xcshareddata/xcschemes/Generate Build.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /dsdump/progressbar.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \author Trevor Fountain 4 | * \author Johannes Buchner 5 | * \author Erik Garrison 6 | * \date 2010-2014 7 | * \copyright BSD 3-Clause 8 | * 9 | * progressbar -- a C class (by convention) for displaying progress 10 | * on the command line (to stderr). 11 | */ 12 | 13 | #ifndef PROGRESSBAR_H 14 | #define PROGRESSBAR_H 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | /** 26 | * Progressbar data structure (do not modify or create directly) 27 | */ 28 | typedef struct _progressbar_t 29 | { 30 | /// maximum value 31 | unsigned long max; 32 | /// current value 33 | unsigned long value; 34 | 35 | /// time progressbar was started 36 | time_t start; 37 | 38 | /// label 39 | const char *label; 40 | 41 | /// characters for the beginning, filling and end of the 42 | /// progressbar. E.g. |### | has |#| 43 | struct { 44 | char begin; 45 | char fill; 46 | char end; 47 | } format; 48 | } progressbar; 49 | 50 | /// Create a new progressbar with the specified label and number of steps. 51 | /// 52 | /// @param label The label that will prefix the progressbar. 53 | /// @param max The number of times the progressbar must be incremented before it is considered complete, 54 | /// or, in other words, the number of tasks that this progressbar is tracking. 55 | /// 56 | /// @return A progressbar configured with the provided arguments. Note that the user is responsible for disposing 57 | /// of the progressbar via progressbar_finish when finished with the object. 58 | progressbar *progressbar_new(const char *label, unsigned long max); 59 | 60 | /// Create a new progressbar with the specified label, number of steps, and format string. 61 | /// 62 | /// @param label The label that will prefix the progressbar. 63 | /// @param max The number of times the progressbar must be incremented before it is considered complete, 64 | /// or, in other words, the number of tasks that this progressbar is tracking. 65 | /// @param format The format of the progressbar. The string provided must be three characters, and it will 66 | /// be interpretted with the first character as the left border of the bar, the second 67 | /// character of the bar and the third character as the right border of the bar. For example, 68 | /// "<->" would result in a bar formatted like "<------ >". 69 | /// 70 | /// @return A progressbar configured with the provided arguments. Note that the user is responsible for disposing 71 | /// of the progressbar via progressbar_finish when finished with the object. 72 | progressbar *progressbar_new_with_format(const char *label, unsigned long max, const char *format); 73 | 74 | /// Free an existing progress bar. Don't call this directly; call *progressbar_finish* instead. 75 | void progressbar_free(progressbar *bar); 76 | 77 | /// Increment the given progressbar. Don't increment past the initialized # of steps, though. 78 | void progressbar_inc(progressbar *bar); 79 | 80 | /// Set the current status on the given progressbar. 81 | void progressbar_update(progressbar *bar, unsigned long value); 82 | 83 | /// Set the label of the progressbar. Note that no rendering is done. The label is simply set so that the next 84 | /// rendering will use the new label. To immediately see the new label, call progressbar_draw. 85 | /// Does not update display or copy the label 86 | void progressbar_update_label(progressbar *bar, const char *label); 87 | 88 | /// Finalize (and free!) a progressbar. Call this when you're done, or if you break out 89 | /// partway through. 90 | void progressbar_finish(progressbar *bar); 91 | 92 | #ifdef __cplusplus 93 | } 94 | #endif 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /dsdump/XRMachOLibraryCplusHelpers.h: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibraryCplusHelpers.h 3 | // dsdump 4 | // 5 | // Created by Derek Selander on 6/4/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #ifndef XRMachOLibraryCplusHelpers_h 10 | #define XRMachOLibraryCplusHelpers_h 11 | 12 | #import "XRMachOLibrary.h" 13 | #import "payload.hpp" 14 | 15 | #pragma clang diagnostic push 16 | #pragma clang diagnostic ignored "-Weverything" 17 | 18 | //#define protected public 19 | //#define private public 20 | //#define class struct 21 | 22 | #import "swift/Demangling/Demangler.h" 23 | 24 | //#undef protected 25 | //#undef private 26 | //#undef class 27 | 28 | 29 | #pragma clang diagnostic pop 30 | 31 | 32 | namespace dshelpers { 33 | extern swift::Demangle::DemangleOptions simplifiedOptions; 34 | extern Context context; 35 | 36 | const char *simple_demangle(const char *mangled, std::string &strout_ref, swift::Demangle::DemangleOptions options = dshelpers::simplifiedOptions); 37 | bool canDemangle(StringRef mangled); 38 | const char *simple_demangle(char *mangled, std::string &strout_ref, swift::Demangle::DemangleOptions options = dshelpers::simplifiedOptions); 39 | const char *simple_demangle(StringRef mangled, std::string &strout_ref, swift::Demangle::DemangleOptions options = dshelpers::simplifiedOptions); 40 | 41 | // Compact mode: display module names or implicit self types in addition to demangled names 42 | const char *compact_demangle(char *mangled, std::string &strout_ref); 43 | 44 | // const char *simple_type(StringRef type); 45 | const char *simple_type(StringRef type, std::string &strout_ref); 46 | const char *simple_type(char* type, std::string &strout_ref); 47 | const char *simple_type(const char* type, std::string &strout_ref); 48 | 49 | 50 | template 51 | T LoadToOffsetDeref(XRMachOLibrary *library, T* t) { 52 | uintptr_t loadAddress = reinterpret_cast(t); 53 | uintptr_t fileOff = [library translateLoadAddressToFileOffset:loadAddress useFatOffset:YES]; 54 | T retT = *reinterpret_cast(&payload::data[fileOff]); 55 | return retT; 56 | } 57 | 58 | template 59 | T LoadToOffset(XRMachOLibrary *library, T t) { 60 | uintptr_t loadAddress = *reinterpret_cast(&t); // TODO find a better way than this... 61 | uintptr_t fileOff = [library translateLoadAddressToFileOffset:(loadAddress) useFatOffset:YES]; 62 | T retT = reinterpret_cast(&payload::data[fileOff]); 63 | return retT; 64 | } 65 | 66 | template 67 | T OffsetToLoad(XRMachOLibrary *library, T t) { 68 | uintptr_t offset = *reinterpret_cast(&t); 69 | uintptr_t loadAddress = [library translateOffsetToLoadAddress:offset - (uintptr_t)&payload::data[0]]; 70 | T retT = *reinterpret_cast(&loadAddress); 71 | return retT; 72 | } 73 | 74 | 75 | 76 | 77 | // template (t); 80 | // 81 | // uintptr_t fileOff = [library translateLoadAddressToFileOffset:loadAddress useFatOffset:YES]; 82 | // T retT = reinterpret_cast(&library.data[fileOff]); 83 | // return retT; 84 | // } 85 | 86 | // ValueTy diskOff(uint8_t *data) { 87 | // DAT 88 | } 89 | 90 | 91 | 92 | #define TODISK(addr) dshelpers::LoadToOffset(self, addr) 93 | 94 | #define TODISKDEREF(addr) dshelpers::LoadToOffsetDeref(self, addr) 95 | 96 | #define FROMDISK(addr) dshelpers::OffsetToLoad(self, addr) 97 | 98 | //#define FROM(addr) dshelpers::LoadToOffsetDeref(self, addr) 99 | 100 | #endif /* XRMachOLibraryCplusHelpers_h */ 101 | -------------------------------------------------------------------------------- /dsdump/Methods.mm: -------------------------------------------------------------------------------- 1 | // 2 | // Methods.cpp 3 | // dsdump 4 | // 5 | // Created by Derek Selander on 4/29/20. 6 | // Copyright © 2020 Selander. All rights reserved. 7 | // 8 | 9 | #include "Methods.h" 10 | char * 11 | encoding_copyArgumentType(const char *t, unsigned int index); 12 | 13 | char * 14 | encoding_copyReturnType(const char *t); 15 | 16 | void 17 | encoding_getArgumentType(const char *t, unsigned int index, 18 | char *dst, size_t dst_len); 19 | 20 | void 21 | encoding_getReturnType(const char *t, char *dst, size_t dst_len); 22 | 23 | unsigned int 24 | encoding_getArgumentInfo(const char *typedesc, unsigned int arg, 25 | const char **type, int *offset); 26 | 27 | unsigned 28 | encoding_getSizeOfArguments(const char *typedesc); 29 | 30 | unsigned int 31 | encoding_getNumberOfArguments(const char *typedesc); 32 | 33 | extern NSDictionary *blacklistedSelectors; 34 | 35 | void dumpObjectiveCMethods(method_list_t* methodList, const char *name, bool isMeta, bool isProtocol, const char * overrideColor) { 36 | 37 | if (methodList == nullptr) { 38 | return; 39 | } 40 | 41 | auto methodListDisk = methodList->disk(); 42 | auto count = methodListDisk->count; 43 | if (xref_options.verbose > VERBOSE_2) { 44 | printf(" %s// %s methods%s\n", dcolor(DSCOLOR_GRAY), isMeta ? "class" : "instance", color_end()); 45 | } 46 | 47 | for (int i = 0; i < count; i++) { 48 | auto method = methodListDisk->GetMethod(i, isProtocol); //&methods[i]; 49 | 50 | auto methodName = isProtocol ? method->name->disk() : method->getName()->disk(); 51 | if (blacklistedSelectors[[NSString stringWithUTF8String:methodName]]) { 52 | continue; 53 | } 54 | 55 | uintptr_t methodAddress = isProtocol ? method->imp->strip_PAC() : (uintptr_t)method->getImp()->load(); 56 | auto types = isProtocol ? method->types->disk() : method->getTypes()->disk(); 57 | auto numArguments = encoding_getNumberOfArguments(types); 58 | 59 | char path[1024]; 60 | encoding_getReturnType(types, path, 1024); 61 | 62 | putchar(' '); 63 | if (xref_options.verbose > VERBOSE_2 && !isProtocol) { 64 | printf(" %s0x%011lx%s ", overrideColor ? overrideColor: dcolor(DSCOLOR_GRAY), (unsigned long)methodAddress, color_end()); 65 | } 66 | 67 | if (xref_options.verbose <= VERBOSE_4) { 68 | printf("%s%c[%s %s]%s\n", overrideColor? overrideColor : dcolor(DSCOLOR_BOLD), "-+"[isMeta ? 1 : 0], name, methodName, color_end()); 69 | continue; 70 | } 71 | printf("%c(%s%s%s)", "-+"[isMeta ? 1 : 0], overrideColor? overrideColor : dcolor(DSCOLOR_BOLD), translate_method_type_to_string(path), color_end()); 72 | 73 | // if (isProtocol) { 74 | // printf("\t%s%c[%s %s\n", dcolor(DSCOLOR_BOLD), "-+"[isMeta ? 1 : 0], name, color_end()); 75 | // } else { 76 | // printf("%s%c[%s %s]%s\n", dcolor(DSCOLOR_BOLD), "-+"[isMeta ? 1 : 0], name, methodName, color_end()); 77 | // } 78 | long index = 0; // (int)(h - methodName); 79 | auto len = strlen(methodName); 80 | for (int i = 0; i < numArguments && index < len; i++) { 81 | auto found = strchr(&methodName[index], ':'); 82 | if (!found) { // hit end of str or no args 83 | printf("%s%s%s", overrideColor ? overrideColor : dcolor(DSCOLOR_BOLD), &methodName[index], color_end()); 84 | break; 85 | } 86 | found++; 87 | auto cur = (found - &methodName[index]); 88 | char dest[1024] = {}; 89 | encoding_getArgumentType(types, i, dest, 1024); 90 | printf("%s%.*s(%s)%sarg%d", overrideColor ? overrideColor : dcolor(DSCOLOR_BOLD), (int)cur, &methodName[index], translate_method_type_to_string(dest), color_end(), i+1); 91 | index = (found - methodName); 92 | if (i < numArguments - 1) { 93 | putchar(' '); 94 | } 95 | } 96 | putchar('\n'); 97 | } 98 | 99 | if (xref_options.verbose > VERBOSE_2) { 100 | putchar('\n'); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /dsdump/XRMachOLibrary.h: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary.h 3 | // xref 4 | // 5 | // Created by Derek Selander on 3/7/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | 10 | #import "miscellaneous.h" 11 | #import "XRMachOLibrary.h" 12 | #import "XRBindSymbol.h" 13 | 14 | #import 15 | #import 16 | #import 17 | #import 18 | 19 | #define DATABUF(offset) ((void*)&payload::data[(offset)]) 20 | 21 | NS_ASSUME_NONNULL_BEGIN 22 | 23 | extern NSMutableSet *pathsSet; 24 | extern NSMutableSet *exploredSet; 25 | extern NSMutableSet *rpathSet; 26 | 27 | @class XRSymbolEntry; 28 | 29 | /// Deal with 32/64 in one value 30 | typedef union { 31 | struct mach_header_64 h64; 32 | struct mach_header h; 33 | } macho_generic_header; 34 | 35 | 36 | typedef struct { 37 | long count; 38 | uint32_t *indirect_sym; 39 | } indirect_symbols_t; 40 | 41 | // An authenticated pointer is: 42 | typedef struct { 43 | // { 44 | int32_t addend; 45 | uint16_t diversityData; 46 | uint16_t hasAddressDiversity : 1; 47 | uint16_t key : 2; 48 | uint16_t zeroes : 11; 49 | uint16_t zero : 1; 50 | uint16_t authenticated : 1; 51 | } PACPointer; 52 | 53 | #ifdef __cplusplus 54 | extern "C" { 55 | #endif 56 | 57 | 58 | #ifdef __cplusplus 59 | } 60 | #endif 61 | 62 | 63 | @interface XRMachOLibrary : NSObject { 64 | size_t _instructions_count; 65 | } 66 | 67 | /// Library dependencies 68 | @property (nonatomic, strong) NSMutableArray *depdencies; 69 | @property (nonatomic, copy) NSString *path; 70 | 71 | 72 | 73 | /// File descriptor 74 | @property (nonatomic, assign) int fd; 75 | 76 | 77 | @property (nonatomic, assign) struct mach_header_64* header; 78 | 79 | @property (nonatomic, strong) NSMutableDictionary *stringObjCDictionary; 80 | @property (nonatomic, strong) NSMutableDictionary *addressObjCDictionary; 81 | @property (nonatomic, strong) NSMutableArray * threadedHolder; 82 | 83 | @property (nonatomic, strong) NSMutableDictionary *addressSymbolDictionary; 84 | @property (nonatomic, strong) NSMutableDictionary *externalObjectiveClassesDict; 85 | 86 | /// The initial MachO command to dictate the file, other ivars will reference offsets of this 87 | //@property (nonatomic, assign) void *load_cmd_buffer; 88 | 89 | 90 | /// Offsets into each load command struct section 91 | @property (nonatomic, strong) NSMutableArray * sectionCommandsArray; 92 | @property (nonatomic, strong) NSMutableArray * segmentCommandsArray; 93 | 94 | @property (nonatomic, strong) NSMutableDictionary * segmentCommandsDictionary; 95 | 96 | 97 | @property (nonatomic, assign) struct build_version_command *build_cmd; 98 | @property (nonatomic, assign) struct version_min_command *version_cmd; 99 | @property (nonatomic, assign) struct uuid_command *uuid_cmd; 100 | 101 | @property (nonatomic, assign) struct symtab_command *symtab; 102 | @property (nonatomic, assign) struct dysymtab_command *dysymtab; 103 | @property (nonatomic, assign) struct nlist_64 *symbols; 104 | @property (nonatomic, assign) char *str_symbols; 105 | 106 | @property (nonatomic, strong) NSMutableDictionary*symbolEntry; 107 | 108 | 109 | 110 | /// 111 | @property (nonatomic, assign) struct linkedit_data_command *function_starts_cmd; 112 | 113 | /// __DATA.__la_symbol_ptr Mach-O section 114 | @property (nonatomic, assign) struct section_64 *lazy_ptr_section; 115 | 116 | 117 | @property (nonatomic, assign) struct dyld_info_command *dyldInfo; 118 | 119 | /// The indirect symbol table *int that points to actual symbols 120 | @property (nonatomic, assign) indirect_symbols_t indirect_symbols; 121 | 122 | - (instancetype)initWithPath:(NSString*)path; 123 | - (NSString *)realizedPath; 124 | - (uintptr_t)translateLoadAddressToFileOffset:(uintptr_t)loadAddress useFatOffset:(BOOL)useFatOffset; 125 | - (uintptr_t)translateOffsetToLoadAddress:(uintptr_t)offset; 126 | 127 | @end 128 | 129 | 130 | /// File handling logic 131 | @interface XRMachOLibrary (FileManagement) 132 | 133 | 134 | @end 135 | 136 | NS_ASSUME_NONNULL_END 137 | -------------------------------------------------------------------------------- /dsdump/dsdump.1: -------------------------------------------------------------------------------- 1 | .Dd __DATE__ 2 | .Dt dsdump 1 3 | .Os Darwin 4 | .Sh NAME 5 | .Nm dsdump 6 | .Nd An improved nm + objc/swift class-dump 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op option... 10 | .Ar 11 | .Sh DESCRIPTION 12 | Provides an "nm-improved" experience when working with Mach-O executables. dsdump has 6 "primary" modes: Symbol table (--sym), Objective-C (--objc), Swift (--swift, -s), File Offset => Virtual Address (--offset, -F), Virtual Address => File Offset (--virtual, -A), Library reference (--library, -l). Omitting all of these options will default to the symbol table mode. 13 | .Sh OPTIONS 14 | .Bl -tag -width indent 15 | .It Fl c, -color 16 | Adds color to output 17 | .It Fl l, -library 18 | Instead of dumping symbols, search all procs for library 19 | .It Fl A, -virtual Ar VirtualAddress(hex) 20 | Converts to the virtual address to the file offset 21 | .It Fl F, -offset Ar FileOffset(hex) 22 | Converts to the file offset to the virtual address 23 | .It Fl O, -opcs 24 | Dump the DYLD opcodes used to bind external symbols at load time 25 | .It Fl f, -filter Ar FilterWord 26 | Specify classes to filter by (case insensitive, can be used multiple times) 27 | .It Fl a, -arch Ar architecture 28 | Specify the arichtecture if file is FAT. Understands x86_64h, x86_64, arm64, arm64e 29 | .It Fl u, -undefined 30 | Only display undefined (externally referenced) symbols or classes 31 | .It Fl U, -defined 32 | Only display defined (internally implemented) symbols or classes 33 | .It Fl v, -verbose 34 | Specifies the verbosity level. The -v option can be used multiple times, while the long argument sets the exact level 0-5. Kind of like codesign(1)'s verbosity that everyone complains about... 35 | .It Fl -objc 36 | Dump the Objective-C classes 37 | .It Fl -swift 38 | Dump the Swift type descriptors (classes, structs, enums) 39 | .It Fl s 40 | Sets mode to Swift mode and verbosity to level 4 41 | .It Fl h, -help 42 | Print out this beautiful, helpful document 43 | .El 44 | .Sh EXAMPLES 45 | List ObjC internal/external classes referenced/implemented by vmmap: 46 | .Dl dsdump --objc $(which vmmap) 47 | .Pp 48 | List all alive processes that have the MobileDevice loaded 49 | .Dl sudo dsdump -l /S*/L*/P*/MobileDevice.framework/MobileDevice 50 | .Pp 51 | List the Objective-C external classes called by vmmap: 52 | .Dl dsdump --objc $(which vmmap) -u 53 | .Pp 54 | List the Objective-C internal classes implemented by vmmap: 55 | .Dl dsdump --objc $(which vmmap) -U 56 | .Pp 57 | Perform an Objective-C "class-dump" in color of vmmap 58 | .Dl dsdump --objc $(which vmmap) -U -vvvc 59 | .Pp 60 | Thoroughly dump the Swift content in color in the Console app 61 | .Dl dsdump --swift /Applications/Utilities/Console.app/Contents/MacOS/Console -cvvvv 62 | .Pp 63 | .Sh VERBOSITY 64 | dsdump can output a range of verbosity between the 3 different modes (--sym, --swift, --objc). The verbosity level can be set by the long form (--verbose=3) or by specifying a count via short form (-vvv). The breakdown of these levels are shown below: 65 | .Pp 66 | --sym: 67 | .Dl 0. Print symbol 68 | .Dl 1. 0 + library path or Mach-O section 69 | .Dl 2. 1 + fullpath to library 70 | .Dl 3. 2 + nlist struct output 71 | .Dl 4. Same as 3... for now 72 | .Dl 5. Same as 3... for now 73 | .Pp 74 | --swift: 75 | .Dl 0. List swift types 76 | .Dl 1. 0 + Parent classes 77 | .Dl 2. 1 + Protocols 78 | .Dl 3. 2 + Swift "type dump" 79 | .Dl 4. 3 + Extended type dump, ObjC bridge methods 80 | .Dl 5. 4 + Commenting in methods 81 | .Pp 82 | --objc: 83 | .Dl 0. List Objective-C classes 84 | .Dl 1. 0 + Parent classes & library basename for external 85 | .Dl 2. 1 + Fullpath to libraries for external + protocols 86 | .Dl 3. 2 + Objective-C "class dump" 87 | .Dl 4. 3 + Print properties 88 | .Dl 5. 4 + Print ivars & offsets 89 | .Pp 90 | .Sh ENVIRONMENT 91 | .Pp 92 | .Bl -tag -width indent 93 | .Ev DSCOLOR 94 | Enables color. Alternatively, use -c 95 | .Pp 96 | .Ev ARCH 97 | .Ar 98 | Specify the architecture if inspecting a FAT executable, Alternatively use --arch 99 | .El 100 | .Sh SEE ALSO 101 | .Xr nm 1 , 102 | .Xr objdump 1 , 103 | .Xr vmmap 1 104 | .Sh BUGS 105 | There's a situation where occassionally dsdump will think the parent class is a RO_ROOT where it will in fact won't be. I'll print this out for now so I can hunt it down 106 | .Pp 107 | ARM64e still needs some luv, especially on the Swift side, especially with Protocols... and not crashing 108 | .Sh AUTHORS 109 | .An "Derek Selander" 110 | .Mt @LOLgrep 111 | -------------------------------------------------------------------------------- /dsdump/XRMachOLibrary+Disassemble.mm: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary+Disassemble.m 3 | // dsdump 4 | // 5 | // Created by Derek Selander on 9/27/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import "XRMachOLibrary+Disassemble.h" 10 | 11 | ///////////////////////////////////////////////////////// 12 | // muwahahahahahaha going to hell for this... 13 | #pragma clang diagnostic push 14 | #pragma clang diagnostic ignored "-Weverything" 15 | #import "capstone.h" 16 | #pragma clang diagnostic pop 17 | // 18 | 19 | #import 20 | 21 | 22 | @implementation XRMachOLibrary (Disassemble) 23 | 24 | - (uintptr_t *)resolveMetadataFromCode:(uintptr_t)address { 25 | 26 | cs_arch arch = self.header->cputype == CPU_TYPE_ARM64? CS_ARCH_ARM64 : CS_ARCH_X86; 27 | cs_mode mode = self.header->cputype == CPU_TYPE_ARM64? CS_MODE_LITTLE_ENDIAN : CS_MODE_64; 28 | static csh handle = 0; 29 | static uint8_t *buffer = NULL; 30 | static dispatch_once_t onceToken; 31 | dispatch_once(&onceToken, ^{ 32 | int err = cs_open(arch, mode, &handle); 33 | if (err != CS_ERR_OK) { 34 | assert(NO); 35 | } 36 | buffer = (uint8_t*)calloc(1000, sizeof(char)); // some high amount to be safe, particularly for variable ins x86 37 | cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON); 38 | }); 39 | 40 | if (!address) { 41 | return NULL; 42 | } 43 | 44 | cs_insn *instructions = NULL; 45 | cs_disasm(handle, (const uint8_t *)address, 200, address, 20, &instructions); 46 | 47 | // x86_64 48 | if (self.header->cputype == CPU_TYPE_X86_64) { 49 | for (int i = 0; i < 20; i++) { 50 | cs_insn insn = instructions[i]; 51 | cs_x86_op *ops = insn.detail->x86.operands; 52 | auto opcount = insn.detail->x86.op_count; 53 | if (opcount == 2 && // Is there 2 operands? 54 | ops[0].type == X86_OP_REG && ops[0].reg == X86_REG_RAX && // is the first operand setting RAX? 55 | ops[1].type == X86_OP_MEM && ops[1].mem.base == X86_REG_RIP) {// is the second operand grabing memory from RIP? 56 | auto displacement = ops[1].mem.disp + insn.size; 57 | auto resolved = displacement + insn.address; 58 | return reinterpret_cast(resolved); 59 | } 60 | } 61 | // ARM64(e) 62 | } else if (self.header->cputype == CPU_TYPE_ARM64 && (self.header->cpusubtype & CPU_SUBTYPE_ARM64E)) { 63 | for (int i = 1; i < 20; i++) { 64 | auto insnADD = instructions[i]; 65 | auto insnADRP = instructions[i - 1]; 66 | 67 | if (insnADD.detail == nullptr || insnADRP.detail == nullptr) { 68 | if (xref_options.debug) { 69 | printf("DEBUG swift pointer AccessFunction %p\n", (void*)address); 70 | } 71 | continue; 72 | } 73 | 74 | auto insnADDCount = insnADD.detail->arm64.op_count; 75 | auto insnADRPCount = insnADRP.detail->arm64.op_count; 76 | 77 | // Is it ADRP/ADD? 78 | if (insnADD.id != ARM64_INS_ADD && insnADRP.id != ARM64_INS_ADRP) { 79 | continue; 80 | } 81 | 82 | // Does this have the telltale since of the usual ADRP/ADD combo count? 83 | if (insnADDCount != 3 && insnADRPCount != 2) { 84 | continue; 85 | } 86 | 87 | // adrp "x8, #0x400008000" 88 | cs_arm64_op *opsADRP = insnADRP.detail->arm64.operands; 89 | if (opsADRP[0].type != ARM64_OP_REG && opsADRP[1].type != ARM64_OP_IMM) { 90 | continue; 91 | } 92 | 93 | // add "x8, x8, #0x98" 94 | cs_arm64_op *opsADD = insnADD.detail->arm64.operands; 95 | if (opsADD[1].type != ARM64_OP_REG && opsADD[2].type != ARM64_OP_IMM) { 96 | continue; 97 | } 98 | 99 | // does the ADD instruction in use equal the same ADRP register? 100 | if (opsADD[1].reg != opsADRP[0].reg) { 101 | continue; 102 | } 103 | 104 | return (uintptr_t*)(opsADRP[1].imm + opsADD[2].imm); 105 | } 106 | } 107 | return NULL; 108 | } 109 | 110 | @end 111 | -------------------------------------------------------------------------------- /dsdump/dyld_process_info_internal.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- 2 | * 3 | * Copyright (c) 2016 Apple Inc. All rights reserved. 4 | * 5 | * @APPLE_LICENSE_HEADER_START@ 6 | * 7 | * This file contains Original Code and/or Modifications of Original Code 8 | * as defined in and that are subject to the Apple Public Source License 9 | * Version 2.0 (the 'License'). You may not use this file except in 10 | * compliance with the License. Please obtain a copy of the License at 11 | * http://www.opensource.apple.com/apsl/ and read it before using this 12 | * file. 13 | * 14 | * The Original Code and all software distributed under the License are 15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 19 | * Please see the License for the specific language governing rights and 20 | * limitations under the License. 21 | * 22 | * @APPLE_LICENSE_HEADER_END@ 23 | */ 24 | 25 | #ifndef _DYLD_PROCESS_INFO_INTERNAL_H_ 26 | #define _DYLD_PROCESS_INFO_INTERNAL_H_ 27 | 28 | #define VIS_HIDDEN __attribute__((visibility("hidden"))) 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | struct dyld_all_image_infos_64 { 39 | uint32_t version; 40 | uint32_t infoArrayCount; 41 | std::atomic infoArray; 42 | uint64_t notification; 43 | bool processDetachedFromSharedRegion; 44 | bool libSystemInitialized; 45 | uint32_t paddingToMakeTheSizeCorrectOn32bitAndDoesntAffect64b; // NOT PART OF DYLD_ALL_IMAGE_INFOS! 46 | uint64_t dyldImageLoadAddress; 47 | uint64_t jitInfo; 48 | uint64_t dyldVersion; 49 | uint64_t errorMessage; 50 | uint64_t terminationFlags; 51 | uint64_t coreSymbolicationShmPage; 52 | uint64_t systemOrderFlag; 53 | uint64_t uuidArrayCount; 54 | uint64_t uuidArray; 55 | uint64_t dyldAllImageInfosAddress; 56 | uint64_t initialImageCount; 57 | uint64_t errorKind; 58 | uint64_t errorClientOfDylibPath; 59 | uint64_t errorTargetDylibPath; 60 | uint64_t errorSymbol; 61 | uint64_t sharedCacheSlide; 62 | std::array sharedCacheUUID; 63 | uint64_t sharedCacheBaseAddress; 64 | std::atomic infoArrayChangeTimestamp; 65 | uint64_t dyldPath; 66 | uint32_t notifyMachPorts[8]; 67 | uint64_t reserved[9]; 68 | uint64_t compact_dyld_image_info_addr; 69 | uint64_t compact_dyld_image_info_size; 70 | }; 71 | 72 | struct dyld_image_info_32 { 73 | uint32_t imageLoadAddress; 74 | uint32_t imageFilePath; 75 | uint32_t imageFileModDate; 76 | }; 77 | struct dyld_image_info_64 { 78 | uint64_t imageLoadAddress; 79 | uint64_t imageFilePath; 80 | uint64_t imageFileModDate; 81 | }; 82 | 83 | #define DYLD_PROCESS_INFO_NOTIFY_MAX_BUFFER_SIZE (32*1024) 84 | #define DYLD_PROCESS_INFO_NOTIFY_LOAD_ID 0x1000 85 | #define DYLD_PROCESS_INFO_NOTIFY_UNLOAD_ID 0x2000 86 | #define DYLD_PROCESS_INFO_NOTIFY_MAIN_ID 0x3000 87 | 88 | 89 | struct dyld_process_info_image_entry { 90 | uuid_t uuid; 91 | uint64_t loadAddress; 92 | uint32_t pathStringOffset; 93 | uint32_t pathLength; 94 | }; 95 | 96 | struct dyld_process_info_notify_header { 97 | mach_msg_header_t header; 98 | uint32_t version; 99 | uint32_t imageCount; 100 | uint32_t imagesOffset; 101 | uint32_t stringsOffset; 102 | uint64_t timestamp; 103 | }; 104 | 105 | struct dyld_uuid_info { 106 | const struct mach_header* imageLoadAddress; /* base address image is mapped into */ 107 | uuid_t imageUUID; /* UUID of image */ 108 | }; 109 | 110 | #endif // _DYLD_PROCESS_INFO_INTERNAL_H_ 111 | 112 | 113 | -------------------------------------------------------------------------------- /dsdump/Protocols.mm: -------------------------------------------------------------------------------- 1 | // 2 | // Protocols.cpp 3 | // dsdump 4 | // 5 | // Created by Derek Selander on 4/29/20. 6 | // Copyright © 2020 Selander. All rights reserved. 7 | // 8 | 9 | #include "Protocols.h" 10 | 11 | 12 | static BOOL printObjectiveCProtocolsFromProtocol(protocol_t* protocol); 13 | /******************************************************************************** 14 | // Protocols 15 | ********************************************************************************/ 16 | 17 | void dumpObjectiveCProtocols(void) { 18 | if (xref_options.undefined) { 19 | return; 20 | } 21 | struct section_64* protocolsSection = payload::sectionsDict["__DATA.__objc_protolist"]; 22 | if (!protocolsSection) { 23 | protocolsSection = payload::sectionsDict["__DATA_CONST.__objc_protolist"]; 24 | } 25 | if (!protocolsSection) { 26 | return; 27 | } 28 | 29 | auto protocolsDisk = payload::LoadToDiskTranslator::Cast(protocolsSection->addr)->disk(); 30 | for (int i = 0; i < protocolsSection->size / PTR_SIZE; i++) { 31 | auto protocol = payload::Cast(protocolsDisk[i]); 32 | if (protocol == nullptr) { 33 | continue; 34 | } 35 | auto protocolDisk = protocol->disk(); 36 | auto name = protocolDisk->mangledName ? protocolDisk->mangledName->disk() : "BUG DEREK"; 37 | if (!ContainsFilteredWords(name)) { 38 | continue; 39 | } 40 | printf("%s@protocol %s%s%s%s", dcolor(DSCOLOR_YELLOW), color_end(), dcolor(DSCOLOR_MAGENTA), name, color_end()); 41 | if(!printObjectiveCProtocolsFromProtocol(protocolDisk)) { 42 | putchar('\n'); 43 | } 44 | 45 | if (protocolDisk->instanceProperties) { 46 | auto properties = protocolDisk->instanceProperties->disk(); 47 | dumpObjCPropertiesWithResolvedAddress(properties); 48 | } 49 | 50 | if (protocolDisk->classMethods) { 51 | auto classMethods = protocolDisk->classMethods->disk(); 52 | dumpObjectiveCMethods(classMethods, name, true, true, dcolor(DSCOLOR_YELLOW)); 53 | } 54 | 55 | if (protocolDisk->instanceMethods) { 56 | auto instanceMethods = protocolDisk->instanceMethods->disk(); 57 | dumpObjectiveCMethods(instanceMethods, name, false, true, dcolor(DSCOLOR_YELLOW)); 58 | } 59 | 60 | auto optionalInstanceMethods = protocolDisk->optionalInstanceMethods; 61 | auto optionalClassMethods = protocolDisk->optionalClassMethods; 62 | if (optionalClassMethods || optionalInstanceMethods) { 63 | printf("%s@optional%s\n", dcolor(DSCOLOR_YELLOW), color_end()); 64 | } 65 | 66 | dumpObjectiveCMethods(optionalClassMethods, name, true, true, dcolor(DSCOLOR_YELLOW)); 67 | dumpObjectiveCMethods(optionalInstanceMethods, name, false, true, dcolor(DSCOLOR_YELLOW)); 68 | 69 | printf("%s@end%s\n\n", dcolor(DSCOLOR_YELLOW), color_end()); 70 | } 71 | } 72 | 73 | 74 | static BOOL printObjectiveCProtocolsFromProtocol(protocol_t* protocol) { 75 | if (xref_options.verbose <= VERBOSE_1) { 76 | return NO; 77 | } 78 | 79 | if (!protocol->disk()->protocols) { 80 | return NO; 81 | } 82 | auto protocolList = protocol->disk()->protocols->disk(); 83 | auto count = protocolList->disk()->count; 84 | if (count == 0) { 85 | return NO; 86 | } 87 | 88 | auto protocols = &protocolList->disk()->first_protocol; 89 | printf("%s <", dcolor(DSCOLOR_YELLOW)); 90 | 91 | for (int i = 0; i < count; i++) { 92 | auto prot = protocols[i]; 93 | auto mangledName = prot->disk()->mangledName->disk(); 94 | printf("%s", mangledName ? mangledName : ""); 95 | if (i != count - 1) { 96 | putchar(','); 97 | putchar(' '); 98 | } 99 | } 100 | printf(">\n%s", color_end()); 101 | 102 | return YES; 103 | } 104 | 105 | BOOL listProtocolsForObjectiveCClass(protocol_list_t* protocolList) { 106 | if (protocolList == nullptr) { 107 | return NO; 108 | } 109 | 110 | auto count = protocolList->disk()->count; 111 | if (count == 0) { 112 | return NO; 113 | } 114 | 115 | auto protocols = &protocolList->disk()->first_protocol; 116 | printf("%s <", dcolor(DSCOLOR_YELLOW)); 117 | 118 | for (int i = 0; i < count; i++) { 119 | auto prot = protocols[i]; 120 | auto mangledName = prot->disk()->mangledName->disk(); 121 | printf("%s", mangledName ? mangledName : ""); 122 | if (i != count - 1) { 123 | putchar(','); 124 | putchar(' '); 125 | } 126 | } 127 | printf(">\n%s", color_end()); 128 | return YES; 129 | } 130 | 131 | BOOL listProtocolsForObjectiveCClass(swift_class* cls) { 132 | if (xref_options.verbose <= VERBOSE_1) { 133 | return NO; 134 | } 135 | 136 | auto rodata = cls->disk()->rodata(); 137 | if (rodata == nullptr) { 138 | return NO; 139 | } 140 | 141 | auto protocolList = rodata->disk()->baseProtocols; 142 | return listProtocolsForObjectiveCClass(protocolList); 143 | } 144 | 145 | -------------------------------------------------------------------------------- /dsdump.xcodeproj/xcshareddata/xcschemes/dsdump.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 44 | 46 | 52 | 53 | 54 | 55 | 58 | 59 | 62 | 63 | 66 | 67 | 70 | 71 | 74 | 75 | 78 | 79 | 82 | 83 | 86 | 87 | 90 | 91 | 94 | 95 | 98 | 99 | 100 | 101 | 107 | 109 | 115 | 116 | 117 | 118 | 120 | 121 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /dsdump/miscellaneous.mm: -------------------------------------------------------------------------------- 1 | // 2 | // miscellaneous.m 3 | // xref 4 | // 5 | // Created by Derek Selander on 3/7/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import "miscellaneous.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #import "payload.hpp" 15 | 16 | extern "C" { 17 | 18 | #define XSTR(x) STR(x) 19 | #define STR(x) #x 20 | 21 | BOOL quiet_mode = NO; 22 | NSMutableSet *pathsSet = nil; 23 | NSMutableSet *exploredSet = nil; 24 | NSMutableSet *rpathSet = nil; 25 | 26 | xref_options_t xref_options; 27 | 28 | __attribute__((constructor)) static void InitializeStuff() { 29 | pathsSet = [NSMutableSet set]; 30 | exploredSet = [NSMutableSet set]; 31 | rpathSet = [NSMutableSet set]; 32 | } 33 | 34 | /******************************************************************************** 35 | // Documentation 36 | ********************************************************************************/ 37 | 38 | static const char * dsdump_usage = "dsdump [option..] "; 39 | static const char* dsdump_version = XSTR(DD_VERSION); 40 | 41 | 42 | 43 | void print_manpage() { 44 | printf("%s\n\n", __manpage_deets ? (const char*)&__manpage_deets : dsdump_usage); 45 | } 46 | 47 | void print_usage() { 48 | printf("Version: %s Built: (%s, %s) %s\n", dsdump_version, __TIME__, __DATE__, dsdump_usage); 49 | } 50 | 51 | /******************************************************************************** 52 | // Colors! 53 | ********************************************************************************/ 54 | 55 | static const std::map colorMap = { 56 | {DSCOLOR_CYAN, "\e[36m"}, 57 | {DSCOLOR_GREEN, "\e[92m"}, 58 | {DSCOLOR_DARK_GREEN, "\e[32m"}, 59 | {DSCOLOR_YELLOW, "\e[33m"}, 60 | {DSCOLOR_YELLOW_LIGHT, "\e[93m"}, 61 | {DSCOLOR_MAGENTA, "\e[95m"}, 62 | {DSCOLOR_PURPLE, "\e[35m"}, 63 | {DSCOLOR_RED, "\e[91m"}, 64 | {DSCOLOR_STRONG_RED, "\e[31;4m"}, 65 | {DSCOLOR_BLUE, "\e[34m"}, 66 | {DSCOLOR_GRAY, "\e[90m"}, 67 | {DSCOLOR_PURPLE_BOLD, "\e[35;1m"}, 68 | {DSCOLOR_LIGHT_BLUE, "\e[94m"}, 69 | {DSCOLOR_CYAN_LIGHT,"\e[96m"}, 70 | {DSCOLOR_BOLD, "\e[1m"}, 71 | {DSCOLOR_CYAN_UNDERLINE, "\033[36;1;4m"} 72 | }; 73 | 74 | char const* dcolor(DSCOLOR c) { 75 | static BOOL useColor = NO; 76 | static dispatch_once_t onceToken; 77 | dispatch_once(&onceToken, ^{ 78 | if (xref_options.color || getenv("DSCOLOR")) { 79 | useColor = YES; 80 | } 81 | }); 82 | if (!useColor) { 83 | return ""; 84 | } 85 | 86 | auto color = colorMap.at(c); 87 | if (color) { 88 | return color; 89 | } 90 | return ""; 91 | } 92 | 93 | char const* color_end() { 94 | static BOOL useColor = NO; 95 | static dispatch_once_t onceToken; 96 | dispatch_once(&onceToken, ^{ 97 | if (xref_options.color || getenv("DSCOLOR")) { 98 | useColor = YES; 99 | } 100 | }); 101 | if (useColor) { 102 | return "\e[0m"; 103 | } 104 | return ""; 105 | } 106 | 107 | void warn_debug(const char *format, ...) { 108 | if (!xref_options.debug) { 109 | return; 110 | } 111 | va_list args; 112 | va_start( args, format ); 113 | dprintf(STDERR_FILENO, format, args ); 114 | va_end( args ); 115 | } 116 | 117 | /******************************************************************************** 118 | // Leb128 Encoding 119 | ********************************************************************************/ 120 | 121 | /* Read a ULEB128 into a 64-bit word. Return (uint64_t)-1 on overflow 122 | or error. On overflow, skip past the rest of the uleb128. */ 123 | uint64_t read_uleb128 (const uint8_t ** offset, const uint8_t * end) { 124 | uint64_t result = 0; 125 | int bit = 0; 126 | 127 | do { 128 | uint64_t b; 129 | 130 | if (*offset == end) 131 | return (uint64_t) -1; 132 | 133 | b = **offset & 0x7f; 134 | 135 | if (bit >= 64 || b << bit >> bit != b) { 136 | result = (uint64_t) -1; 137 | } else { 138 | result |= b << bit; 139 | bit += 7; 140 | } 141 | } while (*(*offset)++ >= 0x80); 142 | return result; 143 | } 144 | 145 | /// Unsigned Leb128 146 | const uint8_t *r_uleb128_decode(uint8_t *data, int *datalen, uint64_t *v) { 147 | uint8_t c = 0xff; 148 | uint64_t s = 0, sum = 0, l = 0; 149 | if (data && *data) { 150 | do { 151 | c = *(data++) & 0xff; 152 | sum |= ((uint64_t) (c & 0x7f) << s); 153 | s += 7; 154 | l++; 155 | } while (c & 0x80); 156 | } 157 | if (v) {*v = sum; } 158 | if (datalen) { *datalen = (int)l; } 159 | return data; 160 | } 161 | 162 | /// Signed Leb128 163 | const uintptr_t r_sleb128_decode(uint8_t *byte, uintptr_t* datalen, uint64_t *v) { 164 | uintptr_t result = 0; 165 | uintptr_t shift = 0; 166 | 167 | size_t size = sizeof(signed int); 168 | uint8_t *cur = byte; 169 | int l =0 ; 170 | do{ 171 | l++; 172 | result |= ((0x7f & *cur) << shift); 173 | shift += 7; 174 | } while((*cur & 0x80) != 0); 175 | 176 | /* sign bit of byte is second high order bit (0x40) */ 177 | if ((shift < size) && (*cur & 0x80)) { 178 | /* sign extend */ 179 | result |= (~0 << shift); 180 | } 181 | 182 | if (v) { *v = result; } 183 | if (datalen) { *datalen = l; } 184 | return result; 185 | } 186 | 187 | 188 | 189 | 190 | 191 | 192 | BOOL ContainsFilteredWords(const char *word) { 193 | static size_t count = 0; 194 | static dispatch_once_t onceToken; 195 | dispatch_once(&onceToken, ^{ 196 | count = payload::filters.size(); 197 | }); 198 | 199 | if (count == 0) { 200 | return YES; 201 | } 202 | 203 | for (auto &it : payload::filters) { 204 | if (strcasestr(word, it)) { 205 | return YES; 206 | } 207 | } 208 | return NO; 209 | } 210 | 211 | 212 | void AddFilter(char * filter) { 213 | payload::filters.insert(filter); 214 | } 215 | 216 | }; // extern "C" 217 | -------------------------------------------------------------------------------- /dsdump/TaskPath.mm: -------------------------------------------------------------------------------- 1 | // 2 | // TaskPath.mm 3 | // xref 4 | // 5 | // Created by Derek Selander on 5/7/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | 19 | extern "C" { 20 | 21 | #import "miscellaneous.h" 22 | BOOL FindLibraryInTask(pid_t task, pid_t pid, const char *search_string, uuid_t uuid, uint64_t *loadAddr, kern_return_t* err); 23 | BOOL isUUIDMatch(task_t task, vm_address_t address, uuid_t uuid); 24 | 25 | void DumpProcessesContainingLibrary(const char *lib_name, uuid_t uuid) { 26 | int pidcount = proc_listallpids(NULL, 0); 27 | int *all_pids = (int*)malloc(pidcount * sizeof(int)); 28 | proc_listallpids(all_pids, pidcount); 29 | for (int i = 0; i < pidcount; i++) { 30 | pid_t pid = all_pids[i]; 31 | task_t pid_task = TASK_NULL; 32 | kern_return_t kr = task_for_pid(mach_task_self(), pid, &pid_task); 33 | 34 | if (kr != KERN_SUCCESS) { continue; } 35 | 36 | 37 | uint64 loadAddr = 0; 38 | if (!FindLibraryInTask(pid_task, pid, lib_name, uuid, &loadAddr, NULL)) { continue; } 39 | 40 | char buffer[PATH_MAX]; 41 | proc_regionfilename(all_pids[i], 0, buffer, PATH_MAX); 42 | 43 | 44 | if (xref_options.verbose >= VERBOSE_1) { 45 | printf("%s0x%012llx%s ", dcolor(DSCOLOR_GRAY), loadAddr, color_end()); 46 | } 47 | // TODO figure out basename bug, this gets screwed up on basename(buffer)... 48 | NSString *p; 49 | if (xref_options.verbose >= VERBOSE_2) { 50 | p = [NSString stringWithUTF8String:buffer]; 51 | } else { 52 | p = [[NSString stringWithUTF8String:buffer] lastPathComponent]; 53 | } 54 | 55 | 56 | printf("%s%d%s", dcolor(DSCOLOR_CYAN), all_pids[i], color_end()); 57 | printf(" %s%s%s", dcolor(DSCOLOR_GREEN), [p UTF8String], color_end()); 58 | putchar('\n'); 59 | } 60 | } 61 | 62 | 63 | BOOL FindLibraryInTask(pid_t task, pid_t pid, const char *search_string, uuid_t uuid, uint64_t *loadAddr, kern_return_t* err) { 64 | 65 | kern_return_t kr = KERN_SUCCESS; 66 | if (loadAddr) { *loadAddr = 0; } 67 | 68 | task_dyld_info_data_t task_dyld_info; 69 | mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; 70 | kr = task_info(task, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, &count); 71 | if (kr != KERN_SUCCESS) { 72 | if (err) *err = kr; 73 | return NO; 74 | } 75 | 76 | if (task_dyld_info.all_image_info_addr == MACH_VM_MIN_ADDRESS) { 77 | if (err) *err = -1; 78 | return NO; 79 | } 80 | 81 | if (task_dyld_info.all_image_info_size > sizeof(struct dyld_all_image_infos)) { 82 | if (err) *err = -1; 83 | return NO; 84 | } 85 | 86 | struct dyld_all_image_infos all_image_infos; 87 | mach_vm_size_t readSize = task_dyld_info.all_image_info_size; 88 | if ((kr = mach_vm_read_overwrite(task, task_dyld_info.all_image_info_addr, task_dyld_info.all_image_info_size, (vm_address_t)&all_image_infos, &readSize) ) != KERN_SUCCESS) { 89 | if (err) *err = kr; 90 | return NO; 91 | } 92 | 93 | // Die if lower than 15 94 | if (all_image_infos.version < 0xf) { return NO; } 95 | 96 | uint32_t imageCount = all_image_infos.infoArrayCount; 97 | size_t imageArraySize = imageCount * sizeof(dyld_image_info); 98 | dyld_image_info *imageArray64 = (dyld_image_info *)malloc(imageArraySize); 99 | if ((kr = ::mach_vm_read_overwrite(task, (mach_vm_address_t)all_image_infos.infoArray, imageArraySize, (vm_address_t)imageArray64, &readSize)) != KERN_SUCCESS ) { 100 | if (err) *err = kr; 101 | free(imageArray64); 102 | return NO; 103 | } 104 | 105 | for (int i = 0; i < imageCount; i++) { 106 | char path[PATH_MAX]; 107 | 108 | // Just restart, there can be a bad addresses 109 | if (::mach_vm_read_overwrite(task, (mach_vm_address_t)imageArray64[i].imageFilePath, PATH_MAX, (vm_address_t)path, &readSize)) { 110 | continue; 111 | } 112 | 113 | // First checkk for the name of the string 114 | // If we got this far, let's go after the UUID 115 | // DYLD has a uuidArray, but that's only for non-shared cache modules 116 | if (strstr(path, search_string) && isUUIDMatch(task, (vm_address_t)imageArray64[i].imageLoadAddress, uuid)) { 117 | if (loadAddr) { 118 | *loadAddr = (uint64_t)imageArray64[i].imageLoadAddress; 119 | } 120 | free(imageArray64); 121 | return YES; 122 | } 123 | } 124 | 125 | // Just couldn't find it... 126 | if (err) *err = KERN_SUCCESS; 127 | free(imageArray64); 128 | return NO; 129 | } 130 | 131 | BOOL isUUIDMatch(task_t task, vm_address_t address, uuid_t uuid) { 132 | struct mach_header_64 header; 133 | mach_vm_size_t size = 0; 134 | if (::mach_vm_read_overwrite(task, address, sizeof(struct mach_header_64), (vm_address_t)&header, &size)) { 135 | return NO; 136 | } 137 | 138 | if (header.magic != MH_MAGIC_64) { 139 | pid_t pid = 0; 140 | pid_for_task(task, &pid); 141 | warn_debug("Didn't find a 0xfeedfacf in %d, %p\n", pid, address); 142 | return NO; 143 | } 144 | 145 | uintptr_t *pointer = (uintptr_t*)malloc(header.sizeofcmds); 146 | if (::mach_vm_read_overwrite(task, address, header.sizeofcmds, (vm_address_t)pointer, &size)) { 147 | return NO; 148 | } 149 | uintptr_t cur = (((uintptr_t)pointer) + sizeof(mach_header_64)); 150 | for (int i = 0; i < header.ncmds; i++) { 151 | load_command *cmd = (load_command *)cur; 152 | if (cmd->cmd == LC_UUID) { 153 | struct uuid_command* uuid_cmd = (struct uuid_command*)cur; 154 | if (strncmp((const char*)&uuid_cmd->uuid, (const char*)uuid, 16) == 0) { 155 | return YES; 156 | } 157 | 158 | } 159 | cur += cmd->cmdsize; 160 | } 161 | 162 | return NO; 163 | } 164 | 165 | } // extern "C" { 166 | -------------------------------------------------------------------------------- /dsdump/FAT/XRMachOLibrary+FAT.mm: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary+FAT.m 3 | // xref 4 | // 5 | // Created by Derek Selander on 5/1/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import "XRMachOLibrary+FAT.h" 10 | #import "../payload.hpp" 11 | #include 12 | #include 13 | 14 | 15 | @implementation XRMachOLibrary (FAT) 16 | 17 | -(NSString *)nameForCPU:(cpu_type_t)cputype subtype:(cpu_subtype_t)subtype { 18 | if (cputype == CPU_TYPE_X86_64 && subtype & (CPU_SUBTYPE_X86_64_H)) { 19 | return @"x86_64h"; 20 | } 21 | 22 | if (cputype == CPU_TYPE_X86_64 && subtype & (CPU_SUBTYPE_X86_64_ALL)) { 23 | return @"x86_64"; 24 | } 25 | 26 | if (cputype == CPU_TYPE_X86 && subtype & CPU_SUBTYPE_X86_64_ALL) { 27 | return @"i386"; 28 | } 29 | 30 | if (cputype == CPU_TYPE_ARM64 && subtype == CPU_SUBTYPE_ARM64_ALL) { 31 | return @"arm64"; 32 | } 33 | 34 | if (cputype == CPU_TYPE_ARM && subtype & CPU_SUBTYPE_ARM_V7) { 35 | return @"armv7"; 36 | } 37 | 38 | if (cputype == CPU_TYPE_ARM64 && subtype & CPU_SUBTYPE_ARM64E) { 39 | return @"arm64e"; 40 | } 41 | 42 | return [NSString stringWithFormat:@"(?) 0x%x %x", cputype, subtype]; 43 | } 44 | 45 | - (intptr_t)offsetForDefaultArchitecture { 46 | return [self offsetForArchitecture:[self defaultArchitectureName] size:NULL]; 47 | } 48 | 49 | - (intptr_t)offsetForArchitecture:(NSString *)architecture size:(size_t*)size { 50 | if (!architecture) { 51 | return FAT_OFFSET_BAD_NAME; 52 | } 53 | 54 | auto magic = *payload::GetData(0); 55 | // big endian 56 | struct fat_header *fat = payload::GetData(0); //(void*)payload::data; 57 | if (magic == MH_CIGAM_64 || magic == MH_MAGIC_64) { 58 | 59 | auto mach_header = payload::GetData(0); 60 | cpu_subtype_t cpu_subytpe = mach_header->cpusubtype; 61 | cpu_subytpe = FIX_ENDIAN(cpu_subytpe); 62 | 63 | cpu_type_t cputype = mach_header->cputype; 64 | cputype = FIX_ENDIAN(cputype); 65 | 66 | if ([[self nameForCPU:cputype subtype:cpu_subytpe] isEqualToString:architecture]) { 67 | if (size) { 68 | *size = payload::size; 69 | } 70 | return 0; 71 | } else { 72 | if (size) { 73 | *size = 0; 74 | } 75 | return FAT_OFFSET_BAD_NAME; 76 | } 77 | } 78 | 79 | 80 | if (!(magic == FAT_MAGIC || magic == FAT_CIGAM)) { 81 | if (size) { 82 | *size = 0; 83 | } 84 | return FAT_OFFSET_BAD_NAME; 85 | } 86 | 87 | for (int i = 0; i < FIX_ENDIAN(fat->nfat_arch); i++) { 88 | struct fat_arch *arch = payload::GetData(sizeof(struct fat_header) + sizeof(struct fat_arch) * i); 89 | NSString *ar = [self nameForCPU:FIX_ENDIAN(arch->cputype) subtype:FIX_ENDIAN(arch->cpusubtype)]; 90 | if ([ar isEqualToString:architecture]) { 91 | if (size) { 92 | *size = FIX_ENDIAN(arch->size); 93 | } 94 | return FIX_ENDIAN(arch->offset); 95 | } 96 | } 97 | if (size) { 98 | *size = 0; 99 | } 100 | return FAT_OFFSET_BAD_NAME; 101 | } 102 | 103 | -(NSString *)printAllArchitectures { 104 | auto magic = *payload::GetData(0); 105 | 106 | if (magic == MH_CIGAM_64) { 107 | auto machHeader = payload::GetData(0); 108 | NSString *name = [self nameForCPU:htonl(machHeader->cputype) subtype:htonl(machHeader->cpusubtype)]; 109 | return [NSString stringWithFormat:@"[ %@ ]", name]; 110 | } 111 | 112 | if (magic == MH_MAGIC_64) { 113 | auto machHeader = payload::GetData(0); 114 | NSString *name = [self nameForCPU:machHeader->cputype subtype:machHeader->cpusubtype]; 115 | return [NSString stringWithFormat:@"[ %@ ]", name]; 116 | } 117 | 118 | NSMutableString *retString = [NSMutableString string]; 119 | struct fat_header *fat = payload::GetData(0); // (struct fat_header *)self.data; 120 | 121 | [retString appendString:@"["]; 122 | for (int i = 0; i < FIX_ENDIAN(fat->nfat_arch); i++) { 123 | // struct fat_arch *arch = (void*)&self.data[sizeof(struct fat_header) + sizeof(struct fat_arch) * i]; 124 | struct fat_arch *arch = payload::GetData(sizeof(struct fat_header) + sizeof(struct fat_arch) * i); 125 | NSString *ar = [self nameForCPU:FIX_ENDIAN(arch->cputype) subtype:FIX_ENDIAN(arch->cpusubtype)]; 126 | [retString appendFormat:@" %@ ", ar]; 127 | if (i < FIX_ENDIAN(fat->nfat_arch) - 1) { 128 | [retString appendFormat:@"|"]; 129 | } 130 | } 131 | [retString appendString:@"]"]; 132 | 133 | return retString; 134 | 135 | } 136 | 137 | - (NSString *)defaultArchitectureName { 138 | static cpu_type_t this_cputype; 139 | static cpu_subtype_t this_cpusubtype; 140 | static dispatch_once_t onceToken; 141 | dispatch_once(&onceToken, ^{ 142 | size_t len = sizeof(cpu_type_t); 143 | sysctlbyname("hw.cputype", &this_cputype, &len, NULL, 0); 144 | assert(len == sizeof(uint32_t)); 145 | len = sizeof(cpu_subtype_t); 146 | sysctlbyname("hw.cpusubtype", &this_cpusubtype, &len, NULL, 0); 147 | assert(len == sizeof(uint32_t)); 148 | }); 149 | 150 | this_cputype |= CPU_ARCH_ABI64; 151 | 152 | struct fat_header *fat = payload::GetData(0); 153 | 154 | // Let's try the closest to the cpu type first... 155 | for (int i = 0; i < FIX_ENDIAN(fat->nfat_arch); i++) { 156 | auto arch = payload::GetData(sizeof(struct fat_header) + sizeof(struct fat_arch) * i); 157 | if (FIX_ENDIAN(arch->cpusubtype) == this_cpusubtype && (FIX_ENDIAN(arch->cputype) == this_cputype)) { 158 | return [self nameForCPU:FIX_ENDIAN(arch->cputype) subtype:FIX_ENDIAN(arch->cpusubtype)]; 159 | } 160 | } 161 | 162 | // If they don't have the right type, try x86_64 163 | for (int i = 0; i < FIX_ENDIAN(fat->nfat_arch); i++) { 164 | auto arch = payload::GetData(sizeof(struct fat_header) + sizeof(struct fat_arch) * i); 165 | if (FIX_ENDIAN(arch->cputype) == CPU_TYPE_X86_64) { 166 | return [self nameForCPU:FIX_ENDIAN(arch->cputype) subtype:FIX_ENDIAN(arch->cpusubtype)]; 167 | } 168 | } 169 | 170 | // Really!? OK, first 64 arch now... 171 | for (int i = 0; i < FIX_ENDIAN(fat->nfat_arch); i++) { 172 | auto arch = payload::GetData(sizeof(struct fat_header) + sizeof(struct fat_arch) * i); 173 | if (FIX_ENDIAN(arch->cputype) & CPU_ARCH_ABI64) { 174 | return [self nameForCPU:FIX_ENDIAN(arch->cputype) subtype:FIX_ENDIAN(arch->cpusubtype)]; 175 | } 176 | } 177 | 178 | return nil; 179 | } 180 | 181 | 182 | @end 183 | -------------------------------------------------------------------------------- /dsdump/progressbar.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \author Trevor Fountain 4 | * \author Johannes Buchner 5 | * \author Erik Garrison 6 | * \date 2010-2014 7 | * \copyright BSD 3-Clause 8 | * 9 | * progressbar -- a C class (by convention) for displaying progress 10 | * on the command line (to stderr). 11 | */ 12 | 13 | #include /* tgetent, tgetnum */ 14 | #include 15 | #include 16 | #include "progressbar.h" 17 | 18 | /// How wide we assume the screen is if termcap fails. 19 | enum { DEFAULT_SCREEN_WIDTH = 80 }; 20 | /// The smallest that the bar can ever be (not including borders) 21 | enum { MINIMUM_BAR_WIDTH = 10 }; 22 | /// The format in which the estimated remaining time will be reported 23 | static const char *const ETA_FORMAT = "ETA:%2dh%02dm%02ds"; 24 | /// The maximum number of characters that the ETA_FORMAT can ever yield 25 | enum { ETA_FORMAT_LENGTH = 13 }; 26 | /// Amount of screen width taken up by whitespace (i.e. whitespace between label/bar/ETA components) 27 | enum { WHITESPACE_LENGTH = 2 }; 28 | /// The amount of width taken up by the border of the bar component. 29 | enum { BAR_BORDER_WIDTH = 2 }; 30 | 31 | /// Models a duration of time broken into hour/minute/second components. The number of seconds should be less than the 32 | /// number of seconds in one minute, and the number of minutes should be less than the number of minutes in one hour. 33 | typedef struct { 34 | int hours; 35 | int minutes; 36 | int seconds; 37 | } progressbar_time_components; 38 | 39 | static void progressbar_draw(const progressbar *bar); 40 | 41 | /** 42 | * Create a new progress bar with the specified label, max number of steps, and format string. 43 | * Note that `format` must be exactly three characters long, e.g. "<->" to render a progress 44 | * bar like "<---------->". Returns NULL if there isn't enough memory to allocate a progressbar 45 | */ 46 | progressbar *progressbar_new_with_format(const char *label, unsigned long max, const char *format) 47 | { 48 | progressbar *new = malloc(sizeof(progressbar)); 49 | if(new == NULL) { 50 | return NULL; 51 | } 52 | 53 | new->max = max; 54 | new->value = 0; 55 | new->start = time(NULL); 56 | assert(3 == strlen(format) && "format must be 3 characters in length"); 57 | new->format.begin = format[0]; 58 | new->format.fill = format[1]; 59 | new->format.end = format[2]; 60 | 61 | progressbar_update_label(new, label); 62 | progressbar_draw(new); 63 | 64 | return new; 65 | } 66 | 67 | /** 68 | * Create a new progress bar with the specified label and max number of steps. 69 | */ 70 | progressbar *progressbar_new(const char *label, unsigned long max) 71 | { 72 | return progressbar_new_with_format(label, max, "|=|"); 73 | } 74 | 75 | void progressbar_update_label(progressbar *bar, const char *label) 76 | { 77 | bar->label = label; 78 | } 79 | 80 | /** 81 | * Delete an existing progress bar. 82 | */ 83 | void progressbar_free(progressbar *bar) 84 | { 85 | free(bar); 86 | } 87 | 88 | /** 89 | * Increment an existing progressbar by `value` steps. 90 | */ 91 | void progressbar_update(progressbar *bar, unsigned long value) 92 | { 93 | bar->value = value; 94 | progressbar_draw(bar); 95 | } 96 | 97 | /** 98 | * Increment an existing progressbar by a single step. 99 | */ 100 | void progressbar_inc(progressbar *bar) 101 | { 102 | progressbar_update(bar, bar->value+1); 103 | } 104 | 105 | static void progressbar_write_char(FILE *file, const int ch, const size_t times) { 106 | size_t i; 107 | for (i = 0; i < times; ++i) { 108 | fputc(ch, file); 109 | } 110 | } 111 | 112 | static int progressbar_max(int x, int y) { 113 | return x > y ? x : y; 114 | } 115 | 116 | static unsigned int get_screen_width(void) { 117 | char termbuf[2048]; 118 | if (tgetent(termbuf, getenv("TERM")) >= 0) { 119 | return tgetnum("co") /* -2 */; 120 | } else { 121 | return DEFAULT_SCREEN_WIDTH; 122 | } 123 | } 124 | 125 | static int progressbar_bar_width(int screen_width, int label_length) { 126 | return progressbar_max(MINIMUM_BAR_WIDTH, screen_width - label_length - ETA_FORMAT_LENGTH - WHITESPACE_LENGTH); 127 | } 128 | 129 | static int progressbar_label_width(int screen_width, int label_length, int bar_width) { 130 | int eta_width = ETA_FORMAT_LENGTH; 131 | 132 | // If the progressbar is too wide to fit on the screen, we must sacrifice the label. 133 | if (label_length + 1 + bar_width + 1 + ETA_FORMAT_LENGTH > screen_width) { 134 | return progressbar_max(0, screen_width - bar_width - eta_width - WHITESPACE_LENGTH); 135 | } else { 136 | return label_length; 137 | } 138 | } 139 | 140 | static int progressbar_remaining_seconds(const progressbar* bar) { 141 | double offset = difftime(time(NULL), bar->start); 142 | if (bar->value > 0 && offset > 0) { 143 | return (offset / (double) bar->value) * (bar->max - bar->value); 144 | } else { 145 | return 0; 146 | } 147 | } 148 | 149 | static progressbar_time_components progressbar_calc_time_components(int seconds) { 150 | int hours = seconds / 3600; 151 | seconds -= hours * 3600; 152 | int minutes = seconds / 60; 153 | seconds -= minutes * 60; 154 | 155 | progressbar_time_components components = {hours, minutes, seconds}; 156 | return components; 157 | } 158 | 159 | static void progressbar_draw(const progressbar *bar) 160 | { 161 | int screen_width = get_screen_width(); 162 | int label_length = (int)strlen(bar->label); 163 | int bar_width = progressbar_bar_width(screen_width, label_length); 164 | int label_width = progressbar_label_width(screen_width, label_length, bar_width); 165 | 166 | int progressbar_completed = (bar->value >= bar->max); 167 | int bar_piece_count = bar_width - BAR_BORDER_WIDTH; 168 | int bar_piece_current = (progressbar_completed) 169 | ? bar_piece_count 170 | : bar_piece_count * ((double) bar->value / bar->max); 171 | 172 | progressbar_time_components eta = (progressbar_completed) 173 | ? progressbar_calc_time_components(difftime(time(NULL), bar->start)) 174 | : progressbar_calc_time_components(progressbar_remaining_seconds(bar)); 175 | 176 | if (label_width == 0) { 177 | // The label would usually have a trailing space, but in the case that we don't print 178 | // a label, the bar can use that space instead. 179 | bar_width += 1; 180 | } else { 181 | // Draw the label 182 | fwrite(bar->label, 1, label_width, stderr); 183 | fputc(' ', stderr); 184 | } 185 | 186 | // Draw the progressbar 187 | fputc(bar->format.begin, stderr); 188 | progressbar_write_char(stderr, bar->format.fill, bar_piece_current); 189 | progressbar_write_char(stderr, ' ', bar_piece_count - bar_piece_current); 190 | fputc(bar->format.end, stderr); 191 | 192 | // Draw the ETA 193 | fputc(' ', stderr); 194 | fprintf(stderr, ETA_FORMAT, eta.hours, eta.minutes, eta.seconds); 195 | fputc('\r', stderr); 196 | } 197 | 198 | /** 199 | * Finish a progressbar, indicating 100% completion, and free it. 200 | */ 201 | void progressbar_finish(progressbar *bar) 202 | { 203 | // Make sure we fill the progressbar so things look complete. 204 | progressbar_draw(bar); 205 | 206 | // Print a newline, so that future outputs to stderr look prettier 207 | fprintf(stderr, "\n"); 208 | 209 | // We've finished with this progressbar, so go ahead and free it. 210 | progressbar_free(bar); 211 | } 212 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NOTE: around ~2021 Apple introduced new binding opcodes which I haven't gotten around to updating yet for dsdump. This results in bad output for objc class dumps. 2 | I recommend you check out [@blacktop](https://twitter.com/blacktop__)'s [ipsw](https://github.com/blacktop/ipsw) instead 3 | 4 | 5 | ## dsdump 6 | An improved nm + objc/swift class-dump ([writeup](https://derekselander.github.io/dsdump/)) 7 | 8 | Works great on Objective-C classes 9 | [![img](media/vmmap.png)](https://store.raywenderlich.com/products/advanced-apple-debugging-and-reverse-engineering) 10 | 11 | ... and Swift types 12 | [![img](media/swift.png)](https://store.raywenderlich.com/products/advanced-apple-debugging-and-reverse-engineering) 13 | 14 | ### man 15 | 16 | 17 | ``` 18 | dsdump(1) BSD General Commands Manual dsdump(1) 19 | 20 | NAME 21 | dsdump -- An improved nm + objc/swift class-dump 22 | 23 | SYNOPSIS 24 | dsdump [option...] 25 | 26 | DESCRIPTION 27 | Provides an "nm-improved" experience when working with Mach-O executa- 28 | bles. dsdump has 3 "primary" modes: Symbol table (--sym), Objective-C 29 | (--objc), and Swift (--swift, -s). Omitting all of these options will 30 | default to the Swift mode. 31 | 32 | OPTIONS 33 | -c, --color 34 | Adds color to output 35 | 36 | -d, --demangle 37 | Demangle Swift and C++ symbols in print symbol mode 38 | 39 | -l, --library 40 | Instead of dumping symbols, search all procs for library 41 | 42 | -O, --opcs 43 | Dump the DYLD opcodes used to bind external symbols at load time 44 | 45 | -f, --filter FilterWord 46 | Specify classes to filter by (case insensitive, can be used mul- 47 | tiple times) 48 | 49 | -a, --arch architecture 50 | Specify the arichtecture if file is FAT. Understands x86_64h, 51 | x86_64, arm64, arm64e 52 | 53 | -u, --undefined 54 | Only display undefined (externally referenced) symbols or classes 55 | 56 | -U, --defined 57 | Only display defined (internally implemented) symbols or classes 58 | 59 | -v, --verbose 60 | Specifies the verbosity level. The -v option can be used multiple 61 | times, while the long argument sets the exact level 0-5. Kind of 62 | like codesign(1)'s verbosity that everyone complains about... 63 | 64 | --objc Dump the Objective-C classes 65 | 66 | -o Sets mode to Objective-C mode and verbosity to level 4 67 | 68 | --swift 69 | Dump the Swift type descriptors (classes, structs, enums) 70 | 71 | -s Sets mode to Swift mode and verbosity to level 4 72 | 73 | -h, --help 74 | Print out this beautiful, helpful document 75 | 76 | EXAMPLES 77 | List ObjC internal/external classes referenced/implemented by vmmap: 78 | dsdump --objc $(which vmmap) 79 | 80 | List all alive processes that have the MobileDevice loaded 81 | sudo dsdump -l /S*/L*/P*/MobileDevice.framework/MobileDevice 82 | 83 | List the Objective-C external classes called by vmmap: 84 | dsdump --objc $(which vmmap) -u 85 | 86 | List the Objective-C internal classes implemented by vmmap: 87 | dsdump --objc $(which vmmap) -U 88 | 89 | Perform an Objective-C "class-dump" in color of vmmap 90 | dsdump --objc $(which vmmap) -U -vvvc 91 | 92 | Thoroughly dump the Swift content in color in the Console app 93 | dsdump --swift 94 | /Applications/Utilities/Console.app/Contents/MacOS/Console -cvvvv 95 | 96 | VERBOSITY 97 | dsdump can output a range of verbosity between the 3 different modes 98 | (--sym, --swift, --objc). The verbosity level can be set by the long form 99 | (--verbose=3) or by specifying a count via short form (-vvv). The break- 100 | down of these levels are shown below: 101 | 102 | --sym: 103 | 0. Print symbol 104 | 1. 0 + library path or Mach-O section 105 | 2. 1 + fullpath to library 106 | 3. 2 + nlist struct output 107 | 4. Same as 3... for now 108 | 5. Same as 3... for now 109 | 110 | --swift: 111 | 0. List swift types 112 | 1. 0 + Parent classes 113 | 2. 1 + Protocols 114 | 3. 2 + Swift type dump 115 | 4. 3 + Extended type dump, ObjC bridge methods 116 | 5. 4 + Commenting in methods 117 | 118 | --objc: 119 | 0. List Objective-C classes 120 | 1. 0 + Parent classes & library basename for external 121 | 2. 1 + Fullpath to libraries for external + protocols 122 | 3. 2 + Objective-C class dump 123 | 4. 3 + Print properties 124 | 5. 4 + Print ivars & offsets 125 | 126 | ENVIRONMENT 127 | DSCOLOR Enables color. Alternatively, use -c 128 | 129 | ARCH Specify the architecture if inspecting a FAT executable, 130 | Alternatively use --arch 131 | 132 | SEE ALSO 133 | nm(1), objdump(1), vmmap(1) 134 | 135 | BUGS 136 | There's a situation where occassionally dsdump will think the parent 137 | class is a RO_ROOT where it will in fact won't be. I'll print this out 138 | for now so I can hunt it down 139 | 140 | ARM64e still needs some luv, especially on the Swift side, especially 141 | with Protocols... and not crashing 142 | 143 | AUTHORS 144 | Derek Selander @LOLgrep 145 | 146 | Darwin March 26, 2020 Darwin 147 | ``` 148 | 149 | 150 | ## Compiling 151 | 152 | Compiling this will be a bit of a pain in the butt on your end. You'll need to clone the Swift language in the same directory. Swift can't be a submodule to this repo since some of their git cloning scripts won't work :| 153 | 154 | ```bash 155 | # cd into the dsdump repo 156 | cd dsdump/ 157 | 158 | # make a directory called swift-source, yes, name it exactly that 159 | mkdir swift-source 160 | 161 | cd swift-source/ 162 | 163 | # clone the Swift repository into swift-source 164 | git clone https://github.com/apple/swift.git 165 | 166 | # checkout 167 | cd swift 168 | git checkout 75670c17272a993ed798cee7e31c20590e94118b 169 | 170 | # Use the Swift update helper script to grab everything else 171 | ./swift/utils/update-checkout --clone-with-ssh --tag swift-5.1.4-RELEASE 172 | ``` 173 | Comment out any remaining problematic code after a build, remove methods in `Metadata.h` as needed (i.e. problematic ARC bridging code on line 700) 174 | 175 | I've included the `libSwiftDemangling.a` static lib that I built into `dsdump/dsdump`. If you want to build entirely from Swift source, you'll need to build this yourself. Otherwise you should be good to go to build `dsdump` via Xcode. 176 | 177 | Alternatively, you can skip all of this by simply grabbing the compiled `dsdump` version in the **compiled** directory [found here](https://github.com/DerekSelander/dsdump/tree/master/compiled). Make sure the SHA256 matches below if you're paranoid. 178 | 179 | Compiled SHA256 180 | ``` 181 | SHA256: 83eebd025b43b58a486235e1bec70a3239995be409605e3ff19bdae07adff917 182 | ``` 183 | 184 | ### Credits 185 | 186 | * [https://opensource.apple.com/source/dyld/dyld-635.2/src/dyldInitialization.cpp.auto.html](https://opensource.apple.com/source/dyld/dyld-635.2/src/dyldInitialization.cpp.auto.html) Specifically the THREADED code for ARM64e 187 | * [https://opensource.apple.com/source/objc4/](https://opensource.apple.com/source/objc4/) Specifically, the objc_class swift_class structs (and all the property, protocol, method, ivar, etc structs) 188 | * [https://github.com/apple/swift](https://github.com/apple/swift) 189 | 190 | ### TODO list for v1 191 | 192 | * Full ARM64e support 193 | * M1 support 194 | * Crashes 195 | * iOS 15/Monterey support 196 | * In process support (TODO TODO guess that means a libdsdump.a) 197 | * Basic dsc listing with options to `dlopen` from cmdline 198 | * header files 199 | -------------------------------------------------------------------------------- /dsdump/payload.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // payload.hpp 3 | // dsdump 4 | // 5 | // Created by Derek Selander on 6/10/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #ifndef payload_hpp 10 | #define payload_hpp 11 | 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | #import 19 | #import 20 | 21 | 22 | 23 | #define ARM64E_MASK 0x000007FFFFFFFFFFUL 24 | #define ARM64E_POINTER(data) ((uintptr_t)data & ARM64E_MASK) 25 | 26 | namespace payload { 27 | 28 | extern uint8_t *data; 29 | extern uintptr_t size; 30 | extern std::vector sections; 31 | extern uintptr_t offset; 32 | extern std::map sectionsDict; 33 | 34 | extern std::unordered_set filters; 35 | uintptr_t Offset2Virtual(uintptr_t f); 36 | 37 | template 38 | T* GetData(uintptr_t offset) { 39 | auto retT = reinterpret_cast(&payload::data[offset]); 40 | return reinterpret_cast(ARM64E_POINTER(retT)) ; 41 | } 42 | 43 | template 44 | T Cast(C val) { 45 | return reinterpret_cast(val); 46 | } 47 | 48 | 49 | /// Used to translate virtual load addresses to disk (mmap'd) offsets 50 | template 51 | struct LoadToDiskTranslator { 52 | template 53 | static payload::LoadToDiskTranslator* Cast(C val) { 54 | return reinterpret_cast*>(val); 55 | } 56 | 57 | /// This assumes the disk address (the address mmap'd in memory) doesn't overlap with the virtual address 58 | inline bool isDisk() { 59 | auto d = reinterpret_cast(payload::data); 60 | // auto v = reinterpret_cast(this); 61 | auto v = strip_PAC(); 62 | return v >= d && v <= (d + payload::size); 63 | } 64 | 65 | inline bool isNull() { 66 | auto h = reinterpret_cast(this); 67 | // Normal case 68 | if (h == 0) { 69 | return true; 70 | } 71 | 72 | // ARM64e case 73 | if (h & (1UL << 62)) { 74 | return true; 75 | } 76 | return false; 77 | } 78 | 79 | /// 80 | uintptr_t strip_PAC() { 81 | auto p = reinterpret_cast(this); 82 | auto isPACCodePointer = reinterpret_cast(p & (1UL << 63)) ? true : false; 83 | // Code ARM64e pointer 84 | if (isPACCodePointer) { 85 | 86 | auto lower32Mask = -1UL >> 32; 87 | auto virtualAddress = payload::Offset2Virtual(lower32Mask & p); 88 | return virtualAddress; 89 | } 90 | 91 | // The DATA ARM64e pointer 92 | return ARM64E_POINTER(p); 93 | } 94 | 95 | /// 96 | T* strip() { 97 | auto p = reinterpret_cast(this); 98 | auto isPACCodePointer = reinterpret_cast(p & (3UL << 62)) ? true : false; 99 | // Code ARM64e pointer 100 | if (isPACCodePointer || isCodePointer) { 101 | 102 | auto lower32Mask = -1UL >> 32; 103 | auto virtualAddress = payload::Offset2Virtual(lower32Mask & p); 104 | return reinterpret_cast(virtualAddress); 105 | } 106 | 107 | // The DATA ARM64e pointer 108 | return reinterpret_cast(ARM64E_POINTER(p)); 109 | } 110 | 111 | /// 112 | inline bool isLoad() { 113 | return !isDisk(); 114 | } 115 | 116 | /// 117 | inline T* load() { 118 | if (isLoad()) {return reinterpret_cast((uintptr_t)strip_PAC()); } 119 | auto offset = reinterpret_cast(strip_PAC()) - reinterpret_cast(payload::data); 120 | for (auto &sec : payload::sections) { 121 | if (sec->offset <= (offset) && (offset) < (sec->offset + sec->size)) { 122 | auto resolvedLoad = offset - sec->offset + sec->addr; 123 | auto payload = reinterpret_cast(resolvedLoad); 124 | return payload; 125 | } 126 | } 127 | ::printf("WARNING: couldn't find address %p in binary!\n", (void*)this); 128 | return nullptr; 129 | } 130 | 131 | /// 132 | inline T* disk() { 133 | // Quiets compiler for null this checks 134 | // auto thisRef = this; 135 | // if (thisRef == nullptr) { 136 | // return nullptr; 137 | // } 138 | 139 | if (isDisk()) { 140 | return reinterpret_cast(ARM64E_POINTER(this)); 141 | } 142 | 143 | // auto loadAddress = reinterpret_cast(ARM64E_POINTER(this)); 144 | auto loadAddress = strip_PAC(); 145 | for (auto &sec : payload::sections) { 146 | if (sec->addr <= loadAddress && loadAddress < sec->addr + sec->size) { 147 | uintptr_t resolvedOffset = loadAddress - sec->addr + sec->offset; 148 | uint8_t *resolvedAddress = &payload::data[resolvedOffset]; 149 | auto payload = payload::LoadToDiskTranslator::Cast(resolvedAddress)->strip_PAC(); 150 | return reinterpret_cast(payload); 151 | } 152 | } 153 | 154 | ::printf("WARNING: couldn't find address %p (%p) in binary!\n", (void*)this, (void*)loadAddress); 155 | return nullptr; 156 | } 157 | 158 | 159 | inline bool validAddress() { 160 | auto loadAddress = reinterpret_cast(ARM64E_POINTER(this)); 161 | for (auto &sec : payload::sections) { 162 | if (sec->addr <= loadAddress && loadAddress < sec->addr + sec->size) { 163 | return true; 164 | } 165 | } 166 | return false; 167 | } 168 | 169 | inline uintptr_t loadAddress() { 170 | auto diskAddress = reinterpret_cast*>(this->disk()); 171 | return reinterpret_cast(diskAddress->load()); 172 | } 173 | 174 | // Using blah.atIndex(i) you get ARM64e resolves via the slightly prettier syntax of blah[i] 175 | inline T Get(int i) { 176 | return reinterpret_cast(ARM64E_POINTER(this->disk()[i])); 177 | } 178 | 179 | inline T* GetDisk(int i) { 180 | auto addr = &this->disk()[i]; 181 | return reinterpret_cast*>(addr)->disk(); 182 | } 183 | }; 184 | 185 | inline bool ValidDiskAddress(uintptr_t addr) { 186 | if ((uintptr_t)payload::data <= addr && addr <= (uintptr_t)payload::data + payload::size) { 187 | return true; 188 | } 189 | 190 | return false; 191 | } 192 | 193 | // IF there's a concrete type, then 194 | template 195 | struct DiskWrapper : payload::LoadToDiskTranslator { 196 | T val; 197 | template 198 | static payload::DiskWrapper* Cast(C val) { 199 | return reinterpret_cast*>(val); 200 | } 201 | }; 202 | 203 | template 204 | static payload::LoadToDiskTranslator* CastToDisk(C val) { 205 | return reinterpret_cast*>(val); 206 | } 207 | 208 | 209 | template 210 | uintptr_t GetLoadAddress(T t) { 211 | auto diskAddress = reinterpret_cast*>(t); 212 | return reinterpret_cast(diskAddress->load()); 213 | } 214 | } 215 | #endif /* payload_hpp */ 216 | -------------------------------------------------------------------------------- /dsdump/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // xref 4 | // 5 | // Created by Derek Selander on 3/7/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #include 12 | #import 13 | #import 14 | 15 | #import "miscellaneous.h" 16 | #import "XRMachOLibrary.h" 17 | #import "XRMachOLibrary+SymbolDumper.h" 18 | #import "TaskPath.h" 19 | #import "XRSymbolEntry.h" 20 | 21 | @import MachO; 22 | 23 | /******************************************************************************* 24 | Declarations 25 | *******************************************************************************/ 26 | 27 | static NSArray * exc_rpaths = nil; 28 | //static int analyzeFD = -1; 29 | static void handle_args(int argc, const char * argv[]); 30 | 31 | /******************************************************************************* 32 | LC_MAIN fun starts haaaaaaaaaaaaaaa 33 | *******************************************************************************/ 34 | 35 | int main(int argc, const char * argv[], const char*envp[]) { 36 | handle_args(argc, argv); 37 | if (argc < 2) { 38 | print_usage(); 39 | exit(1); 40 | } 41 | 42 | const char *_path = argv[optind++]; 43 | if (!_path) { 44 | print_manpage(); 45 | exit(1); 46 | } 47 | char resolved_path[PATH_MAX]; 48 | if (!realpath(_path, resolved_path)) { 49 | printf("Couldn't resolve \"%s\"\n", _path); 50 | exit(1); 51 | } 52 | NSString *path = [NSString stringWithUTF8String:resolved_path]; 53 | [pathsSet addObject:path]; 54 | if (![[NSFileManager defaultManager] fileExistsAtPath:path]) { 55 | printf("File doesn't exist at \"%s\"\n", resolved_path); 56 | exit(1); 57 | } 58 | 59 | XRMachOLibrary *image = [[XRMachOLibrary alloc] initWithPath:path]; 60 | 61 | if (xref_options.library) { 62 | if (geteuid() != 0) { 63 | dprintf(STDERR_FILENO, "Needs root privledges for this operation\n"); 64 | exit(1); 65 | } 66 | DumpProcessesContainingLibrary(basename(resolved_path), image.uuid_cmd->uuid); 67 | } 68 | 69 | // Go through the options and pick a default if nothing is set 70 | if (! ( xref_options.file_offset || xref_options.library)) { 71 | [image dumpSymbols]; 72 | } 73 | 74 | return 0; 75 | } 76 | 77 | 78 | static void handle_args(int argc, const char * argv[]) { 79 | while (1) { 80 | int option_index = 0; 81 | static struct option long_options[] = { 82 | {"arch", required_argument, 0, 0}, 83 | {"offset", required_argument, 0, 0}, 84 | {"virtual", required_argument, 0, 0}, 85 | {"verbose", optional_argument, &xref_options.verbose, 1}, 86 | {"library", no_argument, &xref_options.library, 1}, 87 | {"regex", no_argument, &xref_options.use_regex, 1}, 88 | {"color", no_argument, &xref_options.color, 1}, 89 | {"defined", no_argument, &xref_options.defined, 1}, 90 | {"undefined", no_argument, &xref_options.undefined, 1}, 91 | {"objc", no_argument, &xref_options.objectiveC_mode, 1}, 92 | {"swift", no_argument, &xref_options.swift_mode, 1}, 93 | {"all", no_argument, &xref_options.all_symbols, 1}, 94 | {"analyze", no_argument, &xref_options.analyze, 1}, 95 | {"debug", no_argument, &xref_options.debug, 1}, 96 | {"help", no_argument, &xref_options.help, 1}, 97 | {"opcodes", no_argument, &xref_options.opcodes, 1}, 98 | {"demangle", no_argument, &xref_options.demangle_mode, 1}, 99 | {"sym", no_argument, &xref_options.symbol_mode, 1}, 100 | {0, 0, 0, 0 } 101 | }; 102 | 103 | int c = getopt_long(argc, (char * const *)argv, "F:f:a:A:uUOxscvlZhod", 104 | long_options, &option_index); 105 | if (c == -1) { 106 | break; 107 | } 108 | 109 | switch (c) { 110 | // Case for long getopts, that can't use ints 111 | case 0: 112 | if (strcmp(long_options[option_index].name, "offset") == 0) { 113 | xref_options.file_offset = strtol(optarg, 0, 0); 114 | } else if (strcmp(long_options[option_index].name, "verbose") == 0) { 115 | xref_options.verbose = (int)strtol(optarg, 0, 0); 116 | } else if (strcmp(long_options[option_index].name, "arch") == 0) { 117 | xref_options.arch = optarg; 118 | } else if (strcmp(long_options[option_index].name, "help") == 0) { 119 | print_manpage(); 120 | exit(0); 121 | } else if (strcmp(long_options[option_index].name, "filter") == 0) { 122 | AddFilter(optarg); 123 | } else if (strcmp(long_options[option_index].name, "virtual") == 0) { 124 | xref_options.virtual_address = strtol(optarg, 0, 0); 125 | } 126 | break; 127 | case 'v': 128 | xref_options.verbose++; 129 | break; 130 | case 's': 131 | xref_options.verbose = VERBOSE_4; 132 | xref_options.swift_mode = 1; 133 | break; 134 | case 'o': 135 | xref_options.verbose = VERBOSE_4; 136 | xref_options.objectiveC_mode = 1; 137 | break; 138 | case 'Z': 139 | xref_options.analyze = 1; 140 | break; 141 | case 'c': 142 | xref_options.color = 1; 143 | break; 144 | case 'u': 145 | xref_options.undefined = 1; 146 | break; 147 | case 'U': 148 | xref_options.defined = 1; 149 | break; 150 | case 'l': 151 | xref_options.library = 1; 152 | break; 153 | case 'a': 154 | xref_options.arch = optarg; 155 | break; 156 | case 'f': 157 | AddFilter(optarg); 158 | break; 159 | case 'F': { 160 | char *end; 161 | long address = strtoul(optarg, &end, 10); 162 | if (!address) { 163 | address = strtoul(optarg, &end, 16); 164 | } 165 | if (!address) { 166 | printf("Couldn't parse address, use hex\n"); 167 | exit(1); 168 | } 169 | xref_options.file_offset = address; 170 | break; 171 | } 172 | case 'O': 173 | xref_options.opcodes = 1; 174 | break; 175 | case 'd': 176 | xref_options.demangle_mode = 1; 177 | break; 178 | case 'h': 179 | print_manpage(); 180 | exit(0); 181 | break; 182 | case 'A': { 183 | char *end; 184 | long address = strtoul(optarg, &end, 10); 185 | if (!address) { 186 | address = strtoul(optarg, &end, 16); 187 | } 188 | if (!address) { 189 | printf("-A needs a virtual address"); 190 | exit(1); 191 | } 192 | xref_options.virtual_address = address; 193 | xref_options.virtual_address_count = 1; 194 | if (strchr(optarg, ',')) { 195 | char *post_comma = strchr(optarg, ','); 196 | xref_options.virtual_address_count = (int)strtol(&post_comma[1], NULL, 10); 197 | if (!xref_options.virtual_address_count) { 198 | printf("Should be something like -a 0xffff,5\n"); 199 | exit(1); 200 | } 201 | 202 | } 203 | break; 204 | } case '?': 205 | break; 206 | 207 | default: 208 | printf("?? getopt returned character code 0%o ??\n", c); 209 | } 210 | } 211 | 212 | // Handle some post argument shuffling... 213 | xref_options.color |= getenv("DSCOLOR") ? 1 : 0; 214 | xref_options.debug |= getenv("DEBUG") ? 1 : 0; 215 | 216 | if (!xref_options.arch && getenv("ARCH")) { 217 | xref_options.arch = getenv("ARCH"); 218 | } 219 | 220 | if (!(xref_options.symbol_mode || xref_options.swift_mode || xref_options.objectiveC_mode || xref_options.virtual_address || xref_options.library || xref_options.opcodes || xref_options.file_offset)) { 221 | xref_options.symbol_mode = 1; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /dsdump/XRMachOLibrary+SymbolDumper.mm: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary+SymbolDumper.m 3 | // xref 4 | // 5 | // Created by Derek Selander on 4/22/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import "XRMachOLibrary+SymbolDumper.h" 10 | #import "XRMachOLibrary+Opcode.h" 11 | #import "XRMachOLibrary+ObjectiveC.h" 12 | #import "XRSymbolEntry.h" 13 | #import "XRMachOLibrary+Swift.h" 14 | #import "XRMachOLibraryCplusHelpers.h" 15 | #import 16 | #include 17 | @implementation XRMachOLibrary (SymbolDumper) 18 | 19 | 20 | - (void)dumpSymbols { 21 | 22 | 23 | if (xref_options.swift_mode && [self preparseSwiftTypes]) { 24 | [self preparseSwiftProtocols]; 25 | [self dumpSwiftTypes]; 26 | } 27 | 28 | if (xref_options.objectiveC_mode) { 29 | [self dumpObjectiveCClasses]; 30 | } 31 | 32 | // If no mode specified, defaults to nm 33 | if (xref_options.objectiveC_mode || xref_options.swift_mode) { 34 | return; 35 | } 36 | 37 | if (xref_options.debug) { 38 | struct dysymtab_command * d = self.dysymtab; 39 | printf("\ 40 | ilocalsym: %d, nlocalsym: %d\n\ 41 | iextdefsym: %d, nextdefsym: %d\n\ 42 | iundefsym: %d, nundefsym: %d\n\ 43 | modtaboff: %d, nmodtab: %d\n\ 44 | extrefsymoff: %d, nextrefsyms: %d\n\ 45 | indirectsymoff: %d, nindirectsyms: %d\n\ 46 | extreloff: %d, nextrel: %d\n\ 47 | locreloff: %d, nlocrel: %d\n", d->ilocalsym, d->nlocalsym, d->iextdefsym, d->nextdefsym, d->iundefsym, d->nundefsym, d->modtaboff, d->nmodtab, d->extrefsymoff, d->nextrefsyms, d->indirectsymoff, d->nindirectsyms, d->extrefsymoff, d->nextrel, d->locreloff, d->nlocrel); 48 | } 49 | 50 | if (!xref_options.symbol_mode) { 51 | return; 52 | } 53 | 54 | for (int i = 0; i < self.symtab->nsyms; i++) { 55 | struct nlist_64 symbol = self.symbols[i]; 56 | 57 | // For stripped functions 58 | if ((xref_options.all_symbols || xref_options.analyze) && symbol.n_value && self.symbolEntry[@(symbol.n_value)]) { 59 | XRSymbolEntry *entry = self.symbolEntry[@(symbol.n_value)]; 60 | entry.visited = true; 61 | } 62 | 63 | // If a debugging symbol only print if really verbose 64 | if ((symbol.n_type & N_STAB) && xref_options.verbose < VERBOSE_3) { 65 | continue; 66 | } 67 | 68 | if (xref_options.defined || xref_options.undefined) { 69 | if ((xref_options.defined && symbol.n_type & N_TYPE & N_SECT) || (xref_options.undefined && (symbol.n_type & N_TYPE) == N_UNDF)) { 70 | print_symbol(self, &self.symbols[i], NULL); 71 | } 72 | } else { 73 | print_symbol(self, &self.symbols[i], NULL); 74 | } 75 | 76 | } 77 | 78 | // Enumerate the stripped symbols if all_symbols is set 79 | if (xref_options.all_symbols) { 80 | for (NSNumber *key in self.symbolEntry) { 81 | XRSymbolEntry *entry = self.symbolEntry[key]; 82 | if (entry.visited) { continue; } 83 | printf("0x%011llx %s%s\n", entry.address, dcolor(DSCOLOR_RED), color_end()); 84 | } 85 | } 86 | } 87 | 88 | - (void)dumpExternalSymbols { 89 | uintptr_t base = self.lazy_ptr_section->addr; 90 | size_t align_size = 1 << (self.lazy_ptr_section->align); 91 | const char *cyan = dcolor(DSCOLOR_CYAN); 92 | const char *end = color_end(); 93 | const char *yellow = dcolor(DSCOLOR_YELLOW); 94 | 95 | for (int i = 0; i < self.indirect_symbols.count; i++) { 96 | int offset = self.indirect_symbols.indirect_sym[i]; 97 | struct nlist_64 symbol = self.symbols[offset]; 98 | int libIndex = GET_LIBRARY_ORDINAL(symbol.n_desc); 99 | char * chr = &self.str_symbols[symbol.n_un.n_strx]; 100 | 101 | if (xref_options.verbose >= VERBOSE_1) { 102 | printf(" 0x%-8lx %s%s%s: %s%-40s%s\n", base + (align_size * i), yellow, [self.depdencies[libIndex] UTF8String], end, cyan, chr, end); 103 | } else { 104 | printf(" 0x%-8lx %s%-40s%s\n", base + (align_size * i), cyan, chr, end); 105 | } 106 | } 107 | } 108 | 109 | - (XRBindSymbol *)objCSuperClassFromSymbol:(struct nlist_64 * _Nonnull)sym { 110 | if (!(sym && sym->n_value)) { 111 | return nil; 112 | } 113 | 114 | XRBindSymbol *objcReference; 115 | uintptr_t buff = 0; 116 | uintptr_t fileOff = [self translateLoadAddressToFileOffset:sym->n_value + PTR_SIZE useFatOffset:NO]; 117 | pread(self.fd, &buff, sizeof(void*), fileOff); 118 | 119 | // That buff is 0, then the class is defined elsewhere, use the opcode symbol bindings instead 120 | if (buff == 0) { 121 | objcReference = self.addressObjCDictionary[@(sym->n_value + PTR_SIZE)]; 122 | } else { 123 | objcReference = self.addressObjCDictionary[@(buff)]; 124 | } 125 | 126 | return objcReference; 127 | } 128 | 129 | @end 130 | 131 | 132 | OS_ALWAYS_INLINE 133 | char* demangleCPP(char* mangledSym) { 134 | int status; 135 | char* demangled = abi::__cxa_demangle(mangledSym, nullptr, nullptr, &status); 136 | if (demangled) { 137 | char* symbol{demangled}; 138 | free(demangled); 139 | if (status == 0) { 140 | return symbol; 141 | } 142 | } 143 | return mangledSym; 144 | } 145 | 146 | 147 | void print_symbol(XRMachOLibrary *object, struct nlist_64 * _Nonnull sym, uintptr_t * _Nullable override_addr) { 148 | char * chr = &object.str_symbols[sym->n_un.n_strx]; 149 | BOOL isObjC = NO; 150 | int output_len = 0; 151 | if (xref_options.objectiveC_mode && !strnstr(chr, "_OBJC_CLASS_$_", OBJC_CLASS_LENGTH)) { 152 | return; 153 | } 154 | output_len += printf("0x%011llx ", override_addr ? *override_addr : sym->n_value); 155 | 156 | // Default color for symbol names 157 | DSCOLOR symbolColor = DSCOLOR_CYAN; 158 | 159 | // Demangle and colorize symbols by languages/types 160 | 161 | // I. Demangle mode only (~nm -C option) 162 | if (xref_options.demangle_mode && !xref_options.color) { 163 | // Swfit symbols - Swift stable global mangling: '$s', Swift 4.2: '$S', Swift 4.0: '_T0' 164 | // (Ref: https://github.com/apple/swift/blob/master/docs/ABI/Mangling.rst) 165 | if (strnstr(chr, "_$s", 3) || strnstr(chr, "_swift", 6)) { 166 | std::string outDemangledstring; 167 | dshelpers::compact_demangle(chr, outDemangledstring); 168 | chr = &outDemangledstring[0]; 169 | // C++ symbols 170 | } else if (strnstr(chr, "__Z", 3)) { 171 | chr = demangleCPP(chr); 172 | } 173 | // II. Catagolrize symbols and types with colors 174 | } else if (xref_options.color) { 175 | // Swfit symbols in Red 176 | if (strnstr(chr, "_$s", 3) || strnstr(chr, "_swift", 6)) { 177 | symbolColor = DSCOLOR_RED; 178 | // Demangle Swfit symbols 179 | if (xref_options.demangle_mode) { 180 | std::string outDemangledstring; 181 | dshelpers::compact_demangle(chr, outDemangledstring); 182 | chr = &outDemangledstring[0]; 183 | } 184 | // Objective-C symbols in Green 185 | } else if (strnstr(chr, "_OBJC", 5) || strnstr(chr, "_objc", 5)) { 186 | symbolColor = DSCOLOR_GREEN; 187 | // C++ symbols in Magenta 188 | } else if (strnstr(chr, "__Z", 3)) { 189 | symbolColor = DSCOLOR_MAGENTA; 190 | // Demangle C++ symbols 191 | if (xref_options.demangle_mode) { 192 | chr = demangleCPP(chr); 193 | } 194 | // Constants in Light Yellow 195 | } else if (strnstr(chr, "_k", 3)) { 196 | symbolColor = DSCOLOR_YELLOW_LIGHT; 197 | } 198 | } 199 | 200 | if (xref_options.objectiveC_mode) { 201 | chr += OBJC_CLASS_LENGTH; 202 | isObjC = YES; 203 | } 204 | 205 | // nm -x option 206 | if (xref_options.verbose >= VERBOSE_3) { 207 | output_len += printf("%02x %02x %04x ", sym->n_type, sym->n_sect, sym->n_desc); 208 | } 209 | 210 | // Print the library path if verbose 211 | int libIndex = GET_LIBRARY_ORDINAL(sym->n_desc); 212 | if (xref_options.verbose >= VERBOSE_1) { 213 | if (sym->n_type & N_SECT && sym->n_sect) { 214 | struct section_64 * sec = (struct section_64 *)object.sectionCommandsArray[sym->n_sect].longValue; 215 | output_len += printf("%s%s.%s%s ", dcolor(DSCOLOR_GRAY), sec->segname, sec->sectname, color_end()); 216 | } else if (libIndex > 0 && (sym->n_type & N_TYPE) == N_UNDF) { 217 | 218 | const char *libName; 219 | if (libIndex == DYNAMIC_LOOKUP_ORDINAL) { 220 | libName = "?????????"; 221 | } else { 222 | libName = xref_options.verbose == 1 ? basename((char*)[object.depdencies[libIndex] UTF8String]) : [object.depdencies[libIndex] UTF8String]; 223 | } 224 | output_len += printf("%s%s%s: ", dcolor(DSCOLOR_YELLOW), libName, color_end()); 225 | } 226 | } 227 | 228 | // The actual symbol 229 | printf("%s%s%s ", dcolor(symbolColor), chr, color_end()); 230 | 231 | // If local ObjC class, print parent class 232 | if (isObjC && xref_options.objectiveC_mode && sym->n_value) { 233 | XRBindSymbol * objc_ref = [object objCSuperClassFromSymbol:sym]; 234 | const char* superclassName = [[objc_ref shortName] UTF8String]; 235 | printf(": %s%s%s",dcolor(DSCOLOR_GREEN), superclassName? superclassName : "", color_end()); 236 | } 237 | 238 | putchar('\n'); 239 | } 240 | -------------------------------------------------------------------------------- /dsdump/DSXRLibrary+PID_Info.m: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary+PID_Info.m 3 | // xref 4 | // 5 | // Created by Derek Selander on 5/3/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import "XRMachOLibrary+PID_Info.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | #include 19 | #include 20 | //#include "dyld_process_info_internal.h" 21 | 22 | @implementation XRMachOLibrary (PID_Info) 23 | 24 | 25 | 26 | 27 | void getRegions(task_t pid_task, mach_vm_address_t *address) { 28 | 29 | // getNames(pid_task); 30 | // mach_vm_address_t a_addr = 0; 31 | // struct proc_regionwithpathinfo regioninfo; 32 | // mach_vm_address_t address = 0; 33 | 34 | // vm_info_region_64_t region; 35 | // mach_msg_type_number_t objectsCnt = 0; 36 | mach_vm_size_t size = 0; 37 | // task_t pid_task; 38 | 39 | pid_t pid; 40 | pid_for_task(pid_task, &pid); 41 | 42 | vm_region_submap_short_info_data_64_t submap_info; 43 | mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; 44 | natural_t depth = 9999; // vmmap does this so, why not... 45 | 46 | 47 | // if (!depth) { 48 | // depth = &d; 49 | // } 50 | // mach_vm_purgable_control(<#vm_map_t target_task#>, <#mach_vm_address_t address#>, vm_purgable_t control, <#int *state#>) 51 | // proc_pidinfo 52 | // kern_return_t kr = mach_vm_region_recurse(mach_task_self(), &a_addr, &size, &depth, &submap_info, &count); 53 | while ((KERN_SUCCESS == mach_vm_region_recurse(pid_task, address, &size, &depth, (vm_region_recurse_info_64_t)&submap_info, &count))) { 54 | 55 | assert(count == 0xc); 56 | 57 | // if (submap_info.share_mode == SM_TRUESHARED) { 58 | // printf("yay shared\n"); 59 | // } 60 | 61 | // if (submap_info.user_tag <= VM_MEMORY_MALLOC_LARGE_REUSED) { 62 | // *address += size; 63 | // continue; 64 | // } 65 | // if (*address <= glob_addr && glob_addr <= (*address + size)) { 66 | // printf("yay"); 67 | // } 68 | 69 | 70 | 71 | // printf("submap %d\n", submap_info.is_submap); 72 | 73 | // Da fuk? is_submap doesn't match output, should this be packed or a legit Apple bug? 74 | // if ((long)submap_info.is_submap > 0) { 75 | // if (submap_info.is_submap) { 76 | // printf("submap %x !\n", submap_info.is_submap); 77 | // mach_vm_address_t tmp_addr = *address; 78 | //// getRegions(pid_task, &tmp_addr); 79 | // } 80 | 81 | struct proc_regionwithpathinfo reginfo; 82 | __unused int retval = proc_pidinfo(pid, PROC_PIDREGIONPATHINFO, *address, ®info, sizeof(struct proc_regionwithpathinfo)); 83 | printf("%*s%016llx-%016llx tag: %x %s\n", depth * 4, "", *address, *address + size, submap_info.user_tag, (reginfo.prp_vip.vip_path)); 84 | // depth = 9999; 85 | // depth++; 86 | if (submap_info.is_submap) { 87 | depth++; 88 | // depth = 9999; 89 | } else { 90 | (*address) += size; 91 | } 92 | size = 0; 93 | 94 | // if (addr <= glob_addr && glob_addr <= addr + size) { 95 | // printf("yay"); 96 | // } 97 | // printf("%.*s0x%08llx - 0x%08llx %s\n", depth* 4, " ",*address, *address + size, basename(reginfo.prp_vip.vip_path)); 98 | 99 | // size = 0; 100 | // mach_vm_address_t addr = *address; 101 | } 102 | 103 | exit(0); 104 | } 105 | + (void)load { 106 | 107 | 108 | // proc_pidinfo(pid, PROC_PIDLISTFDS, 0, procFDInfo, bufferSize); 109 | // int numberOfProcFDs = bufferSize / PROC_PIDLISTFD_SIZE; 110 | 111 | // pid_t pid = getpid(); 112 | 113 | // 114 | // struct proc_taskallinfo ff; 115 | // int sz = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, &ff, PROC_PIDTASKALLINFO_SIZE); 116 | // 117 | // 118 | // struct proc_workqueueinfo workqueue; 119 | // sz = proc_pidinfo(pid, PROC_PIDWORKQUEUEINFO, 0, &workqueue, PROC_PIDWORKQUEUEINFO_SIZE); 120 | // 121 | // // Get all them threads 122 | // sz = proc_pidinfo(pid, PROC_PIDWORKQUEUEINFO, 0, &workqueue, PROC_PIDWORKQUEUEINFO_SIZE); 123 | // 124 | // uint64_t threads; 125 | // sz = proc_pidinfo(pid, PROC_PIDLISTTHREADS, 0, &threads, PROC_PIDLISTTHREADS_SIZE); 126 | // 127 | // struct proc_threadinfo thread_info; 128 | // sz = proc_pidinfo(pid, PROC_PIDTHREADINFO, 0, &thread_info, PROC_PIDTHREADINFO_SIZE); 129 | // 130 | // char pidlistfd[0x400]; 131 | // sz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, &pidlistfd, PROC_PIDLISTFD_SIZE); 132 | // 133 | // uintptr_t unknown[5]; // private 134 | // sz = proc_pidinfo(pid, 0x14 /*0x20*/, 0, unknown, 0x28); 135 | // 136 | // 137 | //// struct proc_threadinfo thread_info; 138 | // sz = proc_pidinfo(pid, PROC_PIDTHREADINFO, threads, &thread_info, PROC_PIDTHREADINFO_SIZE); 139 | 140 | //////////////////////////////////////////////////////////////////////////////////// 141 | 142 | 143 | // if (!pd) { perror("couldn't find pid\n"); exit(1); } 144 | // mach_vm_address_t address = 0; 145 | // 146 | // task_t pid_task = 0; 147 | // kern_return_t kr = task_for_pid(mach_task_self(), pd, &pid_task); 148 | // if (kr != KERN_SUCCESS) { 149 | // perror("task_for_pid\n"); 150 | // return; 151 | // } 152 | // getRegions(pid_task, &address); 153 | /////////////////////////////////////////////////////////////////////////////// 154 | // getNoRecurseRegions(pd, &address, 0); 155 | // printf("ok,"); 156 | 157 | // sz = proc_pidinfo(0x2FE, PROC_PIDREGIONPATHINFO, 0, ®ioninfo, PROC_PIDREGIONPATHINFO_SIZE); 158 | 159 | // // Figure out the size of the buffer needed to hold the list of open FDs 160 | // int bufferSize = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, 0, 0); 161 | // if (bufferSize == -1) { 162 | //// printf(UNABLE_TO_GET_PROC_FDS, pid); 163 | //// return 1; 164 | // perror("proc_pidinfo"); 165 | // } 166 | // 167 | // // Get the list of open FDs 168 | // struct proc_fdinfo *procFDInfo = (struct proc_fdinfo *)malloc(bufferSize); 169 | // if (!procFDInfo) { 170 | //// printf(OUT_OF_MEMORY, bufferSize); 171 | //// return 1; 172 | // perror("malloc"); 173 | // } 174 | // proc_pidinfo(pid, PROC_PIDLISTFDS, 0, procFDInfo, bufferSize); 175 | // int numberOfProcFDs = bufferSize / PROC_PIDLISTFD_SIZE; 176 | // 177 | // int i; 178 | // for(i = 0; i < numberOfProcFDs; i++) { 179 | // if(procFDInfo[i].proc_fdtype == PROX_FDTYPE_VNODE) { 180 | // // A file is open 181 | // struct vnode_fdinfowithpath vnodeInfo; 182 | // int bytesUsed = proc_pidfdinfo(pid, procFDInfo[i].proc_fd, PROC_PIDFDVNODEPATHINFO, &vnodeInfo, PROC_PIDFDVNODEPATHINFO_SIZE); 183 | // if (bytesUsed == PROC_PIDFDVNODEPATHINFO_SIZE) { 184 | // printf(OPEN_FILE, vnodeInfo.pvip.vip_path); 185 | // } 186 | // } else if(procFDInfo[i].proc_fdtype == PROX_FDTYPE_SOCKET) { 187 | // // A socket is open 188 | // struct socket_fdinfo socketInfo; 189 | // int bytesUsed = proc_pidfdinfo(pid, procFDInfo[i].proc_fd, PROC_PIDFDSOCKETINFO, &socketInfo, PROC_PIDFDSOCKETINFO_SIZE); 190 | // if (bytesUsed == PROC_PIDFDSOCKETINFO_SIZE) { 191 | // if(socketInfo.psi.soi_family == AF_INET && socketInfo.psi.soi_kind == SOCKINFO_TCP) { 192 | // int localPort = (int)ntohs(socketInfo.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport); 193 | // int remotePort = (int)ntohs(socketInfo.psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport); 194 | // if (remotePort == 0) { 195 | // // Remote port will be 0 when the FD represents a listening socket 196 | // printf(LISTENING_ON_PORT, localPort); 197 | // } else { 198 | // // Remote port will be non-0 when the FD represents communication with a remote socket 199 | // printf(OPEN_SOCKET, localPort, remotePort); 200 | // } 201 | // } 202 | // } 203 | // } 204 | // } 205 | 206 | 207 | 208 | // for (int i = 0; i < num_pids; i++) { 209 | 210 | // pid_t pid = pids[i]; 211 | //////////////////////////////////////////////////////////// 212 | // struct proc_regionwithpathinfo info = {}; 213 | // int k = proc_pidinfo(pid, PROC_PIDREGIONPATHINFO, 0, &info, PROC_PIDREGIONPATHINFO_SIZE); 214 | // if (k < 0) { 215 | // printf("test\n"); 216 | // } 217 | // long cur = info.prp_prinfo.pri_address + info.prp_prinfo.pri_size; 218 | // uintptr_t previous = 0; 219 | // char cur_prot[4] = {}; 220 | // char max_prot[4] = {}; 221 | // 222 | // 223 | // while (previous != info.prp_prinfo.pri_address) { 224 | // previous = info.prp_prinfo.pri_address; 225 | // get_rwx_string(info.prp_prinfo.pri_protection, cur_prot); 226 | // get_rwx_string(info.prp_prinfo.pri_max_protection, max_prot); 227 | // char *path = info.prp_vip.vip_path; 228 | // if (path[0] != '\00') { 229 | // // if (strcmp(basename(path), libName) == 0) { 230 | // // proc_pidinfo(pid, PROC_PIDREGIONPATHINFO, 0, &info, PROC_PIDREGIONPATHINFO_SIZE); 231 | // // printf("%s\n", info.prp_vip.vip_path); 232 | // // break; 233 | // // } 234 | // printf("0x%011llx-0x%011llx %s/%s %s\n", 235 | // info.prp_prinfo.pri_address, 236 | // info.prp_prinfo.pri_address + info.prp_prinfo.pri_size, 237 | // cur_prot, 238 | // max_prot, 239 | // info.prp_vip.vip_path); 240 | // 241 | // } 242 | // 243 | // 244 | // __unused long a = proc_pidinfo(getpid(), PROC_PIDREGIONPATHINFO, cur, &info, PROC_PIDREGIONPATHINFO_SIZE); 245 | // 246 | // 247 | // cur = info.prp_prinfo.pri_address + info.prp_prinfo.pri_size + 1; 248 | // 249 | // 250 | // } 251 | 252 | 253 | 254 | } 255 | 256 | @end 257 | -------------------------------------------------------------------------------- /dsdump/Ripped off/objc-typeencoding.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1999-2007 Apple Inc. All Rights Reserved. 3 | * 4 | * @APPLE_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apple Public Source License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://www.opensource.apple.com/apsl/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPLE_LICENSE_HEADER_END@ 22 | */ 23 | 24 | /*********************************************************************** 25 | * objc-typeencoding.m 26 | * Parsing of old-style type strings. 27 | **********************************************************************/ 28 | 29 | #include 30 | #include 31 | #import 32 | 33 | /*********************************************************************** 34 | * SubtypeUntil. 35 | * 36 | * Delegation. 37 | **********************************************************************/ 38 | static int SubtypeUntil (const char * type, 39 | char end) 40 | { 41 | int level = 0; 42 | const char * head = type; 43 | 44 | // 45 | while (*type) 46 | { 47 | if (!*type || (!level && (*type == end))) 48 | return (int)(type - head); 49 | 50 | switch (*type) 51 | { 52 | case ']': case '}': case ')': level--; break; 53 | case '[': case '{': case '(': level += 1; break; 54 | } 55 | 56 | type += 1; 57 | } 58 | 59 | // _objc_fatal ("Object: SubtypeUntil: end of type encountered prematurely\n"); 60 | return 0; 61 | } 62 | 63 | 64 | /*********************************************************************** 65 | * SkipFirstType. 66 | **********************************************************************/ 67 | static const char * SkipFirstType (const char * type) 68 | { 69 | while (1) 70 | { 71 | switch (*type++) 72 | { 73 | case 'O': /* bycopy */ 74 | case 'n': /* in */ 75 | case 'o': /* out */ 76 | case 'N': /* inout */ 77 | case 'r': /* const */ 78 | case 'V': /* oneway */ 79 | case '^': /* pointers */ 80 | break; 81 | 82 | case '@': /* objects */ 83 | if (type[0] == '?') type++; /* Blocks */ 84 | return type; 85 | 86 | /* arrays */ 87 | case '[': 88 | while ((*type >= '0') && (*type <= '9')) 89 | type += 1; 90 | return type + SubtypeUntil (type, ']') + 1; 91 | 92 | /* structures */ 93 | case '{': 94 | return type + SubtypeUntil (type, '}') + 1; 95 | 96 | /* unions */ 97 | case '(': 98 | return type + SubtypeUntil (type, ')') + 1; 99 | 100 | /* basic types */ 101 | default: 102 | return type; 103 | } 104 | } 105 | } 106 | 107 | 108 | /*********************************************************************** 109 | * encoding_getNumberOfArguments. 110 | **********************************************************************/ 111 | unsigned int 112 | encoding_getNumberOfArguments(const char *typedesc) 113 | { 114 | unsigned nargs; 115 | 116 | // First, skip the return type 117 | typedesc = SkipFirstType (typedesc); 118 | 119 | // Next, skip stack size 120 | while ((*typedesc >= '0') && (*typedesc <= '9')) 121 | typedesc += 1; 122 | 123 | // Now, we have the arguments - count how many 124 | nargs = 0; 125 | while (*typedesc) 126 | { 127 | // Traverse argument type 128 | typedesc = SkipFirstType (typedesc); 129 | 130 | // Skip GNU runtime's register parameter hint 131 | if (*typedesc == '+') typedesc++; 132 | 133 | // Traverse (possibly negative) argument offset 134 | if (*typedesc == '-') 135 | typedesc += 1; 136 | while ((*typedesc >= '0') && (*typedesc <= '9')) 137 | typedesc += 1; 138 | 139 | // Made it past an argument 140 | nargs += 1; 141 | } 142 | 143 | return nargs; 144 | } 145 | 146 | /*********************************************************************** 147 | * encoding_getSizeOfArguments. 148 | **********************************************************************/ 149 | unsigned 150 | encoding_getSizeOfArguments(const char *typedesc) 151 | { 152 | unsigned stack_size; 153 | 154 | // Get our starting points 155 | stack_size = 0; 156 | 157 | // Skip the return type 158 | typedesc = SkipFirstType (typedesc); 159 | 160 | // Convert ASCII number string to integer 161 | while ((*typedesc >= '0') && (*typedesc <= '9')) 162 | stack_size = (stack_size * 10) + (*typedesc++ - '0'); 163 | 164 | return stack_size; 165 | } 166 | 167 | 168 | /*********************************************************************** 169 | * encoding_getArgumentInfo. 170 | **********************************************************************/ 171 | unsigned int 172 | encoding_getArgumentInfo(const char *typedesc, unsigned int arg, 173 | const char **type, int *offset) 174 | { 175 | unsigned nargs = 0; 176 | int self_offset = 0; 177 | bool offset_is_negative = NO; 178 | 179 | // First, skip the return type 180 | typedesc = SkipFirstType (typedesc); 181 | 182 | // Next, skip stack size 183 | while ((*typedesc >= '0') && (*typedesc <= '9')) 184 | typedesc += 1; 185 | 186 | // Now, we have the arguments - position typedesc to the appropriate argument 187 | while (*typedesc && nargs != arg) 188 | { 189 | 190 | // Skip argument type 191 | typedesc = SkipFirstType (typedesc); 192 | 193 | if (nargs == 0) 194 | { 195 | // Skip GNU runtime's register parameter hint 196 | if (*typedesc == '+') typedesc++; 197 | 198 | // Skip negative sign in offset 199 | if (*typedesc == '-') 200 | { 201 | offset_is_negative = YES; 202 | typedesc += 1; 203 | } 204 | else 205 | offset_is_negative = NO; 206 | 207 | while ((*typedesc >= '0') && (*typedesc <= '9')) 208 | self_offset = self_offset * 10 + (*typedesc++ - '0'); 209 | if (offset_is_negative) 210 | self_offset = -(self_offset); 211 | 212 | } 213 | 214 | else 215 | { 216 | // Skip GNU runtime's register parameter hint 217 | if (*typedesc == '+') typedesc++; 218 | 219 | // Skip (possibly negative) argument offset 220 | if (*typedesc == '-') 221 | typedesc += 1; 222 | while ((*typedesc >= '0') && (*typedesc <= '9')) 223 | typedesc += 1; 224 | } 225 | 226 | nargs += 1; 227 | } 228 | 229 | if (*typedesc) 230 | { 231 | int arg_offset = 0; 232 | 233 | *type = typedesc; 234 | typedesc = SkipFirstType (typedesc); 235 | 236 | if (arg == 0) 237 | { 238 | *offset = 0; 239 | } 240 | 241 | else 242 | { 243 | // Skip GNU register parameter hint 244 | if (*typedesc == '+') typedesc++; 245 | 246 | // Pick up (possibly negative) argument offset 247 | if (*typedesc == '-') 248 | { 249 | offset_is_negative = YES; 250 | typedesc += 1; 251 | } 252 | else 253 | offset_is_negative = NO; 254 | 255 | while ((*typedesc >= '0') && (*typedesc <= '9')) 256 | arg_offset = arg_offset * 10 + (*typedesc++ - '0'); 257 | if (offset_is_negative) 258 | arg_offset = - arg_offset; 259 | 260 | *offset = arg_offset - self_offset; 261 | } 262 | 263 | } 264 | 265 | else 266 | { 267 | *type = 0; 268 | *offset = 0; 269 | } 270 | 271 | return nargs; 272 | } 273 | 274 | 275 | void 276 | encoding_getReturnType(const char *t, char *dst, size_t dst_len) 277 | { 278 | size_t len; 279 | const char *end; 280 | 281 | if (!dst) return; 282 | if (!t) { 283 | strncpy(dst, "", dst_len); 284 | return; 285 | } 286 | 287 | end = SkipFirstType(t); 288 | len = end - t; 289 | strncpy(dst, t, MIN(len, dst_len)); 290 | if (len < dst_len) memset(dst+len, 0, dst_len - len); 291 | } 292 | 293 | /******************************************** *************************** 294 | * encoding_copyReturnType. Returns the method's return type string 295 | * on the heap. 296 | **********************************************************************/ 297 | char * 298 | encoding_copyReturnType(const char *t) 299 | { 300 | size_t len; 301 | const char *end; 302 | char *result; 303 | 304 | if (!t) return NULL; 305 | 306 | end = SkipFirstType(t); 307 | len = end - t; 308 | result = (char *)malloc(len + 1); 309 | strncpy(result, t, len); 310 | result[len] = '\0'; 311 | return result; 312 | } 313 | 314 | 315 | void 316 | encoding_getArgumentType(const char *t, unsigned int index, 317 | char *dst, size_t dst_len) 318 | { 319 | size_t len; 320 | const char *end; 321 | int offset; 322 | 323 | if (!dst) return; 324 | if (!t) { 325 | strncpy(dst, "", dst_len); 326 | return; 327 | } 328 | 329 | encoding_getArgumentInfo(t, index, &t, &offset); 330 | 331 | if (!t) { 332 | strncpy(dst, "", dst_len); 333 | return; 334 | } 335 | 336 | end = SkipFirstType(t); 337 | len = end - t; 338 | strncpy(dst, t, MIN(len, dst_len)); 339 | if (len < dst_len) memset(dst+len, 0, dst_len - len); 340 | } 341 | 342 | 343 | /*********************************************************************** 344 | * encoding_copyArgumentType. Returns a single argument's type string 345 | * on the heap. Argument 0 is `self`; argument 1 is `_cmd`. 346 | **********************************************************************/ 347 | char * 348 | encoding_copyArgumentType(const char *t, unsigned int index) 349 | { 350 | size_t len; 351 | const char *end; 352 | char *result; 353 | int offset; 354 | 355 | if (!t) return NULL; 356 | 357 | encoding_getArgumentInfo(t, index, &t, &offset); 358 | 359 | if (!t) return NULL; 360 | 361 | end = SkipFirstType(t); 362 | len = end - t; 363 | result = (char *)malloc(len + 1); 364 | strncpy(result, t, len); 365 | result[len] = '\0'; 366 | return result; 367 | } 368 | -------------------------------------------------------------------------------- /dsdump/Properties.mm: -------------------------------------------------------------------------------- 1 | // 2 | // Properties.cpp 3 | // dsdump 4 | // 5 | // Created by Derek Selander on 4/28/20. 6 | // Copyright © 2020 Selander. All rights reserved. 7 | // 8 | 9 | #import "Properties.h" 10 | #import "Methods.h" 11 | #import "objc-layout.h" 12 | 13 | static const char * kPropertyType = "T"; 14 | static const char * kPropertyOverridenGetter = "G"; 15 | static const char * kPropertyOverridenSetter = "S"; 16 | static const char * kPropertyIsReadOnly = "R"; 17 | static const char * kPropertyIsNonatomic = "N"; 18 | static const char * kPropertyIsAtomic = "A"; 19 | static const char * kPropertyCopy = "C"; 20 | static const char * kPropertyIsStrong = "&"; 21 | static const char * kPropertyIsWeak = "W"; 22 | static const char * kPropertyIVarName = "V"; 23 | static const char * kPropertyClass = "#"; 24 | static const char * kPropertyNSUInteger = "Q"; 25 | 26 | char *copyPropertyAttributeValue(const char *attrs, const char *name); 27 | 28 | static objc_property_attribute_t * 29 | copyPropertyAttributeList(const char *attrs, unsigned int *outCount); 30 | /******************************************************************************** 31 | // Properties 32 | ********************************************************************************/ 33 | 34 | static void printType(const char *type) { 35 | if (!type) { 36 | return; 37 | } 38 | 39 | auto length = strlen(type); 40 | 41 | // First check if it looks like an ObjC class i.e. T=@"NSObject" 42 | if (length > 3 && type[0] == '@' && type[1] == '"' && type[length - 1] == '"') { 43 | printf(" %s%.*s *%s", dcolor(DSCOLOR_DARK_GREEN), (int)(length-3), (type+2), color_end()); 44 | } else { 45 | printf(" %s%s%s ", dcolor(DSCOLOR_DARK_GREEN), translate_method_type_to_string((char*)type), color_end()); 46 | } 47 | 48 | // // Check the Type, T, first for a speed lookup... 49 | //// if (attribute.name && attribute.name[0] == 'T') { 50 | //// 51 | //// return; 52 | //// } 53 | // // if (strcmp(attribute.value, kPropertyClass) == 0) { 54 | // // printf("Class"); 55 | // // } else if ((attribute.value, kPropertyNSUInteger) == 0) { 56 | // // printf("NSUInteger"); 57 | // // } else { 58 | // // printf("%s", attribute.name); 59 | // // } 60 | // 61 | // 62 | // int pointerCount = 0; 63 | // for (int i = 0; i < strlen(type); i++) { 64 | //// if (type[i] == 'i') 65 | // } 66 | } 67 | 68 | static void printPropertyAttributeTypes(const char *propertyAttributes) { 69 | printf(" %s@property %s", dcolor(DSCOLOR_GRAY), color_end()); 70 | auto type = copyPropertyAttributeValue(propertyAttributes, kPropertyType); 71 | 72 | 73 | unsigned int outCount = 0; 74 | auto attrs = copyPropertyAttributeList(propertyAttributes, &outCount); 75 | if (outCount > 1 && strcmp(attrs[outCount -1].name, "V")==0) { 76 | outCount--; 77 | } 78 | 79 | // Assuming the T (type) attribute is always around... 80 | if (outCount > 1 && xref_options.verbose > VERBOSE_4) { 81 | printf("%s(", dcolor(DSCOLOR_GRAY)); 82 | 83 | // Start at 1, skip the type... 84 | for (int i = 1; i < outCount; i++) { 85 | auto attribute = attrs[i]; 86 | 87 | if (strcmp(attribute.name, kPropertyIsStrong) == 0) { 88 | printf("strong"); 89 | } else if (strcmp(attribute.name, kPropertyIsNonatomic) == 0) { 90 | printf("nonatomic"); 91 | } else if (strcmp(attribute.name, kPropertyIsWeak) == 0) { 92 | printf("weak"); 93 | } else if (strcmp(attribute.name, kPropertyIsReadOnly) == 0) { 94 | printf("readonly"); 95 | } else if (strcmp(attribute.name, kPropertyOverridenGetter) == 0) { 96 | printf("getter=%s", attribute.value); 97 | } else if (strcmp(attribute.name, kPropertyOverridenSetter) == 0) { 98 | printf("setter=%s", attribute.value); 99 | } else if (strcmp(attribute.name, kPropertyIsAtomic) == 0) { 100 | printf("atomic"); 101 | } else if (strcmp(attribute.name, kPropertyCopy) == 0) { 102 | printf("copy"); 103 | } 104 | if (i != outCount - 1) { 105 | printf(", "); 106 | } 107 | 108 | } 109 | printf(")%s", color_end()); 110 | } 111 | free(attrs); 112 | 113 | printType(type); 114 | 115 | 116 | auto ivarName = copyPropertyAttributeValue(propertyAttributes, kPropertyIVarName); 117 | 118 | } 119 | 120 | static void printProperty(property_t property) { 121 | auto propertyName = property.name->disk(); 122 | auto propertyAttributes = property.attributes->disk(); 123 | printPropertyAttributeTypes(propertyAttributes); 124 | printf("%s%s%s\n", dcolor(DSCOLOR_GREEN), propertyName, color_end()); 125 | } 126 | 127 | void dumpObjCPropertiesWithResolvedAddress(property_list_t* propertiesList) { 128 | if (xref_options.verbose < VERBOSE_4) { 129 | return; 130 | } 131 | 132 | if (propertiesList == nullptr) { 133 | return; 134 | } 135 | 136 | auto propertiesListDisk = propertiesList->disk(); 137 | auto count = propertiesListDisk->count; 138 | auto properties = &propertiesListDisk->first_property; 139 | if (properties == nullptr) { 140 | return; 141 | } 142 | 143 | for (int i = 0; i < count; i++) { 144 | auto property = properties[i]; 145 | auto propertyName = property.name->disk(); 146 | auto propertyAttributes = property.attributes->disk(); 147 | printProperty(property); 148 | 149 | //printf("%s %s\n", propertyAttributes, skip_ivar_type_name((char*)propertyAttributes)); 150 | // scan_ivar_type_for_layout(propertyAttributes, 0, 0, <#unsigned char *bits#>, <#long *next_offset#>) 151 | /* 152 | static char *scan_ivar_type_for_layout(char *type, long offset, long bits_size, unsigned char *bits, long *next_offset); 153 | static char *scan_basic_ivar_type(char *type, long *size, long *alignment, bool *is_reference); 154 | 155 | static char *skip_ivar_type_name(char *type); 156 | static char *skip_ivar_struct_name(char *type); 157 | 158 | static char *scan_ivar_type_for_layout(char *type, long offset, long bits_size, unsigned char *bits, long *next_offset); 159 | */ 160 | } 161 | 162 | if (count) { 163 | putchar('\n'); 164 | } 165 | } 166 | 167 | void dumpObjCPropertiesWithResolvedAddress(swift_class* cls) { 168 | if (xref_options.verbose < VERBOSE_4) { 169 | return; 170 | } 171 | 172 | auto clsDisk = cls->disk(); 173 | auto rodata = clsDisk->rodata(); 174 | if (rodata == nullptr) { 175 | return; 176 | } 177 | 178 | auto propertiesList = rodata->disk()->baseProperties; 179 | dumpObjCPropertiesWithResolvedAddress(propertiesList); 180 | } 181 | 182 | void dumpObjCPropertiesWithResolvedAddress(protocol_t* prtl) { 183 | if (xref_options.verbose < VERBOSE_4) { 184 | return; 185 | } 186 | auto properties = prtl->disk()->instanceProperties; 187 | if (properties == nullptr) { 188 | return; 189 | } 190 | dumpObjCPropertiesWithResolvedAddress(properties->disk()); 191 | } 192 | 193 | 194 | 195 | ////////////////////// 196 | 197 | 198 | static unsigned int 199 | iteratePropertyAttributes(const char *attrs, 200 | bool (*fn)(unsigned int index, 201 | void *ctx1, void *ctx2, 202 | const char *name, size_t nlen, 203 | const char *value, size_t vlen), 204 | void *ctx1, void *ctx2) 205 | { 206 | if (!attrs) return 0; 207 | 208 | unsigned int attrcount = 0; 209 | 210 | while (*attrs) { 211 | // Find the next comma-separated attribute 212 | const char *start = attrs; 213 | const char *end = start + strcspn(attrs, ","); 214 | 215 | // Move attrs past this attribute and the comma (if any) 216 | attrs = *end ? end+1 : end; 217 | 218 | 219 | // Skip empty attribute 220 | if (start == end) continue; 221 | 222 | // Process one non-empty comma-free attribute [start,end) 223 | const char *nameStart; 224 | const char *nameEnd; 225 | 226 | 227 | if (*start != '\"') { 228 | // single-char short name 229 | nameStart = start; 230 | nameEnd = start+1; 231 | start++; 232 | } 233 | else { 234 | // double-quoted long name 235 | nameStart = start+1; 236 | nameEnd = nameStart + strcspn(nameStart, "\","); 237 | start++; // leading quote 238 | start += nameEnd - nameStart; // name 239 | if (*start == '\"') start++; // trailing quote, if any 240 | } 241 | 242 | // Process one possibly-empty comma-free attribute value [start,end) 243 | const char *valueStart; 244 | const char *valueEnd; 245 | 246 | 247 | 248 | valueStart = start; 249 | valueEnd = end; 250 | 251 | bool more = (*fn)(attrcount, ctx1, ctx2, 252 | nameStart, nameEnd-nameStart, 253 | valueStart, valueEnd-valueStart); 254 | attrcount++; 255 | if (!more) break; 256 | } 257 | 258 | return attrcount; 259 | } 260 | 261 | 262 | static bool 263 | copyOneAttribute(unsigned int index, void *ctxa, void *ctxs, 264 | const char *name, size_t nlen, const char *value, size_t vlen) 265 | { 266 | objc_property_attribute_t **ap = (objc_property_attribute_t**)ctxa; 267 | char **sp = (char **)ctxs; 268 | 269 | objc_property_attribute_t *a = *ap; 270 | char *s = *sp; 271 | 272 | a->name = s; 273 | memcpy(s, name, nlen); 274 | s += nlen; 275 | *s++ = '\0'; 276 | 277 | a->value = s; 278 | memcpy(s, value, vlen); 279 | s += vlen; 280 | *s++ = '\0'; 281 | 282 | a++; 283 | 284 | *ap = a; 285 | *sp = s; 286 | 287 | return YES; 288 | } 289 | 290 | 291 | objc_property_attribute_t * 292 | copyPropertyAttributeList(const char *attrs, unsigned int *outCount) 293 | { 294 | if (!attrs) { 295 | if (outCount) *outCount = 0; 296 | return nil; 297 | } 298 | 299 | // Result size: 300 | // number of commas plus 1 for the attributes (upper bound) 301 | // plus another attribute for the attribute array terminator 302 | // plus strlen(attrs) for name/value string data (upper bound) 303 | // plus count*2 for the name/value string terminators (upper bound) 304 | unsigned int attrcount = 1; 305 | const char *s; 306 | for (s = attrs; s && *s; s++) { 307 | if (*s == ',') attrcount++; 308 | } 309 | 310 | size_t size = 311 | attrcount * sizeof(objc_property_attribute_t) + 312 | sizeof(objc_property_attribute_t) + 313 | strlen(attrs) + 314 | attrcount * 2; 315 | objc_property_attribute_t *result = (objc_property_attribute_t *) 316 | calloc(size, 1); 317 | 318 | objc_property_attribute_t *ra = result; 319 | char *rs = (char *)(ra+attrcount+1); 320 | 321 | attrcount = iteratePropertyAttributes(attrs, copyOneAttribute, &ra, &rs); 322 | 323 | 324 | 325 | if (attrcount == 0) { 326 | free(result); 327 | result = nil; 328 | } 329 | 330 | if (outCount) *outCount = attrcount; 331 | return result; 332 | } 333 | 334 | 335 | static bool 336 | findOneAttribute(unsigned int index, void *ctxa, void *ctxs, 337 | const char *name, size_t nlen, const char *value, size_t vlen) 338 | { 339 | const char *query = (char *)ctxa; 340 | char **resultp = (char **)ctxs; 341 | 342 | if (strlen(query) == nlen && 0 == strncmp(name, query, nlen)) { 343 | char *result = (char *)calloc(vlen+1, 1); 344 | memcpy(result, value, vlen); 345 | result[vlen] = '\0'; 346 | *resultp = result; 347 | return NO; 348 | } 349 | 350 | return YES; 351 | } 352 | 353 | char *copyPropertyAttributeValue(const char *attrs, const char *name) 354 | { 355 | char *result = nil; 356 | 357 | iteratePropertyAttributes(attrs, findOneAttribute, (void*)name, &result); 358 | 359 | return result; 360 | } 361 | 362 | -------------------------------------------------------------------------------- /dsdump/XRMachOLibrary+ObjectiveC.mm: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary+ObjectiveC.m 3 | // xref 4 | // 5 | // Created by Derek Selander on 4/29/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "XRMachOLibrary+ObjectiveC.h" 12 | #import "XRMachOLibrary+SymbolDumper.h" 13 | #import "objc_.h" 14 | #import "XRMachOLibrary+Swift.h" 15 | #import "XRSymbolEntry.h" 16 | #import "Properties.h" 17 | #import "Protocols.h" 18 | #import "Methods.h" 19 | 20 | #pragma clang diagnostic push 21 | #pragma clang diagnostic ignored "-Weverything" 22 | 23 | #define protected public 24 | #define private public 25 | #define class struct 26 | 27 | #import "swift/Demangling/Demangler.h" 28 | 29 | #undef protected 30 | #undef private 31 | #undef class 32 | 33 | #import "XRMachOLibraryCplusHelpers.h" 34 | #pragma clang diagnostic pop 35 | 36 | 37 | /// Swift uses offset references for ivars when referencing methods 38 | static NSMutableDictionary *__ivarsDictionary = nil; 39 | /// Certain things -debugDescription that every class has, no need to display them 40 | NSDictionary *blacklistedSelectors = nil; 41 | 42 | 43 | @implementation XRMachOLibrary (ObjectiveC) 44 | 45 | + (void)load { 46 | blacklistedSelectors = @{ 47 | @".cxx_destruct" : @YES, 48 | @"description" : @YES, 49 | @"debugDescription" : @YES, 50 | @"hash" : @YES 51 | }; 52 | } 53 | 54 | - (void)dumpObjCClassInfo:(const char *)name resolvedAddress:(swift_class*)cls { 55 | auto rodata = cls->disk()->rodata(); 56 | if (rodata == nullptr) { 57 | return; 58 | } 59 | 60 | uint8_t isMeta = rodata->disk()->flags & RO_META ? 1 : 0 ; 61 | auto methodList = rodata->disk()->baseMethodList; 62 | dumpObjectiveCMethods(methodList, name, isMeta); 63 | } 64 | 65 | /******************************************************************************** 66 | // ivars 67 | ********************************************************************************/ 68 | - (void)dumpObjCInstanceVariablesWithResolvedAddress:(swift_class *)cls { 69 | if (xref_options.verbose <= VERBOSE_4) { 70 | return; 71 | } 72 | auto clsDisk = cls->disk(); 73 | auto rodata = clsDisk->rodata(); 74 | if (rodata == nullptr) { 75 | return; 76 | } 77 | 78 | auto rodataDisk = rodata->disk(); 79 | if (rodataDisk->ivarList == nullptr) { 80 | return; 81 | } 82 | 83 | auto ivarList = rodataDisk->ivarList; 84 | if (ivarList == nullptr) { 85 | return; 86 | } 87 | 88 | auto ivarListDisk = ivarList->disk(); 89 | auto ivarCount = ivarListDisk->count; 90 | __ivarsDictionary = [NSMutableDictionary dictionaryWithCapacity:ivarCount]; 91 | 92 | auto ivars = &ivarListDisk->first_ivar; 93 | 94 | printf("{\n"); 95 | auto ivarsDisk = ivars->disk(); 96 | 97 | for (int i = 0; i < ivarCount; i++) { 98 | auto ivr = &ivarsDisk[i]; 99 | auto ivarOffsetPointer = ivr->offset; 100 | auto ivarOffset = ivarOffsetPointer ? *ivarOffsetPointer->disk() : 0; 101 | auto ivarName = ivr->name->disk(); 102 | auto ivarType = ivr->type->disk(); 103 | __ivarsDictionary[@(ivarOffset)] = [NSString stringWithUTF8String:ivarName]; 104 | printf("\t+0x%04x %s %s (0x%x)\n", ivarOffset, ivarType, ivarName, ivr->size); 105 | } 106 | 107 | printf("}\n"); 108 | } 109 | 110 | /******************************************************************************** 111 | // Objective-C class dump 112 | ********************************************************************************/ 113 | - (void)dumpObjectiveCClasses { 114 | // Defined symbols, will go after the __DATA.__objc_classlist pointers 115 | if (xref_options.defined || !(xref_options.undefined || xref_options.defined)) { 116 | 117 | // Wait till ready...dumpObjectiveCProtocols 118 | dumpObjectiveCProtocols(); 119 | 120 | struct section_64* classSection = payload::sectionsDict["__DATA.__objc_classlist"]; 121 | if (classSection == nullptr) { // iOS 13 ARM64E has some changes... 122 | classSection = payload::sectionsDict["__DATA_CONST.__objc_classlist"]; 123 | } 124 | if (classSection == nullptr) { 125 | printf("no Objective-C classes\n"); 126 | return; 127 | } 128 | 129 | auto classes = payload::LoadToDiskTranslator::Cast(classSection->addr); 130 | char modname[1024]; 131 | for (int i = 0; i < classSection->size / PTR_SIZE; i++) { 132 | 133 | auto resolvedAddress = classes->Get(i); 134 | auto cls = payload::Cast(resolvedAddress); 135 | if (xref_options.swift_mode && !cls->isSwift()) { 136 | continue; 137 | } 138 | 139 | const char *name = cls->GetName(); 140 | if (!ContainsFilteredWords(name)) { 141 | continue; 142 | } 143 | 144 | d_offsets off; 145 | if (xref_options.swift_mode) { 146 | std::string str; 147 | dshelpers::simple_demangle(name, str); 148 | printf("0x%011lx %s%s%s", resolvedAddress, dcolor(DSCOLOR_CYAN), dshelpers::simple_demangle(name, str), color_end()); 149 | } else { 150 | printf("0x%011lx %s%s%s", resolvedAddress, dcolor(DSCOLOR_CYAN), name, color_end()); 151 | } 152 | 153 | XRBindSymbol *objcReference = nil; 154 | auto color = dcolor(DSCOLOR_GREEN); 155 | 156 | /////////////////////////////////////////////////// 157 | // Print out the superclass if any verbose level // 158 | /////////////////////////////////////////////////// 159 | if (xref_options.verbose > VERBOSE_NONE) { 160 | // Check if it's a local symbol first via the dylds binding opcodes.... 161 | auto superClassAddressDisk = &cls->disk()->superclass; 162 | auto superClassAddress = payload::GetLoadAddress(superClassAddressDisk); 163 | objcReference = self.addressObjCDictionary[@(superClassAddress)]; 164 | const char *superclassName = objcReference.shortName.UTF8String; 165 | 166 | // Will happen if the superclass is implemented in the same module 167 | if (!superclassName) { 168 | auto supercls = cls->disk()->superclass; 169 | if (supercls) { 170 | if (supercls->validAddress()) { 171 | superclassName = supercls->disk()->GetName(); 172 | color = dcolor(DSCOLOR_MAGENTA); 173 | } else { 174 | //printf("\nproblem derek: %s\n", cls->GetName()); 175 | } 176 | 177 | } 178 | } 179 | color = superclassName ? color : dcolor(DSCOLOR_RED); 180 | std::string str; 181 | dshelpers::simple_demangle(superclassName, str); 182 | if (xref_options.swift_mode && demangleSwiftName(superclassName, &off)) { 183 | printf(" : %s%s%s%s", color, modname, superclassName, color_end()); 184 | } else { 185 | auto rodata = cls->disk()->rodata(); 186 | 187 | if (!superclassName && !(rodata->disk()->flags & RO_ROOT)) { 188 | superclassName = ""; 189 | color = dcolor(DSCOLOR_RED); 190 | } 191 | auto context = Context(); 192 | auto str = StringRef( superclassName); 193 | printf(" : %s%s%s", color, superclassName ? context.demangleSymbolAsString(str).c_str() : "", color_end()); 194 | } 195 | } 196 | 197 | ////////////////////////////////////////////////////// 198 | // Print the libraries of Objc classes if verbose 4 // 199 | ////////////////////////////////////////////////////// 200 | if (xref_options.verbose > VERBOSE_3) { 201 | char *libName = objcReference && objcReference.libOrdinal ? (char*)self.depdencies[objcReference.libOrdinal].UTF8String : NULL; 202 | if (libName) { 203 | printf(" %s%s%s", dcolor(DSCOLOR_YELLOW), libName, color_end()); 204 | } 205 | } 206 | 207 | /////////////////////// 208 | // Dump protocols... // 209 | /////////////////////// 210 | if (!listProtocolsForObjectiveCClass(cls)) { 211 | putchar('\n'); 212 | } 213 | 214 | // property then method dumping logic dumbing logic 215 | if (xref_options.verbose > VERBOSE_2) { 216 | 217 | // Dump ivars... 218 | [self dumpObjCInstanceVariablesWithResolvedAddress:cls]; 219 | 220 | // Dump properties... 221 | dumpObjCPropertiesWithResolvedAddress(cls); 222 | 223 | // Dumps class methods first... 224 | auto metaCls = cls->disk()->isa(); 225 | [self dumpObjCClassInfo:name resolvedAddress:metaCls]; 226 | 227 | // Then Dump instance methods... 228 | [self dumpObjCClassInfo:name resolvedAddress:cls]; 229 | 230 | putchar('\n'); 231 | } 232 | } 233 | } 234 | 235 | [self dumpObjectiveCCategories]; 236 | 237 | // Undefined symbols, use the symbol table 238 | struct nlist_64 *symbols = self.symbols; 239 | if (xref_options.undefined || !(xref_options.undefined || xref_options.defined)) { 240 | for (int i = self.dysymtab->iundefsym; i < self.dysymtab->nundefsym + self.dysymtab->iundefsym; i++) { 241 | struct nlist_64 sym = symbols[i]; 242 | char *chr = &self.str_symbols[sym.n_un.n_strx]; 243 | 244 | if (!strnstr(chr, "_OBJC_CLASS_$_", OBJC_CLASS_LENGTH)) { 245 | continue; 246 | } 247 | 248 | if (!ContainsFilteredWords(&chr[OBJC_CLASS_LENGTH])) { 249 | continue; 250 | } 251 | print_symbol(self, &sym, NULL); 252 | } 253 | } 254 | } 255 | 256 | /******************************************************************************** 257 | // Categories 258 | ********************************************************************************/ 259 | 260 | - (void)dumpObjectiveCCategories { 261 | if (xref_options.undefined) { 262 | return; 263 | } 264 | struct section_64* categoriesSection = payload::sectionsDict["__DATA.__objc_catlist"]; 265 | if (!categoriesSection) { 266 | categoriesSection = payload::sectionsDict["__DATA_CONST.__objc_catlist"]; 267 | } 268 | if (!categoriesSection) { 269 | return; 270 | } 271 | 272 | auto categoriesDisk = payload::LoadToDiskTranslator::Cast(categoriesSection->addr)->disk(); 273 | for (int i = 0; i < categoriesSection->size / PTR_SIZE; i++) { 274 | auto category = payload::Cast(categoriesDisk[i]); 275 | if (category == nullptr) { 276 | continue; 277 | } 278 | auto categoryDisk = category->disk(); 279 | const char * clsName = categoryDisk->cls->validAddress() ? categoryDisk->cls->GetName() : NULL; 280 | 281 | 282 | auto color = dcolor(DSCOLOR_CYAN); 283 | if (!clsName) { // IF the class is implemented in a different module... 284 | auto superClassAddress = payload::GetLoadAddress(&categoryDisk->cls); 285 | XRBindSymbol *objcReference = self.addressObjCDictionary[@(superClassAddress)]; 286 | if (!objcReference) { 287 | clsName = ""; 288 | color = dcolor(DSCOLOR_RED); 289 | } else { 290 | clsName = objcReference.shortName.UTF8String; 291 | } 292 | } 293 | auto categoryName = categoryDisk->name->disk(); 294 | if (!ContainsFilteredWords(clsName) && !ContainsFilteredWords(categoryName)) { 295 | continue; 296 | } 297 | 298 | printf("0x%011lx %s%s(%s)%s\n", reinterpret_cast(category->strip_PAC()), color, clsName, categoryName, color_end()); 299 | 300 | if (xref_options.verbose <= VERBOSE_2) { 301 | continue; 302 | } 303 | 304 | auto dumpCategoryMethods = [&](method_list* methodsList, bool isClassMethod) { 305 | if (methodsList == nullptr) { 306 | return; 307 | } 308 | auto methodsListDisk = methodsList->disk(); 309 | auto count = methodsListDisk->count; 310 | auto c = isClassMethod ? '+' : '-'; 311 | 312 | if (xref_options.verbose > VERBOSE_2) { 313 | printf("\t// %s methods\n", isClassMethod ? "class" : "instance"); 314 | } 315 | for (int j = 0; j < count; j++) { 316 | auto method = methodsListDisk->GetMethod(j, false); 317 | auto methodName = method->getName()->disk(); 318 | printf("\t%s0x%011lx%s %s%c[%s(%s) %s]%s\n", dcolor(DSCOLOR_GRAY), (uintptr_t)method->getImp()->strip_PAC(), color_end(), dcolor(DSCOLOR_BOLD), c, clsName, categoryName, methodName, color_end()); 319 | } 320 | putchar('\n'); 321 | }; 322 | 323 | dumpCategoryMethods(category->disk()->classMethods, true); 324 | dumpCategoryMethods(category->disk()->instanceMethods, false); 325 | 326 | } 327 | } 328 | 329 | @end 330 | 331 | /******************************************************************************** 332 | // Protocols 333 | ********************************************************************************/ 334 | 335 | 336 | 337 | 338 | BOOL demangleSwiftName(const char *name, d_offsets *f) { 339 | 340 | if (!name || strlen(name) == 0) { 341 | f->success = NO; 342 | return NO; 343 | } 344 | if (!strstr(name, "_OBJC_CLASS_$__TtCs")) { 345 | f->success = NO; 346 | return NO; 347 | } 348 | 349 | int index = strlen("_TtC"); 350 | f->mod_off = index; 351 | while (name[index] >= '0' && name[index] <= '9') { 352 | index++; 353 | } 354 | char md_len[5] = {}; 355 | strncpy(md_len, &name[f->mod_off], index - f->mod_off); 356 | f->mod_len = atoi(md_len); 357 | 358 | index += f->mod_len ; 359 | while (name[index] >= '0' && name[index] <= '9') { 360 | index++; 361 | } 362 | f->cls_off = index; 363 | f->mod_off++; 364 | return YES; 365 | } 366 | -------------------------------------------------------------------------------- /dsdump/Ripped off/objc_.h: -------------------------------------------------------------------------------- 1 | // 2 | // objc_.h 3 | // xref 4 | // 5 | // Created by Derek Selander on 5/13/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #ifndef objc__h 10 | #define objc__h 11 | 12 | #define FAST_DATA_MASK 0x00007ffffffffff8UL 13 | 14 | #define RO_META (1<<0) 15 | #define RO_ROOT (1<<1) 16 | 17 | #import "payload.hpp" 18 | 19 | #pragma clang diagnostic push 20 | #pragma clang diagnostic ignored "-Weverything" 21 | 22 | #define protected public 23 | #define private public 24 | //#define class struct 25 | 26 | #import "swift/ABI/MetadataValues.h" 27 | #import "swift/ABI/Metadata.h" 28 | 29 | #undef protected 30 | #undef private 31 | //#undef class 32 | 33 | 34 | #pragma clang diagnostic pop 35 | 36 | #import 37 | 38 | /***************************************************************** 39 | protocols 40 | *****************************************************************/ 41 | 42 | typedef struct method_list method_list_t; 43 | typedef struct property_list property_list_t; 44 | typedef struct protocol_list protocol_list_t; 45 | 46 | struct protocol_t : public payload::LoadToDiskTranslator { 47 | void *isa; 48 | payload::LoadToDiskTranslator *mangledName; 49 | protocol_list_t *protocols; 50 | method_list_t *instanceMethods; 51 | method_list_t *classMethods; 52 | method_list_t *optionalInstanceMethods; 53 | method_list_t *optionalClassMethods; 54 | property_list_t *instanceProperties; 55 | uint32_t size; // sizeof(protocol_t) 56 | uint32_t flags; 57 | // Fields below this point are not always present on disk. 58 | const char **_extendedMethodTypes; 59 | const char *_demangledName; 60 | property_list_t *_classProperties; 61 | }; 62 | 63 | typedef struct protocol_list : public payload::LoadToDiskTranslator { 64 | uintptr_t count; 65 | protocol_t *first_protocol; // variable-size 66 | } protocol_list_t; 67 | 68 | /***************************************************************** 69 | methods 70 | *****************************************************************/ 71 | 72 | typedef struct method : public payload::LoadToDiskTranslator { 73 | payload::LoadToDiskTranslator* name; 74 | payload::LoadToDiskTranslator *types; 75 | // true for isCodePointer, different value for PAC 76 | payload::LoadToDiskTranslator* imp; 77 | 78 | public: 79 | /** 80 | iOS 14/macOS 11 runtime changes 81 | They are now pulling a 'swift' and use relative address offsets for method_t 82 | https://developer.apple.com/videos/play/wwdc2020/10163/ 83 | */ 84 | payload::LoadToDiskTranslator* getName(); 85 | payload::LoadToDiskTranslator* getTypes(); 86 | payload::LoadToDiskTranslator* getImp(); 87 | } method_t; 88 | 89 | typedef struct method_list : public payload::LoadToDiskTranslator { 90 | uint32_t entsizeAndFlags; 91 | uint32_t count; 92 | method_t first_method; 93 | 94 | method_t* GetMethod(int i, bool isProtocol); 95 | } method_list_t; 96 | 97 | /***************************************************************** 98 | ivars 99 | *****************************************************************/ 100 | 101 | typedef struct ivar : public payload::LoadToDiskTranslator { 102 | payload::LoadToDiskTranslator *offset; 103 | payload::LoadToDiskTranslator *name; 104 | payload::LoadToDiskTranslator *type; 105 | // alignment is sometimes -1; use alignment() instead 106 | uint32_t alignment_raw; 107 | uint32_t size; 108 | } ivar_t; 109 | 110 | typedef struct ivar_list : public payload::LoadToDiskTranslator { 111 | uint32_t entsizeAndFlags; 112 | uint32_t count; 113 | payload::LoadToDiskTranslator first_ivar; 114 | } ivar_list_t; 115 | 116 | /***************************************************************** 117 | properties 118 | *****************************************************************/ 119 | 120 | typedef struct property : public payload::LoadToDiskTranslator { 121 | payload::LoadToDiskTranslator *name; 122 | payload::LoadToDiskTranslator *attributes; 123 | } property_t; 124 | 125 | typedef struct property_list : public payload::LoadToDiskTranslator { 126 | uint32_t entsizeAndFlags; 127 | uint32_t count; 128 | property_t first_property; 129 | } property_list_t; 130 | 131 | /***************************************************************** 132 | categories 133 | *****************************************************************/ 134 | 135 | typedef struct swift_class_t swift_class; 136 | 137 | typedef struct category : public payload::LoadToDiskTranslator { 138 | payload::LoadToDiskTranslator* name; 139 | swift_class *cls; 140 | method_list_t *instanceMethods; 141 | method_list_t *classMethods; 142 | protocol_list_t *protocols; 143 | property_list_t *instanceProperties; 144 | // Fields below this point are not always present on disk. 145 | property_list_t *_classProperties; 146 | 147 | method_list_t *methodsForMeta(bool isMeta) { 148 | if (isMeta) return classMethods; 149 | else return instanceMethods; 150 | } 151 | 152 | property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi); 153 | } category_t; 154 | 155 | 156 | /***************************************************************** 157 | class stuff, ro/rw 158 | *****************************************************************/ 159 | 160 | typedef struct class_ro : public payload::LoadToDiskTranslator { 161 | uint32_t flags; 162 | uint32_t instanceStart; 163 | uint32_t instanceSize; 164 | uint32_t reserved; 165 | 166 | const uint8_t * ivarLayout; 167 | 168 | 169 | payload::LoadToDiskTranslator* name; 170 | method_list_t * baseMethodList; 171 | protocol_list_t * baseProtocols; 172 | ivar_list_t* ivarList; 173 | const uint8_t * weakIvarLayout; 174 | property_list_t *baseProperties; 175 | 176 | } class_ro_t; // The structure when on disk or before "realized" in memory 177 | 178 | typedef struct { 179 | uint32_t flags; 180 | uint32_t version; 181 | 182 | const class_ro_t* ro; // class_ro_t* 183 | 184 | void* methods; // method_array_t 185 | void* properties; // property_array_t 186 | void* protocols; // protocol_array_t 187 | void* firstSubclass; // Class 188 | void* nextSiblingClass; // Class 189 | char *demangledName; 190 | 191 | } class_rw_t; // The structure when realize in memory 192 | 193 | 194 | 195 | 196 | typedef enum __attribute__((packed)) { 197 | DescriptorKindModule = 0, 198 | DescriptorKindExtension = 1, 199 | DescriptorKindAnonymous = 2, 200 | DescriptorKindProtocol = 3, 201 | DescriptorKindOpaque = 4, 202 | DescriptorKindClass = 16, 203 | DescriptorKindStruct = 17, 204 | DescriptorKindEnum = 18 205 | } DescriptorKind; 206 | 207 | 208 | typedef struct { 209 | DescriptorKind kind: 8; 210 | int32_t remainder: 24; 211 | } DescriptorFlags; 212 | 213 | 214 | /// Kinds of context descriptor. 215 | //// struct ContextDescriptorKind : uint8_t { 216 | // /// This context descriptor represents a module. 217 | // Module = 0, 218 | // 219 | // /// This context descriptor represents an extension. 220 | // Extension = 1, 221 | // 222 | // /// This context descriptor represents an anonymous possibly-generic context 223 | // /// such as a function body. 224 | // Anonymous = 2, 225 | // 226 | // /// This context descriptor represents a protocol context. 227 | // Protocol = 3, 228 | // 229 | // /// This context descriptor represents an opaque type alias. 230 | // OpaqueType = 4, 231 | // 232 | // /// First kind that represents a type of any sort. 233 | // Type_First = 16, 234 | // 235 | // /// This context descriptor represents a class. 236 | // Class = Type_First, 237 | // 238 | // /// This context descriptor represents a struct. 239 | // Struct = Type_First + 1, 240 | // 241 | // /// This context descriptor represents an enum. 242 | // Enum = Type_First + 2, 243 | // 244 | // /// Last kind that represents a type of any sort. 245 | // Type_Last = 31, 246 | //}; 247 | 248 | #define DescriptorFlagsGetKind(flags) ((flags) & 0xFu) 249 | #define DescriptorFlagsGetIsGeneric(flags) ((flags) & 0x80u) 250 | #define DescriptorFlagsGetIsUnique(flags) ((flags) & 0x40u) 251 | #define DescriptorFlagsGetVersion(flags) (((flags) >> 8u) 0xFFu) 252 | #define DescriptorFlagsGetKindSpecificFlags(flags) (((flags) >> 16u) & 0xFFFFu) 253 | 254 | 255 | // TargetClassDescriptor found in Metadata.h 256 | typedef struct { 257 | // class TargetTypeContextDescriptor, struct TargetContextDescriptor, 258 | DescriptorFlags flags; // struct TargetContextDescriptor, see ContextDescriptorFlags 259 | int32_t parentOffset; // struct TargetContextDescriptor 260 | int32_t namedOffset; // class TargetTypeContextDescriptor 261 | 262 | /// A pointer to the metadata accessor function for this type, only really useful in process 263 | int32_t accessFunctionOffset; // TargetTypeContextDescriptor 264 | 265 | /// A pointer to the field descriptor for the type, if any. 266 | int32_t fieldsOffset; // TargetTypeContextDescriptor 267 | 268 | 269 | 270 | /// The type of the superclass, expressed as a mangled type name that can 271 | /// refer to the generic arguments of the subclass type. 272 | int32_t superclassTypeOffset; 273 | 274 | // union { 275 | /// If this descriptor does not have a resilient superclass, this is the 276 | /// negative size of metadata objects of this class (in words). 277 | // uint32_t MetadataNegativeSizeInWords; 278 | 279 | /// If this descriptor has a resilient superclass, this is a reference 280 | /// to a cache holding the metadata's extents. 281 | int32_t ResilientMetadataBounds; 282 | // }; 283 | 284 | // union { 285 | /// If this descriptor does not have a resilient superclass, this is the 286 | /// positive size of metadata objects of this class (in words). 287 | // uint32_t MetadataPositiveSizeInWords; 288 | 289 | /// Otherwise, these flags are used to do things like indicating 290 | /// the presence of an Objective-C resilient class stub. 291 | uint32_t ExtraClassFlags; 292 | // }; 293 | 294 | /// The number of additional members added by this class to the class 295 | /// metadata. This data is opaque by default to the runtime, other than 296 | /// as exposed in other members; it's really just 297 | /// NumImmediateMembers * sizeof(void*) bytes of data. 298 | /// 299 | /// Whether those bytes are added before or after the address point 300 | /// depends on areImmediateMembersNegative(). 301 | uint32_t NumImmediateMembers; // ABI: could be uint16_t? 302 | 303 | 304 | // int32_t reflectionOffset; 305 | // int32_t parentTyperefOffset; 306 | //// uint32_t fieldOffsetVector; 307 | // int32_t numFields; 308 | // int32_t fieldsOffset; 309 | // 310 | // /// The number of additional members added by this class to the class 311 | // /// metadata. This data is opaque by default to the runtime, other than 312 | // /// as exposed in other members; it's really just 313 | // /// NumImmediateMembers * sizeof(void*) bytes of data. 314 | // /// 315 | // /// Whether those bytes are added before or after the address point 316 | // /// depends on areImmediateMembersNegative(). 317 | // uint32_t NumImmediateMembers; // ABI: could be uint16_t? 318 | // 319 | // 320 | // 321 | // /// The number of stored properties in the class, not including its 322 | // /// superclasses. If there is a field offset vector, this is its length. 323 | // uint32_t NumFields; 324 | // 325 | //// private: 326 | // /// The offset of the field offset vector for this class's stored 327 | // /// properties in its metadata, in words. 0 means there is no field offset 328 | // /// vector. 329 | // /// 330 | // /// If this class has a resilient superclass, this offset is relative to 331 | // /// the size of the resilient superclass metadata. Otherwise, it is 332 | // /// absolute. 333 | // uint32_t FieldOffsetVectorOffset; 334 | // 335 | // 336 | 337 | } swift_descriptor; 338 | 339 | #ifndef FAST_IS_SWIFT_LEGACY // TODO Resolve other define 340 | #define FAST_IS_SWIFT_LEGACY 1 // < 5 341 | #define FAST_IS_SWIFT_STABLE 2 // 5.X 342 | #endif // FAST_IS_SWIFT_LEGACY 343 | 344 | // Swift class heeeeeeeeeeeeeeeereeeeeeeee 345 | typedef struct swift_class_t : public payload::LoadToDiskTranslator { 346 | using SwiftClassDescriptor = payload::LoadToDiskTranslator; 347 | 348 | struct swift_class_t *isa_cls; 349 | struct swift_class_t *superclass; 350 | void *_buckets; 351 | uint32_t _mask; 352 | uint32_t _occupied; 353 | uintptr_t bits; // (class_ro_t* before access (and on disk), class_rw_t * after access) &= FAST_DATA_MASK 354 | uint32_t flags; 355 | uint32_t instanceAddressOffset; 356 | uint32_t instanceSize; 357 | uint16_t instanceAlignMask; 358 | uint16_t reserved; 359 | 360 | uint32_t classSize; 361 | uint32_t classAddressOffset; 362 | SwiftClassDescriptor *descriptor; 363 | void *ivar_destroyer; 364 | uintptr_t *swiftMethods; 365 | 366 | // Reutnrs the rowdata without the bit packing 367 | inline class_ro_t *rodata() { 368 | auto dataBits = this->disk()->bits; 369 | auto resolved = dataBits & FAST_DATA_MASK; 370 | auto rodata = reinterpret_cast(resolved); 371 | return rodata; 372 | } 373 | 374 | // Returns isa without the bit packing 375 | inline struct swift_class_t* isa() { 376 | auto isa = this->disk()->isa_cls; 377 | auto addr = reinterpret_cast(isa); 378 | 379 | return reinterpret_cast(addr & FAST_DATA_MASK & 0x000007FFFFFFFFFFUL); 380 | } 381 | 382 | /// Checks if the current class is a swift class, if NO swift members in swift_class are unavailable 383 | inline bool isSwift() { 384 | auto bits = this->disk()->bits; 385 | return bits & (FAST_IS_SWIFT_LEGACY|FAST_IS_SWIFT_STABLE) ? true : false; 386 | } 387 | 388 | /// Returns the name of the class (works for both Objc & Swift classes) 389 | const char * GetName() { 390 | auto clsDisk = this->disk(); 391 | auto rodata = clsDisk->rodata(); 392 | if (rodata == nullptr) { 393 | return NULL; 394 | } 395 | 396 | if (!rodata->validAddress()) { 397 | return NULL; 398 | } 399 | auto rodataDisk = rodata->disk(); 400 | if (rodataDisk == nullptr) { return NULL; } 401 | auto name = rodataDisk->name; 402 | if (name == nullptr) { 403 | return NULL; 404 | } 405 | return name->disk(); 406 | } 407 | 408 | } swift_class; 409 | 410 | 411 | #endif /* objc__h */ 412 | 413 | 414 | /** 415 | Derek notes 416 | 417 | 418 | [0x10006dae4-0x10006daf4] protocol conformance descriptor for dsdump.someTest : dsdump.AProtocol in dsdump 419 | int32_t*'s in dsdump`__TEXT.__swift5_proto, that point to ConformanceDescriptors 420 | 421 | 422 | [0x10006daf4-0x10006db00] property descriptor for dsdump.someTest.blah : Swift.Optional 423 | Pointed to by the Reflection metadata field descriptor, 424 | 425 | 426 | [0x10006db4c-0x10006db68] nominal type descriptor for dsdump.someTest 427 | int32_t*'s in __TEXT.__swift5_types, the "main things" 428 | 429 | [0x100077d74-0x100077d9c] reflection metadata field descriptor dsdump.someTest 430 | The Fields value in the StructDescriptor 431 | 432 | [0x100079830-0x100079848] protocol witness table for dsdump.someTest : dsdump.AProtocol in dsdump 433 | The GenericWitnessTable, TODO, struct is 8 bytes, but symbol is 24 bytes... da hell? 434 | 435 | [0x100079848-0x1000798a0] value witness table for dsdump.someTest 436 | 437 | 438 | [0x1000798a0-0x1000798a8] full type metadata for dsdump.someTest 439 | 440 | [0x1000798a8-0x1000798c0] type metadata for dsdump.someTest 441 | 442 | */ 443 | -------------------------------------------------------------------------------- /dsdump/Bind Symbols/XRMachOLibrary+Opcode.mm: -------------------------------------------------------------------------------- 1 | // 2 | // XRMachOLibrary+Opcode.m 3 | // xref 4 | // 5 | // Created by Derek Selander on 4/21/19. 6 | // Copyright © 2019 Selander. All rights reserved. 7 | // 8 | 9 | #import "XRMachOLibrary+Opcode.h" 10 | #import "miscellaneous.h" 11 | #import "XRBindSymbol.h" 12 | //#import "XRMachOLibrary_cplus.h" 13 | #import "payload.hpp" 14 | #import "XRSymbolEntry.h" 15 | #import 16 | //#import 17 | //#import 18 | //#import 19 | 20 | 21 | #define BIND_THAT_BAD_BOY [self addToDictionaries:segStartAddr+segOffset symbol:symbolName libord:libraryOrdinal addend:addend] 22 | 23 | uint64_t read_uleb128(const uint8_t** p, const uint8_t* end) 24 | { 25 | uint64_t result = 0; 26 | int bit = 0; 27 | do { 28 | if (*p == end) {assert(0);} 29 | 30 | uint64_t slice = **p & 0x7f; 31 | 32 | if (bit >= 64 || slice << bit >> bit != slice) { assert(0); } 33 | 34 | else { 35 | result |= (slice << bit); 36 | bit += 7; 37 | } 38 | } 39 | while (*((*p)++) & 0x80); 40 | return result; 41 | } 42 | 43 | static int64_t read_sleb128(const uint8_t** p, const uint8_t* end) 44 | { 45 | int64_t result = 0; 46 | int bit = 0; 47 | uint8_t byte; 48 | do { 49 | if (*p == end) { assert(0); } 50 | byte = *((*p)++); 51 | result |= ((byte & 0x7f) << bit); 52 | bit += 7; 53 | } while (byte & 0x80); 54 | // sign extend negative numbers 55 | if ( (byte & 0x40) != 0 ) 56 | result |= (-1LL) << bit; 57 | return result; 58 | } 59 | 60 | 61 | struct EntryWithOffset 62 | { 63 | uintptr_t nodeOffset; 64 | XRSymbolEntry* entry; 65 | 66 | bool operator<(const EntryWithOffset& other) const { return ( nodeOffset < other.nodeOffset ); } 67 | }; 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | @implementation XRMachOLibrary (Opcode) 77 | 78 | - (void)parseTrie:(const uint8_t*)start end:(const uint8_t*)end output:(std::vector&)output baseAddr:(uintptr_t)baseAddr { 79 | // empty trie has no entries 80 | if ( start == end ) 81 | return; 82 | // worst case largest exported symbol names is length of whole trie 83 | char* cummulativeString = new char[end-start]; 84 | std::vector entries; 85 | [self processExportNode:start p:start end:end cumulativeString:cummulativeString curStrOffset:0 output:entries baseAddr:baseAddr]; 86 | // to preserve tie layout order, sort by node offset 87 | std::sort(entries.begin(), entries.end()); 88 | // copy to output 89 | output.reserve(entries.size()); 90 | for (std::vector::iterator it=entries.begin(); it != entries.end(); ++it) 91 | output.push_back(it->entry); 92 | delete [] cummulativeString; 93 | } 94 | 95 | - (void)processExportNode:(const uint8_t* const) start p:(const uint8_t*)p end:(const uint8_t* const)end 96 | cumulativeString:(char* )cummulativeString curStrOffset:(int)curStrOffset output:( 97 | std::vector&) output baseAddr:(uintptr_t)baseAddr { 98 | if ( p >= end ) 99 | throw "malformed trie, node past end"; 100 | const uint64_t terminalSize = read_uleb128(&p, end); 101 | const uint8_t* children = p + terminalSize; 102 | if ( terminalSize != 0 ) { 103 | EntryWithOffset e; 104 | e.nodeOffset = p-start; 105 | XRSymbolEntry *entry = [[XRSymbolEntry alloc] init]; 106 | entry.name = strdup(cummulativeString); 107 | entry.flags = read_uleb128(&p, end); 108 | entry.visited = false; 109 | if (entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT ) { 110 | entry.address = 0; 111 | entry.other = read_uleb128(&p, end); // dylib ordinal 112 | entry.importName = (char*)p; 113 | } 114 | else { 115 | entry.address = read_uleb128(&p, end) + baseAddr; 116 | if ( entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER ) { 117 | entry.other = read_uleb128(&p, end); 118 | } else { 119 | entry.other = 0; 120 | } 121 | } 122 | e.entry = entry; 123 | output.push_back(e); 124 | //#warning fix this... 125 | XRSymbolEntry *symbolEntry = self.symbolEntry[@(e.entry.address)]; 126 | if (!symbolEntry || !symbolEntry.name) { 127 | self.symbolEntry[@(e.entry.address)] = e.entry; 128 | } 129 | } 130 | if ( children > end ) 131 | throw "malformed trie, terminalSize extends beyond trie data"; 132 | const uint8_t childrenCount = *children++; 133 | const uint8_t* s = children; 134 | for (uint8_t i=0; i < childrenCount; ++i) { 135 | int edgeStrLen = 0; 136 | while (*s != '\0') { 137 | cummulativeString[curStrOffset+edgeStrLen] = *s++; 138 | ++edgeStrLen; 139 | } 140 | cummulativeString[curStrOffset+edgeStrLen] = *s++; 141 | uint64_t childNodeOffset = read_uleb128(&s, end); 142 | if (childNodeOffset == 0) 143 | throw "malformed trie, childNodeOffset==0"; 144 | [self processExportNode:start p:start+childNodeOffset end:end cumulativeString:cummulativeString curStrOffset:curStrOffset +edgeStrLen output:output baseAddr:baseAddr]; 145 | } 146 | } 147 | 148 | 149 | 150 | 151 | - (void)parseDYLDOpcodes { 152 | assert(self.dyldInfo); 153 | 154 | static dispatch_once_t onceToken; 155 | dispatch_once(&onceToken, ^{ 156 | self.threadedHolder = [NSMutableArray arrayWithCapacity:100]; 157 | self.addressObjCDictionary = [NSMutableDictionary dictionaryWithCapacity:100]; 158 | self.stringObjCDictionary = [NSMutableDictionary dictionaryWithCapacity:100]; 159 | }); 160 | 161 | // uint8_t *bind_buffer = &self.data[self.dyldInfo->bind_off + self.file_offset]; 162 | 163 | // int i = 0; 164 | // uint64_t bind_address = 0; 165 | // char *symbol = NULL; 166 | // char type = 0; 167 | // uint64_t segment = 0; 168 | // uint64_t seg_off = 0; 169 | // uint64_t addend = 0; 170 | // uint8_t dylib_ord = 0; 171 | // uint64_t threaded_count = 0; 172 | // uint8_t special = 0; 173 | // BOOL done = NO; 174 | // uint8_t opcode = BIND_OPCODE_MASK & bind_buffer[i]; 175 | // uint8_t imm = BIND_IMMEDIATE_MASK & bind_buffer[i]; 176 | 177 | // look bind info 178 | if ( self.dyldInfo ) { 179 | const uint8_t* p = (uint8_t *)&payload::data[self.dyldInfo->bind_off]; 180 | const uint8_t* end = (uint8_t *)&p[self.dyldInfo->bind_size]; 181 | 182 | uint8_t type = 0; 183 | uint64_t segOffset = 0; 184 | uint64_t count; 185 | uint64_t skip; 186 | const char* symbolName = NULL; 187 | uint64_t libraryOrdinal = 0; 188 | uint64_t segIndex; 189 | int64_t addend = 0; 190 | uintptr_t segStartAddr = 0; 191 | uint64_t ordinalTableSize = 0; 192 | uint64_t threaded_count = 0; 193 | bool useThreadedRebaseBind = false; 194 | bool done = false; 195 | const uint8_t *start_off = p; 196 | while ( !done && (p < end) ) { 197 | uint8_t immediate = *p & BIND_IMMEDIATE_MASK; 198 | uint8_t opcode = *p & BIND_OPCODE_MASK; 199 | DEBUG_PRINT("%s0x%04lX%s ", dcolor(DSCOLOR_GRAY), (long)p - (long)start_off, color_end()); 200 | ++p; 201 | switch (opcode) { 202 | case BIND_OPCODE_DONE: { 203 | done = true; 204 | DEBUG_PRINT("%sBIND_OPCODE_DONE%s\n", dcolor(DSCOLOR_CYAN), color_end()); 205 | break; 206 | } case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: { 207 | libraryOrdinal = immediate; 208 | DEBUG_PRINT("%sBIND_OPCODE_SET_DYLIB_ORDINAL_IMM%s (%d)\n", dcolor(DSCOLOR_CYAN), color_end(), immediate); 209 | break; 210 | } case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: { 211 | libraryOrdinal = read_uleb128(&p, end); 212 | DEBUG_PRINT("%sBIND_OPCODE_SET_DYLIB_ORDINAL_ULEB%s (0x%llX)\n", dcolor(DSCOLOR_CYAN), color_end(), libraryOrdinal); 213 | break; 214 | } case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: { 215 | // the special ordinals are negative numbers 216 | if ( immediate == 0 ) 217 | libraryOrdinal = 0; 218 | else { 219 | int8_t signExtended = BIND_OPCODE_MASK | immediate; 220 | libraryOrdinal = signExtended; 221 | } 222 | DEBUG_PRINT("%sBIND_OPCODE_SET_DYLIB_SPECIAL_IMM%s (%d)\n", dcolor(DSCOLOR_CYAN), color_end(), (int)libraryOrdinal); 223 | break; 224 | } case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: { 225 | symbolName = (char*)p; 226 | while (*p != '\0') { ++p; } 227 | ++p; 228 | DEBUG_PRINT("%sBIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM%s (0x%02x, %s%s%s)\n", dcolor(DSCOLOR_CYAN), color_end(), immediate, dcolor(DSCOLOR_YELLOW), symbolName, color_end()); 229 | break; 230 | } case BIND_OPCODE_SET_TYPE_IMM: { 231 | type = immediate; 232 | DEBUG_PRINT("%sBIND_OPCODE_SET_TYPE_IMM%s (%d)\n", dcolor(DSCOLOR_CYAN), color_end(), type); 233 | break; 234 | } case BIND_OPCODE_SET_ADDEND_SLEB: { 235 | addend = read_sleb128(&p, end); 236 | DEBUG_PRINT("%sBIND_OPCODE_SET_ADDEND_SLEB%s (0x%llX)\n", dcolor(DSCOLOR_CYAN), color_end(), addend); 237 | break; 238 | } case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: { 239 | segIndex = immediate; 240 | struct segment_command_64 *seg = (struct segment_command_64 *)self.segmentCommandsArray[immediate].longValue; 241 | segStartAddr = seg->vmaddr; // segStartAddress(segIndex); 242 | segOffset = read_uleb128(&p, end); 243 | DEBUG_PRINT("%sBIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB%s (%d, 0x%08llX) (0x%08llX)\n", dcolor(DSCOLOR_CYAN), color_end(), immediate, segOffset, (segOffset + segStartAddr)); 244 | break; 245 | } case BIND_OPCODE_ADD_ADDR_ULEB: { 246 | uint64_t off = read_uleb128(&p, end); 247 | segOffset += off; 248 | DEBUG_PRINT("%sBIND_OPCODE_ADD_ADDR_ULEB%s (0x%llX) (0x%08llX)\n", dcolor(DSCOLOR_CYAN), color_end(), off, (segOffset + segStartAddr)); 249 | break; 250 | } case BIND_OPCODE_DO_BIND: { 251 | if (threaded_count > 0) { 252 | threaded_count--; 253 | [self addToThreaded:segOffset + segStartAddr symbol:symbolName libord:libraryOrdinal addend:addend]; 254 | } else { 255 | DEBUG_PRINT("%sBIND_OPCODE_DO_BIND%s (0x%08llX, %s%s%s)\n", dcolor(DSCOLOR_CYAN), color_end(), (segOffset + segStartAddr), dcolor(DSCOLOR_YELLOW), symbolName, color_end()); 256 | BIND_THAT_BAD_BOY; 257 | } 258 | segOffset += sizeof(uintptr_t); 259 | break; 260 | } case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: { 261 | uint64_t off = read_uleb128(&p, end) + sizeof(uintptr_t); 262 | DEBUG_PRINT("%sBIND_OPCODE_DO_BIND_ADD_ADDR_ULEB%s (%08llx) (0x%08llX, %s%s%s)\n", dcolor(DSCOLOR_CYAN), color_end(), off, (segOffset + segStartAddr), dcolor(DSCOLOR_YELLOW), symbolName, color_end()); 263 | BIND_THAT_BAD_BOY; 264 | segOffset += off; 265 | break; 266 | } case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: { 267 | uint64_t off = immediate*sizeof(uintptr_t) + sizeof(uintptr_t); 268 | DEBUG_PRINT("%sBIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED%s (0x%llx) (0x%08llX)\n", dcolor(DSCOLOR_CYAN), color_end(), off, (segOffset + segStartAddr)); 269 | BIND_THAT_BAD_BOY; 270 | segOffset += off; 271 | break; 272 | } case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: { 273 | count = read_uleb128(&p, end); 274 | skip = read_uleb128(&p, end); 275 | DEBUG_PRINT("%sBIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB%s (%llu, 0x%08llX)\n", dcolor(DSCOLOR_CYAN), color_end(), count, skip); 276 | for (uint32_t i = 0; i < count; ++i) { 277 | DEBUG_PRINT("\t%sDO_BIND%s (0x%08llX, %s%s%s)\n", dcolor(DSCOLOR_CYAN), color_end(), (segStartAddr + segOffset), dcolor(DSCOLOR_YELLOW), symbolName, color_end()); 278 | BIND_THAT_BAD_BOY; 279 | segOffset += skip + sizeof(uintptr_t); 280 | } 281 | break; 282 | } case BIND_OPCODE_THREADED: { 283 | // Note the immediate is a sub opcode 284 | switch (immediate) { 285 | case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB: 286 | ordinalTableSize = read_uleb128(&p, end); 287 | useThreadedRebaseBind = true; 288 | threaded_count = ordinalTableSize; 289 | DEBUG_PRINT("%sBIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB%s (%llu)\n", dcolor(DSCOLOR_CYAN), color_end(), ordinalTableSize); 290 | break; 291 | case BIND_SUBOPCODE_THREADED_APPLY: { 292 | if ( !useThreadedRebaseBind ) { assert(0); } 293 | DEBUG_PRINT("%sBIND_SUBOPCODE_THREADED_APPLY%s\n", dcolor(DSCOLOR_CYAN), color_end()); 294 | uint64_t delta = 0; 295 | 296 | do { 297 | uintptr_t resolvedAddress = segStartAddr + segOffset; 298 | uintptr_t offset = [self translateLoadAddressToFileOffset:resolvedAddress useFatOffset:YES]; 299 | uint64_t value = *(uintptr_t *)((uintptr_t)DATABUF(offset)); 300 | bool isRebase = (value & (1ULL << 62)) == 0; 301 | 302 | if (isRebase) { 303 | // Do nothing 304 | } else { 305 | // the ordinal is bits [0..15] 306 | uint16_t threadOrdinal = value & 0xFFFF; 307 | if (threadOrdinal >= ordinalTableSize) { assert(0); } 308 | XRBindSymbol *obj = self.threadedHolder[threadOrdinal]; 309 | [self addToDictionaries:segStartAddr + segOffset symbol:(char*)obj.name.UTF8String libord:obj.libOrdinal addend:obj.addend]; 310 | DEBUG_PRINT("\t%sTHREADED_APPLY%s (%p, %s%s%s)\n", dcolor(DSCOLOR_CYAN), color_end(), (void*)resolvedAddress, dcolor(DSCOLOR_YELLOW), obj.name.UTF8String, color_end()); 311 | } 312 | 313 | value &= ~(1ULL << 62); 314 | delta = ( value & 0x3FF8000000000000 ) >> 51; 315 | 316 | // The delta is bits [51..61] 317 | // And bit 62 is to tell us if we are a rebase (0) or bind (1) 318 | segOffset += delta * sizeof(uintptr_t); 319 | 320 | 321 | } while ( delta != 0); 322 | break; 323 | } 324 | default: 325 | assert(0); 326 | } 327 | break; 328 | } default: 329 | assert(0); 330 | } 331 | } 332 | } 333 | } 334 | 335 | - (void)addToDictionaries:(uintptr_t)address symbol:(const char *)symbol libord:(uint64_t)ordinal addend:(uint64_t)addend { 336 | NSString *s = [NSString stringWithUTF8String:symbol]; 337 | NSNumber *a = @(address); 338 | 339 | // if (![s hasPrefix:@"_OBJC_CLASS_$_"]) { return; } 340 | 341 | if (self.addressObjCDictionary[a]) { 342 | if (xref_options.debug) { 343 | dprintf(STDERR_FILENO, "overriding dict: old %s, %p, new; %s, %p\n", self.addressObjCDictionary[a].name.UTF8String, self.addressObjCDictionary[a].address.pointerValue, symbol, (void*)address); 344 | } 345 | } 346 | XRBindSymbol * obj = [[XRBindSymbol alloc] initWithAddress:a symbol:s libord:ordinal addend:addend]; 347 | self.addressObjCDictionary[a] = obj; 348 | self.stringObjCDictionary[s] = obj; 349 | } 350 | 351 | - (void)addToThreaded:(uintptr_t)address symbol:(const char *)symbol libord:(uint64_t)ordinal addend:(uint64_t)addend { 352 | NSString *s = [NSString stringWithUTF8String:symbol]; 353 | NSNumber *a = @(address); 354 | XRBindSymbol * obj = [[XRBindSymbol alloc] initWithAddress:a symbol:s libord:ordinal addend:addend]; 355 | [self.threadedHolder addObject:obj]; 356 | } 357 | 358 | 359 | 360 | 361 | - (void)parseDYLDExports { 362 | if (self.dyldInfo->export_size == 0) { return; } 363 | // Let's just do an upper limit 364 | 365 | 366 | int segindex = 0; 367 | struct segment_command_64 *seg; 368 | do { seg = (struct segment_command_64 *)self.segmentCommandsArray[segindex++].longValue; 369 | } while (seg->maxprot == 0); 370 | uintptr_t baseAddr = seg->vmaddr; 371 | const uint8_t* start = (uint8_t*)DATABUF(self.dyldInfo->export_off); 372 | const uint8_t* end = &start[self.dyldInfo->export_size]; 373 | std::vector list; 374 | [self parseTrie:start end:end output:list baseAddr:baseAddr]; 375 | } 376 | 377 | 378 | 379 | @end 380 | --------------------------------------------------------------------------------