Skip to content

Commit

Permalink
C0rrTrust
Browse files Browse the repository at this point in the history
  • Loading branch information
jakeajames authored Apr 6, 2019
1 parent 1730694 commit a67241d
Show file tree
Hide file tree
Showing 14 changed files with 924 additions and 77 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Library with commonly used patches in open-source jailbreaks. Call this a (light
# Issues
- AMFID patch won't resist after app enters background. Fix would be using a daemon (like amfidebilitate) or injecting a dylib (iOS 11)

# iOS 12 status
# iOS 12 satus
- rootFS remount is broken. There is hardening on snapshot_rename() which *can* and *has* been (privately) bypassed, but it for sure isn't as bad as last year with iOS 11.3.1, where they made **major** changes. The only thing we need is figuring out how they check if the snapshot is the rootfs and not something in /var for example where snapshot_rename works fine.
- kexecute() is also probably broken on A12. Use bazad's PAC bypass which offers the same thing, so this isn't an issue (fr now)
- getting root, unsandboxing, NVRAM lock/unlock, setHSP4(), trustbin(), entitlePid + task_for_pid() are all working fine. The rest that is not on top of my mind should also work fine.
Expand Down
54 changes: 7 additions & 47 deletions amfi_utils.h
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@

//
// amfi_utils.h
// electra
//
// Created by Jamie on 27/01/2018.
// Copyright © 2018 Electra Team. All rights reserved.
//


#import <stdio.h>
#import <sys/types.h>
#import "cs_blob.h"
#import "jelbrek.h"

#define MACHO(p) ((*(unsigned int *)(p) & ~1) == 0xfeedface)

void *load_bytes(FILE *file, off_t offset, size_t size);
int strtail(const char *str, const char *tail);
void getSHA256inplace(const uint8_t* code_dir, uint8_t *out);
uint8_t *getSHA256(const uint8_t* code_dir);
uint8_t *getCodeDirectory(const char* name);
uint64_t ubc_cs_blob_allocate(vm_size_t size);
int cs_validate_csblob(const uint8_t *addr, size_t length, CS_CodeDirectory **rcd, CS_GenericBlob **rentitlements);
uint64_t getCodeSignatureLC(FILE *file, int64_t *machOff);
const struct cs_hash *cs_find_md(uint8_t type);

// thx hieplpvip
void inject_trusts(int pathc, const char *paths[]);

// Trust cache types
typedef char hash_t[20];

struct trust_chain {
Expand All @@ -30,36 +23,3 @@ struct trust_chain {
unsigned int count;
} __attribute__((packed));

/*
Note this patch still came from @xerub's KPPless branch, but detailed below is kind of my adventures which I rediscovered most of what he did
So, as said on twitter by @Morpheus______, iOS 11 now uses SHA256 for code signatures, rather than SHA1 like before.
What confuses me though is that I believe the overall CDHash is SHA1, but each subhash is SHA256. In AMFI.kext, the memcmp
used to check between the current hash and the hashes in the cache seem to be this CDHash. So the question is do I really need
to get every hash, or just the main CDHash and insert that one into the trust chain?
If we look at the trust chain code checker (0xFFFFFFF00637B3E8 6+ 11.1.2), it is pretty basic. The trust chain is in the format of
the following (struct from xerub, but I've checked with AMFI that it is the case):
struct trust_mem {
uint64_t next; // +0x00 - the next struct trust_mem
unsigned char uuid[16]; // +0x08 - The uuid of the trust_mem (it doesn't seem important or checked apart from when importing a new trust chain)
unsigned int count; // +0x18 - Number of hashes there are
unsigned char hashes[]; // +0x1C - The hashes
}
The trust chain checker does the following:
- Find the first struct that has a count > 0
- Loop through all the hashes in the struct, comparing with the current hash
- Keeps going through each chain, then when next is 0, it finishes
UPDATE: a) was using an old version of JTool. Now I realised the CDHash is SHA256
b) For launchd (whose hash resides in the AMFI cache), the first byte is used as an index sort of thing, and the next *19* bytes are used for the check
This probably means that only the first 20 bytes of the CDHash are used in the trust cache check
So our execution method is as follows:
- Calculate the CD Hashes for the target resources that we want to play around with
- Create a custom trust chain struct, and insert it into the existing trust chain - only storing the first 20 bytes of each hash
- ??? PROFIT
*/

107 changes: 107 additions & 0 deletions amfi_utils.m
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,110 @@ int strtail(const char *str, const char *tail)
str += lstr - ltail;
return memcmp(str, tail, ltail);
}

int cs_validate_csblob(const uint8_t *addr, size_t length, CS_CodeDirectory **rcd, CS_GenericBlob **rentitlements) {
uint64_t rcdptr = Kernel_alloc(8);
uint64_t entptr = Kernel_alloc(8);

int ret = (int)Kernel_Execute(Find_cs_validate_csblob(), (uint64_t)addr, length, rcdptr, entptr, 0, 0, 0);
*rcd = (CS_CodeDirectory *)KernelRead_64bits(rcdptr);
*rentitlements = (CS_GenericBlob *)KernelRead_64bits(entptr);

Kernel_free(rcdptr, 8);
Kernel_free(entptr, 8);

return ret;
}

uint64_t ubc_cs_blob_allocate(vm_size_t size) {
uint64_t size_p = Kernel_alloc(sizeof(vm_size_t));
if (!size_p) return 0;
KernelWrite(size_p, &size, sizeof(vm_size_t));
uint64_t alloced = Kernel_Execute(Find_kalloc_canblock(), size_p, 1, Find_cs_blob_allocate_site(), 0, 0, 0, 0);
Kernel_free(size_p, sizeof(vm_size_t));
if (alloced) alloced = ZmFixAddr(alloced);
return alloced;
}

const struct cs_hash *cs_find_md(uint8_t type) {
return (struct cs_hash *)KernelRead_64bits(Find_cs_find_md() + ((type - 1) * 8));
}

uint64_t getCodeSignatureLC(FILE *file, int64_t *machOff) {
size_t offset = 0;
struct load_command *cmd = NULL;

// Init at this
*machOff = -1;

uint32_t *magic = load_bytes(file, offset, sizeof(uint32_t));
int ncmds = 0;

// check magic
if (*magic != 0xFEEDFACF && *magic != 0xBEBAFECA) {
printf("[-] File is not an arm64 or FAT macho!\n");
free(magic);
return 0;
}

// FAT
if(*magic == 0xBEBAFECA) {

uint32_t arch_off = sizeof(struct fat_header);
struct fat_header *fat = (struct fat_header*)load_bytes(file, 0, sizeof(struct fat_header));
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 *)load_bytes(file, arch_off, sizeof(struct fat_arch));

if (ntohl(arch->cputype) == 0x100000c) {
printf("[*] Found arm64\n");
offset = ntohl(arch->offset);
foundarm64 = true;
free(fat);
free(arch);
break;
}
free(arch);
arch_off += sizeof(struct fat_arch);
}

if (!foundarm64) {
printf("[-] Binary does not have any arm64 slice\n");
free(fat);
free(magic);
return 0;
}
}

free(magic);

*machOff = offset;

// get macho header
struct mach_header_64 *mh64 = load_bytes(file, offset, sizeof(struct mach_header_64));
ncmds = mh64->ncmds;
free(mh64);

// next
offset += sizeof(struct mach_header_64);

for (int i = 0; i < ncmds; i++) {
cmd = load_bytes(file, offset, sizeof(struct load_command));

// this!
if (cmd->cmd == LC_CODE_SIGNATURE) {
free(cmd);
return offset;
}

// next
offset += cmd->cmdsize;
free(cmd);
}

return 0;
}
54 changes: 51 additions & 3 deletions cs_blob.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//from: xnu osfmk/kern/cs_blobs.h

#import <mach/mach.h>

typedef struct __attribute__((packed)) {
uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */
uint32_t length; /* total length of CodeDirectory blob */
Expand Down Expand Up @@ -51,6 +53,13 @@ typedef struct __attribute__((packed)) {
/* followed by Blobs in no particular order as indicated by offsets in index */
} CS_SuperBlob;

typedef struct __SC_Scatter {
uint32_t count; // number of pages; zero for sentinel (only)
uint32_t base; // first page number
uint64_t targetOffset; // offset in target
uint64_t spare; // reserved
} SC_Scatter;

/*
* Magic numbers used by Code Signing
*/
Expand Down Expand Up @@ -137,11 +146,35 @@ typedef struct __CodeDirectory {
uint32_t codeLimit; /* limit to main image signature range */
uint8_t hashSize; /* size of each hash in bytes */
uint8_t hashType; /* type of hash (cdHashType* constants) */
uint8_t spare1; /* unused (must be zero) */
uint8_t pageSize; /* log2(page size in bytes); 0 => infinite */
uint8_t platform; /* platform identifier; zero if not platform binary */
uint8_t pageSize; /* log2(page size in bytes); 0 => infinite */
uint32_t spare2; /* unused (must be zero) */

char end_earliest[0];

/* Version 0x20100 */
uint32_t scatterOffset; /* offset of optional scatter vector */
char end_withScatter[0];

/* Version 0x20200 */
uint32_t teamOffset; /* offset of optional team identifier */
char end_withTeam[0];

/* Version 0x20300 */
uint32_t spare3; /* unused (must be zero) */
uint64_t codeLimit64; /* limit to main image signature range, 64 bits */
char end_withCodeLimit64[0];

/* Version 0x20400 */
uint64_t execSegBase; /* offset of executable segment */
uint64_t execSegLimit; /* limit of executable segment */
uint64_t execSegFlags; /* executable segment flags */
char end_withExecSeg[0];

/* followed by dynamic content as located by offset fields above */
} CS_CodeDirectory;
} CS_CodeDirectory
__attribute__ ((aligned(1)));


#define CS_OPS_ENTITLEMENTS_BLOB 7 /* get entitlements blob */
int csops(pid_t pid, unsigned int ops, void *useraddr, size_t usersize);
Expand All @@ -168,7 +201,22 @@ struct cs_blob {
void * csb_entitlements; /* The entitlements as an OSDictionary */
unsigned int csb_signer_type;

unsigned int csb_reconstituted; // iOS 12 only

/* The following two will be replaced by the csb_signer_type. */
unsigned int csb_platform_binary:1;
unsigned int csb_platform_path:1;
};

typedef void (*cs_md_init)(void *ctx);
typedef void (*cs_md_update)(void *ctx, const void *data, size_t size);
typedef void (*cs_md_final)(void *hash, void *ctx);

struct cs_hash {
uint8_t cs_type; /* type code as per code signing */
size_t cs_size; /* size of effective hash (may be truncated) */
size_t cs_digest_size; /* size of native hash */
cs_md_init cs_init;
cs_md_update cs_update;
cs_md_final cs_final;
};
Binary file modified downloads/jelbrekLib.a
Binary file not shown.
48 changes: 47 additions & 1 deletion downloads/jelbrekLib.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@

typedef int (*kexecFunc)(uint64_t function, size_t argument_count, ...);

extern uint32_t KASLR_Slide;
extern uint64_t KernelBase;
extern mach_port_t TFP0;
extern kexecFunc kernel_exec;

/*
Purpose: Initialize jelbrekLib (first thing you have to call)
Expand Down Expand Up @@ -39,6 +42,18 @@ void term_jelbrek(void);
*/
int trustbin(const char *path);

/*
Purpose:
Bypass all codesign checks for a macho. Binary can be signed with SHA1 or SHA256
Parameters:
A path to a macho
Return values:
-1: error
0: success
*/

int bypassCodeSign(const char *macho);

/*
Purpose:
Unsandboxes a process
Expand Down Expand Up @@ -236,6 +251,16 @@ int remountRootFS(void);
*/
uint64_t getVnodeAtPath(const char *path);

/*
Purpose:
Same effect as "ln -sf replacement original" but not persistent through reboots
Parameters:
"original": path you wish to patch
"replacement": where to link the original path
vnode1 & vnode2: pointers where addresses of the two files vnodes will be stored OR NULL
*/
void copyFileInMemory(char *original, char *replacement, uint64_t *vnode1, uint64_t *vnode2);

/*
Purpose:
Do a hex dump I guess
Expand Down Expand Up @@ -340,7 +365,7 @@ int snapshot_check(const char *vol, const char *name);

/*
Purpose:
Patchfinding (by xerub & ninjaprawn)
Patchfinding (by xerub & ninjaprawn & me)
*/
uint64_t Find_allproc(void);
uint64_t Find_add_x0_x0_0x40_ret(void);
Expand All @@ -350,6 +375,7 @@ uint64_t Find_bcopy(void);
uint64_t Find_rootvnode(void);
uint64_t Find_trustcache(void);
uint64_t Find_amficache(void);
uint64_t Find_pmap_load_trust_cache_ppl(void);
uint64_t Find_OSBoolean_True(void);
uint64_t Find_OSBoolean_False(void);
uint64_t Find_zone_map_ref(void);
Expand All @@ -360,6 +386,25 @@ uint64_t Find_bootargs(void);
uint64_t Find_vfs_context_current(void);
uint64_t Find_vnode_lookup(void);
uint64_t Find_vnode_put(void);
uint64_t Find_cs_gen_count(void);
uint64_t Find_cs_validate_csblob(void);
uint64_t Find_kalloc_canblock(void);
uint64_t Find_cs_blob_allocate_site(void);
uint64_t Find_kfree(void);
uint64_t Find_cs_find_md(void);
// PAC
uint64_t Find_l2tp_domain_module_start(void);
uint64_t Find_l2tp_domain_module_stop(void);
uint64_t Find_l2tp_domain_inited(void);
uint64_t Find_sysctl_net_ppp_l2tp(void);
uint64_t Find_sysctl_unregister_oid(void);
uint64_t Find_mov_x0_x4__br_x5(void);
uint64_t Find_mov_x9_x0__br_x1(void);
uint64_t Find_mov_x10_x3__br_x6(void);
uint64_t Find_kernel_forge_pacia_gadget(void);
uint64_t Find_kernel_forge_pacda_gadget(void);
uint64_t Find_IOUserClient_vtable(void);
uint64_t Find_IORegistryEntry__getRegistryEntryID(void);

/*
Purpose:
Expand All @@ -377,6 +422,7 @@ void MakePortFakeTaskPort(mach_port_t port, uint64_t task_kaddr);
For reading & writing & copying & allocating & freeing kernel memory
*/
size_t KernelRead(uint64_t where, void *p, size_t size);
size_t KernelWrite(uint64_t where, void *p, size_t size);
uint32_t KernelRead_32bits(uint64_t where);
uint64_t KernelRead_64bits(uint64_t where);

Expand Down
Loading

0 comments on commit a67241d

Please sign in to comment.