235 lines
8.3 KiB
Swift
235 lines
8.3 KiB
Swift
import Foundation
|
|
import Frida
|
|
import GRDB
|
|
|
|
public struct ProcessSession: Codable, Identifiable, Sendable, FetchableRecord, PersistableRecord {
|
|
public static let databaseTableName = "process_session"
|
|
|
|
public var id: UUID
|
|
public var kind: Kind
|
|
public var host: CollaborationSession.UserInfo?
|
|
public var deviceID: String
|
|
public var deviceName: String
|
|
public var processName: String
|
|
public var iconPNGData: Data?
|
|
|
|
public var phase: Phase
|
|
public var armingState: ArmingState
|
|
public var lastArmPattern: String?
|
|
public var detachReason: SessionDetachReason
|
|
public var lastError: String?
|
|
|
|
public var createdAt: Date
|
|
public var lastKnownPID: UInt
|
|
public var lastAttachedAt: Date?
|
|
|
|
public var processInfo: ProcessInfo?
|
|
public var lastKnownMainModule: ProcessModule?
|
|
public var lastKnownModules: [ProcessModule]?
|
|
public var lastKnownThreads: [ProcessThread]?
|
|
|
|
enum CodingKeys: String, CodingKey {
|
|
case id
|
|
case kind
|
|
case host
|
|
case deviceID = "device_id"
|
|
case deviceName = "device_name"
|
|
case processName = "process_name"
|
|
case iconPNGData = "icon_png_data"
|
|
case phase
|
|
case armingState = "arming_state"
|
|
case lastArmPattern = "last_arm_pattern"
|
|
case detachReason = "detach_reason"
|
|
case lastError = "last_error"
|
|
case createdAt = "created_at"
|
|
case lastKnownPID = "last_known_pid"
|
|
case lastAttachedAt = "last_attached_at"
|
|
case processInfo = "process_info"
|
|
case lastKnownMainModule = "last_known_main_module"
|
|
case lastKnownModules = "last_known_modules"
|
|
case lastKnownThreads = "last_known_threads"
|
|
}
|
|
|
|
public init(
|
|
id: UUID = UUID(),
|
|
kind: Kind,
|
|
host: CollaborationSession.UserInfo? = nil,
|
|
deviceID: String,
|
|
deviceName: String,
|
|
processName: String,
|
|
lastKnownPID: UInt,
|
|
armingState: ArmingState = .unarmed
|
|
) {
|
|
self.id = id
|
|
self.kind = kind
|
|
self.host = host
|
|
self.deviceID = deviceID
|
|
self.deviceName = deviceName
|
|
self.processName = processName
|
|
self.phase = .idle
|
|
self.armingState = armingState
|
|
self.detachReason = .applicationRequested
|
|
self.createdAt = Date()
|
|
self.lastKnownPID = lastKnownPID
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
id = try container.decode(UUID.self, forKey: .id)
|
|
kind = try container.decode(Kind.self, forKey: .kind)
|
|
host = try container.decodeIfPresent(CollaborationSession.UserInfo.self, forKey: .host)
|
|
deviceID = try container.decode(String.self, forKey: .deviceID)
|
|
deviceName = try container.decode(String.self, forKey: .deviceName)
|
|
processName = try container.decode(String.self, forKey: .processName)
|
|
iconPNGData = try container.decodeIfPresent(Data.self, forKey: .iconPNGData)
|
|
phase = try container.decode(Phase.self, forKey: .phase)
|
|
armingState = try container.decodeIfPresent(ArmingState.self, forKey: .armingState) ?? .unarmed
|
|
lastArmPattern = try container.decodeIfPresent(String.self, forKey: .lastArmPattern)
|
|
detachReason = try container.decode(SessionDetachReason.self, forKey: .detachReason)
|
|
lastError = try container.decodeIfPresent(String.self, forKey: .lastError)
|
|
createdAt = try container.decode(Date.self, forKey: .createdAt)
|
|
lastKnownPID = try container.decode(UInt.self, forKey: .lastKnownPID)
|
|
lastAttachedAt = try container.decodeIfPresent(Date.self, forKey: .lastAttachedAt)
|
|
processInfo = try container.decodeIfPresent(ProcessInfo.self, forKey: .processInfo)
|
|
lastKnownMainModule = try container.decodeIfPresent(ProcessModule.self, forKey: .lastKnownMainModule)
|
|
lastKnownModules = try container.decodeIfPresent([ProcessModule].self, forKey: .lastKnownModules)
|
|
lastKnownThreads = try container.decodeIfPresent([ProcessThread].self, forKey: .lastKnownThreads)
|
|
}
|
|
|
|
public struct ProcessInfo: Codable, Sendable {
|
|
public let platform: String
|
|
public let arch: String
|
|
public let pointerSize: Int
|
|
public let identity: String
|
|
|
|
public init(platform: String, arch: String, pointerSize: Int, identity: String) {
|
|
self.platform = platform
|
|
self.arch = arch
|
|
self.pointerSize = pointerSize
|
|
self.identity = identity
|
|
}
|
|
}
|
|
|
|
public enum Kind: Codable, Sendable {
|
|
case spawn(SpawnConfig)
|
|
case attach
|
|
|
|
public var verbDisplayName: String {
|
|
switch self {
|
|
case .spawn: return "spawn"
|
|
case .attach: return "attach"
|
|
}
|
|
}
|
|
|
|
public var reestablishLabel: String {
|
|
switch self {
|
|
case .spawn: return "Re-Spawn"
|
|
case .attach: return "Re-Attach"
|
|
}
|
|
}
|
|
|
|
public var inProgressLabel: String {
|
|
switch self {
|
|
case .spawn: return "Spawning…"
|
|
case .attach: return "Attaching…"
|
|
}
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case kind
|
|
case config
|
|
}
|
|
|
|
private enum KindTag: String, Codable {
|
|
case spawn
|
|
case attach
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
switch self {
|
|
case .spawn(let config):
|
|
try container.encode(KindTag.spawn, forKey: .kind)
|
|
try container.encode(config, forKey: .config)
|
|
case .attach:
|
|
try container.encode(KindTag.attach, forKey: .kind)
|
|
}
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
let tag = try container.decode(KindTag.self, forKey: .kind)
|
|
switch tag {
|
|
case .spawn:
|
|
let config = try container.decode(SpawnConfig.self, forKey: .config)
|
|
self = .spawn(config)
|
|
case .attach:
|
|
self = .attach
|
|
}
|
|
}
|
|
}
|
|
|
|
public var supportsArmForNextLaunch: Bool {
|
|
guard case .spawn = kind else { return false }
|
|
if case .armed = armingState { return true }
|
|
return lastArmPattern != nil
|
|
}
|
|
|
|
public enum Phase: Int, Codable, Sendable {
|
|
case idle
|
|
case attaching
|
|
case awaitingInitialResume
|
|
case attached
|
|
}
|
|
|
|
public enum ArmingState: Codable, Sendable, Equatable {
|
|
case unarmed
|
|
case armed(matchPattern: String, armedAt: Date)
|
|
|
|
public var armedSince: Date? {
|
|
if case .armed(_, let date) = self { return date }
|
|
return nil
|
|
}
|
|
|
|
public var matchPattern: String? {
|
|
if case .armed(let pattern, _) = self { return pattern }
|
|
return nil
|
|
}
|
|
|
|
private enum CodingKeys: String, CodingKey {
|
|
case state
|
|
case matchPattern = "match_pattern"
|
|
case armedAt = "armed_at"
|
|
}
|
|
|
|
private enum StateTag: String, Codable {
|
|
case unarmed
|
|
case armed
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
switch self {
|
|
case .unarmed:
|
|
try container.encode(StateTag.unarmed, forKey: .state)
|
|
case .armed(let pattern, let date):
|
|
try container.encode(StateTag.armed, forKey: .state)
|
|
try container.encode(pattern, forKey: .matchPattern)
|
|
try container.encode(date, forKey: .armedAt)
|
|
}
|
|
}
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
switch try container.decode(StateTag.self, forKey: .state) {
|
|
case .unarmed:
|
|
self = .unarmed
|
|
case .armed:
|
|
let pattern = try container.decode(String.self, forKey: .matchPattern)
|
|
let armedAt = try container.decode(Date.self, forKey: .armedAt)
|
|
self = .armed(matchPattern: pattern, armedAt: armedAt)
|
|
}
|
|
}
|
|
}
|
|
}
|