Skip to content

Instantly share code, notes, and snippets.

@khanhduytran0
Created February 18, 2025 01:51
Show Gist options
  • Save khanhduytran0/787e789d1630c84974773506b24d6ab3 to your computer and use it in GitHub Desktop.
Save khanhduytran0/787e789d1630c84974773506b24d6ab3 to your computer and use it in GitHub Desktop.
Boot tvOS on compatible iDevices (WIP)
#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 */
#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;
@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