Created
February 18, 2025 01:51
-
-
Save khanhduytran0/787e789d1630c84974773506b24d6ab3 to your computer and use it in GitHub Desktop.
Boot tvOS on compatible iDevices (WIP)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _MOUNT_ARGS_H | |
#define _MOUNT_ARGS_H | |
#include <stdint.h> | |
#include <sys/time.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <sys/types.h> | |
enum { | |
APFS_MOUNT_AS_ROOT = 0, /* mount the default snapshot */ | |
APFS_MOUNT_FILESYSTEM, /* mount live fs */ | |
APFS_MOUNT_SNAPSHOT, /* mount custom snapshot in apfs_mountarg.snapshot */ | |
APFS_MOUNT_FOR_CONVERSION, /* mount snapshot while suppling some representation of im4p and im4m */ | |
APFS_MOUNT_FOR_VERIFICATION, /* Fusion mount with tier 1 & 2, set by mount_apfs when -C is used (Conversion mount) */ | |
APFS_MOUNT_FOR_INVERSION, /* Fusion mount with tier 1 only, set by mount_apfs when -c is used */ | |
APFS_MOUNT_MODE_SIX, /* ??????? */ | |
APFS_MOUNT_FOR_INVERT, /* ??? mount for invert */ | |
APFS_MOUNT_IMG4 /* mount live fs while suppling some representation of im4p and im4m */ | |
}; | |
#define APFS_MOUNT_IMG4_MAXSZ 0x100000 | |
#define APFS_AUTH_ENV_GENERIC 4 | |
#define APFS_AUTH_ENV_SUPPLEMENTAL 5 | |
#define APFS_AUTH_ENV_PDI_NONCE 6 | |
#define APFS_CRYPTEX_TYPE_GENERIC 0x67746776 /* vgtg */ | |
#define APFS_CRYPTEX_TYPE_BRAIN 0x73796162 /* bays */ | |
#define APFS_FLAGS_SMALL_N_OPT 0x400000000 /* mount_apfs -n */ | |
#define APFS_FLAGS_LARGE_R_OPT 0x200000000 /* mount_apfs -R */ | |
#define APFS_FLAGS_LARGET_S_OPT 0x800000000 /* mount_apfs -S */ | |
#define APFSFSMNT_SKIPSANITY 0x1 | |
#define APFSFSMNT_CHECKPOINT 0x2 | |
#define APFSFSMNT_DEMOMODE 0x4 | |
#define APFSFSMNT_TINYOBJCACHE 0x8 | |
#define APFSFSMNT_CREATETMPCP 0x10 | |
#define APFSFSMNT_LOADTMPCP 0x20 | |
#define APFSFSMNT_COMMITTMPCP 0x40 | |
/* Fourth argument to mount(2) when mounting apfs */ | |
struct apfs_mount_args { | |
#ifndef KERNEL | |
char* fspec; /* path to device to mount from */ | |
#endif | |
uint64_t apfs_flags; /* The standard mount flags, OR'd with apfs-specific flags (APFS_FLAGS_* above) */ | |
uint32_t mount_mode; /* APFS_MOUNT_* */ | |
uint32_t pad1; /* padding */ | |
uint32_t unk_flags; /* yet another type some sort of flags (bitfield), possibly volume role related */ | |
union { | |
char snapshot[256]; /* snapshot name */ | |
struct { | |
char tier1_dev[128]; /* Tier 1 device (Fusion mount) */ | |
char tier2_dev[128]; /* Tier 2 device (Fusion mount) */ | |
}; | |
}; | |
void* im4p_ptr; | |
uint32_t im4p_size; | |
uint32_t pad2; /* padding */ | |
void* im4m_ptr; | |
uint32_t im4m_size; | |
uint32_t pad3; /* padding */ | |
uint32_t cryptex_type; /* APFS_CRYPTEX_TYPE_* */ | |
int32_t auth_mode; /* APFS_AUTH_ENV_* */ | |
uid_t uid; | |
gid_t gid; | |
}__attribute__((packed, aligned(4))); | |
typedef struct apfs_mount_args apfs_mount_args_t; | |
#define HFSFSMNT_NOXONFILES 0x1 /* disable execute permissions for files */ | |
#define HFSFSMNT_WRAPPER 0x2 /* mount HFS wrapper (if it exists) */ | |
#define HFSFSMNT_EXTENDED_ARGS 0x4 /* indicates new fields after "flags" are valid */ | |
/* | |
* Fourth argument to mount(2) when mounting hfs | |
*/ | |
typedef struct hfs_mount_args { | |
#ifndef KERNEL | |
char *fspec; /* block special device to mount */ | |
#endif | |
uid_t hfs_uid; /* uid that owns hfs files (standard HFS only) */ | |
gid_t hfs_gid; /* gid that owns hfs files (standard HFS only) */ | |
mode_t hfs_mask; /* mask to be applied for hfs perms (standard HFS only) */ | |
uint32_t hfs_encoding; /* encoding for this volume (standard HFS only) */ | |
struct timezone hfs_timezone; /* user time zone info (standard HFS only) */ | |
int flags; /* mounting flags, see HFSFSMNT_* */ | |
int journal_tbuffer_size; /* size in bytes of the journal transaction buffer */ | |
int journal_flags; /* flags to pass to journal_open/create */ | |
int journal_disable; /* don't use journaling (potentially dangerous) */ | |
} hfs_mount_args_t; | |
/* Fourth argument to mount(2) when mounting tmpfs */ | |
typedef struct tmpfs_mount_args { | |
uint64_t max_pages; /* maximum amount of memory pages to be used for this tmpfs*/ | |
uint64_t max_nodes; /* maximum amount of inodes in this tmpfs */ | |
uint64_t case_insensitive; /* 1 = case insensitive, 0 = case sensitive */ | |
} tmpfs_mount_args_t; | |
#endif /* MOUNR_ARGS_H */ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <os/atomic_private.h> | |
#import <libjailbreak.h> | |
#import <physrw.h> | |
#define SET(t, f) (t) |= (f) | |
#define CLR(t, f) (t) &= ~(f) | |
#define ISSET(t, f) ((t) & (f)) | |
#define NULLVP NULL | |
#define XNU_PTRAUTH_SIGNED_PTR(A) | |
#define MNTK_SYSTEM 0x00000040 | |
#define MNTK_VIRTUALDEV 0x00200000 | |
#define VROOT 0x000001 | |
#define VSYSTEM 0x000004 | |
#define VMOUNT 0x000080 | |
#define VMOUNTEDHERE 0x40000000 | |
#define VNODE_REF_FORCE 0x1 | |
#define v_mountedhere v_un.vu_mountedhere | |
#define v_socket v_un.vu_socket | |
#define v_specinfo v_un.vu_specinfo | |
#define v_fifoinfo v_un.vu_fifoinfo | |
#define v_ubcinfo v_un.vu_ubcinfo | |
#define v_rdev v_specinfo->si_rdev | |
#define panic(...) do { printf(__VA_ARGS__); printf("\n"); abort(); } while (0) | |
#define vnode_lock_convert(v) lck_mtx_convert_spin(&(v)->v_lock) | |
const off_t | |
off_p_pfd = 0xf8, | |
off_fd_cdir = 0x20, | |
off_fg_data = 0x38, | |
off_fp_glob = 0x10; | |
LIST_HEAD(buflists, buf); | |
typedef int kauth_action_t; | |
typedef uint32_t vfs_switch_root_flags_t; | |
typedef uintptr_t __smrq_link_t; | |
typedef uint64_t lck_mtx_t[2]; | |
typedef uint32_t pending_io_t; | |
typedef int64_t daddr64_t; | |
dev_t *rootdev_p; | |
vnode_t *rootvnode_p, *rootvp_p; | |
enum vtype { | |
/* 0 */ | |
VNON, | |
/* 1 - 5 */ | |
VREG, VDIR, VBLK, VCHR, VLNK, | |
/* 6 - 10 */ | |
VSOCK, VFIFO, VBAD, VSTR, VCPLX | |
}; | |
typedef struct { | |
uintptr_t opaque[2] __kernel_data_semantics; | |
} lck_rw_t; | |
struct smrq_link { | |
__smrq_link_t next; | |
__smrq_link_t *prev; | |
}; | |
TAILQ_HEAD(vnodelst, vnode); | |
struct mount { | |
TAILQ_ENTRY(mount) mnt_list; /* mount list */ | |
int32_t mnt_count; /* reference on the mount */ | |
lck_mtx_t mnt_mlock; /* mutex that protects mount point */ | |
const struct vfsops * XNU_PTRAUTH_SIGNED_PTR("mount.vfsops") mnt_op; /* operations on fs */ | |
struct vfstable * XNU_PTRAUTH_SIGNED_PTR("mount.mnt_vtable") mnt_vtable; /* configuration info */ | |
struct vnode * XNU_PTRAUTH_SIGNED_PTR("mount.mnt_vnodecovered") mnt_vnodecovered; /* vnode we mounted on */ | |
struct vnodelst mnt_vnodelist; /* list of vnodes this mount */ | |
struct vnodelst mnt_workerqueue; /* list of vnodes this mount */ | |
struct vnodelst mnt_newvnodes; /* list of vnodes this mount */ | |
uint32_t mnt_flag; /* flags */ | |
uint32_t mnt_kern_flag; /* kernel only flags. NOTE: See mnt_supl_kern_flags below! */ | |
uint32_t mnt_compound_ops; /* Available compound operations */ | |
uint32_t mnt_lflag; /* mount life cycle flags */ | |
uint32_t mnt_maxsymlinklen; /* max size of short symlink */ | |
struct vfsstatfs mnt_vfsstat; /* cache of filesystem stats */ | |
qaddr_t mnt_data; /* private data */ | |
/* Cached values of the IO constraints for the device */ | |
uint32_t mnt_maxreadcnt; /* Max. byte count for read */ | |
uint32_t mnt_maxwritecnt; /* Max. byte count for write */ | |
uint32_t mnt_segreadcnt; /* Max. segment count for read */ | |
uint32_t mnt_segwritecnt; /* Max. segment count for write */ | |
uint32_t mnt_maxsegreadsize; /* Max. segment read size */ | |
uint32_t mnt_maxsegwritesize; /* Max. segment write size */ | |
uint32_t mnt_alignmentmask; /* Mask of bits that aren't addressable via DMA */ | |
uint32_t mnt_devblocksize; /* the underlying device block size */ | |
uint32_t mnt_ioqueue_depth; /* the maxiumum number of commands a device can accept */ | |
uint32_t mnt_ioscale; /* scale the various throttles/limits imposed on the amount of I/O in flight */ | |
uint32_t mnt_ioflags; /* flags for underlying device */ | |
uint32_t mnt_minsaturationbytecount; /* if non-zero, mininum amount of writes (in bytes) needed to max out throughput */ | |
pending_io_t mnt_pending_write_size __attribute__((aligned(sizeof(pending_io_t)))); /* byte count of pending writes */ | |
pending_io_t mnt_pending_read_size __attribute__((aligned(sizeof(pending_io_t)))); /* byte count of pending reads */ | |
struct timeval mnt_last_write_issued_timestamp; | |
struct timeval mnt_last_write_completed_timestamp; | |
int64_t mnt_max_swappin_available; | |
lck_rw_t mnt_rwlock; /* mutex readwrite lock */ | |
lck_mtx_t mnt_renamelock; /* mutex that serializes renames that change shape of tree */ | |
vnode_t mnt_devvp; /* the device mounted on for local file systems */ | |
uint32_t mnt_devbsdunit; /* the BSD unit number of the device */ | |
uint64_t mnt_throttle_mask; /* the throttle mask of what devices will be affected by I/O from this mnt */ | |
void *mnt_throttle_info; /* used by the throttle code */ | |
int32_t mnt_crossref; /* refernces to cover lookups crossing into mp */ | |
int32_t mnt_iterref; /* refernces to cover iterations; drained makes it -ve */ | |
//... | |
}; | |
struct namecache { | |
TAILQ_ENTRY(namecache) nc_entry; /* chain of all entries */ | |
TAILQ_ENTRY(namecache) nc_child; /* chain of ncp's that are children of a vp */ | |
union { | |
LIST_ENTRY(namecache) nc_link; /* chain of ncp's that 'name' a vp */ | |
TAILQ_ENTRY(namecache) nc_negentry; /* chain of ncp's that 'name' a vp */ | |
} nc_un; | |
struct smrq_link nc_hash; /* hash chain */ | |
uint32_t nc_vid; /* vid for nc_vp */ | |
uint32_t nc_counter; /* flags */ | |
vnode_t nc_dvp; /* vnode of parent of name */ | |
vnode_t nc_vp; /* vnode the name refers to */ | |
unsigned int nc_hashval; /* hashval of stringname */ | |
const char *nc_name; /* pointer to segment name in string cache */ | |
}; | |
struct specinfo { | |
struct vnode **si_hashchain; | |
struct vnode *si_specnext; | |
long si_flags; | |
dev_t si_rdev; | |
int32_t si_opencount; | |
daddr_t si_size; /* device block size in bytes */ | |
daddr64_t si_lastr; /* last read blkno (read-ahead) */ | |
u_int64_t si_devsize; /* actual device size in bytes */ | |
u_int8_t si_initted; | |
u_int8_t si_throttleable; | |
u_int16_t si_isssd; | |
u_int32_t si_devbsdunit; | |
u_int64_t si_throttle_mask; | |
thread_t si_mountingowner; | |
}; | |
struct vnode { | |
lck_mtx_t v_lock; | |
TAILQ_ENTRY(vnode) v_freelist; /* vnode freelist */ | |
TAILQ_ENTRY(vnode) v_mntvnodes; /* vnodes for mount point */ | |
TAILQ_HEAD(, namecache) v_ncchildren; /* name cache entries that regard us as their parent */ | |
LIST_HEAD(, namecache) v_nclinks; /* name cache entries that name this vnode */ | |
uint64_t v_defer_reclaimlist; | |
uint32_t v_listflag; | |
uint32_t v_flag; | |
uint16_t v_lflag; | |
uint8_t v_iterblkflags; | |
uint8_t v_references; | |
int32_t v_kusecount; | |
int32_t v_usecount; | |
int32_t v_iocount; | |
uint64_t v_owner; | |
uint16_t v_type; | |
uint16_t v_tag; | |
uint32_t v_id; | |
union { | |
mount_t vu_mountedhere; | |
uint64_t vu_socket; | |
struct specinfo *vu_specinfo; | |
uint64_t vu_fifoinfo; | |
uint64_t vu_ubcinfo; | |
} v_un; | |
struct buflists v_cleanblkhd; | |
struct buflists v_dirtyblkhd; | |
struct klist v_knotes; | |
kauth_cred_t /* XNU_PTRAUTH_SIGNED_PTR("vnode.v_cred")*/ v_cred; | |
kauth_action_t v_authorized_actions; | |
int v_cred_timestamp; | |
int v_nc_generation; | |
int32_t v_numoutput; | |
int32_t v_writecount; | |
uint32_t v_holdcount; | |
const char *v_name; | |
vnode_t /* XNU_PTRAUTH_SIGNED_PTR("vnode.v_parent") */ v_parent; | |
struct lockf *v_lockf; | |
int(**v_op)(void *); | |
mount_t /* XNU_PTRAUTH_SIGNED_PTR("vnode.v_mount") */ v_mount; | |
void *v_data; | |
}; | |
typedef struct vnode * vnode_t; | |
extern TAILQ_HEAD(mntlist, mount) mountlist; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@import Darwin; | |
#import "mount_args.h" | |
#import "structs.h" | |
#define xPAC(field) ((typeof(field))UNSIGN_PTR((uint64_t)field)) | |
#define SET_OR32(target, value) kwrite32((uint64_t)&target, kread32((uint64_t)&target) | value) | |
#define UNSET_AND32(target, value) kwrite32((uint64_t)&target, kread32((uint64_t)&target) & ~(value)) | |
int reboot3(uint64_t flags, ...); | |
// kfund | |
uint64_t getVnodeAtPath(const char* filename) { | |
int file_index = open(filename, O_RDONLY); | |
if (file_index == -1) return 0; | |
uint64_t proc = proc_find(getpid()); | |
uint64_t filedesc = kread_ptr(proc + off_p_pfd); | |
uint64_t openedfile = kread64(filedesc + (8 * file_index)); | |
uint64_t fileglob = kread_ptr(openedfile + off_fp_glob); | |
uint64_t vnode = kread_ptr(fileglob + off_fg_data); | |
close(file_index); | |
return vnode; | |
} | |
int mount_apfs(const char *dir, int flags, char *device) { | |
apfs_mount_args_t args = { device, flags, APFS_MOUNT_FILESYSTEM , 0, 0, { "" }, NULL, 0, 0, NULL, 0, 0, 0, 0, 0, 0 }; | |
return mount("apfs", dir, flags, &args); | |
} | |
int main(int argc, char *argv[]) { | |
if (getuid() != 0) { | |
printf("Must be run as root\n"); | |
return 1; | |
} | |
jbclient_initialize_primitives(); | |
printf("0x%llx 0x%llx\n", kconstant(physBase), kconstant(physSize)); | |
vnode_t vnode = (vnode_t)getVnodeAtPath("/"); | |
mount_t v_mount = (mount_t)xPAC(kread64((uint64_t)&vnode->v_mount)); | |
UNSET_AND32(v_mount->mnt_flag, MNT_ROOTFS); | |
UNSET_AND32(vnode->v_flag, VROOT); | |
printf("Unset root flags done\n"); | |
uint64_t credBackup = 0; | |
jbclient_root_steal_ucred(0, &credBackup); | |
BOOL cancelled = NO, success = NO; | |
int rootopts = 0;//rootful or MNT_RDONLY | |
unmount("/private/var/MobileSoftwareUpdate", MNT_FORCE); | |
unmount("/private/var/hardware", MNT_FORCE); | |
if (mount_apfs("/", rootopts, "/dev/disk1s11") != 0) { | |
printf("Mount / failed: %d\n", errno); | |
// Since we've not mounted real rootfs yet, pretend we're succeed | |
cancelled = YES; | |
goto done; | |
} | |
if (mount("devfs", "/dev", 0, (void *)"devfs") != 0) { | |
printf("Mount /dev failed: %d\n", errno); | |
goto done; | |
} | |
if (mount_apfs("/private/preboot", 0, "/dev/disk1s9") != 0) { | |
printf("Mount /private/preboot failed: %d\n", errno); | |
goto done; | |
} | |
if (mount_apfs("/private/var", 0, "/dev/disk1s8") != 0) { | |
printf("Mount /private/var failed: %d\n", errno); | |
goto done; | |
} | |
// transfer some volume mountpoints to us | |
// these MUST be read-only for safety reasons | |
if (mount_apfs("/private/var/MobileSoftwareUpdate", MNT_RDONLY, "/dev/disk1s5") != 0) { | |
printf("Mount /private/var/MobileSoftwareUpdate failed: %d\n", errno); | |
//goto done; | |
} | |
if (mount_apfs("/private/var/hardware", MNT_RDONLY, "/dev/disk1s4") != 0) { | |
printf("Mount /private/var/hardware failed: %d\n", errno); | |
//goto done; | |
} | |
if (mount("bindfs", "/System/Library/Caches/com.apple.factorydata", MNT_RDONLY, (void *)"/private/var/hardware/FactoryData/System/Library/Caches/com.apple.factorydata") != 0) { | |
printf("Mount /factorydata failed: %d\n", errno); | |
//goto done; | |
} | |
success = YES; | |
done: | |
if (cancelled || success) { | |
vnode_t new_vnode = (vnode_t)getVnodeAtPath("/"); | |
mount_t new_mount = (mount_t)xPAC(kread64((uint64_t)&new_vnode->v_mount)); | |
SET_OR32(new_mount->mnt_flag, MNT_ROOTFS); | |
SET_OR32(new_vnode->v_flag, VROOT); | |
} else { | |
// Restore volumes we transferred. FIXME: does not work | |
printf("Unmounting rootfs due to failure\n"); | |
unmount("/", MNT_FORCE); | |
mount_apfs("/private/var/MobileSoftwareUpdate", 0, "/dev/disk1s5"); | |
mount_apfs("/private/var/hardware", 0, "/dev/disk1s4"); | |
} | |
SET_OR32(v_mount->mnt_flag, MNT_ROOTFS); | |
SET_OR32(vnode->v_flag, VROOT); | |
if (cancelled) { | |
printf("Failed to mount rootfs, did you mount it elsewhere? Unmount it and try again\n"); | |
return 1; | |
} | |
jbclient_root_steal_ucred(credBackup, NULL); | |
printf("Press enter to userspace reboot\n"); | |
getchar(); | |
reboot3(0x2000000000000000llu); // RB2_USERREBOOT | |
printf("Done. Press enter to exit\n"); | |
getchar(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment