Skip to content

Commit

Permalink
Implement parsing LC_FUNCTION_STARTS and use it to find function star…
Browse files Browse the repository at this point in the history
…ts if available
  • Loading branch information
Lars Fröder committed Aug 30, 2024
1 parent 99ec129 commit b30883c
Showing 3 changed files with 79 additions and 3 deletions.
64 changes: 61 additions & 3 deletions src/MachO.c
Original file line number Diff line number Diff line change
@@ -169,9 +169,9 @@ int macho_enumerate_load_commands(MachO *macho, void (^enumeratorBlock)(struct l
LOAD_COMMAND_APPLY_BYTE_ORDER(&loadCommand, LITTLE_TO_HOST_APPLIER);

if (strcmp(load_command_to_string(loadCommand.cmd), "LC_UNKNOWN") == 0)
{
printf("Ignoring unknown command: 0x%x.\n", loadCommand.cmd);
}
{
printf("Ignoring unknown command: 0x%x.\n", loadCommand.cmd);
}
else {
// TODO: Check if cmdsize matches expected size for cmd
uint8_t cmd[loadCommand.cmdsize];
@@ -314,6 +314,64 @@ int macho_enumerate_rpaths(MachO *macho, void (^enumeratorBlock)(const char *rpa
return 0;
}

uint64_t macho_get_base_address(MachO *macho)
{
__block int64_t base = UINT64_MAX;
macho_enumerate_load_commands(macho, ^(struct load_command loadCommand, uint64_t offset, void *cmd, bool *stop) {
if (loadCommand.cmd == LC_SEGMENT_64) {
struct segment_command_64 *segmentCommand = (struct segment_command_64 *)cmd;
SEGMENT_COMMAND_64_APPLY_BYTE_ORDER(segmentCommand, LITTLE_TO_HOST_APPLIER);
if (segmentCommand->vmaddr < base) {
base = segmentCommand->vmaddr;
}
}
});
return base;
}

int macho_enumerate_function_starts(MachO *macho, void (^enumeratorBlock)(uint64_t funcAddr, bool *stop))
{
__block uint64_t functionStartsDataOff = 0, functionStartsDataSize = 0;

macho_enumerate_load_commands(macho, ^(struct load_command loadCommand, uint64_t offset, void *cmd, bool *stopLC) {
if (loadCommand.cmd == LC_FUNCTION_STARTS) {
struct linkedit_data_command *functionStartsCommand = (struct linkedit_data_command *)cmd;
LINKEDIT_DATA_COMMAND_APPLY_BYTE_ORDER(functionStartsCommand, LITTLE_TO_HOST_APPLIER);
functionStartsDataOff = functionStartsCommand->dataoff;
functionStartsDataSize = functionStartsCommand->datasize;
*stopLC = true;
}
});

if (!functionStartsDataOff || !functionStartsDataSize) return -1;

uint8_t *info = malloc(functionStartsDataSize);
if (macho_read_at_offset(macho, functionStartsDataOff, functionStartsDataSize, info) == 0) {
uint8_t *infoEnd = &info[functionStartsDataSize];
uint64_t address = macho_get_base_address(macho);
for (uint8_t *p = info; (*p != 0) && (p < infoEnd); ) {
bool stop = false;
uint64_t delta = 0;
uint32_t shift = 0;
bool more = true;
do {
uint8_t byte = *p++;
delta |= ((byte & 0x7F) << shift);
shift += 7;
if (byte < 0x80) {
address += delta;
enumeratorBlock(address, &stop);
more = false;
}
} while (more);
if (stop) break;
}
}
free(info);

return 0;
}

int macho_parse_segments(MachO *macho)
{
return macho_enumerate_load_commands(macho, ^(struct load_command loadCommand, uint64_t offset, void *cmd, bool *stop) {
2 changes: 2 additions & 0 deletions src/MachO.h
Original file line number Diff line number Diff line change
@@ -58,12 +58,14 @@ int macho_translate_vmaddr_to_fileoff(MachO *macho, uint64_t vmaddr, uint64_t *f
int macho_read_at_vmaddr(MachO *macho, uint64_t vmaddr, size_t size, void *outBuf);
int macho_write_at_vmaddr(MachO *macho, uint64_t vmaddr, size_t size, const void *inBuf);
int macho_read_string_at_vmaddr(MachO *macho, uint64_t vmaddr, char **outString);
uint64_t macho_get_base_address(MachO *macho);

int macho_enumerate_load_commands(MachO *macho, void (^enumeratorBlock)(struct load_command loadCommand, uint64_t offset, void *cmd, bool *stop));
int macho_enumerate_sections(MachO *macho, void (^enumeratorBlock)(struct section_64 *section, struct segment_command_64 *segment, bool *stop));
int macho_enumerate_symbols(MachO *macho, void (^enumeratorBlock)(const char *name, uint8_t type, uint64_t vmaddr, bool *stop));
int macho_enumerate_dependencies(MachO *macho, void (^enumeratorBlock)(const char *dylibPath, uint32_t cmd, struct dylib* dylib, bool *stop));
int macho_enumerate_rpaths(MachO *macho, void (^enumeratorBlock)(const char *rpath, bool *stop));
int macho_enumerate_function_starts(MachO *macho, void (^enumeratorBlock)(uint64_t funcAddr, bool *stop));

// Initialise a MachO object from a MemoryStream and it's corresponding FAT arch descriptor
MachO *macho_init(MemoryStream *stream, struct fat_arch_64 archDescriptor);
16 changes: 16 additions & 0 deletions src/PatchFinder.c
Original file line number Diff line number Diff line change
@@ -266,8 +266,23 @@ uint64_t pfsec_find_next_inst(PFSection *section, uint64_t startAddr, uint32_t s

uint64_t pfsec_find_function_start(PFSection *section, uint64_t midAddr)
{
// If the MachO contains function starts, use those to determine the function start
__block uint64_t start = 0;
if (macho_enumerate_function_starts(section->macho, ^(uint64_t funcAddr, bool *stop){
if (funcAddr < midAddr) {
start = funcAddr;
}
else {
*stop = true;
}
}) == 0) {
return start;
}

// If it doesn't contain the function starts, try to find it based on a heuristic approach
if (section->macho->machHeader.cputype == CPU_TYPE_ARM64) {
if ((section->macho->machHeader.cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64E) {
// Find start of function by going back until we find a PACIBSP
uint64_t addr = midAddr;
while (addr > section->vmaddr && addr < (section->vmaddr + section->size)) {
uint32_t curInst = pfsec_read32(section, addr);
@@ -276,6 +291,7 @@ uint64_t pfsec_find_function_start(PFSection *section, uint64_t midAddr)
}
}
else if ((section->macho->machHeader.cpusubtype & ~CPU_SUBTYPE_MASK) == CPU_SUBTYPE_ARM64_ALL) {
// Find start of function by going back until we find a stack frame push
// Technique adapted from pongoOS
uint64_t frameAddr = pfsec_find_prev_inst(section, midAddr, 0, 0x910003fd, 0xff8003ff); // add x29, sp, ?
if (frameAddr) {

0 comments on commit b30883c

Please sign in to comment.