Skip to content

Instantly share code, notes, and snippets.

@ichitaso
Forked from jakeajames/Makefile
Created March 31, 2019 19:27
Show Gist options
  • Save ichitaso/c6781b7218d5bc77ff71bc05a61ac773 to your computer and use it in GitHub Desktop.
Save ichitaso/c6781b7218d5bc77ff71bc05a61ac773 to your computer and use it in GitHub Desktop.
reverse kCFCoreFoundationVersion checks. Uses code from xerub. Code will suck in some places. I warned you.
Package: ga.jakeashacks.pcfvc
Name: patch_cfversion_checks
Depends:
Version: 0.0.1
Architecture: iphoneos-arm
Description: A COOL tool for some STAR!
Maintainer: Jake James
Author: Jake James
Section: System
Tag: role::hacker
//
// patchfinder64.c
// extra_recipe
//
// Created by xerub on 06/06/2017.
// Copyright © 2017 xerub. All rights reserved.
//
#include "macho_offset_finder.h"
uint8_t *Macho = NULL;
size_t Macho_size = 0;
addr_t TEXT_Base = 0;
addr_t TEXT_Size = 0;
addr_t Prelink_Base = 0;
addr_t Prelink_Size = 0;
addr_t CString_base = 0;
addr_t CString_size = 0;
addr_t PString_base = 0;
addr_t PString_size = 0;
addr_t OSLog_base = 0;
addr_t OSLog_size = 0;
addr_t Data_base = 0;
addr_t Data_size = 0;
addr_t Data_const_base = 0;
addr_t Data_const_size = 0;
addr_t GOT_base = 0;
addr_t GOT_size = 0;
addr_t GOT_offset = 0;
addr_t PPLText_base = 0;
addr_t PPLText_size = 0;
addr_t MachoBase = -1;
addr_t Macho_entry = 0;
void *Macho_mh = 0;
addr_t Macho_delta = 0;
unsigned char *
Boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen,
const unsigned char* needle, size_t nlen)
{
size_t last, scan = 0;
size_t bad_char_skip[UCHAR_MAX + 1]; /* Officially called:
* bad character shift */
/* Sanity checks on the parameters */
if (nlen <= 0 || !haystack || !needle)
return NULL;
/* ---- Preprocess ---- */
/* Initialize the table to default value */
/* When a character is encountered that does not occur
* in the needle, we can safely skip ahead for the whole
* length of the needle.
*/
for (scan = 0; scan <= UCHAR_MAX; scan = scan + 1)
bad_char_skip[scan] = nlen;
/* C arrays have the first byte at [0], therefore:
* [nlen - 1] is the last byte of the array. */
last = nlen - 1;
/* Then populate it with the analysis of the needle */
for (scan = 0; scan < last; scan = scan + 1)
bad_char_skip[needle[scan]] = last - scan;
/* ---- Do the matching ---- */
/* Search the haystack, while the needle can still be within it. */
while (hlen >= nlen)
{
/* scan from the end of the needle */
for (scan = last; haystack[scan] == needle[scan]; scan = scan - 1)
if (scan == 0) /* If the first byte matches, we've found it. */
return (void *)haystack;
/* otherwise, we need to skip some bytes and start again.
Note that here we are getting the skip value based on the last byte
of needle, no matter where we didn't match. So if needle is: "abcd"
then we are skipping based on 'd' and that value will be 4, and
for "abcdd" we again skip on 'd' but the value will be only 1.
The alternative of pretending that the mismatched character was
the last character is slower in the normal case (E.g. finding
"abcd" in "...azcd..." gives 4 by using 'd' but only
4-2==2 using 'z'. */
hlen -= bad_char_skip[haystack[last]];
haystack += bad_char_skip[haystack[last]];
}
return NULL;
}
/* disassembler **************************************************************/
int HighestSetBit(int N, uint32_t imm)
{
int i;
for (i = N - 1; i >= 0; i--) {
if (imm & (1 << i)) {
return i;
}
}
return -1;
}
uint64_t ZeroExtendOnes(unsigned M, unsigned N) // zero extend M ones to N width
{
(void)N;
return ((uint64_t)1 << M) - 1;
}
uint64_t RORZeroExtendOnes(unsigned M, unsigned N, unsigned R)
{
uint64_t val = ZeroExtendOnes(M, N);
if (R == 0) {
return val;
}
return ((val >> R) & (((uint64_t)1 << (N - R)) - 1)) | ((val & (((uint64_t)1 << R) - 1)) << (N - R));
}
uint64_t Replicate(uint64_t val, unsigned bits)
{
uint64_t ret = val;
unsigned shift;
for (shift = bits; shift < 64; shift += bits) { // XXX actually, it is either 32 or 64
ret |= (val << shift);
}
return ret;
}
int DecodeBitMasks(unsigned immN, unsigned imms, unsigned immr, int immediate, uint64_t *newval)
{
unsigned levels, S, R, esize;
int len = HighestSetBit(7, (immN << 6) | (~imms & 0x3F));
if (len < 1) {
return -1;
}
levels = ZeroExtendOnes(len, 6);
if (immediate && (imms & levels) == levels) {
return -1;
}
S = imms & levels;
R = immr & levels;
esize = 1 << len;
*newval = Replicate(RORZeroExtendOnes(S + 1, esize, R), esize);
return 0;
}
int DecodeMov(uint32_t opcode, uint64_t total, int first, uint64_t *newval)
{
unsigned o = (opcode >> 29) & 3;
unsigned k = (opcode >> 23) & 0x3F;
unsigned rn, rd;
uint64_t i;
if (k == 0x24 && o == 1) { // MOV (bitmask imm) <=> ORR (immediate)
unsigned s = (opcode >> 31) & 1;
unsigned N = (opcode >> 22) & 1;
if (s == 0 && N != 0) {
return -1;
}
rn = (opcode >> 5) & 0x1F;
if (rn == 31) {
unsigned imms = (opcode >> 10) & 0x3F;
unsigned immr = (opcode >> 16) & 0x3F;
return DecodeBitMasks(N, imms, immr, 1, newval);
}
} else if (k == 0x25) { // MOVN/MOVZ/MOVK
unsigned s = (opcode >> 31) & 1;
unsigned h = (opcode >> 21) & 3;
if (s == 0 && h > 1) {
return -1;
}
i = (opcode >> 5) & 0xFFFF;
h *= 16;
i <<= h;
if (o == 0) { // MOVN
*newval = ~i;
return 0;
} else if (o == 2) { // MOVZ
*newval = i;
return 0;
} else if (o == 3 && !first) { // MOVK
*newval = (total & ~((uint64_t)0xFFFF << h)) | i;
return 0;
}
} else if ((k | 1) == 0x23 && !first) { // ADD (immediate)
unsigned h = (opcode >> 22) & 3;
if (h > 1) {
return -1;
}
rd = opcode & 0x1F;
rn = (opcode >> 5) & 0x1F;
if (rd != rn) {
return -1;
}
i = (opcode >> 10) & 0xFFF;
h *= 12;
i <<= h;
if (o & 2) { // SUB
*newval = total - i;
return 0;
} else { // ADD
*newval = total + i;
return 0;
}
}
return -1;
}
/* patchfinder ***************************************************************/
addr_t
Step64(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask)
{
addr_t end = start + length;
while (start < end) {
uint32_t x = *(uint32_t *)(buf + start);
if ((x & mask) == what) {
return start;
}
start += 4;
}
return 0;
}
// str8 = Step64_back(Macho, ref, ref - bof, INSN_STR8);
addr_t
Step64_back(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask)
{
addr_t end = start - length;
while (start >= end) {
uint32_t x = *(uint32_t *)(buf + start);
if ((x & mask) == what) {
return start;
}
start -= 4;
}
return 0;
}
// Finds start of function
addr_t
BOF64(const uint8_t *buf, addr_t start, addr_t where)
{
for (; where >= start; where -= 4) {
uint32_t op = *(uint32_t *)(buf + where);
if ((op & 0xFFC003FF) == 0x910003FD) {
unsigned delta = (op >> 10) & 0xFFF;
//printf("%x: ADD X29, SP, #0x%x\n", where, delta);
if ((delta & 0xF) == 0) {
addr_t prev = where - ((delta >> 4) + 1) * 4;
uint32_t au = *(uint32_t *)(buf + prev);
if ((au & 0xFFC003E0) == 0xA98003E0) {
//printf("%x: STP x, y, [SP,#-imm]!\n", prev);
if (*(uint32_t *)(buf + prev - 4) == 0xd503237f) return prev - 4;
return prev;
}
// try something else
while (where > start) {
where -= 4;
au = *(uint32_t *)(buf + where);
// SUB SP, SP, #imm
if ((au & 0xFFC003FF) == 0xD10003FF && ((au >> 10) & 0xFFF) == delta + 0x10) {
if (*(uint32_t *)(buf + where - 4) == 0xd503237f) return where - 4;
return where;
}
// STP x, y, [SP,#imm]
if ((au & 0xFFC003E0) != 0xA90003E0) {
where += 4;
break;
}
}
}
}
}
return 0;
}
addr_t
Follow_call64(const uint8_t *buf, addr_t call)
{
long long w;
w = *(uint32_t *)(buf + call) & 0x3FFFFFF;
w <<= 64 - 26;
w >>= 64 - 26 - 2;
return call + w;
}
addr_t
XREF64(const uint8_t *buf, addr_t start, addr_t end, addr_t what)
{
addr_t i;
uint64_t value[32];
memset(value, 0, sizeof(value));
end &= ~3;
for (i = start & ~3; i < end; i += 4) {
uint32_t op = *(uint32_t *)(buf + i);
unsigned reg = op & 0x1F;
if ((op & 0x9F000000) == 0x90000000) {
signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
//printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF));
value[reg] = ((long long)adr << 1) + (i & ~0xFFF);
/*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) {
unsigned rd = op & 0x1F;
unsigned rm = (op >> 16) & 0x1F;
//printf("%llx: MOV X%d, X%d\n", i, rd, rm);
value[rd] = value[rm];*/
} else if ((op & 0xFF000000) == 0x91000000) {
unsigned rn = (op >> 5) & 0x1F;
unsigned shift = (op >> 22) & 3;
unsigned imm = (op >> 10) & 0xFFF;
if (shift == 1) {
imm <<= 12;
} else {
//assert(shift == 0);
if (shift > 1) continue;
}
//printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm);
value[reg] = value[rn] + imm;
} else if ((op & 0xF9C00000) == 0xF9400000) {
unsigned rn = (op >> 5) & 0x1F;
unsigned imm = ((op >> 10) & 0xFFF) << 3;
//printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
if (!imm) continue; // XXX not counted as true xref
value[reg] = value[rn] + imm; // XXX address, not actual value
/*} else if ((op & 0xF9C00000) == 0xF9000000) {
unsigned rn = (op >> 5) & 0x1F;
unsigned imm = ((op >> 10) & 0xFFF) << 3;
//printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
if (!imm) continue; // XXX not counted as true xref
value[rn] = value[rn] + imm; // XXX address, not actual value*/
} else if ((op & 0x9F000000) == 0x10000000) {
signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
//printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i);
value[reg] = ((long long)adr >> 11) + i;
} else if ((op & 0xFF000000) == 0x58000000) {
unsigned adr = (op & 0xFFFFE0) >> 3;
//printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i);
value[reg] = adr + i; // XXX address, not actual value
}
else if ((op & 0xFC000000) == 0x94000000) {
if (Follow_call64(buf, i) == what) {
return i;
}
}
else if ((op & 0xFC000000) == 0x14000000) {
if (Follow_call64(buf, i) == what) {
return i;
}
}
if (value[reg] == what) {
return i;
}
}
return 0;
}
addr_t
Calc64(const uint8_t *buf, addr_t start, addr_t end, int which)
{
addr_t i;
uint64_t value[32];
memset(value, 0, sizeof(value));
end &= ~3;
for (i = start & ~3; i < end; i += 4) {
uint32_t op = *(uint32_t *)(buf + i);
unsigned reg = op & 0x1F;
if ((op & 0x9F000000) == 0x90000000) {
signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
//printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF));
value[reg] = ((long long)adr << 1) + (i & ~0xFFF);
/*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) {
unsigned rd = op & 0x1F;
unsigned rm = (op >> 16) & 0x1F;
//printf("%llx: MOV X%d, X%d\n", i, rd, rm);
value[rd] = value[rm];*/
} else if ((op & 0xFF000000) == 0x91000000) {
unsigned rn = (op >> 5) & 0x1F;
unsigned shift = (op >> 22) & 3;
unsigned imm = (op >> 10) & 0xFFF;
if (shift == 1) {
imm <<= 12;
} else {
//assert(shift == 0);
if (shift > 1) continue;
}
//printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm);
value[reg] = value[rn] + imm;
} else if ((op & 0xFF000000) == 0xd2000000) {
unsigned val = (op & 0x1fffe0) >> 5; // idk if this is really correct but works for our purpose
value[reg] = val;
}
else if ((op & 0xF9C00000) == 0xF9400000) {
unsigned rn = (op >> 5) & 0x1F;
unsigned imm = ((op >> 10) & 0xFFF) << 3;
//printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
if (!imm) continue; // XXX not counted as true xref
value[reg] = value[rn] + imm; // XXX address, not actual value
} else if ((op & 0xF9C00000) == 0xb9400000) { // 32bit
unsigned rn = (op >> 5) & 0x1F;
unsigned imm = ((op >> 10) & 0xFFF) << 2;
if (!imm) continue; // XXX not counted as true xref
value[reg] = value[rn] + imm; // XXX address, not actual value
} else if ((op & 0xF9C00000) == 0xF9000000) {
unsigned rn = (op >> 5) & 0x1F;
unsigned imm = ((op >> 10) & 0xFFF) << 3;
//printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
if (!imm) continue; // XXX not counted as true xref
value[rn] = value[rn] + imm; // XXX address, not actual value
} else if ((op & 0x9F000000) == 0x10000000) {
signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
//printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i);
value[reg] = ((long long)adr >> 11) + i;
} else if ((op & 0xFF000000) == 0x58000000) {
unsigned adr = (op & 0xFFFFE0) >> 3;
//printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i);
value[reg] = adr + i; // XXX address, not actual value
}
}
return value[which];
}
addr_t
Calc64mov(const uint8_t *buf, addr_t start, addr_t end, int which)
{
addr_t i;
uint64_t value[32];
memset(value, 0, sizeof(value));
end &= ~3;
for (i = start & ~3; i < end; i += 4) {
uint32_t op = *(uint32_t *)(buf + i);
unsigned reg = op & 0x1F;
uint64_t newval;
int rv = DecodeMov(op, value[reg], 0, &newval);
if (rv == 0) {
if (((op >> 31) & 1) == 0) {
newval &= 0xFFFFFFFF;
}
value[reg] = newval;
}
}
return value[which];
}
addr_t
Find_call64(const uint8_t *buf, addr_t start, size_t length)
{
return Step64(buf, start, length, 0x94000000, 0xFC000000);
}
addr_t
Follow_cbz(const uint8_t *buf, addr_t cbz)
{
return cbz + ((*(int *)(buf + cbz) & 0x3FFFFE0) << 10 >> 13);
}
uint64_t machoOffset = 0;
int
InitPatchfinder(addr_t base, const char *filename)
{
size_t rv;
uint8_t buf1[0x8000];
unsigned i, j;
const uint8_t *q;
addr_t min = -1;
addr_t max = 0;
int is64 = 0;
int fd = open(filename, O_RDONLY);
if (fd < 0) {
return -1;
}
rv = read(fd, buf1, sizeof(buf1));
if (rv != sizeof(buf1)) {
close(fd);
return -1;
}
if (!MACHO(buf1) && *(uint32_t*)buf1 != 0xBEBAFECA) {
close(fd);
return -1;
}
if (*(uint32_t*)buf1 == 0xBEBAFECA) {
uint32_t arch_off = sizeof(struct fat_header);
struct fat_header *fat = (struct fat_header*)(buf1 + 0);
bool foundarm64 = false;
int n = ntohl(fat->nfat_arch);
printf("[*] Binary is FAT with %d architectures\n", n);
while (n-- > 0) {
struct fat_arch *arch = (struct fat_arch *)(buf1 + arch_off);
if (ntohl(arch->cputype) == 0x100000c) {
printf("[*] Found arm64\n");
machoOffset = ntohl(arch->offset);
foundarm64 = true;
break;
}
arch_off += sizeof(struct fat_arch);
}
if (!foundarm64) {
printf("[-] Binary does not have any arm64 slice\n");
return -1;
}
}
uint8_t *buf = (uint8_t *)(((uint64_t)&buf1) + machoOffset);
const struct mach_header *hdr = (struct mach_header *)buf;
if (IS64(buf)) {
is64 = 4;
}
q = buf + sizeof(struct mach_header) + is64;
for (i = 0; i < hdr->ncmds; i++) {
const struct load_command *cmd = (struct load_command *)q;
if (cmd->cmd == LC_SEGMENT_64) {
const struct segment_command_64 *seg = (struct segment_command_64 *)q;
if (min > seg->vmaddr) {
min = seg->vmaddr;
}
if (max < seg->vmaddr + seg->vmsize) {
max = seg->vmaddr + seg->vmsize;
}
if (!strcmp(seg->segname, "__TEXT")) {
const struct section_64 *sec = (struct section_64 *)(seg + 1);
for (j = 0; j < seg->nsects; j++) {
if (!strcmp(sec[j].sectname, "__text")) {
TEXT_Base = sec[j].addr;
TEXT_Size = sec[j].size;
}
}
}
else if (!strcmp(seg->segname, "__PPLTEXT")) {
PPLText_base = seg->vmaddr;
PPLText_size = seg->filesize;
}
else if (!strcmp(seg->segname, "__PLK_TEXT_EXEC")) {
Prelink_Base = seg->vmaddr;
Prelink_Size = seg->filesize;
}
else if (!strcmp(seg->segname, "__DATA")) {
const struct section_64 *sec = (struct section_64 *)(seg + 1);
for (j = 0; j < seg->nsects; j++) {
if (!strcmp(sec[j].sectname, "__data")) {
Data_base = sec[j].addr;
Data_size = sec[j].size;
}
else if (!strcmp(sec[j].sectname, "__const")) {
Data_const_base = sec[j].addr;
Data_const_size = sec[j].size;
}
else if (!strcmp(sec[j].sectname, "__got")) {
GOT_base = sec[j].addr;
GOT_size = sec[j].size;
GOT_offset = (uint64_t)sec - (uint64_t)buf;
}
}
}
else if (!strcmp(seg->segname, "__TEXT")) {
const struct section_64 *sec = (struct section_64 *)(seg + 1);
for (j = 0; j < seg->nsects; j++) {
if (!strcmp(sec[j].sectname, "__cstring")) {
CString_base = sec[j].addr;
CString_size = sec[j].size;
}
if (!strcmp(sec[j].sectname, "__os_log")) {
OSLog_base = sec[j].addr;
OSLog_size = sec[j].size;
}
}
}
else if (!strcmp(seg->segname, "__PRELINK_TEXT")) {
const struct section_64 *sec = (struct section_64 *)(seg + 1);
for (j = 0; j < seg->nsects; j++) {
if (!strcmp(sec[j].sectname, "__text")) {
PString_base = sec[j].addr;
PString_size = sec[j].size;
}
}
}
else if (!strcmp(seg->segname, "__LINKEDIT")) {
Macho_delta = seg->vmaddr - min - seg->fileoff;
}
}
else if (cmd->cmd == LC_UNIXTHREAD) {
uint32_t *ptr = (uint32_t *)(cmd + 1);
uint32_t flavor = ptr[0];
struct {
uint64_t x[29]; /* General purpose registers x0-x28 */
uint64_t fp; /* Frame pointer x29 */
uint64_t lr; /* Link register x30 */
uint64_t sp; /* Stack pointer x31 */
uint64_t pc; /* Program counter */
uint32_t cpsr; /* Current program status register */
} *thread = (void *)(ptr + 2);
if (flavor == 6) {
Macho_entry = thread->pc;
}
}
q = q + cmd->cmdsize;
}
MachoBase = min;
TEXT_Base -= MachoBase;
Prelink_Base -= MachoBase;
CString_base -= MachoBase;
PString_base -= MachoBase;
OSLog_base -= MachoBase;
Data_base -= MachoBase;
Data_const_base -= MachoBase;
PPLText_base -= MachoBase;
GOT_base -= MachoBase;
Macho_size = max - min;
Macho = calloc(1, Macho_size);
if (!Macho) {
close(fd);
return -1;
}
q = buf + sizeof(struct mach_header) + is64;
for (i = 0; i < hdr->ncmds; i++) {
const struct load_command *cmd = (struct load_command *)q;
if (cmd->cmd == LC_SEGMENT_64) {
const struct segment_command_64 *seg = (struct segment_command_64 *)q;
size_t sz = pread(fd, Macho + seg->vmaddr - min, seg->filesize, seg->fileoff);
if (sz != seg->filesize) {
close(fd);
free(Macho);
return -1;
}
if (!Macho_mh) {
Macho_mh = Macho + machoOffset + seg->vmaddr - min;
}
//printf("%s\n", seg->segname);
if (!strcmp(seg->segname, "__LINKEDIT")) {
Macho_delta = seg->vmaddr - min - seg->fileoff;
}
}
q = q + cmd->cmdsize;
}
Macho += machoOffset;
close(fd);
(void)base;
return 0;
}
void
TermPatchfinder(void)
{
free(Macho);
}
addr_t
Find_register_value(addr_t where, int reg)
{
addr_t val;
addr_t bof = 0;
where -= MachoBase;
if (where > TEXT_Base) {
bof = BOF64(Macho, TEXT_Base, where);
if (!bof) {
bof = TEXT_Base;
}
} else if (where > Prelink_Base) {
bof = BOF64(Macho, Prelink_Base, where);
if (!bof) {
bof = Prelink_Base;
}
}
val = Calc64(Macho, bof, where, reg);
if (!val) {
return 0;
}
return val + MachoBase;
}
addr_t
Find_reference(addr_t to, int n, int type)
{
addr_t ref, end;
addr_t base;
addr_t size;
base = TEXT_Base;
size = TEXT_Size;
if (type == 1) {
base = Prelink_Base;
size = Prelink_Size;
}
if (type == 4) {
base = PPLText_base;
size = PPLText_size;
}
if (n <= 0) {
n = 1;
}
end = base + size;
to -= MachoBase;
do {
ref = XREF64(Macho, base, end, to);
if (!ref) {
return 0;
}
base = ref + 4;
} while (--n > 0);
return ref + MachoBase;
}
addr_t
Find_strref(const char *string, int n, int type, bool exactMatch)
{
uint8_t *str;
addr_t base, size;
if (type == 1) {
base = PString_base;
size = PString_size;
}
else if (type == 2) {
base = OSLog_base;
size = OSLog_size;
}
else if (type == 3) {
base = Data_base;
size = Data_size;
}
else {
base = CString_base;
size = CString_size;
}
str = Boyermoore_horspool_memmem(Macho + base, size, (uint8_t *)string, strlen(string));
if (exactMatch) {
while (strcmp((char *)str, string)) {
base += ((uint64_t)str - (uint64_t)Macho - (uint64_t)base) + 1;
size -= strlen((char *)str) + 1;
str = Boyermoore_horspool_memmem(Macho + base, size, (uint8_t *)string, strlen(string));
}
}
if (!str) {
return 0;
}
return Find_reference(str - Macho + MachoBase, n, type);
}
//
// macho_offset_finder.h
// Th0r_empty_list
//
// Created by Jake James on 3/30/19.
// Copyright © 2019 Jake James. All rights reserved.
//
#ifndef macho_offset_finder_h
#define macho_offset_finder_h
#import <stdio.h>
#import <fcntl.h>
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#import <unistd.h>
#import <stdbool.h>
#import <mach-o/loader.h>
#import <mach-o/fat.h>
#define UCHAR_MAX 255
#define INSN_RET 0xD65F03C0, 0xFFFFFFFF
#define INSN_CALL 0x94000000, 0xFC000000
#define INSN_B 0x14000000, 0xFC000000
#define INSN_CBZ 0x34000000, 0xFC000000
#define INSN_ADRP 0x90000000, 0x9F000000
typedef unsigned long long addr_t;
#define IS64(image) (*(uint8_t *)(image) & 1)
#define MACHO(p) ((*(unsigned int *)(p) & ~1) == 0xfeedface)
extern uint64_t machoOffset;
unsigned char *Boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen, const unsigned char* needle, size_t nlen);
int HighestSetBit(int N, uint32_t imm);
uint64_t ZeroExtendOnes(unsigned M, unsigned N) ;
uint64_t RORZeroExtendOnes(unsigned M, unsigned N, unsigned R);
uint64_t Replicate(uint64_t val, unsigned bits);
int DecodeBitMasks(unsigned immN, unsigned imms, unsigned immr, int immediate, uint64_t *newval);
int DecodeMov(uint32_t opcode, uint64_t total, int first, uint64_t *newval);
addr_t Step64(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask);
addr_t Step64_back(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask);
addr_t BOF64(const uint8_t *buf, addr_t start, addr_t where);
addr_t Follow_call64(const uint8_t *buf, addr_t call);
addr_t XREF64(const uint8_t *buf, addr_t start, addr_t end, addr_t what);
addr_t Calc64mov(const uint8_t *buf, addr_t start, addr_t end, int which);
addr_t Find_call64(const uint8_t *buf, addr_t start, size_t length);
addr_t Follow_cbz(const uint8_t *buf, addr_t cbz);
extern uint8_t *Macho;
extern size_t Macho_size;
extern addr_t TEXT_Base;
extern addr_t TEXT_Size;
extern addr_t Prelink_Base;
extern addr_t Prelink_Size;
extern addr_t CString_base;
extern addr_t CString_size;
extern addr_t PString_base;
extern addr_t PString_size;
extern addr_t OSLog_base;
extern addr_t OSLog_size;
extern addr_t Data_base;
extern addr_t Data_size;
extern addr_t Data_const_base;
extern addr_t Data_const_size;
extern addr_t GOT_base;
extern addr_t GOT_size;
extern addr_t GOT_offset;
extern addr_t PPLText_base;
extern addr_t PPLText_size;
extern addr_t MachoBase;
extern addr_t Macho_entry;
extern void *Macho_mh;
extern addr_t Macho_delta;
int InitPatchfinder(addr_t base, const char *filename);
void TermPatchfinder(void);
addr_t Find_register_value(addr_t where, int reg);
addr_t Find_reference(addr_t to, int n, int type);
addr_t Find_strref(const char *string, int n, int type, bool exactMatch);
#endif /* macho_offset_finder_h */
//
// machoSymbolFinder.c
// machoSymbolFinder
//
// Created by Jake James on 8/21/18.
// Copyright © 2018 Jake James. All rights reserved.
//
#include "machoSymbolFinder.h"
#define SWAP32(p) __builtin_bswap32(p)
FILE *file;
uint32_t offset = 0;
void *load_bytes(FILE *obj_file, off_t offset, uint32_t size) {
void *buf = calloc(1, size);
fseek(obj_file, offset, SEEK_SET);
fread(buf, size, 1, obj_file);
return buf;
}
void write_bytes(FILE *obj_file, off_t offset, size_t size, void *bytes) {
fseek(obj_file, offset, SEEK_SET);
fwrite(bytes, size, 1, obj_file);
}
uint64_t find_load_cmd(uint32_t lc, size_t machoOffset) {
int ncmds = 0;
struct load_command *cmd = NULL;
size_t offset = machoOffset;
uint32_t *magic = load_bytes(file, offset, sizeof(uint32_t)); //at offset 0 we have the magic number
if (*magic == 0xFEEDFACF) {
struct mach_header_64 *mh64 = load_bytes(file, offset, sizeof(struct mach_header_64));
ncmds = mh64->ncmds;
free(mh64);
offset += sizeof(struct mach_header_64);
for (int i = 0; i < ncmds; i++) {
cmd = load_bytes(file, offset, sizeof(struct load_command));
if (cmd->cmd == lc) {
free(cmd);
return offset;
}
offset += cmd->cmdsize;
free(cmd);
}
}
//----32bit magic number----//
else if (*magic == 0xFEEDFACE) {
struct mach_header *mh = load_bytes(file, offset, sizeof(struct mach_header));
ncmds = mh->ncmds;
free(mh);
offset += sizeof(struct mach_header);
for (int i = 0; i < ncmds; i++) {
cmd = load_bytes(file, offset, sizeof(struct load_command));
if (cmd->cmd == lc) {
free(cmd);
return offset;
}
offset += cmd->cmdsize;
free(cmd);
}
}
else {
printf("[!] Unrecognized file\n");
return 0;
}
return 0;
}
uint64_t find_symbol(const char *symbol, bool verbose, size_t machoOffset) {
//----This will store symbol address----//
uint64_t addr = 0;
//----This variable will hold the binary location as we move on through reading it----//
size_t offset = machoOffset;
size_t sym_offset = 0;
int ncmds = 0;
struct load_command *cmd = NULL;
uint32_t *magic = load_bytes(file, offset, sizeof(uint32_t)); //at offset 0 we have the magic number
if (verbose) printf("[i] MAGIC = 0x%x\n", *magic);
//----64bit magic number----//
if (*magic == 0xFEEDFACF) {
if (verbose) printf("[i] 64bit binary\n");
struct mach_header_64 *mh64 = load_bytes(file, offset, sizeof(struct mach_header_64));
ncmds = mh64->ncmds;
free(mh64);
offset += sizeof(struct mach_header_64);
if (verbose) printf("[i] %d LOAD COMMANDS\n", ncmds);
for (int i = 0; i < ncmds; i++) {
cmd = load_bytes(file, offset, sizeof(struct load_command));
if (verbose) printf("[i] LOAD COMMAND %d = 0x%x\n", i, cmd->cmd);
if (cmd->cmd == LC_SYMTAB) {
if (verbose) printf("[+] Found LC_SYMTAB command!\n");
struct symtab_command *symtab = load_bytes(file, offset, cmd->cmdsize);
if (verbose) printf("\t[i] %d symbols\n", symtab->nsyms);
if (verbose) printf("\t[i] Symbol table at 0x%lx\n", symtab->symoff + machoOffset);
for (int i = 0; i < symtab->nsyms; i++) {
struct symbol *sym = load_bytes(file, symtab->symoff + sym_offset + machoOffset, sizeof(struct symbol));
int symlen = 0;
uint64_t sym_str_addr = sym->table_index + symtab->stroff + machoOffset;
uint8_t *byte = load_bytes(file, sym_str_addr + symlen, 1);
//strings end with 0 so that's how we know it's over
while (*byte != 0) {
free(byte);
symlen++;
byte = load_bytes(file, sym_str_addr+symlen, 1);
}
free(byte);
char *sym_name = load_bytes(file, sym_str_addr, symlen + 1);
if (verbose) printf("\t%s: 0x%llx\n", sym_name, sym->address);
if (!strcmp(sym_name, symbol)) {
addr = sym->address;
if (!verbose) return addr;
}
free(sym_name);
sym_offset += sizeof(struct symbol);
free(sym);
}
free(symtab);
free(cmd);
break;
}
offset += cmd->cmdsize;
free(cmd);
}
}
//----32bit magic number----//
else if (*magic == 0xFEEDFACE) {
if (verbose) printf("[i] 32bit binary\n");
struct mach_header *mh = load_bytes(file, offset, sizeof(struct mach_header));
ncmds = mh->ncmds;
free(mh);
offset += sizeof(struct mach_header);
if (verbose) printf("[i] %d LOAD COMMANDS\n", ncmds);
for (int i = 0; i < ncmds; i++) {
cmd = load_bytes(file, offset, sizeof(struct load_command));
if (verbose) printf("[i] LOAD COMMAND %d = 0x%x\n", i, cmd->cmd);
if (cmd->cmd == LC_SYMTAB) {
if (verbose) printf("[+] Found LC_SYMTAB command!\n");
struct symtab_command *symtab = load_bytes(file, offset, cmd->cmdsize);
if (verbose) printf("\t[i] %d symbols\n", symtab->nsyms);
if (verbose) printf("\t[i] Symbol table at 0x%lx\n", symtab->symoff + machoOffset);
for (int i = 0; i < symtab->nsyms; i++) {
struct symbol *sym = load_bytes(file, symtab->symoff + sym_offset + machoOffset, sizeof(struct symbol));
int symlen = 0;
uint64_t sym_str_addr = sym->table_index + symtab->stroff + machoOffset;
uint8_t *byte = load_bytes(file, sym_str_addr+symlen, 1);
while (*byte != 0) {
free(byte);
symlen++;
byte = load_bytes(file, sym_str_addr+symlen, 1);
}
free(byte);
char *sym_name = load_bytes(file, sym_str_addr, symlen + 1);
if (verbose) printf("\t%s: 0x%llx\n", sym_name, sym->address);
if (!strcmp(sym_name, symbol)) {
addr = sym->address;
if (!verbose) return addr;
}
free(sym_name);
sym_offset += sizeof(struct symbol);
free(sym);
}
free(symtab);
free(cmd);
break;
}
offset += cmd->cmdsize;
free(cmd);
}
}
else {
if (verbose) printf("[!] Unrecognized file\n");
return -1;
}
return addr;
}
uint64_t find_symbol_address(const char *symbol, bool verbose, size_t machoOffset) {
//----This will store symbol address----//
uint64_t addr = 0;
//----This variable will hold the binary location as we move on through reading it----//
size_t offset = machoOffset;
size_t sym_offset = 0;
int ncmds = 0;
struct load_command *cmd = NULL;
uint32_t *magic = load_bytes(file, offset, sizeof(uint32_t)); //at offset 0 we have the magic number
if (verbose) printf("[i] MAGIC = 0x%x\n", *magic);
//----64bit magic number----//
if (*magic == 0xFEEDFACF) {
if (verbose) printf("[i] 64bit binary\n");
struct mach_header_64 *mh64 = load_bytes(file, offset, sizeof(struct mach_header_64));
ncmds = mh64->ncmds;
free(mh64);
offset += sizeof(struct mach_header_64);
if (verbose) printf("[i] %d LOAD COMMANDS\n", ncmds);
for (int i = 0; i < ncmds; i++) {
cmd = load_bytes(file, offset, sizeof(struct load_command));
if (verbose) printf("[i] LOAD COMMAND %d = 0x%x\n", i, cmd->cmd);
if (cmd->cmd == LC_SYMTAB) {
if (verbose) printf("[+] Found LC_SYMTAB command!\n");
struct symtab_command *symtab = load_bytes(file, offset, cmd->cmdsize);
if (verbose) printf("\t[i] %d symbols\n", symtab->nsyms);
if (verbose) printf("\t[i] Symbol table at 0x%lx\n", symtab->symoff + machoOffset);
for (int i = 0; i < symtab->nsyms; i++) {
struct symbol *sym = load_bytes(file, symtab->symoff + sym_offset + machoOffset, sizeof(struct symbol));
int symlen = 0;
uint64_t sym_str_addr = sym->table_index + symtab->stroff + machoOffset;
uint8_t *byte = load_bytes(file, sym_str_addr+symlen, 1);
//strings end with 0 so that's how we know it's over
while (*byte != 0) {
free(byte);
symlen++;
byte = load_bytes(file, sym_str_addr+symlen, 1);
}
free(byte);
char *sym_name = load_bytes(file, sym_str_addr, symlen + 1);
if (verbose) printf("\t%s: 0x%llx\n", sym_name, sym->address);
if (!strcmp(sym_name, symbol)) {
addr = sym->address;
if (!verbose) return symtab->symoff + sym_offset;
}
free(sym_name);
sym_offset += sizeof(struct symbol);
free(sym);
}
free(symtab);
free(cmd);
break;
}
offset += cmd->cmdsize;
free(cmd);
}
}
//----32bit magic number----//
else if (*magic == 0xFEEDFACE) {
if (verbose) printf("[i] 32bit binary\n");
struct mach_header *mh = load_bytes(file, offset, sizeof(struct mach_header));
ncmds = mh->ncmds;
free(mh);
offset += sizeof(struct mach_header);
if (verbose) printf("[i] %d LOAD COMMANDS\n", ncmds);
for (int i = 0; i < ncmds; i++) {
cmd = load_bytes(file, offset, sizeof(struct load_command));
if (verbose) printf("[i] LOAD COMMAND %d = 0x%x\n", i, cmd->cmd);
if (cmd->cmd == LC_SYMTAB) {
if (verbose) printf("[+] Found LC_SYMTAB command!\n");
struct symtab_command *symtab = load_bytes(file, offset, cmd->cmdsize);
if (verbose) printf("\t[i] %d symbols\n", symtab->nsyms);
if (verbose) printf("\t[i] Symbol table at 0x%lx\n", symtab->symoff + machoOffset);
for (int i = 0; i < symtab->nsyms; i++) {
struct symbol *sym = load_bytes(file, symtab->symoff + sym_offset + machoOffset, sizeof(struct symbol));
int symlen = 0;
uint64_t sym_str_addr = sym->table_index + symtab->stroff + machoOffset;
uint8_t *byte = load_bytes(file, sym_str_addr + symlen, 1);
while (*byte != 0) {
free(byte);
symlen++;
byte = load_bytes(file, sym_str_addr + symlen, 1);
}
free(byte);
char *sym_name = load_bytes(file, sym_str_addr, symlen + 1);
if (verbose) printf("\t%s: 0x%llx\n", sym_name, sym->address);
if (!strcmp(sym_name, symbol)) {
addr = sym->address;
if (!verbose) return symtab->symoff + sym_offset;
}
free(sym_name);
sym_offset += sizeof(struct symbol);
free(sym);
}
free(symtab);
free(cmd);
break;
}
offset += cmd->cmdsize;
free(cmd);
}
}
else {
if (verbose) printf("[!] Unrecognized file\n");
return -1;
}
return addr;
}
int initWithMacho(const char *macho) {
file = fopen(macho, "r+b");
if (!file) {
printf("[-] Can't open '%s', error %d ('%s')\n", macho, errno, strerror(errno));
return -1;
}
return 0;
}
//
// machoSymbolFinder.h
// machoSymbolFinder
//
// Created by Jake James on 8/21/18.
// Copyright © 2018 Jake James. All rights reserved.
//
#import <unistd.h>
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#import <stdbool.h>
#import <errno.h>
#import <mach-o/loader.h>
#import <mach-o/swap.h>
// dunno if the built-in headers have something like this but I couldn't find any so DIY :)
struct symbol {
uint32_t table_index;
uint8_t type;
uint8_t section_index;
uint16_t description;
uint64_t address;
};
extern FILE *file;
void *load_bytes(FILE *obj_file, off_t offset, uint32_t size);
void write_bytes(FILE *obj_file, off_t offset, size_t size, void *bytes);
uint64_t find_load_cmd(uint32_t lc, size_t offset);
uint64_t find_symbol(const char *symbol, bool verbose, size_t offset);
uint64_t find_symbol_address(const char *symbol, bool verbose, size_t offset);
int initWithMacho(const char *macho);
#import "macho_offset_finder.h"
#import "machoSymbolFinder.h"
int main(int argc, char **argv, char **envp) {
if (argc != 2) {
printf("[-] Usage: \n\t%s /path/to/thin_macho\n", argv[0]);
return -1;
}
int rv = InitPatchfinder(0, argv[1]);
if (rv) {
printf("[-] Failed to init patchfinder\n");
return -1;
}
rv = initWithMacho(argv[1]);
if (rv) {
printf("[-] Failed to init symbolfinder\n");
return -1;
}
// took me so long to figure out the logic of this
uint64_t addr = find_symbol_address("_kCFCoreFoundationVersionNumber", false, machoOffset) + machoOffset;
printf("[*] kCFCoreFoundationVersionNumber symbol at 0x%llx\n", addr);
if (!addr || addr == machoOffset) {
printf("[-] Failed to find _kCFCoreFoundationVersionNumber address\n");
return -1;
}
uint64_t symtab_addr = find_load_cmd(LC_SYMTAB, machoOffset);
struct symtab_command *cmd = load_bytes(file, symtab_addr, sizeof(struct symtab_command));
symtab_addr = cmd->symoff + machoOffset;
free(cmd);
uint64_t dysymtab_addr = find_load_cmd(LC_DYSYMTAB, machoOffset);
if (!symtab_addr || symtab_addr == machoOffset) {
printf("[-] symtab_addr = 0?\n");
return -1;
}
if (!dysymtab_addr || dysymtab_addr == machoOffset) {
printf("[-] dysymtab_addr = 0?\n");
return -1;
}
uint64_t value = (addr - symtab_addr) / 0x10;
printf("[*] Index in symtab: %lld\n", value);
struct dysymtab_command *dysymtab = load_bytes(file, dysymtab_addr, sizeof(struct dysymtab_command));
dysymtab_addr = dysymtab->indirectsymoff + machoOffset;
uint32_t nsyms = dysymtab->nindirectsyms;
free(dysymtab);
uint32_t index;
for (index = 0; index < nsyms; index++) {
uint32_t *val = load_bytes(file, dysymtab_addr + index * sizeof(uint32_t), sizeof(uint32_t));
if (*val == value) {
free(val);
break;
}
free(val);
}
printf("[*] Index in dysymtab: %d\n", index);
struct section_64 *got = load_bytes(file, GOT_offset + machoOffset, sizeof(struct section_64));
uint32_t first_index = got->reserved1;
uint64_t actual_addr = got->offset + (index - first_index) * 8;
printf("[*] Actual kCFCoreFoundationVersionNumber address: 0x%llx\n", actual_addr);
free(got);
uint64_t ref;
for (int i = 1; (ref = Find_reference(actual_addr, i, 0)); i++) {
for (int i = 0; i < 24; i += 4) {
uint32_t op = *(uint32_t*)(Macho + ref + i);
if ((op & 0xff00000f) == 0x5400000c) {
printf("[*] Found:\n\top = 0x%x (B.GT) at offset %lld\n", op, ref + i);
op -= 1;
printf("[+] Patched instruction, now:\n\t0x%x (B.LT)\n", op);
write_bytes(file, machoOffset + ref + i, 4, &op);
}
else if ((op & 0xff00000f) == 0x5400000b) {
printf("[*] Found:\n\top = 0x%x (B.LT) at offset %lld\n", op, ref + i);
op += 1;
printf("[+] Patched instruction, now:\n\t0x%x (B.GT)\n", op);
write_bytes(file, machoOffset + ref + i, 4, &op);
}
else if ((op & 0xff00000f) == 0x5400000d) {
printf("[*] Found:\n\top = 0x%x (B.LE) at offset %lld\n", op, ref + i);
op -= 3;
printf("[+] Patched instruction, now:\n\t0x%x (B.GE)\n", op);
write_bytes(file, machoOffset + ref + i, 4, &op);
}
else if ((op & 0xff00000f) == 0x5400000a) {
printf("Found:\n\top = 0x%x (B.GE) at offset %lld\n", op, ref + i);
op += 3;
printf("Patched instruction, now:\n\t0x%x\n (B.LE)\n", op);
write_bytes(file, machoOffset + ref + i, 4, &op);
}
}
}
return 0;
}
include $(THEOS)/makefiles/common.mk
export ARCHS = arm64
TOOL_NAME = patch_cfversion_checks
patch_cfversion_checks_FILES = $(wildcard *.c) $(wildcard *.m)
CFLAGS += -Wno-macro-redefined
include $(THEOS_MAKE_PATH)/tool.mk
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment