Skip to content

Commit

Permalink
SideStore 0.6.0 REQUIRED fix [#36](#36) [khanhduytran0#347](khanhduyt…
Browse files Browse the repository at this point in the history
…ran0#347), 128 keychain access group, add Hide LC from Dyld api option
  • Loading branch information
hugeBlack committed Feb 14, 2025
1 parent 8677ba7 commit 2f74ae6
Show file tree
Hide file tree
Showing 17 changed files with 255 additions and 79 deletions.
2 changes: 1 addition & 1 deletion LiveContainerSwiftUI/LCAppInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ typedef NS_ENUM(NSInteger, LCOrientationLock){
@property bool isLocked;
@property bool isHidden;
@property bool doSymlinkInbox;
//@property bool ignoreDlopenError;
@property bool hideLiveContainer;
@property bool fixBlackScreen;
@property bool dontInjectTweakLoader;
@property bool bypassAssertBarrierOnQueue;
Expand Down
24 changes: 12 additions & 12 deletions LiveContainerSwiftUI/LCAppInfo.m
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ - (instancetype)initWithBundlePath:(NSString*)bundlePath {
@"LCOrientationLock",
@"cachedColor",
@"LCContainers",
@"ignoreDlopenError"
@"hideLiveContainer"
];
for(NSString* key in lcAppInfoKeys) {
_info[key] = _infoPlist[key];
Expand Down Expand Up @@ -451,17 +451,17 @@ - (void)setDoSymlinkInbox:(bool)doSymlinkInbox {

}

//- (bool)ignoreDlopenError {
// if(_info[@"ignoreDlopenError"] != nil) {
// return [_info[@"ignoreDlopenError"] boolValue];
// } else {
// return NO;
// }
//}
//- (void)setIgnoreDlopenError:(bool)ignoreDlopenError {
// _info[@"ignoreDlopenError"] = [NSNumber numberWithBool:ignoreDlopenError];
// [self save];
//}
- (bool)hideLiveContainer {
if(_info[@"hideLiveContainer"] != nil) {
return [_info[@"hideLiveContainer"] boolValue];
} else {
return NO;
}
}
- (void)setHideLiveContainer:(bool)hideLiveContainer {
_info[@"hideLiveContainer"] = [NSNumber numberWithBool:hideLiveContainer];
[self save];
}

- (bool)fixBlackScreen {
if(_info[@"fixBlackScreen"] != nil) {
Expand Down
6 changes: 6 additions & 0 deletions LiveContainerSwiftUI/LCAppListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,7 @@ struct LCAppListView : View, LCAppBannerDelegate, LCAppModelDelegate {
finalNewApp.dataUUID = appToReplace.appInfo.dataUUID
finalNewApp.orientationLock = appToReplace.appInfo.orientationLock
finalNewApp.dontInjectTweakLoader = appToReplace.appInfo.dontInjectTweakLoader
finalNewApp.hideLiveContainer = appToReplace.appInfo.hideLiveContainer
finalNewApp.autoSaveDisabled = false
finalNewApp.save()
}
Expand Down Expand Up @@ -554,6 +555,11 @@ struct LCAppListView : View, LCAppBannerDelegate, LCAppModelDelegate {
}

func installFromUrl(urlStr: String) async {
// ignore any install request if we are installing another app
if self.installprogressVisible {
return
}

if sharedModel.multiLCStatus == 2 {
errorInfo = "lc.appList.manageInPrimaryTip".loc
errorShow = true
Expand Down
22 changes: 11 additions & 11 deletions LiveContainerSwiftUI/LCAppModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class LCAppModel: ObservableObject, Hashable {
@Published var uiUseLCBundleId : Bool
@Published var uiBypassAssertBarrierOnQueue : Bool
@Published var uiSigner : Signer
// @Published var uiIgnoreDlopenError : Bool
@Published var uiHideLiveContainer : Bool
@Published var uiFixBlackScreen : Bool
@Published var uiDontInjectTweakLoader : Bool
@Published var uiOrientationLock : LCOrientationLock
Expand Down Expand Up @@ -59,7 +59,7 @@ class LCAppModel: ObservableObject, Hashable {
self.uiSigner = appInfo.signer
self.uiOrientationLock = appInfo.orientationLock
self.uiUseLCBundleId = appInfo.doUseLCBundleId
// self.uiIgnoreDlopenError = appInfo.ignoreDlopenError
self.uiHideLiveContainer = appInfo.hideLiveContainer
self.uiFixBlackScreen = appInfo.fixBlackScreen
self.uiDontInjectTweakLoader = appInfo.dontInjectTweakLoader

Expand Down Expand Up @@ -92,7 +92,7 @@ class LCAppModel: ObservableObject, Hashable {
uiSelectedContainer = newContainer;
}
appInfo.containers = uiContainers;
newContainer.makeLCContainerInfoPlist(appIdentifier: appInfo.bundleIdentifier()!, keychainGroupId: 0)
newContainer.makeLCContainerInfoPlist(appIdentifier: appInfo.bundleIdentifier()!, keychainGroupId: Int.random(in: 0..<SharedModel.keychainAccessGroupCount))
appInfo.dataUUID = newName
uiDefaultDataFolder = newName
}
Expand Down Expand Up @@ -143,9 +143,9 @@ class LCAppModel: ObservableObject, Hashable {
}
isAppRunning = true
defer {
DispatchQueue.main.async {
Task{ await MainActor.run {
self.isAppRunning = false
}
}}

}
try await signApp(force: true)
Expand All @@ -155,9 +155,9 @@ class LCAppModel: ObservableObject, Hashable {
var signError : String? = nil
var signSuccess = false
defer {
DispatchQueue.main.async {
Task{ await MainActor.run {
self.isSigningInProgress = false
}
}}
}

await withCheckedContinuation({ c in
Expand Down Expand Up @@ -195,16 +195,16 @@ class LCAppModel: ObservableObject, Hashable {
tweakFolderUrl = LCPath.tweakPath.appendingPathComponent(tweakFolder)
}
try await LCUtils.signTweaks(tweakFolderUrl: tweakFolderUrl, force: force, signer: self.appInfo.signer) { p in
DispatchQueue.main.async {
Task{ await MainActor.run {
self.isSigningInProgress = true
}
}}
}

// sign global tweak
try await LCUtils.signTweaks(tweakFolderUrl: LCPath.tweakPath, force: force, signer: self.appInfo.signer) { p in
DispatchQueue.main.async {
Task{ await MainActor.run {
self.isSigningInProgress = true
}
}}
}


Expand Down
42 changes: 19 additions & 23 deletions LiveContainerSwiftUI/LCAppSettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ struct LCAppSettingsView : View{
}
}
}
if(model.uiContainers.count < 3) {
if(model.uiContainers.count < SharedModel.keychainAccessGroupCount) {
Button {
Task{ await createFolder() }
} label: {
Expand Down Expand Up @@ -268,26 +268,22 @@ struct LCAppSettingsView : View{
}
}

// Section {
// Toggle(isOn: $model.uiIgnoreDlopenError) {
// Text("lc.appSettings.ignoreDlopenError".loc)
// }
// .onChange(of: model.uiIgnoreDlopenError, perform: { newValue in
// Task { await setIgnoreDlopenError(newValue) }
// })
// } footer: {
// Text("lc.appSettings.ignoreDlopenErrorDesc".loc)
// }

Section {
Toggle(isOn: $model.uiHideLiveContainer) {
Text("lc.appSettings.hideLiveContainer".loc)
}
.onChange(of: model.uiHideLiveContainer, perform: { newValue in
Task { await setHideLiveContainer(newValue) }
})

Toggle(isOn: $model.uiDontInjectTweakLoader) {
Text("lc.appSettings.dontInjectTweakLoader".loc)
}
.onChange(of: model.uiDontInjectTweakLoader, perform: { newValue in
Task { await setDontInjectTweakLoader(newValue) }
})
} footer: {
Text("lc.appSettings.dontInjectTweakLoaderDesc".loc)
Text("lc.appSettings.hideLiveContainerDesc".loc)
}


Expand Down Expand Up @@ -400,13 +396,13 @@ struct LCAppSettingsView : View{
let newContainer = LCContainer(folderName: newName, name: displayName, isShared: model.uiIsShared, isolateAppGroup: false)
// assign keychain group
var keychainGroupSet : Set<Int> = Set(minimumCapacity: 3)
for i in 0...2 {
for i in 0..<SharedModel.keychainAccessGroupCount {
keychainGroupSet.insert(i)
}
for container in model.uiContainers {
keychainGroupSet.remove(container.keychainGroupId)
}
guard let freeKeyChainGroup = keychainGroupSet.first else {
guard let freeKeyChainGroup = keychainGroupSet.randomElement() else {
errorInfo = "lc.container.notEnoughKeychainGroup".loc
errorShow = true
return
Expand Down Expand Up @@ -531,10 +527,10 @@ struct LCAppSettingsView : View{
model.uiUseLCBundleId = doUseLCBundleId
}

// func setIgnoreDlopenError(_ ignoreDlopenError : Bool) async {
// appInfo.ignoreDlopenError = ignoreDlopenError
// model.uiIgnoreDlopenError = ignoreDlopenError
// }
func setHideLiveContainer(_ hideLiveContainer : Bool) async {
appInfo.hideLiveContainer = hideLiveContainer
model.uiHideLiveContainer = hideLiveContainer
}

func setFixBlackScreen(_ fixBlackScreen : Bool) async {
appInfo.fixBlackScreen = fixBlackScreen
Expand Down Expand Up @@ -652,7 +648,7 @@ extension LCAppSettingsView : LCContainerViewDelegate {

extension LCAppSettingsView : LCSelectContainerViewDelegate {
func addContainers(containers: Set<String>) {
if containers.count + model.uiContainers.count > 3 {
if containers.count + model.uiContainers.count > SharedModel.keychainAccessGroupCount {
errorInfo = "lc.container.tooMuchContainers".loc
errorShow = true
return
Expand All @@ -663,14 +659,14 @@ extension LCAppSettingsView : LCSelectContainerViewDelegate {
newContainer.loadName()
if newContainer.keychainGroupId == -1 {
// assign keychain group for old containers
var keychainGroupSet : Set<Int> = Set(minimumCapacity: 3)
for i in 0...2 {
var keychainGroupSet : Set<Int> = Set(minimumCapacity: SharedModel.keychainAccessGroupCount)
for i in 0..<SharedModel.keychainAccessGroupCount {
keychainGroupSet.insert(i)
}
for container in model.uiContainers {
keychainGroupSet.remove(container.keychainGroupId)
}
guard let freeKeyChainGroup = keychainGroupSet.first else {
guard let freeKeyChainGroup = keychainGroupSet.randomElement() else {
errorInfo = "lc.container.notEnoughKeychainGroup".loc
errorShow = true
return
Expand Down
18 changes: 9 additions & 9 deletions LiveContainerSwiftUI/LCDownloadView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,23 @@ public final class DownloadHelper : ObservableObject {
func download(url: URL, to: URL) async throws {
var ansError: Error? = nil
cancelled = false

await MainActor.run {
self.isDownloading = true
}

await withCheckedContinuation { c in
continuation = c
let session = URLSession(configuration: .default, delegate: DownloadDelegate(progressCallback: { progress, downloaded, total in
DispatchQueue.main.async {
Task{ await MainActor.run {
self.downloadProgress = progress
self.downloadedSize = downloaded
self.totalSize = total
}
}}
}, completeCallback: {tempFileURL, error in
DispatchQueue.main.async {
Task{ await MainActor.run {
self.isDownloading = false
}
}}
if let error {
print(error)
ansError = error
Expand All @@ -52,11 +57,6 @@ public final class DownloadHelper : ObservableObject {

downloadTask = session.downloadTask(with: url)
downloadTask?.resume()
DispatchQueue.main.async {
self.isDownloading = true
}

print("resume~")
}
if let ansError {
throw ansError
Expand Down
3 changes: 1 addition & 2 deletions LiveContainerSwiftUI/LCJITLessDiagnoseView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,10 @@ struct LCEntitlementView : View {
}
}
if let keyChainAccessGroups = entitlementDict["keychain-access-groups"] as? Array<String> {
let keyChainGroupCount = 3
var notFound = true
if keyChainAccessGroups.contains("\(entitlementTeamId).com.kdt.livecontainer.shared") {
notFound = false
for i in 1..<keyChainGroupCount {
for i in 1..<SharedModel.keychainAccessGroupCount {
if !keyChainAccessGroups.contains("\(entitlementTeamId).com.kdt.livecontainer.shared.\(i)") {
notFound = true
continue
Expand Down
2 changes: 1 addition & 1 deletion LiveContainerSwiftUI/LCWebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//

import SwiftUI
import WebKit
@preconcurrency import WebKit

struct LCWebView: View {
@State private var webView : WebView
Expand Down
34 changes: 34 additions & 0 deletions LiveContainerSwiftUI/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,40 @@
}
}
},
"lc.appSettings.hideLiveContainer" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Hide LiveContainer from Dyld Api"
}
},
"zh_CN" : {
"stringUnit" : {
"state" : "translated",
"value" : "从Dyld Api中隐藏LiveContainer"
}
}
}
},
"lc.appSettings.hideLiveContainerDesc" : {
"extractionState" : "manual",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Might help bypass some app’s injection detection."
}
},
"zh_CN" : {
"stringUnit" : {
"state" : "translated",
"value" : "可能可以帮助绕过一些App的注入检测。"
}
}
}
},
"lc.appSettings.launchWithJit" : {
"extractionState" : "manual",
"localizations" : {
Expand Down
9 changes: 7 additions & 2 deletions LiveContainerSwiftUI/Shared.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class SharedModel: ObservableObject {
UIDevice.current.userInterfaceIdiom == .phone
}()

public static let keychainAccessGroupCount = 128

func updateMultiLCStatus() {
if LCUtils.appUrlScheme()?.lowercased() != "livecontainer" {
multiLCStatus = 2
Expand Down Expand Up @@ -94,9 +96,9 @@ class AlertHelper<T> : ObservableObject {
func open() async -> T? {
await withCheckedContinuation { c in
self.c = c
DispatchQueue.main.async {
Task { await MainActor.run {
self.show = true
}
}}
}
return self.result
}
Expand Down Expand Up @@ -818,6 +820,9 @@ extension LCUtils {
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.")
if let mountStatusUrl = URL(string: "\(JITStresmerEBAddress)/mount_status") {
await UIApplication.shared.open(mountStatusUrl)
}
return false
}

Expand Down
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,7 @@ make package
- The guest app's entry point calls `UIApplicationMain` and start up like any other iOS apps.

### Multi-Account support & Keychain Semi-Separation
[3 keychain access groups](./entitlements.xml) are created and LiveContainer allocates them to each container of same app. So you can create 3 container with different keychain access groups.

#### Why only 3?
The [original thought was 256](https://github.com/hugeBlack/LiveContainer/blob/256keychainAccessGroup/entitlements.xml), but due to a [SideStore bug](https://github.com/SideStore/SideStore/issues/782) (latest AltStore don't have it), we can only declare 3 keychain access groups before SideStore fails to sign. So the limit is 3.
[128 keychain access groups](./entitlements.xml) are created and LiveContainer allocates them randomly to each container of the same app. So you can create 128 container with different keychain access groups.

## Limitations
- Entitlements from the guest app are not applied to the host app. This isn't a big deal since sideloaded apps requires only basic entitlements.
Expand Down
Loading

0 comments on commit 2f74ae6

Please sign in to comment.