diff --git a/AppDelegate.m b/AppDelegate.m index 276a9a4..3194ba5 100755 --- a/AppDelegate.m +++ b/AppDelegate.m @@ -89,7 +89,7 @@ //filterObj = [[Filter alloc] init]; //init virus total object - //virusTotalObj = [[VirusTotal alloc] init]; + virusTotalObj = [[VirusTotal alloc] init]; //init array for virus total threads //vtThreads = [NSMutableArray array]; @@ -329,11 +329,7 @@ { //alloc/init taskTableController = [[TaskTableController alloc] initWithNibName:@"FlatView" bundle:nil]; - /*if(self.taskTableController != nil) - { - //update iVar - self.currentViewController = self.taskTableController; - }*/ + break; } case TREE_VIEW: @@ -341,13 +337,6 @@ //alloc/init taskTableController = [[TaskTableController alloc] initWithNibName:@"TreeView" bundle:nil]; - - - /*if(self.taskTableController != nil) - { - //update iVar - self.currentViewController = self.taskTableController; - }*/ break; } } @@ -361,16 +350,19 @@ return; } -//TODO: add checks to make sure or handle switch to tree view!!!! //reload (to re-draw) a specific row in table -(void)reloadRow:(Task*)task item:(ItemBase*)item pane:(NSUInteger)pane { //table view - NSTableView* tableView = nil; + __block NSTableView* tableView = nil; //row - NSUInteger row = 0; + __block NSUInteger row = 0; + //run everything on main thread + // ->ensures table view isn't changed out from under us.... + dispatch_async(dispatch_get_main_queue(), ^{ + //top table (pane) if(PANE_TOP == pane) { @@ -390,20 +382,15 @@ goto bail; } - //reload just the row - // ->on main thread - dispatch_async(dispatch_get_main_queue(), ^{ - - //begin updates - [tableView beginUpdates]; - - //reload row - [tableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:(row)] columnIndexes:[NSIndexSet indexSetWithIndex:0]]; - - //end updates - [tableView endUpdates]; - - }); + //begin updates + [tableView beginUpdates]; + + //reload row + [tableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:(row)] columnIndexes:[NSIndexSet indexSetWithIndex:0]]; + + //end updates + [tableView endUpdates]; + } //reload item // ->tree view @@ -411,10 +398,10 @@ { //begin updates [tableView beginUpdates]; - + //reload [(NSOutlineView*)tableView reloadItem:task]; - + //end updates [tableView endUpdates]; } @@ -427,6 +414,9 @@ //bail bail: + ; + + }); //dispatch on main thread return; } @@ -438,20 +428,14 @@ bail: //tag NSUInteger segmentTag = 0; - /* - if(YES == [task.binary.name isEqualToString:@"launchd"]) - { - NSLog(@"asdf"); - }*/ - //get segment tag segmentTag = [[self.bottomPaneBtn selectedCell] tagForSegment:[self.bottomPaneBtn selectedSegment]]; //ignore reloads for unselected tasks - if(self.currentTask != task) + // ->note: when no task is passed in, always reload though + if( (nil != task) && + (self.currentTask != task)) { - //NSLog(@"selected task: %@ not match %@", self.currentTask.binary.name, task.binary.name); - //ignore goto bail; } @@ -1321,6 +1305,9 @@ bail: //reload top pane [self reloadTaskTable]; + //select top row + [self.taskTableController.itemView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; + return; } diff --git a/ItemView.m b/ItemView.m index a97634f..669d621 100644 --- a/ItemView.m +++ b/ItemView.m @@ -489,6 +489,9 @@ NSTableCellView* createTaskView(NSTableView* tableView, id owner, Task* task) //task's name frame CGRect nameFrame = {0}; + //task's path frame + CGRect pathFrame = {0}; + //sanity check if(nil == task.binary) { @@ -552,6 +555,22 @@ NSTableCellView* createTaskView(NSTableView* tableView, id owner, Task* task) // ->immediately follows name [((NSTextField*)[taskCell viewWithTag:TABLE_ROW_PID_LABEL]) setStringValue:[NSString stringWithFormat:@"(%@)", task.pid]]; + /* + //outline view has indentations + // ->adjust path width to account for this + if(YES == [tableView isKindOfClass:[NSOutlineView class]]) + { + //get path frame + pathFrame = ((NSTextField*)[tableView viewWithTag:TABLE_ROW_SUB_TEXT_TAG]).frame; + + //subtract indendtation for level + pathFrame.size.width -= [(NSOutlineView*) tableView levelForItem:task]; + + //set path frame + ((NSTextField*)[tableView viewWithTag:TABLE_ROW_SUB_TEXT_TAG]).frame = pathFrame; + } + */ + //set path [[taskCell viewWithTag:TABLE_ROW_SUB_TEXT_TAG] setStringValue:task.binary.path]; diff --git a/Items/Binary.m b/Items/Binary.m index 7f0d579..8dcd4fa 100644 --- a/Items/Binary.m +++ b/Items/Binary.m @@ -50,17 +50,7 @@ //grab attributes //self.attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:self.path error:nil]; - - //do this in bg! - //set signing info - //self.signingInfo = extractSigningInfo(self.path); - - //computes hashes - // ->set 'md5' and 'sha1' iVars - //self.hashes = hashFile(self.path); - - //call into filter object to check if file is known // ->apple-signed or whitelisted //self.isTrusted = [((AppDelegate*)[[NSApplication sharedApplication] delegate]).filterObj isTrustedFile:self]; @@ -165,7 +155,6 @@ bail: return taskIcon; } -//TODO: green signing for apple!!! //get signing info (which takes a while to generate) // ->this method should be called in the background -(void)generatedSigningInfo diff --git a/Queue.h b/Queue.h index 4849758..53f8ac4 100644 --- a/Queue.h +++ b/Queue.h @@ -25,6 +25,8 @@ } +/* PROPERTIES */ + //event queue @property(retain, atomic)NSMutableArray* eventQueue; diff --git a/Queue.m b/Queue.m index 77688e3..dd0c175 100644 --- a/Queue.m +++ b/Queue.m @@ -9,6 +9,7 @@ #import "Queue.h" #import "Consts.h" #import "Binary.h" +#import "VirusTotal.h" #import "AppDelegate.h" @implementation Queue @@ -50,6 +51,12 @@ // ->don't want UI thread, etc to suffer [NSThread sleepForTimeInterval:5.0f]; + //VT object + VirusTotal* vtObject = nil; + + //grab VT object + vtObject = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).virusTotalObj; + //for ever while(YES) { @@ -69,15 +76,21 @@ //get item off queue binary = [eventQueue dequeue]; - //process binary - // ->hash, etc - if(YES == [binary isKindOfClass:[Binary class]]) + //sanity check + if(YES != [binary isKindOfClass:[Binary class]]) { - //process - //->for now, just hash, etc - [binary generateDetailedInfo]; + //ignore + continue; } + //process + //->for now, just hash, etc + [binary generateDetailedInfo]; + + //add item for VT processing + [vtObject addItem:binary]; + + //unlock [self.queueCondition unlock]; diff --git a/Task.m b/Task.m index cad6381..8cfd0b6 100644 --- a/Task.m +++ b/Task.m @@ -44,6 +44,8 @@ @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 -(id)initWithPID:(NSNumber*)taskPID andPath:(NSString*)taskPath @@ -112,6 +114,10 @@ // ->this will processing [((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.binaryQueue enqueue:self.binary]; + //add to VT queue + // ->this will trigger background submission to VT + [((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.binaryQueue enqueue:self.binary]; + //add it to 'global' list existingBinaries[taskPath] = self.binary; @@ -376,7 +382,7 @@ bail: } //add to queue - // ->this will processing + // ->this will trigger background processing [((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.binaryQueue enqueue:dylib]; //add to list of new dylibs diff --git a/TaskEnumerator.h b/TaskEnumerator.h index b956adb..8dbd38b 100644 --- a/TaskEnumerator.h +++ b/TaskEnumerator.h @@ -38,12 +38,11 @@ //TODO: weak OK? @property (nonatomic, retain) NSXPCConnection* xpcConnection; -//queue object -// ->contains watch items that should be processed +//queue +// ->contains binaries that should be processed @property (nonatomic, retain) Queue* binaryQueue; - /* METHODS */ //enumerate all tasks diff --git a/TaskEnumerator.m b/TaskEnumerator.m index cdd0446..9dafb79 100644 --- a/TaskEnumerator.m +++ b/TaskEnumerator.m @@ -87,6 +87,7 @@ //init binary processing queue binaryQueue = [[Queue alloc] init]; + } return self; diff --git a/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index ca56366..2ddd5ee 100644 --- a/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -36,11 +36,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "456474906.073599" + timestampString = "456541711.785304" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "671" - endingLineNumber = "671" + startingLineNumber = "661" + endingLineNumber = "661" landmarkName = "-reloadTaskTable" landmarkType = "5"> @@ -115,15 +115,57 @@ shouldBeEnabled = "No" ignoreCount = "0" continueAfterRunningActions = "No" - filePath = "AppDelegate.m" - timestampString = "456474979.625707" + filePath = "ItemView.m" + timestampString = "456521289.11776" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "416" - endingLineNumber = "416" - landmarkName = "-reloadRow:item:pane:" + startingLineNumber = "484" + endingLineNumber = "484" + landmarkName = "createTaskView()" + landmarkType = "7"> + + + + + + + + + + + + diff --git a/UI/FlatView.xib b/UI/FlatView.xib index d9ecca1..aa4a35b 100755 --- a/UI/FlatView.xib +++ b/UI/FlatView.xib @@ -18,9 +18,9 @@ - - - + + + @@ -31,7 +31,7 @@ - + @@ -45,7 +45,7 @@ - + @@ -185,7 +185,7 @@ - + @@ -302,7 +302,7 @@ - + @@ -385,7 +385,7 @@ - + @@ -471,8 +471,10 @@ - - + + + + diff --git a/UI/TreeView.xib b/UI/TreeView.xib index 529f55b..66d91e0 100755 --- a/UI/TreeView.xib +++ b/UI/TreeView.xib @@ -15,12 +15,12 @@ - + - - - + + + @@ -31,7 +31,7 @@ - + @@ -44,117 +44,123 @@ - - + + - + - - + + - + - + - - + + - + - - - + + + - - - + + - - + + - + - - - + + + - - - + + - - + + - + - - - + + - + - + - + - - + + - + @@ -162,20 +168,21 @@ - - - - - - - - - - + + + + + + + + + + + - - + + @@ -199,7 +206,13 @@ - + + + + + + + diff --git a/Utilities.m b/Utilities.m index 18fcde4..5d79c4b 100644 --- a/Utilities.m +++ b/Utilities.m @@ -112,9 +112,13 @@ NSDictionary* extractSigningInfo(NSString* path) //init signing status signingStatus = [NSMutableDictionary dictionary]; + //signingStatus[KEY_SIGNATURE_STATUS] = @0; + //return signingStatus; + //create static code status = SecStaticCodeCreateWithPath((__bridge CFURLRef)([NSURL fileURLWithPath:path]), kSecCSDefaultFlags, &staticCode); + //TODO: called same before!? //save signature status signingStatus[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status]; @@ -131,6 +135,7 @@ NSDictionary* extractSigningInfo(NSString* path) //check signature status = SecStaticCodeCheckValidityWithErrors(staticCode, kSecCSDoNotValidateResources, NULL, NULL); + //TODO: called same above? //(re)save signature status signingStatus[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status]; diff --git a/VTInfoWindowController.m b/VTInfoWindowController.m index d95d826..606cdf2 100644 --- a/VTInfoWindowController.m +++ b/VTInfoWindowController.m @@ -262,7 +262,8 @@ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //make request to VT - result = [vtObj reScan:self.fileObj]; + //TODO: re-enable + //result = [vtObj reScan:self.fileObj]; //got result // ->update UI and launch browswer to show report @@ -296,11 +297,16 @@ // ->will update VT button in UI once results are retrieved if(nil != scanID) { + ////TODO: re-enable + + /* //kick off task to re-query VT // ->wait 60 seconds though to give VT servers some time to process dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 60 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [vtObj getInfoForItem:self.fileObj scanID:scanID rowIndex:self.rowIndex]; }); + + */ } //ask app delegate to update item in table @@ -368,7 +374,8 @@ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //submit file to VT - result = [vtObj submit:self.fileObj]; + //TODO: re-enable + //result = [vtObj submit:self.fileObj]; // ->need this for (re)queries scanID = result[VT_RESULTS_SCANID]; @@ -380,10 +387,13 @@ // ->will update VT button in UI once results are retrieved if(nil != scanID) { + //TODO: re-enable + /* //kick off task to re-query VT dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 60 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [vtObj getInfoForItem:self.fileObj scanID:scanID rowIndex:self.rowIndex]; }); + */ } //got response diff --git a/VirusTotal.h b/VirusTotal.h index a0f4ad2..8251a28 100644 --- a/VirusTotal.h +++ b/VirusTotal.h @@ -6,6 +6,7 @@ // Copyright (c) 2015 Objective-See. All rights reserved. // +#import "Binary.h" #import @interface VirusTotal : NSObject @@ -13,8 +14,18 @@ } +/* PROPERTIES */ + +//array for (up to 25) items +@property (nonatomic, retain)NSMutableArray* items; + + /* METHODS */ +//add item +// ->will query VT when 25 items are hit +-(void)addItem:(Binary*)binary; + //thread function // ->runs in the background to get virus total info about a plugin's items //-(void)getInfo:(PluginBase*)plugin; @@ -23,17 +34,17 @@ -(NSDictionary*)postRequest:(NSURL*)url parameters:(id)params; //submit a file to VT --(NSDictionary*)submit:(File*)fileObj; +//-(NSDictionary*)submit:(File*)fileObj; //submit a rescan request --(NSDictionary*)reScan:(File*)fileObj; +//-(NSDictionary*)reScan:(File*)fileObj; //process results // ->updates items (found, detection ratio, etc) --(void)processResults:(NSArray*)items results:(NSDictionary*)results; +-(void)processResults:(NSMutableDictionary*)queriedItems results:(NSDictionary*)results; //get info for a single item // ->will callback into AppDelegate to reload plugin --(void)getInfoForItem:(File*)fileObj scanID:(NSString*)scanID rowIndex:(NSUInteger)rowIndex; +//-(void)getInfoForItem:(File*)fileObj scanID:(NSString*)scanID rowIndex:(NSUInteger)rowIndex; @end diff --git a/VirusTotal.m b/VirusTotal.m index e9a5349..207ec8c 100644 --- a/VirusTotal.m +++ b/VirusTotal.m @@ -14,6 +14,118 @@ @implementation VirusTotal +@synthesize items; + +//init +-(id)init +{ + //init super + self = [super init]; + if(nil != self) + { + //alloc array for items + items = [NSMutableArray array]; + } + + return self; +} + +//add item +// ->will query VT when 25 items are hit +-(void)addItem:(Binary*)binary +{ + //sync + @synchronized(self.items) + { + //add item + [self.items addObject:binary]; + } + + //query VT once 25 items have been gathered + if(VT_MAX_QUERY_COUNT == self.items.count) + { + //process + [self queryVT]; + } +} + +//make query to VT +-(void)queryVT +{ + //VT query URL + NSURL* queryURL = nil; + + //item data + NSMutableDictionary* itemData = nil; + + //array of queried items + // ->needed so can save VT results back into binaries + NSMutableDictionary* queriedItems = nil; + + //parameters + NSMutableArray* parameters = nil; + + //results + NSDictionary* results = nil; + + //init query URL + queryURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", VT_QUERY_URL, VT_API_KEY]]; + + //alloc list for items + parameters = [NSMutableArray array]; + + //alloc dictionary for queried items + queriedItems = [NSMutableDictionary dictionary]; + + //sync + @synchronized(self.items) + { + + //add all binaries to VT query + for(Binary* item in self.items) + { + //alloc item data + itemData = [NSMutableDictionary dictionary]; + + //auto start location + itemData[@"autostart_location"] = @"n/a"; + + //set item name + itemData[@"autostart_entry"] = item.name; + + //set item path + itemData[@"image_path"] = item.path; + + //set hash + itemData[@"hash"] = item.hashes[KEY_HASH_SHA1]; + + //set creation times + itemData[@"creation_datetime"] = [item.attributes.fileCreationDate description]; + + //add item to parameters + [parameters addObject:itemData]; + + //save as queried item + queriedItems[item.hashes[KEY_HASH_SHA1]] = item; + } + + //remove all items + // ->since they've been added to VT request + [self.items removeAllObjects]; + + }//sync + + //make query to VT + results = [self postRequest:queryURL parameters:parameters]; + if(nil != results) + { + //process results + [self processResults:queriedItems results:results]; + } + + return; +} + /* //thread function // ->runs in the background to get virus total info about a plugin's items @@ -494,45 +606,68 @@ bail: } //process results -// ->save VT info into each File obj and all flagged files --(void)processResults:(NSArray*)items results:(NSDictionary*)results +// ->save VT info into +-(void)processResults:(NSMutableDictionary*)queriedItems results:(NSDictionary*)results { + //queried binary obj + Binary* queriedItem = nil; + + //flag for top pane reload + // ->will be set if any of the queried binaries are a task executable + BOOL reloadTopPane = NO; + + //flag for bottom pane reload + // ->will be set if any of the queried binaries are a dylib + BOOL reloadBottomPane = NO; + //process all results // ->save VT result dictionary into File obj for(NSDictionary* result in results[VT_RESULTS]) { - //sync - // ->since array will be reset if user clicks 'stop' scan - @synchronized(items) + //extract ('match') queried item + // ->VT gives us back a hash + queriedItem = queriedItems[result[@"hash"]]; + + //sanity check + if(nil == queriedItem) { - - //find all items that match - // ->might be dupes, which is fine - for(Binary* item in items) - { - //for matches, save vt info - if(YES == [result[@"hash"] isEqualToString:item.hashes[KEY_HASH_SHA1]]) - { - //save - item.vtInfo = result; - - //if its flagged save in File's plugin - if(0 != [result[VT_RESULTS_POSITIVES] unsignedIntegerValue]) - { - /* - //sync - // ->since array will be reset if user clicks 'stop' scan - @synchronized(item.plugin.flaggedItems) - { - //save - [item.plugin.flaggedItems addObject:item]; - } - */ - } - } + //skip + continue; } - - }//sync + + //save VT results into item + queriedItem.vtInfo = result; + + //for task executables + // ->set flag to reload top + if(YES == queriedItem.isTaskBinary) + { + //set + reloadTopPane = YES; + } + //for dylibs + // ->set flag to reload bottom + else + { + reloadBottomPane = YES; + } + + //TODO: do something with detections!? + //if(0 != [result[VT_RESULTS_POSITIVES] unsignedIntegerValue]) + } + + //reload top pane + if(YES == reloadTopPane) + { + //reload + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadTaskTable]; + } + + //reload bottom pane + if(YES == reloadBottomPane) + { + //reload + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadBottomPane:nil itemView:DYLIBS_VIEW]; } return;