Skip to content

Instantly share code, notes, and snippets.

@EthanArbuckle
Last active March 19, 2025 15:24
Show Gist options
  • Save EthanArbuckle/6b3ce401dd1f98caadb29f0580a419df to your computer and use it in GitHub Desktop.
Save EthanArbuckle/6b3ce401dd1f98caadb29f0580a419df to your computer and use it in GitHub Desktop.
find function by string xref
/*
Found string at address: 0x1048aa84d
Found reference in function: hidden_function
Found function that references string: 0x1048a89ec
*/
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import <mach-o/loader.h>
#import <mach-o/dyld.h>
#import <dlfcn.h>
#define is_adrp(insn) ((insn & 0x9F000000) == 0x90000000)
#define is_add_imm(insn) ((insn & 0xFFC00000) == 0x91000000)
#define is_adr(insn) ((insn & 0x9F000000) == 0x10000000)
extern struct mach_header_64 *_dyld_get_prog_image_header(void);
int main(int argc, char *argv[]);
static uint64_t get_slide(void) {
static int main_index = -1;
if (main_index == -1) {
for (int i = 0; i < _dyld_image_count(); i++) {
if ((const struct mach_header_64 *)_dyld_get_image_header(i) == _dyld_get_prog_image_header()) {
main_index = i;
break;
}
}
}
return _dyld_get_image_vmaddr_slide(main_index);
}
static const struct segment_command_64 *find_text_segment(const struct mach_header_64 *mh) {
const struct load_command *lc = (const struct load_command *)((const char *)mh + sizeof(struct mach_header_64));
for (uint32_t i = 0; i < mh->ncmds; i++) {
if (lc->cmd == LC_SEGMENT_64) {
const struct segment_command_64 *seg = (const struct segment_command_64 *)lc;
if (strcmp(seg->segname, "__TEXT") == 0) {
return seg;
}
}
lc = (const struct load_command*)((uintptr_t)lc + lc->cmdsize);
}
return NULL;
}
static const struct section_64 *find_cstring_section(const struct segment_command_64 *text_seg) {
const struct section_64 *sect = (const struct section_64*)((uintptr_t)text_seg + sizeof(struct segment_command_64));
for (uint32_t i = 0; i < text_seg->nsects; i++) {
if (strcmp(sect[i].sectname, "__cstring") == 0) {
return &sect[i];
}
}
return NULL;
}
uint64_t find_string_address(const char *target_str) {
const struct mach_header_64 *mh = _dyld_get_prog_image_header();
const struct segment_command_64 *text_seg = find_text_segment(mh);
if (text_seg == NULL) {
return 0;
}
const struct section_64 *cstring_sect = find_cstring_section(text_seg);
if (cstring_sect == NULL) {
return 0;
}
intptr_t slide = get_slide();
const char *string_base = (const char *)(cstring_sect->addr + slide);
uint64_t section_size = cstring_sect->size;
if (string_base == NULL || section_size == 0) {
return 0;
}
const char *string_end = string_base + section_size;
const char *current = string_base;
while (current < string_end && current >= string_base) {
if (current && *current != '\0') {
if (strcmp(current, target_str) == 0) {
return (uint64_t)current;
}
size_t len = strnlen(current, string_end - current);
current += len + 1;
}
else {
current++;
}
}
return 0;
}
static void hidden_function(void) {
printf("abc123 %d\n", getpid());
}
uint64_t find_string_refs(uint64_t string_addr, uint64_t search_start, uint64_t search_end) {
uint32_t *current = (uint32_t *)search_start;
struct mach_header_64 *mh = (struct mach_header_64 *)_dyld_get_prog_image_header();
uint32_t *end = (uint32_t *)((uintptr_t)mh + (search_end - search_start));
uint64_t last_adrp_target = 0;
int last_adrp_reg = -1;
int main_index = -1;
for (int i = 0; i < _dyld_image_count(); i++) {
const struct mach_header_64 *image_header = (const struct mach_header_64 *)_dyld_get_image_header(i);
if (image_header == mh) {
main_index = i;
break;
}
}
while (current < end) {
uint32_t insn = *current;
if (is_adrp(insn)) {
int rd = (insn & 0x1F);
uint64_t immhi = (insn >> 5) & 0x7FFFF;
uint64_t immlo = (insn >> 29) & 0x3;
int64_t imm = (immhi << 2) | immlo;
if (imm & (1ULL << 20)) {
imm |= ~((1ULL << 21) - 1);
}
uint64_t pc = (uint64_t)current & ~0xFFF;
last_adrp_target = pc + (imm << 12);
last_adrp_reg = rd;
}
if (is_add_imm(insn)) {
int rn = (insn >> 5) & 0x1F;
uint64_t imm = ((insn >> 10) & 0xFFF);
if (rn == last_adrp_reg) {
uint64_t target = last_adrp_target + imm;
if (target == string_addr) {
Dl_info info;
dladdr((void *)current, &info);
// needed because our lookup itself is a ref to the str
if ((uint64_t)info.dli_saddr == (uint64_t)main) {
current++;
continue;
}
printf("Found reference in function: %s\n", info.dli_sname);
return (uint64_t)info.dli_saddr;
}
}
}
current++;
}
return 0;
}
int main(int argc, char *argv[]) {
hidden_function();
const struct mach_header_64 *mh = _dyld_get_prog_image_header();
if (mh == NULL) {
return 1;
}
const char *target_str = "abc123 %d\n";
uint64_t str_addr = find_string_address(target_str);
if (str_addr) {
printf("Found string at address: 0x%llx\n", str_addr);
const struct segment_command_64 *text_seg = find_text_segment(mh);
if (text_seg) {
uint64_t search_start = (uint64_t)mh;
uint64_t search_end = search_start + text_seg->vmsize;
uint64_t target_function = find_string_refs(str_addr, search_start, search_end);
if (target_function) {
printf("Found function that references string: 0x%llx\n", target_function);
}
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment