├── .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 | 
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
--------------------------------------------------------------------------------