From b41a9642a961c09d4cbfdfd76ac6df576b20a864 Mon Sep 17 00:00:00 2001 From: Patrick Wardle Date: Sat, 1 Aug 2015 12:46:24 -0700 Subject: [PATCH] v 1.0.0 RC improved filtering (network connections etc) improved UI (flagged files red in info window, centering components, etc) code cleanup (moved init XPC connection into AppDelegate, etc) error msgs now syslog'd extra sanity checks, etc fixed logic issue w/ refreshing & filtering --- 3rdParty/OrderedDictionary.h | 1 - 3rdParty/OrderedDictionary.m | 46 +-- AppDelegate.h | 11 + AppDelegate.m | 341 +++++++++--------- Filter.m | 61 ++-- InfoWindowController.m | 44 +-- Items/Connection.m | 1 - Items/File.m | 7 +- RequestRootWindowController.m | 15 +- Task.h | 3 - Task.m | 41 +-- TaskEnumerator.h | 11 - TaskEnumerator.m | 71 ++-- .../xcdebugger/Breakpoints_v2.xcbkptlist | 174 ++++----- TaskTableController.m | 26 +- UI/FileInfoWindow.xib | 4 +- UI/FlatView.xib | 13 +- UI/NetworkInfoWindow.xib | 7 +- UI/TaskInfoWindow.xib | 4 +- UI/TreeView.xib | 13 +- Utilities.m | 24 +- VirusTotal.m | 26 +- en.lproj/MainMenu.xib | 80 ++-- kkRowCell.m | 5 - main.m | 3 +- remoteTaskService/remoteTaskService.m | 21 +- 26 files changed, 478 insertions(+), 575 deletions(-) diff --git a/3rdParty/OrderedDictionary.h b/3rdParty/OrderedDictionary.h index 74de921..45fd2d4 100644 --- a/3rdParty/OrderedDictionary.h +++ b/3rdParty/OrderedDictionary.h @@ -36,7 +36,6 @@ - (void)insertObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)anIndex; - (id)keyAtIndex:(NSUInteger)anIndex; - (NSUInteger)indexOfKey:(id)aKey; --(void)reverse; //sort // ->by pid, name, etc diff --git a/3rdParty/OrderedDictionary.m b/3rdParty/OrderedDictionary.m index ef84e39..177c83e 100644 --- a/3rdParty/OrderedDictionary.m +++ b/3rdParty/OrderedDictionary.m @@ -101,12 +101,7 @@ NSString *DescriptionForObject(NSObject *object, id locale, NSUInteger indent) return [array objectEnumerator]; } -/* -- (NSEnumerator *)reverseKeyEnumerator -{ - return [array reverseObjectEnumerator]; -} -*/ + -(void)insertObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)anIndex { @@ -139,19 +134,13 @@ bail: return item; } -//TODO: add sanity checks -//given an key +//given a key // ->return its index -(NSUInteger)indexOfKey:(id)aKey { return [array indexOfObject:aKey]; } --(void)reverse -{ - array = [[[array reverseObjectEnumerator] allObjects] mutableCopy]; -} - //sort // ->by pid, name, etc -(void)sort:(NSUInteger)sortBy @@ -159,13 +148,14 @@ bail: //task sorted by name NSArray* sortedTasks = nil; - // + //sort by pid if(SORT_BY_PID == sortBy) { [array sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { return [obj1 compare:obj2]; }]; } + //sort by name else if(SORT_BY_NAME == sortBy) { //get array of tasks, sorted by binary name @@ -174,37 +164,9 @@ bail: //extract sorted pids into array array = [[sortedTasks valueForKey:@"pid"] mutableCopy]; - } - return; } - - -/* -- (NSString *)descriptionWithLocale:(id)locale indent:(NSUInteger)level -{ - NSMutableString *indentString = [NSMutableString string]; - NSUInteger i, count = level; - for (i = 0; i < count; i++) - { - [indentString appendFormat:@" "]; - } - - NSMutableString *description = [NSMutableString string]; - [description appendFormat:@"%@{\n", indentString]; - for (NSObject *key in self) - { - [description appendFormat:@"%@ %@ = %@;\n", - indentString, - DescriptionForObject(key, locale, level), - DescriptionForObject([self objectForKey:key], locale, level)]; - } - [description appendFormat:@"%@}\n", indentString]; - return description; -} -*/ - @end diff --git a/AppDelegate.h b/AppDelegate.h index 375771b..05f060d 100755 --- a/AppDelegate.h +++ b/AppDelegate.h @@ -138,6 +138,9 @@ //top constraint @property(nonatomic, retain)NSLayoutConstraint* trailingConstraint; +//remote XPC interface +@property (nonatomic, retain) NSXPCConnection* xpcConnection; + //action for 'refresh' button // ->query OS to refresh/reload all tasks - (IBAction)refreshTasks:(id)sender; @@ -145,6 +148,14 @@ /* METHODS */ + +//complete a few inits +// ->then invoke helper method to start enum'ing task (in bg thread) +-(void)go; + +//init (setup) XPC connection +-(BOOL)initXPC; + - (IBAction)switchView:(id)sender; //init tracking areas for buttons diff --git a/AppDelegate.m b/AppDelegate.m index c9a2173..174f500 100755 --- a/AppDelegate.m +++ b/AppDelegate.m @@ -9,18 +9,19 @@ #import "Exception.h" #import "Utilities.h" #import "AppDelegate.h" +#import "serviceInterface.h" #import "TaskTableController.h" #import "RequestRootWindowController.h" #import "Task.h" -//TODO: add IPV6 :: as 0.0.0.0? (since we do this for IPV4 i think) -//TODO: filter out dup'd networks (airportd 0:0..) -not sure want to do this -//TODO: first time (w/ auth) dylibs don't show up? -//TODO: remove 'pref' from menu - or disable? +//TODO: path truncated in 'info' window (1password mini) - but weird when selected :/ +// resize text manually? http://stackoverflow.com/questions/6519995/modifying-an-nstextfields-font-size-according-to-content-length +//TODO: filter out dup'd networks (airportd 0:0..) -not sure want to do this //TODO: 'flagged' items button? +//TODO: add 'am i on main thread' guard and test @implementation AppDelegate @@ -46,6 +47,7 @@ @synthesize taskEnumerator; @synthesize viewSelector; @synthesize searchButton; +@synthesize xpcConnection; //@synthesize taskScrollView; @@ -71,23 +73,11 @@ // ->main entry point -(void)applicationDidFinishLaunching:(NSNotification *)notification { - //TODO: remove - [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints"]; - - //user defaults - //NSUserDefaults* defaults = nil; - - //flag for first time - //BOOL isFirstRun = NO; - //first thing... // ->install exception handlers! //TODO: re-enable //installExceptionHandlers(); - //self.taskScrollView.wantsLayer = TRUE; - //self.taskScrollView.layer.cornerRadius = 20; - //init virus total object virusTotalObj = [[VirusTotal alloc] init]; @@ -109,39 +99,17 @@ if(YES != [self isAuthenticated]) { //display auth popup - // ->will kick off task emun on successful auth :) + // ->will invoke 'go' method on successful auth [self askForRoot]; } + //go! + // ->setup tracking areas and begin thread that explores tasks else { - //init mouse-over areas - [self initTrackingAreas]; - //go! - [self exploreTasks]; + [self go]; } - - - /* - //load defaults - defaults = [NSUserDefaults standardUserDefaults]; - - //extact first run key - // ->nil means first time! - if(nil == [defaults objectForKey:PREF_FIRST_RUN]) - { - //set flag - isFirstRun = YES; - - //set flag persistently - [defaults setBool:NO forKey:PREF_FIRST_RUN]; - - //flush/save - [defaults synchronize]; - } - */ - //set default top pane view to flat self.taskViewFormat = FLAT_VIEW; @@ -177,10 +145,95 @@ return; } +//complete a few inits +// ->then invoke helper method to start enum'ing task (in bg thread) +-(void)go +{ + //init XPC + if(YES != [self initXPC]) + { + + //bail + goto bail; + } + + //init mouse-over areas + [self initTrackingAreas]; + + //go! + [self exploreTasks]; + +//bail +bail: + + return; +} + + +//init (setup) XPC connection +-(BOOL)initXPC +{ + //status + BOOL initialized = NO; + + //alloc XPC connection + xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.remoteTaskService"]; + + //sanity check + if(nil == self.xpcConnection) + { + //err msg + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: failed to find/initialize XPC service"); + + //bail + goto bail; + } + + //set remote object interface + self.xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(remoteTaskProto)]; + + //set classes + // ->arrays & strings are what is ok to vend + [self.xpcConnection.remoteObjectInterface + setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], nil] + forSelector: @selector(enumerateDylibs:withReply:) + argumentIndex: 0 // the first parameter + ofReply: YES // in the method itself. + ]; + + //set classes + // ->arrays & strings are what is ok to vend + [self.xpcConnection.remoteObjectInterface + setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], nil] + forSelector: @selector(enumerateFiles:withReply:) + argumentIndex: 0 // the first parameter + ofReply: YES // in the method itself. + ]; + + //set classes + // ->arrays & strings are what is ok to vend + [self.xpcConnection.remoteObjectInterface + setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], [NSNumber class], nil] + forSelector: @selector(enumerateNetwork:withReply:) + argumentIndex: 0 // the first parameter + ofReply: YES // in the method itself. + ]; + + //resume + [self.xpcConnection resume]; + + //happy + initialized = YES; + +//bail +bail: + + return initialized; +} + //check if app is auth'd // ->specifically, if XPC service is setuid -//TODO: error checking!? -(BOOL)isAuthenticated { //flag @@ -195,9 +248,29 @@ //get path to XPC service xpcService = getPath2XPC(); + //sanity check + if(nil == xpcService) + { + //err msg + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: failed to get path to XPC service"); + + //bail + goto bail; + } + //get XPC services' attributes fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:xpcService error:nil]; + //sanity check + if(nil == fileAttributes) + { + //err msg + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: failed to file attributes for XPC service"); + + //bail + goto bail; + } + //check if (fully) auth'd // ->owned by r00t & SETUID if( (0 == [fileAttributes[NSFileOwnerAccountID] unsignedLongValue]) && @@ -206,6 +279,9 @@ //set flag isAuthenticated = YES; } + +//bail +bail: return isAuthenticated; } @@ -426,6 +502,9 @@ bail: //tag NSUInteger segmentTag = 0; + //not found msg + NSString* noItemsMsg = nil; + //get segment tag segmentTag = [[self.bottomPaneBtn selectedCell] tagForSegment:[self.bottomPaneBtn selectedSegment]]; @@ -456,7 +535,7 @@ bail: self.bottomViewController.tableItems = self.currentTask.dylibs; //if there is none - self.noItemsLabel.stringValue = @"no dylibs found"; + noItemsMsg = @"no dylibs found"; break; @@ -467,7 +546,7 @@ bail: self.bottomViewController.tableItems = self.currentTask.files; //if there is none - self.noItemsLabel.stringValue = @"no files found"; + noItemsMsg = @"no files found"; break; @@ -478,7 +557,7 @@ bail: self.bottomViewController.tableItems = self.currentTask.connections; //if there is none - self.noItemsLabel.stringValue = @"no network connections found"; + noItemsMsg = @"no network connections found"; break; @@ -486,8 +565,13 @@ bail: break; } + //execute on current thread if(YES == [NSThread isMainThread]) { + //set not found label + self.noItemsLabel.stringValue = noItemsMsg; + + //finalize [self finalizeBottomReload]; } else @@ -496,7 +580,12 @@ bail: // ->in main UI thread dispatch_async(dispatch_get_main_queue(), ^{ + //set not found label + self.noItemsLabel.stringValue = noItemsMsg; + + //finalize [self finalizeBottomReload]; + }); } @@ -550,10 +639,14 @@ bail: // ->use all tasks if(YES != self.taskTableController.isFiltered) { - //TODO: sync? YESSS!!!! - + //sync + @synchronized(self.taskEnumerator.tasks) + { + //get tasks tasks = self.taskEnumerator.tasks; + + //reload each row w/ new VT info for(NSNumber* taskPid in tasks) { //extract task @@ -566,11 +659,18 @@ bail: [self reloadRow:task]; } } + + }//sync + } //when filtered // ->use filtered items else { + //sync + @synchronized(self.taskTableController.filteredItems) + { + //filtered items for(Task* task in self.taskTableController.filteredItems) { @@ -581,13 +681,14 @@ bail: [self reloadRow:task]; } } + + }//sync } - }//top pane //bottom pane - // ->can just invoke 'reloadRow' method (which has logic to handle filtering, ignoring 'not found' items, etc. + // ->can just invoke 'reloadRow' method (which has logic to handle filtering, ignoring 'not found' items, etc.) else { //reload @@ -708,9 +809,8 @@ bail: return; } -//TODO: don't think we need to sort by pid, since tree view, just uses kids!!! //sort tasks -// ->either name (flat view) or pid (tree view) +// ->just by name (for flat view) -(void)sortTasksForView:(OrderedDictionary*)tasks; { //sort tasks @@ -720,13 +820,6 @@ bail: //sort [tasks sort:SORT_BY_NAME]; } - //sort tasks - // ->tree view, sort by pid - else - { - //sort - [tasks sort:SORT_BY_PID]; - } return; } @@ -746,7 +839,6 @@ bail: dispatch_async(dispatch_get_main_queue(), ^{ //refresh - //[self.taskTableController refresh]; [(id)self.taskTableController refresh]; }); @@ -756,7 +848,6 @@ bail: // ->just refresh else { - //TODO: currentViewCont?!? //refresh [(id)self.taskTableController refresh]; } @@ -764,85 +855,6 @@ bail: return; } - -/* -//callback method, invoked by virus total when plugin's items have been processed -// ->reload table if plugin matches active plugin --(void)itemsProcessed:(PluginBase*)plugin -{ - //if there are any flagged items - // ->reload category table (to trigger title turning red) - if(0 != plugin.flaggedItems.count) - { - //execute on main (UI) thread - dispatch_sync(dispatch_get_main_queue(), ^{ - - //reload category table - [self.categoryTableController customReload]; - - }); - } - - //check if active plugin matches - if(plugin == self.selectedPlugin) - { - //execute on main (UI) thread - dispatch_sync(dispatch_get_main_queue(), ^{ - - //scroll to top of item table - [self.taskTableController scrollToTop]; - - //reload item table - [self.taskTableController.itemTableView reloadData]; - - }); - } - - return; -} -*/ - -/* - -//update a single row --(void)itemProcessed:(File*)fileObj rowIndex:(NSUInteger)rowIndex -{ - //reload category table (on main thread) - // ->ensures correct title color (red, or reset) - dispatch_sync(dispatch_get_main_queue(), ^{ - - //reload category table - [self.categoryTableController customReload]; - - }); - - - - //check if active plugin matches - if(fileObj.plugin == self.selectedPlugin) - { - //execute on main (UI) thread - dispatch_sync(dispatch_get_main_queue(), ^{ - - //start table updates - [self.taskTableController.itemTableView beginUpdates]; - - //update - [self.taskTableController.itemTableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:rowIndex] columnIndexes:[NSIndexSet indexSetWithIndex:0]]; - - //end table updates - [self.taskTableController.itemTableView endUpdates]; - - }); - } - - - return; -} - -*/ - - //callback when user has updated prefs // ->reload table, etc -(void)applyPreferences @@ -1064,8 +1076,10 @@ bail: //start JSON [output appendString:@"{\"tasks:\":["]; - //TODO: sync taskEnumerator.tasks!! since user can click 'refresh' etc? - + //sync + @synchronized(self.taskEnumerator.tasks) + { + //get tasks for(NSNumber* taskPid in self.taskEnumerator.tasks) { @@ -1073,6 +1087,8 @@ bail: [output appendFormat:@"{%@},", [self.taskEnumerator.tasks[taskPid] toJSON]]; } + }//sync + //remove last ',' if(YES == [output hasSuffix:@","]) { @@ -1088,7 +1104,7 @@ bail: if(YES != [output writeToURL:[panel URL] atomically:NO encoding:NSUTF8StringEncoding error:&error]) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: saving output to %@ failed with %@", [panel URL], error); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: saving output to %s failed with %s", [[panel URL] fileSystemRepresentation], [[error description] UTF8String]); //init popup w/ error msg saveResultPopup = [NSAlert alertWithMessageText:@"ERROR: failed to save output" defaultButton:@"Ok" alternateButton:nil otherButton:nil informativeTextWithFormat:@"details: %@", error]; @@ -1117,11 +1133,11 @@ bail: //automatically invoked when user clicks 'search' button // ->perform global search -//TODO: implement +//TODO: implement (in next version) - (IBAction)search:(id)sender { //'unimplemented' msg - __block NSAlert* errorPopup = nil; + NSAlert* errorPopup = nil; //init popup w/ msg errorPopup = [NSAlert alertWithMessageText:@"sorry, 'global search' not yet implemented" defaultButton:@"Ok" alternateButton:nil otherButton:nil informativeTextWithFormat:@"...should be in next version :)"]; @@ -1267,7 +1283,6 @@ bail: //(automatically) invoked on segmented button click or (manually) on task (top pane) switch // ->change input (for pane) & then refresh it -//TODO: make xpcConnection appDelegate iVar -(IBAction)selectBottomPaneContent:(id)sender { //tag @@ -1296,21 +1311,6 @@ bail: //get segment tag segmentTag = [[self.bottomPaneBtn selectedCell] tagForSegment:[self.bottomPaneBtn selectedSegment]]; - /* - //for dylibs - // ->don't want to re-enum if initial enumerations is still occuring - if( (DYLIBS_VIEW == segmentTag) && - (YES != [self.taskEnumerator shouldEnumDylibs]) && - (0 != self.currentTask.dylibs.count) ) - { - //just reload pane - [self reloadBottomPane:self.currentTask itemView:segmentTag]; - - //bail - goto bail; - } - */ - //always hide 'no items' label self.noItemsLabel.hidden = YES; @@ -1354,7 +1354,7 @@ bail: //(re)enumerate dylibs via XPC // ->triggers table reload when done - [self.currentTask enumerateDylibs:self.taskEnumerator.xpcConnection allDylibs:self.taskEnumerator.dylibs]; + [self.currentTask enumerateDylibs:self.xpcConnection allDylibs:self.taskEnumerator.dylibs]; break; @@ -1366,7 +1366,7 @@ bail: //(re)enumerate files via XPC // ->triggers table reload when done - [self.currentTask enumerateFiles:self.taskEnumerator.xpcConnection]; + [self.currentTask enumerateFiles:self.xpcConnection]; break; @@ -1378,7 +1378,7 @@ bail: //(re)enumerate network connections via XPC // ->triggers table reload when done - [self.currentTask enumerateNetworking:self.taskEnumerator.xpcConnection]; + [self.currentTask enumerateNetworking:self.xpcConnection]; break; @@ -1571,7 +1571,6 @@ bail: } - //action for 'refresh' button // ->query OS to refresh/reload all tasks -(IBAction)refreshTasks:(id)sender @@ -1581,6 +1580,18 @@ bail: //select top row [self.taskTableController.itemView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; + + //TODO: don't reset filtered items? + // ...will require some smart filtering :/ + + //unset filter flag + self.taskTableController.isFiltered = NO; + + //remove all filtered items + [self.taskTableController.filteredItems removeAllObjects]; + + //reset filter box + self.filterTasksBox.stringValue = @""; //scroll to top [self.taskTableController scrollToTop]; diff --git a/Filter.m b/Filter.m index cfb9956..8d9792d 100644 --- a/Filter.m +++ b/Filter.m @@ -176,39 +176,58 @@ NSString * const KEYWORDS[] = {@"#apple", @"#nonapple", @"#signed", @"#unsigned" } //filter network connections -//TODO: match on state, etc? -//TODO: make format of range seach/check/continue +//TODO: match on family/connection type -(void)filterConnections:(NSString*)filterText items:(NSMutableArray*)items results:(NSMutableArray*)results { - //local IP addr range - NSRange localIPRange = {0}; - - //local port range - NSRange localPortRange = {0}; - - //TODO: add remote ip/port, state, proto, family? - //first reset filter'd items [results removeAllObjects]; //iterate over all tasks for(Connection* item in items) { - //init name range - localIPRange = [item.localIPAddr rangeOfString:filterText options:NSCaseInsensitiveSearch]; - - //init path range - localPortRange = [[NSString stringWithFormat:@"%d", [item.localPort unsignedShortValue]] rangeOfString:filterText options:NSCaseInsensitiveSearch]; - - //check for match - if( (NSNotFound != localIPRange.location) || - (NSNotFound != localPortRange.location) ) + //check local ip + if(NSNotFound != [item.localIPAddr rangeOfString:filterText options:NSCaseInsensitiveSearch].location) { //save match [results addObject:item]; + + //next + continue; } - }//all items + //check local port + if(NSNotFound != [[NSString stringWithFormat:@"%d", [item.localPort unsignedShortValue]] rangeOfString:filterText options:NSCaseInsensitiveSearch].location) + { + //save match + [results addObject:item]; + + //next + continue; + } + + //check remote ip + if( (nil != item.remoteIPAddr) && + (NSNotFound != [item.remoteIPAddr rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) + { + //save match + [results addObject:item]; + + //next + continue; + } + + //check remote port + if( (nil != item.remoteIPAddr) && + (NSNotFound != [[NSString stringWithFormat:@"%d", [item.remotePort unsignedShortValue]] rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) + { + //save match + [results addObject:item]; + + //next + continue; + } + + }//all connections return; } @@ -346,7 +365,7 @@ bail: BOOL isFlagged = NO; //check - //TODO: query VT if needed? + // ->note: assumes that VT query has already completed... if( (nil != item.vtInfo) && (0 != [item.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) { diff --git a/InfoWindowController.m b/InfoWindowController.m index a768f01..8a8b75b 100644 --- a/InfoWindowController.m +++ b/InfoWindowController.m @@ -119,6 +119,15 @@ //set name [self.name setStringValue:[self valueForStringItem:task.binary.name default:@"unknown"]]; + //flagged items + // ->make name red! + if( (nil != task.binary.vtInfo) && + (0 != [task.binary.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) + { + //red + self.name.textColor = [NSColor redColor]; + } + //set command line // ->done just in time (first time) if(nil == task.arguments) @@ -129,18 +138,6 @@ //set args [self.arguments setStringValue:[self valueForStringItem:[task.arguments componentsJoinedByString:@""] default:@"no arguments"]]; - - //TODO:enable - /* - //flagged files - // ->make name red! - if( (nil != ((File*)self.itemObj).vtInfo) && - (0 != [((File*)self.itemObj).vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) - { - //set color (light red) - self.name.textColor = [NSColor redColor]; - } - */ //set path [self.path setStringValue:[self valueForStringItem:task.binary.path default:@"unknown"]]; @@ -174,10 +171,10 @@ [self.sign setStringValue:[self valueForStringItem:[task.binary formatSigningInfo] default:@"not signed"]]; } - //handle binaries + //handle binaries (dylibs) else if(YES == [self.itemObj isKindOfClass:[Binary class]]) { - //type cast + //cast as binary/dylib dylib = (Binary*)self.itemObj; //set icon @@ -186,17 +183,14 @@ //set name [self.name setStringValue:[self valueForStringItem:dylib.name default:@"unknown"]]; - //TODO: enable?! - /* - //flagged files - // ->make name red! - if( (nil != ((File*)self.itemObj).vtInfo) && - (0 != [((File*)self.itemObj).vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) - { - //set color (light red) - self.name.textColor = [NSColor redColor]; - } - */ + //flagged items + // ->make name red! + if( (nil != dylib.vtInfo) && + (0 != [dylib.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) + { + //red + self.name.textColor = [NSColor redColor]; + } //set path [self.path setStringValue:[self valueForStringItem:dylib.path default:@"unknown"]]; diff --git a/Items/Connection.m b/Items/Connection.m index a3e3d90..ba68dc1 100644 --- a/Items/Connection.m +++ b/Items/Connection.m @@ -19,7 +19,6 @@ { //super //self = [super initWithParams:params]; - //TODO: think about this - Connection doesn't share any baseItem stuffz self = [super init]; if(nil != self) { diff --git a/Items/File.m b/Items/File.m index 03ac056..9e4e7bf 100644 --- a/Items/File.m +++ b/Items/File.m @@ -5,13 +5,13 @@ // Created by Patrick Wardle on 2/19/15. // Copyright (c) 2015 Objective-See. All rights reserved. // - - #import "File.h" #import "Consts.h" #import "Utilities.h" #import "AppDelegate.h" +#import + @implementation File @synthesize type; @@ -26,11 +26,10 @@ if(self) { //always skip not-existent paths - // ->also get set a directory flag at the same time ;) if(YES != [[NSFileManager defaultManager] fileExistsAtPath:params[KEY_RESULT_PATH]]) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: %@ not found", params[KEY_RESULT_PATH]); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: %s not found", [params[KEY_RESULT_PATH] UTF8String]); //set self to nil self = nil; diff --git a/RequestRootWindowController.m b/RequestRootWindowController.m index eb0d3ec..f199215 100644 --- a/RequestRootWindowController.m +++ b/RequestRootWindowController.m @@ -9,9 +9,10 @@ #import "Utilities.h" #import "AppDelegate.h" - #import "RequestRootWindowController.h" +#import + @implementation RequestRootWindowController @@ -123,7 +124,7 @@ if(errAuthorizationSuccess != osStatus) { //err msg - NSLog(@"ERROR: AuthorizationCreate() failed with %d", osStatus); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: AuthorizationCreate() failed with %d", osStatus); //bail goto bail; @@ -134,7 +135,7 @@ if(errAuthorizationSuccess != osStatus) { //err msg - NSLog(@"ERROR: AuthorizationExecuteWithPrivileges() failed with %d", osStatus); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: AuthorizationExecuteWithPrivileges() failed with %d", osStatus); //set result msg [self.statusMsg setStringValue: [NSString stringWithFormat:@"error: failed with %d", osStatus]]; @@ -169,7 +170,7 @@ if(errAuthorizationSuccess != osStatus) { //err msg - NSLog(@"ERROR: AuthorizationExecuteWithPrivileges() failed with %d", osStatus); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: AuthorizationExecuteWithPrivileges() failed with %d", osStatus); //set result msg [self.statusMsg setStringValue: [NSString stringWithFormat:@"error: failed with %d", osStatus]]; @@ -187,9 +188,11 @@ //no exit self.shouldExit = NO; - //start enumerating tasks - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) exploreTasks]; + //call back into app delegate + // ->kick off task enum, etc + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) go]; + //bail bail: diff --git a/Task.h b/Task.h index 11d433d..f4a9a19 100644 --- a/Task.h +++ b/Task.h @@ -77,8 +77,6 @@ struct dyld_image_info_32 { //get command-line args -(void)getArguments; --(void)generateBinaryInfo; - //enumerate all dylibs // ->new ones are added to 'existingDylibs' (global) dictionary -(void)enumerateDylibs:(NSXPCConnection*)xpcConnection allDylibs:(NSMutableDictionary*)allDylibs; @@ -90,7 +88,6 @@ struct dyld_image_info_32 { -(void)enumerateNetworking:(NSXPCConnection*)xpcConnection; //convert self to JSON string -// TODO: add dylibs, files, networking -(NSString*)toJSON; @end diff --git a/Task.m b/Task.m index eb6a1e3..76ec424 100644 --- a/Task.m +++ b/Task.m @@ -32,22 +32,16 @@ @synthesize pid; @synthesize uid; -//@synthesize icon; -//@synthesize name; -//@synthesize path; @synthesize ppid; @synthesize files; @synthesize binary; -//@synthesize bundle; @synthesize dylibs; @synthesize children; @synthesize arguments; @synthesize connections; -//TODO: make sure we only check signature of binary once!!! - //init w/ a pid + path -// note: time consuming init's are done in '' method +// note: time consuming init's are done in other methods -(id)initWithPID:(NSNumber*)taskPID andPath:(NSString*)taskPath { //existing binaries @@ -61,7 +55,6 @@ self = [super init]; if(nil != self) { - //TODO: sync? or always access w/ direct var //grab existings binaries existingBinaries = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.executables; @@ -118,8 +111,12 @@ // ->this will process in background [((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.binaryQueue enqueue:self.binary]; - //add it to 'global' list - existingBinaries[taskPath] = self.binary; + //sync + @synchronized(existingBinaries) + { + //add it to 'global' list + existingBinaries[taskPath] = self.binary; + } } }//init self @@ -130,15 +127,6 @@ bail: return self; } -// --(void)generateBinaryInfo -{ - //create main binary - //self.binary = [[Binary alloc] initWithParams:@{KEY_RESULT_PATH:self.path}]; - - return; -} - //get command-line args -(void)getArguments { @@ -310,7 +298,6 @@ bail: { //free free(processArgs); - } return; @@ -524,9 +511,12 @@ bail: //generate detailed info [newFile generateDetailedInfo]; - //TODO: sync! - //save into global list - [((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.files setObject:newFile forKey:filePath]; + //sync + @synchronized(((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.files) + { + //save into global list + [((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.files setObject:newFile forKey:filePath]; + } } }]; @@ -542,7 +532,8 @@ bail: //remove any existing enum'd networking sockets/connections [self.connections removeAllObjects]; - NSLog(@"invoking XPC to enumer networking"); + //dbg msg + //NSLog(@"invoking XPC to enumer networking"); //invoke XPC service (running as r00t) // ->will enumerate network sockets/connections, then invoke reply block so can save into iVar @@ -565,10 +556,8 @@ bail: } } - ////TODO: on main thead? //reload bottom pane [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadBottomPane:self itemView:NETWORKING_VIEW]; - }]; return; diff --git a/TaskEnumerator.h b/TaskEnumerator.h index 8dbd38b..b407206 100644 --- a/TaskEnumerator.h +++ b/TaskEnumerator.h @@ -19,9 +19,6 @@ /* PROPERTIES */ -//flag indicating first scan is complete -//@property BOOL firstScanComplete; - //all tasks objects @property(nonatomic, retain)OrderedDictionary* tasks; @@ -34,10 +31,6 @@ //all dylibs @property(nonatomic, retain)NSMutableDictionary* dylibs; -//remote XPC interface -//TODO: weak OK? -@property (nonatomic, retain) NSXPCConnection* xpcConnection; - //queue // ->contains binaries that should be processed @property (nonatomic, retain) Queue* binaryQueue; @@ -56,10 +49,6 @@ // ->ensures order of parent's (by pid), is preserved -(void)generateAncestries:(OrderedDictionary*)newTasks; -//determine if dylibs should be (re)enumerated -// ->generally yes, unless the first enumeration (of all tasks) is not complete -//-(BOOL)shouldEnumDylibs; - //remove a task // ->contain extra logic to remove children, etc -(void)removeTask:(Task*)task; diff --git a/TaskEnumerator.m b/TaskEnumerator.m index 198f8d9..a35489f 100644 --- a/TaskEnumerator.m +++ b/TaskEnumerator.m @@ -14,9 +14,10 @@ #import "AppDelegate.h" #import "Utilities.h" #import "TaskEnumerator.h" -#import "serviceInterface.h" -#include -#include + +#import +#import +#import @@ -29,9 +30,8 @@ @synthesize dylibs; @synthesize binaryQueue; @synthesize executables; -@synthesize xpcConnection; -//@synthesize firstScanComplete; + //init -(id)init @@ -49,42 +49,6 @@ //alloc dylibs dictionary dylibs = [NSMutableDictionary dictionary]; - //alloc XPC connection - xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.remoteTaskService"]; - - //set remote object interface - self.xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(remoteTaskProto)]; - - //set classes - // ->arrays & strings are what is ok to vend - [self.xpcConnection.remoteObjectInterface - setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], nil] - forSelector: @selector(enumerateDylibs:withReply:) - argumentIndex: 0 // the first parameter - ofReply: YES // in the method itself. - ]; - - //set classes - // ->arrays & strings are what is ok to vend - [self.xpcConnection.remoteObjectInterface - setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], nil] - forSelector: @selector(enumerateFiles:withReply:) - argumentIndex: 0 // the first parameter - ofReply: YES // in the method itself. - ]; - - //set classes - // ->arrays & strings are what is ok to vend - [self.xpcConnection.remoteObjectInterface - setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], [NSNumber class], nil] - forSelector: @selector(enumerateNetwork:withReply:) - argumentIndex: 0 // the first parameter - ofReply: YES // in the method itself. - ]; - - //resume - [self.xpcConnection resume]; - //init binary processing queue binaryQueue = [[Queue alloc] init]; @@ -96,7 +60,7 @@ //enumerate all tasks // ->calls back into app delegate to update task (top) table when pau -// TOOD: call every x # of seconds? +// TODO: call every x # of seconds? -(void)enumerateTasks { //(new) task item @@ -150,10 +114,14 @@ continue; } - //TODO: sync? + //sync + @synchronized(self.tasks) + { //add new task [self.tasks setObject:newTask forKey:newTask.pid]; + + }//sync }//add new tasks @@ -243,7 +211,7 @@ if(status < 0) { //err - NSLog(@"OBJECTIVE-SEE ERROR: proc_listpids() failed with %d", status); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: proc_listpids() failed with %d", status); //bail goto bail; @@ -313,11 +281,8 @@ bail: return allTasks; } -//TODO: remove! //insert tasks into appropriate parent // ->ensures order of parent's (by pid), is preserved -//TODO: what about parentless procs!? (e.g. malware?) -//TODO: what about dead-parents, 'desktop helper' -(void)generateAncestries:(OrderedDictionary*)newTasks { //task @@ -353,12 +318,20 @@ bail: //get task task = newTasks[key]; + //ignore tasks that have died + if(YES != isAlive([task.pid intValue])) + { + //next + continue; + } + //get parent parent = newTasks[task.ppid]; - //when parent is nil + //when parent is nil or dead // ->default to launchd (pid 0x1) - if(nil == parent) + if( (nil == parent) || + (YES != isAlive([task.pid intValue])) ) { //default parent = self.tasks[@1]; diff --git a/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 70d2e4c..80041c9 100644 --- a/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -46,11 +46,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "VirusTotal.m" - timestampString = "457651407.339698" + timestampString = "459880884.303039" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "121" - endingLineNumber = "121" + startingLineNumber = "123" + endingLineNumber = "123" landmarkName = "-addItem:" landmarkType = "5"> @@ -62,27 +62,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "Task.m" - timestampString = "458643111.392416" + timestampString = "460097895.698263" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "345" - endingLineNumber = "345" - landmarkName = "-enumerateDylibs:allDylibs:" - landmarkType = "5"> - - - - @@ -103,22 +87,6 @@ landmarkType = "5"> - - - - @@ -158,11 +126,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "Task.m" - timestampString = "458643111.392416" + timestampString = "460097895.698263" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "438" - endingLineNumber = "438" + startingLineNumber = "425" + endingLineNumber = "425" landmarkName = "-enumerateDylibs:allDylibs:" landmarkType = "5"> @@ -174,11 +142,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "VirusTotal.m" - timestampString = "457651407.339698" + timestampString = "459880884.303039" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "112" - endingLineNumber = "112" + startingLineNumber = "114" + endingLineNumber = "114" landmarkName = "-addItem:" landmarkType = "5"> @@ -190,11 +158,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "RequestRootWindowController.m" - timestampString = "457652630.623627" + timestampString = "459880884.303039" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "24" - endingLineNumber = "24" + startingLineNumber = "25" + endingLineNumber = "25" landmarkName = "-awakeFromNib" landmarkType = "5"> @@ -222,11 +190,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "Filter.m" - timestampString = "457848671.809721" + timestampString = "460097193.779482" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "217" - endingLineNumber = "217" + startingLineNumber = "236" + endingLineNumber = "236" landmarkName = "-binaryFulfillsKeyword:binary:" landmarkType = "5"> @@ -238,31 +206,15 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "Filter.m" - timestampString = "457943064.104332" + timestampString = "460097193.779482" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "343" - endingLineNumber = "343" + startingLineNumber = "362" + endingLineNumber = "362" landmarkName = "-isFlagged:" landmarkType = "5"> - - - - @@ -286,31 +238,15 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "TaskEnumerator.m" - timestampString = "458803386.948431" + timestampString = "459880884.303039" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "156" - endingLineNumber = "156" + startingLineNumber = "123" + endingLineNumber = "123" landmarkName = "-enumerateTasks" landmarkType = "5"> - - - - @@ -334,11 +270,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "459044856.502796" + timestampString = "460150711.336469" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "259" - endingLineNumber = "259" + startingLineNumber = "338" + endingLineNumber = "338" landmarkName = "@implementation AppDelegate" landmarkType = "3"> @@ -350,14 +286,46 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "TaskTableController.m" - timestampString = "459048374.675901" + timestampString = "459840842.999072" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "882" - endingLineNumber = "882" + startingLineNumber = "868" + endingLineNumber = "868" landmarkName = "-outlineView:rowViewForItem:" landmarkType = "5"> + + + + + + + + diff --git a/TaskTableController.m b/TaskTableController.m index f17998e..c16f9f5 100644 --- a/TaskTableController.m +++ b/TaskTableController.m @@ -25,8 +25,6 @@ #import -//TODO: need to do some sync or logic to handle swaps -otherwise crashes!!! - @implementation TaskTableController @synthesize itemView; @@ -520,8 +518,6 @@ bail: return; } - -//TODO: make sure this works for outline view!!! //grab a task at a row -(Task*)taskForRow:(id)sender { @@ -566,19 +562,9 @@ bail: //get row that's about to be selected rowView = [self.itemView viewAtColumn:0 row:taskRow makeIfNecessary:YES]; - //TODO: need this? - //when not filtered, use all tasks - //if(YES != isFiltered) - //{ - //extract task - // ->pid of task is view's id :) - task = tasks[[NSNumber numberWithInteger:(rowView.tag - PID_TAG_DELTA)]]; - //} - //when filtered, use filtered items - //else - //{ - // task = self.filteredItems[ - //} + //extract task + // ->pid of task is view's id :) + task = tasks[[NSNumber numberWithInteger:(rowView.tag - PID_TAG_DELTA)]]; //bail bail: @@ -837,9 +823,9 @@ bail: } -//TODO: combine logic - -//TODO: when combine, use pid of task is view's id - to lookup task :) -- (void)outlineViewSelectionDidChange:(NSNotification *)notification +//automatically ccalled when row is selected in outline view +// ->invoke helper function to handle selection +-(void)outlineViewSelectionDidChange:(NSNotification *)notification { //handle selection [self handleRowSelection]; diff --git a/UI/FileInfoWindow.xib b/UI/FileInfoWindow.xib index c2f57ea..397aaef 100644 --- a/UI/FileInfoWindow.xib +++ b/UI/FileInfoWindow.xib @@ -1,5 +1,5 @@ - + @@ -36,7 +36,7 @@ - + diff --git a/UI/FlatView.xib b/UI/FlatView.xib index 2a8c648..e336252 100755 --- a/UI/FlatView.xib +++ b/UI/FlatView.xib @@ -48,11 +48,11 @@ - - + + - - + + @@ -169,11 +169,12 @@ + - + - + diff --git a/UI/NetworkInfoWindow.xib b/UI/NetworkInfoWindow.xib index e458612..38b6dc1 100644 --- a/UI/NetworkInfoWindow.xib +++ b/UI/NetworkInfoWindow.xib @@ -1,5 +1,5 @@ - + @@ -8,16 +8,11 @@ - - - - - diff --git a/UI/TaskInfoWindow.xib b/UI/TaskInfoWindow.xib index ada1991..38563d5 100644 --- a/UI/TaskInfoWindow.xib +++ b/UI/TaskInfoWindow.xib @@ -1,5 +1,5 @@ - + @@ -38,7 +38,7 @@ - + diff --git a/UI/TreeView.xib b/UI/TreeView.xib index 092e4e8..30208f7 100755 --- a/UI/TreeView.xib +++ b/UI/TreeView.xib @@ -48,11 +48,11 @@ - - + + - - + + @@ -172,13 +172,14 @@ - + - + + diff --git a/Utilities.m b/Utilities.m index 60fa30f..c9b622f 100644 --- a/Utilities.m +++ b/Utilities.m @@ -11,6 +11,7 @@ #import #import +#import #import #import #import @@ -126,7 +127,7 @@ NSDictionary* extractSigningInfo(NSString* path) if(STATUS_SUCCESS != status) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: SecStaticCodeCreateWithPath() failed on %@ with %d", path, status); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: SecStaticCodeCreateWithPath() failed on %s with %d", [path UTF8String], status); //bail goto bail; @@ -149,7 +150,7 @@ NSDictionary* extractSigningInfo(NSString* path) if(STATUS_SUCCESS != status) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: SecCodeCopySigningInformation() failed on %@ with %d", path, status); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: SecCodeCopySigningInformation() failed on %s with %d", [path UTF8String], status); //bail goto bail; @@ -158,9 +159,13 @@ NSDictionary* extractSigningInfo(NSString* path) //determine if binary is signed by Apple signingStatus[KEY_SIGNING_IS_APPLE] = [NSNumber numberWithBool:isApple(path)]; } - - //TODO: bail, unsigned? - + //error + // ->not signed, or something else, so no need to check cert's names + else + { + //bail + goto bail; + } //init array for certificate names signingStatus[KEY_SIGNING_AUTHORITIES] = [NSMutableArray array]; @@ -245,7 +250,7 @@ BOOL isApple(NSString* path) if(STATUS_SUCCESS != status) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: SecStaticCodeCreateWithPath() failed on %@ with %d", path, status); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: SecStaticCodeCreateWithPath() failed on %s with %d", [path UTF8String], status); //bail goto bail; @@ -258,7 +263,7 @@ BOOL isApple(NSString* path) (requirementRef == NULL) ) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: SecRequirementCreateWithString() failed on %@ with %d", path, status); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: SecRequirementCreateWithString() failed on %s with %d", [path UTF8String], status); //bail goto bail; @@ -409,8 +414,7 @@ NSDictionary* hashFile(NSString* filePath) if(nil == (fileContents = [NSData dataWithContentsOfFile:filePath])) { //err msg - //TODO: re-enable - //NSLog(@"OBJECTIVE-SEE ERROR: couldn't load %@ to hash", filePath); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: couldn't load %s to hash", [filePath UTF8String]); //bail goto bail; @@ -694,8 +698,6 @@ BOOL isAlive(pid_t targetPID) } - //NSLog(@"killing %d: %d/%d", targetPID, result, errno); - return isAlive; } diff --git a/VirusTotal.m b/VirusTotal.m index 4b14826..614f443 100644 --- a/VirusTotal.m +++ b/VirusTotal.m @@ -12,6 +12,8 @@ #import "VirusTotal.h" #import "AppDelegate.h" +#import + @implementation VirusTotal @synthesize items; @@ -439,7 +441,7 @@ if(nil == postData) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: failed to convert request %@ to JSON", postData); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: failed to convert request %s to JSON", [[postData description] UTF8String]); //bail goto bail; @@ -472,11 +474,11 @@ //sanity check(s) if( (nil == vtData) || - (nil != error) || - (200 != (long)[(NSHTTPURLResponse *)httpResponse statusCode]) ) + (nil != error) || + (200 != (long)[(NSHTTPURLResponse *)httpResponse statusCode]) ) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: failed to query VirusTotal (%@, %@)", error, httpResponse); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: failed to query VirusTotal (%s, %s)", [[error description] UTF8String], [[httpResponse description] UTF8String]); //bail goto bail; @@ -493,7 +495,7 @@ @catch (NSException *exception) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: converting response %@ to JSON threw %@", vtData, exception); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: converting response %s to JSON threw %s", [[vtData description] UTF8String], [[exception description] UTF8String]); //bail goto bail; @@ -503,7 +505,7 @@ if(nil == results) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: failed to convert response %@ to JSON", vtData); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: failed to convert response %s to JSON", [[vtData description] UTF8String]); //bail goto bail; @@ -578,7 +580,7 @@ bail: if(nil == fileContents) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: failed to load %@ into memory for submission", item.path); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: failed to load %s into memory for submission", [item.path UTF8String]); //bail goto bail; @@ -618,7 +620,7 @@ bail: (200 != (long)[(NSHTTPURLResponse *)httpResponse statusCode]) ) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: failed to query VirusTotal (%@, %@)", error, httpResponse); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: failed to query VirusTotal (%s, %s)", [[error description] UTF8String], [[httpResponse description] UTF8String]); //bail goto bail; @@ -635,7 +637,7 @@ bail: @catch (NSException *exception) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: converting response %@ to JSON threw %@", vtData, exception); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: converting response %s to JSON threw %s", [[vtData description] UTF8String], [[exception description] UTF8String]); //bail goto bail; @@ -645,7 +647,7 @@ bail: if(nil == results) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: failed to convert response %@ to JSON", vtData); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: failed to convert response %s to JSON", [[vtData description] UTF8String]); //bail goto bail; @@ -683,13 +685,12 @@ bail: if(nil == result) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: failed to re-scan %@", item.name); + syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: failed to re-scan %s", [item.name UTF8String]); //bail goto bail; } - //bail bail: @@ -726,6 +727,7 @@ bail: //TODO: do something with detections!? + // ->blinking button, user's can click to see 'flagged items' popup //if(0 != [result[VT_RESULTS_POSITIVES] unsignedIntegerValue]) } diff --git a/en.lproj/MainMenu.xib b/en.lproj/MainMenu.xib index 4d62de1..8433ff0 100755 --- a/en.lproj/MainMenu.xib +++ b/en.lproj/MainMenu.xib @@ -2,8 +2,9 @@ - + + @@ -24,7 +25,7 @@ - +