ktool Public API#
ktool#
In the majority of use cases, this is the only module you should ever need to import. (solely ‘import ktool’)
These functions perform all of the heavy lifting of loading in a file and processing its metadata.
This module also imports many classes from other files in this module you may need to use.
- ktool.load_macho_file(fp: Union[BinaryIO, BytesIO], use_mmaped_io=True) MachOFile #
Takes a bare file or BytesIO object and loads it as a MachOFile. You likely dont need this unless you only care about loading info about basic slices; The MachOFile is still accessible from the
Image
class.
- ktool.load_image(fp: Union[BinaryIO, MachOFile, Slice, BytesIO], slice_index=0, load_symtab=True, load_imports=True, load_exports=True, use_mmaped_io=True) Image #
Take a bare file, MachOFile, BytesIO, or Slice, and load MachO/dyld metadata about that item
- ktool.macho_verify(fp: Union[BinaryIO, MachOFile, Slice, Image]) None #
Disable “ignore malformed” flag if set, then try loading the Image, throwing a MalformedMachOException if anything fails
- ktool.load_objc_metadata(image: Image) ObjCImage #
Load an ObjCImage object (containing the processed ObjC metadata) from an Image
- ktool.generate_headers(objc_image: ObjCImage, sort_items=False) Dict[str, Header] #
Generate a list of “Header Name” -> Header objects from an ObjC Image
- ktool.generate_text_based_stub(image: Image, compatibility=True) str #
Generate a Text-Based-Stub (.tbd) from a MachO
- ktool.macho_combine(slices: List[Slice]) BytesIO #
Create a Fat MachO from thin MachO slices, returned as a BytesIO in-memory representation
Image#
This class represents the Mach-O Binary as a whole.
It’s the root object in the massive tree of information we’re going to build up about the binary
This class on its own does not handle populating its fields. The MachOImageLoader class set is responsible for loading in and processing the raw values to it.
You should obtain an instance of this class using the public ktool.load_image()
API
- class ktool.Image(macho_slice: Slice)#
This class represents the Mach-O Binary as a whole.
It can be initialized without a Slice if you are building a Mach-O Representation from runtime data.
- macho_header: MachOImageHeader#
if Image was initialized with a macho_slice, this attribute will contain an MachOImageHeader with basic info loaded from the Mach-O Header
- base_name: str#
“basename” of the Image’s install name (“SpringBoard” for “/System/Library/Frameworks/SpringBoard.framework/SpringBoard”)
- install_name: str#
Install Name of the image (if it exists). “” if the library does not have one.
- linked_images: List[LinkedImage]#
List of
LinkedImage
s this image links
- segments: Dict[str, Segment]#
Dictionary mapping
segment_name
toSegment
. You can obtain a list of segments from this usingsegments.values()
- imports: List[Symbol]#
List of
Symbol
objects this image imports
- exports: List[Symbol]#
List of
Symbol
objects this image exports
- symbols: Dict[int, Symbol]#
Address -> Symbol map for symbols embedded within this image
- import_table: Dict[int, Symbol]#
Address -> Symbol map for imported Symbols
- export_table: Dict[int, Symbol]#
Address -> Symbol map for exported Symbols
- function_starts: List[int]#
List of function start addresses
- uuid: bytes#
Raw bytes representing the Image’s UUID if it has one.
- vm: _VirtualMemoryMap#
Reference to the VM translation table object the
Image
uses. You probably shouldn’t use this, but it’s here if you need it.
- dylib: LinkedImage#
LinkedImage that represents this Image itself.
- serialize() dict #
Return image metadata as a dictionary of json-serializable keys and objects
- vm_check(address: int) bool #
Check if an address resolves within the VM translation table
- get_int_at(address: int, length: int, vm=False, section_name=None) int #
Method that performs VM address translation if
vm
is true, then falls through toSlice().get_int_at(address, length)
The underlying
Slice
class should handle specifying endianness. If you for some reason need to load an int in the opposite endianness, you’ll need to do VM translation yourself usingimage.vm.get_file_address
and then call theSlice
method yourself.
- get_bytes_at(address: int, length: int, vm=False, section_name=None) bytes #
Pull
length
bytes
fromaddress
.Does VM address translation then falls through to
Slice.get_bytes_at()
- load_struct(address: int, struct_type: Struct, vm=False, section_name=None, endian='little', force_reload=True) Struct #
Load a struct of
struct_type
fromaddress
, performing address translation ifvm
.This struct will be cached; if we need to for some reason reload the struct at this address, pass
force_reload=True
- get_str_at(address: int, length: int, vm=False, section_name=None) str #
Load a fixed-length string from
address
with the sizelength
.Does VM address translation then falls through to
Slice.get_str_at()
- get_cstr_at(address: int, limit: int = 0, vm=False, section_name=None) str #
Load a null-terminated string from
address
, stopping afterlimit
iflimit
is not 0
- decode_uleb128(address: int)#
Decode uleb from starting address, returning the value, and the end address of the leb128
MachOImageHeader#
- class ktool.MachOImageHeader#
the class method
from_image()
should be used for loading this class.- classmethod from_image(macho_slice) MachOImageHeader #
Load an MachOImageHeader from a macho_slice
- is64: bool
Is this image a 64 bit Mach-O?
- dyld_header: Union[dyld_header, dyld_header_64]
Dyld Header struct
- filetype: MH_FILETYPE
MachO Filetype
- flags: MH_FLAGS
MachO File Flags
- load_commands: List[load_command]
List of load command structs
- insert_load_cmd(load_command, index=- 1, suffix=None) MachOImageHeader #
Insert a load command.
- remove_load_command(index) MachOImageHeader #
Remove a load command.
- serialize() dict #
Return image metadata as a dictionary of json-serializable keys and objects
MachOImageLoader#
- class ktool.MachOImageLoader#
Warning
Do not use this! Use
ktool.load_image()
!!This class takes our initialized “Image” object, parses through the raw data behind it, and fills out its properties.
ObjCImage#
- class ktool.ObjCImage#
-
- classmethod from_values(image: Image, name: str, classlist: List[Class], catlist: List[Category] protolist: List[Protocol], type_processor=None) ObjCImage #
Create an ObjCImage instance from somehow preloaded values
- image: Image
- name: str
Image Install Base Name
- classlist: List[Class]
- catlist: List[Category]
- protolist: List[Protocol]
- class_map: Dict[int, Class]
Map of Load addresses to Classes. Used as a cache.
- cat_map: Dict[int, Category]
Map of Load addresses to Categories. ‘’
- prot_map: Dict[int, Protocol]
Map of Load addresses to protocols
- serialize() dict #
Return image metadata as a dictionary of json-serializable keys and objects
- vm_check(address: int) bool #
Check if an address resolves within the VM translation table
- get_int_at(address: int, length: int, vm=False, section_name=None) int #
Method that performs VM address translation if
vm
is true, then falls through to Slice().get_int_at(address, length)
- load_struct(address: int, struct_type: Struct, vm=True, section_name=None, endian='little', force_reload=True) Struct #
Load a struct of
struct_type
fromaddress
, performing address translation ifvm
. This struct will be cached; if we need to for some reason reload the struct at this address, passforce_reload=True
- get_str_at(address: int, length: int, vm=True, section_name=None) str #
Load a fixed-length string from
address
with the sizelength
.
- get_cstr_at(address: int, limit: int = 0, vm=True, section_name=None) str #
Load a null-terminated string from
address
, stopping afterlimit
if :python:limit is set
MachOFile#
The MachOFile is the early base responsible for loading super basic info about the MachO and populating the Slice objects.
These Slices handle actually reading/parsing data from the MachO once they’ve been loaded.
We from this point on essentially “ignore” the MachOFile, for the sake of not overcomplicating the File Offset -> Address translation, and make code more readable and less confusing.
- class ktool.MachOFile(file: Union[BinaryIO, BytesIO], use_mmaped_io=True)#
Where file is a file pointer or BytesIO object. use_mmaped_io should be False when operating on BytesIO
ktool.load_macho_file()
should be used in place of manually initializing this.- file: Union[mmap, BinaryIO]
File object underlying functions should use to load data.
- slices: List[Slice]
List of slices within this MachO file
- type: MachOFileType
FAT or THIN filetype
- uses_mmaped_io: bool
Whether the MachOFile should be operated on using mmaped IO (and whether .file is a mmap object)
- magic: bytes
Magic at the beginning of the file (FAT_MAGIC/MH_MAGIC)
Slice#
- class ktool.Slice(macho_file: MachOFile, arch_struct: fat_arch = None, offset=0)#
This class, loaded by MachOFile, represents an underlying slice.
MachOFile should handle loading it, and you shouldn’t need to ever initialize it yourself.
- macho_file#
Underlying MachO File this struct is located in
- arch_struct#
If this slice was loaded from a fat_macho, the arch_struct representing it in the Fat Header
- offset#
File offset for this slice
- type#
CPUType
of the Slice
- subtype#
CPUSubType
of the Slice
- size#
Size of the slice
- byte_order#
Byte Order (“little” or “big”) of the Slice.
- load_struct(address: int, struct_type: Struct, endian='little')#
Load a struct from
address
- get_int_at(addr: int, count: int, endian='little') int #
Load int from an address.
The code for this method (and the rest of the
get_
methods) will either use mmapped or non-mmapped io based on the MachOFile’s .use_mmaped_io attribute.
- get_bytes_at(addr: int, count: int, endian='little') int #
Load
count
bytes fromaddress
- get_str_at(addr: int, count: int) str #
Load a fixed-length string from
address
with the sizelength
.
- get_cstr_at(addr: int, limit: int) str #
Load a null-terminated string from
address
, stopping afterlimit
iflimit
is not 0
- decode_uleb128(address: int)#
Decode uleb from starting address, returning the value, and the end address of the leb128
- patch(address: int, raw: bytes) None #
Patch Bytes in the slice
Segment#
- class ktool.Segment(image, cmd: Union[segment_command, segment_command_64])#
Object Representation of a MachO Segment
- name: str
Segment Name
- sections: Dict[str, Section]
Dictionary of Sections within this Segment.
You can get a list of Sections using
my_segment.sections.values()
- cmd#
Underlying segment_command (or segment_command_64)
- vm_address#
VM Address of the Segment
- file_address#
File address (in the Slice) of the Segment
- size#
Size of the segment
- serialize() dict #
Return image metadata as a dictionary of json-serializable keys and objects
Section#
- class ktool.Section(segment: Segment, Union[section, section_64])#
Section within a MachO Segment
- name: str
Name of the Section
- vm_address: int
VM Address of the Section
- file_address: int
File Address (within the Slice) of the Section
- size: int
Size of the Section
- serialize() dict #
Return image metadata as a dictionary of json-serializable keys and objects
ktool.macho#
ktool.image#
VM / MisalignedVM#
This is the translation table used by the Image class to translate VM addresses to their File counterparts.
You can fetch it using Image().vm
the VM
class will be used if the image can be mapped to 16k/4k segments. If it can’t, it will automatically fall back to MisalignedVM
. The aligned VM is approximately 2x faster at doing translations.
Their outward APIs are (nearly) identical.
- class ktool.macho.VM#
- add_segment(segment: Segment)#
Map a segment.
- translate(address: int) int #
Translate VM address to file address.
- vm_check(address: int) int #
Check whether an address is mapped in the VM. Calls
translate()
and catches any exceptions.
- vm_base_addr: int
“Base Address” of the file. Used primarily for function starts processing. If you’re familiar with dyld source, it’s the equivalent to this: https://github.com/apple-opensource/ld64/blob/e28c028b20af187a16a7161d89e91868a450cadc/src/other/dyldinfo.cpp#L156
- detag_kern_64: bool
Should we apply 64 bit kernel detagging to translated pointers?
- detag_64: bool
Should we detag chained fixups from pointers?
- page_size: int
If this is not a
MisalignedVM
, this attribute exists and contains the page size of the VM
ktool.loader#
Symbol#
- class ktool.loader.Symbol#
Initializing this class should be done with either the
.from_image()
or.from_values()
class methods- fullname: str
Original name of the Symbol as is embedded in the executable ( _OBJC_CLASS_$_SomeClassName )
- name: str
Name of the symbol with some typing information removed ( _SomeClassName )
- address: int
Address the symbol represents
- external: bool
Whether this symbol was imported.
- ordinal: int
If this symbol was imported, the index of the library it was imported from.
- classmethod from_image(image: Image, cmd: symtab_command, entry: NList32 or NList64 item)#
Generate a Symbol loaded from the Symbol Table. Any other method of loading symbols needs to use .from_values()
- classmethod from_values(fullname: str, address: int, external=False, ordinal=0)#
Create a symbol from preprocessed or custom values.
SymbolTable#
- class ktool.loader.SymbolTable(image: Image, cmd: symtab_command)#
Representation of the Symbol Table pointed to by the LC_SYMTAB command
- ext: List[Symbol]
List of external symbols
- table: List[Symbol]
Entire list of symbols in the table
ChainedFixups#
Chained Fixup Processor class.
- class ktool.loader.ChainedFixups#
- classmethod from_image(image: Image, chained_fixup_cmd: linkedit_data_command) ChainedFixups #
Load chained fixups from the relevant command
- symbols: List[Symbol]
Symbols loaded from within the chained fixups
ExportTrie#
Export Trie Processor class.
- class ktool.loader.ExportTrie#
- classmethod from_image(image: Image, export_start, export_size) ExportTrie #
Load chained fixups from the relevant command
- symbols: List[Symbol]
Symbols loaded from within the chained fixups
ktool.objc#
Everything in the ObjC module implements the “Constructable” Base class
This theoretically allows it to be used to generate headers from metadata dumped using ObjC Runtime Functions, and it has been tested and confirmed functional at doing that :)
Class#
- class ktool.objc.Class#
- classmethod from_image(image: Image, class_ptr: int, meta=False) Class #
Take a location of a pointer to a class (For example, the location of an entry in the __objc_classlist section) and process its metadata
- classmethod from_values(name, superclass_name, methods: List[Method], properties: List[‘Property’], ivars: List[‘Ivar’], protocols: List[‘Protocol’], load_errors=None, structs=None) Class #
Create a Class instance from somehow preloaded values
- name: str
Classname
- meta: bool
Whether this method is a MetaClass (these hold “class methods”)
- superclass: str
Name of the superclass
- load_errors: List[str]
List of errors encountered while loading metadata
- struct_list: List[Struct_Representation]
List of structs embedded in this class. Will eventually be used for header specific struct resolution
- methods: List[Method]
- properties: List[Property]
- protocols: List[Protocol]
- ivars: List[Ivar]
- serialize() dict #
Return image metadata as a dictionary of json-serializable keys and objects
Method#
- class ktool.objc.Method#
- classmethod from_image(objc_image: ObjCImage, sel_addr, types_addr, is_meta, vm_addr, rms, rms_are_direct)#
- classmethod from_values(name, type_encoding, type_processor=None)#
- meta: bool
Class method instead of Instance method
- sel: str
Selector
- type_string#
Unparsed Type String
- types: List[Type]
List of types
- return_string: str
Type of the return value
- arguments: List[str]
List of the types of arguments
- signature: str
Fully built method signature
- serialize() dict #
Return image metadata as a dictionary of json-serializable keys and objects
Property#
- class ktool.objc.Property#
- classmethod from_image(objc_image: ObjCImage, property: objc2_prop)#
- classmethod from_values(name, attr_string, type_processor=None)#
- name: str
- type: str
- is_id: bool
Is the type an ObjC class
- attributes#
Property Attributes (e.g. nonatomic, readonly, weak)
- attr_string: str
Property Attribute String
- getter: str
Property getter name
- setter: str
Property setter name
- ivarname: str
Name of the ivar backing this property
- serialize() dict #
Return image metadata as a dictionary of json-serializable keys and objects
Ivar#
- class ktool.objc.Ivar#
- classmethod from_image(objc_image: ObjCImage, ivar: objc2_ivar)#
- classmethod from_values(name, type_encoding, type_processor=None)#
- name: str
- is_id: bool
Whether Ivar type is an ObjC Class
- type: str
Renderable type
- serialize() dict #
Return image metadata as a dictionary of json-serializable keys and objects
Category#
- class ktool.objc.Category#
- classmethod from_image(objc_image: ObjCImage, category_ptr)#
- classmethod from_values(classname, name, methods, properties, load_errors=None, struct_list=None)#
- name#
Category Name (e.g., if you defined a category as “UIColor+MyAdditions”, it would be “MyAdditions”)
- classname#
Original class being extended (“UIColor” in “UIColor+MyAdditions”)
- load_errors: List[str]
List of errors encountered while loading metadata
- struct_list: List[Struct_Representation]
List of structs embedded in this category. Will eventually be used for header specific struct resolution
- methods: List[Method]
- properties: List[Property]
- protocols: List[Protocol]
- serialize() dict #
Return image metadata as a dictionary of json-serializable keys and objects
Protocol#
- class ktool.objc.Protocol#
- classmethod from_image(objc_image: ObjCImage, category_ptr)#
- classmethod from_values(classname, name, methods, properties, load_errors=None, struct_list=None)#
- name#
Category Name (e.g., if you defined a category as “UIColor+MyAdditions”, it would be “MyAdditions”)
- classname#
Original class being extended (“UIColor” in “UIColor+MyAdditions”)
- load_errors: List[str]
List of errors encountered while loading metadata
- struct_list: List[Struct_Representation]
List of structs embedded in this protocol. Will eventually be used for header specific struct resolution
- methods: List[Method]
- opt_methods: List[Method]
Methods that may (but are not required to) be implemented by classes conforming to this protocol
- properties: List[Property]
- serialize() dict #
Return image metadata as a dictionary of json-serializable keys and objects
Type Processing / Encoding#
- class ktool.objc.TypeProcessor#
Responsible for cacheing loaded structs (for dumping) and types, and for processing them as well.
- structs: Dict[str, Struct_Representation]
Dictionary of Struct Name -> Struct Representations stored for dumping
- type_cache: Dict[str, List[Type]]
Cache of processed typestrings (to avoid re-parsing identical typestrings)
- class ktool.objc.Type(processor: TypeProcessor, type_string, pointer_count=0)#
For parsing and saving a specific type encoding.
Calling str(a_type_instance) will render the type as it appears in headers.
- type: EncodedType
Enum containing either NORMAL, NAMED, or STRUCT
- value: Union[str, Struct_Representation]
Renderable text representing the type
- class ktool.objc.Struct_Representation(processor: TypeProcessor, type_string)#
Can be embedded in Type().value for representing a struct embedded in a type string.
Calling str(instance) will generate renderable text for headers.
- name: str
- fields: List[str]
Encoded Field Types
- field_names: List[str]
Field names (if they were embedded also, they aren’t always)
ktool.headers#
Header#
HeaderUtils#
- class ktool.headers.HeaderUtils#
- static header_head(image: ktool.Image) str #
This is the prefix comments at the very top of the headers generated
TypeResolver#
The Type Resolver is just in charge of figuring out where imports came from.
- Initialize it with an objc image, then pass it a type name, and it’ll try to figure out which
framework that class should be imported from (utilizing the image’s imports)
- class ktool.headers.TypeResolver(objc_image: ktool.ObjCImage)#
- find_linked(classname: str) str #
Takes a classname and attempts to find the Install name of the image it came from.
Returns “” If its a local Class, “-Protocol” if it’s a local protocol, None if it cant be found, or the install name if it was found in a linked image.
HeaderGenerator#
This class creates all of the Header objects from the ObjCImage
Warning
Do not use this, use ktool.generate_headers(objc_image) !
- class ktool.headers.HeaderGenerator(objc_image: ObjCImage)#
- type_resolver: TypeResolver
- headers: Dict[str, Header]
StructHeader#
This class generates a header from the struct definitions saved in the objc_image’s type processor