diff --git a/LiveContainerSwiftUI/LCAppListView.swift b/LiveContainerSwiftUI/LCAppListView.swift
index dae773a..8dac48e 100644
--- a/LiveContainerSwiftUI/LCAppListView.swift
+++ b/LiveContainerSwiftUI/LCAppListView.swift
@@ -579,7 +579,7 @@ struct LCAppListView : View, LCAppBannerDelegate, LCAppModelDelegate {
return
}
- if !installUrl.startAccessingSecurityScopedResource() {
+ if !FileManager.default.isReadableFile(atPath: installUrl.path) && !installUrl.startAccessingSecurityScopedResource() {
errorInfo = "lc.appList.ipaAccessError".loc
errorShow = true
return
diff --git a/LiveContainerSwiftUI/Localizable.xcstrings b/LiveContainerSwiftUI/Localizable.xcstrings
index f91207e..56f7e02 100644
--- a/LiveContainerSwiftUI/Localizable.xcstrings
+++ b/LiveContainerSwiftUI/Localizable.xcstrings
@@ -3216,7 +3216,7 @@
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Install another LiveContainer"
+ "value" : "Install Another LiveContainer"
}
},
"zh_CN" : {
@@ -3267,7 +3267,7 @@
"en" : {
"stringUnit" : {
"state" : "translated",
- "value" : "Reinstall another LiveContainer"
+ "value" : "Reinstall Another LiveContainer"
}
},
"zh_CN" : {
diff --git a/LiveContainerSwiftUI/Shared.swift b/LiveContainerSwiftUI/Shared.swift
index 721a4bb..aaebe5b 100644
--- a/LiveContainerSwiftUI/Shared.swift
+++ b/LiveContainerSwiftUI/Shared.swift
@@ -793,15 +793,44 @@ extension LCUtils {
onServerMessage?("Please make sure the VPN is connected if the server is not in your local network.")
do {
+
+ onServerMessage?("Contacting JitStreamer-EB server at \(JITStresmerEBAddress)...")
+
+ let session = URLSession.shared
+ let decoder = JSONDecoder()
+
+ let mountStatusUrlStr = "\(JITStresmerEBAddress)/mount"
+ guard let mountStatusUrl = URL(string: mountStatusUrlStr) else { return false }
+ let mountRequest = URLRequest(url: mountStatusUrl)
+
+ // check mount status
+ onServerMessage?("Checking mount status...")
+ let (mountData, mountResponse) = try await session.asyncRequest(request: mountRequest)
+ guard let mountData else {
+ onServerMessage?("Failed to mount status from server!")
+ return false
+ }
+ let mountResponseObj = try decoder.decode(JITStreamerEBMountResponse.self, from: mountData)
+ guard mountResponseObj.ok else {
+ onServerMessage?(mountResponseObj.error ?? "Mounting failed with unknown error.")
+ return false
+ }
+ if mountResponseObj.mounting {
+ onServerMessage?("Your device is currently mounting the developer disk image. Leave your device on and connected. Once this finishes, you can run JitStreamer again.")
+ onServerMessage?("Check \(JITStresmerEBAddress)/mount_status for mounting status.")
+ return false
+ }
+
+ // send launch_app request
let launchJITUrlStr = "\(JITStresmerEBAddress)/launch_app/\(Bundle.main.bundleIdentifier ?? "")"
guard let launchJITUrl = URL(string: launchJITUrlStr) else { return false }
- let session = URLSession.shared
+
- onServerMessage?("Contacting JitStreamer-EB server at \(JITStresmerEBAddress)...")
+ onServerMessage?("Sending launch request...")
let request1 = URLRequest(url: launchJITUrl)
let (data, response) = try await session.asyncRequest(request: request1)
- let decoder = JSONDecoder()
+
guard let data else {
onServerMessage?("Failed to retrieve data from server!")
return false
@@ -813,11 +842,7 @@ extension LCUtils {
return false
}
- if launchAppResponse.mounting {
- onServerMessage?("Your device is currently mounting the developer disk image. Leave your device on and connected. Once this finishes, you can run JitStreamer again.")
- } else {
- onServerMessage?("Your app will launch soon! You are position \(launchAppResponse.position ?? -1) in the queue.")
- }
+ onServerMessage?("Your app will launch soon! You are position \(launchAppResponse.position ?? -1) in the queue.")
// start polling status
let statusUrlStr = "\(JITStresmerEBAddress)/status"
@@ -841,23 +866,10 @@ extension LCUtils {
}
if statusResponse.done {
onServerMessage?("Server done.")
- if launchAppResponse.mounting {
- onServerMessage?("Run the app again to enable JIT.")
- }
-
return true
}
- if statusResponse.in_progress {
- if launchAppResponse.mounting {
- onServerMessage?("JIT enabling in progress. (Attempt \(i + 1)/\(maxTries))")
- } else {
- onServerMessage?("Mounting in progress. (Attempt \(i + 1)/\(maxTries))")
- }
-
- } else {
- onServerMessage?("Your app will launch soon! You are position \(launchAppResponse.position ?? -1) in the queue. (Attempt \(i + 1)/\(maxTries))")
- }
+ onServerMessage?("Your app will launch soon! You are position \(launchAppResponse.position ?? -1) in the queue. (Attempt \(i + 1)/\(maxTries))")
}
@@ -876,7 +888,6 @@ extension LCUtils {
struct JITStreamerEBLaunchAppResponse : Codable {
let ok: Bool
let launching: Bool
- let mounting: Bool
let position: Int?
let error: String?
}
@@ -884,7 +895,12 @@ struct JITStreamerEBLaunchAppResponse : Codable {
struct JITStreamerEBStatusResponse : Codable {
let ok: Bool
let done: Bool
- let in_progress: Bool
let position: Int?
let error: String?
}
+
+struct JITStreamerEBMountResponse : Codable {
+ let ok: Bool
+ let mounting: Bool
+ let error: String?
+}
diff --git a/Makefile b/Makefile
index 961f6a0..69c7e9a 100644
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,7 @@ export CONFIG_COMMIT = $(shell git log --oneline | sed '2,10000000d' | cut -b 1-
# Build the app
APPLICATION_NAME = LiveContainer
-$(APPLICATION_NAME)_FILES = dyld_bypass_validation.m main.m utils.m LCSharedUtils.m NSUserDefaults.m SecItem.m fishhook/fishhook.c
+$(APPLICATION_NAME)_FILES = dyld_bypass_validation.m main.m utils.m LCSharedUtils.m Tweaks/NSUserDefaults.m Tweaks/SecItem.m Tweaks/NSBundle+FixCydiaSubstrate.m Tweaks/Dyld.m fishhook/fishhook.c
$(APPLICATION_NAME)_CODESIGN_FLAGS = -Sentitlements.xml
$(APPLICATION_NAME)_CFLAGS = -fobjc-arc
$(APPLICATION_NAME)_LDFLAGS = -e _LiveContainerMain -rpath @loader_path/Frameworks
diff --git a/Resources/Info.plist b/Resources/Info.plist
index e1e5d87..85d4155 100644
--- a/Resources/Info.plist
+++ b/Resources/Info.plist
@@ -42,7 +42,7 @@
CFBundlePackageType
APPL
CFBundleShortVersionString
- 3.2.55
+ 3.2.56
CFBundleSignature
????
CFBundleSupportedPlatforms
@@ -61,7 +61,7 @@
CFBundleVersion
- 3.2.55
+ 3.2.56
LSApplicationCategoryType
public.app-category.games
LSApplicationQueriesSchemes
diff --git a/TweakLoader/Makefile b/TweakLoader/Makefile
index 00537f4..6d20968 100644
--- a/TweakLoader/Makefile
+++ b/TweakLoader/Makefile
@@ -6,7 +6,7 @@ include $(THEOS)/makefiles/common.mk
LIBRARY_NAME = TweakLoader
-TweakLoader_FILES = TweakLoader.m NSBundle+FixCydiaSubstrate.m NSFileManager+GuestHooks.m UIKit+GuestHooks.m utils.m DocumentPicker.m FBSSerialQueue.m ../Localization.m
+TweakLoader_FILES = TweakLoader.m NSFileManager+GuestHooks.m UIKit+GuestHooks.m utils.m DocumentPicker.m FBSSerialQueue.m ../Localization.m
TweakLoader_CFLAGS = -objc-arc
TweakLoader_INSTALL_PATH = /Applications/LiveContainer.app/Frameworks
TweakLoader_PRIVATE_FRAMEWORKS = CoreServices FrontBoard RunningBoardServices
diff --git a/TweakLoader/NSBundle+FixCydiaSubstrate.m b/TweakLoader/NSBundle+FixCydiaSubstrate.m
deleted file mode 100644
index c7bd554..0000000
--- a/TweakLoader/NSBundle+FixCydiaSubstrate.m
+++ /dev/null
@@ -1,29 +0,0 @@
-#import
-#import "utils.h"
-
-__attribute__((constructor))
-static void NSBundleHookInit(void) {
- swizzle(NSBundle.class, @selector(bundlePath), @selector(hook_bundlePath));
- swizzle(NSBundle.class, @selector(executablePath), @selector(hook_executablePath));
-
-}
-
-@implementation NSBundle(FixCydiaSubstrate)
-
-- (NSString *)hook_bundlePath {
- NSString *path = self.hook_bundlePath;
- if ([path hasPrefix:@"/var"]) {
- return [@"/private" stringByAppendingPathComponent:path];
- }
- return path;
-}
-
-- (NSString *)hook_executablePath {
- NSString *path = self.hook_executablePath;
- if ([path hasPrefix:@"/var"]) {
- return [@"/private" stringByAppendingPathComponent:path];
- }
- return path;
-}
-
-@end
diff --git a/Tweaks/Dyld.m b/Tweaks/Dyld.m
new file mode 100644
index 0000000..83ac007
--- /dev/null
+++ b/Tweaks/Dyld.m
@@ -0,0 +1,77 @@
+//
+// Dyld.m
+// LiveContainer
+//
+// Created by s s on 2025/2/7.
+//
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#import "../fishhook/fishhook.h"
+
+uint32_t appMainImageIndex = 0;
+void* appExecutableHandle = 0;
+
+void* (*orig_dlsym)(void * __handle, const char * __symbol);
+uint32_t (*orig_dyld_image_count)(void);
+const struct mach_header* (*orig_dyld_get_image_header)(uint32_t image_index);
+intptr_t (*orig_dyld_get_image_vmaddr_slide)(uint32_t image_index);
+const char* (*orig_dyld_get_image_name)(uint32_t image_index);
+
+static inline int translateImageIndex(int origin) {
+ if(origin == 1) {
+ return appMainImageIndex;
+ }
+ if(origin >= appMainImageIndex) {
+ return origin + 1;
+ }
+ return origin;
+}
+
+
+void* hook_dlsym(void * __handle, const char * __symbol) {
+ if(__handle == (void*)RTLD_MAIN_ONLY) {
+ if(strcmp(__symbol, MH_EXECUTE_SYM) == 0) {
+ return (void*)orig_dyld_get_image_header(appMainImageIndex);
+ }
+ __handle = appExecutableHandle;
+ }
+
+ __attribute__((musttail)) return orig_dlsym(__handle, __symbol);
+}
+
+uint32_t hook_dyld_image_count(void) {
+ return orig_dyld_image_count() - 1;
+}
+
+const struct mach_header* hook_dyld_get_image_header(uint32_t image_index) {
+ __attribute__((musttail)) return orig_dyld_get_image_header(translateImageIndex(image_index));
+}
+
+intptr_t hook_dyld_get_image_vmaddr_slide(uint32_t image_index) {
+ __attribute__((musttail)) return orig_dyld_get_image_vmaddr_slide(translateImageIndex(image_index));
+}
+
+const char* hook_dyld_get_image_name(uint32_t image_index) {
+ __attribute__((musttail)) return orig_dyld_get_image_name(translateImageIndex(image_index));
+}
+
+
+
+void DyldHooksInit(void) {
+ // hook dlsym to solve RTLD_MAIN_ONLY, hook other functions to hide LiveContainer itself
+ rebind_symbols((struct rebinding[5]){
+ {"dlsym", (void *)hook_dlsym, (void **)&orig_dlsym},
+ {"_dyld_image_count", (void *)hook_dyld_image_count, (void **)&orig_dyld_image_count},
+ {"_dyld_get_image_header", (void *)hook_dyld_get_image_header, (void **)&orig_dyld_get_image_header},
+ {"_dyld_get_image_vmaddr_slide", (void *)hook_dyld_get_image_vmaddr_slide, (void **)&orig_dyld_get_image_vmaddr_slide},
+ {"_dyld_get_image_name", (void *)hook_dyld_get_image_name, (void **)&orig_dyld_get_image_name},
+ },5);
+}
diff --git a/Tweaks/NSBundle+FixCydiaSubstrate.m b/Tweaks/NSBundle+FixCydiaSubstrate.m
new file mode 100644
index 0000000..0bdf353
--- /dev/null
+++ b/Tweaks/NSBundle+FixCydiaSubstrate.m
@@ -0,0 +1,22 @@
+#import
+#import "Tweaks.h"
+#import
+
+@implementation NSString(LiveContainer)
+- (NSString *)lc_realpath {
+ // stringByResolvingSymlinksInPath does not fully resolve symlink, and some apps will cradh without /private prefix
+ char result[PATH_MAX];
+ realpath(self.fileSystemRepresentation, result);
+ return [NSString stringWithUTF8String:result];
+}
+@end
+@implementation NSBundle(LiveContainer)
+// Built-in initWith* will strip out the /private prefix, which could crash certain apps
+// This initializer replicates +[NSBundle mainBundle] to solve this issue (FIXME: may not work)
+- (instancetype)initWithPathForMainBundle:(NSString *)path {
+ self = [self init];
+ CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:path.lc_realpath];
+ object_setIvar(self, class_getInstanceVariable(self.class, "_cfBundle"), CFBridgingRelease(CFBundleCreate(NULL, url)));
+ return self;
+}
+@end
diff --git a/NSUserDefaults.m b/Tweaks/NSUserDefaults.m
similarity index 100%
rename from NSUserDefaults.m
rename to Tweaks/NSUserDefaults.m
diff --git a/SecItem.m b/Tweaks/SecItem.m
similarity index 99%
rename from SecItem.m
rename to Tweaks/SecItem.m
index 08a6662..9cef96d 100644
--- a/SecItem.m
+++ b/Tweaks/SecItem.m
@@ -8,7 +8,7 @@
#import
#import "utils.h"
#import
-#import "fishhook/fishhook.h"
+#import "../fishhook/fishhook.h"
extern void* (*msHookFunction)(void *symbol, void *hook, void **old);
OSStatus (*orig_SecItemAdd)(CFDictionaryRef attributes, CFTypeRef *result);
diff --git a/Tweaks/Tweaks.h b/Tweaks/Tweaks.h
new file mode 100644
index 0000000..d4b3f30
--- /dev/null
+++ b/Tweaks/Tweaks.h
@@ -0,0 +1,21 @@
+//
+// Tweaks.h
+// LiveContainer
+//
+// Created by s s on 2025/2/7.
+//
+
+void swizzle(Class class, SEL originalAction, SEL swizzledAction);
+
+
+void NUDGuestHooksInit(void);
+void SecItemGuestHooksInit(void);
+void DyldHooksInit(void);
+
+@interface NSBundle(LiveContainer)
+- (instancetype)initWithPathForMainBundle:(NSString *)path;
+@end
+
+
+extern uint32_t appMainImageIndex;
+extern void* appExecutableHandle;
diff --git a/ZSign/macho.cpp b/ZSign/macho.cpp
index 062e832..694b12c 100644
--- a/ZSign/macho.cpp
+++ b/ZSign/macho.cpp
@@ -362,6 +362,6 @@ bool is_64bit_macho(const char *filepath) {
fread(&magic, sizeof(uint32_t), 1, file);
fclose(file);
- // 64-bit Mach-O magic number is 0xfeedfacf
- return magic == 0xfeedfacf;
+ // check 64-bit Mach-O magic number
+ return magic == MH_MAGIC_64 || magic == FAT_CIGAM;
}
diff --git a/control b/control
index bd543a2..1337a62 100644
--- a/control
+++ b/control
@@ -1,6 +1,6 @@
Package: com.kdt.livecontainer
Name: livecontainer
-Version: 3.2.55
+Version: 3.2.56
Architecture: iphoneos-arm
Description: Run iOS app without actually installing it!
Maintainer: khanhduytran0
diff --git a/main.m b/main.m
index f174d9d..0aac82a 100644
--- a/main.m
+++ b/main.m
@@ -15,6 +15,7 @@
#include
#include "TPRO.h"
#import "fishhook/fishhook.h"
+#import "Tweaks/Tweaks.h"
#include
static int (*appMain)(int, char**);
@@ -26,9 +27,6 @@
NSBundle* lcMainBundle;
NSDictionary* guestAppInfo;
-void NUDGuestHooksInit();
-void SecItemGuestHooksInit();
-
@implementation NSUserDefaults(LiveContainer)
+ (instancetype)lcUserDefaults {
return lcUserDefaults;
@@ -203,19 +201,6 @@ static void overwriteExecPath(NSString *bundlePath) {
return (void *)header + entryoff;
}
-uint32_t appMainImageIndex = 0;
-void* appExecutableHandle = 0;
-void* (*orig_dlsym)(void * __handle, const char * __symbol);
-void* new_dlsym(void * __handle, const char * __symbol) {
- if(__handle == (void*)RTLD_MAIN_ONLY) {
- if(strcmp(__symbol, MH_EXECUTE_SYM) == 0) {
- return (void*)_dyld_get_image_header(appMainImageIndex);
- }
- __handle = appExecutableHandle;
- }
-
- __attribute__((musttail)) return orig_dlsym(__handle, __symbol);
-}
static NSString* invokeAppMain(NSString *selectedApp, NSString *selectedContainer, int argc, char *argv[]) {
NSString *appError = nil;
@@ -260,7 +245,7 @@ static void overwriteExecPath(NSString *bundlePath) {
}
}
- NSBundle *appBundle = [[NSBundle alloc] initWithPath:bundlePath];
+ NSBundle *appBundle = [[NSBundle alloc] initWithPathForMainBundle:bundlePath];
if(!appBundle) {
return @"App not found";
@@ -426,8 +411,7 @@ static void overwriteExecPath(NSString *bundlePath) {
uint32_t appIndex = _dyld_image_count();
appMainImageIndex = appIndex;
- // hook dlsym to solve RTLD_MAIN_ONLY
- rebind_symbols((struct rebinding[1]){{"dlsym", (void *)new_dlsym, (void **)&orig_dlsym}},1);
+ DyldHooksInit();
void *appHandle = dlopen(*path, RTLD_LAZY|RTLD_GLOBAL|RTLD_FIRST);
appExecutableHandle = appHandle;
@@ -455,8 +439,8 @@ static void overwriteExecPath(NSString *bundlePath) {
}
NSLog(@"[LCBootstrap] loaded bundle");
- // Find main()
- appMain = getAppEntryPoint(appHandle, appIndex);
+ // Find main(), pass 1 as we hooked _dyld_get_image_header
+ appMain = getAppEntryPoint(appHandle, 1);
if (!appMain) {
appError = @"Could not find the main entry point";
NSLog(@"[LCBootstrap] %@", appError);