Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
khanhduytran0 committed Aug 6, 2024
1 parent 36b39ea commit 350b99d
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 133 deletions.
4 changes: 2 additions & 2 deletions LCAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ @implementation LCAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UIViewController *viewController;
if ([NSBundle.mainBundle.executablePath.lastPathComponent isEqualToString:@"JITLessSetup"]) {
viewController = [[LCJITLessSetupViewController alloc] init];
viewController = [LCJITLessSetupViewController new];
_rootViewController = [[UINavigationController alloc] initWithRootViewController:viewController];
} else {
_rootViewController = [[LCTabBarController alloc] init];
_rootViewController = [LCTabBarController new];
}
_window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
_window.rootViewController = _rootViewController;
Expand Down
2 changes: 1 addition & 1 deletion AppInfo.h → LCAppInfo.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface AppInfo : NSObject {
@interface LCAppInfo : NSObject {
NSMutableDictionary* _info;
NSString* _bundlePath;
}
Expand Down
4 changes: 2 additions & 2 deletions AppInfo.m → LCAppInfo.m
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "AppInfo.h"
#import "LCAppInfo.h"

@implementation AppInfo
@implementation LCAppInfo
- (instancetype)initWithBundlePath:(NSString*)bundlePath {
self = [super init];
if(self) {
Expand Down
5 changes: 5 additions & 0 deletions LCAppListViewController.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#import <UIKit/UIKit.h>

@interface LCAppListViewController : UITableViewController <UIDocumentPickerDelegate>
@property(nonatomic) NSString* acError;
@end
152 changes: 35 additions & 117 deletions LCRootViewController.m → LCAppListViewController.m
Original file line number Diff line number Diff line change
@@ -1,82 +1,24 @@
#import <CommonCrypto/CommonDigest.h>
#import <SafariServices/SafariServices.h>
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
#import "LCRootViewController.h"
@import CommonCrypto;
@import Darwin;
@import MachO;
@import SafariServices;
@import UniformTypeIdentifiers;

#import "LCAppInfo.h"
#import "LCAppListViewController.h"
#import "LCUtils.h"
#import "MBRoundProgressView.h"
#import "UIKitPrivate.h"
#import "UIViewController+LCAlert.h"
#import "unarchive.h"
#import "AppInfo.h"

/*
#include <libgen.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include <sys/mman.h>
#include <sys/stat.h>

static uint32_t rnd32(uint32_t v, uint32_t r) {
r--;
return (v + r) & ~r;
}

static void insertDylibCommand(uint32_t cmd, const char *path, struct mach_header_64 *header) {
const char *name = cmd==LC_ID_DYLIB ? basename((char *)path) : path;
struct dylib_command *dylib = (struct dylib_command *)(sizeof(struct mach_header_64) + (void *)header+header->sizeofcmds);
dylib->cmd = cmd;
dylib->cmdsize = sizeof(struct dylib_command) + rnd32((uint32_t)strlen(name) + 1, 8);
dylib->dylib.name.offset = sizeof(struct dylib_command);
dylib->dylib.compatibility_version = 0x10000;
dylib->dylib.current_version = 0x10000;
dylib->dylib.timestamp = 2;
strncpy((void *)dylib + dylib->dylib.name.offset, name, strlen(name));
header->ncmds++;
header->sizeofcmds += dylib->cmdsize;
}

static void patchExecSlice(const char *path, struct mach_header_64 *header) {
uint8_t *imageHeaderPtr = (uint8_t*)header + sizeof(struct mach_header_64);

// Literally convert an executable to a dylib
if (header->magic == MH_MAGIC_64) {
//assert(header->flags & MH_PIE);
header->filetype = MH_DYLIB;
header->flags |= MH_NO_REEXPORTED_DYLIBS;
header->flags &= ~MH_PIE;
}

BOOL hasDylibCommand = NO,
hasLoaderCommand = NO;
const char *tweakLoaderPath = "@loader_path/../../Tweaks/TweakLoader.dylib";
struct load_command *command = (struct load_command *)imageHeaderPtr;
for(int i = 0; i < header->ncmds > 0; i++) {
if(command->cmd == LC_ID_DYLIB) {
hasDylibCommand = YES;
} else if(command->cmd == LC_LOAD_DYLIB) {
struct dylib_command *dylib = (struct dylib_command *)command;
char *dylibName = (void *)dylib + dylib->dylib.name.offset;
if (!strncmp(dylibName, tweakLoaderPath, strlen(tweakLoaderPath))) {
hasLoaderCommand = YES;
}
}
command = (struct load_command *)((void *)command + command->cmdsize);
}
if (!hasDylibCommand) {
insertDylibCommand(LC_ID_DYLIB, path, header);
}
if (!hasLoaderCommand) {
insertDylibCommand(LC_LOAD_DYLIB, tweakLoaderPath, header);
}

// Patch __PAGEZERO to map just a single zero page, fixing "out of address space"
struct segment_command_64 *seg = (struct segment_command_64 *)imageHeaderPtr;
assert(seg->cmd == LC_SEGMENT_64);
if (seg->vmaddr == 0) {
assert(seg->vmsize == 0x100000000);
seg->vmaddr = 0x100000000 - 0x4000;
seg->vmsize = 0x4000;
}
}
*/

@implementation NSURL(hack)
- (BOOL)safari_isHTTPFamilyURL {
Expand All @@ -85,42 +27,14 @@ - (BOOL)safari_isHTTPFamilyURL {
}
@end

@interface LCRootViewController ()
@interface LCAppListViewController ()
@property(atomic) NSMutableArray<NSString *> *objects;
@property(nonatomic) NSString *bundlePath, *docPath, *tweakPath;

@property(nonatomic) MBRoundProgressView *progressView;
@end

@implementation LCRootViewController

- (void)patchExecutable:(const char *)path {
int fd = open(path, O_RDWR, (mode_t)0600);
struct stat s;
fstat(fd, &s);
void *map = mmap(NULL, s.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

uint32_t magic = *(uint32_t *)map;
if (magic == FAT_CIGAM) {
// Find compatible slice
struct fat_header *header = (struct fat_header *)map;
struct fat_arch *arch = (struct fat_arch *)(map + sizeof(struct fat_header));
for (int i = 0; i < OSSwapInt32(header->nfat_arch); i++) {
if (OSSwapInt32(arch->cputype) == CPU_TYPE_ARM64) {
patchExecSlice(path, (struct mach_header_64 *)(map + OSSwapInt32(arch->offset)));
}
arch = (struct fat_arch *)((void *)arch + sizeof(struct fat_arch));
}
} else if (magic == MH_MAGIC_64) {
patchExecSlice(path, (struct mach_header_64 *)map);
} else {
[self showDialogTitle:@"Error" message:@"32-bit app is not supported"];
}

msync(map, s.st_size, MS_SYNC);
munmap(map, s.st_size);
close(fd);
}
@implementation LCAppListViewController

- (void)loadView {
[super loadView];
Expand Down Expand Up @@ -191,16 +105,16 @@ - (NSString *)performInstallIPA:(NSURL *)url progress:(NSProgress *)progress {
extract(url.path, temp, progress);
[url stopAccessingSecurityScopedResource];

NSArray* PayloadContents = [[fm contentsOfDirectoryAtPath:[temp stringByAppendingPathComponent: @"Payload"] error:nil] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
NSArray* payloadContents = [[fm contentsOfDirectoryAtPath:[temp stringByAppendingPathComponent: @"Payload"] error:nil] filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id object, NSDictionary *bindings) {
return [object hasSuffix:@".app"];
}]];
if (!PayloadContents.firstObject) {
if (!payloadContents.firstObject) {
return @"App bundle not found";
}

NSString *AppName = PayloadContents[0];
NSString *oldAppName = AppName;
NSString *outPath = [self.bundlePath stringByAppendingPathComponent: AppName];
NSString *appName = payloadContents[0];
NSString *oldAppName = appName;
NSString *outPath = [self.bundlePath stringByAppendingPathComponent: appName];

__block int selectedAction = -1;
if ([fm fileExistsAtPath:outPath isDirectory:nil]) {
Expand All @@ -213,7 +127,7 @@ - (NSString *)performInstallIPA:(NSURL *)url progress:(NSProgress *)progress {
id handler = ^(UIAlertAction *action) {
selectedAction = [alert.actions indexOfObject:action];
if (selectedAction == 0) { // Replace
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.objects indexOfObject:AppName] inSection:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[self.objects indexOfObject:appName] inSection:0];
[self.objects removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
Expand All @@ -228,7 +142,7 @@ - (NSString *)performInstallIPA:(NSURL *)url progress:(NSProgress *)progress {
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}

AppInfo* appInfo = [[AppInfo alloc] initWithBundlePath:outPath];
LCAppInfo* appInfo = [[LCAppInfo alloc] initWithBundlePath:outPath];
NSString *dataUUID = appInfo.dataUUID;
NSString *tweakFolder = appInfo.tweakFolder ?: @"";
switch (selectedAction) {
Expand All @@ -238,16 +152,16 @@ - (NSString *)performInstallIPA:(NSURL *)url progress:(NSProgress *)progress {
case 2: // Keep both, don't share data
dataUUID = NSUUID.UUID.UUIDString;
case 1: // Keep both, share data
AppName = [NSString stringWithFormat:@"%@%ld.app", [AppName substringToIndex:AppName.length-4], (long)CFAbsoluteTimeGetCurrent()];
outPath = [self.bundlePath stringByAppendingPathComponent:AppName];
appName = [NSString stringWithFormat:@"%@%ld.app", [appName substringToIndex:appName.length-4], (long)CFAbsoluteTimeGetCurrent()];
outPath = [self.bundlePath stringByAppendingPathComponent:appName];
break;
}
NSString *payloadPath = [temp stringByAppendingPathComponent:@"Payload"];
if (selectedAction != 3) { // Did not cancel
self.objects[0] = AppName;
self.objects[0] = appName;
[fm moveItemAtPath:[payloadPath stringByAppendingPathComponent:oldAppName] toPath:outPath error:&error];
// Reconstruct AppInfo with the new Info.plist
appInfo = [[AppInfo alloc] initWithBundlePath:outPath];
appInfo = [[LCAppInfo alloc] initWithBundlePath:outPath];
// Write data UUID
appInfo.dataUUID = dataUUID;
appInfo.tweakFolder = tweakFolder;
Expand Down Expand Up @@ -346,10 +260,9 @@ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"Cell"];
cell.preservesSuperviewLayoutMargins = NO;
cell.separatorInset = UIEdgeInsetsZero;
cell.layoutMargins = UIEdgeInsetsZero;
Expand All @@ -370,7 +283,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N
[cell.imageView addSubview:self.progressView];
return cell;
}
AppInfo* appInfo = [[AppInfo alloc] initWithBundlePath: [NSString stringWithFormat:@"%@/%@", self.bundlePath, self.objects[indexPath.row]]];
LCAppInfo* appInfo = [[LCAppInfo alloc] initWithBundlePath: [NSString stringWithFormat:@"%@/%@", self.bundlePath, self.objects[indexPath.row]]];
cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ - %@\n%@", [appInfo version], [appInfo bundleIdentifier], [appInfo dataUUID]];
cell.textLabel.text = [appInfo displayName];
cell.imageView.image = [[appInfo icon] _imageWithSize:CGSizeMake(60, 60)];
Expand All @@ -382,10 +295,10 @@ - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPa
}

- (void)deleteAppAtIndexPath:(NSIndexPath *)indexPath {
AppInfo* appInfo = [[AppInfo alloc] initWithBundlePath: [NSString stringWithFormat:@"%@/%@", self.bundlePath, self.objects[indexPath.row]]];
LCAppInfo* appInfo = [[LCAppInfo alloc] initWithBundlePath: [NSString stringWithFormat:@"%@/%@", self.bundlePath, self.objects[indexPath.row]]];
UIAlertController* uninstallAlert = [UIAlertController alertControllerWithTitle:@"Confirm Uninstallation" message:[NSString stringWithFormat:@"Are you sure you want to uninstall %@?", [appInfo displayName]] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* uninstallApp = [UIAlertAction actionWithTitle:@"Uninstall" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action) {
NSError *error = nil;
NSError *error = nil;
[[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@/%@", self.bundlePath, self.objects[indexPath.row]] error:&error];
if (error) {
[self showDialogTitle:@"Error" message:error.localizedDescription];
Expand Down Expand Up @@ -443,7 +356,12 @@ - (void)patchExecAndSignIfNeed:(NSIndexPath *)indexPath shouldSort:(BOOL)sortNam
int currentPatchRev = 5;
if ([info[@"LCPatchRevision"] intValue] < currentPatchRev) {
NSString *execPath = [NSString stringWithFormat:@"%@/%@", appPath, info[@"CFBundleExecutable"]];
[self patchExecutable:execPath.UTF8String];
NSString *error;
LCPatchExecutable(execPath.UTF8String, &error);
if (error) {
[self showDialogTitle:@"Error" message:error];
return;
}
info[@"LCPatchRevision"] = @(currentPatchRev);
[info writeToFile:infoPath atomically:YES];
}
Expand Down Expand Up @@ -529,7 +447,7 @@ - (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuCo
if (self.objects[indexPath.row].length == 0) return nil;

NSFileManager *fm = NSFileManager.defaultManager;
AppInfo* appInfo = [[AppInfo alloc] initWithBundlePath:[NSString stringWithFormat:@"%@/%@", self.bundlePath, self.objects[indexPath.row]]];
LCAppInfo* appInfo = [[LCAppInfo alloc] initWithBundlePath:[NSString stringWithFormat:@"%@/%@", self.bundlePath, self.objects[indexPath.row]]];

NSString *dataPath = [NSString stringWithFormat:@"%@/Data/Application", self.docPath];
NSArray *dataFolderNames = [fm contentsOfDirectoryAtPath:dataPath error:nil];
Expand Down
94 changes: 94 additions & 0 deletions LCMachOUtils.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
@import Darwin;
@import Foundation;
@import MachO;

static uint32_t rnd32(uint32_t v, uint32_t r) {
r--;
return (v + r) & ~r;
}

static void insertDylibCommand(uint32_t cmd, const char *path, struct mach_header_64 *header) {
const char *name = cmd==LC_ID_DYLIB ? basename((char *)path) : path;
struct dylib_command *dylib = (struct dylib_command *)(sizeof(struct mach_header_64) + (void *)header+header->sizeofcmds);
dylib->cmd = cmd;
dylib->cmdsize = sizeof(struct dylib_command) + rnd32((uint32_t)strlen(name) + 1, 8);
dylib->dylib.name.offset = sizeof(struct dylib_command);
dylib->dylib.compatibility_version = 0x10000;
dylib->dylib.current_version = 0x10000;
dylib->dylib.timestamp = 2;
strncpy((void *)dylib + dylib->dylib.name.offset, name, strlen(name));
header->ncmds++;
header->sizeofcmds += dylib->cmdsize;
}

static void patchExecSlice(const char *path, struct mach_header_64 *header) {
uint8_t *imageHeaderPtr = (uint8_t*)header + sizeof(struct mach_header_64);

// Literally convert an executable to a dylib
if (header->magic == MH_MAGIC_64) {
//assert(header->flags & MH_PIE);
header->filetype = MH_DYLIB;
header->flags |= MH_NO_REEXPORTED_DYLIBS;
header->flags &= ~MH_PIE;
}

BOOL hasDylibCommand = NO,
hasLoaderCommand = NO;
const char *tweakLoaderPath = "@loader_path/../../Tweaks/TweakLoader.dylib";
struct load_command *command = (struct load_command *)imageHeaderPtr;
for(int i = 0; i < header->ncmds > 0; i++) {
if(command->cmd == LC_ID_DYLIB) {
hasDylibCommand = YES;
} else if(command->cmd == LC_LOAD_DYLIB) {
struct dylib_command *dylib = (struct dylib_command *)command;
char *dylibName = (void *)dylib + dylib->dylib.name.offset;
if (!strncmp(dylibName, tweakLoaderPath, strlen(tweakLoaderPath))) {
hasLoaderCommand = YES;
}
}
command = (struct load_command *)((void *)command + command->cmdsize);
}
if (!hasDylibCommand) {
insertDylibCommand(LC_ID_DYLIB, path, header);
}
if (!hasLoaderCommand) {
insertDylibCommand(LC_LOAD_DYLIB, tweakLoaderPath, header);
}

// Patch __PAGEZERO to map just a single zero page, fixing "out of address space"
struct segment_command_64 *seg = (struct segment_command_64 *)imageHeaderPtr;
assert(seg->cmd == LC_SEGMENT_64);
if (seg->vmaddr == 0) {
assert(seg->vmsize == 0x100000000);
seg->vmaddr = 0x100000000 - 0x4000;
seg->vmsize = 0x4000;
}
}

void LCPatchExecutable(const char *path, NSString **error) {
int fd = open(path, O_RDWR, (mode_t)0600);
struct stat s;
fstat(fd, &s);
void *map = mmap(NULL, s.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

uint32_t magic = *(uint32_t *)map;
if (magic == FAT_CIGAM) {
// Find compatible slice
struct fat_header *header = (struct fat_header *)map;
struct fat_arch *arch = (struct fat_arch *)(map + sizeof(struct fat_header));
for (int i = 0; i < OSSwapInt32(header->nfat_arch); i++) {
if (OSSwapInt32(arch->cputype) == CPU_TYPE_ARM64) {
patchExecSlice(path, (struct mach_header_64 *)(map + OSSwapInt32(arch->offset)));
}
arch = (struct fat_arch *)((void *)arch + sizeof(struct fat_arch));
}
} else if (magic == MH_MAGIC_64) {
patchExecSlice(path, (struct mach_header_64 *)map);
} else {
*error = @"32-bit app is not supported";
}

msync(map, s.st_size, MS_SYNC);
munmap(map, s.st_size);
close(fd);
}
5 changes: 0 additions & 5 deletions LCRootViewController.h

This file was deleted.

Loading

0 comments on commit 350b99d

Please sign in to comment.