2022-09-02 23:19:48 +08:00
|
|
|
@import Foundation;
|
|
|
|
@import CoreServices;
|
|
|
|
#import "CoreServices.h"
|
|
|
|
#import <objc/runtime.h>
|
|
|
|
#import "dlfcn.h"
|
2022-11-21 01:27:14 +08:00
|
|
|
#import <TSUtil.h>
|
2022-09-02 23:19:48 +08:00
|
|
|
|
2022-09-04 00:49:53 +08:00
|
|
|
// uicache on steroids
|
|
|
|
|
2022-10-19 03:38:43 +08:00
|
|
|
extern NSSet<NSString*>* immutableAppBundleIdentifiers(void);
|
2022-09-06 03:39:17 +08:00
|
|
|
extern NSDictionary* dumpEntitlementsFromBinaryAtPath(NSString* binaryPath);
|
2022-09-04 00:49:53 +08:00
|
|
|
|
|
|
|
NSDictionary* constructGroupsContainersForEntitlements(NSDictionary* entitlements, BOOL systemGroups)
|
|
|
|
{
|
|
|
|
if(!entitlements) return nil;
|
|
|
|
|
|
|
|
NSString* entitlementForGroups;
|
|
|
|
NSString* mcmClass;
|
|
|
|
if(systemGroups)
|
|
|
|
{
|
|
|
|
entitlementForGroups = @"com.apple.security.system-groups";
|
|
|
|
mcmClass = @"MCMSystemDataContainer";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
entitlementForGroups = @"com.apple.security.application-groups";
|
|
|
|
mcmClass = @"MCMSharedDataContainer";
|
|
|
|
}
|
|
|
|
|
|
|
|
NSArray* groupIDs = entitlements[entitlementForGroups];
|
|
|
|
if(groupIDs && [groupIDs isKindOfClass:[NSArray class]])
|
|
|
|
{
|
|
|
|
NSMutableDictionary* groupContainers = [NSMutableDictionary new];
|
|
|
|
|
|
|
|
for(NSString* groupID in groupIDs)
|
|
|
|
{
|
|
|
|
MCMContainer* container = [NSClassFromString(mcmClass) containerWithIdentifier:groupID createIfNecessary:YES existed:nil error:nil];
|
|
|
|
if(container.url)
|
|
|
|
{
|
|
|
|
groupContainers[groupID] = container.url.path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return groupContainers.copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOL constructContainerizationForEntitlements(NSDictionary* entitlements)
|
|
|
|
{
|
|
|
|
NSNumber* noContainer = entitlements[@"com.apple.private.security.no-container"];
|
|
|
|
if(noContainer && [noContainer isKindOfClass:[NSNumber class]])
|
|
|
|
{
|
|
|
|
if(noContainer.boolValue)
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NSNumber* containerRequired = entitlements[@"com.apple.private.security.container-required"];
|
|
|
|
if(containerRequired && [containerRequired isKindOfClass:[NSNumber class]])
|
|
|
|
{
|
|
|
|
if(!containerRequired.boolValue)
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSString* constructTeamIdentifierForEntitlements(NSDictionary* entitlements)
|
|
|
|
{
|
|
|
|
NSString* teamIdentifier = entitlements[@"com.apple.developer.team-identifier"];
|
|
|
|
if(teamIdentifier && [teamIdentifier isKindOfClass:[NSString class]])
|
|
|
|
{
|
|
|
|
return teamIdentifier;
|
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSDictionary* constructEnvironmentVariablesForContainerPath(NSString* containerPath)
|
|
|
|
{
|
|
|
|
NSString* tmpDir = [containerPath stringByAppendingPathComponent:@"tmp"];
|
|
|
|
return @{
|
|
|
|
@"CFFIXED_USER_HOME" : containerPath,
|
|
|
|
@"HOME" : containerPath,
|
|
|
|
@"TMPDIR" : tmpDir
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-10-30 06:45:30 +08:00
|
|
|
void registerPath(char* cPath, int unregister, BOOL system)
|
2022-09-02 23:19:48 +08:00
|
|
|
{
|
2022-09-04 00:49:53 +08:00
|
|
|
if(!cPath) return;
|
|
|
|
NSString* path = [NSString stringWithUTF8String:cPath];
|
2022-11-21 01:27:14 +08:00
|
|
|
loadMCMFramework();
|
2022-09-04 00:49:53 +08:00
|
|
|
|
|
|
|
LSApplicationWorkspace* workspace = [LSApplicationWorkspace defaultWorkspace];
|
|
|
|
if(unregister && ![[NSFileManager defaultManager] fileExistsAtPath:path])
|
|
|
|
{
|
|
|
|
LSApplicationProxy* app = [LSApplicationProxy applicationProxyForIdentifier:path];
|
|
|
|
if(app.bundleURL)
|
|
|
|
{
|
|
|
|
path = [app bundleURL].path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
path = [path stringByResolvingSymlinksInPath];
|
|
|
|
|
|
|
|
NSDictionary* appInfoPlist = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingPathComponent:@"Info.plist"]];
|
|
|
|
NSString* appBundleID = [appInfoPlist objectForKey:@"CFBundleIdentifier"];
|
|
|
|
|
2022-10-19 03:38:43 +08:00
|
|
|
if([immutableAppBundleIdentifiers() containsObject:appBundleID.lowercaseString]) return;
|
2022-10-18 02:30:49 +08:00
|
|
|
|
2022-09-04 00:49:53 +08:00
|
|
|
if(appBundleID && !unregister)
|
|
|
|
{
|
|
|
|
MCMContainer* appContainer = [NSClassFromString(@"MCMAppDataContainer") containerWithIdentifier:appBundleID createIfNecessary:YES existed:nil error:nil];
|
|
|
|
NSString* containerPath = [appContainer url].path;
|
|
|
|
|
|
|
|
NSMutableDictionary* dictToRegister = [NSMutableDictionary dictionary];
|
|
|
|
|
|
|
|
// Add entitlements
|
|
|
|
|
|
|
|
NSString* appExecutablePath = [path stringByAppendingPathComponent:appInfoPlist[@"CFBundleExecutable"]];
|
2022-09-06 03:39:17 +08:00
|
|
|
NSDictionary* entitlements = dumpEntitlementsFromBinaryAtPath(appExecutablePath);
|
2022-09-04 00:49:53 +08:00
|
|
|
if(entitlements)
|
|
|
|
{
|
|
|
|
dictToRegister[@"Entitlements"] = entitlements;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Misc
|
|
|
|
|
2022-10-30 06:45:30 +08:00
|
|
|
dictToRegister[@"ApplicationType"] = system ? @"System" : @"User";
|
2022-09-04 00:49:53 +08:00
|
|
|
dictToRegister[@"CFBundleIdentifier"] = appBundleID;
|
|
|
|
dictToRegister[@"CodeInfoIdentifier"] = appBundleID;
|
|
|
|
dictToRegister[@"CompatibilityState"] = @0;
|
|
|
|
if(containerPath)
|
|
|
|
{
|
|
|
|
dictToRegister[@"Container"] = containerPath;
|
|
|
|
dictToRegister[@"EnvironmentVariables"] = constructEnvironmentVariablesForContainerPath(containerPath);
|
|
|
|
}
|
2022-11-20 22:33:54 +08:00
|
|
|
dictToRegister[@"IsDeletable"] = @NO;
|
2022-09-04 00:49:53 +08:00
|
|
|
dictToRegister[@"Path"] = path;
|
|
|
|
dictToRegister[@"IsContainerized"] = @(constructContainerizationForEntitlements(entitlements));
|
2022-09-05 20:48:06 +08:00
|
|
|
dictToRegister[@"SignerOrganization"] = @"Apple Inc.";
|
|
|
|
dictToRegister[@"SignatureVersion"] = @132352;
|
|
|
|
dictToRegister[@"SignerIdentity"] = @"Apple iPhone OS Application Signing";
|
2022-09-10 02:22:34 +08:00
|
|
|
dictToRegister[@"IsAdHocSigned"] = @YES;
|
|
|
|
dictToRegister[@"LSInstallType"] = @1;
|
2022-09-22 23:38:58 +08:00
|
|
|
dictToRegister[@"HasMIDBasedSINF"] = @0;
|
|
|
|
dictToRegister[@"MissingSINF"] = @0;
|
|
|
|
dictToRegister[@"FamilyID"] = @0;
|
|
|
|
dictToRegister[@"IsOnDemandInstallCapable"] = @0;
|
2022-09-04 00:49:53 +08:00
|
|
|
|
|
|
|
NSString* teamIdentifier = constructTeamIdentifierForEntitlements(entitlements);
|
|
|
|
if(teamIdentifier) dictToRegister[@"TeamIdentifier"] = teamIdentifier;
|
|
|
|
|
|
|
|
// Add group containers
|
|
|
|
|
|
|
|
NSDictionary* appGroupContainers = constructGroupsContainersForEntitlements(entitlements, NO);
|
|
|
|
NSDictionary* systemGroupContainers = constructGroupsContainersForEntitlements(entitlements, NO);
|
|
|
|
NSMutableDictionary* groupContainers = [NSMutableDictionary new];
|
|
|
|
[groupContainers addEntriesFromDictionary:appGroupContainers];
|
|
|
|
[groupContainers addEntriesFromDictionary:systemGroupContainers];
|
|
|
|
if(groupContainers.count)
|
|
|
|
{
|
|
|
|
if(appGroupContainers.count)
|
|
|
|
{
|
|
|
|
dictToRegister[@"HasAppGroupContainers"] = @YES;
|
|
|
|
}
|
|
|
|
if(systemGroupContainers.count)
|
|
|
|
{
|
|
|
|
dictToRegister[@"HasSystemGroupContainers"] = @YES;
|
|
|
|
}
|
|
|
|
dictToRegister[@"GroupContainers"] = groupContainers.copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add plugins
|
|
|
|
|
|
|
|
NSString* pluginsPath = [path stringByAppendingPathComponent:@"PlugIns"];
|
|
|
|
NSArray* plugins = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:pluginsPath error:nil];
|
|
|
|
|
|
|
|
NSMutableDictionary* bundlePlugins = [NSMutableDictionary dictionary];
|
|
|
|
for (NSString* pluginName in plugins)
|
|
|
|
{
|
|
|
|
NSString* pluginPath = [pluginsPath stringByAppendingPathComponent:pluginName];
|
|
|
|
|
|
|
|
NSDictionary* pluginInfoPlist = [NSDictionary dictionaryWithContentsOfFile:[pluginPath stringByAppendingPathComponent:@"Info.plist"]];
|
|
|
|
NSString* pluginBundleID = [pluginInfoPlist objectForKey:@"CFBundleIdentifier"];
|
|
|
|
|
|
|
|
if(!pluginBundleID) continue;
|
|
|
|
MCMContainer* pluginContainer = [NSClassFromString(@"MCMPluginKitPluginDataContainer") containerWithIdentifier:pluginBundleID createIfNecessary:YES existed:nil error:nil];
|
|
|
|
NSString* pluginContainerPath = [pluginContainer url].path;
|
|
|
|
|
|
|
|
NSMutableDictionary* pluginDict = [NSMutableDictionary dictionary];
|
|
|
|
|
|
|
|
// Add entitlements
|
|
|
|
|
|
|
|
NSString* pluginExecutablePath = [pluginPath stringByAppendingPathComponent:pluginInfoPlist[@"CFBundleExecutable"]];
|
2022-09-06 03:39:17 +08:00
|
|
|
NSDictionary* pluginEntitlements = dumpEntitlementsFromBinaryAtPath(pluginExecutablePath);
|
2022-09-04 00:49:53 +08:00
|
|
|
if(pluginEntitlements)
|
|
|
|
{
|
|
|
|
pluginDict[@"Entitlements"] = pluginEntitlements;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Misc
|
|
|
|
|
|
|
|
pluginDict[@"ApplicationType"] = @"PluginKitPlugin";
|
|
|
|
pluginDict[@"CFBundleIdentifier"] = pluginBundleID;
|
|
|
|
pluginDict[@"CodeInfoIdentifier"] = pluginBundleID;
|
|
|
|
pluginDict[@"CompatibilityState"] = @0;
|
|
|
|
if(pluginContainerPath)
|
|
|
|
{
|
|
|
|
pluginDict[@"Container"] = pluginContainerPath;
|
|
|
|
pluginDict[@"EnvironmentVariables"] = constructEnvironmentVariablesForContainerPath(pluginContainerPath);
|
|
|
|
}
|
|
|
|
pluginDict[@"Path"] = pluginPath;
|
|
|
|
pluginDict[@"PluginOwnerBundleID"] = appBundleID;
|
|
|
|
pluginDict[@"IsContainerized"] = @(constructContainerizationForEntitlements(pluginEntitlements));
|
2022-09-05 20:48:06 +08:00
|
|
|
pluginDict[@"SignerOrganization"] = @"Apple Inc.";
|
|
|
|
pluginDict[@"SignatureVersion"] = @132352;
|
|
|
|
pluginDict[@"SignerIdentity"] = @"Apple iPhone OS Application Signing";
|
2022-09-04 00:49:53 +08:00
|
|
|
|
|
|
|
NSString* pluginTeamIdentifier = constructTeamIdentifierForEntitlements(pluginEntitlements);
|
|
|
|
if(pluginTeamIdentifier) pluginDict[@"TeamIdentifier"] = pluginTeamIdentifier;
|
|
|
|
|
|
|
|
// Add plugin group containers
|
|
|
|
|
|
|
|
NSDictionary* pluginAppGroupContainers = constructGroupsContainersForEntitlements(pluginEntitlements, NO);
|
|
|
|
NSDictionary* pluginSystemGroupContainers = constructGroupsContainersForEntitlements(pluginEntitlements, NO);
|
|
|
|
NSMutableDictionary* pluginGroupContainers = [NSMutableDictionary new];
|
|
|
|
[pluginGroupContainers addEntriesFromDictionary:pluginAppGroupContainers];
|
|
|
|
[pluginGroupContainers addEntriesFromDictionary:pluginSystemGroupContainers];
|
|
|
|
if(pluginGroupContainers.count)
|
|
|
|
{
|
|
|
|
if(pluginAppGroupContainers.count)
|
|
|
|
{
|
|
|
|
pluginDict[@"HasAppGroupContainers"] = @YES;
|
|
|
|
}
|
|
|
|
if(pluginSystemGroupContainers.count)
|
|
|
|
{
|
|
|
|
pluginDict[@"HasSystemGroupContainers"] = @YES;
|
|
|
|
}
|
|
|
|
pluginDict[@"GroupContainers"] = pluginGroupContainers.copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
[bundlePlugins setObject:pluginDict forKey:pluginBundleID];
|
|
|
|
}
|
|
|
|
[dictToRegister setObject:bundlePlugins forKey:@"_LSBundlePlugins"];
|
|
|
|
|
|
|
|
if(![workspace registerApplicationDictionary:dictToRegister])
|
|
|
|
{
|
|
|
|
NSLog(@"Error: Unable to register %@", path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NSURL* url = [NSURL fileURLWithPath:path];
|
|
|
|
if(![workspace unregisterApplication:url])
|
|
|
|
{
|
|
|
|
NSLog(@"Error: Unable to unregister %@", path);
|
|
|
|
}
|
|
|
|
}
|
2022-09-06 03:39:17 +08:00
|
|
|
}
|