Identify a process instance by a deterministic identity returned from the agent (per-OS, with a Process.mainModule + pid fallback) and stash it on ProcessSession.processInfo. Rekey memory_page into memory_page_module (module UUID + offset) so cached bytes survive ASLR, and memory_page_anon (process identity + address) for JIT / heap regions that only survive within one process instance. Purge stale anon pages on every fresh attach. Add updateProcessInfo, updateModuleAnalysis and updateInsight SessionOps plus a side-channel publish-memory-page op and a +fetch-memory-page request. The driver write-throughs every page it fetches; non-drivers pull on miss and persist locally. Module-analysis rows flow the same way so non-drivers can key their cache by module UUID. The lab-join snapshot now carries processInfo and module_analyses too. Refresh actions on the insight detail view: Reread Bytes drops the pages overlapping the visible range; Reanalyze Module drops the module's analysis and pages, re-running r2 on next access. The macOS detail view now reads the insight live from insightsBySession via observation; GTK forwards .insightUpdated into the detail and reschedules a refresh when the resolved address changes.
99 lines
3.0 KiB
Swift
99 lines
3.0 KiB
Swift
import Foundation
|
|
import GRDB
|
|
|
|
public enum MemoryPage {
|
|
public static let size: UInt64 = 0x1000
|
|
public static let mask: UInt64 = size &- 1
|
|
|
|
public static func base(of address: UInt64) -> UInt64 {
|
|
address & ~mask
|
|
}
|
|
}
|
|
|
|
public enum MemoryPageRegion: Sendable, Equatable {
|
|
case module(uuid: String, offset: UInt64)
|
|
case anonymous(identity: String, address: UInt64)
|
|
}
|
|
|
|
public struct MemoryPagePublish: Sendable {
|
|
public let region: MemoryPageRegion
|
|
public let bytes: Data
|
|
public let capturedAt: Date
|
|
|
|
public init(region: MemoryPageRegion, bytes: Data, capturedAt: Date = Date()) {
|
|
self.region = region
|
|
self.bytes = bytes
|
|
self.capturedAt = capturedAt
|
|
}
|
|
}
|
|
|
|
public struct MemoryPageModule: Codable, Sendable, FetchableRecord, PersistableRecord {
|
|
public static let databaseTableName = "memory_page_module"
|
|
|
|
public var sessionID: UUID
|
|
public var moduleUUID: String
|
|
public var offset: UInt64
|
|
public var bytes: Data
|
|
public var capturedAt: Date
|
|
|
|
public init(sessionID: UUID, moduleUUID: String, offset: UInt64, bytes: Data, capturedAt: Date = Date()) {
|
|
self.sessionID = sessionID
|
|
self.moduleUUID = moduleUUID
|
|
self.offset = offset
|
|
self.bytes = bytes
|
|
self.capturedAt = capturedAt
|
|
}
|
|
|
|
public func encode(to container: inout PersistenceContainer) {
|
|
container["session_id"] = sessionID
|
|
container["module_uuid"] = moduleUUID
|
|
container["offset"] = Int64(bitPattern: offset)
|
|
container["bytes"] = bytes
|
|
container["captured_at"] = capturedAt
|
|
}
|
|
|
|
public init(row: Row) throws {
|
|
sessionID = row["session_id"]
|
|
moduleUUID = row["module_uuid"]
|
|
let raw: Int64 = row["offset"]
|
|
offset = UInt64(bitPattern: raw)
|
|
bytes = row["bytes"]
|
|
capturedAt = row["captured_at"]
|
|
}
|
|
}
|
|
|
|
public struct MemoryPageAnon: Codable, Sendable, FetchableRecord, PersistableRecord {
|
|
public static let databaseTableName = "memory_page_anon"
|
|
|
|
public var sessionID: UUID
|
|
public var processIdentity: String
|
|
public var address: UInt64
|
|
public var bytes: Data
|
|
public var capturedAt: Date
|
|
|
|
public init(sessionID: UUID, processIdentity: String, address: UInt64, bytes: Data, capturedAt: Date = Date()) {
|
|
self.sessionID = sessionID
|
|
self.processIdentity = processIdentity
|
|
self.address = address
|
|
self.bytes = bytes
|
|
self.capturedAt = capturedAt
|
|
}
|
|
|
|
public func encode(to container: inout PersistenceContainer) {
|
|
container["session_id"] = sessionID
|
|
container["process_identity"] = processIdentity
|
|
container["address"] = Int64(bitPattern: address)
|
|
container["bytes"] = bytes
|
|
container["captured_at"] = capturedAt
|
|
}
|
|
|
|
public init(row: Row) throws {
|
|
sessionID = row["session_id"]
|
|
processIdentity = row["process_identity"]
|
|
let raw: Int64 = row["address"]
|
|
address = UInt64(bitPattern: raw)
|
|
bytes = row["bytes"]
|
|
capturedAt = row["captured_at"]
|
|
}
|
|
}
|