├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── Absolute_Solver.png ├── Package.swift ├── README.md ├── Sources └── AbsoluteSolver │ └── AbsoluteSolver.swift └── absolutesolver.png /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm/config/registries.json 8 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 9 | .netrc 10 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Absolute_Solver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/AbsoluteSolver-iOS/7e0f4b45a3a1d37dfd646a6f55336e77d6f8613d/Absolute_Solver.png -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 5.7 2 | // Package.swift 3 | // 4 | // 5 | // Created by Hariz Shirazi on 2023-04-09. 6 | // Copyright © J.C. Jensen in Spaaaaace!!!!. Some rights reserved. 7 | // 8 | 9 | 10 | import PackageDescription 11 | 12 | let package = Package( 13 | name: "AbsoluteSolver", 14 | products: [ 15 | // Products define the executables and libraries a package produces, and make them visible to other packages. 16 | .library( 17 | name: "AbsoluteSolver", 18 | targets: ["AbsoluteSolver"]), 19 | ], 20 | dependencies: [ 21 | // Dependencies declare other packages that this package depends on. 22 | .package(url: "https://github.com/BomberFish/DirtyCowKit", branch: "main"), 23 | ], 24 | targets: [ 25 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 26 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 27 | .target( 28 | name: "AbsoluteSolver", 29 | dependencies: ["DirtyCowKit"]), 30 | ] 31 | ) 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Absolute Solver icon](./Absolute_Solver.png) 2 | 3 | # Absolute Solver 4 | 5 | A file manager that will modify/delete files By Any Means Necessary™ 6 | 7 | ## Used in: 8 | - [AppCommander](https://github.com/BomberFish/AppCommander) 9 | - [ControlConfig (soon)](https://github.com/f1shy-dev/ControlConfig) 10 | 11 | Usage: 12 | ```swift 13 | import AbsoluteSolver 14 | 15 | // Replace file with data 16 | AbsoluteSolver.replace(at: URL, with: NSData) 17 | 18 | // Delete file 19 | AbsoluteSolver.delete(at: URL) 20 | 21 | // Copy file 22 | AbsoluteSolver.copy(at: URL, to: URL) 23 | 24 | // Read data from file (returns Data) 25 | AbsoluteSolver.readFile(path: String) 26 | 27 | // Delete contents of directory (but not directory itself) 28 | AbsoluteSolver.delDirectoryContents(path: String, progress: ((Double, String)) -> ()) 29 | 30 | // Plist Padding (returns Data) 31 | AbsoluteSolver.padPlist(replacementData: Data, filePath: String) 32 | ``` 33 | -------------------------------------------------------------------------------- /Sources/AbsoluteSolver/AbsoluteSolver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AbsoluteSolver.swift 3 | // 4 | // 5 | // Created by Hariz Shirazi on 2023-04-09. 6 | // Copyright © J.C. Jensen in Spaaaaace!!!!. Some rights reserved. 7 | // 8 | 9 | import Foundation 10 | import MacDirtyCow 11 | 12 | public enum AbsoluteSolver { 13 | 14 | // MARK: - Replace file 15 | 16 | public static func replace(at: URL, with: NSData, progress: ((String)) -> () = {message in 17 | print(message) 18 | }) throws { 19 | // In case the specified path is empty 20 | if at == URL(string: "") { 21 | throw "AbsoluteSolver Error: Can I get a location?" 22 | } 23 | 24 | do { 25 | progress("[AbsoluteSolver] Replacing \(at.path)! X)") 26 | var fm_error: String? = nil 27 | let fileAttributes = try FileManager.default.attributesOfItem(atPath: at.path) 28 | let owner = fileAttributes[.ownerAccountName] as? String ?? "unknown" 29 | if owner == "root" { 30 | // Do not show to Apple employees. 31 | // They will NOT like it. 32 | if MacDirtyCow.isMDCSafe { 33 | progress("[AbsoluteSolver] Using MDC method for file \(at.path)") 34 | do { 35 | try MacDirtyCow.overwriteFileWithDataImpl(originPath: at.path, replacementData: Data(with)) 36 | progress("[AbsoluteSolver] MDC overwrite success! X)") 37 | } catch { 38 | progress("[AbsoluteSolver] MDC overwrite failed! X(") 39 | throw "AbsoluteSolver: Error replacing file at \(at.path) (Using MDC): \(error.localizedDescription)" 40 | } 41 | // if !success { 42 | // progress("[AbsoluteSolver] MDC overwrite failed! X(") 43 | // // Haptic.shared.notify(.error) 44 | // throw "AbsoluteSolver: Error replacing file at \(at.path) (Using MDC)" 45 | // } else { 46 | // progress("[AbsoluteSolver] MDC overwrite success! X)") 47 | // // Haptic.shared.notify(.success) 48 | // } 49 | } else { 50 | // you cant get ram out of thin air 51 | // also prevents catastrophic failure and corruption 👍👍👍 52 | progress("[AbsoluteSolver] PANIC!!! OUT OF RAM!!! THIS IS REALLY REALLY REALLY BAD!!!!!") 53 | // Haptic.shared.notify(.error) 54 | throw "AbsoluteSolver:\n Overwrite failed!\nInsufficient RAM! Please reopen the app." 55 | } 56 | } else if owner == "mobile" { 57 | progress("[AbsoluteSolver] Using FM method for file \(at.path)") 58 | do { 59 | try with.write(to: at, options: .atomic) 60 | progress("[AbsoluteSolver] FM overwrite success! X)") 61 | } catch { 62 | // print("[AbsoluteSolver] FM overwrite failed! X(") 63 | fm_error = error.localizedDescription 64 | progress("[AbsoluteSolver] Warning: FM overwrite failed, using MDC for \(at.path). Error: \(fm_error ?? "Unknown")") 65 | if MacDirtyCow.isMDCSafe { 66 | progress("[AbsoluteSolver] Using MDC method for file \(at.path)") 67 | // let success = MacDirtyCow.overwriteFileWithDataImpl(originPath: at.path, replacementData: Data(with)) 68 | do { 69 | try MacDirtyCow.overwriteFileWithDataImpl(originPath: at.path, replacementData: Data(with)) 70 | progress("[AbsoluteSolver] MDC overwrite success! X)") 71 | } catch { 72 | progress("[AbsoluteSolver] MDC overwrite failed! X(") 73 | if fm_error != nil { 74 | throw "AbsoluteSolver: Error replacing file at \(at.path) (Using MDC): \(error.localizedDescription). Unsandboxed FileManager returned error: \(fm_error ?? "Unknown")" 75 | } else { 76 | throw "AbsoluteSolver: Error replacing file at \(at.path) (Using MDC): \(error.localizedDescription)" 77 | } 78 | } 79 | // if !success { 80 | // progress("[AbsoluteSolver] MDC overwrite failed") 81 | // // Haptic.shared.notify(.error) 82 | // if fm_error != nil { 83 | // throw "AbsoluteSolver: Error replacing file at \(at.path) (Using MDC). Unsandboxed FileManager returned error: \(fm_error ?? "Unknown")" 84 | // } else { 85 | // throw "AbsoluteSolver: Error replacing file at \(at.path) (Using MDC)" 86 | // } 87 | // } else { 88 | // progress("[AbsoluteSolver] MDC overwrite success!") 89 | // // Haptic.shared.notify(.success) 90 | // } 91 | } else { 92 | // you cant get ram out of thin air 93 | // also prevents catastrophic failure and corruption 👍👍👍 94 | progress("[AbsoluteSolver] PANIC!!! OUT OF RAM!!! THIS IS REALLY REALLY REALLY BAD!!!!!") 95 | // Haptic.shared.notify(.error) 96 | throw "AbsoluteSolver: Guru Meditation: Insufficient RAM! Please reopen the app." 97 | } 98 | } 99 | } else if owner == "unknown" { 100 | progress("[AbsoluteSolver] Error: Could not find owner for file \(at.path)?!") 101 | // Haptic.shared.notify(.error) 102 | throw "Error replacing file at \(at.path) (Edit Style: AbsoluteSolver)\nCould not find owner?!" 103 | } else { 104 | progress("[AbsoluteSolver] Warning: Unexpected owner for file \(at.path)! Using MDC...") 105 | // Haptic.shared.notify(.error) 106 | // throw "Error replacing file at \(at.path) (Edit Style: AbsoluteSolver)\nUnexpected file owner!" 107 | do { 108 | try MacDirtyCow.overwriteFileWithDataImpl(originPath: at.path, replacementData: Data(with)) 109 | progress("[AbsoluteSolver] MDC overwrite success! X)") 110 | } catch { 111 | progress("[AbsoluteSolver] MDC overwrite failed! X(") 112 | throw "AbsoluteSolver: Error replacing file at \(at.path) (Using MDC): \(error.localizedDescription)" 113 | } 114 | // let success = MacDirtyCow.overwriteFileWithDataImpl(originPath: at.path, replacementData: Data(with)) 115 | // if !success { 116 | // progress("[AbsoluteSolver] MDC overwrite failed") 117 | // // Haptic.shared.notify(.error) 118 | // throw "AbsoluteSolver: Error replacing file at \(at.path) (Using MDC)" 119 | // } else { 120 | // progress("[AbsoluteSolver] MDC overwrite success!") 121 | // // Haptic.shared.notify(.success) 122 | // } 123 | } 124 | } catch { 125 | progress("[AbsoluteSolver] Error: \(error.localizedDescription)") 126 | // Haptic.shared.notify(.error) 127 | throw "AbsoluteSolver: Error replacing file at \(at.path)\n\(error.localizedDescription)" 128 | } 129 | } 130 | 131 | // MARK: - Delete files 132 | // chainsaw hand time 133 | public static func delete(at: URL, progress: ((String)) -> () = {progress in 134 | print(progress) 135 | }) throws { 136 | // In case the specified path is empty 137 | if at == URL(string: "") { 138 | throw "AbsoluteSolver Error: Can I get a location?" 139 | } 140 | progress("[AbsoluteSolver] Disassembling \(at.path)! X)") 141 | do { 142 | let fileAttributes = try FileManager.default.attributesOfItem(atPath: at.path) 143 | let owner = fileAttributes[.ownerAccountName] as? String ?? "unknown" 144 | if owner == "root" { 145 | progress("[AbsoluteSolver] Skipping file \(at.path), owned by root") 146 | // progress("[AbsoluteSolver] Using MDC method") 147 | // do { 148 | // try MDCModify.delete(at: at) 149 | // } catch { 150 | // throw "Error deleting file at \(at.path) (Edit Style: AbsoluteSolver)\n\(error.localizedDescription)" 151 | // } 152 | } else if owner == "mobile" { 153 | progress("[AbsoluteSolver] Using FM method for file \(at.path)") 154 | do { 155 | //destroy file with this sick as hell railgun 156 | try FileManager.default.removeItem(at: at) 157 | progress("[AbsoluteSolver] FM delete success!") 158 | // Haptic.shared.notify(.success) 159 | } catch { 160 | throw "Error disassembling file at \(at.path) (Edit Style: AbsoluteSolver)\n\(error.localizedDescription)" 161 | } 162 | } else if owner == "unknown" { 163 | progress("[AbsoluteSolver] Error: Could not find owner for file \(at.path)?!") 164 | // Haptic.shared.notify(.error) 165 | throw "AbsoluteSolver: Error disassembling file at \(at.path)\nCould not find owner?!" 166 | } else { 167 | progress("[AbsoluteSolver] Error: Unexpected owner for file \(at.path)!") 168 | // Haptic.shared.notify(.error) 169 | throw "AbsoluteSolver: Error disassembling file at \(at.path)\nUnexpected file owner!" 170 | } 171 | } catch { 172 | progress("[AbsoluteSolver] Error disassembling file \(at.path): \(error.localizedDescription)") 173 | // Haptic.shared.notify(.error) 174 | throw "AbsoluteSolver: Error disassembling file at \(at.path)\n\(error.localizedDescription)" 175 | } 176 | } 177 | 178 | // MARK: - copy files 179 | 180 | public static func copy(at: URL, to: URL, progress: ((String)) -> () = {progress in 181 | print(progress) 182 | }) throws { 183 | // In case the specified path is empty 184 | if at == URL(string: "") || to == URL(string: "") { 185 | throw "AbsoluteSolver Error: Can I get a location?" 186 | } 187 | do { 188 | do { 189 | try FileManager.default.copyItem(at: at, to: to) 190 | } catch { 191 | progress("AbsoluteSolver: Cannot copy file \(to.path) to \(at.path): \(error.localizedDescription). X(") 192 | throw "AbsoluteSolver: Cannot copy file \(to.path) to \(at.path): \(error.localizedDescription)" 193 | } 194 | 195 | } catch { 196 | progress("[AbsoluteSolver] Error: \(error.localizedDescription). X(") 197 | // Haptic.shared.notify(.error) 198 | throw "AbsoluteSolver: Error replacing file at \(at.path)\n\(error.localizedDescription)" 199 | } 200 | } 201 | 202 | // MARK: - reads file contents 203 | 204 | public static func readFile(path: String, progress: ((String)) -> () = {progress in 205 | print(progress) 206 | }) throws -> Data { 207 | progress("[AbsoluteSolver] Reading from \(path)! X)") 208 | // In case the specified path is empty 209 | if path == "" { 210 | progress("[AbsoluteSolver] path is empty? wtf?") 211 | throw "AbsoluteSolver Error: Can I get a location?" 212 | } 213 | do { 214 | return (try Data(contentsOf: URL(fileURLWithPath: path))) 215 | } catch { 216 | // do { 217 | // progress("[AbsoluteSolver] Warning: Swift read failed for file \(path)! Using ObjC read...") 218 | // return try AbsoluteSolver_ObjCHelper().readRawDataFromFile(atPath: path)! 219 | // } catch { 220 | // throw "AbsoluteSolver: Error reading from file \(path): \(error.localizedDescription)" 221 | // } 222 | progress("AbsoluteSolver: Error reading from file \(path): \(error.localizedDescription). X(") 223 | throw "AbsoluteSolver: Error reading from file \(path): \(error.localizedDescription)" 224 | } 225 | } 226 | 227 | // MARK: - deletes all the contents of directories. usually. 228 | 229 | public static func delDirectoryContents(path: String, progress: ((Double, String)) -> ()) throws { 230 | // In case the specified path is empty 231 | if path == "" { 232 | throw "AbsoluteSolver Error: Can I get a location?" 233 | } 234 | var contents = [""] 235 | var currentfile = 0 236 | do { 237 | contents = try FileManager.default.contentsOfDirectory(atPath: path) 238 | for file in contents { 239 | //print("disassembling \(file)") 240 | do { 241 | try delete(at: URL(fileURLWithPath: path).appendingPathComponent(file)) 242 | currentfile += 1 243 | progress((Double(currentfile / contents.count), file)) 244 | } catch { 245 | throw "\(error.localizedDescription)" 246 | } 247 | } 248 | } catch { 249 | progress((0.0, "AbsoluteSolver: Error disassembling the contents of \(path): \(error.localizedDescription)! X(")) 250 | throw "AbsoluteSolver: Error disassembling the contents of \(path): \(error.localizedDescription)" 251 | } 252 | } 253 | 254 | // MARK: - Plist padding 255 | 256 | public static func padPlist(replacementData: Data, filePath: String, progress: ((String)) -> () = {progress in 257 | print(progress) 258 | }) throws -> Data? { 259 | // In case the specified path is empty 260 | if filePath == "" { 261 | throw "AbsoluteSolver Error: Can I get a location?" 262 | } 263 | guard let currentData = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else { return nil } 264 | if replacementData.count == currentData.count { return replacementData } 265 | 266 | progress("[AbsoluteSolver] Using new plist padding method! X)") 267 | var data = replacementData 268 | let trailerData = Array(data.suffix(32)) 269 | var offsetTableOffset = trailerData[24..<32].withUnsafeBytes { 270 | $0.load(as: UInt64.self).bigEndian 271 | } 272 | // progress("og count: \(data.count)") 273 | if trailerData[0] == 170 && trailerData[1] == 170 { 274 | let amountOfPaddingBytes = trailerData[2..<6] 275 | var amountOfPadding = 0 276 | for byte in amountOfPaddingBytes { 277 | amountOfPadding = amountOfPadding << 8 278 | amountOfPadding = amountOfPadding | Int(byte) 279 | } 280 | // progress("padding digits: \(amountOfPadding)") 281 | offsetTableOffset = offsetTableOffset - UInt64(amountOfPadding) 282 | 283 | data = 284 | data[0.. currentData.count { 290 | progress("[AbsoluteSolver] Using old plist padding method! X)") 291 | guard let Default_Data = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else { return nil } 292 | if replacementData.count == Default_Data.count { return replacementData } 293 | progress("pd.count " + String(replacementData.count) + ", dd.count " + String(Default_Data.count)) 294 | guard let Plist = try? PropertyListSerialization.propertyList(from: replacementData, format: nil) as? [String: Any] else { return nil } 295 | var EditedDict = Plist 296 | guard var newData = try? PropertyListSerialization.data(fromPropertyList: EditedDict, format: .binary, options: 0) else { return nil } 297 | var count = 0 298 | progress("DefaultData - " + String(Default_Data.count)) 299 | while true { 300 | newData = try! PropertyListSerialization.data(fromPropertyList: EditedDict, format: .binary, options: 0) 301 | if newData.count >= Default_Data.count { break } 302 | count += 1 303 | EditedDict.updateValue(String(repeating: "*", count: Int(floor(Double(count/2)))), forKey: "0") 304 | EditedDict.updateValue(String(repeating: "+", count: count - Int(floor(Double(count/2)))), forKey: "MdC") 305 | } 306 | progress("ImportData - " + String(newData.count)) 307 | return newData 308 | } 309 | 310 | let amountOfBytesBeingAdded = currentData.count - data.count 311 | let amountOfBytesBeingAddedAs4Bytes = withUnsafeBytes( 312 | of: Int32(amountOfBytesBeingAdded).bigEndian, Array.init) 313 | 314 | var newData = data[0..= Default_Data.count { break } 346 | count += 1 347 | EditedDict.updateValue(String(repeating: "*", count: Int(floor(Double(count/2)))), forKey: "0") 348 | EditedDict.updateValue(String(repeating: "+", count: count - Int(floor(Double(count/2)))), forKey: "MdC") 349 | } 350 | progress("ImportData - " + String(newData.count)) 351 | return newData 352 | } 353 | return newData 354 | } 355 | 356 | public static func replaceDDICert() throws { 357 | let pem: Data = Data(base64Encoded: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMyRENDQWtHZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREJlTVFzd0NRWURWUVFHRXdKRFFURUwKTUFrR0ExVUVDQXdDUWtNeEpUQWpCZ05WQkFvTUhFb3VReTRnU21WdWMyVnVJR2x1SUZOd1lXRmhZV0ZqWlNFaApJU0V4R3pBWkJnTlZCQU1NRWtGaWMyOXNkWFJsVTI5c2RtVnlJRVJFU1RBZUZ3MHdOekEwTVRZeU1qVTFNekZhCkZ3MHhOREEwTVRZeU1qVTFNekZhTUY0eEN6QUpCZ05WQkFZVEFrTkJNUXN3Q1FZRFZRUUlEQUpDUXpFbE1DTUcKQTFVRUNnd2NTaTVETGlCS1pXNXpaVzRnYVc0Z1UzQmhZV0ZoWVdObElTRWhJVEViTUJrR0ExVUVBd3dTUVdKegpiMngxZEdWVGIyeDJaWElnUkVSSk1JR2ZNQTBHQ1NxR1NJYjNEUUVCQVFVQUE0R05BRENCaVFLQmdRRE9vWFI3ClE5eVdqVWVxQUEwdCtVZTJUUnR3MG5nc1htdmdhVE9xSWp3Y3E1ZGNZRFdBZU9rdnZ0VGdNUVNHN2VDeVlBclMKQ29RMjVMVXlhRk1LV1NQVG9RUFRscnhhYmRDNXJkRzNqdHBjUXJVUzJJWXZ0d0E0aUE3SkoyL25BUEQ3OWEyWgo5a2VKaEUzbGVKZ1A2N3BnSXBVS2RkRWxPc3ZOT0lhS2g5a3ZYUUlEQVFBQm80R2xNSUdpTUIwR0ExVWREZ1FXCkJCUlk5TE54STJ0MEFRYXBmZFV6bjRaOHNnWjdhVEJ3QmdOVkhTTUVhVEJub1dLa1lEQmVNUXN3Q1FZRFZRUUcKRXdKRFFURUxNQWtHQTFVRUNBd0NRa014SlRBakJnTlZCQW9NSEVvdVF5NGdTbVZ1YzJWdUlHbHVJRk53WVdGaApZV0ZqWlNFaElTRXhHekFaQmdOVkJBTU1Fa0ZpYzI5c2RYUmxVMjlzZG1WeUlFUkVTWUlCQVRBUEJnTlZIUk1CCkFmOEVCVEFEQVFIL01BMEdDU3FHU0liM0RRRUJDd1VBQTRHQkFJbFZVMkN0Q3NtelRhOW1LWlVHYW1FSTBqMDgKOTBDeU9nL1Nqei8wQm1QRkMwTlhGNjI5dUlLVjRoMFV5cjQvVGZWZG01eW5LWG42MWtXMEhjOTR2STh5RnY2YworMlExS0p3TDhOWjgrVm9XNjNoZWlUOGpXbmEvY29BZHJicFZ2SGFhbWgvNkZXbit0YnNtZlg3c1MvYmxaN2Z2CndYRjFBWDZkRk5MUEFVYmoKLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=")! 358 | do { 359 | try replace(at: URL(fileURLWithPath: "/System/Library/Lockdown/iPhoneDebug.pem"), with: pem as NSData) 360 | } catch { 361 | throw error.localizedDescription 362 | } 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /absolutesolver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BomberFish/AbsoluteSolver-iOS/7e0f4b45a3a1d37dfd646a6f55336e77d6f8613d/absolutesolver.png --------------------------------------------------------------------------------