diff --git a/.gitignore b/.gitignore index bf8bd3e..75736b6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ KnockKnock.xcodeproj/xcuserdata/* Carthage/Build Carthage/Checkouts +.DS_Store diff --git a/Assets.xcassets/Contents.json b/Assets.xcassets/Contents.json index da4a164..73c0059 100644 --- a/Assets.xcassets/Contents.json +++ b/Assets.xcassets/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Assets.xcassets/Friends1Password.imageset/darkMode.png b/Assets.xcassets/Friends1Password.imageset/darkMode.png deleted file mode 100644 index 771e67b..0000000 Binary files a/Assets.xcassets/Friends1Password.imageset/darkMode.png and /dev/null differ diff --git a/Assets.xcassets/Friends1Password.imageset/lightMode.png b/Assets.xcassets/Friends1Password.imageset/lightMode.png deleted file mode 100644 index 5789fbc..0000000 Binary files a/Assets.xcassets/Friends1Password.imageset/lightMode.png and /dev/null differ diff --git a/Assets.xcassets/Friends1Password.imageset/Contents.json b/Assets.xcassets/FriendsKandji.imageset/Contents.json similarity index 100% rename from Assets.xcassets/Friends1Password.imageset/Contents.json rename to Assets.xcassets/FriendsKandji.imageset/Contents.json diff --git a/Assets.xcassets/FriendsKandji.imageset/darkMode.png b/Assets.xcassets/FriendsKandji.imageset/darkMode.png new file mode 100644 index 0000000..1da19ad Binary files /dev/null and b/Assets.xcassets/FriendsKandji.imageset/darkMode.png differ diff --git a/Assets.xcassets/FriendsKandji.imageset/lightMode.png b/Assets.xcassets/FriendsKandji.imageset/lightMode.png new file mode 100644 index 0000000..db16049 Binary files /dev/null and b/Assets.xcassets/FriendsKandji.imageset/lightMode.png differ diff --git a/Assets.xcassets/FriendsKolide.imageset/Contents.json b/Assets.xcassets/FriendsKolide.imageset/Contents.json new file mode 100644 index 0000000..28e5e0e --- /dev/null +++ b/Assets.xcassets/FriendsKolide.imageset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "mac", + "filename" : "darkMode.png" + }, + { + "idiom" : "mac", + "filename" : "lightMode.png", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ] + }, + { + "idiom" : "mac", + "filename" : "darkMode.png", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ] + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Assets.xcassets/FriendsKolide.imageset/darkMode.png b/Assets.xcassets/FriendsKolide.imageset/darkMode.png new file mode 100644 index 0000000..bbf7504 Binary files /dev/null and b/Assets.xcassets/FriendsKolide.imageset/darkMode.png differ diff --git a/Assets.xcassets/FriendsKolide.imageset/lightMode.png b/Assets.xcassets/FriendsKolide.imageset/lightMode.png new file mode 100644 index 0000000..86cc7b6 Binary files /dev/null and b/Assets.xcassets/FriendsKolide.imageset/lightMode.png differ diff --git a/Assets.xcassets/FriendsMacPaw.imageset/Contents.json b/Assets.xcassets/FriendsMacPaw.imageset/Contents.json new file mode 100644 index 0000000..28e5e0e --- /dev/null +++ b/Assets.xcassets/FriendsMacPaw.imageset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "mac", + "filename" : "darkMode.png" + }, + { + "idiom" : "mac", + "filename" : "lightMode.png", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ] + }, + { + "idiom" : "mac", + "filename" : "darkMode.png", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ] + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Assets.xcassets/FriendsMacPaw.imageset/darkMode.png b/Assets.xcassets/FriendsMacPaw.imageset/darkMode.png new file mode 100644 index 0000000..8912c1b Binary files /dev/null and b/Assets.xcassets/FriendsMacPaw.imageset/darkMode.png differ diff --git a/Assets.xcassets/FriendsMacPaw.imageset/lightMode.png b/Assets.xcassets/FriendsMacPaw.imageset/lightMode.png new file mode 100644 index 0000000..c45960b Binary files /dev/null and b/Assets.xcassets/FriendsMacPaw.imageset/lightMode.png differ diff --git a/Assets.xcassets/FriendsMosyle.imageset/Contents.json b/Assets.xcassets/FriendsMosyle.imageset/Contents.json new file mode 100644 index 0000000..c27ce88 --- /dev/null +++ b/Assets.xcassets/FriendsMosyle.imageset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "filename" : "darkMode.png", + "idiom" : "mac" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ], + "filename" : "lightMode.png", + "idiom" : "mac" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "darkMode.png", + "idiom" : "mac" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Assets.xcassets/FriendsMosyle.imageset/darkMode.png b/Assets.xcassets/FriendsMosyle.imageset/darkMode.png new file mode 100644 index 0000000..2917a01 Binary files /dev/null and b/Assets.xcassets/FriendsMosyle.imageset/darkMode.png differ diff --git a/Assets.xcassets/FriendsMosyle.imageset/lightMode.png b/Assets.xcassets/FriendsMosyle.imageset/lightMode.png new file mode 100644 index 0000000..44ed7ab Binary files /dev/null and b/Assets.xcassets/FriendsMosyle.imageset/lightMode.png differ diff --git a/Assets.xcassets/FriendsPANW.imageset/Contents.json b/Assets.xcassets/FriendsPANW.imageset/Contents.json new file mode 100644 index 0000000..c27ce88 --- /dev/null +++ b/Assets.xcassets/FriendsPANW.imageset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "filename" : "darkMode.png", + "idiom" : "mac" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ], + "filename" : "lightMode.png", + "idiom" : "mac" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "darkMode.png", + "idiom" : "mac" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Assets.xcassets/FriendsPANW.imageset/darkMode.png b/Assets.xcassets/FriendsPANW.imageset/darkMode.png new file mode 100644 index 0000000..72de9dc Binary files /dev/null and b/Assets.xcassets/FriendsPANW.imageset/darkMode.png differ diff --git a/Assets.xcassets/FriendsPANW.imageset/lightMode.png b/Assets.xcassets/FriendsPANW.imageset/lightMode.png new file mode 100644 index 0000000..2e7472d Binary files /dev/null and b/Assets.xcassets/FriendsPANW.imageset/lightMode.png differ diff --git a/Assets.xcassets/FriendsSophos.imageset/Contents.json b/Assets.xcassets/FriendsSophos.imageset/Contents.json new file mode 100644 index 0000000..c27ce88 --- /dev/null +++ b/Assets.xcassets/FriendsSophos.imageset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "filename" : "darkMode.png", + "idiom" : "mac" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ], + "filename" : "lightMode.png", + "idiom" : "mac" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "darkMode.png", + "idiom" : "mac" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Assets.xcassets/FriendsSophos.imageset/darkMode.png b/Assets.xcassets/FriendsSophos.imageset/darkMode.png new file mode 100644 index 0000000..883f3e6 Binary files /dev/null and b/Assets.xcassets/FriendsSophos.imageset/darkMode.png differ diff --git a/Assets.xcassets/FriendsSophos.imageset/lightMode.png b/Assets.xcassets/FriendsSophos.imageset/lightMode.png new file mode 100644 index 0000000..3826c41 Binary files /dev/null and b/Assets.xcassets/FriendsSophos.imageset/lightMode.png differ diff --git a/Cartfile b/Cartfile index cf79f9f..f56b57d 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1 @@ -github "getsentry/sentry-cocoa" "6.2.0" +github "getsentry/sentry-cocoa" "8.4.0" diff --git a/Cartfile.resolved b/Cartfile.resolved index cf79f9f..f56b57d 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1 @@ -github "getsentry/sentry-cocoa" "6.2.0" +github "getsentry/sentry-cocoa" "8.4.0" diff --git a/Consts.h b/Consts.h index f0f9971..53f6289 100755 --- a/Consts.h +++ b/Consts.h @@ -16,8 +16,11 @@ #define REQUESTED_FULL_DISK_ACCESS @"requestedFullDiskAccess" //supported plugins -static NSString * const SUPPORTED_PLUGINS[] = {@"AuthorizationPlugins", @"BrowserExtensions", @"CronJobs", @"DirectoryServicesPlugins", @"EventRules", @"Extensions", @"Kexts", @"LaunchItems", @"DylibInserts", @"DylibProxies", @"LoginItems", @"LogInOutHooks", @"PeriodicScripts", @"QuicklookPlugins", @"SpotlightImporters", @"StartupScripts", @"SystemExtensions"}; +static NSString * const SUPPORTED_PLUGINS[] = {@"AuthorizationPlugins", @"BrowserExtensions", @"BTM", @"CronJobs", @"DirectoryServicesPlugins", @"EventRules", @"Extensions", @"Kexts", @"LaunchItems", @"DylibInserts", @"DylibProxies", @"LoginItems", @"LogInOutHooks", @"PeriodicScripts", @"QuicklookPlugins", @"SpotlightImporters", @"StartupScripts", @"SystemExtensions"}; +//static NSString * const SUPPORTED_PLUGINS[] = {@"BTM"}; + + //sentry crash reporting URL #define SENTRY_DSN @"https://ba5d094e87014a529b25d90bae010b1c@sentry.io/1321683" diff --git a/ItemTableController.m b/ItemTableController.m index 334121c..f29257b 100755 --- a/ItemTableController.m +++ b/ItemTableController.m @@ -348,7 +348,7 @@ -(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn //set plist [((NSTextField*)[itemCell viewWithTag:TABLE_ROW_PLIST_LABEL]) setStringValue:((File*)item).plist]; - + //show [((NSTextField*)[itemCell viewWithTag:TABLE_ROW_PLIST_LABEL]) setHidden:NO]; } diff --git a/KnockKnock.xcodeproj/project.pbxproj b/KnockKnock.xcodeproj/project.pbxproj index b761264..868247f 100755 --- a/KnockKnock.xcodeproj/project.pbxproj +++ b/KnockKnock.xcodeproj/project.pbxproj @@ -12,7 +12,6 @@ 7D0DAB881CA758790049BAFF /* periodicIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 7D0DAB871CA758790049BAFF /* periodicIcon.png */; }; 7D44F4C51DAAEDE40085859C /* DylibProxies.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D44F4C41DAAEDE40085859C /* DylibProxies.m */; }; 7D44F4C71DAAEF3E0085859C /* proxyIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 7D44F4C61DAAEF3E0085859C /* proxyIcon.png */; }; - 7DAA77FD1B8C5786004840B9 /* MachO.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DAA77FC1B8C5786004840B9 /* MachO.m */; }; 7DE29CA41CA7BC5600DFA6A6 /* StartupScripts.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DE29CA21CA7BC5600DFA6A6 /* StartupScripts.m */; }; 7DE29CA61CA7C10500DFA6A6 /* startupScriptsIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 7DE29CA51CA7C10500DFA6A6 /* startupScriptsIcon.png */; }; 7DE2FE2E1D3F30BE006C1438 /* Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DE2FE2D1D3F30BE006C1438 /* Extensions.m */; }; @@ -106,6 +105,11 @@ CDBE491A1B5B25E30031FC22 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDBE49191B5B25E30031FC22 /* SystemConfiguration.framework */; }; CDBE491D1B5B44BB0031FC22 /* LogInOutHooks.m in Sources */ = {isa = PBXBuildFile; fileRef = CDBE491C1B5B44BB0031FC22 /* LogInOutHooks.m */; }; CDBE491F1B5B46040031FC22 /* logInOutIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = CDBE491E1B5B46040031FC22 /* logInOutIcon.png */; }; + CDBFE3992A49B8B9005A9819 /* libDumpBTM.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CDBFE3942A49B8B9005A9819 /* libDumpBTM.a */; }; + CDBFE39A2A49B8BA005A9819 /* MachO.m in Sources */ = {isa = PBXBuildFile; fileRef = CDBFE3972A49B8B9005A9819 /* MachO.m */; }; + CDBFE39D2A49B9F8005A9819 /* BTM.m in Sources */ = {isa = PBXBuildFile; fileRef = CDBFE39C2A49B9F8005A9819 /* BTM.m */; }; + CDBFE3A12A4AEC80005A9819 /* btmIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = CDBFE3A02A4AEC80005A9819 /* btmIcon.png */; }; + CDCA2C212A49B71500E8DD57 /* libDumpBTM.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CDCA2C202A49B71500E8DD57 /* libDumpBTM.a */; }; CDD1837F23774DF100BA6B86 /* QuicklookPlugins.m in Sources */ = {isa = PBXBuildFile; fileRef = CDD1837E23774DF100BA6B86 /* QuicklookPlugins.m */; }; CDD83FD41B50C48C0037124E /* Cronjobs.m in Sources */ = {isa = PBXBuildFile; fileRef = CDD83FD31B50C48C0037124E /* Cronjobs.m */; }; CDD83FD61B50C76F0037124E /* cronIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = CDD83FD51B50C76F0037124E /* cronIcon.png */; }; @@ -155,8 +159,6 @@ 7D44F4C31DAAEDE40085859C /* DylibProxies.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DylibProxies.h; path = Plugins/DylibProxies.h; sourceTree = ""; }; 7D44F4C41DAAEDE40085859C /* DylibProxies.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DylibProxies.m; path = Plugins/DylibProxies.m; sourceTree = ""; }; 7D44F4C61DAAEF3E0085859C /* proxyIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = proxyIcon.png; path = images/proxyIcon.png; sourceTree = SOURCE_ROOT; }; - 7DAA77FB1B8C5786004840B9 /* MachO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MachO.h; path = ../MachO/MachOParser/MachO.h; sourceTree = ""; }; - 7DAA77FC1B8C5786004840B9 /* MachO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MachO.m; path = ../MachO/MachOParser/MachO.m; sourceTree = ""; }; 7DE29CA21CA7BC5600DFA6A6 /* StartupScripts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = StartupScripts.m; path = Plugins/StartupScripts.m; sourceTree = ""; }; 7DE29CA31CA7BC5600DFA6A6 /* StartupScripts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StartupScripts.h; path = Plugins/StartupScripts.h; sourceTree = ""; }; 7DE29CA51CA7C10500DFA6A6 /* startupScriptsIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = startupScriptsIcon.png; path = images/startupScriptsIcon.png; sourceTree = SOURCE_ROOT; }; @@ -290,6 +292,14 @@ CDBE491B1B5B44BB0031FC22 /* LogInOutHooks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LogInOutHooks.h; path = Plugins/LogInOutHooks.h; sourceTree = ""; }; CDBE491C1B5B44BB0031FC22 /* LogInOutHooks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = LogInOutHooks.m; path = Plugins/LogInOutHooks.m; sourceTree = ""; }; CDBE491E1B5B46040031FC22 /* logInOutIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = logInOutIcon.png; path = images/logInOutIcon.png; sourceTree = SOURCE_ROOT; }; + CDBFE3942A49B8B9005A9819 /* libDumpBTM.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libDumpBTM.a; sourceTree = ""; }; + CDBFE3952A49B8B9005A9819 /* dumpBTM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dumpBTM.h; sourceTree = ""; }; + CDBFE3972A49B8B9005A9819 /* MachO.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MachO.m; sourceTree = ""; }; + CDBFE3982A49B8B9005A9819 /* MachO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MachO.h; sourceTree = ""; }; + CDBFE39B2A49B9F8005A9819 /* BTM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BTM.h; path = Plugins/BTM.h; sourceTree = ""; }; + CDBFE39C2A49B9F8005A9819 /* BTM.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BTM.m; path = Plugins/BTM.m; sourceTree = ""; }; + CDBFE3A02A4AEC80005A9819 /* btmIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = btmIcon.png; path = images/btmIcon.png; sourceTree = ""; }; + CDCA2C202A49B71500E8DD57 /* libDumpBTM.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libDumpBTM.a; path = Libraries/BTM/libDumpBTM.a; sourceTree = ""; }; CDD1837D23774DF000BA6B86 /* QuicklookPlugins.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = QuicklookPlugins.h; path = Plugins/QuicklookPlugins.h; sourceTree = ""; }; CDD1837E23774DF100BA6B86 /* QuicklookPlugins.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = QuicklookPlugins.m; path = Plugins/QuicklookPlugins.m; sourceTree = ""; }; CDD83FD21B50C48C0037124E /* Cronjobs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Cronjobs.h; path = Plugins/Cronjobs.h; sourceTree = ""; }; @@ -324,8 +334,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + CDCA2C212A49B71500E8DD57 /* libDumpBTM.a in Frameworks */, CDBE491A1B5B25E30031FC22 /* SystemConfiguration.framework in Frameworks */, CD6DE4D1219E9AD30058094E /* Sentry.framework in Frameworks */, + CDBFE3992A49B8B9005A9819 /* libDumpBTM.a in Frameworks */, CDDBC2301B04771100B021E0 /* ServiceManagement.framework in Frameworks */, CDF08CEA1AC8D97B009B3423 /* Quartz.framework in Frameworks */, CD6095731A87067D00E091CD /* Security.framework in Frameworks */, @@ -339,6 +351,7 @@ 1D21BC42172AF43D009D1CFD = { isa = PBXGroup; children = ( + CDBFE3922A49B89A005A9819 /* Libraries */, CDF08CE31AC89FD4009B3423 /* 3rdParty */, CD5854D921A6103A00A438B0 /* ClickableTextField.h */, CD5854DA21A6103A00A438B0 /* ClickableTextField.m */, @@ -350,7 +363,6 @@ CD7B9FA91AD08FA100DF3C71 /* KKRow.h */, CD7B9FAA1AD08FA100DF3C71 /* KKRow.m */, 1D21BC54172AF43D009D1CFD /* KnockKnock */, - CDF08CD81AC881F9009B3423 /* MachOParser */, CD6BBBEE1B52032D00506D0D /* NSApplicationKeyEvents.h */, CD6BBBEF1B52032D00506D0D /* NSApplicationKeyEvents.m */, CDA81D861A96F429009790E2 /* Plugins */, @@ -375,6 +387,7 @@ 1D21BC4D172AF43D009D1CFD /* Frameworks */ = { isa = PBXGroup; children = ( + CDCA2C202A49B71500E8DD57 /* libDumpBTM.a */, CD6DE4D0219E9AD30058094E /* Sentry.framework */, CDBE49191B5B25E30031FC22 /* SystemConfiguration.framework */, CDDBC22F1B04771100B021E0 /* ServiceManagement.framework */, @@ -420,7 +433,6 @@ CDF08CC51AC46E75009B3423 /* VTButton.m */, ); name = KnockKnock; - path = "Lesson 53"; sourceTree = ""; }; 1D21BC55172AF43D009D1CFD /* Supporting Files */ = { @@ -440,6 +452,7 @@ isa = PBXGroup; children = ( CDAB98A01AEAFAFA00C75B4B /* authorizationIcon.png */, + CDBFE3A02A4AEC80005A9819 /* btmIcon.png */, CD6BBBE81B50D67A00506D0D /* binaryIcon.icns */, CDF08CF11ACA6864009B3423 /* browserIcon.png */, CDA81D5F1A95B4E9009790E2 /* bug.png */, @@ -532,6 +545,8 @@ CDA81D861A96F429009790E2 /* Plugins */ = { isa = PBXGroup; children = ( + CDBFE39B2A49B9F8005A9819 /* BTM.h */, + CDBFE39C2A49B9F8005A9819 /* BTM.m */, CDAB98971AEAA6CB00C75B4B /* AuthorizationPlugins.h */, CDAB98981AEAA6CB00C75B4B /* AuthorizationPlugins.m */, CDA81E081A9B0AD8009790E2 /* BrowserExtensions.h */, @@ -598,13 +613,31 @@ name = UI; sourceTree = ""; }; - CDF08CD81AC881F9009B3423 /* MachOParser */ = { + CDBFE3922A49B89A005A9819 /* Libraries */ = { isa = PBXGroup; children = ( - 7DAA77FB1B8C5786004840B9 /* MachO.h */, - 7DAA77FC1B8C5786004840B9 /* MachO.m */, + CDBFE3932A49B8B9005A9819 /* BTM */, + CDBFE3962A49B8B9005A9819 /* MachO */, ); - name = MachOParser; + path = Libraries; + sourceTree = ""; + }; + CDBFE3932A49B8B9005A9819 /* BTM */ = { + isa = PBXGroup; + children = ( + CDBFE3942A49B8B9005A9819 /* libDumpBTM.a */, + CDBFE3952A49B8B9005A9819 /* dumpBTM.h */, + ); + path = BTM; + sourceTree = ""; + }; + CDBFE3962A49B8B9005A9819 /* MachO */ = { + isa = PBXGroup; + children = ( + CDBFE3972A49B8B9005A9819 /* MachO.m */, + CDBFE3982A49B8B9005A9819 /* MachO.h */, + ); + path = MachO; sourceTree = ""; }; CDF08CE31AC89FD4009B3423 /* 3rdParty */ = { @@ -690,6 +723,7 @@ 7D0DAB881CA758790049BAFF /* periodicIcon.png in Resources */, CD02195E1AD39A74005148A2 /* ResultsWindow.xib in Resources */, CDA81D5B1A95B4B4009790E2 /* InfoPlist.strings in Resources */, + CDBFE3A12A4AEC80005A9819 /* btmIcon.png in Resources */, CDA81D6B1A95B4E9009790E2 /* show.png in Resources */, CD24F074219D51210081B0E5 /* whitelistedKexts.json in Resources */, CDF08CF31ACA6864009B3423 /* browserIcon.png in Resources */, @@ -760,9 +794,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7DAA77FD1B8C5786004840B9 /* MachO.m in Sources */, CD83887F1AACCEDF000EB098 /* VirusTotal.m in Sources */, 7D0DAB861CA752AE0049BAFF /* PeriodicScrips.m in Sources */, + CDBFE39A2A49B8BA005A9819 /* MachO.m in Sources */, + CDBFE39D2A49B9F8005A9819 /* BTM.m in Sources */, CDF08CDF1AC886F2009B3423 /* VTInfoWindowController.m in Sources */, CD0219611AD39A83005148A2 /* ResultsWindowController.m in Sources */, CDA81DEE1A99B5F8009790E2 /* Filter.m in Sources */, @@ -933,7 +968,7 @@ CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2.3.0; + CURRENT_PROJECT_VERSION = 2.4.0; DEVELOPMENT_TEAM = VBG97UB4TA; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -942,10 +977,15 @@ ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "KnockKnock-Prefix.pch"; + HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/Libraries/**"; INFOPLIST_FILE = "KnockKnock-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Libraries/BTM", + ); MACOSX_DEPLOYMENT_TARGET = 10.11; - MARKETING_VERSION = 2.3.0; + MARKETING_VERSION = 2.4.0; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = KnockKnock; @@ -962,7 +1002,7 @@ CODE_SIGN_IDENTITY = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 2.3.0; + CURRENT_PROJECT_VERSION = 2.4.0; DEVELOPMENT_TEAM = VBG97UB4TA; ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -971,10 +1011,15 @@ ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "KnockKnock-Prefix.pch"; + HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/Libraries/**"; INFOPLIST_FILE = "KnockKnock-Info.plist"; LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Libraries/BTM", + ); MACOSX_DEPLOYMENT_TARGET = 10.11; - MARKETING_VERSION = 2.3.0; + MARKETING_VERSION = 2.4.0; ONLY_ACTIVE_ARCH = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = KnockKnock; diff --git a/Libraries/BTM/dumpBTM.h b/Libraries/BTM/dumpBTM.h new file mode 100644 index 0000000..77d8b76 --- /dev/null +++ b/Libraries/BTM/dumpBTM.h @@ -0,0 +1,41 @@ +// +// dumpBTM.h +// library +// +// Created by Patrick Wardle on 1/20/23. +// + +@import Foundation; + +//keys for dictionary +#define KEY_BTM_PATH @"path" +#define KEY_BTM_ERROR @"error" +#define KEY_BTM_VERSION @"version" +#define KEY_BTM_ITEMS_BY_USER_ID @"itemsByUserIdentifier" + +//keys for item(s) +#define KEY_BTM_ITEM_UUID @"uuid" +#define KEY_BTM_ITEM_NAME @"name" +#define KEY_BTM_ITEM_DEV_NAME @"devName" +#define KEY_BTM_ITEM_TEAM_ID @"teamID" +#define KEY_BTM_ITEM_TYPE @"type" +#define KEY_BTM_ITEM_TYPE_DETAILS @"typeDetails" +#define KEY_BTM_ITEM_DISPOSITION @"disposition" +#define KEY_BTM_ITEM_DISPOSITION_DETAILS @"dispositionDetails" +#define KEY_BTM_ITEM_ID @"id" +#define KEY_BTM_ITEM_URL @"url" +#define KEY_BTM_ITEM_EXE_PATH @"exePath" +#define KEY_BTM_ITEM_GENERATION @"generation" +#define KEY_BTM_ITEM_BUNDLE_ID @"bundleID" +#define KEY_BTM_ITEM_ASSOCIATED_IDS @"associatedBundleIDs" +#define KEY_BTM_ITEM_PARENT_ID @"parentID" +#define KEY_BTM_ITEM_EMBEDDED_IDS @"embeddedIDs" + +//APIs +// note: path is optional +NSInteger dumpBTM(NSURL* path); +NSDictionary* parseBTM(NSURL* path); + + + + diff --git a/Libraries/BTM/libDumpBTM.a b/Libraries/BTM/libDumpBTM.a new file mode 100644 index 0000000..6b50ed5 Binary files /dev/null and b/Libraries/BTM/libDumpBTM.a differ diff --git a/Libraries/MachO/MachO.h b/Libraries/MachO/MachO.h new file mode 100644 index 0000000..0e2df2f --- /dev/null +++ b/Libraries/MachO/MachO.h @@ -0,0 +1,54 @@ +// +// MachO.h +// MachOParser +// +// Created by Patrick Wardle on 2/6/15. +// Copyright (c) 2015 Objective-See, LLC. All rights reserved. +// + +#import + +/* CONSTS */ + +//dictionary keys +#define KEY_BINARY_PATH @"binaryPath" +#define KEY_MACHO_HEADERS @"machoHeaders" +#define KEY_LOAD_COMMANDS @"loadCommands" + +#define KEY_HEADER_OFFSET @"headerOffset" +#define KEY_HEADER_SIZE @"headerSize" +#define KEY_HEADER_BINARY_TYPE @"headerType" +#define KEY_HEADER_BYTE_ORDER @"headerByteOrder" +#define KEY_IS_PACKED @"isPacked" +#define KEY_IS_ENCRYPTED @"isEncryted" + +#define KEY_LC_RPATHS @"lcRpath" +#define KEY_LC_REEXPORT_DYLIBS @"lcRexports" +#define KEY_LC_LOAD_DYLIBS @"lcLoadDylib" +#define KEY_LC_LOAD_WEAK_DYLIBS @"lcLoadWeakDylib" + + +@interface MachO : NSObject +{ + +} + +//info dictionary +// ->contains everything parsed out of the file +@property(nonatomic, retain)NSMutableDictionary* binaryInfo; + +//binary's data +@property(nonatomic, retain)NSData* binaryData; + +//segment names found in various packers +@property(nonatomic, retain)NSSet* packerSegmentNames; + + +/* METHODS */ + +//parse a binary +// ->extract all required/interesting stuff +-(BOOL)parse:(NSString*)binaryPath classify:(BOOL)shouldClassify; + + +@end diff --git a/Libraries/MachO/MachO.m b/Libraries/MachO/MachO.m new file mode 100644 index 0000000..2d1d188 --- /dev/null +++ b/Libraries/MachO/MachO.m @@ -0,0 +1,857 @@ +// +// MachO.m +// MachOParser +// +// Created by Patrick Wardle on 2/6/15. +// Copyright (c) 2015 Objective-See, LLC. All rights reserved. +// + +#import "MachO.h" + +#import +#import +#import +#import +#import + +@implementation MachO + +@synthesize binaryInfo; +@synthesize binaryData; +@synthesize packerSegmentNames; + +//init +-(id)init +{ + //init super + self = [super init]; + if(nil != self) + { + //alloc info dictionary + // ->contains everything collected about the file + binaryInfo = [NSMutableDictionary dictionary]; + + //init array for machO headers + self.binaryInfo[KEY_MACHO_HEADERS] = [NSMutableArray array]; + + //init array for LC_RPATHS + self.binaryInfo[KEY_LC_RPATHS] = [NSMutableArray array]; + + //init array for LC_REEXPORT_DYLIBs + self.binaryInfo[KEY_LC_REEXPORT_DYLIBS] = [NSMutableArray array]; + + //init array for LC_LOAD_DYLIBs + self.binaryInfo[KEY_LC_LOAD_DYLIBS] = [NSMutableArray array]; + + //init array for LC_LOAD_WEAK_DYLIBs + self.binaryInfo[KEY_LC_LOAD_WEAK_DYLIBS] = [NSMutableArray array]; + + //init packer seg names + // upx: __XHDR + // mpress: __MPRESS__* + packerSegmentNames = [NSSet setWithObjects:@"__XHDR", @"__MPRESS__", nil]; + } + + return self; +} + +//parse a binary +// ->extract all required/interesting stuff +-(BOOL)parse:(NSString*)binaryPath classify:(BOOL)shouldClassify +{ + //ret var + BOOL wasParsed = NO; + + //dbg msg + //NSLog(@"parsing %@", binaryPath); + + //save path + self.binaryInfo[KEY_BINARY_PATH] = binaryPath; + + //load binary into memory + self.binaryData = [NSData dataWithContentsOfFile:binaryPath]; + if( (nil == self.binaryData) || + (NULL == [self.binaryData bytes]) ) + { + //err msg + //NSLog(@"OBJECTIVE-SEE ERROR: failed to load %@ into memory", binaryPath); + + //bail + goto bail; + } + + //parse headers + // ->populates 'KEY_MACHO_HEADERS' array in 'binaryInfo' iVar + if(YES != [self parseHeaders]) + { + //err msg + //NSLog(@"OBJECTIVE-SEE ERROR: failed to find any machO headers"); + + //bail + goto bail; + } + + //parse headers + // ->populates 'KEY_MACHO_HEADERS' array in 'binaryInfo' iVar + if(YES != [self parseLoadCmds]) + { + //err msg + //NSLog(@"OBJECTIVE-SEE ERROR: failed to parse load commands"); + + //bail + goto bail; + } + + //dbg msg + //NSLog(@"parsed load commands"); + + //only do packer/encryption checks if specified + if(YES == shouldClassify) + { + //first determine if binary is encrypted + self.binaryInfo[KEY_IS_ENCRYPTED] = [NSNumber numberWithBool:[self isEncrypted]]; + + //all encrypted binaries will also appear packed + // ->so only check if unencrypted binaries are unpacked + if(YES != [self.binaryInfo[KEY_IS_ENCRYPTED] boolValue]) + { + //determine if packed + self.binaryInfo[KEY_IS_PACKED] = [NSNumber numberWithBool:[self isPacked]]; + } + } + + //happy + wasParsed = YES; + +//bail +bail: + + return wasParsed; +} + +//parse all machO headers +-(BOOL)parseHeaders +{ + //return var + BOOL wasParsed = NO; + + //start of macho header + const uint32_t *headerStart = NULL; + + //swapped flag + BOOL shouldSwap = NO; + + //header dictionary + NSDictionary* header = nil; + + //number of machO headers + uint32_t headerCount = 0; + + //header offsets + NSMutableArray* headerOffsets = nil; + + //per-architecture header + struct fat_arch *arch = NULL; + + //pointer to binary's data + const void* binaryBytes = NULL; + + //alloc array + headerOffsets = [NSMutableArray array]; + + //grab binary's bytes + binaryBytes = [self.binaryData bytes]; + if(NULL == binaryBytes) + { + //bail + goto bail; + } + + //init start of header + headerStart = binaryBytes; + + //handle universal (fat) case + if( (FAT_MAGIC == *headerStart) || + (FAT_CIGAM == *headerStart) ) + { + //dbg msg + //NSLog(@"parsing universal binary"); + + //swap if needed + if(FAT_CIGAM == *headerStart) + { + //set flag + shouldSwap = YES; + + //swap + swap_fat_header((struct fat_header*)headerStart, 0); + } + + //get number of fat_arch structs + // ->one per each architecture + headerCount = ((struct fat_header*)binaryBytes)->nfat_arch; + + //get offsets of all headers + for(uint32_t i = 0; i < headerCount; i++) + { + //get current struct fat_arch * + // ->base + size of fat_header + size of fat_archs + arch = (struct fat_arch*)((unsigned char*)binaryBytes + sizeof(struct fat_header) + i * sizeof(struct fat_arch)); + + //swap if needed + if(YES == shouldSwap) + { + //swap + swap_fat_arch(arch, 0x01, 0); + } + + //sanity check + // ->make sure arch is something 'within' binary + if( ((unsigned char*)arch < (unsigned char*)binaryBytes) || + ((unsigned char*)(binaryBytes + self.binaryData.length) < (unsigned char*)((unsigned char*)arch + sizeof(struct fat_arch))) ) + { + //err + goto bail; + } + + //save into header offset array + [headerOffsets addObject:[NSNumber numberWithUnsignedInt:arch->offset]]; + } + } + + //not fat + // ->just add start as (only) header offset + else + { + //dbg msg + //NSLog(@"parsing non-universal binary"); + + //add start + [headerOffsets addObject:@0x0]; + } + + //classify all headers + for(NSNumber* headerOffset in headerOffsets) + { + //skip invalid header offsets + if(headerOffset.unsignedIntValue > [self.binaryData length]) + { + //skip + continue; + } + + //grab start of header + headerStart = binaryBytes + headerOffset.unsignedIntValue; + + //classify header + switch(*headerStart) + { + //32bit mach-O + // ->little-endian version + case MH_CIGAM: + + //swap + swap_mach_header((struct mach_header*)headerStart, 0); + + //init header dictionary + header = @{ + KEY_HEADER_OFFSET:headerOffset, + KEY_HEADER_SIZE:@(sizeof(struct mach_header)), + KEY_HEADER_BINARY_TYPE:[NSNumber numberWithInt:((struct mach_header*)headerStart)->filetype], + KEY_HEADER_BYTE_ORDER: [NSNumber numberWithInt:LITTLE_ENDIAN], + KEY_LOAD_COMMANDS: [[NSPointerArray alloc] initWithOptions:NSPointerFunctionsOpaqueMemory] + }; + + //add header + [self.binaryInfo[KEY_MACHO_HEADERS] addObject:header]; + + //next + break; + + //32-bit mach-O + // ->big-endian version + case MH_MAGIC: + + //init header dictionary + header = @{ + KEY_HEADER_OFFSET:headerOffset, + KEY_HEADER_SIZE:@(sizeof(struct mach_header)), + KEY_HEADER_BINARY_TYPE:[NSNumber numberWithInt:((struct mach_header*)headerStart)->filetype], + KEY_HEADER_BYTE_ORDER: [NSNumber numberWithInt:BIG_ENDIAN], + KEY_LOAD_COMMANDS: [[NSPointerArray alloc] initWithOptions:NSPointerFunctionsOpaqueMemory] + }; + + //add header + [self.binaryInfo[KEY_MACHO_HEADERS] addObject:header]; + + //next + break; + + //64-bit mach-O + // ->little-endian version + case MH_CIGAM_64: + + //swap + swap_mach_header_64((struct mach_header_64*)headerStart, 0); + + //init header dictionary + header = @{ + KEY_HEADER_OFFSET:headerOffset, + KEY_HEADER_SIZE:@(sizeof(struct mach_header_64)), + KEY_HEADER_BINARY_TYPE:[NSNumber numberWithInt:((struct mach_header_64*)headerStart)->filetype], + KEY_HEADER_BYTE_ORDER: [NSNumber numberWithInt:LITTLE_ENDIAN], + KEY_LOAD_COMMANDS: [[NSPointerArray alloc] initWithOptions:NSPointerFunctionsOpaqueMemory] + }; + + //add header + [self.binaryInfo[KEY_MACHO_HEADERS] addObject:header]; + + //next + break; + + //64-bit mach-O + // ->big-endian version + case MH_MAGIC_64: + + //init header dictionary + header = @{ + KEY_HEADER_OFFSET:headerOffset, + KEY_HEADER_SIZE:@(sizeof(struct mach_header_64)), + KEY_HEADER_BINARY_TYPE:[NSNumber numberWithInt:((struct mach_header_64*)headerStart)->filetype], + KEY_HEADER_BYTE_ORDER: [NSNumber numberWithInt:BIG_ENDIAN], + KEY_LOAD_COMMANDS: [[NSPointerArray alloc] initWithOptions:NSPointerFunctionsOpaqueMemory] + }; + + //add header + [self.binaryInfo[KEY_MACHO_HEADERS] addObject:header]; + + //next + break; + + default: + + //err msg + //NSLog(@"OBJECTIVE-SEE ERROR: unknown machO magic: %#x", *headerStart); + + //next + break; + + }//switch, classifying headers + + }//for all headers + + //sanity check + // ->make sure parser found at least one header + if(0 != [self.binaryInfo[KEY_MACHO_HEADERS] count]) + { + //happy + wasParsed = YES; + } + +//bail +bail: + + return wasParsed; +} + +//parse the load commands +// ->for now just save LC_RPATH, LC_LOAD_DYLIB, and LC_LOAD_WEAK_DYLIB +-(BOOL)parseLoadCmds +{ + //ret var + BOOL wasParsed = NO; + + //pointer to load command structure + struct load_command *loadCommand = NULL; + + //path in load commands such as LC_LOAD_DYLIB + NSString* path = nil; + + //pointer to binary's data + const void* binaryBytes = NULL; + + //current macho header + struct mach_header* currentHeader = NULL; + + //grab binary's bytes + binaryBytes = [self.binaryData bytes]; + if(NULL == binaryBytes) + { + //bail + goto bail; + } + + //iterate over all machO headers + for(NSDictionary* machoHeader in self.binaryInfo[KEY_MACHO_HEADERS]) + { + //get pointer to current machO header + currentHeader = (struct mach_header*)(unsigned char*)(binaryBytes + [machoHeader[KEY_HEADER_OFFSET] unsignedIntegerValue]); + + //get first load command + // ->immediately follows header + loadCommand = (struct load_command*)(unsigned char*)(binaryBytes + [machoHeader[KEY_HEADER_OFFSET] unsignedIntegerValue] + [machoHeader[KEY_HEADER_SIZE] unsignedIntValue]); + + //iterate over all load commands + // ->number of commands is in 'ncmds' member of (current) header struct + for(uint32_t i = 0; i < currentHeader->ncmds; i++) + { + //sanity check load command + if((unsigned char*)loadCommand > (unsigned char*)((unsigned char*)currentHeader + [machoHeader[KEY_HEADER_SIZE] unsignedIntegerValue] + currentHeader->sizeofcmds)) + { + //bail + goto bail; + } + + //swap if needed + if(LITTLE_ENDIAN == [machoHeader[KEY_HEADER_BYTE_ORDER] unsignedIntegerValue]) + { + //swap + // ->manually swap, cuz don't won't to affect in memory values + switch (OSSwapBigToHostInt32(loadCommand->cmd)) + { + case LC_SEGMENT: + + //swap + swap_segment_command((struct segment_command *)loadCommand, 0x0); + break; + + case LC_SEGMENT_64: + + //swap + swap_segment_command_64((struct segment_command_64 *)loadCommand, 0x0); + break; + + default: + + //swap + swap_load_command(loadCommand, 0x0); + break; + + }//switch + + }//need to swap + + //save load command + [machoHeader[KEY_LOAD_COMMANDS] addPointer:loadCommand]; + + //handle load commands of interest + switch(loadCommand->cmd) + { + //LC_RPATHs + // ->extract and save path + case LC_RPATH: + + //extract name + path = [self extractPath:loadCommand byteOrder:machoHeader[KEY_HEADER_BYTE_ORDER]]; + + //save if new + if(YES != [self.binaryInfo[KEY_LC_RPATHS] containsObject:path]) + { + //save + [self.binaryInfo[KEY_LC_RPATHS] addObject:path]; + } + + break; + + //LC_REEXPORT_DYLIB + // ->extract and save path + case LC_REEXPORT_DYLIB: + + //extract name + path = [self extractPath:loadCommand byteOrder:machoHeader[KEY_HEADER_BYTE_ORDER]]; + + //save if new + if(YES != [self.binaryInfo[KEY_LC_REEXPORT_DYLIBS] containsObject:path]) + { + //save + [self.binaryInfo[KEY_LC_REEXPORT_DYLIBS] addObject:path]; + } + + break; + + //LC_LOAD_DYLIB and LC_LOAD_WEAK_DYLIB + // ->extract and save path + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + + //extract name + path = [self extractPath:loadCommand byteOrder:machoHeader[KEY_HEADER_BYTE_ORDER]]; + + //save if new dylib + if( (LC_LOAD_DYLIB == loadCommand->cmd) && + (YES != [self.binaryInfo[KEY_LC_LOAD_DYLIBS] containsObject:path]) ) + { + //save + [self.binaryInfo[KEY_LC_LOAD_DYLIBS] addObject:path]; + } + + //save if new weak dylib + else if( (LC_LOAD_WEAK_DYLIB == loadCommand->cmd) && + (YES != [self.binaryInfo[KEY_LC_LOAD_WEAK_DYLIBS] containsObject:path]) ) + { + //save + [self.binaryInfo[KEY_LC_LOAD_WEAK_DYLIBS] addObject:path]; + } + + break; + + default: + + break; + } + + //got to next load command + // ->immediately follows current one + loadCommand = (struct load_command *)(((unsigned char*)((unsigned char*)loadCommand + loadCommand->cmdsize))); + + }//all load commands + + }//all machO headers + + //happy + wasParsed = YES; + +//bail +bail: + + return wasParsed; +} + +//determine if binary is encrypted +// with OS X's native encryption scheme +// see: http://osxbook.com/book/bonus/chapter7/tpmdrmmyth/ +-(BOOL)isEncrypted +{ + //flag + BOOL encrypted = NO; + + //load command + struct load_command* loadCommand = NULL; + + //flags + uint32_t segmentFlags = 0; + + //check text segments + // ->any marked encrypted; set flag + for(NSMutableDictionary* machoHeader in self.binaryInfo[KEY_MACHO_HEADERS]) + { + //check all load commands + for(NSUInteger i = 0; i< [machoHeader[KEY_LOAD_COMMANDS] count]; i++) + { + //grab load command + loadCommand = [machoHeader[KEY_LOAD_COMMANDS] pointerAtIndex:i]; + + //ignore non-segments + if( (loadCommand->cmd != LC_SEGMENT) && + (loadCommand->cmd != LC_SEGMENT_64) ) + { + //skip + continue; + } + + //ignore everything that is not a text segment + // ->for name check, segment_command & segment_command_64 are same + if(0 != strncmp(((struct segment_command *)loadCommand)->segname, SEG_TEXT, sizeof(((struct segment_command *)loadCommand)->segname))) + { + //skip + continue; + } + + //grab flags + // ->32bit + if(sizeof(struct mach_header) == [machoHeader[KEY_HEADER_SIZE] integerValue]) + { + //flags + segmentFlags = ((struct segment_command *)loadCommand)->flags; + + } + //grab flags + // ->64bit + else if(sizeof(struct mach_header_64) == [machoHeader[KEY_HEADER_SIZE] integerValue]) + { + //flags + segmentFlags = ((struct segment_command_64 *)loadCommand)->flags; + + } + + //check if segment is protected + if(SG_PROTECTED_VERSION_1 == (segmentFlags & SG_PROTECTED_VERSION_1)) + { + //set flag + encrypted = YES; + + //bail + // ->any marked encrypted; set flag for all + goto bail; + } + + }//all load commands + + }//all macho headers (e.g. fat file) + +//bail +bail: + + return encrypted; +} + +//determine if packed +// segment names and/or entropy +// see: https://github.com/hiddenillusion/AnalyzePE/blob/master/peutils.py +-(BOOL)isPacked +{ + //flag + BOOL packed = NO; + + //file's data + NSData* fileData = nil; + + //file's data, as bytes + char* fileBytes = NULL; + + //load command + struct load_command* loadCommand = NULL; + + //segment offset + u_int64_t segmentOffset = 0; + + //segment size + u_int64_t segmentSize = 0; + + //segment entropy + float segmentEntropy = 0.0f; + + //total + float totalCompressedData = 0.0f; + + //segment name + NSString* segmentName = nil; + + //segment name length + NSUInteger segmentNameLength = 0; + + //open/read into file + fileData = [NSData dataWithContentsOfFile:self.binaryInfo[KEY_BINARY_PATH]]; + if(nil == fileData) + { + //bail + goto bail; + } + + //get raw bytes + fileBytes = (char*)[fileData bytes]; + + //check text segments + // ->any marked encrypted; set flag + for(NSMutableDictionary* machoHeader in self.binaryInfo[KEY_MACHO_HEADERS]) + { + //check all load commands + for(NSUInteger i = 0; i<[machoHeader[KEY_LOAD_COMMANDS] count]; i++) + { + //grab load command + loadCommand = [machoHeader[KEY_LOAD_COMMANDS] pointerAtIndex:i]; + + //ignore non-segments + if( (loadCommand->cmd != LC_SEGMENT) && + (loadCommand->cmd != LC_SEGMENT_64) ) + { + //skip + continue; + } + + //init segment name length + segmentNameLength = MIN(strlen(((struct segment_command *)loadCommand)->segname), sizeof(((struct segment_command *)loadCommand)->segname)); + + //sanity check + if(0 == segmentNameLength) + { + //skip + continue; + } + + //init segment name + segmentName = [[NSString alloc] initWithBytes:((struct segment_command *)loadCommand)->segname length:segmentNameLength encoding:NSUTF8StringEncoding]; + + //check if segment name matches known packer + // ->upx, mpress etc have unique segment names + if(YES == [self.packerSegmentNames containsObject:segmentName]) + { + //dbg msg + //NSLog(@"found match w/ packed section: %@", segmentName); + + //got match + packed = YES; + + //bail + // ->its packed + goto bail; + + } + + //32bit + // ->get offset/size of segment + if(sizeof(struct mach_header) == [machoHeader[KEY_HEADER_SIZE] integerValue]) + { + //offset + segmentOffset = ((struct segment_command *)loadCommand)->fileoff; + + //size + segmentSize = ((struct segment_command *)loadCommand)->filesize; + + } + //64bit + // ->get offset/size of segment + else if(sizeof(struct mach_header_64) == [machoHeader[KEY_HEADER_SIZE] integerValue]) + { + //offset + segmentOffset = ((struct segment_command_64 *)loadCommand)->fileoff; + + //size + segmentSize = ((struct segment_command_64 *)loadCommand)->filesize; + } + + //calc entropy + // ->does entire segment... + segmentEntropy = [self calcEntropy:&fileBytes[segmentOffset] length:segmentSize]; + + //dbg msg + //NSLog(@"%s's entropy: %f", ((struct segment_command *)loadCommand)->segname, segmentEntropy); + + //TODO: test more! + if(segmentEntropy > 7.2f) + { + //inc total + totalCompressedData += segmentSize; + } + + }//all load commands + + //dbg msg + //NSLog(@"final calc: %f\n", (1.0 * totalCompressedData)/fileData.length); + + //final calculation for architecture + if( ((1.0 * totalCompressedData)/fileData.length) > .2) + { + //set + packed = YES; + + //bail + // ->its packed + goto bail; + + } + + }//for all macho headers (e.g. fat file) + +//bail +bail: + + return packed; +} + +//based on https://github.com/erocarrera/pefile/blob/master/pefile.py +-(float)calcEntropy:(char*)data length:(NSUInteger)length +{ + //entropy + float entropy = 0.0f; + + //occurances array + unsigned int occurrences[256] = {0}; + + //intermediate var + float pX = 0.0f; + + //sanity check + if(0 == length) + { + //bail + goto bail; + } + + //count all occurances + for(NSUInteger i = 0; iis a little tricky due to offsets and lengths of strings (null paddings, etc) +-(NSString*)extractPath:(struct load_command *)loadCommand byteOrder:(NSNumber*)byteOrder +{ + //offset + size_t pathOffset = 0; + + //path bytes + char* pathBytes = NULL; + + //length of path + size_t pathLength = 0; + + //path + NSString* path = nil; + + //set path offset + // ->different based on load command type + switch(loadCommand->cmd) + { + //LC_RPATHs + case LC_RPATH: + + //set offset + pathOffset = sizeof(struct rpath_command); + + break; + + //LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIBS + case LC_LOAD_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_WEAK_DYLIB: + + //set offset + pathOffset = sizeof(struct dylib_command); + + break; + + default: + break; + } + + //init pointer to path's bytes + pathBytes = (char*)loadCommand + pathOffset; + + //set path's length + // ->min of strlen/value calculated from load command size + pathLength = MIN(strlen(pathBytes), (loadCommand->cmdsize - pathOffset)); + + //create nstring version of path + path = [[NSString alloc] initWithBytes:pathBytes length:pathLength encoding:NSUTF8StringEncoding]; + + return path; +} + + +@end diff --git a/Plugins/BTM.h b/Plugins/BTM.h new file mode 100755 index 0000000..f2b132c --- /dev/null +++ b/Plugins/BTM.h @@ -0,0 +1,19 @@ +// +// BTM.h +// KnockKnock +// +// Created by Patrick Wardle on 6/26/23. +// Copyright (c) 2023 Objective-See. All rights reserved. +// + +#import "PluginBase.h" +#import + +@interface BTM : PluginBase +{ + +} + +/* (custom) METHODS */ + +@end diff --git a/Plugins/BTM.m b/Plugins/BTM.m new file mode 100755 index 0000000..10945c4 --- /dev/null +++ b/Plugins/BTM.m @@ -0,0 +1,236 @@ +// +// BTM.m +// KnockKnock +// +// Created by Patrick Wardle on 6/26/23. +// Copyright (c) 2023 Objective-See. All rights reserved. + +#import "BTM.h" +#import "File.h" +#import "dumpBTM.h" +#import "Utilities.h" + +//plugin name +#define PLUGIN_NAME @"Background Managed Tasks" + +//plugin description +#define PLUGIN_DESCRIPTION @"agents, daemons, & login items managed by BTM" + +//plugin icon +#define PLUGIN_ICON @"btmIcon" + +@implementation BTM + +//init +// set name, description, etc +-(id)init +{ + //super + self = [super init]; + if(self) + { + //set name + self.name = PLUGIN_NAME; + + //set description + self.description = PLUGIN_DESCRIPTION; + + //set icon + self.icon = PLUGIN_ICON; + } + + return self; +} + +//scan for btm items +-(void)scan +{ + //only available on macOS 13+ + if(@available(macOS 13, *)) + { + + //contents + NSDictionary* contents = nil; + + //items + NSMutableDictionary* items = nil; + + //items sorted + NSArray* itemsSorted = nil; + + //paths (for dups) + NSMutableSet* paths = nil; + + //parents + NSMutableArray* parents = nil; + + //parse BTM db + contents = parseBTM(nil); + if(noErr != [contents[KEY_BTM_ERROR] integerValue]) + { + //error + goto bail; + } + + //init + items = [NSMutableDictionary dictionary]; + + //init + parents = [NSMutableArray array]; + + //init + paths = [NSMutableSet set]; + + //iterate over all items + // sorted by each user uuid + for(NSString* uuid in contents[KEY_BTM_ITEMS_BY_USER_ID]) + { + //iterate over each item + for(NSDictionary* item in contents[KEY_BTM_ITEMS_BY_USER_ID][uuid]) + { + //File obj + File* fileObj = nil; + + //type + NSUInteger type = 0; + + //path + NSString* path = nil; + + //plist + NSString* plist = nil; + + //parent identifier + NSString* parentID = nil; + + //extract type + type = [item[KEY_BTM_ITEM_TYPE] unsignedIntegerValue]; + + //ignore any items that have "embeddded item ids" + // these seems to be parents, and not the actual items persisted + if(nil != item[KEY_BTM_ITEM_EMBEDDED_IDS]) + { + //skip + continue; + } + + //agent / daemon + if( (type & 0x8) || + (type & 0x10) ) + { + //path + path = item[KEY_BTM_ITEM_EXE_PATH]; + + //plist + plist = [item[KEY_BTM_ITEM_URL] path]; + } + + //login item + // don't have full path, so construct via parent + else if(type & 0x4) + { + //parent id + parentID = item[KEY_BTM_ITEM_PARENT_ID]; + + //find parent + for(NSDictionary* parent in contents[KEY_BTM_ITEMS_BY_USER_ID][uuid]) + { + //no match? + if(YES != [parent[KEY_BTM_ITEM_ID] isEqualToString:parentID]) + { + //skip + continue; + } + + //path = parent URL + login item URL + path = [NSString stringWithFormat:@"%@%@", [parent[KEY_BTM_ITEM_URL] path], [item[KEY_BTM_ITEM_URL] path]]; + + //update path from app's bundle to executable + path = [[NSBundle bundleWithPath:path] executablePath]; + + //save parent uuid + [parents addObject:parent[KEY_BTM_ITEM_UUID]]; + + //done + break; + } + } + + //app + else if(type & 0x2) + { + //extract path + path = [item[KEY_BTM_ITEM_URL] path]; + + //update path from app's bundle to executable + path = [[NSBundle bundleWithPath:path] executablePath]; + } + + //sanity check + if( (nil == path) && + (nil == plist) ) + { + //next + continue; + } + + //plist nil + // ...will for non agents/daemons + if(nil == plist) + { + //init w/o plist + fileObj = [[File alloc] initWithParams:@{KEY_RESULT_PLUGIN:self, KEY_RESULT_PATH:path}]; + } + else + { + fileObj = [[File alloc] initWithParams:@{KEY_RESULT_PLUGIN:self, KEY_RESULT_PATH:path, KEY_RESULT_PLIST:plist}]; + } + + //error in init? + if(nil == fileObj) + { + continue; + } + + //new + // save + if(YES != [paths containsObject:fileObj.path]) + { + //save path + [paths addObject:fileObj.path]; + + //save + items[item[KEY_BTM_ITEM_UUID]] = fileObj; + } + + } + } + + //remove all parents + for(NSString* key in parents) + { + //remove + [items removeObjectForKey:key]; + } + + //sort by name + itemsSorted = [[items allValues] sortedArrayUsingComparator:^NSComparisonResult(File* itemOne, File* itemTwo) { + return [itemOne.name compare:itemTwo.name]; + }]; + + //add each to UI + for(File* item in itemsSorted) + { + //process item + // save and report to UI + [super processItem:item]; + } + + bail: + + return; + + }//macOS 13+ +} + +@end diff --git a/Plugins/DylibProxies.m b/Plugins/DylibProxies.m index aaf7b7a..ddae033 100755 --- a/Plugins/DylibProxies.m +++ b/Plugins/DylibProxies.m @@ -4,7 +4,7 @@ // #import "File.h" -#import "MachO.h" +#import "MachO/MachO.h" #import "Utilities.h" #import "DylibProxies.h" diff --git a/Results/File.m b/Results/File.m index b98ce44..48f70be 100755 --- a/Results/File.m +++ b/Results/File.m @@ -8,7 +8,7 @@ #import "File.h" -#import "MachO.h" +#import "MachO/MachO.h" #import "Consts.h" #import "Signing.h" #import "Utilities.h" diff --git a/UI/AboutWindow.xib b/UI/AboutWindow.xib index 9631c69..ca9ff70 100644 --- a/UI/AboutWindow.xib +++ b/UI/AboutWindow.xib @@ -1,8 +1,8 @@ - + - + @@ -19,7 +19,7 @@ - + @@ -37,11 +37,11 @@ - + - + diff --git a/Utilities.m b/Utilities.m index c5f7671..2d1c4ee 100755 --- a/Utilities.m +++ b/Utilities.m @@ -40,9 +40,8 @@ SInt32 getVersion(OSType selector) return version; } - //disable std err -void disableSTDERR() +void disableSTDERR(void) { //file handle int devNull = -1; @@ -717,7 +716,7 @@ BOOL isNetworkConnected() //check if app is pristine // ->that is to say, nobody modified on-disk image/resources (white lists!, etc) -OSStatus verifySelf() +OSStatus verifySelf(void) { //status OSStatus status = !noErr; diff --git a/en.lproj/MainMenu.xib b/en.lproj/MainMenu.xib index 19633e6..afc4a82 100755 --- a/en.lproj/MainMenu.xib +++ b/en.lproj/MainMenu.xib @@ -1,8 +1,8 @@ - + - + @@ -53,11 +53,11 @@ - + - - + + @@ -200,7 +200,7 @@ - + @@ -659,13 +659,10 @@ - + - - - - - - - - - - - - - - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + + + + + diff --git a/images/btmIcon.png b/images/btmIcon.png new file mode 100644 index 0000000..c78aafb Binary files /dev/null and b/images/btmIcon.png differ diff --git a/patrons.txt b/patrons.txt index 4cb0e51..5f0b213 100644 --- a/patrons.txt +++ b/patrons.txt @@ -1,5 +1,5 @@ Patrons (2^6+): -Jan Koum, Christian Blümlein, MikeyH +Jan Koum, Nick, Cane Juice, Christian Blümlein, M S, Shain Singh Friends of Objective-See: -1Password, Jamf, SmugMug, Guardian Mobile Firewall, SecureMac, iVerify, Halo Privacy +Jamf, Mosyle, Kandji, CleanMyMac X, Kolide, Palo Alto Networks, SmugMug, Guardian Mobile Firewall, iVerify, Halo Privacy