Supports sideloading more than 3 apps via MacDirtyCow exploit

The MacDirtyCow exploit allows users to remove the 3 active apps limit on iOS 16.1.2 and earlier. To support this, we’ve added a new (hidden) “Enforce 3-App Limit” setting that can be disabled to allow sideloading more than 3 apps.
This commit is contained in:
Riley Testut
2023-02-06 17:36:05 -06:00
parent 90b4fa7605
commit eeea64f780
7 changed files with 118 additions and 11 deletions

View File

@@ -25,6 +25,7 @@ extension SettingsViewController
case instructions
case techyThings
case credits
case macDirtyCow
case debug
}
@@ -70,6 +71,7 @@ class SettingsViewController: UITableViewController
@IBOutlet private var accountTypeLabel: UILabel!
@IBOutlet private var backgroundRefreshSwitch: UISwitch!
@IBOutlet private var enforceThreeAppLimitSwitch: UISwitch!
@IBOutlet private var versionLabel: UILabel!
@@ -146,6 +148,7 @@ private extension SettingsViewController
}
self.backgroundRefreshSwitch.isOn = UserDefaults.standard.isBackgroundRefreshEnabled
self.enforceThreeAppLimitSwitch.isOn = !UserDefaults.standard.ignoreActiveAppsLimit
if self.isViewLoaded
{
@@ -209,6 +212,16 @@ private extension SettingsViewController
case .credits:
settingsHeaderFooterView.primaryLabel.text = NSLocalizedString("CREDITS", comment: "")
case .macDirtyCow:
if isHeader
{
settingsHeaderFooterView.primaryLabel.text = NSLocalizedString("MACDIRTYCOW", comment: "")
}
else
{
settingsHeaderFooterView.secondaryLabel.text = NSLocalizedString("If you've removed the 3-sideloaded app limit via the MacDirtyCow exploit, disable this setting to sideload more than 3 apps at a time.", comment: "")
}
case .debug:
settingsHeaderFooterView.primaryLabel.text = NSLocalizedString("DEBUG", comment: "")
}
@@ -225,6 +238,20 @@ private extension SettingsViewController
let size = settingsHeaderFooterView.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
return size.height
}
func isSectionHidden(_ section: Section) -> Bool
{
switch section
{
case .macDirtyCow:
let ios16_2 = OperatingSystemVersion(majorVersion: 16, minorVersion: 2, patchVersion: 0)
let isMacDirtyCowExploitSupported = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios16_2)
return !(isMacDirtyCowExploitSupported && UserDefaults.standard.isDebugModeEnabled)
default: return false
}
}
}
private extension SettingsViewController
@@ -279,6 +306,16 @@ private extension SettingsViewController
UserDefaults.standard.isBackgroundRefreshEnabled = sender.isOn
}
@IBAction func toggleEnforceThreeAppLimit(_ sender: UISwitch)
{
UserDefaults.standard.ignoreActiveAppsLimit = !sender.isOn
if UserDefaults.standard.activeAppsLimit != nil
{
UserDefaults.standard.activeAppsLimit = InstalledApp.freeAccountActiveAppsLimit
}
}
@available(iOS 14, *)
@IBAction func addRefreshAppsShortcut()
{
@@ -376,6 +413,7 @@ extension SettingsViewController
let section = Section.allCases[section]
switch section
{
case _ where isSectionHidden(section): return 0
case .signIn: return (self.activeTeam == nil) ? 1 : 0
case .account: return (self.activeTeam == nil) ? 0 : 3
case .appRefresh: return AppRefreshRow.allCases.count
@@ -404,9 +442,10 @@ extension SettingsViewController
let section = Section.allCases[section]
switch section
{
case _ where isSectionHidden(section): return nil
case .signIn where self.activeTeam != nil: return nil
case .account where self.activeTeam == nil: return nil
case .signIn, .account, .patreon, .appRefresh, .techyThings, .credits, .debug:
case .signIn, .account, .patreon, .appRefresh, .techyThings, .credits, .macDirtyCow, .debug:
let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "HeaderFooterView") as! SettingsHeaderFooterView
self.prepare(headerView, for: section, isHeader: true)
return headerView
@@ -420,8 +459,9 @@ extension SettingsViewController
let section = Section.allCases[section]
switch section
{
case _ where isSectionHidden(section): return nil
case .signIn where self.activeTeam != nil: return nil
case .signIn, .patreon, .appRefresh:
case .signIn, .patreon, .appRefresh, .macDirtyCow:
let footerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "HeaderFooterView") as! SettingsHeaderFooterView
self.prepare(footerView, for: section, isHeader: false)
return footerView
@@ -435,9 +475,10 @@ extension SettingsViewController
let section = Section.allCases[section]
switch section
{
case _ where isSectionHidden(section): return 1.0
case .signIn where self.activeTeam != nil: return 1.0
case .account where self.activeTeam == nil: return 1.0
case .signIn, .account, .patreon, .appRefresh, .techyThings, .credits, .debug:
case .signIn, .account, .patreon, .appRefresh, .techyThings, .credits, .macDirtyCow, .debug:
let height = self.preferredHeight(for: self.prototypeHeaderFooterView, in: section, isHeader: true)
return height
@@ -450,9 +491,10 @@ extension SettingsViewController
let section = Section.allCases[section]
switch section
{
case _ where isSectionHidden(section): return 1.0
case .signIn where self.activeTeam != nil: return 1.0
case .account where self.activeTeam == nil: return 1.0
case .signIn, .patreon, .appRefresh:
case .signIn, .patreon, .appRefresh, .macDirtyCow:
let height = self.preferredHeight(for: self.prototypeHeaderFooterView, in: section, isHeader: false)
return height
@@ -520,7 +562,7 @@ extension SettingsViewController
case .refreshAttempts: break
}
case .account, .patreon, .instructions, .techyThings: break
case .account, .patreon, .instructions, .techyThings, .macDirtyCow: break
}
}
}