From cf517d0809b21acd87c3df7acb7552d6226b0e2c Mon Sep 17 00:00:00 2001 From: Sam Bingner Date: Thu, 20 Dec 2018 16:05:22 -1000 Subject: Update to work properly with dual-hash binaries and fix Copyright info files --- .gitmodules | 3 - CSCommon.h | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Makefile | 7 +- async_wake_ios | 1 - control | 2 +- inject.c | 245 -------------------------------------------------------- inject.m | 161 +++++++++++++++++++++++++++++++++++++ kern_funcs.c | 187 ++++++++++++++++++++++++++++++++++++++++++ kern_funcs.h | 13 +++ patchfinder64.c | 22 +++-- 10 files changed, 609 insertions(+), 260 deletions(-) delete mode 100644 .gitmodules create mode 100644 CSCommon.h delete mode 160000 async_wake_ios delete mode 100644 inject.c create mode 100644 inject.m create mode 100644 kern_funcs.c create mode 100644 kern_funcs.h diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 9ee3a12..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "async_wake_ios"] - path = async_wake_ios - url = git@github.com:xerub/async_wake_ios.git diff --git a/CSCommon.h b/CSCommon.h new file mode 100644 index 0000000..5b24b60 --- /dev/null +++ b/CSCommon.h @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/*! + @header CSCommon + CSCommon is the common header of all Code Signing API headers. + It defines types, constants, and error codes. +*/ +#ifndef _H_CSCOMMON +#define _H_CSCOMMON + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + + +/* + Code Signing specific OSStatus codes. + [Assigned range 0xFFFE_FAxx]. +*/ +enum { + errSecCSUnimplemented = -67072, /* unimplemented code signing feature */ + errSecCSInvalidObjectRef, /* invalid API object reference */ + errSecCSInvalidFlags, /* invalid or inapprpopriate API flag(s) specified */ + errSecCSObjectRequired, /* a required pointer argument was NULL */ + errSecCSStaticCodeNotFound, /* cannot find code object on disk */ + errSecCSUnsupportedGuestAttributes, /* cannot locate guests using this attribute set */ + errSecCSInvalidAttributeValues, /* given attribute values are invalid */ + errSecCSNoSuchCode, /* host has no guest with the requested attributes */ + errSecCSMultipleGuests, /* host has multiple guests with this attribute value */ + errSecCSGuestInvalid, /* code identity has been invalidated */ + errSecCSUnsigned, /* code object is not signed */ + errSecCSSignatureFailed, /* code or signature modified */ + errSecCSSignatureNotVerifiable, /* signature cannot be read, e.g., due to a filesystem that maps root to an unprivileged user */ + errSecCSSignatureUnsupported, /* unsupported type or version of signature */ + errSecCSBadDictionaryFormat, /* a required plist file or resource is malformed */ + errSecCSResourcesNotSealed, /* resources are not sealed by signature */ + errSecCSResourcesNotFound, /* cannot find sealed resources in code */ + errSecCSResourcesInvalid, /* the sealed resource directory is invalid */ + errSecCSBadResource, /* a sealed resource is missing or invalid */ + errSecCSResourceRulesInvalid, /* invalid resource selection rule(s) */ + errSecCSReqInvalid, /* invalid or corrupted code requirement(s) */ + errSecCSReqUnsupported, /* unsupported type or version of code requirement(s) */ + errSecCSReqFailed, /* failed to satisfy code requirement(s) */ + errSecCSBadObjectFormat, /* object file format invalid or unsuitable */ + errSecCSInternalError, /* internal error in Code Signing subsystem */ + errSecCSHostReject, /* code rejected its host */ + errSecCSNotAHost, /* this code is not a host */ + errSecCSSignatureInvalid, /* invalid format for signature */ + errSecCSHostProtocolRelativePath, /* host protocol violation - absolute guest path required */ + errSecCSHostProtocolContradiction, /* host protocol violation - contradictory hosting modes */ + errSecCSHostProtocolDedicationError, /* host protocol violation - operation not allowed with/for a dedicated guest */ + errSecCSHostProtocolNotProxy, /* host protocol violation - proxy hosting not engaged */ + errSecCSHostProtocolStateError, /* host protocol violation - invalid guest state change request */ + errSecCSHostProtocolUnrelated, /* host protocol violation - the given guest is not a guest of the given host */ + errSecCSInvalidOperation, /* requested operation is not valid */ + errSecCSNotSupported, /* operation not supported for this type of code */ +}; + + +/* + * Code Signing specific CFError "user info" keys. + * In calls that can return CFErrorRef indications, if a CFErrorRef is actually + * returned, its "user info" dictionary will contain some (but not all) of the + * following keys to more closely describe the circumstances of the failure. + */ +extern const CFStringRef kSecCFErrorPattern; /* CFStringRef: invalid resource selection pattern encountered */ +extern const CFStringRef kSecCFErrorResourceSeal; /* CFTypeRef: invalid component in resource seal (CodeResources) */ +extern const CFStringRef kSecCFErrorResourceAdded; /* CFURLRef: unsealed resource found */ +extern const CFStringRef kSecCFErrorResourceAltered; /* CFURLRef: modified resource found */ +extern const CFStringRef kSecCFErrorResourceMissing; /* CFURLRef: sealed (non-optional) resource missing */ +extern const CFStringRef kSecCFErrorInfoPlist; /* CFTypeRef: Info.plist dictionary or component found invalid */ +extern const CFStringRef kSecCFErrorGuestAttributes; /* CFTypeRef: Guest attribute set of element not accepted */ +extern const CFStringRef kSecCFErrorRequirementSyntax; /* CFStringRef: compilation error for Requirement source */ + + +/*! + @typedef SecCodeRef + This is the type of a reference to running code. + + In many (but not all) calls, this can be passed to a SecStaticCodeRef + argument, which performs an implicit SecCodeCopyStaticCode call and + operates on the result. +*/ +typedef struct __SecCode *SecCodeRef; /* running code */ + +/*! + @typedef SecStaticCodeRef + This is the type of a reference to static code on disk. +*/ +typedef struct __SecCode const *SecStaticCodeRef; /* code on disk */ + +/*! + @typedef SecRequirementRef + This is the type of a reference to a code requirement. +*/ +typedef struct __SecRequirement *SecRequirementRef; /* code requirement */ + + +/*! + @typedef SecGuestRef + An abstract handle to identify a particular Guest in the context of its Host. + + Guest handles are assigned by the host at will, with kSecNoGuest (zero) being + reserved as the null value). They can be reused for new children if desired. +*/ +typedef u_int32_t SecGuestRef; + +enum { + kSecNoGuest = 0, /* not a valid SecGuestRef */ +}; + + +/*! + @typddef SecCSFlags + This is the type of flags arguments to Code Signing API calls. + It provides a bit mask of request and option flags. All of the bits in these + masks are reserved to Apple; if you set any bits not defined in these headers, + the behavior is generally undefined. + + This list describes the flags that are shared among several Code Signing API calls. + Flags that only apply to one call are defined and documented with that call. + Global flags are assigned from high order down (31 -> 0); call-specific flags + are assigned from the bottom up (0 -> 31). + + @constant kSecCSDefaultFlags + When passed to a flags argument throughout, indicates that default behavior + is desired. Do not mix with other flags values. + @constant kSecCSConsiderExpiration + When passed to a call that performs code validation, requests that code signatures + made by expired certificates be rejected. By default, expiration of participating + certificates is not automatic grounds for rejection. +*/ +typedef uint32_t SecCSFlags; + +enum { + kSecCSDefaultFlags = 0, /* no particular flags (default behavior) */ + + kSecCSConsiderExpiration = 1 << 31, /* consider expired certificates invalid */ +}; + + +/*! + @typedef SecCodeSignatureFlags + This is the type of option flags that can be embedded in a code signature + during signing, and that govern the use of the signature thereafter. + Some of these flags can be set through the codesign(1) command's --options + argument; some are set implicitly based on signing circumstances; and all + can be set with the kSecCodeSignerFlags item of a signing information dictionary. + + @constant kSecCodeSignatureHost + Indicates that the code may act as a host that controls and supervises guest + code. If this flag is not set in a code signature, the code is never considered + eligible to be a host, and any attempt to act like one will be ignored. + @constant kSecCodeSignatureAdhoc + The code has been sealed without a signing identity. No identity may be retrieved + from it, and any code requirement placing restrictions on the signing identity + will fail. This flag is set by the code signing API and cannot be set explicitly. + @constant kSecCodeSignatureForceHard + Implicitly set the "hard" status bit for the code when it starts running. + This bit indicates that the code prefers to be denied access to a resource + if gaining such access would cause its invalidation. Since the hard bit is + sticky, setting this option bit guarantees that the code will always have + it set. + @constant kSecCodeSignatureForceKill + Implicitly set the "kill" status bit for the code when it starts running. + This bit indicates that the code wishes to be terminated with prejudice if + it is ever invalidated. Since the kill bit is sticky, setting this option bit + guarantees that the code will always be valid, since it will die immediately + if it becomes invalid. + @constant kSecCodeSignatureForceExpiration + Forces the kSecCSConsiderExpiration on all validations of the code. + */ +typedef uint32_t SecCodeSignatureFlags; + +enum { + kSecCodeSignatureHost = 0x0001, /* may host guest code */ + kSecCodeSignatureAdhoc = 0x0002, /* must be used without signer */ + kSecCodeSignatureForceHard = 0x0100, /* always set HARD mode on launch */ + kSecCodeSignatureForceKill = 0x0200, /* always set KILL mode on launch */ + kSecCodeSignatureForceExpiration = 0x0400, /* force certificat expiration checks */ +}; + + +/*! + @typedef SecRequirementType + An enumeration indicating different types of internal requirements for code. +*/ +typedef uint32_t SecRequirementType; + +enum { + kSecHostRequirementType = 1, /* what hosts may run us */ + kSecGuestRequirementType = 2, /* what guests we may run */ + kSecDesignatedRequirementType = 3, /* designated requirement */ + kSecLibraryRequirementType = 4, /* what libraries we may link against */ + kSecInvalidRequirementType, /* invalid type of Requirement (must be last) */ + kSecRequirementTypeCount = kSecInvalidRequirementType /* number of valid requirement types */ +}; + + +#ifdef __cplusplus +} +#endif + +#endif //_H_CSCOMMON diff --git a/Makefile b/Makefile index 17cb1cd..cf1826f 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,11 @@ ARCHS ?= arm64 +target ?= iphone:11.0:11.0 include $(THEOS)/makefiles/common.mk TOOL_NAME = inject -inject_CODESIGN_FLAGS = -Hsha256 -Sentitlements.xml -inject_FRAMEWORKS = IOKit +inject_CODESIGN_FLAGS = -Hsha256 -Hsha1 -Sentitlements.xml +inject_FRAMEWORKS = IOKit Security inject_CFLAGS = -Wno-error=unused-function -Wno-error=unused-variable -Wno-error=missing-braces -Iinclude -inject_FILES = inject.c patchfinder64.c async_wake_ios/async_wake_ios/libjb/trav.c +inject_FILES = inject.m patchfinder64.c kern_funcs.c include $(THEOS_MAKE_PATH)/tool.mk diff --git a/async_wake_ios b/async_wake_ios deleted file mode 160000 index 76592d9..0000000 --- a/async_wake_ios +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 76592d9b1628accdc4d3f832e0eed6788a13f995 diff --git a/control b/control index bc7d266..99d91ee 100644 --- a/control +++ b/control @@ -1,6 +1,6 @@ Package: trustinjector Name: Trust Cache Injector -Version: 0.0.1 +Version: 0.1 Architecture: iphoneos-arm Description: Inject files to kernel trust cache Maintainer: Sam Bingner diff --git a/inject.c b/inject.c deleted file mode 100644 index 82a0c57..0000000 --- a/inject.c +++ /dev/null @@ -1,245 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "async_wake_ios/async_wake_ios/libjb.h" -#include "patchfinder64.h" -#include - -mach_port_t tfp0 = MACH_PORT_NULL; - -void wk32(uint64_t kaddr, uint32_t val) { - if (tfp0 == MACH_PORT_NULL) { - printf("attempt to write to kernel memory before any kernel memory write primitives available\n"); - sleep(3); - return; - } - - kern_return_t err; - err = mach_vm_write(tfp0, - (mach_vm_address_t)kaddr, - (vm_offset_t)&val, - (mach_msg_type_number_t)sizeof(uint32_t)); - - if (err != KERN_SUCCESS) { - printf("tfp0 write failed: %s %x\n", mach_error_string(err), err); - return; - } -} - -void wk64(uint64_t kaddr, uint64_t val) { - uint32_t lower = (uint32_t)(val & 0xffffffff); - uint32_t higher = (uint32_t)(val >> 32); - wk32(kaddr, lower); - wk32(kaddr+4, higher); -} - -uint32_t rk32(uint64_t kaddr) { - kern_return_t err; - uint32_t val = 0; - mach_vm_size_t outsize = 0; - err = mach_vm_read_overwrite(tfp0, - (mach_vm_address_t)kaddr, - (mach_vm_size_t)sizeof(uint32_t), - (mach_vm_address_t)&val, - &outsize); - if (err != KERN_SUCCESS){ - printf("tfp0 read failed %s addr: 0x%llx err:%x port:%x\n", mach_error_string(err), kaddr, err, tfp0); - sleep(3); - return 0; - } - - if (outsize != sizeof(uint32_t)){ - printf("tfp0 read was short (expected %lx, got %llx\n", sizeof(uint32_t), outsize); - sleep(3); - return 0; - } - return val; -} - -uint64_t rk64(uint64_t kaddr) { - uint64_t lower = rk32(kaddr); - uint64_t higher = rk32(kaddr+4); - uint64_t full = ((higher<<32) | lower); - return full; -} - -uint64_t kmem_alloc(uint64_t size) { - if (tfp0 == MACH_PORT_NULL) { - printf("attempt to allocate kernel memory before any kernel memory write primitives available\n"); - sleep(3); - return 0; - } - - kern_return_t err; - mach_vm_address_t addr = 0; - mach_vm_size_t ksize = round_page_kernel(size); - err = mach_vm_allocate(tfp0, &addr, ksize, VM_FLAGS_ANYWHERE); - if (err != KERN_SUCCESS) { - printf("unable to allocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err); - sleep(3); - return 0; - } - return addr; -} - -// https://github.com/JonathanSeals/kernelversionhacker/blob/3dcbf59f316047a34737f393ff946175164bf03f/kernelversionhacker.c#L92 - -#define IMAGE_OFFSET 0x2000 -#define MACHO_HEADER_MAGIC 0xfeedfacf -#define MAX_KASLR_SLIDE 0x21000000 -#define KERNEL_SEARCH_ADDRESS 0xfffffff007004000 - -#define ptrSize sizeof(uintptr_t) - -static vm_address_t get_kernel_base(mach_port_t tfp0) -{ - uint64_t addr = 0; - addr = KERNEL_SEARCH_ADDRESS+MAX_KASLR_SLIDE; - - while (1) { - char *buf; - mach_msg_type_number_t sz = 0; - kern_return_t ret = vm_read(tfp0, addr, 0x200, (vm_offset_t*)&buf, &sz); - - if (ret) { - goto next; - } - - if (*((uint32_t *)buf) == MACHO_HEADER_MAGIC) { - int ret = vm_read(tfp0, addr, 0x1000, (vm_offset_t*)&buf, &sz); - if (ret != KERN_SUCCESS) { - printf("Failed vm_read %i\n", ret); - goto next; - } - - for (uintptr_t i=addr; i < (addr+0x2000); i+=(ptrSize)) { - mach_msg_type_number_t sz; - int ret = vm_read(tfp0, i, 0x120, (vm_offset_t*)&buf, &sz); - - if (ret != KERN_SUCCESS) { - printf("Failed vm_read %i\n", ret); - exit(-1); - } - if (!strcmp(buf, "__text") && !strcmp(buf+0x10, "__PRELINK_TEXT")) { - return addr; - } - } - } - - next: - addr -= 0x200000; - } -} - -size_t -kread(uint64_t where, void *p, size_t size) -{ - int rv; - size_t offset = 0; - while (offset < size) { - mach_vm_size_t sz, chunk = 2048; - if (chunk > size - offset) { - chunk = size - offset; - } - rv = mach_vm_read_overwrite(tfp0, where + offset, chunk, (mach_vm_address_t)p + offset, &sz); - if (rv || sz == 0) { - fprintf(stderr, "[e] error reading kernel @%p\n", (void *)(offset + where)); - break; - } - offset += sz; - } - return offset; -} - -size_t -kwrite(uint64_t where, const void *p, size_t size) -{ - int rv; - size_t offset = 0; - while (offset < size) { - size_t chunk = 2048; - if (chunk > size - offset) { - chunk = size - offset; - } - rv = mach_vm_write(tfp0, where + offset, (mach_vm_offset_t)p + offset, (mach_msg_type_number_t)chunk); - if (rv) { - fprintf(stderr, "[e] error writing kernel @%p\n", (void *)(offset + where)); - break; - } - offset += chunk; - } - return offset; -} - -mach_port_t try_restore_port() { - mach_port_t port = MACH_PORT_NULL; - kern_return_t err; - err = host_get_special_port(mach_host_self(), 0, 4, &port); - if (err == KERN_SUCCESS && port != MACH_PORT_NULL) { - printf("got persisted port!\n"); - // make sure rk64 etc use this port - return port; - } - printf("unable to retrieve persisted port\n"); - return MACH_PORT_NULL; -} - -int injectTrustCache(int argc, char* argv[], uint64_t trust_chain, uint64_t amficache) { - printf("Injecting to trust cache...\n"); - struct trust_mem mem; - size_t length = 0; - uint64_t kernel_trust = 0; - - mem.next = rk64(trust_chain); - *(uint64_t *)&mem.uuid[0] = 0xabadbabeabadbabe; - *(uint64_t *)&mem.uuid[8] = 0xabadbabeabadbabe; - - for (int i = 1; i < argc; i++) { - int rv = grab_hashes(argv[i], kread, amficache, mem.next); - if (rv) { - printf("Failed to inject to trust cache.\n"); - return -1; - } - } - - length = (sizeof(mem) + numhash * 20 + 0xFFFF) & ~0xFFFF; - kernel_trust = kmem_alloc(length); - printf("alloced: 0x%zx => 0x%llx\n", length, kernel_trust); - - mem.count = numhash; - kwrite(kernel_trust, &mem, sizeof(mem)); - kwrite(kernel_trust + sizeof(mem), allhash, numhash * 20); - wk64(trust_chain, kernel_trust); - printf("Successfully injected to trust cache.\n"); - return 0; -} - -int main(int argc, char* argv[]) { - if (argc < 2) { - fprintf(stderr,"Usage: inject /full/path/to/executable\n"); - fprintf(stderr,"Inject executables to trust cache\n"); - return -1; - } - tfp0 = try_restore_port(); - uint64_t kernel_base = get_kernel_base(tfp0); - init_kernel(kernel_base, NULL); - uint64_t trust_chain = find_trustcache(); - uint64_t amficache = find_amficache(); - term_kernel(); - return injectTrustCache(argc, argv, trust_chain, amficache); -} diff --git a/inject.m b/inject.m new file mode 100644 index 0000000..2ca834a --- /dev/null +++ b/inject.m @@ -0,0 +1,161 @@ +/* + * inject.m + * + * Created by Sam Bingner on 9/27/2018 + * Copyright 2018 Sam Bingner. All Rights Reserved. + * + */ + +#include +#include +#include +#include "patchfinder64.h" +#include "CSCommon.h" +#include "kern_funcs.h" + +OSStatus SecStaticCodeCreateWithPathAndAttributes(CFURLRef path, SecCSFlags flags, CFDictionaryRef attributes, SecStaticCodeRef _Nullable *staticCode); +OSStatus SecCodeCopySigningInformation(SecStaticCodeRef code, SecCSFlags flags, CFDictionaryRef _Nullable *information); +CFStringRef (*_SecCopyErrorMessageString)(OSStatus status, void * __nullable reserved) = NULL; + +mach_port_t tfp0 = MACH_PORT_NULL; + +enum { + cdHashTypeSHA1 = 1, + cdHashTypeSHA256 = 2 +}; + +#define TRUST_CDHASH_LEN (20) + +struct trust_mem { + uint64_t next; //struct trust_mem *next; + unsigned char uuid[16]; + unsigned int count; + //unsigned char data[]; +} __attribute__((packed)); + +struct hash_entry_t { + uint16_t num; + uint16_t start; +} __attribute__((packed)); + +typedef uint8_t hash_t[TRUST_CDHASH_LEN]; + +mach_port_t try_restore_port() { + mach_port_t port = MACH_PORT_NULL; + kern_return_t err; + + err = host_get_special_port(mach_host_self(), 0, 4, &port); + if (err == KERN_SUCCESS && port != MACH_PORT_NULL) { + fprintf(stderr, "got persisted port!\n"); + // make sure rk64 etc use this port + return port; + } + fprintf(stderr, "unable to retrieve persisted port\n"); + return MACH_PORT_NULL; +} + +int injectTrustCache(int argc, char* argv[], uint64_t trust_chain, uint64_t amficache) { + struct trust_mem mem; + uint64_t kernel_trust = 0; + + mem.next = rk64(trust_chain); + *(uint64_t *)&mem.uuid[0] = 0xabadbabeabadbabe; + *(uint64_t *)&mem.uuid[8] = 0xabadbabeabadbabe; + NSMutableArray *hashes = [[NSMutableArray alloc] init]; + SecStaticCodeRef staticCode; + NSDictionary *info; + + for (int i = 1; i < argc; i++) { + OSStatus result = SecStaticCodeCreateWithPathAndAttributes(CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)@(argv[i]), kCFURLPOSIXPathStyle, false), kSecCSDefaultFlags, NULL, &staticCode); + if (result != errSecSuccess) { + if (_SecCopyErrorMessageString != NULL) { + CFStringRef error = _SecCopyErrorMessageString(result, NULL); + fprintf(stderr, "Unable to generate cdhash for %s: %s\n", argv[i], [(id)error UTF8String]); + CFRelease(error); + } else { + fprintf(stderr, "Unable to generate cdhash for %s: %d\n", argv[i], result); + } + continue; + } + + result = SecCodeCopySigningInformation(staticCode, kSecCSDefaultFlags, (CFDictionaryRef*)&info); + CFRelease(staticCode); + if (result != errSecSuccess) { + fprintf(stderr, "Unable to copy cdhash info for %s\n", argv[i]); + continue; + } + NSArray *cdhashes = info[@"cdhashes"]; + NSArray *algos = info[@"digest-algorithms"]; + NSUInteger algoIndex = [algos indexOfObject:@(cdHashTypeSHA256)]; + + if (cdhashes == nil) { + printf("%s: no cdhashes\n", argv[i]); + } else if (algos == nil) { + printf("%s: no algos\n", argv[i]); + } else if (algoIndex == NSNotFound) { + printf("%s: does not have SHA256 hash\n", argv[i]); + } else { + NSData *cdhash = [cdhashes objectAtIndex:algoIndex]; + if (cdhash != nil) { + printf("%s: OK\n", argv[i]); + [hashes addObject:cdhash]; + } else { + printf("%s: missing SHA256 cdhash entry\n", argv[i]); + } + } + [info release]; + } + int numHashes = [hashes count]; + + if (numHashes < 1) { + fprintf(stderr, "Found no hashes to inject\n"); + [hashes release]; + return 0; + } + size_t length = (sizeof(mem) + numHashes * TRUST_CDHASH_LEN + 0xFFFF) & ~0xFFFF; + char *buffer = malloc(numHashes * TRUST_CDHASH_LEN); + if (buffer == NULL) { + fprintf(stderr, "Unable to allocate memory for cdhashes: %s\n", strerror(errno)); + [hashes release]; + return -3; + } + char *curbuf = buffer; + for (NSData *hash in hashes) { + memcpy(curbuf, [hash bytes], TRUST_CDHASH_LEN); + curbuf += TRUST_CDHASH_LEN; + } + kernel_trust = kmem_alloc(length); + + mem.count = numHashes; + kwrite(kernel_trust, &mem, sizeof(mem)); + kwrite(kernel_trust + sizeof(mem), buffer, mem.count * TRUST_CDHASH_LEN); + wk64(trust_chain, kernel_trust); + + [hashes release]; + return numHashes; +} + +int main(int argc, char* argv[]) { + if (argc < 2) { + fprintf(stderr,"Usage: inject /full/path/to/executable\n"); + fprintf(stderr,"Inject executables to trust cache\n"); + return -1; + } + void *lib = dlopen("/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY); + if (lib != NULL) { + _SecCopyErrorMessageString = dlsym(lib, "SecCopyErrorMessageString"); + dlclose(lib); + } + tfp0 = try_restore_port(); + if (tfp0 == MACH_PORT_NULL) + return -2; + uint64_t kernel_base = get_kernel_base(tfp0); + init_kernel(kernel_base, NULL); + uint64_t trust_chain = find_trustcache(); + uint64_t amficache = find_amficache(); + term_kernel(); + printf("Injecting to trust cache...\n"); + int ninjected = injectTrustCache(argc, argv, trust_chain, amficache); + printf("Successfully injected [%d/%d] to trust cache.\n", ninjected, argc - 1); + return argc - ninjected - 1; +} diff --git a/kern_funcs.c b/kern_funcs.c new file mode 100644 index 0000000..967cb13 --- /dev/null +++ b/kern_funcs.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "patchfinder64.h" +#include +#include "CSCommon.h" + +extern mach_port_t tfp0; + +void wk32(uint64_t kaddr, uint32_t val) { + if (tfp0 == MACH_PORT_NULL) { + printf("attempt to write to kernel memory before any kernel memory write primitives available\n"); + sleep(3); + return; + } + + kern_return_t err; + err = mach_vm_write(tfp0, + (mach_vm_address_t)kaddr, + (vm_offset_t)&val, + (mach_msg_type_number_t)sizeof(uint32_t)); + + if (err != KERN_SUCCESS) { + printf("tfp0 write failed: %s %x\n", mach_error_string(err), err); + return; + } +} + +void wk64(uint64_t kaddr, uint64_t val) { + uint32_t lower = (uint32_t)(val & 0xffffffff); + uint32_t higher = (uint32_t)(val >> 32); + wk32(kaddr, lower); + wk32(kaddr+4, higher); +} + +uint32_t rk32(uint64_t kaddr) { + kern_return_t err; + uint32_t val = 0; + mach_vm_size_t outsize = 0; + err = mach_vm_read_overwrite(tfp0, + (mach_vm_address_t)kaddr, + (mach_vm_size_t)sizeof(uint32_t), + (mach_vm_address_t)&val, + &outsize); + if (err != KERN_SUCCESS){ + printf("tfp0 read failed %s addr: 0x%llx err:%x port:%x\n", mach_error_string(err), kaddr, err, tfp0); + sleep(3); + return 0; + } + + if (outsize != sizeof(uint32_t)){ + printf("tfp0 read was short (expected %lx, got %llx\n", sizeof(uint32_t), outsize); + sleep(3); + return 0; + } + return val; +} + +uint64_t rk64(uint64_t kaddr) { + uint64_t lower = rk32(kaddr); + uint64_t higher = rk32(kaddr+4); + uint64_t full = ((higher<<32) | lower); + return full; +} + +uint64_t kmem_alloc(uint64_t size) { + if (tfp0 == MACH_PORT_NULL) { + printf("attempt to allocate kernel memory before any kernel memory write primitives available\n"); + sleep(3); + return 0; + } + + kern_return_t err; + mach_vm_address_t addr = 0; + mach_vm_size_t ksize = round_page_kernel(size); + err = mach_vm_allocate(tfp0, &addr, ksize, VM_FLAGS_ANYWHERE); + if (err != KERN_SUCCESS) { + printf("unable to allocate kernel memory via tfp0: %s %x\n", mach_error_string(err), err); + sleep(3); + return 0; + } + return addr; +} + +// https://github.com/JonathanSeals/kernelversionhacker/blob/3dcbf59f316047a34737f393ff946175164bf03f/kernelversionhacker.c#L92 + +#define IMAGE_OFFSET 0x2000 +#define MACHO_HEADER_MAGIC 0xfeedfacf +#define MAX_KASLR_SLIDE 0x21000000 +#define KERNEL_SEARCH_ADDRESS 0xfffffff007004000 + +#define ptrSize sizeof(uintptr_t) + +vm_address_t get_kernel_base(mach_port_t tfp0) +{ + uint64_t addr = 0; + addr = KERNEL_SEARCH_ADDRESS+MAX_KASLR_SLIDE; + + while (1) { + char *buf; + mach_msg_type_number_t sz = 0; + kern_return_t ret = vm_read(tfp0, addr, 0x200, (vm_offset_t*)&buf, &sz); + + if (ret) { + goto next; + } + + if (*((uint32_t *)buf) == MACHO_HEADER_MAGIC) { + int ret = vm_read(tfp0, addr, 0x1000, (vm_offset_t*)&buf, &sz); + if (ret != KERN_SUCCESS) { + printf("Failed vm_read %i\n", ret); + goto next; + } + + for (uintptr_t i=addr; i < (addr+0x2000); i+=(ptrSize)) { + mach_msg_type_number_t sz; + int ret = vm_read(tfp0, i, 0x120, (vm_offset_t*)&buf, &sz); + + if (ret != KERN_SUCCESS) { + printf("Failed vm_read %i\n", ret); + exit(-1); + } + if (!strcmp(buf, "__text") && !strcmp(buf+0x10, "__PRELINK_TEXT")) { + return addr; + } + } + } + + next: + addr -= 0x200000; + } +} + +size_t +kread(uint64_t where, void *p, size_t size) +{ + int rv; + size_t offset = 0; + while (offset < size) { + mach_vm_size_t sz, chunk = 2048; + if (chunk > size - offset) { + chunk = size - offset; + } + rv = mach_vm_read_overwrite(tfp0, where + offset, chunk, (mach_vm_address_t)p + offset, &sz); + if (rv || sz == 0) { + fprintf(stderr, "[e] error reading kernel @%p\n", (void *)(offset + where)); + break; + } + offset += sz; + } + return offset; +} + +size_t +kwrite(uint64_t where, const void *p, size_t size) +{ + int rv; + size_t offset = 0; + while (offset < size) { + size_t chunk = 2048; + if (chunk > size - offset) { + chunk = size - offset; + } + rv = mach_vm_write(tfp0, where + offset, (mach_vm_offset_t)p + offset, (mach_msg_type_number_t)chunk); + if (rv) { + fprintf(stderr, "[e] error writing kernel @%p\n", (void *)(offset + where)); + break; + } + offset += chunk; + } + return offset; +} diff --git a/kern_funcs.h b/kern_funcs.h new file mode 100644 index 0000000..e37ef1d --- /dev/null +++ b/kern_funcs.h @@ -0,0 +1,13 @@ +#ifndef _KERN_FUNCS_H_ +#define _KERN_FUNCS_H_ + +void wk32(uint64_t kaddr, uint32_t val); +void wk64(uint64_t kaddr, uint64_t val); +uint32_t rk32(uint64_t kaddr); +uint64_t rk64(uint64_t kaddr); +uint64_t kmem_alloc(uint64_t size); +vm_address_t get_kernel_base(mach_port_t tfp0); +size_t kread(uint64_t where, void *p, size_t size); +size_t kwrite(uint64_t where, const void *p, size_t size); + +#endif // _KERN_FUNCS_H_ diff --git a/patchfinder64.c b/patchfinder64.c index dfc6264..24e9b27 100644 --- a/patchfinder64.c +++ b/patchfinder64.c @@ -78,6 +78,7 @@ boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen, /* disassembler **************************************************************/ +/* static int HighestSetBit(int N, uint32_t imm) { int i; @@ -195,6 +196,7 @@ static int DecodeMov(uint32_t opcode, uint64_t total, int first, uint64_t *newva return -1; } +*/ /* patchfinder ***************************************************************/ static addr_t @@ -365,6 +367,7 @@ calc64(const uint8_t *buf, addr_t start, addr_t end, int which) return value[which]; } +/* static addr_t calc64mov(const uint8_t *buf, addr_t start, addr_t end, int which) { @@ -394,6 +397,7 @@ find_call64(const uint8_t *buf, addr_t start, size_t length) { return step64(buf, start, length, 0x94000000, 0xFC000000); } +*/ static addr_t follow_call64(const uint8_t *buf, addr_t call) @@ -438,7 +442,9 @@ static addr_t pstring_size = 0; static addr_t kerndumpbase = -1; static addr_t kernel_entry = 0; static void *kernel_mh = 0; +#ifndef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ static addr_t kernel_delta = 0; +#endif int init_kernel(addr_t base, const char *filename) @@ -596,7 +602,10 @@ init_kernel(addr_t base, const char *filename) void term_kernel(void) { - if (kernel != NULL) free(kernel); + if (kernel != NULL) { + free(kernel); + kernel = NULL; + } } /* these operate on VA ******************************************************/ @@ -1452,7 +1461,7 @@ addr_t find_vnode_lookup(void) { } addr_t find_vnode_put(void) { - addr_t call1, call2, call3, call4, call5, call6, call7; + addr_t call1, call2, call3; addr_t func1; addr_t ent_str = find_strref("KBY: getparent(%p) != parent_vp(%p)", 1, 1); @@ -1519,7 +1528,7 @@ addr_t find_vnode_getattr(void) { } addr_t find_SHA1Init(void) { - addr_t call1, call2; + addr_t call1; addr_t func1; addr_t id_str = find_strref("chip-id", 1, 1); @@ -1569,7 +1578,7 @@ addr_t find_SHA1Final(void) { } addr_t find_csblob_entitlements_dictionary_set(void) { - addr_t call1, call2, call3, call4, call5, call6, call7; + addr_t call1, call2, call3, call7; addr_t func1; addr_t ent_str = find_strref("entitlements are not a dictionary", 1, 1); @@ -1595,7 +1604,7 @@ addr_t find_csblob_entitlements_dictionary_set(void) { return val; } addr_t find_kernel_task(void) { - addr_t call1, call2, call3, call4, call5, call6, call7; + addr_t call1; addr_t func1; addr_t str = find_strref("\"thread_terminate\"", 1, 0); @@ -1611,8 +1620,7 @@ addr_t find_kernel_task(void) { addr_t find_kernproc(void) { - addr_t call1, call2, call3, call4, call5, call6, call7; - addr_t func1; + addr_t call1, call2, call3, call4, call5, call6; addr_t err_str = find_strref("0 == error", 1, 0); err_str -= kerndumpbase; -- cgit v1.2.3