diff --git a/AboutWindowController.m b/AboutWindowController.m index 4b8ea1e..ad8511d 100755 --- a/AboutWindowController.m +++ b/AboutWindowController.m @@ -32,8 +32,13 @@ //super [super windowDidLoad]; - //make white - [self.window setBackgroundColor: NSColor.whiteColor]; + //not in dark mode? + // make window white + if(YES != isDarkMode()) + { + //make white + self.window.backgroundColor = NSColor.whiteColor; + } //set version sting [self.versionLabel setStringValue:[NSString stringWithFormat:@"version: %@", getAppVersion()]]; diff --git a/AppDelegate.h b/AppDelegate.h index 1ebcafa..08ce0e3 100755 --- a/AppDelegate.h +++ b/AppDelegate.h @@ -20,6 +20,15 @@ #import +/* GLOBALS */ + +//shared enumerator +extern ItemEnumerator* sharedItemEnumerator; + +//filter object +extern Filter* itemFilter; + + @interface AppDelegate : NSObject { @@ -28,6 +37,9 @@ /* PROPERTIES */ +//friends +@property (weak) IBOutlet NSWindow *friends; + //flag for secondary scan // ->need to restart shared enumerator @property BOOL secondaryScan; @@ -35,9 +47,6 @@ //plugin objects @property(nonatomic, retain)NSMutableArray* plugins; -//shared item enumerator object -@property(nonatomic, retain)ItemEnumerator* sharedItemEnumerator; - //version string @property (weak) IBOutlet NSTextField *versionString; @@ -77,9 +86,6 @@ //non-UI thread that performs actual scan @property(nonatomic, strong)NSThread *scannerThread; -//filter object -@property(nonatomic, retain)Filter* filterObj; - //virus total object @property(nonatomic, retain)VirusTotal* virusTotalObj; diff --git a/AppDelegate.m b/AppDelegate.m index 0ecb0b5..18e3c9e 100755 --- a/AppDelegate.m +++ b/AppDelegate.m @@ -4,31 +4,22 @@ // #import "Consts.h" -#import "Exception.h" #import "Utilities.h" #import "PluginBase.h" #import "AppDelegate.h" -//supported plugins -NSString * const SUPPORTED_PLUGINS[] = {@"AuthorizationPlugins", @"BrowserExtensions", @"CronJobs", @"EventRules", @"Extensions", @"Kexts", @"LaunchItems", @"DylibInserts", @"DylibProxies", @"LoginItems", @"LogInOutHooks", @"PeriodicScripts", @"SpotlightImporters", @"StartupScripts"}; -//TODO: white list for el capitan! (think we are good, but test in VM) - -//TODO: cmdline interface - -//TODO: scan other volume - -//TODO: delete items - -//TODO: search +//TODO: scan other volumes +//TODO: support delete items +//TODO: search in UI //TODO: better parsing of args for /sh etc? or at least don't say they are 'APPLE' (and thus filter out) // or make them a cmd! @implementation AppDelegate +@synthesize friends; @synthesize plugins; -@synthesize filterObj; @synthesize vtThreads; @synthesize scanButton; @synthesize isConnected; @@ -40,7 +31,6 @@ NSString * const SUPPORTED_PLUGINS[] = {@"AuthorizationPlugins", @"BrowserExtens @synthesize scanButtonLabel; @synthesize progressIndicator; @synthesize itemTableController; -@synthesize sharedItemEnumerator; @synthesize aboutWindowController; @synthesize prefsWindowController; @synthesize showPreferencesButton; @@ -64,18 +54,16 @@ NSString * const SUPPORTED_PLUGINS[] = {@"AuthorizationPlugins", @"BrowserExtens } //automatically invoked by OS -// ->main entry point -(void)applicationDidFinishLaunching:(NSNotification *)notification { + //defaults + NSUserDefaults* defaults = nil; + //app's (self) signing status OSStatus signingStatus = -1; - - //first thing... - // ->install exception handlers! - installExceptionHandlers(); //init filter object - filterObj = [[Filter alloc] init]; + itemFilter = [[Filter alloc] init]; //init virus total object virusTotalObj = [[VirusTotal alloc] init]; @@ -86,16 +74,6 @@ NSString * const SUPPORTED_PLUGINS[] = {@"AuthorizationPlugins", @"BrowserExtens //alloc shared item enumerator sharedItemEnumerator = [[ItemEnumerator alloc] init]; - //check that OS is supported - if(YES != isSupportedOS()) - { - //show alert - [self showUnsupportedAlert]; - - //exit - exit(0); - } - //verify self signingStatus = verifySelf(); @@ -111,7 +89,28 @@ NSString * const SUPPORTED_PLUGINS[] = {@"AuthorizationPlugins", @"BrowserExtens //kick off thread to begin enumerating shared objects // ->this takes awhile, so do it now/first! - [self.sharedItemEnumerator start]; + [sharedItemEnumerator start]; + + //load defaults + defaults = [NSUserDefaults standardUserDefaults]; + + //first time run? + // show thanks to friends window! + if(YES != [defaults boolForKey:NOT_FIRST_TIME]) + { + //set key + [defaults setBool:YES forKey:NOT_FIRST_TIME]; + + //show thanks + [self toggleFriends:nil]; + + //close after 3 seconds + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + + //hide + [self.friends close]; + }); + } //instantiate all plugins objects self.plugins = [self instantiatePlugins]; @@ -158,6 +157,27 @@ NSString * const SUPPORTED_PLUGINS[] = {@"AuthorizationPlugins", @"BrowserExtens return; } +//toggle friends view +- (IBAction)toggleFriends:(id)sender +{ + //hide + if(YES == self.friends.visible) + { + //close to hide + [self.friends close]; + } + //show + else + { + [self.friends makeKeyAndOrderFront:self]; + [NSApp activateIgnoringOtherApps:YES]; + } + + + return; +} + + //display alert about OS not being supported -(void)showUnsupportedAlert { @@ -239,7 +259,6 @@ NSString * const SUPPORTED_PLUGINS[] = {@"AuthorizationPlugins", @"BrowserExtens } //create instances of all registered plugins -// ->returns list -(NSMutableArray*)instantiatePlugins { //plugin objects @@ -321,7 +340,7 @@ NSString * const SUPPORTED_PLUGINS[] = {@"AuthorizationPlugins", @"BrowserExtens if(YES == self.secondaryScan) { //start it - [self.sharedItemEnumerator start]; + [sharedItemEnumerator start]; } //start scanner thread @@ -370,10 +389,16 @@ NSString * const SUPPORTED_PLUGINS[] = {@"AuthorizationPlugins", @"BrowserExtens }); + //set callback + plugin.callback = ^(ItemBase* item) + { + [self itemFound:item]; + }; + //scan - // ->will call back up into UI as items are found + // will invoke callback as items are found [plugin scan]; - + //when 'disable VT' prefs not selected and network is reachable // ->kick of thread to perform VT query in background if( (YES != self.prefsWindowController.disableVTQueries) && @@ -766,13 +791,13 @@ NSString * const SUPPORTED_PLUGINS[] = {@"AuthorizationPlugins", @"BrowserExtens -(void)completeScan { //tell enumerator to stop - [self.sharedItemEnumerator stop]; + [sharedItemEnumerator stop]; //cancel enumerator thread - if(YES == [self.sharedItemEnumerator.enumeratorThread isExecuting]) + if(YES == [sharedItemEnumerator.enumeratorThread isExecuting]) { //cancel - [self.sharedItemEnumerator.enumeratorThread cancel]; + [sharedItemEnumerator.enumeratorThread cancel]; } //sync to cancel all VT threads diff --git a/Assets.xcassets/AppIcon.appiconset/Contents.json b/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..7cd4f8e --- /dev/null +++ b/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "icon_16x16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "icon_16x16@2x.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "icon_32x32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "icon_32x32@2x.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "icon_128x128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "icon_128x128@2x.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "icon_256x256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "icon_256x256@2x.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "icon_512x512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "icon_512x512@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/images/icon.iconset/icon_128x128.png b/Assets.xcassets/AppIcon.appiconset/icon_128x128.png similarity index 100% rename from images/icon.iconset/icon_128x128.png rename to Assets.xcassets/AppIcon.appiconset/icon_128x128.png diff --git a/images/icon.iconset/icon_128x128@2x.png b/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png similarity index 100% rename from images/icon.iconset/icon_128x128@2x.png rename to Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png diff --git a/images/icon.iconset/icon_16x16.png b/Assets.xcassets/AppIcon.appiconset/icon_16x16.png similarity index 100% rename from images/icon.iconset/icon_16x16.png rename to Assets.xcassets/AppIcon.appiconset/icon_16x16.png diff --git a/images/icon.iconset/icon_16x16@2x.png b/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png similarity index 100% rename from images/icon.iconset/icon_16x16@2x.png rename to Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png diff --git a/images/icon.iconset/icon_256x256.png b/Assets.xcassets/AppIcon.appiconset/icon_256x256.png similarity index 100% rename from images/icon.iconset/icon_256x256.png rename to Assets.xcassets/AppIcon.appiconset/icon_256x256.png diff --git a/images/icon.iconset/icon_256x256@2x.png b/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png similarity index 100% rename from images/icon.iconset/icon_256x256@2x.png rename to Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png diff --git a/images/icon.iconset/icon_32x32.png b/Assets.xcassets/AppIcon.appiconset/icon_32x32.png similarity index 100% rename from images/icon.iconset/icon_32x32.png rename to Assets.xcassets/AppIcon.appiconset/icon_32x32.png diff --git a/images/icon.iconset/icon_32x32@2x.png b/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png similarity index 100% rename from images/icon.iconset/icon_32x32@2x.png rename to Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png diff --git a/images/icon.iconset/icon_512x512.png b/Assets.xcassets/AppIcon.appiconset/icon_512x512.png similarity index 100% rename from images/icon.iconset/icon_512x512.png rename to Assets.xcassets/AppIcon.appiconset/icon_512x512.png diff --git a/images/icon.iconset/icon_512x512@2x.png b/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png similarity index 100% rename from images/icon.iconset/icon_512x512@2x.png rename to Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png diff --git a/Assets.xcassets/Contents.json b/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Assets.xcassets/FriendsDigita.imageset/Contents.json b/Assets.xcassets/FriendsDigita.imageset/Contents.json new file mode 100644 index 0000000..d54931d --- /dev/null +++ b/Assets.xcassets/FriendsDigita.imageset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "mac", + "filename" : "digita.pdf" + }, + { + "idiom" : "mac", + "filename" : "digitaLight.pdf", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ] + }, + { + "idiom" : "mac", + "filename" : "digitaDark.pdf", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ] + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "preserves-vector-representation" : true + } +} \ No newline at end of file diff --git a/Assets.xcassets/FriendsDigita.imageset/digita.pdf b/Assets.xcassets/FriendsDigita.imageset/digita.pdf new file mode 100644 index 0000000..fcae0fc Binary files /dev/null and b/Assets.xcassets/FriendsDigita.imageset/digita.pdf differ diff --git a/Assets.xcassets/FriendsDigita.imageset/digitaDark.pdf b/Assets.xcassets/FriendsDigita.imageset/digitaDark.pdf new file mode 100644 index 0000000..fcae0fc Binary files /dev/null and b/Assets.xcassets/FriendsDigita.imageset/digitaDark.pdf differ diff --git a/Assets.xcassets/FriendsDigita.imageset/digitaLight.pdf b/Assets.xcassets/FriendsDigita.imageset/digitaLight.pdf new file mode 100644 index 0000000..fcae0fc Binary files /dev/null and b/Assets.xcassets/FriendsDigita.imageset/digitaLight.pdf differ diff --git a/Assets.xcassets/FriendsMalwarebytes.imageset/Contents.json b/Assets.xcassets/FriendsMalwarebytes.imageset/Contents.json new file mode 100644 index 0000000..f3c6d2f --- /dev/null +++ b/Assets.xcassets/FriendsMalwarebytes.imageset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "mac", + "filename" : "malwarebytes.pdf" + }, + { + "idiom" : "mac", + "filename" : "malwarebytesLight.pdf", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ] + }, + { + "idiom" : "mac", + "filename" : "malwarebytesDark.pdf", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ] + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "preserves-vector-representation" : true + } +} \ No newline at end of file diff --git a/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytes.pdf b/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytes.pdf new file mode 100644 index 0000000..943c2de Binary files /dev/null and b/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytes.pdf differ diff --git a/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytesDark.pdf b/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytesDark.pdf new file mode 100644 index 0000000..943c2de Binary files /dev/null and b/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytesDark.pdf differ diff --git a/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytesLight.pdf b/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytesLight.pdf new file mode 100644 index 0000000..943c2de Binary files /dev/null and b/Assets.xcassets/FriendsMalwarebytes.imageset/malwarebytesLight.pdf differ diff --git a/Assets.xcassets/FriendsSophos.imageset/Contents.json b/Assets.xcassets/FriendsSophos.imageset/Contents.json new file mode 100644 index 0000000..3221459 --- /dev/null +++ b/Assets.xcassets/FriendsSophos.imageset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "mac", + "filename" : "sophos.png" + }, + { + "idiom" : "mac", + "filename" : "sophosLight.png", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "light" + } + ] + }, + { + "idiom" : "mac", + "filename" : "sophosDark.png", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ] + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "preserves-vector-representation" : true + } +} \ No newline at end of file diff --git a/Assets.xcassets/FriendsSophos.imageset/sophos.png b/Assets.xcassets/FriendsSophos.imageset/sophos.png new file mode 100644 index 0000000..a5da643 Binary files /dev/null and b/Assets.xcassets/FriendsSophos.imageset/sophos.png differ diff --git a/Assets.xcassets/FriendsSophos.imageset/sophosDark.png b/Assets.xcassets/FriendsSophos.imageset/sophosDark.png new file mode 100644 index 0000000..a5da643 Binary files /dev/null and b/Assets.xcassets/FriendsSophos.imageset/sophosDark.png differ diff --git a/Assets.xcassets/FriendsSophos.imageset/sophosLight.png b/Assets.xcassets/FriendsSophos.imageset/sophosLight.png new file mode 100644 index 0000000..a5da643 Binary files /dev/null and b/Assets.xcassets/FriendsSophos.imageset/sophosLight.png differ diff --git a/Cartfile b/Cartfile new file mode 100644 index 0000000..36d649c --- /dev/null +++ b/Cartfile @@ -0,0 +1 @@ +github "getsentry/sentry-cocoa" "4.1.0" diff --git a/Cartfile.resolved b/Cartfile.resolved new file mode 100644 index 0000000..36d649c --- /dev/null +++ b/Cartfile.resolved @@ -0,0 +1 @@ +github "getsentry/sentry-cocoa" "4.1.0" diff --git a/CategoryTableController.m b/CategoryTableController.m index 5b05731..279c0e5 100755 --- a/CategoryTableController.m +++ b/CategoryTableController.m @@ -138,11 +138,11 @@ [categoryCell.textField setTextColor:[NSColor redColor]]; } //no flagged items - // ->set title's color to black + // ->set title's color else { - //black - [categoryCell.textField setTextColor:[NSColor blackColor]]; + //reset + categoryCell.textField.textColor = NSColor.controlTextColor; } //set detailed text diff --git a/Consts.h b/Consts.h index 899b364..08ef8db 100755 --- a/Consts.h +++ b/Consts.h @@ -9,6 +9,12 @@ #ifndef KK_Consts_h #define KK_Consts_h +//not first run +#define NOT_FIRST_TIME @"notFirstTime" + +//supported plugins +static NSString * const SUPPORTED_PLUGINS[] = {@"AuthorizationPlugins", @"BrowserExtensions", @"CronJobs", @"EventRules", @"Extensions", @"Kexts", @"LaunchItems", @"DylibInserts", @"DylibProxies", @"LoginItems", @"LogInOutHooks", @"PeriodicScripts", @"SpotlightImporters", @"StartupScripts"}; + //button text, start scan #define START_SCAN @"Start Scan" @@ -45,16 +51,23 @@ //success #define STATUS_SUCCESS 0 -//keys for signing stuff +//signers +enum Signer{None, Apple, AppStore, DevID, AdHoc}; + +//signature status #define KEY_SIGNATURE_STATUS @"signatureStatus" -#define KEY_SIGNING_AUTHORITIES @"signingAuthorities" -#define KEY_SIGNING_IS_APPLE @"signedByApple" -//OS version x -#define OS_MAJOR_VERSION_X 10 +//signer +#define KEY_SIGNATURE_SIGNER @"signatureSigner" -//OS version lion -#define OS_MINOR_VERSION_LION 8 +//signing auths +#define KEY_SIGNATURE_AUTHORITIES @"signatureAuthorities" + +//code signing id +#define KEY_SIGNATURE_IDENTIFIER @"signatureIdentifier" + +//entitlements +#define KEY_SIGNATURE_ENTITLEMENTS @"signatureEntitlements" //OS version yosemite #define OS_MINOR_VERSION_YOSEMITE 10 @@ -98,6 +111,12 @@ //dyld_ key for applications #define APPLICATION_DYLD_KEY @"LSEnvironment" +//user name +#define USER_NAME @"userName" + +//user (home) directory +#define USER_DIRECTORY @"userDirectory" + //menu //tag for prefs menu item @@ -161,6 +180,9 @@ // ->for long paths... #define ELLIPIS @"..." +//known kexts +#define WHITE_LISTED_KEXTS @"whitelistedKexts" + //known file hashes #define WHITE_LISTED_FILES @"whitelistedFiles" diff --git a/Exception.h b/Exception.h deleted file mode 100755 index 6f9eb9d..0000000 --- a/Exception.h +++ /dev/null @@ -1,17 +0,0 @@ -// Created by Patrick Wardle on 2/6/15. -// Copyright (c) 2015 Objective-See, LLC. All rights reserved. - -#import -#import - -//install exception/signal handlers -void installExceptionHandlers(void); - -//exception handler for Obj-C exceptions -void exceptionHandler(NSException *exception); - -//signal handler for *nix style exceptions -void signalHandler(int signal, siginfo_t *info, void *context); - -//display an alert -void showAlert(void); diff --git a/Exception.m b/Exception.m deleted file mode 100755 index c34e107..0000000 --- a/Exception.m +++ /dev/null @@ -1,157 +0,0 @@ -// Created by Patrick Wardle on 2/6/15. -// Copyright (c) 2015 Objective-See, LLC. All rights reserved. - -#import "Consts.h" -#import "Exception.h" -#import "Utilities.h" - - -//install exception/signal handlers -void installExceptionHandlers() -{ - //sigaction struct - struct sigaction sa = {0}; - - //init struct - sigemptyset (&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - sa.sa_sigaction = signalHandler; - - //exception handler - NSSetUncaughtExceptionHandler(&exceptionHandler); - - //install signal handlers - sigaction(SIGILL, &sa, NULL); - sigaction(SIGSEGV, &sa, NULL); - sigaction(SIGBUS, &sa, NULL); - sigaction(SIGABRT, &sa, NULL); - sigaction(SIGTRAP, &sa, NULL); - sigaction(SIGFPE, &sa, NULL); - - return; -} - -//display error alert -void showAlert() -{ - //response - // ->index of button click - NSModalResponse response = 0; - - //alert box - NSAlert* fullScanAlert = nil; - - //alloc/init alert - fullScanAlert = [NSAlert alertWithMessageText:@"ERROR: detected unrecoverable fault" defaultButton:@"Exit" alternateButton:@"Info" otherButton:nil informativeTextWithFormat:@"click 'Info' to help fix the issue!"]; - - //and show it - response = [fullScanAlert runModal]; - - //handle case where user clicks 'Info' - // ->take 'em to error page - if(0 == response) - { - //open page in browser - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://objective-see.com/errors.html"]]; - } - - //kill app - exit(0); - - return; -} - - - -//exception handler -// will be invoked for Obj-C exceptions -void exceptionHandler(NSException *exception) -{ - //error msg - NSString* errMsg = nil; - - //err msg - syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: OS version: %s /App version: %s", [[[NSProcessInfo processInfo] operatingSystemVersionString] UTF8String], [getAppVersion() UTF8String]); - - //create error msg - errMsg = [NSString stringWithFormat:@"unhandled obj-c exception caught [name: %@ / reason: %@]", [exception name], [exception reason]]; - - //err msg - syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: %s", [errMsg UTF8String]); - - //err msg - syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: %s", [[[NSThread callStackSymbols] description] UTF8String]); - - //main thread - // ->just show UI alert - if(YES == [NSThread isMainThread]) - { - //show - showAlert(); - } - //back thread - // ->have to show it on main thread - else - { - //show alert - // ->in main UI thread - dispatch_sync(dispatch_get_main_queue(), ^{ - - //show - showAlert(); - - }); - } - - return; -} - -//handler for signals -// will be invoked for BSD/*nix signals -void signalHandler(int signal, siginfo_t *info, void *context) -{ - //error msg - NSString* errMsg = nil; - - //context - ucontext_t *uContext = NULL; - - //err msg - syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: OS version: %s /App version: %s", [[[NSProcessInfo processInfo] operatingSystemVersionString] UTF8String], [getAppVersion() UTF8String]); - - //typecast context - uContext = (ucontext_t *)context; - - //create error msg - errMsg = [NSString stringWithFormat:@"unhandled exception caught, si_signo: %d /si_code: %s /si_addr: %p /rip: %p", - info->si_signo, (info->si_code == SEGV_MAPERR) ? "SEGV_MAPERR" : "SEGV_ACCERR", info->si_addr, (unsigned long*)uContext->uc_mcontext->__ss.__rip]; - - //err msg - syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: %s", [errMsg UTF8String]); - - //err msg - syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: %s", [[[NSThread callStackSymbols] description] UTF8String]); - - //main thread - // ->just show UI alert - if(YES == [NSThread isMainThread]) - { - //show - showAlert(); - } - //back thread - // ->have to show it on main thread - else - { - //show alert - // ->in main UI thread - dispatch_sync(dispatch_get_main_queue(), ^{ - - //show - showAlert(); - - }); - } - - return; -} diff --git a/Filter.h b/Filter.h index de4680f..36524d1 100755 --- a/Filter.h +++ b/Filter.h @@ -25,6 +25,8 @@ //white listed extensions @property(nonatomic, retain)NSDictionary* trustedExtensions; +//white listed kexts +@property(nonatomic, retain)NSDictionary* trustedKexts; /* METHODS */ @@ -32,15 +34,16 @@ // ->file hashes, known commands, etc -(NSDictionary*)loadWhitelist:(NSString*)fileName; -//check if a File obj is whitelisted +//check if a File obj is white listed -(BOOL)isTrustedFile:(File*)fileObj; -//check if a Command obj is whitelisted +//check if a Command obj is white listed -(BOOL)isKnownCommand:(Command*)commandObj; -//check if a Extension obj is whitelisted +//check if a Extension obj is white listed -(BOOL)isTrustedExtension:(Extension*)extensionObj; - +//check if kext is white listed +-(BOOL)isTrustedKext:(File*)fileObj; @end diff --git a/Filter.m b/Filter.m index d96bda7..f13b558 100755 --- a/Filter.m +++ b/Filter.m @@ -11,6 +11,7 @@ @implementation Filter +@synthesize trustedKexts; @synthesize trustedFiles; @synthesize knownCommands; @synthesize trustedExtensions; @@ -34,6 +35,9 @@ //load known extensions self.trustedExtensions = [self loadWhitelist:WHITE_LISTED_EXTENSIONS]; + + //load known kexts + self.trustedKexts = [self loadWhitelist:WHITE_LISTED_KEXTS]; } return self; @@ -71,7 +75,7 @@ //check if a File obj is known // ->whitelisted *or* signed by apple --(BOOL)isTrustedFile:(File*)fileObj +-(BOOL)isTrustedFile:(File*)file { //flag BOOL isTrusted = NO; @@ -80,23 +84,37 @@ NSArray* knownHashes = nil; //lookup based on name - knownHashes = self.trustedFiles[fileObj.path]; + knownHashes = self.trustedFiles[file.path]; - //first check if hash is known + //check if hash is known if( (nil != knownHashes) && - (YES == [knownHashes containsObject:[fileObj.hashes[KEY_HASH_MD5] lowercaseString]]) ) + (YES == [knownHashes containsObject:[file.hashes[KEY_HASH_MD5] lowercaseString]]) ) { //got match isTrusted = YES; + + //bail + goto bail; } - //otherwise check if its signed by apple - // ->apple-signed files are always trusted - else + + //if kext + // check if trusted (apple, or 3rd-party, ships with OS) + if( (YES == [file.path hasPrefix:@"/Library/Extensions/"]) || + (YES == [file.path hasPrefix:@"/System/Library/Extensions/"]) ) { - //check for apple signature - isTrusted = [fileObj.signingInfo[KEY_SIGNING_IS_APPLE] boolValue]; + //check + isTrusted = [self isTrustedKext:file]; + + //bail + goto bail; } + //finally, then check if its signed by apple + // note: apple-signed files are always trusted + isTrusted = (Apple == [file.signingInfo[KEY_SIGNATURE_SIGNER] intValue]); + +bail: + return isTrusted; } @@ -125,5 +143,52 @@ return isTrusted; } +//check if a kext obj is known +// whitelisted *or* signed by apple +-(BOOL)isTrustedKext:(File*)file +{ + //flag + BOOL isTrusted = NO; + + //(trusted) signing id + // either list of hashes, or dev id + id whitelistInfo = nil; + + //lookup based on name + whitelistInfo = self.trustedKexts[file.path]; + + //hashes? + if( (YES == [whitelistInfo isKindOfClass:[NSArray class]]) && + (YES == [whitelistInfo containsObject:[file.hashes[KEY_HASH_MD5] lowercaseString]]) ) + { + //got match + isTrusted = YES; + + //bail + goto bail; + } + + //dev id? + // note: these are only for kexts that ship with macOS! + if( (YES == [whitelistInfo isKindOfClass:[NSString class]]) && + (YES == [[file.signingInfo[KEY_SIGNATURE_AUTHORITIES] lastObject] isEqualToString:@"Apple Root CA"]) && + (YES == [file.signingInfo[KEY_SIGNATURE_AUTHORITIES] containsObject:whitelistInfo]) ) + { + //got match + isTrusted = YES; + + //bail + goto bail; + } + + //check for apple signature + // kexts that belong to apple, are trusted + isTrusted = (Apple == [file.signingInfo[KEY_SIGNATURE_SIGNER] intValue]); + +bail: + + return isTrusted; +} + @end diff --git a/InfoWindowController.m b/InfoWindowController.m index 03e7bd7..05e2af1 100755 --- a/InfoWindowController.m +++ b/InfoWindowController.m @@ -28,8 +28,13 @@ //super [super windowDidLoad]; - //make white - [self.window setBackgroundColor: NSColor.whiteColor]; + //not in dark mode? + // make window white + if(YES != isDarkMode()) + { + //make white + self.window.backgroundColor = NSColor.whiteColor; + } return; } diff --git a/ItemEnumerator.m b/ItemEnumerator.m index 4ae3d7c..12dfbd7 100755 --- a/ItemEnumerator.m +++ b/ItemEnumerator.m @@ -69,37 +69,28 @@ NSString * const LAUNCHITEM_SEARCH_DIRECTORIES[] = {@"/System/Library/LaunchDaem } //generate list of all launch items (daemons & agents) -// ->save into iVar, 'launchItem' -(void)enumerateLaunchItems { //all launch items NSMutableArray* allLaunchItems = nil; - //number of search directories - NSUInteger directoryCount = 0; - - //current launch item directory - NSString* launchItemDirectory = nil; + //all launch item directories, expanded + NSMutableArray* launchItemDirectories = nil; //alloc array for all launch items allLaunchItems = [NSMutableArray array]; - //get number of search directories - directoryCount = sizeof(LAUNCHITEM_SEARCH_DIRECTORIES)/sizeof(LAUNCHITEM_SEARCH_DIRECTORIES[0]); + //expand list + launchItemDirectories = expandPaths(LAUNCHITEM_SEARCH_DIRECTORIES, sizeof(LAUNCHITEM_SEARCH_DIRECTORIES)/sizeof(LAUNCHITEM_SEARCH_DIRECTORIES[0])); - //iterate over all launch item directories - // ->cumulativelly save all launch items - for(NSUInteger i=0; i < directoryCount; i++) + //iterate over all launch item direcoties + // grab all .plists and add to cumulative array + for(NSString* launchItemDirectory in launchItemDirectories) { - //extract current directory - launchItemDirectory = [LAUNCHITEM_SEARCH_DIRECTORIES[i] stringByExpandingTildeInPath]; - - //iterate over all launch item (plists) in current launch item directory - // ->build full path it launch item and save it into array + //grab all plist for(NSString* plist in directoryContents(launchItemDirectory, @"self ENDSWITH '.plist'")) { - //build full path to item/plist - // ->save it into array + //build full path to item/plist and save [allLaunchItems addObject:[NSString stringWithFormat:@"%@/%@", launchItemDirectory, plist]]; } } diff --git a/ItemTableController.m b/ItemTableController.m index 1f8046a..b8ba877 100755 --- a/ItemTableController.m +++ b/ItemTableController.m @@ -275,9 +275,8 @@ hasTrackingArea = YES; } - //default - // ->set main textfield's color to black - itemCell.textField.textColor = [NSColor blackColor]; + //default color + itemCell.textField.textColor = NSColor.controlTextColor; //default // ->hide plist label @@ -400,14 +399,14 @@ vtDetectionRatio = [NSString stringWithFormat:@"%lu/%lu", (unsigned long)[((File*)item).vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue], (unsigned long)[((File*)item).vtInfo[VT_RESULTS_TOTAL] unsignedIntegerValue]]; //known 'good' files (0 positivies) - // ->(re)set to black/gray + // ->(re)set colors if(0 == [((File*)item).vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) { - //(re)set title black - itemCell.textField.textColor = [NSColor blackColor]; + //(re)set title color + itemCell.textField.textColor = NSColor.controlTextColor; - //set color (black) - stringAttributes[NSForegroundColorAttributeName] = [NSColor blackColor]; + //(re)set color + stringAttributes[NSForegroundColorAttributeName] = NSColor.controlTextColor; //set string (vt ratio), with attributes [vtButton setAttributedTitle:[[NSAttributedString alloc] initWithString:vtDetectionRatio attributes:stringAttributes]]; @@ -489,7 +488,7 @@ customizedItemName = [[NSMutableAttributedString alloc] initWithString:@""]; //add existing name - // ->uses existing color (red or black) + // ->uses existing color [customizedItemName appendAttributedString:[[NSMutableAttributedString alloc] initWithString:itemCell.textField.stringValue attributes:@{NSForegroundColorAttributeName:itemCell.textField.textColor}]]; //add '(' @@ -889,47 +888,37 @@ NSImage* getCodeSigningIcon(File* binary) //signature image NSImage* codeSignIcon = nil; - //set signature status icon - if(nil != binary.signingInfo) - { - //binary is signed by apple - if(YES == [binary.signingInfo[KEY_SIGNING_IS_APPLE] boolValue]) - { - //set - codeSignIcon = [NSImage imageNamed:@"signedAppleIcon"]; - } - - //binary is signed - else if(STATUS_SUCCESS == [binary.signingInfo[KEY_SIGNATURE_STATUS] integerValue]) - { - //set - codeSignIcon = [NSImage imageNamed:@"signed"]; - } - - //binary not signed - else if(errSecCSUnsigned == [binary.signingInfo[KEY_SIGNATURE_STATUS] integerValue]) - { - //set - codeSignIcon = [NSImage imageNamed:@"unsigned"]; - } - - //unknown - else - { - //set - codeSignIcon = [NSImage imageNamed:@"unknown"]; - } - } - //signing info is nil - // ->just to unknown - else + //no signing info or signing error + if( (nil == binary.signingInfo) || + (nil == binary.signingInfo[KEY_SIGNATURE_STATUS]) || + (errSecSuccess != [binary.signingInfo[KEY_SIGNATURE_STATUS] intValue]) ) { //set codeSignIcon = [NSImage imageNamed:@"unknown"]; } + + //apple? + else if(Apple == [binary.signingInfo[KEY_SIGNATURE_SIGNER] intValue]) + { + //set + codeSignIcon = [NSImage imageNamed:@"signedAppleIcon"]; + } + //signed + else if(errSecSuccess == [binary.signingInfo[KEY_SIGNATURE_STATUS] intValue]) + { + //set + codeSignIcon = [NSImage imageNamed:@"signed"]; + } + + //unsigned + else if(errSecCSUnsigned == [binary.signingInfo[KEY_SIGNATURE_STATUS] intValue]) + { + //set + codeSignIcon = [NSImage imageNamed:@"unsigned"]; + } + return codeSignIcon; - } @end diff --git a/KKRow.m b/KKRow.m index c12b9d5..d757d5d 100755 --- a/KKRow.m +++ b/KKRow.m @@ -7,6 +7,7 @@ // #import "KKRow.h" +#import "Utilities.h" @implementation KKRow @@ -25,14 +26,27 @@ //make selection rect selectionRect = NSInsetRect(self.bounds, 2.5, 2.5); - //set stroke - [[NSColor colorWithCalibratedWhite:.65 alpha:1.0] setStroke]; + //dark mode highlight + if(YES == isDarkMode()) + { + //set stroke + [[NSColor colorWithCalibratedWhite:.25 alpha:1.0] setStroke]; - //set fill - [[NSColor colorWithCalibratedWhite:.82 alpha:1.0] setFill]; + //set fill + [[NSColor colorWithCalibratedWhite:.50 alpha:1.0] setFill]; + } + //light mode highlight + else + { + //set stroke + [[NSColor colorWithCalibratedWhite:.65 alpha:1.0] setStroke]; + + //set fill + [[NSColor colorWithCalibratedWhite:.82 alpha:1.0] setFill]; + } //create selection path - // ->with rounded corners + // ...with rounded corners selectionPath = [NSBezierPath bezierPathWithRoundedRect:selectionRect xRadius:5 yRadius:5]; //fill diff --git a/KnockKnock-Info.plist b/KnockKnock-Info.plist index 1b0e161..e6ed36a 100755 --- a/KnockKnock-Info.plist +++ b/KnockKnock-Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.9.3 + 2.0.0 CFBundleSignature ???? CFBundleVersion - 1.9.3 + 2.0.0 LSMinimumSystemVersion ${MACOSX_DEPLOYMENT_TARGET} NSHumanReadableCopyright diff --git a/KnockKnock.xcodeproj/project.pbxproj b/KnockKnock.xcodeproj/project.pbxproj index 3072047..7351a76 100755 --- a/KnockKnock.xcodeproj/project.pbxproj +++ b/KnockKnock.xcodeproj/project.pbxproj @@ -24,11 +24,13 @@ CD0219501AD34D8B005148A2 /* PrefsWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CD02194E1AD34D8B005148A2 /* PrefsWindow.xib */; }; CD0219531AD34D9A005148A2 /* AboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CD0219521AD34D9A005148A2 /* AboutWindowController.m */; }; CD0219551AD34E4C005148A2 /* kkIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = CD0219541AD34E4C005148A2 /* kkIcon.png */; }; - CD02195C1AD38823005148A2 /* kkRowCell.m in Sources */ = {isa = PBXBuildFile; fileRef = CD02195B1AD38823005148A2 /* kkRowCell.m */; }; CD02195E1AD39A74005148A2 /* ResultsWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CD02195D1AD39A74005148A2 /* ResultsWindow.xib */; }; CD0219611AD39A83005148A2 /* ResultsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CD0219601AD39A83005148A2 /* ResultsWindowController.m */; }; + CD24F074219D51210081B0E5 /* whitelistedKexts.json in Resources */ = {isa = PBXBuildFile; fileRef = CD24F073219D51210081B0E5 /* whitelistedKexts.json */; }; + CD24F077219DF3DB0081B0E5 /* Signing.m in Sources */ = {isa = PBXBuildFile; fileRef = CD24F075219DF3DA0081B0E5 /* Signing.m */; }; CD2B613320675EEC00BF72E9 /* EventRules.m in Sources */ = {isa = PBXBuildFile; fileRef = CD2B611220675EEB00BF72E9 /* EventRules.m */; }; CD2B61442067656200BF72E9 /* eventRulesIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = CD2B61432067656200BF72E9 /* eventRulesIcon.png */; }; + CD585494219FE61D00A438B0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CD585493219FE61D00A438B0 /* Assets.xcassets */; }; CD6095731A87067D00E091CD /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD6095721A87067D00E091CD /* Security.framework */; }; CD6975061B02F60400CE819B /* RFOverlayScroller.m in Sources */ = {isa = PBXBuildFile; fileRef = CD6975031B02F60400CE819B /* RFOverlayScroller.m */; }; CD6975071B02F60400CE819B /* RFOverlayScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = CD6975051B02F60400CE819B /* RFOverlayScrollView.m */; }; @@ -36,8 +38,9 @@ CD6BBBEB1B50DF7100506D0D /* signedAppleIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = CD6BBBEA1B50DF7100506D0D /* signedAppleIcon.png */; }; CD6BBBED1B51C62B00506D0D /* unknown.png in Resources */ = {isa = PBXBuildFile; fileRef = CD6BBBEC1B51C62B00506D0D /* unknown.png */; }; CD6BBBF01B52032D00506D0D /* NSApplicationKeyEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = CD6BBBEF1B52032D00506D0D /* NSApplicationKeyEvents.m */; }; + CD6DE4D1219E9AD30058094E /* Sentry.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD6DE4D0219E9AD30058094E /* Sentry.framework */; }; + CD6DE4D3219E9C560058094E /* Sentry.framework in Copy Framework(s) */ = {isa = PBXBuildFile; fileRef = CD6DE4D0219E9AD30058094E /* Sentry.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CD7B9F4D1ACB959200DF3C71 /* logoAppleOver.png in Resources */ = {isa = PBXBuildFile; fileRef = CD7B9F4C1ACB959200DF3C71 /* logoAppleOver.png */; }; - CD7B9F501ACB9A8400DF3C71 /* Exception.m in Sources */ = {isa = PBXBuildFile; fileRef = CD7B9F4F1ACB9A8400DF3C71 /* Exception.m */; }; CD7B9F531ACBAE2900DF3C71 /* SpotlightImporters.m in Sources */ = {isa = PBXBuildFile; fileRef = CD7B9F521ACBAE2900DF3C71 /* SpotlightImporters.m */; }; CD7B9FA41ACBCFAD00DF3C71 /* spotlightIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = CD7B9FA31ACBCFAD00DF3C71 /* spotlightIcon.png */; }; CD7B9FA71ACCAE5E00DF3C71 /* startScanOver.png in Resources */ = {isa = PBXBuildFile; fileRef = CD7B9FA51ACCAE5E00DF3C71 /* startScanOver.png */; }; @@ -59,7 +62,6 @@ CDA81D701A95B4E9009790E2 /* stopScan.png in Resources */ = {isa = PBXBuildFile; fileRef = CDA81D661A95B4E9009790E2 /* stopScan.png */; }; CDA81D711A95B4E9009790E2 /* stopScanBG.png in Resources */ = {isa = PBXBuildFile; fileRef = CDA81D671A95B4E9009790E2 /* stopScanBG.png */; }; CDA81D721A95B4E9009790E2 /* virus.png in Resources */ = {isa = PBXBuildFile; fileRef = CDA81D681A95B4E9009790E2 /* virus.png */; }; - CDA81D741A95B4FB009790E2 /* icon.iconset in Resources */ = {isa = PBXBuildFile; fileRef = CDA81D731A95B4FB009790E2 /* icon.iconset */; }; CDA81D771A95B7C1009790E2 /* CategoryTableController.m in Sources */ = {isa = PBXBuildFile; fileRef = CDA81D761A95B7C1009790E2 /* CategoryTableController.m */; }; CDA81D7B1A95D29B009790E2 /* ItemTableController.m in Sources */ = {isa = PBXBuildFile; fileRef = CDA81D7A1A95D29B009790E2 /* ItemTableController.m */; }; CDA81D8E1A96F557009790E2 /* Kexts.m in Sources */ = {isa = PBXBuildFile; fileRef = CDA81D8B1A96F557009790E2 /* Kexts.m */; }; @@ -114,6 +116,20 @@ CDF08CF41ACA6864009B3423 /* loginIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = CDF08CF21ACA6864009B3423 /* loginIcon.png */; }; /* End PBXBuildFile section */ +/* Begin PBXCopyFilesBuildPhase section */ + CD6DE4D2219E9C3A0058094E /* Copy Framework(s) */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + CD6DE4D3219E9C560058094E /* Sentry.framework in Copy Framework(s) */, + ); + name = "Copy Framework(s)"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 1D21BC4B172AF43D009D1CFD /* KnockKnock.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KnockKnock.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1D21BC4E172AF43D009D1CFD /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; @@ -142,14 +158,17 @@ CD0219511AD34D9A005148A2 /* AboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutWindowController.h; sourceTree = ""; }; CD0219521AD34D9A005148A2 /* AboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AboutWindowController.m; sourceTree = ""; }; CD0219541AD34E4C005148A2 /* kkIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = kkIcon.png; path = images/kkIcon.png; sourceTree = SOURCE_ROOT; }; - CD02195A1AD38823005148A2 /* kkRowCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = kkRowCell.h; sourceTree = ""; }; - CD02195B1AD38823005148A2 /* kkRowCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = kkRowCell.m; sourceTree = ""; }; CD02195D1AD39A74005148A2 /* ResultsWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ResultsWindow.xib; path = UI/ResultsWindow.xib; sourceTree = ""; }; CD02195F1AD39A83005148A2 /* ResultsWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResultsWindowController.h; sourceTree = ""; }; CD0219601AD39A83005148A2 /* ResultsWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ResultsWindowController.m; sourceTree = ""; }; + CD24F072219AAC930081B0E5 /* main.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = main.h; sourceTree = SOURCE_ROOT; }; + CD24F073219D51210081B0E5 /* whitelistedKexts.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = whitelistedKexts.json; path = WhiteList/whitelistedKexts.json; sourceTree = ""; }; + CD24F075219DF3DA0081B0E5 /* Signing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Signing.m; sourceTree = SOURCE_ROOT; }; + CD24F076219DF3DB0081B0E5 /* Signing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Signing.h; sourceTree = SOURCE_ROOT; }; CD2B611220675EEB00BF72E9 /* EventRules.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = EventRules.m; path = Plugins/EventRules.m; sourceTree = ""; }; CD2B611320675EEB00BF72E9 /* EventRules.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EventRules.h; path = Plugins/EventRules.h; sourceTree = ""; }; CD2B61432067656200BF72E9 /* eventRulesIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = eventRulesIcon.png; path = images/eventRulesIcon.png; sourceTree = SOURCE_ROOT; }; + CD585493219FE61D00A438B0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = SOURCE_ROOT; }; CD6095721A87067D00E091CD /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; CD6975021B02F60400CE819B /* RFOverlayScroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RFOverlayScroller.h; path = 3rdParty/RFOverlayScroller.h; sourceTree = ""; }; CD6975031B02F60400CE819B /* RFOverlayScroller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RFOverlayScroller.m; path = 3rdParty/RFOverlayScroller.m; sourceTree = ""; }; @@ -160,9 +179,8 @@ CD6BBBEC1B51C62B00506D0D /* unknown.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = unknown.png; path = images/unknown.png; sourceTree = SOURCE_ROOT; }; CD6BBBEE1B52032D00506D0D /* NSApplicationKeyEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSApplicationKeyEvents.h; sourceTree = ""; }; CD6BBBEF1B52032D00506D0D /* NSApplicationKeyEvents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSApplicationKeyEvents.m; sourceTree = ""; }; + CD6DE4D0219E9AD30058094E /* Sentry.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sentry.framework; path = Carthage/Build/Mac/Sentry.framework; sourceTree = ""; }; CD7B9F4C1ACB959200DF3C71 /* logoAppleOver.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = logoAppleOver.png; path = images/logoAppleOver.png; sourceTree = SOURCE_ROOT; }; - CD7B9F4E1ACB9A8400DF3C71 /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Exception.h; sourceTree = SOURCE_ROOT; }; - CD7B9F4F1ACB9A8400DF3C71 /* Exception.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Exception.m; sourceTree = SOURCE_ROOT; }; CD7B9F511ACBAE2900DF3C71 /* SpotlightImporters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SpotlightImporters.h; path = Plugins/SpotlightImporters.h; sourceTree = ""; }; CD7B9F521ACBAE2900DF3C71 /* SpotlightImporters.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SpotlightImporters.m; path = Plugins/SpotlightImporters.m; sourceTree = ""; }; CD7B9FA31ACBCFAD00DF3C71 /* spotlightIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = spotlightIcon.png; path = images/spotlightIcon.png; sourceTree = SOURCE_ROOT; }; @@ -192,7 +210,6 @@ CDA81D661A95B4E9009790E2 /* stopScan.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = stopScan.png; path = images/stopScan.png; sourceTree = SOURCE_ROOT; }; CDA81D671A95B4E9009790E2 /* stopScanBG.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = stopScanBG.png; path = images/stopScanBG.png; sourceTree = SOURCE_ROOT; }; CDA81D681A95B4E9009790E2 /* virus.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = virus.png; path = images/virus.png; sourceTree = SOURCE_ROOT; }; - CDA81D731A95B4FB009790E2 /* icon.iconset */ = {isa = PBXFileReference; lastKnownFileType = folder.iconset; name = icon.iconset; path = images/icon.iconset; sourceTree = SOURCE_ROOT; }; CDA81D751A95B7C1009790E2 /* CategoryTableController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CategoryTableController.h; sourceTree = ""; }; CDA81D761A95B7C1009790E2 /* CategoryTableController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CategoryTableController.m; sourceTree = ""; }; CDA81D791A95D29B009790E2 /* ItemTableController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ItemTableController.h; sourceTree = ""; }; @@ -275,6 +292,7 @@ buildActionMask = 2147483647; files = ( CDBE491A1B5B25E30031FC22 /* SystemConfiguration.framework in Frameworks */, + CD6DE4D1219E9AD30058094E /* Sentry.framework in Frameworks */, CDDBC2301B04771100B021E0 /* ServiceManagement.framework in Frameworks */, CDF08CEA1AC8D97B009B3423 /* Quartz.framework in Frameworks */, CD6095731A87067D00E091CD /* Security.framework in Frameworks */, @@ -292,8 +310,6 @@ CD6BBBEF1B52032D00506D0D /* NSApplicationKeyEvents.m */, CDAB989A1AEAC95500C75B4B /* ItemEnumerator.h */, CDAB989B1AEAC95500C75B4B /* ItemEnumerator.m */, - CD02195A1AD38823005148A2 /* kkRowCell.h */, - CD02195B1AD38823005148A2 /* kkRowCell.m */, CD7B9FA91AD08FA100DF3C71 /* KKRow.h */, CD7B9FAA1AD08FA100DF3C71 /* KKRow.m */, CDF08CE31AC89FD4009B3423 /* 3rdParty */, @@ -324,6 +340,7 @@ 1D21BC4D172AF43D009D1CFD /* Frameworks */ = { isa = PBXGroup; children = ( + CD6DE4D0219E9AD30058094E /* Sentry.framework */, CDBE49191B5B25E30031FC22 /* SystemConfiguration.framework */, CDDBC22F1B04771100B021E0 /* ServiceManagement.framework */, CDF08CE91AC8D97B009B3423 /* Quartz.framework */, @@ -347,21 +364,21 @@ 1D21BC54172AF43D009D1CFD /* KnockKnock */ = { isa = PBXGroup; children = ( - CD7B9F4E1ACB9A8400DF3C71 /* Exception.h */, - CD7B9F4F1ACB9A8400DF3C71 /* Exception.m */, CDF08CC71AC4C678009B3423 /* PrefsWindowController.h */, CDF08CC81AC4C678009B3423 /* PrefsWindowController.m */, - CDA81D731A95B4FB009790E2 /* icon.iconset */, CDA81D561A95B4B4009790E2 /* MainMenu.xib */, CDF08CC41AC46E75009B3423 /* VTButton.h */, CDF08CC51AC46E75009B3423 /* VTButton.m */, CDA81D441A95B492009790E2 /* AppDelegate.h */, CDA81D451A95B492009790E2 /* AppDelegate.m */, CDA81D481A95B492009790E2 /* Consts.h */, + CD24F076219DF3DB0081B0E5 /* Signing.h */, + CD24F075219DF3DA0081B0E5 /* Signing.m */, CDA81D4D1A95B492009790E2 /* Utilities.h */, CDA81D4E1A95B492009790E2 /* Utilities.m */, CD6095501A8329FA00E091CD /* images */, 1D21BC55172AF43D009D1CFD /* Supporting Files */, + CD585493219FE61D00A438B0 /* Assets.xcassets */, ); name = KnockKnock; path = "Lesson 53"; @@ -374,6 +391,7 @@ CDA81D581A95B4B4009790E2 /* KnockKnock-Info.plist */, CDA81D591A95B4B4009790E2 /* KnockKnock-Prefix.pch */, CDA81D5A1A95B4B4009790E2 /* main.m */, + CD24F072219AAC930081B0E5 /* main.h */, ); name = "Supporting Files"; sourceTree = ""; @@ -506,6 +524,7 @@ CDA81DEF1A99B8B2009790E2 /* WhiteList */ = { isa = PBXGroup; children = ( + CD24F073219D51210081B0E5 /* whitelistedKexts.json */, CDA81DF01A99B8C2009790E2 /* whitelistedCommands.json */, CDA81DF11A99B8C2009790E2 /* whitelistedExtensions.json */, CDA81DF21A99B8C2009790E2 /* whitelistedFiles.json */, @@ -558,6 +577,7 @@ 1D21BC47172AF43D009D1CFD /* Sources */, 1D21BC48172AF43D009D1CFD /* Frameworks */, 1D21BC49172AF43D009D1CFD /* Resources */, + CD6DE4D2219E9C3A0058094E /* Copy Framework(s) */, ); buildRules = ( ); @@ -574,7 +594,7 @@ 1D21BC43172AF43D009D1CFD /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0920; + LastUpgradeCheck = 1010; ORGANIZATIONNAME = "Objective-See"; TargetAttributes = { 1D21BC4A172AF43D009D1CFD = { @@ -611,6 +631,7 @@ CD02195E1AD39A74005148A2 /* ResultsWindow.xib in Resources */, CDA81D5B1A95B4B4009790E2 /* InfoPlist.strings in Resources */, CDA81D6B1A95B4E9009790E2 /* show.png in Resources */, + CD24F074219D51210081B0E5 /* whitelistedKexts.json in Resources */, CDF08CF31ACA6864009B3423 /* browserIcon.png in Resources */, CDAB98A11AEAFAFA00C75B4B /* authorizationIcon.png in Resources */, CD6BBBEB1B50DF7100506D0D /* signedAppleIcon.png in Resources */, @@ -630,6 +651,7 @@ CD7B9F4D1ACB959200DF3C71 /* logoAppleOver.png in Resources */, CDA81DCB1A9960A3009790E2 /* virusTotal.png in Resources */, CDA81DF41A99B8C2009790E2 /* whitelistedExtensions.json in Resources */, + CD585494219FE61D00A438B0 /* Assets.xcassets in Resources */, CDA81DD41A9970A0009790E2 /* unsigned.png in Resources */, CD2B61442067656200BF72E9 /* eventRulesIcon.png in Resources */, CD7B9FA71ACCAE5E00DF3C71 /* startScanOver.png in Resources */, @@ -655,7 +677,6 @@ CDBE491F1B5B46040031FC22 /* logInOutIcon.png in Resources */, CDA81D721A95B4E9009790E2 /* virus.png in Resources */, CDA81DC91A9960A3009790E2 /* info.png in Resources */, - CDA81D741A95B4FB009790E2 /* icon.iconset in Resources */, CDA81D6E1A95B4E9009790E2 /* startScan.png in Resources */, CD02194F1AD34D8B005148A2 /* AboutWindow.xib in Resources */, CDA81DCA1A9960A3009790E2 /* infoBG.png in Resources */, @@ -687,7 +708,6 @@ CDA81E071A9AFFA7009790E2 /* LoginItems.m in Sources */, CD2B613320675EEC00BF72E9 /* EventRules.m in Sources */, CDA81DEA1A997BF1009790E2 /* InfoWindowController.m in Sources */, - CD02195C1AD38823005148A2 /* kkRowCell.m in Sources */, CDA81E041A9AB89C009790E2 /* LaunchItems.m in Sources */, CDF08CE61AC89FE1009B3423 /* HyperlinkTextField.m in Sources */, CDA81E001A9A7D26009790E2 /* File.m in Sources */, @@ -696,13 +716,13 @@ CDA81D5E1A95B4B4009790E2 /* main.m in Sources */, 7DE2FE2E1D3F30BE006C1438 /* Extensions.m in Sources */, 7DE29CA41CA7BC5600DFA6A6 /* StartupScripts.m in Sources */, - CD7B9F501ACB9A8400DF3C71 /* Exception.m in Sources */, CD6975061B02F60400CE819B /* RFOverlayScroller.m in Sources */, CDA81E0A1A9B0AD8009790E2 /* BrowserExtensions.m in Sources */, CDD83FD41B50C48C0037124E /* Cronjobs.m in Sources */, CDAB989F1AEADE5A00C75B4B /* DylibInserts.m in Sources */, CDAB98991AEAA6CB00C75B4B /* AuthorizationPlugins.m in Sources */, CDA81DFE1A9A7D26009790E2 /* Command.m in Sources */, + CD24F077219DF3DB0081B0E5 /* Signing.m in Sources */, CD0219531AD34D9A005148A2 /* AboutWindowController.m in Sources */, CDA81E011A9A7D26009790E2 /* ItemBase.m in Sources */, CDA81DFF1A9A7D26009790E2 /* Extension.m in Sources */, @@ -751,11 +771,13 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; @@ -799,11 +821,13 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; @@ -832,12 +856,20 @@ 1D21BC69172AF43D009D1CFD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Developer ID Application"; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = VBG97UB4TA; + ENABLE_HARDENED_RUNTIME = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/Mac", + ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "KnockKnock-Prefix.pch"; INFOPLIST_FILE = "KnockKnock-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = KnockKnock; WRAPPER_EXTENSION = app; @@ -847,12 +879,20 @@ 1D21BC6A172AF43D009D1CFD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Developer ID Application"; COMBINE_HIDPI_IMAGES = YES; DEVELOPMENT_TEAM = VBG97UB4TA; + ENABLE_HARDENED_RUNTIME = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Carthage/Build/Mac", + ); GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "KnockKnock-Prefix.pch"; INFOPLIST_FILE = "KnockKnock-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = KnockKnock; WRAPPER_EXTENSION = app; diff --git a/KnockKnock.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/KnockKnock.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/KnockKnock.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/KnockKnock.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/KnockKnock.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 0dcfe4e..a736bee 100644 --- a/KnockKnock.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/KnockKnock.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -20,11 +20,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "Plugins/DylibProxies.m" - timestampString = "543929808.128709" + timestampString = "564302535.652194" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "148" - endingLineNumber = "148" + startingLineNumber = "189" + endingLineNumber = "189" landmarkName = "-enumLoadedDylibs" landmarkType = "7"> @@ -36,7 +36,7 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "ItemTableController.m" - timestampString = "543929808.129369" + timestampString = "564302535.652258" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "136" @@ -52,7 +52,7 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "ItemTableController.m" - timestampString = "543929808.129438" + timestampString = "564302535.652302" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "251" @@ -68,7 +68,7 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "ItemTableController.m" - timestampString = "543929808.129501" + timestampString = "564302535.652339" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" startingLineNumber = "254" @@ -77,5 +77,53 @@ landmarkType = "7"> + + + + + + + + + + + + diff --git a/KnockKnock.xcodeproj/xcuserdata/patrick.xcuserdatad/xcschemes/KnockKnock.xcscheme b/KnockKnock.xcodeproj/xcuserdata/patrick.xcuserdatad/xcschemes/KnockKnock.xcscheme index 7678a10..bcd035c 100644 --- a/KnockKnock.xcodeproj/xcuserdata/patrick.xcuserdatad/xcschemes/KnockKnock.xcscheme +++ b/KnockKnock.xcodeproj/xcuserdata/patrick.xcuserdatad/xcschemes/KnockKnock.xcscheme @@ -1,6 +1,6 @@ @@ -46,7 +45,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" + debugAsWhichUser = "root" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" @@ -63,6 +62,12 @@ ReferencedContainer = "container:KnockKnock.xcodeproj"> + + + + diff --git a/Plugins/AuthorizationPlugins.m b/Plugins/AuthorizationPlugins.m index 4c58a91..73bb278 100755 --- a/Plugins/AuthorizationPlugins.m +++ b/Plugins/AuthorizationPlugins.m @@ -48,12 +48,7 @@ NSString* const AUTHORIZATION_SEARCH_DIRECTORIES[] = {@"/System/Library/CoreServ //scan for auth plugins -(void)scan { - //auth plugin directory - NSString* authPluginDirectory = nil; - - //number of search directories - NSUInteger directoryCount = 0; - + //all auth plugins NSArray* allAuthPlugins = nil; @@ -63,19 +58,10 @@ NSString* const AUTHORIZATION_SEARCH_DIRECTORIES[] = {@"/System/Library/CoreServ //File obj File* fileObj = nil; - //dbg msg - //NSLog(@"%@: scanning", PLUGIN_NAME); - - //get number of search directories - directoryCount = sizeof(AUTHORIZATION_SEARCH_DIRECTORIES)/sizeof(AUTHORIZATION_SEARCH_DIRECTORIES[0]); - //iterate over all auth plugin search directories - // ->get all authorization plugins and process each of them - for(NSUInteger i=0; i < directoryCount; i++) + // get all authorization plugins and process each of them + for(NSString* authPluginDirectory in expandPaths(AUTHORIZATION_SEARCH_DIRECTORIES, sizeof(AUTHORIZATION_SEARCH_DIRECTORIES)/sizeof(AUTHORIZATION_SEARCH_DIRECTORIES[0]))) { - //extract current directory - authPluginDirectory = [AUTHORIZATION_SEARCH_DIRECTORIES[i] stringByExpandingTildeInPath]; - //get all items in current directory allAuthPlugins = directoryContents(authPluginDirectory, nil); diff --git a/Plugins/BrowserExtensions.m b/Plugins/BrowserExtensions.m index 0dc655f..326a6b3 100755 --- a/Plugins/BrowserExtensions.m +++ b/Plugins/BrowserExtensions.m @@ -40,6 +40,7 @@ // ->chrome #define CHROME_SECURE_PREFERENCES_FILE @"~/Library/Application Support/Google/Chrome/Default/Secure Preferences" + //plugin search directory // ->firefox #define FIREFOX_EXTENSION_DIRECTORY @"~/Library/Application Support/Firefox/Profiles/" @@ -164,8 +165,8 @@ return browsers; } - //scan for Safari extensions +// note: on Mojave+ this will just silently fail :( -(void)scanExtensionsSafari:(NSString*)browserPath { //status @@ -197,7 +198,7 @@ Extension* extensionObj = nil; //query keychain to get safari extensions - status = SecKeychainFindGenericPassword (NULL, (UInt32)strlen(SAFARI_KEYCHAIN_SERVICE), SAFARI_KEYCHAIN_SERVICE, (UInt32)strlen(SAFARI_KEYCHAIN_ACCOUNT), SAFARI_KEYCHAIN_ACCOUNT, &keychainDataLength, &keychainData, &keychainItemRef); + status = SecKeychainFindGenericPassword(NULL, (UInt32)strlen(SAFARI_KEYCHAIN_SERVICE), SAFARI_KEYCHAIN_SERVICE, (UInt32)strlen(SAFARI_KEYCHAIN_ACCOUNT), SAFARI_KEYCHAIN_ACCOUNT, &keychainDataLength, &keychainData, &keychainItemRef); //on success // ->convert binary plist keychain data (extensions) into dictionary @@ -217,18 +218,15 @@ } //make sure extensions were found - // ->bail if nothing was found/parsed + // bail if nothing was found/parsed if(nil == extensions) { - //err msg - //NSLog(@"OBJECTIVE-SEE ERROR: querying keychain for Safari extensions failed with %d", status); - //bail goto bail; } //iterate over all installed extensions - // ->save/report enabled ones + // save/report enabled ones for(NSDictionary* extension in extensions[@"Installed Extensions"]) { //alloc extension info @@ -327,12 +325,18 @@ bail: //scan for Chrome extensions -(void)scanExtensionsChrome:(NSString*)browserPath { + //all users + NSMutableDictionary* users = nil; + //preference files NSMutableArray* preferenceFiles = nil; //profile directories NSArray* profiles = nil; + //(current) profile directory + NSString* profileDirectory = nil; + //preferences NSDictionary* preferences = nil; @@ -357,28 +361,52 @@ bail: //alloc list for preference files preferenceFiles = [NSMutableArray array]; - //add default ('Preferences') - [preferenceFiles addObject:[CHROME_PREFERENCES_FILE stringByExpandingTildeInPath]]; + //alloc users dictionary + users = [NSMutableDictionary dictionary]; - //add default ('Secure Preferences') - [preferenceFiles addObject:[CHROME_SECURE_PREFERENCES_FILE stringByExpandingTildeInPath]]; - - //get profile dirs - // ->'Profile 1', etc... - profiles = directoryContents([CHROME_BASE_PROFILE_DIRECTORY stringByExpandingTildeInPath], @"self BEGINSWITH 'Profile'"); - - //build and append full paths of preferences files to list - for(NSString* profile in profiles) + //root? + // can scan all users + if(0 == geteuid()) { - //add default prefs - [preferenceFiles addObject:[NSString stringWithFormat:@"%@/%@/Preferences", [CHROME_BASE_PROFILE_DIRECTORY stringByExpandingTildeInPath], profile]]; - - //add secure prefs - [preferenceFiles addObject:[NSString stringWithFormat:@"%@/%@/Secure Preferences", [CHROME_BASE_PROFILE_DIRECTORY stringByExpandingTildeInPath], profile]]; + //all + users = allUsers(); + } + //just current user + else + { + //current + users[getConsoleUser()] = @{USER_NAME:getConsoleUser(), USER_DIRECTORY:NSHomeDirectoryForUser(getConsoleUser())}; } - //process all preference files - // ->load/parse/extract extensions + //get profile files for all users + for(NSString* userID in users) + { + //add default ('Preferences') + [preferenceFiles addObject:[users[userID][USER_DIRECTORY] stringByAppendingPathComponent:[CHROME_PREFERENCES_FILE substringFromIndex:1]]]; + + //add default ('Secure Preferences') + [preferenceFiles addObject:[users[userID][USER_DIRECTORY] stringByAppendingPathComponent:[CHROME_SECURE_PREFERENCES_FILE substringFromIndex:1]]]; + + //get profile dirs + // 'Profile 1', etc... + profiles = directoryContents([users[userID][USER_DIRECTORY] stringByAppendingPathComponent:[CHROME_BASE_PROFILE_DIRECTORY substringFromIndex:1]], @"self BEGINSWITH 'Profile'"); + + //build and append full paths of preferences files to list + for(NSString* profile in profiles) + { + //init profile directory + profileDirectory = [users[userID][USER_DIRECTORY] stringByAppendingPathComponent:[CHROME_BASE_PROFILE_DIRECTORY substringFromIndex:1]]; + + //add default prefs + [preferenceFiles addObject:[NSString stringWithFormat:@"%@/%@/Preferences", profileDirectory, profile]]; + + //add secure prefs + [preferenceFiles addObject:[NSString stringWithFormat:@"%@/%@/Secure Preferences", profileDirectory, profile]]; + } + } + + //now process all preference files + // load/parse/extract extensions from each file for(NSString* preferenceFile in preferenceFiles) { //skip non-existent preference files @@ -520,9 +548,12 @@ bail: //scan for Firefox extensions -(void)scanExtensionsFirefox:(NSString*)browserPath { + //users + NSMutableDictionary* users = nil; + //Firefox profiles NSArray* profiles = nil; - + //path to extensions NSString* extensionsFile = nil; @@ -541,8 +572,7 @@ bail: //extension path NSMutableString* path = nil; - - //extension info + //extension info NSMutableDictionary* extensionInfo = nil; //Extension object @@ -551,186 +581,204 @@ bail: //init extension IDs array extensionIDs = [NSMutableArray array]; - //get all profiles - profiles = directoryContents([FIREFOX_EXTENSION_DIRECTORY stringByExpandingTildeInPath], nil); + //alloc users dictionary + users = [NSMutableDictionary dictionary]; - //iterate over all addons and extensions files in profile directories - //->extact all addons and extensions - for(NSString* profile in profiles) + //root? + // can scan all users + if(0 == geteuid()) { - //init extension files array - extensionFiles = [NSMutableArray array]; - - //init extension info dictionary - extensionInfo = [NSMutableDictionary dictionary]; - - //init path to first extensions (addons.json) file - extensionsFile = [NSString stringWithFormat:@"%@/%@/addons.json", [FIREFOX_EXTENSION_DIRECTORY stringByExpandingTildeInPath], profile]; - - //only add to list if it exists - if(YES == [[NSFileManager defaultManager] fileExistsAtPath:extensionsFile]) + //all + users = allUsers(); + } + //just current user + else + { + //current + users[getConsoleUser()] = @{USER_NAME:getConsoleUser(), USER_DIRECTORY:NSHomeDirectoryForUser(getConsoleUser())}; + } + + //get profile files for all users + for(NSString* userID in users) + { + //get user profiles + profiles = directoryContents([users[userID][USER_DIRECTORY] stringByAppendingPathComponent:[FIREFOX_EXTENSION_DIRECTORY substringFromIndex:1]], nil); + + //iterate over all addons and extensions files in profile directories + //->extact all addons and extensions + for(NSString* profile in profiles) { - //save - [extensionFiles addObject:extensionsFile]; - } - - //init path to second extensions (extensions.json) file - extensionsFile = [NSString stringWithFormat:@"%@/%@/extensions.json", [FIREFOX_EXTENSION_DIRECTORY stringByExpandingTildeInPath], profile]; - - //only add to list if it exists - if(YES == [[NSFileManager defaultManager] fileExistsAtPath:extensionsFile]) - { - //save - [extensionFiles addObject:extensionsFile]; - } - - //process both files - for(NSString* extensionFile in extensionFiles) - { - //load extensions - // ->wrap since we are serializing JSON - @try + //init extension files array + extensionFiles = [NSMutableArray array]; + + //init extension info dictionary + extensionInfo = [NSMutableDictionary dictionary]; + + //init path to first extensions (addons.json) file + extensionsFile = [NSString stringWithFormat:@"%@/%@/addons.json", [users[userID][USER_DIRECTORY] stringByAppendingPathComponent:[FIREFOX_EXTENSION_DIRECTORY substringFromIndex:1]], profile]; + if(YES == [[NSFileManager defaultManager] fileExistsAtPath:extensionsFile]) { - //load em - // ->extension files, under 'addons' key - extensions = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:extensionFile] options:kNilOptions error:NULL][@"addons"]; - } - //catch any exceptions - // ->just try next - @catch(NSException *exception) - { - //next - continue; + //save + [extensionFiles addObject:extensionsFile]; } - //parse out all extensions - for(NSDictionary* extension in extensions) + //init path to second extensions (extensions.json) file + extensionsFile = [NSString stringWithFormat:@"%@/%@/extensions.json", [users[userID][USER_DIRECTORY] stringByAppendingPathComponent:[FIREFOX_EXTENSION_DIRECTORY substringFromIndex:1]], profile]; + if(YES == [[NSFileManager defaultManager] fileExistsAtPath:extensionsFile]) { - //ignore dups - if(YES == [extensionIDs containsObject:extension[@"id"]]) + //save + [extensionFiles addObject:extensionsFile]; + } + + //process both files + for(NSString* extensionFile in extensionFiles) + { + //load extensions + // ->wrap since we are serializing JSON + @try { - //skip + //load em + // ->extension files, under 'addons' key + extensions = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfFile:extensionFile] options:kNilOptions error:NULL][@"addons"]; + } + //catch any exceptions + // ->just try next + @catch(NSException *exception) + { + //next continue; } - //extract/save extension ID - extensionInfo[KEY_EXTENSION_ID] = extension[@"id"]; - - //extract/save path, name, details - // ->case: addons.json file - if(YES == [[extensionFile lastPathComponent] isEqualToString:@"addons.json"]) + //parse out all extensions + for(NSDictionary* extension in extensions) { - //extract path - path = [NSMutableString stringWithFormat:@"%@/extensions/%@.xpi", [extensionFile stringByDeletingLastPathComponent], extension[@"id"]]; - - //skip invalid/not found paths - if(YES != [[NSFileManager defaultManager] fileExistsAtPath:path]) + //ignore dups + if(YES == [extensionIDs containsObject:extension[@"id"]]) { //skip continue; } - //save path - extensionInfo[KEY_RESULT_PATH] = path; - - //skip blank names - if(nil == extension[@"name"]) - { - //skip - continue; - } + //extract/save extension ID + extensionInfo[KEY_EXTENSION_ID] = extension[@"id"]; - //extract/save name - extensionInfo[KEY_RESULT_NAME] = extension[@"name"]; - - //extract/save details - if(nil != extension[@"description"]) + //extract/save path, name, details + // ->case: addons.json file + if(YES == [[extensionFile lastPathComponent] isEqualToString:@"addons.json"]) { - //save - extensionInfo[KEY_EXTENSION_DETAILS] = extension[@"description"]; - } - } - //extract/save path, name, details - // ->case: extensions.json file - else - { - //extract path - path = [NSMutableString stringWithFormat:@"%@/extensions/%@", [extensionFile stringByDeletingLastPathComponent], extension[@"id"]]; - - //skip invalid/not found paths - // ->note: also checks for extensions that end in .xpi (to account for newer versions of firefox) - if(YES != [[NSFileManager defaultManager] fileExistsAtPath:path]) - { - //create .xpi version - [path appendString:@".xpi"]; + //extract path + path = [NSMutableString stringWithFormat:@"%@/extensions/%@.xpi", [extensionFile stringByDeletingLastPathComponent], extension[@"id"]]; - //check this variation too + //skip invalid/not found paths if(YES != [[NSFileManager defaultManager] fileExistsAtPath:path]) { //skip continue; } + + //save path + extensionInfo[KEY_RESULT_PATH] = path; + + //skip blank names + if(nil == extension[@"name"]) + { + //skip + continue; + } + + //extract/save name + extensionInfo[KEY_RESULT_NAME] = extension[@"name"]; + + //extract/save details + if(nil != extension[@"description"]) + { + //save + extensionInfo[KEY_EXTENSION_DETAILS] = extension[@"description"]; + } + } + //extract/save path, name, details + // ->case: extensions.json file + else + { + //extract path + path = [NSMutableString stringWithFormat:@"%@/extensions/%@", [extensionFile stringByDeletingLastPathComponent], extension[@"id"]]; + + //skip invalid/not found paths + // ->note: also checks for extensions that end in .xpi (to account for newer versions of firefox) + if(YES != [[NSFileManager defaultManager] fileExistsAtPath:path]) + { + //create .xpi version + [path appendString:@".xpi"]; + + //check this variation too + if(YES != [[NSFileManager defaultManager] fileExistsAtPath:path]) + { + //skip + continue; + } + } + + //save path + extensionInfo[KEY_RESULT_PATH] = path; + + //extract default locale + defaultLocale = extension[@"defaultLocale"]; + + //skip nil defaultLocales + if(nil == defaultLocale) + { + //skip + continue; + } + + //skip blank names + if(nil == defaultLocale[@"name"]) + { + //skip + continue; + } + + //extract/save name + extensionInfo[KEY_RESULT_NAME] = defaultLocale[@"name"]; + + //extract/save details + if(nil != defaultLocale[@"description"]) + { + //save + extensionInfo[KEY_EXTENSION_DETAILS] = defaultLocale[@"description"]; + } } - //save path - extensionInfo[KEY_RESULT_PATH] = path; + //save extension ID + // ->prevents dups (since multiple files are being parsed) + [extensionIDs addObject:extensionInfo[KEY_EXTENSION_ID]]; - //extract default locale - defaultLocale = extension[@"defaultLocale"]; + //save browser path (i.e. Firefox) + extensionInfo[KEY_EXTENSION_BROWSER] = browserPath; - //skip nil defaultLocales - if(nil == defaultLocale) + //save plugin + extensionInfo[KEY_RESULT_PLUGIN] = self; + + //create Extension object for launch item + // ->skip those that err out for any reason + if(nil == (extensionObj = [[Extension alloc] initWithParams:extensionInfo])) { //skip continue; } - //skip blank names - if(nil == defaultLocale[@"name"]) - { - //skip - continue; - } + //process item + // ->save and report to UI + [super processItem:extensionObj]; - //extract/save name - extensionInfo[KEY_RESULT_NAME] = defaultLocale[@"name"]; - - //extract/save details - if(nil != defaultLocale[@"description"]) - { - //save - extensionInfo[KEY_EXTENSION_DETAILS] = defaultLocale[@"description"]; - } - } + }//for all extension in file - //save extension ID - // ->prevents dups (since multiple files are being parsed) - [extensionIDs addObject:extensionInfo[KEY_EXTENSION_ID]]; - - //save browser path (i.e. Firefox) - extensionInfo[KEY_EXTENSION_BROWSER] = browserPath; - - //save plugin - extensionInfo[KEY_RESULT_PLUGIN] = self; - - //create Extension object for launch item - // ->skip those that err out for any reason - if(nil == (extensionObj = [[Extension alloc] initWithParams:extensionInfo])) - { - //skip - continue; - } - - //process item - // ->save and report to UI - [super processItem:extensionObj]; - - }//for all extension in file + }//for all extension files + + }//for all profiles - }//for all extension files - - }//for all profiles - + }//for all users + return; } diff --git a/Plugins/Cronjobs.m b/Plugins/Cronjobs.m index 1d01497..590fa0c 100755 --- a/Plugins/Cronjobs.m +++ b/Plugins/Cronjobs.m @@ -21,7 +21,7 @@ @implementation CronJobs //init -// ->set name, description, etc +// set name, description, etc -(id)init { //super @@ -47,32 +47,72 @@ //output from crontab NSData* taskOutput = nil; + //cron file + // for now, just current user's + NSString* cronFile = nil; + + //root? + // scan all user's cron jobs + if(0 == geteuid()) + { + //get all users + for(NSString* user in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:CRON_FILES_DIRECTORY error:nil]) + { + //path + cronFile = [NSString stringWithFormat:@"%@/%@", CRON_FILES_DIRECTORY, user]; + + //exec cron + // pass in user + taskOutput = execTask(CRONTAB, @[@"-l", @"-u", user]); + if( (nil == taskOutput) || + (0 == taskOutput.length) ) + { + //skip + continue; + } + + //process + [self processJobs:taskOutput path:cronFile]; + } + } + + //no root + // only scan current user's + else + { + //init cron file to current user + cronFile = [NSString stringWithFormat:@"%@/%@", CRON_FILES_DIRECTORY, NSUserName()]; + + //exec cron + // just for current user + taskOutput = execTask(CRONTAB, @[@"-l"]); + if( (nil == taskOutput) || + (0 == taskOutput.length) ) + { + //bail + goto bail; + } + + //process + [self processJobs:taskOutput path:cronFile]; + } + +bail: + + return; +} + +//parse/process cron jobs +-(void)processJobs:(NSData*)output path:(NSString*)path +{ //converted to string NSString* cronJobs = nil; - //cron file - // ->for now, just current user's - NSString* cronFile = nil; - //Command obj Command* commandObj = nil; - //init cron file - // ->for now, just path to current users & only used for path of command (not directly read, etc) - cronFile = [NSString stringWithFormat:@"%@/%@", CRON_FILES_DIRECTORY, NSUserName()]; - - //exec cron - // ->just for current user - taskOutput = execTask(CRONTAB, @[@"-l"]); - if( (nil == taskOutput) || - (0 == taskOutput.length) ) - { - //bail - goto bail; - } - //convert to (trimmed) string - cronJobs = [[[NSString alloc] initWithData:taskOutput encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + cronJobs = [[[NSString alloc] initWithData:output encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; //sanity check // ->skip blank results @@ -82,7 +122,7 @@ //bail goto bail; } - + //create Command obj for each // ->and call back up into UI to add for(NSString* cronJob in [cronJobs componentsSeparatedByString:@"\n"]) @@ -96,7 +136,7 @@ } //create Command object for job - commandObj = [[Command alloc] initWithParams:@{KEY_RESULT_PLUGIN:self, KEY_RESULT_COMMAND:cronJob, KEY_RESULT_PATH:cronFile}]; + commandObj = [[Command alloc] initWithParams:@{KEY_RESULT_PLUGIN:self, KEY_RESULT_COMMAND:cronJob, KEY_RESULT_PATH:path}]; //skip Command objects that err'd out for any reason if(nil == commandObj) @@ -110,7 +150,6 @@ [super processItem:commandObj]; } -//bail bail: return; diff --git a/Plugins/DylibInserts.h b/Plugins/DylibInserts.h index 8a2f64d..8951255 100755 --- a/Plugins/DylibInserts.h +++ b/Plugins/DylibInserts.h @@ -10,6 +10,12 @@ #import "PluginBase.h" +/* GLOBALS */ + +//shared enumerator +extern ItemEnumerator* sharedItemEnumerator; + + @interface DylibInserts : PluginBase { diff --git a/Plugins/DylibInserts.m b/Plugins/DylibInserts.m index 63aab44..4ca6ce8 100755 --- a/Plugins/DylibInserts.m +++ b/Plugins/DylibInserts.m @@ -111,7 +111,7 @@ //try grab launch items // ->will only !nil, when enumeration is complete - launchItems = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).sharedItemEnumerator.launchItems; + launchItems = sharedItemEnumerator.launchItems; //keep trying until we get em! } while(nil == launchItems); @@ -223,7 +223,7 @@ //try grab installed apps // ->will only !nil, when enumeration is complete - installedApps = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).sharedItemEnumerator.applications; + installedApps = sharedItemEnumerator.applications; //exit loop once we have apps if(nil != installedApps) diff --git a/Plugins/DylibProxies.m b/Plugins/DylibProxies.m index fd2bf5f..76192bb 100755 --- a/Plugins/DylibProxies.m +++ b/Plugins/DylibProxies.m @@ -17,6 +17,9 @@ //plugin icon #define PLUGIN_ICON @"proxyIcon" +//(privacy) protected directories +NSString * const PROTECTED_DIRECTORIES[] = {@"~/Library/Application Support/AddressBook", @"~/Library/Calendars", @"~/Pictures", @"~/Library/Mail", @"~/Library/Messages", @"~/Library/Safari", @"~/Library/Cookies", @"~/Library/HomeKit", @"~/Library/IdentityServices", @"~/Library/Metadata/CoreSpotlight", @"~/Library/PersonalizationPortrait", @"~/Library/Suggestions"}; + @implementation DylibProxies //init @@ -74,13 +77,23 @@ //file path NSString* filePath = nil; + //(privacy) protected directories + NSArray* protectedDirectories = nil; + + //flag + BOOL isProtected = NO; + //pool @autoreleasepool { //alloc array dylibs = [NSMutableArray array]; - + + //init set of (privacy) protected directories + // these will be skipped, as otherwise we will generate a privacy prompt + protectedDirectories = expandPaths(PROTECTED_DIRECTORIES, sizeof(PROTECTED_DIRECTORIES)/sizeof(PROTECTED_DIRECTORIES[0])); + //exec 'file' to get file type results = execTask(LSOF, @[@"-Fn", @"/"]); if( (nil == results) || @@ -102,9 +115,12 @@ } //iterate over all results - // ->make file info dictionary for files (not sockets, etc) + // make file info dictionary for files (not sockets, etc) for(NSString* result in splitResults) { + //reset + isProtected = NO; + //skip any odd/weird/short lines // lsof outpupt will be in format: 'n if( (YES != [result hasPrefix:@"n"]) || @@ -115,12 +131,37 @@ } //init file path - // ->result, minus first (lsof-added) char + // result, minus first (lsof-added) char filePath = [result substringFromIndex:0x1]; + //skip any files in (privacy) protected directories + // as otherwise we will generate a privacy prompt (on Mojave) + for(NSString* directory in protectedDirectories) + { + //reset + isProtected = NO; + + //check + if(YES == [filePath hasPrefix:directory]) + { + //set flag + isProtected = YES; + + //done + break; + } + } + + //skip (privacy) protected files + if(YES == isProtected) + { + //skip + continue; + } + //skip 'non files' / non-executable files if( (YES != [[NSFileManager defaultManager] fileExistsAtPath:filePath]) || - (YES != isURLExecutable([NSURL fileURLWithPath:filePath])) ) + (YES != isExecutable(filePath)) ) { //skip continue; diff --git a/Plugins/Extensions.m b/Plugins/Extensions.m index 694d76f..9e2c4b7 100755 --- a/Plugins/Extensions.m +++ b/Plugins/Extensions.m @@ -3,6 +3,7 @@ // KnockKnock // // Notes: view via these via System Preferences->Extensions, or pluginkit -vmA +// only for current user, since we utilized 'pluginkit' which is "for current user" #import "File.h" #import "Utilities.h" @@ -69,7 +70,7 @@ [extensions addObjectsFromArray:[self parseExtensions:[[[NSString alloc] initWithData:taskOutput encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]]]; //load finder syncs from plist - finderSyncs = [NSDictionary dictionaryWithContentsOfFile:[FINDER_SYNCS stringByExpandingTildeInPath]]; + finderSyncs = [NSDictionary dictionaryWithContentsOfFile:[NSHomeDirectoryForUser(getConsoleUser()) stringByAppendingPathComponent:[FINDER_SYNCS substringFromIndex:1]]]; if( (nil == finderSyncs) || (nil == finderSyncs[@"displayOrder"]) ) { diff --git a/Plugins/LaunchItems.h b/Plugins/LaunchItems.h index 25c1efa..fcd56f3 100755 --- a/Plugins/LaunchItems.h +++ b/Plugins/LaunchItems.h @@ -10,6 +10,12 @@ #import "PluginBase.h" +/* GLOBALS */ + +//shared enumerator +extern ItemEnumerator* sharedItemEnumerator; + + @interface LaunchItems : PluginBase { diff --git a/Plugins/LaunchItems.m b/Plugins/LaunchItems.m index 584dd98..01b8eb8 100755 --- a/Plugins/LaunchItems.m +++ b/Plugins/LaunchItems.m @@ -77,7 +77,7 @@ //try grab launch items // ->will only !nil, when enumeration is complete - launchItems = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).sharedItemEnumerator.launchItems; + launchItems = sharedItemEnumerator.launchItems; //keep trying until we get em! } while(nil == launchItems); diff --git a/Plugins/LogInOutHooks.m b/Plugins/LogInOutHooks.m index d85b0d2..dc27259 100755 --- a/Plugins/LogInOutHooks.m +++ b/Plugins/LogInOutHooks.m @@ -52,25 +52,13 @@ NSString* const HOOK_SEARCH_FILES[] = {@"/Library/Preferences/com.apple.loginwin //scan for login items -(void)scan { - //number of search directories - NSUInteger fileCount = 0; - - //login window plist - NSString* loginWindowPlist = nil; - //plist data NSDictionary* plistContents = nil; - //get number of login/out files - fileCount = sizeof(HOOK_SEARCH_FILES)/sizeof(HOOK_SEARCH_FILES[0]); - //iterate over all login/out file // ->get all hooks and process em - for(NSUInteger i=0; i < fileCount; i++) + for(NSString* loginWindowPlist in expandPaths(HOOK_SEARCH_FILES, sizeof(HOOK_SEARCH_FILES)/sizeof(HOOK_SEARCH_FILES[0]))) { - //extract current file - loginWindowPlist = [HOOK_SEARCH_FILES[i] stringByExpandingTildeInPath]; - //load plist contents plistContents = [NSDictionary dictionaryWithContentsOfFile:loginWindowPlist]; @@ -89,7 +77,6 @@ NSString* const HOOK_SEARCH_FILES[] = {@"/Library/Preferences/com.apple.loginwin } } -//bail bail: return; diff --git a/Plugins/LoginItems.h b/Plugins/LoginItems.h index c3c0653..c4a1e24 100755 --- a/Plugins/LoginItems.h +++ b/Plugins/LoginItems.h @@ -16,10 +16,6 @@ /* PROPERTIES */ -//all enabled jobs -// ->includes (sandboxed) login items -@property(nonatomic, retain) NSMutableArray* enabledJobs; - /* (custom) METHODS */ //enumerate traditional login items @@ -30,5 +26,4 @@ // ->scan /Applications for 'Contents/Library/LoginItems/' and xref w/ launchd jobs -(NSMutableArray*)enumSandboxItems; - @end diff --git a/Plugins/LoginItems.m b/Plugins/LoginItems.m index 6abca44..adcdb1b 100755 --- a/Plugins/LoginItems.m +++ b/Plugins/LoginItems.m @@ -18,9 +18,13 @@ //plugin icon #define PLUGIN_ICON @"loginIcon" -@implementation LoginItems +//plist (old) +#define LOGIN_ITEM_PLIST_OLD @"~/Library/Preferences/com.apple.loginitems.plist" -@synthesize enabledJobs; +//plist (new) +#define LOGIN_ITEM_PLIST_NEW @"~/Library/Application Support/com.apple.backgroundtaskmanagementagent/backgrounditems.btm" + +@implementation LoginItems //init // ->set name, description, etc @@ -30,9 +34,6 @@ self = [super init]; if(self) { - //alloc array for enabled jobs - enabledJobs = [NSMutableArray array]; - //set name self.name = PLUGIN_NAME; @@ -49,9 +50,6 @@ //scan for login items -(void)scan { - //all jobs - NSArray* jobs = nil; - //detected (auto-started) login item File* fileObj = nil; @@ -59,32 +57,9 @@ //NSLog(@"%@: scanning", PLUGIN_NAME); //login items - // ->both traditional and sandboxed + // both traditional and sandboxed NSMutableArray* loginItems = nil; - - //reset enabled jobs - [self.enabledJobs removeAllObjects]; - - //get all jobs - // ->includes all enabled (sandboxed) login items, even if not running :) - jobs = (__bridge NSArray *)SMCopyAllJobDictionaries(kSMDomainUserLaunchd); - - //build list of enabled jobs - // ->save their bundle IDs - for(NSDictionary* job in jobs) - { - //skip non-enabled jobs or jobs w/o bundles ids - if( (YES != [[job objectForKey:@"OnDemand"] boolValue]) || - (nil == [job objectForKey:@"Label"]) ) - { - //next - continue; - } - - //save enabled job - [enabledJobs addObject:[job objectForKey:@"Label"]]; - } - + //first get traditional items loginItems = [self enumTraditionalItems]; @@ -94,13 +69,11 @@ //remove any duplicates loginItems = [[[NSSet setWithArray:loginItems] allObjects] mutableCopy]; - //enum and create traditional login items + //process all for(NSString* loginItem in loginItems) { //create File object for login item fileObj = [[File alloc] initWithParams:@{KEY_RESULT_PLUGIN:self, KEY_RESULT_PATH:loginItem}]; - - //skip File objects that err'd out for any reason if(nil == fileObj) { //skip @@ -108,18 +81,15 @@ } //process item - // ->save and report to UI + // save and report to UI [super processItem:fileObj]; } - //release jobs - CFRelease((CFArrayRef)jobs); - return; } //enumerate traditional login items -// ->basically just invoke LSSharedFileListCopySnapshot(), etc to get list of items +// invoke LSSharedFileListCopySnapshot(), etc to get list of items -(NSMutableArray*)enumTraditionalItems { //(traditional) login items @@ -132,7 +102,7 @@ CFArrayRef loginItems = nil; //seed - // ->needed for 'LSSharedFileListCopySnapshot' function + // needed for 'LSSharedFileListCopySnapshot' function UInt32 snapshotSeed = 0; //login item reference @@ -151,7 +121,7 @@ loginItems = LSSharedFileListCopySnapshot(sharedListRef, &snapshotSeed); //iterate over all items - // ->extract path, init File obj, and report to UI + // extracting path for each for(id item in (__bridge NSArray *)loginItems) { //type-cast @@ -181,8 +151,277 @@ return traditionalItems; } +//extract login items from alias data +// older versions of OSX use this format... +-(NSMutableDictionary*)extractFromAlias:(NSDictionary*)data +{ + //login items + NSMutableDictionary* loginItems = nil; + + //init + loginItems = [NSMutableDictionary dictionary]; + + //name + NSString* name = nil; + + //alias + NSData* alias = nil; + + //bookmark + CFDataRef bookmark = NULL; + + //bookmark url + CFURLRef url = NULL; + + //path + NSString* path = nil; + + //extract current login items + for(NSDictionary* loginItem in data[@"SessionItems"][@"CustomListItems"]) + { + //extract alias + alias = loginItem[@"Alias"]; + if(nil == alias) + { + //skip + continue; + } + + //create bookmark + bookmark = CFURLCreateBookmarkDataFromAliasRecord(kCFAllocatorDefault,(__bridge CFDataRef)(alias)); + if(NULL == bookmark) + { + //skip + continue; + } + + //resolve bookmark data into URL + url = CFURLCreateByResolvingBookmarkData(kCFAllocatorDefault, bookmark, kCFBookmarkResolutionWithoutUIMask, nil, nil, nil, nil); + + //now release bookmark + CFRelease(bookmark); + + //sanity check + if(nil == url) + { + //skip + continue; + } + + //extract path + path = CFBridgingRelease(CFURLCopyPath(url)); + + //now release url + CFRelease(url); + + //sanity check + if(nil == path) + { + //skip + continue; + } + + //use name from app bundle + // otherwise from 'NSURLNameKey' + name = [NSBundle bundleWithPath:path].infoDictionary[@"CFBundleName"]; + if(0 == name.length) + { + //extract name + name = loginItem[@"Name"]; + } + + //sanity check + if(nil == name) + { + //skip + continue; + } + + //add + // key: path + // value: name + loginItems[path] = name; + } + + return loginItems; +} + +//extract login items from bookmark data +// newer versions of macOS use this format... +-(NSMutableDictionary*)extractFromBookmark:(NSDictionary*)data +{ + //login items + NSMutableDictionary* loginItems = nil; + + //init + loginItems = [NSMutableDictionary dictionary]; + + //bookmark data + NSData* bookmark = nil; + + //bookmark properties + NSDictionary* properties = nil; + + //name + NSString* name = nil; + + //path + NSString* path = nil; + + //extract current login items + for(id object in data[@"$objects"]) + { + //reset + bookmark = nil; + + //straight data? + if(YES == [object isKindOfClass:[NSData class]]) + { + //assign + bookmark = object; + } + + //dictionary w/ data? + if(YES == [object isKindOfClass:[NSDictionary class]]) + { + //extract bookmark data + bookmark = [object objectForKey:@"NS.data"]; + } + + //no data? + if(nil == bookmark) + { + //skip + continue; + } + + //extact properties + // 'resourceValuesForKeys' returns a dictionary, but we want the 'NSURLBookmarkAllPropertiesKey' dictionary inside that + properties = [NSURL resourceValuesForKeys:@[@"NSURLBookmarkAllPropertiesKey"] fromBookmarkData:bookmark][@"NSURLBookmarkAllPropertiesKey"]; + if(nil == properties) + { + //skip + continue; + } + + //extract path + path = properties[@"_NSURLPathKey"]; + + //use name from app bundle + // otherwise from 'NSURLNameKey' + name = [NSBundle bundleWithPath:path].infoDictionary[@"CFBundleName"]; + if(0 == name.length) + { + //extract name + name = properties[@"NSURLNameKey"]; + } + + //skip any issues + if( (nil == name) || + (nil == path) ) + { + //skip + continue; + } + + //add + // key: path + // value: name + loginItems[path] = name; + } + + return loginItems; +} + +//enumerate registered login items +-(NSMutableDictionary*)enumRegisteredItems +{ + //flag + BOOL bookmarkFormat = NO; + + //plist file + NSString* plist = nil; + + //plist data + NSDictionary* plistData = nil; + + //users + NSMutableDictionary* users = nil; + + //registered items + NSMutableDictionary* registeredItems = nil; + + //alloc registered login items + registeredItems = [NSMutableDictionary dictionary]; + + //alloc users dictionary + users = [NSMutableDictionary dictionary]; + + //set flag + // pre-10.13, did not use bookmark format + bookmarkFormat = (13 < getVersion(gestaltSystemVersionMinor)); + + //root? + // can scan all users + if(0 == geteuid()) + { + //all + users = allUsers(); + } + //just current user + else + { + //current + users[getConsoleUser()] = @{USER_NAME:getConsoleUser(), USER_DIRECTORY:NSHomeDirectoryForUser(getConsoleUser())}; + } + + //process all plists + for(NSString* userID in users) + { + //new format? + // use new plist + if(YES == bookmarkFormat) + { + //new plist + plist = [users[userID][USER_DIRECTORY] stringByAppendingPathComponent:[LOGIN_ITEM_PLIST_NEW substringFromIndex:1]]; + + //load plist data + plistData = [NSDictionary dictionaryWithContentsOfFile:plist]; + if(0 == plistData.count) + { + //skip + continue; + } + + //extract login items + [registeredItems addEntriesFromDictionary:[self extractFromBookmark:plistData]]; + } + //old format + // use old plist + else + { + //old plist + plist = [users[userID][USER_DIRECTORY] stringByAppendingPathComponent:[LOGIN_ITEM_PLIST_OLD substringFromIndex:1]]; + + //load plist data + plistData = [NSDictionary dictionaryWithContentsOfFile:plist]; + if(0 == plistData.count) + { + //skip + continue; + } + + //extract login items + [registeredItems addEntriesFromDictionary:[self extractFromAlias:plistData]]; + } + } + + return registeredItems; +} + + //enumerate sandboxed (app) login items -// ->scan /Applications for 'Contents/Library/LoginItems/' and xref w/ launchd jobs +// scan /Applications for 'Contents/Library/LoginItems/' and xref w/ those in various plists jobs -(NSMutableArray*)enumSandboxItems { //(sandbox) login items @@ -194,6 +433,10 @@ //path to (sandboxed) login item directory NSString* loginItemDir = nil; + //registered items + // extracted from various plists + NSMutableDictionary* registeredItems = nil; + //candidate login items NSArray* candidateItems = nil; @@ -203,6 +446,9 @@ //alloc array sandboxItems = [NSMutableArray array]; + //generate list of registered items + registeredItems = [self enumRegisteredItems]; + //get all installed applications applications = [[NSFileManager defaultManager] directoryContentsAtPath:@"/Applications"]; @@ -211,8 +457,6 @@ { //init path to possible (sandboxed) login item dir loginItemDir = [NSString stringWithFormat:@"/Applications/%@/Contents/Library/LoginItems/", application]; - - //skip if app doesn't have any login items if(YES != [[NSFileManager defaultManager] fileExistsAtPath:loginItemDir]) { //next @@ -220,18 +464,16 @@ } //get all app's login items - // ->these should (each) be apps/bundles themselves + // these should (each) be apps/bundles themselves candidateItems = [[NSFileManager defaultManager] directoryContentsAtPath:loginItemDir]; //process app's candidate login items - // ->get bundle, path, and bundle id - // then check to make sure there is a job that matches! + // get bundle, path, and bundle id + // then check to make sure there is a registered item that matches! for(NSString* candidateItem in candidateItems) { //get bundle for candidate login item candidateItemBundle = [NSBundle bundleWithPath:[NSString stringWithFormat:@"%@/%@", loginItemDir, candidateItem]]; - - //skip ones that don't have bundles, binary paths, etc if( (nil == candidateItemBundle) || (nil == candidateItemBundle.executablePath) ) { @@ -239,15 +481,15 @@ continue; } - //skip items that aren't enabled - if(YES != [self.enabledJobs containsObject:candidateItemBundle.bundleIdentifier]) + //skip if does match any registered items + if(nil == registeredItems[candidateItemBundle.bundlePath]) { - //next + //skip continue; } //save (sandboxed) login item - [sandboxItems addObject:candidateItemBundle.executablePath]; + [sandboxItems addObject:candidateItemBundle.bundlePath]; }//app's login item(s) diff --git a/Plugins/PeriodicScrips.m b/Plugins/PeriodicScrips.m index b3fe883..3842056 100755 --- a/Plugins/PeriodicScrips.m +++ b/Plugins/PeriodicScrips.m @@ -50,9 +50,6 @@ NSString* const PERIODIC_SCRIPTS_SEARCH_DIRECTORIES[] = {@"/etc/periodic/daily", //scan for periodic scripts -(void)scan { - //periodic scripts directory - NSString* periodScriptDirectory = nil; - //number of search directories NSUInteger directoryCount = 0; @@ -69,7 +66,7 @@ NSString* const PERIODIC_SCRIPTS_SEARCH_DIRECTORIES[] = {@"/etc/periodic/daily", directoryCount = sizeof(PERIODIC_SCRIPTS_SEARCH_DIRECTORIES)/sizeof(PERIODIC_SCRIPTS_SEARCH_DIRECTORIES[0]); //always a period script config file - // ->its executed by each period script, so should be reported + // its executed by each period script, so should be reported fileObj = [[File alloc] initWithParams:@{KEY_RESULT_PLUGIN:self, KEY_RESULT_PATH:PERIOD_CONFIG}]; if(nil != fileObj) { @@ -79,21 +76,18 @@ NSString* const PERIODIC_SCRIPTS_SEARCH_DIRECTORIES[] = {@"/etc/periodic/daily", } //iterate over all script directories - // ->get all script files and process them + // get all script files and process them for(NSUInteger i=0; i < directoryCount; i++) { - //extract current directory - periodScriptDirectory = [PERIODIC_SCRIPTS_SEARCH_DIRECTORIES[i] stringByExpandingTildeInPath]; - //get all items in current directory - allPeriodicScripts = directoryContents(periodScriptDirectory, nil); + allPeriodicScripts = directoryContents(PERIODIC_SCRIPTS_SEARCH_DIRECTORIES[i], nil); //iterate over all importers // ->perform some sanity checks and then save for(NSString* periodicScript in allPeriodicScripts) { //build full path to script - periodScriptPathPath = [NSString stringWithFormat:@"%@/%@", periodScriptDirectory, periodicScript]; + periodScriptPathPath = [NSString stringWithFormat:@"%@/%@", PERIODIC_SCRIPTS_SEARCH_DIRECTORIES[i], periodicScript]; //create File object for script fileObj = [[File alloc] initWithParams:@{KEY_RESULT_PLUGIN:self, KEY_RESULT_PATH:periodScriptPathPath}]; diff --git a/Plugins/PluginBase.h b/Plugins/PluginBase.h index c8db622..fb2fe83 100755 --- a/Plugins/PluginBase.h +++ b/Plugins/PluginBase.h @@ -15,6 +15,9 @@ } +//callback +@property(copy, nonatomic) void (^callback)(ItemBase*); + //name @property(retain, nonatomic)NSString* name; @@ -46,8 +49,4 @@ // ->save and report (if necessary) -(void)processItem:(ItemBase*)item; - - - - @end diff --git a/Plugins/PluginBase.m b/Plugins/PluginBase.m index 16b0f92..f93fdcf 100755 --- a/Plugins/PluginBase.m +++ b/Plugins/PluginBase.m @@ -75,7 +75,7 @@ //process and item -// ->save and report (if necessary) +// save and report (if necessary) -(void)processItem:(ItemBase*)item { //exit if scanner (self) thread was cancelled @@ -107,8 +107,12 @@ } } - //report it to the UI - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) itemFound:item]; + //invoke callback + if(nil != self.callback) + { + //invoke + self.callback(item); + } return; } @@ -129,4 +133,4 @@ return; } -@end \ No newline at end of file +@end diff --git a/Plugins/SpotlightImporters.m b/Plugins/SpotlightImporters.m index 2c7d403..f33126e 100755 --- a/Plugins/SpotlightImporters.m +++ b/Plugins/SpotlightImporters.m @@ -46,12 +46,6 @@ NSString * const SPOTLIGHT_SEARCH_DIRECTORIES[] = {@"/System/Library/Spotlight", //scan for spotlight importers -(void)scan { - //spotlight importer directory - NSString* importerDirectory = nil; - - //number of search directories - NSUInteger directoryCount = 0; - //all spotlight importers NSArray* allImporters = nil; @@ -64,16 +58,10 @@ NSString * const SPOTLIGHT_SEARCH_DIRECTORIES[] = {@"/System/Library/Spotlight", //dbg msg //NSLog(@"%@: scanning", PLUGIN_NAME); - //get number of search directories - directoryCount = sizeof(SPOTLIGHT_SEARCH_DIRECTORIES)/sizeof(SPOTLIGHT_SEARCH_DIRECTORIES[0]); - //iterate over all spotlight importer search directories - // ->get all spotlight importer bundles and process each of them - for(NSUInteger i=0; i < directoryCount; i++) + // get all spotlight importer bundles and process each of them + for(NSString* importerDirectory in expandPaths(SPOTLIGHT_SEARCH_DIRECTORIES, sizeof(SPOTLIGHT_SEARCH_DIRECTORIES)/sizeof(SPOTLIGHT_SEARCH_DIRECTORIES[0]))) { - //extract current directory - importerDirectory = [SPOTLIGHT_SEARCH_DIRECTORIES[i] stringByExpandingTildeInPath]; - //get all items in current directory allImporters = directoryContents(importerDirectory, nil); diff --git a/PrefsWindowController.m b/PrefsWindowController.m index a42fb25..1090a1c 100755 --- a/PrefsWindowController.m +++ b/PrefsWindowController.m @@ -7,6 +7,7 @@ // +#import "Utilities.h" #import "AppDelegate.h" #import "PrefsWindowController.h" @@ -35,8 +36,13 @@ //super [super windowDidLoad]; - //make white - [self.window setBackgroundColor: NSColor.whiteColor]; + //not in dark mode? + // make window white + if(YES != isDarkMode()) + { + //make white + self.window.backgroundColor = NSColor.whiteColor; + } //make button selected [self.window makeFirstResponder:self.okButton]; diff --git a/Results/Extension.m b/Results/Extension.m index 85a94ea..f9efbb6 100755 --- a/Results/Extension.m +++ b/Results/Extension.m @@ -30,7 +30,7 @@ //call into filter object to check if file is known // ->signed or whitelisted - self.isTrusted = [((AppDelegate*)[[NSApplication sharedApplication] delegate]).filterObj isTrustedExtension:self]; + self.isTrusted = [itemFilter isTrustedExtension:self]; } return self; diff --git a/Results/File.h b/Results/File.h index f6e9f59..0634a7d 100755 --- a/Results/File.h +++ b/Results/File.h @@ -7,7 +7,6 @@ // #import "ItemBase.h" - #import #import diff --git a/Results/File.m b/Results/File.m index 185f00b..ca36373 100755 --- a/Results/File.m +++ b/Results/File.m @@ -10,6 +10,7 @@ #import "File.h" #import "MachO.h" #import "Consts.h" +#import "Signing.h" #import "Utilities.h" #import "AppDelegate.h" @@ -32,6 +33,9 @@ //flag for directories BOOL isDirectory = NO; + //cs flags + SecCSFlags flags = kSecCSDefaultFlags | kSecCSCheckNestedCode | kSecCSDoNotValidateResources | kSecCSCheckAllArchitectures; + //mach-O parser MachO* machoParser = nil; @@ -108,12 +112,12 @@ //grab attributes self.attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:self.path error:nil]; - //extract signing info - self.signingInfo = extractSigningInfo(self.path); + //extract signing info statically + self.signingInfo = extractSigningInfo(0, self.path, flags); //call into filter object to check if file is known - // ->apple-signed or whitelisted - self.isTrusted = [((AppDelegate*)[[NSApplication sharedApplication] delegate]).filterObj isTrustedFile:self]; + // apple-signed or whitelisted (hash or signing id) + self.isTrusted = [itemFilter isTrustedFile:self]; //alloc macho parser iVar // ->new instance for each file! @@ -124,8 +128,8 @@ if(YES == [machoParser parse:self.path classify:YES]) { //unset 'packed' flag for apple signed binaries - // ->as apple doesn't pack binaries, but packer algo has some false positives - if(YES == [self.signingInfo[KEY_SIGNING_IS_APPLE] boolValue]) + // as apple doesn't pack binaries, but packer algo has some false positives + if(Apple == [self.signingInfo[KEY_SIGNATURE_SIGNER] intValue]) { //unset machoParser.binaryInfo[KEY_IS_PACKED] = @NO; @@ -232,7 +236,7 @@ bail: prettyPrint = [NSMutableString string];//stringWithString:@"signed by:"]; //add each signing auth - for(NSString* signingAuthority in self.signingInfo[KEY_SIGNING_AUTHORITIES]) + for(NSString* signingAuthority in self.signingInfo[KEY_SIGNATURE_AUTHORITIES]) { //append [prettyPrint appendString:[NSString stringWithFormat:@"%@, ", signingAuthority]]; diff --git a/ResultsWindowController.m b/ResultsWindowController.m index 6a3dfcc..dc9c23d 100755 --- a/ResultsWindowController.m +++ b/ResultsWindowController.m @@ -34,8 +34,13 @@ //super [super windowDidLoad]; - //make white - [self.window setBackgroundColor: NSColor.whiteColor]; + //not in dark mode? + // make window white + if(YES != isDarkMode()) + { + //make white + self.window.backgroundColor = NSColor.whiteColor; + } //set details self.detailsLabel.stringValue = self.details; diff --git a/Signing.h b/Signing.h new file mode 100644 index 0000000..b4b1c79 --- /dev/null +++ b/Signing.h @@ -0,0 +1,32 @@ +// +// File: Signing.h +// Project: Proc Info +// +// Created by: Patrick Wardle +// Copyright: 2017 Objective-See +// License: Creative Commons Attribution-NonCommercial 4.0 International License +// + +#ifndef Signing_h +#define Signing_h + +#import +#import + +/* FUNCTIONS */ + +//get the signing info of a item +// pid specified: extract dynamic code signing info +// path specified: generate static code signing info +NSMutableDictionary* extractSigningInfo(pid_t pid, NSString* path, SecCSFlags flags); + +//determine who signed item +NSNumber* extractSigner(SecStaticCodeRef code, SecCSFlags flags, BOOL isDynamic); + +//validate a requirement +OSStatus validateRequirement(SecStaticCodeRef code, SecRequirementRef requirement, SecCSFlags flags, BOOL isDynamic); + +//extract (names) of signing auths +NSMutableArray* extractSigningAuths(NSDictionary* signingDetails); + +#endif diff --git a/Signing.m b/Signing.m new file mode 100644 index 0000000..62155a8 --- /dev/null +++ b/Signing.m @@ -0,0 +1,323 @@ +// +// File: Signing.m +// Project: Proc Info +// +// Created by: Patrick Wardle +// Copyright: 2017 Objective-See +// License: Creative Commons Attribution-NonCommercial 4.0 International License +// + +#import "Consts.h" +#import "Signing.h" +#import "Utilities.h" + +#import +#import + +//get the signing info of a item +// pid specified: extract dynamic code signing info +// path specified: generate static code signing info +NSMutableDictionary* extractSigningInfo(pid_t pid, NSString* path, SecCSFlags flags) +{ + //info dictionary + NSMutableDictionary* signingInfo = nil; + + //status + OSStatus status = !errSecSuccess; + + //static code ref + SecStaticCodeRef staticCode = NULL; + + //dynamic code ref + SecCodeRef dynamicCode = NULL; + + //signing details + CFDictionaryRef signingDetails = NULL; + + //signing authorities + NSMutableArray* signingAuths = nil; + + //init signing status + signingInfo = [NSMutableDictionary dictionary]; + + //dynamic code checks + // no path, dynamic check via pid + if(nil == path) + { + //generate dynamic code ref via pid + status = SecCodeCopyGuestWithAttributes(NULL, (__bridge CFDictionaryRef _Nullable)(@{(__bridge NSString *)kSecGuestAttributePid : [NSNumber numberWithInt:pid]}), kSecCSDefaultFlags, &dynamicCode); + if(errSecSuccess != status) + { + //set error + signingInfo[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status]; + + //bail + goto bail; + } + + //validate code + status = SecCodeCheckValidity(dynamicCode, flags, NULL); + if(errSecSuccess != status) + { + //set error + signingInfo[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status]; + + //bail + goto bail; + } + + //happily signed + signingInfo[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:errSecSuccess]; + + //determine signer + // apple, app store, dev id, adhoc, etc... + signingInfo[KEY_SIGNATURE_SIGNER] = extractSigner(dynamicCode, flags, YES); + + //extract signing info + status = SecCodeCopySigningInformation(dynamicCode, kSecCSSigningInformation, &signingDetails); + if(errSecSuccess != status) + { + //bail + goto bail; + } + } + + //static code checks + else + { + //create static code ref via path + status = SecStaticCodeCreateWithPath((__bridge CFURLRef)([NSURL fileURLWithPath:path]), kSecCSDefaultFlags, &staticCode); + if(errSecSuccess != status) + { + //set error + signingInfo[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status]; + + //bail + goto bail; + } + + //check signature + status = SecStaticCodeCheckValidity(staticCode, flags, NULL); + if(errSecSuccess != status) + { + //set error + signingInfo[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status]; + + //bail + goto bail; + } + + //happily signed + signingInfo[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:errSecSuccess]; + + //determine signer + // apple, app store, dev id, adhoc, etc... + signingInfo[KEY_SIGNATURE_SIGNER] = extractSigner(staticCode, flags, NO); + + //extract signing info + status = SecCodeCopySigningInformation(staticCode, kSecCSSigningInformation, &signingDetails); + if(errSecSuccess != status) + { + //bail + goto bail; + } + } + + //extract code signing id + if(nil != [(__bridge NSDictionary*)signingDetails objectForKey:(__bridge NSString*)kSecCodeInfoIdentifier]) + { + //extract/save + signingInfo[KEY_SIGNATURE_IDENTIFIER] = [(__bridge NSDictionary*)signingDetails objectForKey:(__bridge NSString*)kSecCodeInfoIdentifier]; + } + + //extract entitlements + if(nil != [(__bridge NSDictionary*)signingDetails objectForKey:(__bridge NSString*)kSecCodeInfoEntitlementsDict]) + { + //extract/save + signingInfo[KEY_SIGNATURE_ENTITLEMENTS] = [(__bridge NSDictionary*)signingDetails objectForKey:(__bridge NSString*)kSecCodeInfoEntitlementsDict]; + } + + //extract signing authorities + signingAuths = extractSigningAuths((__bridge NSDictionary *)(signingDetails)); + if(0 != signingAuths.count) + { + //save + signingInfo[KEY_SIGNATURE_AUTHORITIES] = signingAuths; + } + +bail: + + //free signing info + if(NULL != signingDetails) + { + //free + CFRelease(signingDetails); + + //unset + signingDetails = NULL; + } + + //free dynamic code + if(NULL != dynamicCode) + { + //free + CFRelease(dynamicCode); + + //unset + dynamicCode = NULL; + } + + //free static code + if(NULL != staticCode) + { + //free + CFRelease(staticCode); + + //unset + staticCode = NULL; + } + + return signingInfo; +} + +//determine who signed item +NSNumber* extractSigner(SecStaticCodeRef code, SecCSFlags flags, BOOL isDynamic) +{ + //result + NSNumber* signer = nil; + + //"anchor apple" + static SecRequirementRef isApple = nil; + + //"anchor apple generic" + static SecRequirementRef isDevID = nil; + + //"anchor apple generic and certificate leaf [subject.CN] = \"Apple Mac OS Application Signing\"" + static SecRequirementRef isAppStore = nil; + + //token + static dispatch_once_t onceToken = 0; + + //only once + // init requirements + dispatch_once(&onceToken, ^{ + + //init apple signing requirement + SecRequirementCreateWithString(CFSTR("anchor apple"), kSecCSDefaultFlags, &isApple); + + //init dev id signing requirement + SecRequirementCreateWithString(CFSTR("anchor apple generic"), kSecCSDefaultFlags, &isDevID); + + //init app store signing requirement + SecRequirementCreateWithString(CFSTR("anchor apple generic and certificate leaf [subject.CN] = \"Apple Mac OS Application Signing\""), kSecCSDefaultFlags, &isAppStore); + }); + + //check 1: "is apple" (proper) + if(errSecSuccess == validateRequirement(code, isApple, flags, isDynamic)) + { + //set signer to apple + signer = [NSNumber numberWithInt:Apple]; + } + + //check 2: "is app store" + // note: this is more specific than dev id, so do it first + else if(errSecSuccess == validateRequirement(code, isAppStore, flags, isDynamic)) + { + //set signer to app store + signer = [NSNumber numberWithInt:AppStore]; + } + + //check 3: "is dev id" + else if(errSecSuccess == validateRequirement(code, isDevID, flags, isDynamic)) + { + //set signer to dev id + signer = [NSNumber numberWithInt:DevID]; + } + + //otherwise + // has to be adhoc? + else + { + //set signer to ad hoc + signer = [NSNumber numberWithInt:AdHoc]; + } + + return signer; +} + +//validate a requirement +OSStatus validateRequirement(SecStaticCodeRef code, SecRequirementRef requirement, SecCSFlags flags, BOOL isDynamic) +{ + //result + OSStatus result = -1; + + //dynamic check? + if(YES == isDynamic) + { + //validate dynamically + result = SecCodeCheckValidity((SecCodeRef)code, flags, requirement); + } + //static check + else + { + //validate statically + result = SecStaticCodeCheckValidity(code, flags, requirement); + } + + return result; +} + +//extract (names) of signing auths +NSMutableArray* extractSigningAuths(NSDictionary* signingDetails) +{ + //signing auths + NSMutableArray* authorities = nil; + + //cert chain + NSArray* certificateChain = nil; + + //index + NSUInteger index = 0; + + //cert + SecCertificateRef certificate = NULL; + + //common name on chert + CFStringRef commonName = NULL; + + //init array for certificate names + authorities = [NSMutableArray array]; + + //get cert chain + certificateChain = [signingDetails objectForKey:(__bridge NSString*)kSecCodeInfoCertificates]; + if(0 == certificateChain.count) + { + //no certs + goto bail; + } + + //extract/save name of all certs + for(index = 0; index < certificateChain.count; index++) + { + //reset + commonName = NULL; + + //extract cert + certificate = (__bridge SecCertificateRef)([certificateChain objectAtIndex:index]); + + //get common name + if( (errSecSuccess == SecCertificateCopyCommonName(certificate, &commonName)) && + (NULL != commonName) ) + { + //save + [authorities addObject:(__bridge id _Nonnull)(commonName)]; + + //release + CFRelease(commonName); + } + } + +bail: + + return authorities; +} diff --git a/Utilities.h b/Utilities.h index 1f22c62..f03b64b 100755 --- a/Utilities.h +++ b/Utilities.h @@ -9,16 +9,23 @@ #ifndef KnockKnock_Utilities_h #define KnockKnock_Utilities_h -/* METHODS */ - -//check if OS is supported -BOOL isSupportedOS(void); +/* FUNCTIONS */ //get OS's major or minor version SInt32 getVersion(OSType selector); -//get the signing info of a file -NSDictionary* extractSigningInfo(NSString* path); +//disable std err +void disableSTDERR(void); + +//get name of logged in user +NSString* getConsoleUser(void); + +//get all users +NSMutableDictionary* allUsers(void); + +//give a list of paths +// convert any `~` to all or current user +NSMutableArray* expandPaths(const __strong NSString* const paths[], int count); //if string is too long to fit into a the text field // ->truncate and insert ellipises before /file @@ -46,9 +53,6 @@ NSString* getAppVersion(void); //convert a textview to a clickable hyperlink void makeTextViewHyperlink(NSTextField* textField, NSURL* url); -//determine if a file is signed by Apple proper -BOOL isApple(NSString* path); - //set the color of an attributed string NSMutableAttributedString* setStringColor(NSAttributedString* string, NSColor* color); @@ -81,10 +85,14 @@ NSString* which(NSString* processName); NSMutableArray* runningProcesses(void); //check if a file is an executable -BOOL isURLExecutable(NSURL* file); +BOOL isExecutable(NSString* file); //lookup object in dictionary // note: key can be case-insensitive id extractFromDictionary(NSDictionary* dictionary, NSString* sensitiveKey); +//check if (full) dark mode +// meaning, Mojave+ and dark mode enabled +BOOL isDarkMode(void); + #endif diff --git a/Utilities.m b/Utilities.m index b4d2263..4307778 100755 --- a/Utilities.m +++ b/Utilities.m @@ -14,64 +14,10 @@ #import #import #import +#import +#import #import -/* GLOBALS */ - -//flag for yosemite+ -// ->needed for code-signing flags -BOOL isYosemitePlus = NO; - - -//check if OS is supported -// ->also set's Yosemite+ flag (for signing check flags) -BOOL isSupportedOS() -{ - //return - BOOL isSupported = NO; - - //major version - SInt32 versionMajor = 0; - - //minor version - SInt32 versionMinor = 0; - - //get major version - versionMajor = getVersion(gestaltSystemVersionMajor); - - //get minor version - versionMinor = getVersion(gestaltSystemVersionMinor); - - //sanity check - if( (-1 == versionMajor) || - (-1 == versionMinor) ) - { - //err - goto bail; - } - - //check that OS is supported - // ->10.8+ ? - if( (versionMajor == OS_MAJOR_VERSION_X) && - (versionMinor >= OS_MINOR_VERSION_LION) ) - { - //set flag - isSupported = YES; - } - - //also set yosemite+ flag - if( (versionMajor == OS_MAJOR_VERSION_X) && - (versionMinor >= OS_MINOR_VERSION_YOSEMITE) ) - { - //set flag - isYosemitePlus = YES; - } - -//bail -bail: - - return isSupported; -} //get OS's major or minor version SInt32 getVersion(OSType selector) @@ -90,223 +36,158 @@ SInt32 getVersion(OSType selector) goto bail; } -//bail bail: return version; } -//get the signing info of a file -NSDictionary* extractSigningInfo(NSString* path) -{ - //info dictionary - NSMutableDictionary* signingStatus = nil; - - //code - SecStaticCodeRef staticCode = NULL; - - //status - OSStatus status = !STATUS_SUCCESS; - - //signing information - CFDictionaryRef signingInformation = NULL; - - //cert chain - NSArray* certificateChain = nil; - - //index - NSUInteger index = 0; - - //cert - SecCertificateRef certificate = NULL; - - //common name on chert - CFStringRef commonName = NULL; - - //flags - SecCSFlags csFlags = kSecCSDefaultFlags; - - //init signing status - signingStatus = [NSMutableDictionary dictionary]; - - //create static code - status = SecStaticCodeCreateWithPath((__bridge CFURLRef)([NSURL fileURLWithPath:path]), kSecCSDefaultFlags, &staticCode); - - //save signature status - signingStatus[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status]; - - //init flags - // ->yosemite+ can do 'stronger' checks - if(YES == isYosemitePlus) - { - //set - csFlags = kSecCSCheckNestedCode|kSecCSStrictValidate|kSecCSDoNotValidateResources; - } - - //check signature - status = SecStaticCodeCheckValidity(staticCode, csFlags, NULL); - - //(re)save signature status - signingStatus[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status]; - if(STATUS_SUCCESS != status) - { - //bail - goto bail; - } - //grab signing authorities - status = SecCodeCopySigningInformation(staticCode, kSecCSSigningInformation, &signingInformation); +//disable std err +void disableSTDERR() +{ + //file handle + int devNull = -1; - //(re)save signature status - signingStatus[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status]; - if(STATUS_SUCCESS != status) - { - //bail - goto bail; - } - - //determine if binary is signed by Apple - signingStatus[KEY_SIGNING_IS_APPLE] = [NSNumber numberWithBool:isApple(path)]; + //open /dev/null + devNull = open("/dev/null", O_RDWR); - //init array for certificate names - signingStatus[KEY_SIGNING_AUTHORITIES] = [NSMutableArray array]; + //dup + dup2(devNull, STDERR_FILENO); - //get cert chain - certificateChain = [(__bridge NSDictionary*)signingInformation objectForKey:(__bridge NSString*)kSecCodeInfoCertificates]; + //close + close(devNull); - //handle case there is no cert chain - // ->adhoc? (/Library/Frameworks/OpenVPN.framework/Versions/Current/bin/openvpn-service) - if(0 == certificateChain.count) - { - //set - [signingStatus[KEY_SIGNING_AUTHORITIES] addObject:@"signed, but no signing authorities (adhoc?)"]; - } - - //got cert chain - // ->add each to list - else - { - //get name of all certs - for(index = 0; index < certificateChain.count; index++) - { - //extract cert - certificate = (__bridge SecCertificateRef)([certificateChain objectAtIndex:index]); - - //get common name - status = SecCertificateCopyCommonName(certificate, &commonName); - - //skip ones that error out - if( (STATUS_SUCCESS != status) || - (NULL == commonName)) - { - //skip - continue; - } - - //save - [signingStatus[KEY_SIGNING_AUTHORITIES] addObject:(__bridge NSString*)commonName]; - - //release name - CFRelease(commonName); - } - } - -//bail -bail: - - //free signing info - if(NULL != signingInformation) - { - //free - CFRelease(signingInformation); - } - - //free static code - if(NULL != staticCode) - { - //free - CFRelease(staticCode); - } - - return signingStatus; + return; } -//determine if a file is signed by Apple proper -BOOL isApple(NSString* path) +//get name of logged in user +NSString* getConsoleUser() { - //flag - BOOL isApple = NO; + //copy/return user + return CFBridgingRelease(SCDynamicStoreCopyConsoleUser(NULL, NULL, NULL)); +} + +//get all user +// includes name/home directory +NSMutableDictionary* allUsers() +{ + //users + NSMutableDictionary* users = nil; - //code - SecStaticCodeRef staticCode = NULL; + //query + CSIdentityQueryRef query = nil; - //signing reqs - SecRequirementRef requirementRef = NULL; + //query results + CFArrayRef results = NULL; - //flags - SecCSFlags csFlags = kSecCSDefaultFlags; + //error + CFErrorRef error = NULL; - //status - OSStatus status = -1; + //identiry + CBIdentity* identity = NULL; - //create static code - status = SecStaticCodeCreateWithPath((__bridge CFURLRef)([NSURL fileURLWithPath:path]), kSecCSDefaultFlags, &staticCode); - if(STATUS_SUCCESS != status) + //alloc dictionary + users = [NSMutableDictionary dictionary]; + + //init query + query = CSIdentityQueryCreate(NULL, kCSIdentityClassUser, CSGetLocalIdentityAuthority()); + + //exec query + if(true != CSIdentityQueryExecute(query, 0, &error)) { //bail goto bail; } - //create req string w/ 'anchor apple' - // (3rd party: 'anchor apple generic') - status = SecRequirementCreateWithString(CFSTR("anchor apple"), kSecCSDefaultFlags, &requirementRef); - if( (STATUS_SUCCESS != status) || - (requirementRef == NULL) ) - { - //bail - goto bail; - } + //grab results + results = CSIdentityQueryCopyResults(query); - //init flags - // ->yosemite+ can do 'stronger' checks - if(YES == isYosemitePlus) + //process all results + // add user and home directory + for (int i = 0; i < CFArrayGetCount(results); ++i) { - //set - csFlags = kSecCSCheckNestedCode|kSecCSStrictValidate|kSecCSDoNotValidateResources; + //grab identity + identity = [CBIdentity identityWithCSIdentity:(CSIdentityRef)CFArrayGetValueAtIndex(results, i)]; + + //add user + users[identity.uniqueIdentifier] = @{USER_NAME:identity.posixName, USER_DIRECTORY:NSHomeDirectoryForUser(identity.posixName)}; } - //check if file is signed by apple - // ->i.e. it conforms to req string - status = SecStaticCodeCheckValidity(staticCode, csFlags, requirementRef); - if(STATUS_SUCCESS != status) - { - //bail - // ->just means app isn't signed by apple, or something didn't validate - goto bail; - } - - //ok, happy (SecStaticCodeCheckValidity() didn't fail) - // ->file is signed by Apple - isApple = YES; - -//bail bail: - //free req reference - if(NULL != requirementRef) + //release results + if(NULL != results) { - //free - CFRelease(requirementRef); - } - - //free static code - if(NULL != staticCode) - { - //free - CFRelease(staticCode); + //release + CFRelease(results); } - return isApple; + //release query + if(NULL != query) + { + //release + CFRelease(query); + } + + return users; +} + +//give a list of paths +// convert any `~` to all or current user +NSMutableArray* expandPaths(const __strong NSString* const paths[], int count) +{ + //expanded paths + NSMutableArray* expandedPaths = nil; + + //(current) path + const NSString* path = nil; + + //all users + NSMutableDictionary* users = nil; + + //grab all users + users = allUsers(); + + //alloc list + expandedPaths = [NSMutableArray array]; + + //iterate/expand + for(NSInteger i = 0; i < count; i++) + { + //grab path + path = paths[i]; + + //no `~`? + // just add and continue + if(YES != [path hasPrefix:@"~"]) + { + //add as is + [expandedPaths addObject:path]; + + //next + continue; + } + + //handle '~' case + // root? add each user + if(0 == geteuid()) + { + //add each user + for(NSString* user in users) + { + [expandedPaths addObject:[users[user][USER_DIRECTORY] stringByAppendingPathComponent:[path substringFromIndex:1]]]; + } + } + //otherwise + // just convert to current user + else + { + [expandedPaths addObject:[path stringByExpandingTildeInPath]]; + } + + } + + return expandedPaths; } //given a path to binary @@ -368,13 +249,13 @@ NSImage* getIconForBinary(NSString* binary, NSBundle* bundle) NSString* iconExtension = nil; //system's document icon - static NSData* documentIcon = nil; + static NSImage* documentIcon = nil; //icon NSImage* icon = nil; - //if not bundle was passed in - // ->try find one + //no bundle? + // try find one if(nil == bundle) { //load bundle @@ -382,7 +263,7 @@ NSImage* getIconForBinary(NSString* binary, NSBundle* bundle) } //for app's - // ->extract their icon + // extract their icon if(nil != bundle) { //get file @@ -414,27 +295,25 @@ NSImage* getIconForBinary(NSString* binary, NSBundle* bundle) //extract icon icon = [[NSWorkspace sharedWorkspace] iconForFile:binary]; - //load system document icon - // ->static var, so only load once if(nil == documentIcon) { //load - documentIcon = [[[NSWorkspace sharedWorkspace] iconForFileType: - NSFileTypeForHFSTypeCode(kGenericDocumentIcon)] TIFFRepresentation]; + documentIcon = [[NSWorkspace sharedWorkspace] iconForFileType: + NSFileTypeForHFSTypeCode(kGenericDocumentIcon)]; } //if 'iconForFile' method doesn't find and icon, it returns the system 'document' icon // ->the system 'application' icon seems more applicable, so use that here... - if(YES == [[icon TIFFRepresentation] isEqual:documentIcon]) + if(YES == [icon isEqual:documentIcon]) { //set icon to system 'applicaiton' icon icon = [[NSWorkspace sharedWorkspace] - iconForFileType: NSFileTypeForHFSTypeCode(kGenericApplicationIcon)]; + iconForFileType: NSFileTypeForHFSTypeCode(kGenericApplicationIcon)]; } //'iconForFileType' returns small icons - // ->so set size to 64 - [icon setSize:NSMakeSize(64, 64)]; + // so set size to 64 @2x + [icon setSize:NSMakeSize(128, 128)]; } return icon; @@ -1187,22 +1066,16 @@ bail: } //check if a file is an executable -BOOL isURLExecutable(NSURL* file) +BOOL isExecutable(NSString* file) { //return BOOL isExecutable = NO; - //bundle url - CFURLRef bundleURL = NULL; - //architecture ref CFArrayRef archArrayRef = NULL; - //create bundle - bundleURL = CFURLCreateFromFileSystemRepresentation(NULL, (uint8_t*)[[file path] UTF8String], strlen((const char *)(uint8_t*)[[file path] UTF8String]), true); - //get executable arch's - archArrayRef = CFBundleCopyExecutableArchitecturesForURL(bundleURL); + archArrayRef = CFBundleCopyExecutableArchitecturesForURL((__bridge CFURLRef)[NSURL fileURLWithPath:file]); //check arch for i386/x6_64 if(NULL != archArrayRef) @@ -1211,13 +1084,6 @@ BOOL isURLExecutable(NSURL* file) isExecutable = [(__bridge NSArray*)archArrayRef containsObject:[NSNumber numberWithInt:kCFBundleExecutableArchitectureX86_64]] || [(__bridge NSArray*)archArrayRef containsObject:[NSNumber numberWithInt:kCFBundleExecutableArchitectureI386]]; } - //free bundle url - if(NULL != bundleURL) - { - //free - CFRelease(bundleURL); - } - //free arch ref if(NULL != archArrayRef) { @@ -1250,3 +1116,33 @@ id extractFromDictionary(NSDictionary* dictionary, NSString* sensitiveKey) return object; } + +//check if (full) dark mode +// meaning, Mojave+ and dark mode enabled +BOOL isDarkMode() +{ + //flag + BOOL darkMode = NO; + + //not mojave? + // bail, since not true dark mode + if(YES != [[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){10, 14, 0}]) + { + //bail + goto bail; + } + + //not dark mode? + if(YES != [[[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"] isEqualToString:@"Dark"]) + { + //bail + goto bail; + } + + //ok, mojave dark mode it is! + darkMode = YES; + +bail: + + return darkMode; +} diff --git a/VTButton.m b/VTButton.m index 04cc395..d45fedb 100755 --- a/VTButton.m +++ b/VTButton.m @@ -101,11 +101,11 @@ color = [NSColor redColor]; } //non-flagged files - // ->just black + // color: default else { - //gray - color = [NSColor blackColor]; + //default + color = NSColor.controlTextColor; } //set string @@ -133,7 +133,7 @@ color = [NSColor colorWithCalibratedRed:(255/255.0f) green:(1.0/255.0f) blue:(1.0/255.0f) alpha:0.66]; } //non-flagged files - // ->just black + // ->color: gray else { //gray @@ -169,7 +169,7 @@ color = [NSColor colorWithCalibratedRed:(255/255.0f) green:(1.0/255.0f) blue:(1.0/255.0f) alpha:0.66]; } //non-flagged files - // ->just black + // color: gray else { //gray @@ -177,7 +177,7 @@ } } //mouse is up - // ->reset color to black/red + // ->reset color else { //flagged files @@ -189,11 +189,11 @@ color = [NSColor redColor]; } //non-flagged files - // ->just black + // color: default else { - //gray - color = [NSColor blackColor]; + //default + color = NSColor.controlTextColor; } } diff --git a/VTInfoWindowController.m b/VTInfoWindowController.m index ec884b1..e7e399e 100755 --- a/VTInfoWindowController.m +++ b/VTInfoWindowController.m @@ -51,11 +51,13 @@ //super [super windowDidLoad]; - //make it modal - //[[NSApplication sharedApplication] runModalForWindow:self.window]; - - //make white - [self.window setBackgroundColor: NSColor.whiteColor]; + //not in dark mode? + // make window white + if(YES != isDarkMode()) + { + //make white + self.window.backgroundColor = NSColor.whiteColor; + } //make close button selected [self.window makeFirstResponder:self.closeButton]; @@ -97,8 +99,8 @@ //file status (known/unknown) if(YES == isKnown) { - //default color to black - textColor = [NSColor blackColor]; + //reset + textColor = NSColor.controlTextColor; //set color to red if its flagged if(0 != [self.fileObj.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) @@ -227,7 +229,7 @@ //pre-req [self.overlayView setWantsLayer:YES]; - //set overlay's view color to black + //set overlay's view color to white self.overlayView.layer.backgroundColor = [NSColor whiteColor].CGColor; //make it semi-transparent diff --git a/WhiteList/whitelistedFiles.json b/WhiteList/whitelistedFiles.json index 4b727a4..b6556e5 100644 --- a/WhiteList/whitelistedFiles.json +++ b/WhiteList/whitelistedFiles.json @@ -1,37 +1,4 @@ { - "/Library/Extensions/HighPointIOP.kext/Contents/MacOS/HighPointIOP":["e387f72b2e74fec83e7ea9f0f038d64f", "4d6367d14fa1b6f7c6faf4e48a3293d0"], - "/Library/Extensions/PromiseSTEX.kext/Contents/MacOS/PromiseSTEX":["ba899baf6a8f0cabf81e0b0009fac3c9", "66307f53513760997ace335fef99b95d", "db6dd9dc9421315f4c25b46f8170bcc3"], - "/Library/Extensions/ATTOCelerityFC8.kext/Contents/MacOS/ATTOCelerityFC8":["549e3f145f87b9dff09a068671dd74ce", "b1ec21821b5b6b4ff0735dd01a6ee840"], - "/Library/Extensions/HighPointRR.kext/Contents/MacOS/HighPointRR":["c61804dcfa5447b8bdde8aec93d0fb06", "e36e03df0b95244bbefcf2f074f88411"], - "/Library/Extensions/CalDigitHDProDrv.kext/Contents/MacOS/CalDigitHDProDrv":["9cf6fccbb11ba69de1fa7fe5184660a0"], - "/Library/Extensions/SoftRAID.kext/Contents/MacOS/SoftRAID":["5b031ffe78c8ad0962ad7c74032ded32", "defa280116342c6e9373dc41ba4f6535", "51e477fb53ebeefeeb7f7ce3a5bd8929", "42496f9bbe8a83a796eea5e63e72e8cd"], - "/Library/Extensions/ATTOExpressSASHBA2.kext/Contents/MacOS/ATTOExpressSASHBA2":["65a3c352c56514cdb96468f50474430f", "53e1008f80b148241effa105f3d12199"], - "/Library/Extensions/ArcMSR.kext/Contents/MacOS/ArcMSR":["416efde75a393f9505673ba99f24e873"], - "/Library/Extensions/ATTOExpressSASRAID2.kext/Contents/MacOS/ATTOExpressSASRAID2":["4f43c6550ad647d2416d48c58e41553e", "54e5e3af5485802ce0b98ff2d69086cb"], - "/Library/Extensions/ACS6x.kext/Contents/MacOS/ACS6x":["276f55fccf8a7783b06dbde3b1bd36f5"], - "/System/Library/Extensions/Accusys6xxxx.kext/Contents/MacOS/Accusys6xxxx":["b02c769f3b6635e307b551462a48107d"], - "/System/Library/Extensions/AppleBacklightExpert.kext/AppleBacklightExpert":["6501354452b48465dea904e27fe8c5f9", "8854000a050f4bbadd36db2b8118ffa1", "678c0efdde7c4a90e5e5b76855c9ee85"], - "/System/Library/Extensions/ArcMSR.kext/Contents/MacOS/ArcMSR":["c57237576ff817b13246296ec786cce2"], - "/System/Library/Extensions/ATTOCelerityFC.kext/Contents/MacOS/ATTOCelerityFC":["c061f1786d1e9b07846c1bfa1a906190"], - "/System/Library/Extensions/ATTOCelerityFC8.kext/Contents/MacOS/ATTOCelerityFC8":["bbf4c3a42934c36d8ae804c7c7386547"], - "/System/Library/Extensions/ATTOExpressPCI4.kext/Contents/MacOS/ATTOExpressPCI4":["433e4f9488d90020e480c56893c8a80f"], - "/System/Library/Extensions/ATTOExpressSASHBA.kext/Contents/MacOS/ATTOExpressSASHBA":["4d5ef99ed9d52e8a0550b8a91c6a9aca"], - "/System/Library/Extensions/ATTOExpressSASHBA2.kext/Contents/MacOS/ATTOExpressSASHBA2":["e440639d53144ccb09d9796e675169f9"], - "/System/Library/Extensions/ATTOExpressSASHBA3.kext/Contents/MacOS/ATTOExpressSASHBA3":["864aed4dd2f45fbc39a95fec1efb47db"], - "/System/Library/Extensions/ATTOExpressSASRAID.kext/Contents/MacOS/ATTOExpressSASRAID":["e68c0f60ca8e10bbd241ce1335a3dca8"], - "/System/Library/Extensions/ATTOExpressSASRAID2.kext/Contents/MacOS/ATTOExpressSASRAID2":["ed974b9a9e41b01d648f64bec5959b46"], - "/System/Library/Extensions/AudioAUUC.kext/AudioAUUC":["6e02093ece9dc275a30ecd3417b1b016", "a785d5ccef1991009af1d4d25a0373c2", "020e254d626ea0eb8432880314bdffda"], - "/System/Library/Extensions/CalDigitHDProDrv.kext/Contents/MacOS/CalDigitHDProDrv":["a53366611eed8c2cb2cf12b79f893ee3"], - "/System/Library/Extensions/CoreStorage.kext/Contents/MacOS/CoreStorage":["7ffecdf00647495170b05bb64bc0ca26"], - "/System/Library/Extensions/IOGraphicsFamily.kext/IOGraphicsFamily":["e037d3730c01962ebcd4bba09d69679c", "6c16ebd149ac177978393ee98ff84c2d", "68941445971cf95576ff28831c423d04"], - "/System/Library/Extensions/IONDRVSupport.kext/IONDRVSupport":["013c73074d7993cba3e230b5511cd901", "f470a6cacf052cbe469dc400973511d2", "6688610c05329bfc4909b4bc21230d6d"], - "/System/Library/Extensions/IOPCIFamily.kext/IOPCIFamily":["6a9d57017a18fc470d323547d1ab86e4", "562be2c6d9d55b3ad2d8ee00e9da4ca6", "a899be1ccbe7cdc6fb54f92f7aa09dc3"], - "/System/Library/Extensions/HighPointIOP.kext/Contents/MacOS/HighPointIOP":["64e3e122c1f0d24fd0b225c18f44440d"], - "/System/Library/Extensions/HighPointRR.kext/Contents/MacOS/HighPointRR":["316bb5757bbe7ef70044f85717ff97df"], - "/System/Library/Extensions/JMicronATA.kext/Contents/MacOS/JMicronATA":["9c8207d44446861a5b40e2ac4c6f5dba"], - "/System/Library/Extensions/PromiseSTEX.kext/Contents/MacOS/PromiseSTEX":["1cd38bebacdc6ba85a86a8b65474b8b5"], - "/System/Library/Extensions/SoftRAID.kext/Contents/MacOS/SoftRAID":["18bfb361ae05cff7f2a2310d685a44fc"], - "/System/Library/Extensions/webdav_fs.kext/Contents/MacOS/webdav_fs":["6b9068594520afce60dcf99262d1af12"], "/usr/libexec/configureLocalKDC":["19f6e328d0286ad7f81ee8224e369726"], "/usr/libexec/locate.updatedb":["e8cc729ae05233c414eb0c672d836fc1"], "/usr/libexec/ntpd-wrapper":["baf0968c6df24734e079baeb01851221"], diff --git a/WhiteList/whitelistedKexts.json b/WhiteList/whitelistedKexts.json new file mode 100644 index 0000000..aea0632 --- /dev/null +++ b/WhiteList/whitelistedKexts.json @@ -0,0 +1,35 @@ +{ + "/Library/Extensions/HighPointIOP.kext/Contents/MacOS/HighPointIOP":"Developer ID Application: HighPoint Technologies, Inc (DX6G69M9N2)", + "/Library/Extensions/PromiseSTEX.kext/Contents/MacOS/PromiseSTEX":"Developer ID Application: Promise Technology Mobile Apps (268CCUR4WN)", + "/Library/Extensions/ATTOCelerityFC8.kext/Contents/MacOS/ATTOCelerityFC8":"Developer ID Application: ATTO Technology, Inc. (FC94733TZD)", + "/Library/Extensions/HighPointRR.kext/Contents/MacOS/HighPointRR":"Developer ID Application: HighPoint Technologies, Inc (DX6G69M9N2)", + "/Library/Extensions/CalDigitHDProDrv.kext/Contents/MacOS/CalDigitHDProDrv":"Developer ID Application: CalDigit, Inc (8R7PS6VYW7)", + "/Library/Extensions/SoftRAID.kext/Contents/MacOS/SoftRAID":"Developer ID Application: SoftRAID LLC (NDGSU3WA4Y)", + "/Library/Extensions/ATTOExpressSASHBA2.kext/Contents/MacOS/ATTOExpressSASHBA2":"Developer ID Application: ATTO Technology, Inc. (FC94733TZD)", + "/Library/Extensions/ArcMSR.kext/Contents/MacOS/ArcMSR":"Developer ID Application: Areca Technology Corporation (34JN824YNC)", + "/Library/Extensions/ATTOExpressSASRAID2.kext/Contents/MacOS/ATTOExpressSASRAID2":"Developer ID Application: ATTO Technology, Inc. (FC94733TZD)", + "/Library/Extensions/ACS6x.kext/Contents/MacOS/ACS6x":"Developer ID Application: Accusys,Inc (K3TDMD9Y6B)", + "/System/Library/Extensions/Accusys6xxxx.kext/Contents/MacOS/Accusys6xxxx":["b02c769f3b6635e307b551462a48107d"], + "/System/Library/Extensions/AppleBacklightExpert.kext/AppleBacklightExpert":["6501354452b48465dea904e27fe8c5f9", "8854000a050f4bbadd36db2b8118ffa1", "678c0efdde7c4a90e5e5b76855c9ee85"], + "/System/Library/Extensions/ArcMSR.kext/Contents/MacOS/ArcMSR":["c57237576ff817b13246296ec786cce2"], + "/System/Library/Extensions/ATTOCelerityFC.kext/Contents/MacOS/ATTOCelerityFC":["c061f1786d1e9b07846c1bfa1a906190"], + "/System/Library/Extensions/ATTOCelerityFC8.kext/Contents/MacOS/ATTOCelerityFC8":["bbf4c3a42934c36d8ae804c7c7386547"], + "/System/Library/Extensions/ATTOExpressPCI4.kext/Contents/MacOS/ATTOExpressPCI4":["433e4f9488d90020e480c56893c8a80f"], + "/System/Library/Extensions/ATTOExpressSASHBA.kext/Contents/MacOS/ATTOExpressSASHBA":["4d5ef99ed9d52e8a0550b8a91c6a9aca"], + "/System/Library/Extensions/ATTOExpressSASHBA2.kext/Contents/MacOS/ATTOExpressSASHBA2":["e440639d53144ccb09d9796e675169f9"], + "/System/Library/Extensions/ATTOExpressSASHBA3.kext/Contents/MacOS/ATTOExpressSASHBA3":["864aed4dd2f45fbc39a95fec1efb47db"], + "/System/Library/Extensions/ATTOExpressSASRAID.kext/Contents/MacOS/ATTOExpressSASRAID":["e68c0f60ca8e10bbd241ce1335a3dca8"], + "/System/Library/Extensions/ATTOExpressSASRAID2.kext/Contents/MacOS/ATTOExpressSASRAID2":["ed974b9a9e41b01d648f64bec5959b46"], + "/System/Library/Extensions/AudioAUUC.kext/AudioAUUC":["6e02093ece9dc275a30ecd3417b1b016", "a785d5ccef1991009af1d4d25a0373c2", "020e254d626ea0eb8432880314bdffda"], + "/System/Library/Extensions/CalDigitHDProDrv.kext/Contents/MacOS/CalDigitHDProDrv":["a53366611eed8c2cb2cf12b79f893ee3"], + "/System/Library/Extensions/CoreStorage.kext/Contents/MacOS/CoreStorage":["7ffecdf00647495170b05bb64bc0ca26"], + "/System/Library/Extensions/IOGraphicsFamily.kext/IOGraphicsFamily":["e037d3730c01962ebcd4bba09d69679c", "6c16ebd149ac177978393ee98ff84c2d", "68941445971cf95576ff28831c423d04"], + "/System/Library/Extensions/IONDRVSupport.kext/IONDRVSupport":["013c73074d7993cba3e230b5511cd901", "f470a6cacf052cbe469dc400973511d2", "6688610c05329bfc4909b4bc21230d6d"], + "/System/Library/Extensions/IOPCIFamily.kext/IOPCIFamily":["6a9d57017a18fc470d323547d1ab86e4", "562be2c6d9d55b3ad2d8ee00e9da4ca6", "a899be1ccbe7cdc6fb54f92f7aa09dc3"], + "/System/Library/Extensions/HighPointIOP.kext/Contents/MacOS/HighPointIOP":["64e3e122c1f0d24fd0b225c18f44440d"], + "/System/Library/Extensions/HighPointRR.kext/Contents/MacOS/HighPointRR":["316bb5757bbe7ef70044f85717ff97df"], + "/System/Library/Extensions/JMicronATA.kext/Contents/MacOS/JMicronATA":["9c8207d44446861a5b40e2ac4c6f5dba"], + "/System/Library/Extensions/PromiseSTEX.kext/Contents/MacOS/PromiseSTEX":["1cd38bebacdc6ba85a86a8b65474b8b5"], + "/System/Library/Extensions/SoftRAID.kext/Contents/MacOS/SoftRAID":["18bfb361ae05cff7f2a2310d685a44fc"], + "/System/Library/Extensions/webdav_fs.kext/Contents/MacOS/webdav_fs":["6b9068594520afce60dcf99262d1af12"] +} diff --git a/en.lproj/MainMenu.xib b/en.lproj/MainMenu.xib index 36276a6..6afcca4 100755 --- a/en.lproj/MainMenu.xib +++ b/en.lproj/MainMenu.xib @@ -1,8 +1,8 @@ - + - + @@ -43,10 +43,10 @@ - + - + @@ -79,7 +79,7 @@ - + @@ -152,11 +152,11 @@ - + + + + + + - + diff --git a/images/eventRulesIcon.png b/images/eventRulesIcon.png new file mode 100644 index 0000000..3385071 Binary files /dev/null and b/images/eventRulesIcon.png differ diff --git a/kkRowCell.h b/kkRowCell.h deleted file mode 100755 index cd65c5f..0000000 --- a/kkRowCell.h +++ /dev/null @@ -1,13 +0,0 @@ -// -// kkRowCell.h -// KnockKnock -// -// Created by Patrick Wardle on 4/6/15. -// Copyright (c) 2015 Objective-See. All rights reserved. -// - -#import - -@interface kkRowCell : NSTableCellView - -@end diff --git a/kkRowCell.m b/kkRowCell.m deleted file mode 100755 index ab26bfd..0000000 --- a/kkRowCell.m +++ /dev/null @@ -1,23 +0,0 @@ -// -// kkRowCell.m -// KnockKnock -// -// Created by Patrick Wardle on 4/6/15. -// Copyright (c) 2015 Objective-See. All rights reserved. -// - -#import "kkRowCell.h" - -@implementation kkRowCell - -- (void)drawRect:(NSRect)dirtyRect { - [super drawRect:dirtyRect]; - - // Drawing code here. -} - -- (void)setBackgroundStyle:(NSBackgroundStyle)backgroundStyle { - [super setBackgroundStyle: NSBackgroundStyleLight]; -} - -@end diff --git a/main.h b/main.h new file mode 100644 index 0000000..611de23 --- /dev/null +++ b/main.h @@ -0,0 +1,37 @@ +// +// main.h +// KnockKnock +// +// Created by Patrick Wardle on 11/12/18. +// Copyright © 2018 Objective-See. All rights reserved. +// + +#ifndef main_h +#define main_h + +#import "Consts.h" +#import "Filter.h" +#import "Utilities.h" +#import "VirusTotal.h" +#import "AppDelegate.h" +#import "ItemEnumerator.h" + +#import + +/* GLOBALS */ + +//filter object +Filter* itemFilter; + +//shared item enumerator object +ItemEnumerator* sharedItemEnumerator = nil; + +/* FUNCTIONS */ + +//perform a cmdline scan +BOOL cmdlineScan(void); + +//pretty print JSON +void prettyPrintJSON(NSString* output); + +#endif /* main_h */ diff --git a/main.m b/main.m index 6677362..3b4a340 100755 --- a/main.m +++ b/main.m @@ -6,33 +6,252 @@ // Copyright (c) 2015 Objective-See. All rights reserved. // -#import +@import Sentry; +#import "main.h" int main(int argc, char *argv[]) { - //return var - int retVar = -1; + //status + int status = -1; @autoreleasepool { + //disable stderr + // sentry dumps to this, and we want only JSON to output... + disableSTDERR(); + + //sentry client + SentryClient *client = nil; + + //init sentry client + client = [[SentryClient alloc] initWithDsn:@"https://ba5d094e87014a529b25d90bae010b1c@sentry.io/1321683" didFailWithError:nil]; + + //set shared client + SentryClient.sharedClient = client; + + //start crash handler + [SentryClient.sharedClient startCrashHandlerWithError:nil]; + //handle '-scan' - // ->cmdline scan without UI - if( (argc >= 2) && - (YES == [[NSString stringWithUTF8String:argv[1]] isEqualToString:@"-whothere"]) ) + // cmdline scan without UI + if(YES == [[[NSProcessInfo processInfo] arguments] containsObject:@"-whosthere"]) { + //scan + if(YES != cmdlineScan()) + { + //err msg + printf("\nKNOCKKNOCK ERROR: cmdline scan failed\n\n"); + + //bail + goto bail; + } + //happy + status = 0; + + //done + goto bail; } //otherwise - // ->just kick off app, as we're root now + // just kick off app for UI instance else { //app away - retVar = NSApplicationMain(argc, (const char **)argv); + status = NSApplicationMain(argc, (const char **)argv); } }//pool - - return retVar; +bail: + + return status; +} + +//perform a cmdline scan +BOOL cmdlineScan() +{ + //virus total obj + VirusTotal* virusTotal = nil; + + //virus total thread + NSThread* virusTotalThread = nil; + + //flag + BOOL scanned = NO; + + //flag + BOOL includeApple = NO; + + //flag + BOOL skipVirusTotal = NO; + + //flag + BOOL prettyPrint = NO; + + //output + NSMutableString* output = nil; + + //plugin object + PluginBase* plugin = nil; + + //init filter object + itemFilter = [[Filter alloc] init]; + + //init virus total object + virusTotal = [[VirusTotal alloc] init]; + + //alloc shared item enumerator + sharedItemEnumerator = [[ItemEnumerator alloc] init]; + + //start shared enumerator + [sharedItemEnumerator start]; + + //set flag + // include apple items? + includeApple = [[[NSProcessInfo processInfo] arguments] containsObject:@"-apple"]; + + //set flag + // skip virus total? + skipVirusTotal = [[[NSProcessInfo processInfo] arguments] containsObject:@"-skipVT"]; + + //set flag + // pretty print json? + prettyPrint = [[[NSProcessInfo processInfo] arguments] containsObject:@"-pretty"]; + + //init output string + output = [NSMutableString string]; + + //start JSON + [output appendString:@"{"]; + + //iterate over all supported plugins + // invoke scan method on each plugin... + for(NSUInteger i=0; i < sizeof(SUPPORTED_PLUGINS)/sizeof(SUPPORTED_PLUGINS[0]); i++) + { + //init plugin + plugin = [(PluginBase*)([NSClassFromString(SUPPORTED_PLUGINS[i]) alloc]) init]; + if(nil == plugin) + { + //skip + continue; + } + + //no callback needed + plugin.callback = nil; + + //scan + [plugin scan]; + + //query VT + // unless user explicity says otherwise + if(YES != skipVirusTotal) + { + //alloc thread + // will query virus total to get info about all detected items + virusTotalThread = [[NSThread alloc] initWithTarget:virusTotal selector:@selector(getInfo:) object:plugin]; + + //start thread + [virusTotalThread start]; + + //wait until thread is done + while(YES != virusTotalThread.isFinished) + { + //nap + [NSThread sleepForTimeInterval:1.0]; + } + } + + //append plugin name to output + [output appendString:[NSString stringWithFormat:@"\"%@\":[", plugin.name]]; + + //iterate over all plugin items + // convert to JSON/append to output + for(ItemBase* item in plugin.allItems) + { + //skip apple / trusted items? + if( (YES != includeApple) && + (YES == item.isTrusted) ) + { + //skip + continue; + } + + //add item + [output appendFormat:@"{%@},", [item toJSON]]; + } + + //remove last ',' + if(YES == [output hasSuffix:@","]) + { + //remove + [output deleteCharactersInRange:NSMakeRange([output length]-1, 1)]; + } + + //terminate list + [output appendString:@"],"]; + + }//all plugins + + //remove last ',' + if(YES == [output hasSuffix:@","]) + { + //remove + [output deleteCharactersInRange:NSMakeRange([output length]-1, 1)]; + } + + //terminate list + [output appendString:@"}"]; + + //pretty print? + if(YES == prettyPrint) + { + //make me pretty! + prettyPrintJSON(output); + } + else + { + //output + printf("%s\n", output.UTF8String); + } + + //happy + scanned = YES; + +bail: + + return scanned; +} + +//pretty print JSON +void prettyPrintJSON(NSString* output) +{ + //data + NSData* data = nil; + + //object + id object = nil; + + //pretty data + NSData* prettyData = nil; + + //pretty string + NSString* prettyString = nil; + + //covert to data + data = [output dataUsingEncoding:NSUTF8StringEncoding]; + + //serialize + object = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; + + //covert to pretty data + prettyData = [NSJSONSerialization dataWithJSONObject:object options:NSJSONWritingPrettyPrinted error:nil]; + + //covert to pretty string + prettyString = [NSString stringWithUTF8String:prettyData.bytes]; + + //output + printf("%s\n", prettyString.UTF8String); + + return; }