Skip to content

Commit

Permalink
Implement restore root FS
Browse files Browse the repository at this point in the history
  • Loading branch information
UInt2048 committed Aug 27, 2024
1 parent 610f01e commit d40f52f
Show file tree
Hide file tree
Showing 14 changed files with 241 additions and 14 deletions.
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ SHELL = /bin/bash
TARGET_GUI = Spice
TARGET_CLI = spice
PACKAGE = lol.spyware.spicy
VERSION = 1.0.166
VERSION = 1.0.167

BIN = bin
RES = res
Expand All @@ -28,7 +28,7 @@ endif
UNTETHER = lib$(TARGET_CLI).dylib
TRAMP = trampoline
ICONS := $(wildcard $(RES)/Icon-*.png)
FILES := $(TARGET_GUI) Info.plist Base.lproj/LaunchScreen.storyboardc $(ICONS:$(RES)/%=%) Unrestrict.dylib bootstrap.tar.lzma jailbreak-resources.deb
FILES := $(TARGET_GUI) Info.plist Base.lproj/LaunchScreen.storyboardc $(ICONS:$(RES)/%=%) Unrestrict.dylib bootstrap.tar.lzma jailbreak-resources.deb uicache

IGCC ?= $(SDK_RESULT) clang -mios-version-min=10.0
ARCH_GUI ?= -arch $(ARCH_RESULT)
Expand Down Expand Up @@ -73,6 +73,10 @@ $(APP)/jailbreak-resources.deb:
echo Copying file to $@
cp $(RES)/jailbreak-resources.deb $@

$(APP)/uicache:
echo Copying file to $@
cp $(RES)/uicache $@

# TODO: Make more accurate prerequisites

$(SRC_ALL)/offsets.h:
Expand Down
12 changes: 6 additions & 6 deletions docs/Packages
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
Package: lol.spyware.spiceuntether
Version: 1.0.166
Version: 1.0.167
Architecture: iphoneos-arm
Maintainer: UInt2048
Depends: firmware (>= 11.0), firmware (<= 11.4.1)
Filename: ./lol.spyware.spiceuntether_1.0.166_iphoneos-arm.deb
Size: 132392
MD5sum: 9e94c26d66b8280cd855757485133220
SHA1: 62f4717a0876a894d59a7ededb7b940b210d0c34
SHA256: 97c7bb43cbf5328920cc184957625cf004b4daa45414d091a6a74ff02f9bd332
Filename: ./lol.spyware.spiceuntether_1.0.167_iphoneos-arm.deb
Size: 134044
MD5sum: 89315d377cadd3419f9bcdd68a50ddcd
SHA1: 4399fdecf40ef17555f100011bff6f3b3bcdf117
SHA256: 76dbef6eaa8c17653c3dd20e8ff04bc193c9830afeb98d324d91fb5c608abebf
Section: System
Description: Upgrades the Spice jailbreak to untethered
Author: JakeBlair420
Expand Down
Binary file modified docs/Packages.bz2
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added res/uicache
Binary file not shown.
34 changes: 32 additions & 2 deletions src/app/MainVC.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ void sendLog(void* controller, NSString* log) {

@implementation MainVC

UIButton *jbButton;
UIButton *jbButton, *restoreButton;
UILabel *spiceLabel, *titleLabel;
bool hasJailbroken = false;
uint32_t jailbreakFlags = 0;

-(void)showLog:(NSString *)log
{
Expand Down Expand Up @@ -65,6 +66,17 @@ - (void)loadView
[jbButton setBackgroundColor:[UIColor colorWithRed:1.00 green:0.00 blue:0.00 alpha:0.0]];
jbButton.titleLabel.font = [UIFont systemFontOfSize:30];
[jbButton addTarget:self action:@selector(actionJailbreak) forControlEvents:UIControlEventTouchUpInside];

#ifdef __LP64__
restoreButton = [UIButton buttonWithType:UIButtonTypeSystem];
restoreButton.translatesAutoresizingMaskIntoConstraints = NO;
[restoreButton setTitle:@"Restore RootFS" forState:UIControlStateNormal];
[restoreButton setTitleColor:[UIColor colorWithRed:110.0/255.0 green:59.0/255.0 blue:38.0/255.0 alpha:1.0] forState:UIControlStateNormal];
[restoreButton setTitleColor:[UIColor colorWithRed:35.0/255.0 green:75.0/255.0 blue:155.0/255.0 alpha:1.0] forState:UIControlStateHighlighted];
[restoreButton setBackgroundColor:[UIColor colorWithRed:1.00 green:0.00 blue:0.00 alpha:0.0]];
restoreButton.titleLabel.font = [UIFont systemFontOfSize:30];
[restoreButton addTarget:self action:@selector(actionRootFS) forControlEvents:UIControlEventTouchUpInside];
#endif

spiceLabel = [UILabel new];
spiceLabel.translatesAutoresizingMaskIntoConstraints = NO;
Expand Down Expand Up @@ -96,6 +108,12 @@ - (void)loadView
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:jbButton attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:jbButton attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.7 constant:0.0]];

#ifdef __LP64__
[self.view addSubview:restoreButton];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:restoreButton attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:restoreButton attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.5 constant:0.0]];
#endif

[self.view addSubview:spiceLabel];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:spiceLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:spiceLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:0.4 constant:0.0]];
Expand All @@ -106,6 +124,18 @@ - (void)loadView
}

- (void)actionJailbreak
{
jailbreakFlags &= ~JBOPT_RESTORE_ROOT_FS;
[self actionJailbreakInternal];
}

- (void)actionRootFS
{
jailbreakFlags |= JBOPT_RESTORE_ROOT_FS;
[self actionJailbreakInternal];
}

- (void)actionJailbreakInternal
{
if (hasJailbroken)
{
Expand All @@ -119,7 +149,7 @@ - (void)actionJailbreak
[jbButton setTitle:@"Jailbreaking..." forState:UIControlStateNormal];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) {
int ret = jailbreak(0, self, &sendLog);
int ret = jailbreak(jailbreakFlags, self, &sendLog);
NSLog(@"jailbreak ret: %d", ret);

if (ret != 0) {
Expand Down
1 change: 1 addition & 0 deletions src/shared/iokit.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,5 @@ kern_return_t IOConnectCallAsyncScalarMethod(io_connect_t client, uint32_t selec
kern_return_t IOConnectCallAsyncStructMethod(io_connect_t client, uint32_t selector, mach_port_t wake_port, uint64_t *ref, uint32_t refCnt, const void *inStruct, size_t inStructCnt, void *outStruct, size_t *outStructCnt);
kern_return_t IOConnectTrap6(io_connect_t client, uint32_t index, uintptr_t a, uintptr_t b, uintptr_t c, uintptr_t d, uintptr_t e, uintptr_t f);

io_registry_entry_t IORegistryEntryFromPath(mach_port_t mainPort, const io_string_t path);
#endif
3 changes: 2 additions & 1 deletion src/shared/jailbreak.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

#include "common.h"

#define JBOPT_POST_ONLY (1 << 0) /* post-exploitation only */
#define JBOPT_POST_ONLY (1 << 0) /* post-exploitation only */
#define JBOPT_RESTORE_ROOT_FS (1 << 1) /* restore root file system snapshot */

extern offsets_t offs;

Expand Down
5 changes: 5 additions & 0 deletions src/shared/jailbreak.m
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ kern_return_t jailbreak(uint32_t opt, void* controller, void (*sendLog)(void*, N

MACH(remount_root_fs());
PWN_LOG("remounted root fs");

if (opt & JBOPT_RESTORE_ROOT_FS) {
ret = restore_root_fs();
goto out;
}

updateStage(16);

Expand Down
3 changes: 2 additions & 1 deletion src/shared/root_fs.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

int snapshot_count(const char *path);
int mount_unionfs(const char *dmg_path);
int remount_root_fs(void);
kern_return_t remount_root_fs(void);
kern_return_t restore_root_fs(void);
187 changes: 186 additions & 1 deletion src/shared/root_fs.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
#include <sys/mount.h>
#include <sys/snapshot.h>

#include "iokit.h"
#include "jailbreak.h"
#include "kcall.h"
#include "kmem.h"
#include "kutils.h"
#include "jailbreak.h"
#include "kents.h"
#include "utils.h"

// Kernel.framework -> hfs/hfs_mount.h
struct hfs_mount_args {
Expand All @@ -35,6 +37,8 @@
attrreference_t name_info;
} val_attrs_t;

#define RB_QUICK 0x400 // quick and ungraceful reboot with file system caches flushed

// creds https://github.com/sbingner/snappy/blob/master/snappy.m#L90
int snapshot_count(const char *path)
{
Expand Down Expand Up @@ -120,6 +124,35 @@ int snapshot_rename(const char *path, const char *from, const char *to)
return NULL;
}

const char *get_boot_snapshot_name()
{
io_registry_entry_t chosen = IORegistryEntryFromPath(0, "IODeviceTree:/chosen");
CFTypeRef prop = IORegistryEntryCreateCFProperty(chosen, (__bridge CFStringRef)@"boot-manifest-hash", kCFAllocatorDefault, 0);
IOObjectRelease(chosen);

if (CFGetTypeID(prop) != CFDataGetTypeID()) {
LOG("Failed to get boot manifest hash");
CFRelease(prop);
return NULL;
}

CFIndex len = CFDataGetLength((CFDataRef)prop);
UInt8 buf[len];
CFDataGetBytes((CFDataRef)prop, CFRangeMake(0, len), buf);
CFRelease(prop);

NSMutableString* name = @"com.apple.os.update-".mutableCopy;
for (CFIndex i = 0; i < len; ++i) {
[name appendFormat:@"%02X", buf[i]];
}

const char* ret = [name UTF8String];

LOG("Boot snapshot: %s", ret);

return ret;
}

uint64_t vnode_from_path(const char *dev_path)
{
LOG("finding vnode: %s", dev_path);
Expand Down Expand Up @@ -327,10 +360,162 @@ kern_return_t remount_root_fs()

// note to self: 0x400 causes hard reboot w/o flushin any fs shit
// without this, snapshot rename won't come into place
reboot(0x400);
reboot(RB_QUICK);

return KERN_SUCCESS;
}

return dunk_on_mac_mount();
}

// Ripped from https://github.com/Odyssey-Team/Odyssey/blob/9253c774c1c67a943a98f288cd0edb55b892d302/Odyssey/post-exploit/utils/remount.swift#L311-L398

// Copyright 2020, CoolStar. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY COOLSTAR AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifdef __LP64__
#define OFFSET_PROC_P_UCRED 0x100
#define SPICE_MOUNT_POINT "/var/tmp/rootfs"
#define SPICE_MOUNT_APPLICATIONS @"/var/tmp/rootfs/Applications"

kern_return_t restore_root_fs() {
int snap_count = snapshot_count("/");
LOG("snap_count: %d", snap_count);

// if the device *is* a snapshot, we won't be able to count them
if (snap_count < 0) {
LOG("RootFS restore not required");
return KERN_SUCCESS;
}

const char* boot_snapshot = get_boot_snapshot_name();
if (boot_snapshot == NULL) {
LOG("Boot snapshot not present?");
return KERN_FAILURE;
}

// Remove /var/cache and /var/lib
#define fileMgr [NSFileManager defaultManager]
[fileMgr removeItemAtPath:@"/var/cache" error:nil];
[fileMgr removeItemAtPath:@"/var/lib" error:nil];

// find credz
uint64_t our_proc = find_proc(getpid());
uint64_t our_ucred = rk64(our_proc + OFFSET_PROC_P_UCRED);
uint64_t kern_ucred = rk64(find_proc(0) + OFFSET_PROC_P_UCRED);

// set kern ucred (bypass perm. check)
wk64(our_proc + OFFSET_PROC_P_UCRED, kern_ucred);

mkdir(SPICE_MOUNT_POINT, 0755);
chown(SPICE_MOUNT_POINT, 0, 0);

int fd = open("/", O_RDONLY, 0);

if (fd <= 0) {
LOG("Failed to get root file descriptor");
goto fail;
}

if (fs_snapshot_rename(fd, "original_rootfs", boot_snapshot, 0) != KERN_SUCCESS) {
LOG("fs_snapshot_rename failed");
goto fail;
}

if (fs_snapshot_revert(fd, boot_snapshot, 0) != KERN_SUCCESS) {
LOG("fs_snapshot_revert failed");
goto fail;
}

if (fs_snapshot_mount(fd, SPICE_MOUNT_POINT, boot_snapshot, 0) != KERN_SUCCESS) {
LOG("fs_snapshot_mount failed");
goto fail;
}

close(fd);

// Actual restore done, now deregister apps that shouldn't be here
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef resourcesUrl = CFBundleCopyResourcesDirectoryURL(mainBundle);
int len = 4096;
char *bundle_path = malloc(len);
CFURLGetFileSystemRepresentation(resourcesUrl, TRUE, (UInt8 *)bundle_path, len);
LOG("bundle path: %s", bundle_path);

#define COPY_RESOURCE(name, to_path) \
do { \
unlink(to_path); \
[fileMgr copyItemAtPath:[NSString stringWithFormat:@"%s/%s", bundle_path, name] toPath:@to_path error:nil]; \
chown(to_path, 0, 0); \
chmod(to_path, 755); \
} while (0)

#define UICACHE_PATH "/var/containers/Bundle/Application/uicache"

COPY_RESOURCE("uicache", UICACHE_PATH);

NSArray* rootApps = [fileMgr contentsOfDirectoryAtPath:@"/Applications" error:nil];

if (rootApps == nil) {
rootApps = [NSArray init];
}

NSArray* mntApps = [fileMgr contentsOfDirectoryAtPath:SPICE_MOUNT_APPLICATIONS error:nil];

if (mntApps == nil) {
mntApps = [NSArray init];
}

NSMutableSet* apps = [NSMutableSet setWithArray:rootApps];
[apps minusSet:[NSMutableSet setWithArray:mntApps]];

if (apps.count > 0) {
char *args[2 * apps.count + 2];
args[0] = UICACHE_PATH;

size_t i = 0;

for (NSString* app in apps) {
LOG("Unregistering %@", app);
args[++i] = "-u";
args[++i] = strdup([[NSString stringWithFormat:@"/Applications/%@", app] UTF8String]);
}

args[++i] = NULL;

int status = execprog(UICACHE_PATH, (const char**)args);
if (status != 0) {
LOG("posix_spawn failed: %d", status);
}
}

unmount(SPICE_MOUNT_POINT, 0);
rmdir(SPICE_MOUNT_POINT);
unlink(UICACHE_PATH);

// Restore creds
wk64(our_proc + OFFSET_PROC_P_UCRED, our_ucred);

// note to self: 0x400 causes hard reboot w/o flushin any fs shit
// without this, snapshot rename won't come into place
LOG("Restore successful. Reboot is required.");
reboot(0);

return KERN_SUCCESS;

fail:
// Restore creds
wk64(our_proc + OFFSET_PROC_P_UCRED, our_ucred);

return KERN_FAILURE;
}
#endif
2 changes: 1 addition & 1 deletion src/shared/utils.m
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ int execprog(const char *prog, const char* args[]) {
return rv;
}

pid_t pd;
pid_t pd = 0;
if ((rv = posix_spawn(&pd, prog, &child_fd_actions, NULL, (char**)args, NULL))) {
printf("posix_spawn error: %d (%s)\n", rv, strerror(rv));
return rv;
Expand Down

0 comments on commit d40f52f

Please sign in to comment.