Skip to content

Commit

Permalink
v2.2.0 (#19)
Browse files Browse the repository at this point in the history
* dump the btm file to capture other persistence items

* capture fish config file

* bump to v2.2.0

* memory usage dump

* formatting

* added unified logging to ignore/disable list

* unified log addition

* bump to v2.2.0

* collection of diagnosticsreports and crashreporter files

* fix username
  • Loading branch information
stuartjash committed Dec 2, 2023
1 parent a9b23f5 commit 49aab5a
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 27 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
![](https://github.com/jamf/aftermath/blob/main/AftermathLogo.png)


![](https://img.shields.io/badge/release-2.1.1-bright%20green) ![](https://img.shields.io/badge/macOS-12.0%2B-blue) ![](https://img.shields.io/badge/license-MIT-orange)
![](https://img.shields.io/badge/release-2.2.0-bright%20green) ![](https://img.shields.io/badge/macOS-12.0%2B-blue) ![](https://img.shields.io/badge/license-MIT-orange)


## About
Expand Down Expand Up @@ -85,7 +85,7 @@ To uninstall the aftermath binary, run the `AftermathUninstaller.pkg` from the [
--deep or -d -> perform a deep scan of the file system for modified and accessed timestamped metadata
WARNING: This will be a time-intensive, memory-consuming scan.
--disable -> disable a set of aftermath features that may collect personal user data
Available features to disable: browsers -> collecting browser information | browser-killswitch -> force-closes browers | -> databases -> tcc & lsquarantine databases | filesystem -> walking the filesystem for timestamps | proc-info -> collecting process information via TrueTree and eslogger | all -> all aforementioned options
Available features to disable: browsers -> collecting browser information | browser-killswitch -> force-closes browers | -> databases -> tcc & lsquarantine databases | filesystem -> walking the filesystem for timestamps | proc-info -> collecting process information via TrueTree and eslogger | slack -> slack data | ul -> unified logging modules | all -> all aforementioned options
usage: --disable browsers browser-killswitch databases filesystem proc-info slack
--disable all
--es-logs -> specify which Endpoint Security events (space-separated) to collect (defaults are: create exec mmap). To disable, see --disable es-logs
Expand Down
20 changes: 20 additions & 0 deletions aftermath.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
5E93B0AE2941608D009D2AB5 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E93B0AD2941608D009D2AB5 /* Data.swift */; };
5E93B0B0294160B6009D2AB5 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E93B0AF294160B6009D2AB5 /* String.swift */; };
5EA438FF2A7010FF00F3E2B9 /* XProtectBehavioralService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EA438FE2A7010FF00F3E2B9 /* XProtectBehavioralService.swift */; };
5ECE5DC12ADF2B4A00939BB0 /* BTM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ECE5DC02ADF2B4A00939BB0 /* BTM.swift */; };
5ECE5DC42AE0406700939BB0 /* MemoryModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ECE5DC32AE0406700939BB0 /* MemoryModule.swift */; };
5ECE5DC62AE040B700939BB0 /* Stat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5ECE5DC52AE040B700939BB0 /* Stat.swift */; };
5EFDDCD72AC6661A00EEF193 /* Brave.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EFDDCD62AC6661A00EEF193 /* Brave.swift */; };
70A44403275707A90035F40E /* SystemReconModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70A44402275707A90035F40E /* SystemReconModule.swift */; };
70A44405275A76990035F40E /* LSQuarantine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70A44404275A76990035F40E /* LSQuarantine.swift */; };
Expand Down Expand Up @@ -91,6 +94,9 @@
5E93B0AD2941608D009D2AB5 /* Data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = "<group>"; };
5E93B0AF294160B6009D2AB5 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
5EA438FE2A7010FF00F3E2B9 /* XProtectBehavioralService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XProtectBehavioralService.swift; sourceTree = "<group>"; };
5ECE5DC02ADF2B4A00939BB0 /* BTM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTM.swift; sourceTree = "<group>"; };
5ECE5DC32AE0406700939BB0 /* MemoryModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryModule.swift; sourceTree = "<group>"; };
5ECE5DC52AE040B700939BB0 /* Stat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stat.swift; sourceTree = "<group>"; };
5EFDDCD62AC6661A00EEF193 /* Brave.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Brave.swift; sourceTree = "<group>"; };
70A44402275707A90035F40E /* SystemReconModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemReconModule.swift; sourceTree = "<group>"; };
70A44404275A76990035F40E /* LSQuarantine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LSQuarantine.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -177,6 +183,15 @@
path = endpointSecurity;
sourceTree = "<group>";
};
5ECE5DC22AE0405500939BB0 /* memory */ = {
isa = PBXGroup;
children = (
5ECE5DC32AE0406700939BB0 /* MemoryModule.swift */,
5ECE5DC52AE040B700939BB0 /* Stat.swift */,
);
path = memory;
sourceTree = "<group>";
};
70A44401275707800035F40E /* systemRecon */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -266,6 +281,7 @@
A09B239B2848F6050062D592 /* Periodic.swift */,
A007834D28947D71008489EA /* Emond.swift */,
A007834F28947E80008489EA /* LoginItems.swift */,
5ECE5DC02ADF2B4A00939BB0 /* BTM.swift */,
);
path = persistence;
sourceTree = "<group>";
Expand Down Expand Up @@ -383,6 +399,7 @@
A374535B2757C1110074B65C /* extensions */,
A0E1E3F9275ED4B7008D0DC6 /* filesystem */,
A02509F228ADB1930030D6A7 /* helpers */,
5ECE5DC22AE0405500939BB0 /* memory */,
A0E1E3F4275ED2D6008D0DC6 /* network */,
A0776CAC27482FF2007D18D8 /* persistence */,
A029AB132876A01300649701 /* processes */,
Expand Down Expand Up @@ -538,6 +555,7 @@
A029AB1C28774CA400649701 /* Tree.swift in Sources */,
A007835028947E80008489EA /* LoginItems.swift in Sources */,
A0C930D428A4318F0011FB87 /* Timeline.swift in Sources */,
5ECE5DC12ADF2B4A00939BB0 /* BTM.swift in Sources */,
A374535A275735B40074B65C /* LoginHooks.swift in Sources */,
70CF9E3A27611C6100FD884B /* ShellHistoryAndProfiles.swift in Sources */,
A0E1E3EB275EC800008D0DC6 /* Firefox.swift in Sources */,
Expand All @@ -546,6 +564,7 @@
5E93B0B0294160B6009D2AB5 /* String.swift in Sources */,
A0E1E3E9275EC736008D0DC6 /* BrowserModule.swift in Sources */,
A02509F428ADB1A80030D6A7 /* CHelpers.swift in Sources */,
5ECE5DC62AE040B700939BB0 /* Stat.swift in Sources */,
70A44403275707A90035F40E /* SystemReconModule.swift in Sources */,
A029AB2B2877F52D00649701 /* launchdXPC.m in Sources */,
A0E1E3EF275EC810008D0DC6 /* Safari.swift in Sources */,
Expand Down Expand Up @@ -574,6 +593,7 @@
A0D6D54927FE52C1002BB3C8 /* SystemExtensions.swift in Sources */,
A08342D6284A8247005E437A /* AnalysisModule.swift in Sources */,
A029AB192876A29600649701 /* Pids.swift in Sources */,
5ECE5DC42AE0406700939BB0 /* MemoryModule.swift in Sources */,
A08342D8284E48FC005E437A /* LogFiles.swift in Sources */,
A0D6D54327F76C58002BB3C8 /* Cron.swift in Sources */,
A02509F128AD93DA0030D6A7 /* Storyline.swift in Sources */,
Expand Down
11 changes: 8 additions & 3 deletions aftermath/Command.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class Command {
static var collectDirs: [String] = []
static var unifiedLogsFile: String? = nil
static var esLogs: [String] = ["create", "exec", "mmap"]
static let version: String = "2.1.0"
static var disableFeatures: [String:Bool] = ["all": false, "browsers": false, "browser-killswitch": false, "databases": false, "filesystem": false, "proc-info": false, "slack": false]
static let version: String = "2.2.0"
static var disableFeatures: [String:Bool] = ["all": false, "browsers": false, "browser-killswitch": false, "databases": false, "filesystem": false, "proc-info": false, "slack": false, "ul": false]

static func main() {
setup(with: CommandLine.arguments)
Expand Down Expand Up @@ -209,6 +209,11 @@ class Command {
// FileSystem
let fileSysModule = FileSystemModule()
fileSysModule.run()


// Memory
let memoryModule = MemoryModule()
memoryModule.run()


// Artifacts
Expand Down Expand Up @@ -264,7 +269,7 @@ class Command {
print(" usage: --collect-dirs /Users/<USER>/Downloads /tmp")
print("--deep -> performs deep scan and captures metadata from Users entire directory (WARNING: this may be time-consuming)")
print("--disable -> disable a set of aftermath features that may collect personal user data")
print(" usage: --disable browsers browser-killswitch databases filesystem proc-info slack")
print(" usage: --disable browsers browser-killswitch databases filesystem proc-info slack ul")
print(" --disable all")
print("--es-logs -> specify which Endpoint Security events (space-separated) to collect (defaults are: create exec mmap)")
print(" usage: --es-logs exec open rename")
Expand Down
36 changes: 36 additions & 0 deletions artifacts/LogFiles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,44 @@ class LogFiles: ArtifactsModule {
}
}

func collectDiagnosticsReports() {
let diagReportsDir = self.createNewDir(dir: self.logFilesDir, dirname: "diagnostics_reports")

let files = filemanager.filesInDirRecursive(path: "/Library/Logs/DiagnosticReports")
for file in files {
let filePath = URL(fileURLWithPath: file.relativePath)
if (filemanager.fileExists(atPath: filePath.path)) {
self.copyFileToCase(fileToCopy: filePath, toLocation: diagReportsDir)
}
}

for user in getBasicUsersOnSystem() {
let files = filemanager.filesInDirRecursive(path: "\(user.homedir)/Library/Logs/DiagnosticReports")
for file in files {
let filePath = URL(fileURLWithPath: file.relativePath)
if (filemanager.fileExists(atPath: filePath.path)) {
self.copyFileToCase(fileToCopy: filePath, toLocation: diagReportsDir, newFileName: "\(user.username)_\(filePath.lastPathComponent)")
}
}
}
}

func collectCrashReports() {
let crashReportsDir = self.createNewDir(dir: self.logFilesDir, dirname: "crash_reporter")

let files = filemanager.filesInDirRecursive(path: "/Library/Logs/CrashReporter")
for file in files {
let filePath = URL(fileURLWithPath: file.relativePath)
if (filemanager.fileExists(atPath: filePath.path)) {
self.copyFileToCase(fileToCopy: filePath, toLocation: crashReportsDir)
}
}
}

override func run() {
captureLogFiles()
captureUserLogs()
collectDiagnosticsReports()
collectCrashReports()
}
}
4 changes: 2 additions & 2 deletions artifacts/ShellHistoryAndProfiles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class BashProfiles: ArtifactsModule {

let userFiles = [ ".bash_history", ".bash_profile", ".bashrc", ".bash_logout",
".zsh_history", ".zshenv", ".zprofile", ".zshrc", ".zlogin", ".zlogout",
".sh_history"
".sh_history", ".config/fish/config.fish"
]

let globalFiles = ["/etc/profile", "/etc/zshenv", "/etc/zprofile", "/etc/zshrc", "/etc/zlogin", "/etc/zlogout"]
Expand All @@ -31,7 +31,7 @@ class BashProfiles: ArtifactsModule {
for filename in userFiles {
let path = URL(fileURLWithPath: "\(user.homedir)/\(filename)")
if (filemanager.fileExists(atPath: path.path)) {
let newFileName = "\(user.username)_\(filename)"
let newFileName = "\(user.username)_\(filename.replacingOccurrences(of: "/", with: ""))"
self.copyFileToCase(fileToCopy: path, toLocation: self.profilesDir, newFileName: newFileName)
}

Expand Down
5 changes: 2 additions & 3 deletions filesystem/browsers/Safari.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ class Safari: BrowserModule {
self.addTextToFile(atUrl: safariDownloads, text: "timestamp,url")

for user in getBasicUsersOnSystem() {
let downloadsPlist = URL(fileURLWithPath: "\(user.homedir)/Library/Safari/Downloads.plist")

let downloadsPlist = URL(fileURLWithPath: "\(user.homedir)/Library/Safari/Downloads.plist")
if filemanager.fileExists(atPath: downloadsPlist.path) {
let plistDict = Aftermath.getPlistAsDict(atUrl: downloadsPlist)

Expand All @@ -144,7 +144,6 @@ class Safari: BrowserModule {
}
}
self.addTextToFile(atUrl: safariDownloads, text: "\(timestamp),\(url)")

}
}
}
Expand Down
22 changes: 22 additions & 0 deletions memory/MemoryModule.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// MemoryModule.swift
// aftermath
//
// Created by Stuart Ashenbrenner on 10/18/23.
//

import Foundation

class MemoryModule: AftermathModule {
let name = "Memory Module"
let dirName = "Memory"
let description = "A module for collecting memory data"
lazy var moduleDirRoot = self.createNewDirInRoot(dirName: dirName)

func run() {
self.log("Collecting available memory information")

let stat = Stat()
stat.run()
}
}
92 changes: 92 additions & 0 deletions memory/Stat.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// Stat.swift
// aftermath
//
// Created by Stuart Ashenbrenner on 10/18/23.
//

import Foundation

class Stat: MemoryModule {

private func scannerModule(inputString: String, stringToFind: String) -> Double? {
let scanner = Scanner(string: inputString)

if scanner.scanUpTo(stringToFind, into: nil),
scanner.scanString(stringToFind, into: nil) {
var result: Double = 0.0

if scanner.scanDouble(&result) {
return result
}
}
return nil
}

override func run() {

let writeFile = self.createNewCaseFile(dirUrl: moduleDirRoot, filename: "memory_usage.txt")

// trim extra characters from output
var trimSet = CharacterSet.whitespacesAndNewlines
trimSet.insert(charactersIn: "\"")

// create vm_stat shell
let command = "vm_stat"
let vmstatOutput = Aftermath.shell(command)

// convert bytes to GiB
let byteConverter = 0.00000000093132257

// pagesize
var pagesizeOutput = Aftermath.shell("pagesize")
pagesizeOutput = pagesizeOutput.trimmingCharacters(in: trimSet)
let pagesizeDouble = Double(pagesizeOutput)

// parse vm_stat output
var componentDict = [String:Double]()
let vmLines = vmstatOutput.split(separator: "\n")
for l in vmLines {
let components = l.split(separator: ":")

if components.count == 2 {
let key = components[0].trimmingCharacters(in: trimSet)
let value = components[1].trimmingCharacters(in: trimSet)
let valueDouble = Double(value)
let updatedValue = valueDouble ?? 0 * pagesizeDouble!
componentDict[key] = updatedValue
}
}

// app memory
let appMemory = (componentDict["Anonymous pages"] ?? 0.0) - (componentDict["Pages purgeable"] ?? 0.0)
let wired = Double(componentDict["Pages wired down"]!)
let active = Double(componentDict["Pages active"]!)
let inactive = Double(componentDict["Pages inactive"]!)
let spec = Double(componentDict["Pages speculative"]!)
let throttled = Double(componentDict["Pages throttled"]!)
let freeMemory = Double(componentDict["Pages free"]!)
let purgeable = Double(componentDict["Pages purgeable"]!)
let compressed = Double(componentDict["Pages occupied by compressor"]!)
let tradTotal = ((wired + active + inactive + spec + throttled + freeMemory + compressed) * pagesizeDouble!) * byteConverter
let fileBacked = Double(componentDict["File-backed pages"]!)
let physicalTotal = ((appMemory + wired + compressed + fileBacked + purgeable + freeMemory) * pagesizeDouble!) * byteConverter

// swap usage
let vmSwapUsageOutput = Aftermath.shell("sysctl vm.swapusage")

if let vmSwapUsage = scannerModule(inputString: vmSwapUsageOutput, stringToFind: "used = ") {
self.addTextToFile(atUrl: writeFile, text: "Swap used: \(vmSwapUsage * 0.0009765625)\n")
}

// memory pressure
let memoryPressureOutput = Aftermath.shell("memory_pressure")
if let memoryPressure = scannerModule(inputString: memoryPressureOutput, stringToFind: "percentage: ") {
self.addTextToFile(atUrl: writeFile, text: "Memory Pressure: \(100 - memoryPressure)%")
}

// write out
self.addTextToFile(atUrl: writeFile, text: "\nTraditional Memory:\nWired Memory: \(wired)\nActive Memory: \(active)\nInactive Memory: \(inactive)\nPages Speculative: \(spec)\nPages Throttled: \(throttled)\nPurgeable: \(purgeable)\nCompressed: \(compressed)\nFree Memory: \(freeMemory)\nTotal: \(String(format: "%.2f", tradTotal))GiB\n")
self.addTextToFile(atUrl: writeFile, text: "\nActivity Monitor Memory:\nApp Memory: \(appMemory)\nWired: \(wired)\nCompressed: \(compressed)\nMemory Used: \(appMemory + wired + compressed)\nCached files: \(fileBacked + purgeable)\nTotal Physical: \(String(format: "%.2f", physicalTotal))GiB\n")
}
}
21 changes: 21 additions & 0 deletions persistence/BTM.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// BTM.swift
// aftermath
//
// Created by Stuart Ashenbrenner on 10/17/23.
//

import Foundation

class BTM: PersistenceModule {

override func run() {
self.log("Dumping btm file")

let command = "sfltool dumpbtm"
let output = Aftermath.shell(command)

let btmDumpFile = self.createNewCaseFile(dirUrl: moduleDirRoot, filename: "btm.txt")
self.addTextToFile(atUrl: btmDumpFile, text: output)
}
}
10 changes: 10 additions & 0 deletions persistence/PersistenceModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,34 @@ class PersistenceModule: AftermathModule, AMProto {
let hooks = LoginHooks(saveToRawDir: persistenceRawDir)
hooks.run()

// capture all cron tabs
let cron = Cron(saveToRawDir: persistenceRawDir)
cron.run()

// collect overrides file
let overrides = Overrides(saveToRawDir: persistenceRawDir)
overrides.run()

// write out all system extensions
let systemExtensions = SystemExtensions(saveToRawDir: persistenceRawDir)
systemExtensions.run()

// collect any periodic scripts
let periodicScripts = Periodic(saveToRawDir: persistenceRawDir)
periodicScripts.run()

// on older OSs, collect emond
let emond = Emond(saveToRawDir: persistenceRawDir)
emond.run()

// gather all Login Items
let loginItems = LoginItems(saveToRawDir: persistenceRawDir)
loginItems.run()

// dump the BTM file
let btmParser = BTM()
btmParser.run()

self.log("Finished gathering persistence mechanisms")

}
Expand Down
Loading

0 comments on commit 49aab5a

Please sign in to comment.