/* * ocalc.c - utility to calculate the instruction offset inside a mach-o binary * coded by ghalen -- 2009/03/20 * * This code is roughly based on 'offset12pl' by Fractal Guru * Remember to use the right offset for the arch you're playing with :) * * - ghalen / http://kmem.se */ #include #include #include #include #include #include #include void get_offset(const char *buf, uint32_t base, uint32_t my_offset, uint32_t arch) { int i, j; uint32_t addr = 0x0, size = 0x0, offset = 0x0, real_offset = 0x0; uint32_t ncmds, nsects, cmdsize, cmd; struct mach_header *mh; /* mach header */ struct load_command *lc; /* load commands */ struct segment_command *sc; /* segment command */ struct section *st; /* section */ mh = (struct mach_header *)(buf + base); lc = (struct load_command*)((char*)mh + sizeof(struct mach_header)); ncmds = (arch == CPU_TYPE_I386)?(mh->ncmds):(ntohl(mh->ncmds)); for (i = 0; i < ncmds; i++) { cmd = (arch == CPU_TYPE_I386)?(lc->cmd):(ntohl(lc->cmd)); if (cmd != LC_SEGMENT) goto end; sc = (struct segment_command *)lc; if (strcmp(sc->segname, "__TEXT")) goto end; st = (struct section *)((char *)sc + sizeof(struct segment_command)); nsects = (arch == CPU_TYPE_I386)?(sc->nsects):(ntohl(sc->nsects)); for (j = 0; j < nsects; j++) { if (!strcmp(st[j].sectname, "__text")) break; } addr = (arch == CPU_TYPE_I386)?(st[j].addr):(ntohl(st[j].addr)); size = (arch == CPU_TYPE_I386)?(st[j].size):(ntohl(st[j].size)); offset = (arch == CPU_TYPE_I386)?(st[j].offset):(ntohl(st[j].offset)); end: cmdsize = (arch == CPU_TYPE_I386)?(lc->cmdsize):(ntohl(lc->cmdsize)); lc = (struct load_command*)((char*)lc + cmdsize); } if (my_offset < addr || my_offset > (addr + size)) { fprintf(stderr, " ! your offset is outside the code region for this arch\n"); return; } real_offset = base + offset + my_offset - addr; fprintf(stderr, " -> actual offset for this arch: %d (%p)\n", real_offset, (void *)real_offset); } int main(int argc, char *argv[]) { unsigned int sz; uint32_t intel_base = 0x0, my_offset, narchs = 0x0, ppc_base = 0x0; int i; char *buf; FILE *fp; struct fat_header *fh; /* fat header */ struct fat_arch *fa; /* fat arch */ struct mach_header *mh; /* mach header */ if (argc != 3) { fprintf(stderr, "usage: %s \n", argv[0]); return 0; } my_offset = (uint32_t)strtoul(argv[2], NULL, 0); fp = fopen(argv[1], "r"); if (!fp) { fprintf(stderr, "could not open file\n"); exit(EXIT_FAILURE); } fseek(fp, 0, SEEK_END); sz = ftell(fp); fseek(fp, 0, SEEK_SET); buf = malloc(sz); fread(buf, sz, 1, fp); fclose(fp); /* do we have a fat binary? * if that's the case, start parsing the fat header */ if (ntohl(*(unsigned int*)buf) == FAT_MAGIC) { fh = (struct fat_header *)(buf); narchs = ntohl(fh->nfat_arch); fprintf(stderr, " -> found fat header with %x archs\n", narchs); fa = (struct fat_arch *)(buf + sizeof(struct fat_header)); for (i = 0; i < narchs; i++) { switch (ntohl(fa->cputype)) { case CPU_TYPE_I386: intel_base = ntohl(fa->offset); fprintf(stderr, " + intel offset: %x\n", intel_base); get_offset(buf, intel_base, my_offset, CPU_TYPE_I386); break; case CPU_TYPE_POWERPC: ppc_base = ntohl(fa->offset); fprintf(stderr, " + ppc offset: %x\n", ppc_base); get_offset(buf, ppc_base, my_offset, CPU_TYPE_POWERPC); break; default: break; } fa += ((sizeof(struct fat_arch) * i) + 1); } } else { /* if not, check the mach header */ fprintf(stderr, " -> this is not a fat binary, checking arch ... "); fflush(stderr); mh = (struct mach_header *)(buf); switch (mh->cputype) { case CPU_TYPE_I386: fprintf(stderr, "i386 found\n"); get_offset(buf, 0, my_offset, CPU_TYPE_I386); break; case CPU_TYPE_POWERPC: fprintf(stderr, "ppc found\n"); get_offset(buf, 0, my_offset, CPU_TYPE_POWERPC); break; default: fprintf(stderr, "unknown\nthis is binary is neither I386 nor PPC.\n"); break; } } free(buf); return 0; }