Fix first launch and package hook as a signed framework

This commit is contained in:
admin committed 2026-11-12 03:27:18 +08:00
1 parent c1d0293404
commit 97a4fad71f
6 files changed
+147 -20

No files matched your search

+67 -10
View File
@@ -36,6 +36,7 @@ static NSString *WBLocalized(NSString *key)
@"scanning_containers_short": @"Scanning system containers…", @"ls_found": @"LaunchServices database located",
@"init_failed": @"Initialization Failed", @"ok": @"OK",
@"no_group": @"No usable App Group; worse_query cannot run",
@"no_group_hint": @"Re-sign with a certificate and provisioning profile that include an App Group entitlement.",
@"no_ls": @"LaunchServices database not found",
@"no_containers": @"The LaunchServices database returned no containers",
},
@@ -47,6 +48,7 @@ static NSString *WBLocalized(NSString *key)
@"scanning_containers_short": @"正在扫描系统容器…", @"ls_found": @"已定位 LaunchServices 数据库",
@"init_failed": @"初始化失败", @"ok": @"好",
@"no_group": @"未找到可用的 App Group,无法执行 worse_query",
@"no_group_hint": @"请使用包含 App Group 权限的证书和描述文件重新签名。",
@"no_ls": @"未找到 LaunchServices 数据库",
@"no_containers": @"LaunchServices 数据库没有返回容器列表",
},
@@ -780,9 +782,17 @@ static void (*gOrigLoad)(id, SEL);
static NSString *(*gOrigFileSizeString)(id, SEL);
static void WBStartPreparation(void);
static BOOL (*gOrigDidFinishLaunching)(id, SEL, UIApplication *, NSDictionary *);
static CFAbsoluteTime gLaunchStarted;
static BOOL WBHookDidFinishLaunching(id self, SEL _cmd, UIApplication *application, NSDictionary *launchOptions)
{
gLaunchStarted = CFAbsoluteTimeGetCurrent();
// The original app shows a one-time "Message From Developer" alert that
// competes with the launch progress sheet and makes the first run look
// blank. The hook has its own launch status UI, so skip it.
NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;
[defaults setBool:YES forKey:@"FirstLaunch"];
[defaults synchronize];
BOOL result = gOrigDidFinishLaunching ? gOrigDidFinishLaunching(self, _cmd, application, launchOptions) : YES;
WBStartPreparation();
return result;
@@ -790,13 +800,29 @@ static BOOL WBHookDidFinishLaunching(id self, SEL _cmd, UIApplication *applicati
static __weak id gAppsTableViewController;
static WBProgressViewController *gProgressController;
static dispatch_semaphore_t gReadySemaphore;
static NSCondition *gReadyCondition;
static BOOL gPrepared;
static BOOL gPreparing;
static BOOL gWaitingForRoot;
static BOOL gReloadScheduled;
static BOOL WBWaitForRegistry(void)
{
if (WBRegistry.shared.ready) return YES;
if (!gReadyCondition) return NO;
[gReadyCondition lock];
NSDate *deadline = [NSDate dateWithTimeIntervalSinceNow:120];
while (!WBRegistry.shared.ready && !gPrepared && deadline.timeIntervalSinceNow > 0) {
[gReadyCondition waitUntilDate:deadline];
}
BOOL ready = WBRegistry.shared.ready;
[gReadyCondition unlock];
return ready;
}
static NSArray *WBHookAllInstalled(id self, SEL _cmd)
{
if (!WBRegistry.shared.ready) WBWaitForRegistry();
NSArray *proxies = WBRegistry.shared.proxies;
if (WBRegistry.shared.ready && proxies.count) return proxies;
return gOrigAllInstalled ? gOrigAllInstalled(self, _cmd) : @[];
@@ -804,6 +830,7 @@ static NSArray *WBHookAllInstalled(id self, SEL _cmd)
static NSArray *WBHookAllApplications(id self, SEL _cmd)
{
if (!WBRegistry.shared.ready) WBWaitForRegistry();
NSArray *proxies = WBRegistry.shared.proxies;
if (WBRegistry.shared.ready && proxies.count) return proxies;
return gOrigAllApplications ? gOrigAllApplications(self, _cmd) : @[];
@@ -872,9 +899,6 @@ static void WBHookDetailReload(id self, SEL _cmd)
static void WBHookLoad(id self, SEL _cmd)
{
gAppsTableViewController = self;
if (gReadySemaphore && !WBRegistry.shared.ready) {
dispatch_semaphore_wait(gReadySemaphore, dispatch_time(DISPATCH_TIME_NOW, 120 * NSEC_PER_SEC));
}
gOrigLoad(self, _cmd);
}
@@ -1467,7 +1491,7 @@ static void WBInstallHooks(void)
method_setImplementation(didFinish, (IMP)WBHookDidFinishLaunching);
}
gReadySemaphore = dispatch_semaphore_create(0);
gReadyCondition = [NSCondition new];
[[NSNotificationCenter defaultCenter] addObserverForName:@"WBAppSizeDidUpdate" object:nil queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification *note) {
if (gReloadScheduled) return;
gReloadScheduled = YES;
@@ -1661,7 +1685,6 @@ static void WBRunDebugRefresh(void)
static void WBStartPreparation(void)
{
if (gPreparing || gPrepared) return;
gPreparing = YES;
UIWindow *window = UIApplication.sharedApplication.keyWindow;
if (!window) {
@@ -1673,25 +1696,58 @@ static void WBStartPreparation(void)
}
}
UIViewController *root = window.rootViewController;
if (root && !gProgressController) {
if (!root) {
// On a cold launch the window/root view may not exist yet. Wait for it
// instead of running the preparation with no visible progress sheet.
if (!gWaitingForRoot && CFAbsoluteTimeGetCurrent() - gLaunchStarted < 10) {
gWaitingForRoot = YES;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 100 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
gWaitingForRoot = NO;
WBStartPreparation();
});
return;
}
if (!gWaitingForRoot) {
// Last-resort fallback: start even without a presenter.
gPreparing = YES;
[WBRegistry.shared prepareWithProgress:nil completion:^(BOOL ok, NSString *message) {
gPrepared = YES;
[gReadyCondition lock];
[gReadyCondition broadcast];
[gReadyCondition unlock];
}];
}
return;
}
gPreparing = YES;
[root loadViewIfNeeded];
UIViewController *presenter = root;
while (presenter.presentedViewController) presenter = presenter.presentedViewController;
if (!gProgressController) {
gProgressController = [WBProgressViewController new];
gProgressController.modalPresentationStyle = UIModalPresentationOverFullScreen;
[root presentViewController:gProgressController animated:YES completion:nil];
[presenter presentViewController:gProgressController animated:YES completion:nil];
}
[WBRegistry.shared prepareWithProgress:^(double fraction, NSString *status) {
[gProgressController updateFraction:fraction status:status];
} completion:^(BOOL ok, NSString *message) {
gPrepared = YES;
if (gReadySemaphore) dispatch_semaphore_signal(gReadySemaphore);
[gReadyCondition lock];
[gReadyCondition broadcast];
[gReadyCondition unlock];
dispatch_async(dispatch_get_main_queue(), ^{
if (gProgressController) {
[gProgressController dismissViewControllerAnimated:YES completion:nil];
gProgressController = nil;
}
if (!ok && message.length) {
NSString *detail = message;
if ([message containsString:@"App Group"]) {
detail = [message stringByAppendingFormat:@"\n\n%@", WBLocalized(@"no_group_hint")];
}
UIAlertController *alert = [UIAlertController alertControllerWithTitle:WBLocalized(@"init_failed")
message:message
message:detail
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:WBLocalized(@"ok") style:UIAlertActionStyleDefault handler:nil]];
UIWindow *keyWindow = UIApplication.sharedApplication.keyWindow;
@@ -1711,6 +1767,7 @@ __attribute__((constructor))
static void WBHookInit(void)
{
@autoreleasepool {
gLaunchStarted = CFAbsoluteTimeGetCurrent();
WBInstallHooks();
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidFinishLaunchingNotification