Files
luma/Sources/LumaCore/Models/ProcessModule.swift
Ole André Vadla Ravnås b22d2dabf8 core: Classify rerooted system modules
Match the distinctive /usr/lib markers anywhere in the path so a
jailbroken iOS reroot resolves: dyld appears as /cores/usr/lib/dyld
and was misclassified as an app module. Keep generic roots anchored
so app libraries under /data/app and /home stay app, and add Android
system roots (/system, /apex, /vendor, /product, /system_ext).
2026-07-10 16:44:06 +02:00

104 lines
3.5 KiB
Swift

public struct ModuleDelta: Sendable {
public let added: [ProcessModule]
public let removed: [ProcessModule]
public init(added: [ProcessModule] = [], removed: [ProcessModule] = []) {
self.added = added
self.removed = removed
}
public var isEmpty: Bool { added.isEmpty && removed.isEmpty }
public func applied(to base: [ProcessModule]?) -> [ProcessModule] {
var result = base ?? []
if !removed.isEmpty {
let removedBases = Set(removed.map { $0.base })
result.removeAll { removedBases.contains($0.base) }
}
result.append(contentsOf: added)
return result
}
}
public enum ModuleAnalysisStatus: Sendable, Hashable {
case notAnalyzed
case analyzing
case analyzed
}
extension Sequence where Element == ProcessModule {
public func sortedByOrigin() -> [ProcessModule] {
sorted { lhs, rhs in
if lhs.isSystemModule != rhs.isSystemModule { return !lhs.isSystemModule }
return lhs.name.localizedCaseInsensitiveCompare(rhs.name) == .orderedAscending
}
}
}
extension Array where Element == ProcessModule {
public func sidebarHighlights(
mainModule: ProcessModule?,
selectedID: ProcessModule.ID?,
limit: Int = SidebarHighlights.defaultLimit
) -> [ProcessModule] {
let main = mainModule.flatMap { wanted in first { $0.id == wanted.id } } ?? first
let peers = filter { $0.id != main?.id && $0.isSystemModule == (main?.isSystemModule ?? false) }
.prefix(Swift.max(0, limit - 1))
.sorted { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending }
let featured = ([main].compactMap { $0 } + peers)
return featured.withSelected(selectedID, from: self, limit: limit)
}
}
public struct ProcessModule: Hashable, Identifiable, Codable, Sendable {
public var id: String { "\(path)@0x\(String(base, radix: 16))" }
public let name: String
public let path: String
public let base: UInt64
public let size: UInt64
public init(name: String, path: String, base: UInt64, size: UInt64) {
self.name = name
self.path = path
self.base = base
self.size = size
}
private static let rerootedSystemMarkers = ["/usr/lib/", "/usr/local/lib/", "/usr/libexec/"]
private static let systemPathPrefixes = [
"/usr/", "/lib/", "/lib64/", "/opt/", "/System/", "/Library/",
"/system/", "/apex/", "/vendor/", "/product/", "/system_ext/",
]
public var isSystemModule: Bool {
let windows = path.lowercased()
if windows.contains(":\\windows\\") || windows.hasPrefix("\\windows\\") {
return true
}
if Self.rerootedSystemMarkers.contains(where: path.contains) {
return true
}
return Self.systemPathPrefixes.contains(where: path.hasPrefix)
}
public func toJSON() -> [String: Any] {
return [
"name": name,
"path": path,
"base": String(format: "0x%llx", base),
"size": Int(size),
]
}
public static func fromJSON(_ obj: [String: Any]) -> ProcessModule? {
guard let name = obj["name"] as? String,
let path = obj["path"] as? String,
let baseStr = obj["base"] as? String,
let size = obj["size"] as? Int
else { return nil }
let base = UInt64(baseStr.dropFirst(2), radix: 16) ?? 0
return ProcessModule(name: name, path: path, base: base, size: UInt64(size))
}
}