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 to Segment. You can obtain a list of segments from this using segments.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 to Slice().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 using image.vm.get_file_address and then call the Slice method yourself.

read_bytearray(address: int, length: int, vm=False, section_name=None) bytes#

Pull length bytes from address.

Does VM address translation then falls through to Slice.read_bytearray()

read_struct(address: int, struct_type: Struct, vm=False, section_name=None, endian='little', force_reload=True) Struct#

Load a struct of struct_type from address, performing address translation if vm.

This struct will be cached; if we need to for some reason reload the struct at this address, pass force_reload=True

read_fixed_len_str(address: int, length: int, vm=False, section_name=None) str#

Load a fixed-length string from address with the size length.

Does VM address translation then falls through to Slice.read_fixed_len_str()

read_cstr(address: int, limit: int = 0, vm=False, section_name=None) str#

Load a null-terminated string from address, stopping after limit if limit is not 0

read_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.

classmethod load(macho_slice: Slice, load_symtab=True, load_imports=True, load_exports=True) Image#

Take a MachO Slice object and Load an image.

ObjCImage#

class ktool.ObjCImage#
classmethod from_image(image: Image) ObjCImage#

Take an Image class and process its ObjC Metadata

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)

read_struct(address: int, struct_type: Struct, vm=True, section_name=None, endian='little', force_reload=True) Struct#

Load a struct of struct_type from address, performing address translation if vm. This struct will be cached; if we need to for some reason reload the struct at this address, pass force_reload=True

read_fixed_len_str(address: int, length: int, vm=True, section_name=None) str#

Load a fixed-length string from address with the size length.

read_cstr(address: int, limit: int = 0, vm=True, section_name=None) str#

Load a null-terminated string from address, stopping after limit 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.

read_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.

read_bytearray(addr: int, count: int, endian='little') int#

Load count bytes from address

read_fixed_len_str(addr: int, count: int) str#

Load a fixed-length string from address with the size length.

read_cstr(addr: int, limit: int) str#

Load a null-terminated string from address, stopping after limit if limit is not 0

read_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

LinkedImage#

class ktool.macho.LinkedImage(image: Image, cmd)#
install_name: str

Full Install name of the image

local: bool

Whether this “LinkedImage” is actually local (ID_DYLIB)

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

BindingTable#

Binding Table Processor

class ktool.loader.BindingTable(image: Image, table_start: int, table_size: Int)#
symbol_table: List[Symbol]

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)

process(type_to_process: str) List[Type]#

Process a typestring, returning a list of types embedded in it.

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#

class ktool.headers.Header#
text#

Plain generated header contents

str(my_header) will also return this value.

generate_highlighted_text()#

generate and return ANSI Color highlighted header text

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

class ktool.headers.StructHeader(objc_image: ObjCImage)#
text#

Struct Header Text

CategoryHeader#

class ktool.headers.CategoryHeader#
text: str

Fully generated Header text.

UmbrellaHeader#

Generates a header that imports all headers in header_list

class ktool.headers.UmbrellaHeader(header_list: dict)#
text#