From ff77a49ed5e892e6eef02e2d563e1722cbfd0a0f Mon Sep 17 00:00:00 2001 From: Patrick Wardle Date: Sun, 21 Jun 2015 21:37:52 -1000 Subject: [PATCH] largely completed virus total logic began implementing sorting logic (just for top pane) --- AppDelegate.h | 18 +- AppDelegate.m | 253 ++++++++++++++- ItemView.h | 6 +- ItemView.m | 159 ++++++++++ Items/Binary.h | 4 + Items/Binary.m | 2 +- Queue.m | 1 - Task.m | 13 +- .../xcshareddata/TaskExplorer.xccheckout | 8 +- .../xcdebugger/Breakpoints_v2.xcbkptlist | 298 +++++++++++++++--- TaskTableController.h | 12 +- TaskTableController.m | 276 +++++++++------- VTButton.h | 2 +- VTButton.m | 22 +- VTInfoWindowController.h | 6 +- VTInfoWindowController.m | 69 ++-- VirusTotal.h | 12 +- VirusTotal.m | 133 +++++--- en.lproj/MainMenu.xib | 9 +- remoteTaskService/remoteTaskService.m | 4 + 20 files changed, 1020 insertions(+), 287 deletions(-) diff --git a/AppDelegate.h b/AppDelegate.h index 2c5a6b4..a7b2007 100755 --- a/AppDelegate.h +++ b/AppDelegate.h @@ -31,8 +31,10 @@ } -//@property (readonly) NSViewController *currentViewController; -//@property(nonatomic, retain)NSViewController *currentViewController; +//'filter task' search box +// ->top pane +@property (weak) IBOutlet NSSearchField *filterTasksBox; + //(current) bottom view controller @property(nonatomic, retain)TaskTableController *bottomViewController; @@ -47,11 +49,6 @@ //task table controller object @property (nonatomic, retain)TaskTableController *taskTableController; -//tree (outline) controller -//@property (nonatomic, retain)TreeViewController* treeViewController; - -//array to hold binary objects that are in array -//@property (nonatomic, retain)NSMutableArray *tableContents; //current task view format // ->flat or tree @@ -72,6 +69,10 @@ @property (weak) IBOutlet NSView *bottomPane; +//'filter items' search box +// ->bottom pane +@property (weak) IBOutlet NSSearchField *filterItemsBox; + @property (assign) IBOutlet NSWindow *window; @property (weak) IBOutlet NSButton *logoButton; @@ -176,5 +177,8 @@ // ->either name (flat view) or pid (tree view) -(void)sortTasksForView:(OrderedDictionary*)tasks; +//filter tasks +-(void)filterTasks:(NSString*)filterText; + @end diff --git a/AppDelegate.m b/AppDelegate.m index 3194ba5..83ff731 100755 --- a/AppDelegate.m +++ b/AppDelegate.m @@ -5,6 +5,7 @@ #import "Consts.h" #import "Binary.h" +#import "Connection.h" #import "Exception.h" #import "Utilities.h" #import "AppDelegate.h" @@ -341,6 +342,15 @@ } } + //always unset filter flag + self.taskTableController.isFiltered = NO; + + //always reset filter text + [self.filterTasksBox setStringValue:@""]; + + //set bottom view? + self.bottomPaneBtn.selectedSegment = DYLIBS_VIEW; + //add subview [self.topPane addSubview:[self.taskTableController view]]; @@ -353,20 +363,42 @@ //reload (to re-draw) a specific row in table -(void)reloadRow:(Task*)task item:(ItemBase*)item pane:(NSUInteger)pane { + //item's task + // ->will use current task if task arg is nil + __block Task* itemTask = nil; + //table view __block NSTableView* tableView = nil; //row __block NSUInteger row = 0; + //segment (for bottom pane + __block NSUInteger segmentTag = 0; + //run everything on main thread // ->ensures table view isn't changed out from under us.... dispatch_async(dispatch_get_main_queue(), ^{ + + //get item's task + // ->use passed in task if non-nil + if(nil != itemTask) + { + //set + itemTask = task; + } + //passed in task is nil + // ->use currently selected one + else + { + //set + itemTask = self.currentTask; + } //top table (pane) if(PANE_TOP == pane) { - //get row that task is loaded in + //top table view tableView = [((id)self.taskTableController) itemView]; //reloadItem @@ -375,7 +407,7 @@ { //get index where task is // TODO: doesn't account for filtering, etc!!! - row = [self.taskEnumerator.tasks indexOfKey:task.pid]; + row = [self.taskEnumerator.tasks indexOfKey:itemTask.pid]; if(NSNotFound == row) { //bail @@ -400,7 +432,7 @@ [tableView beginUpdates]; //reload - [(NSOutlineView*)tableView reloadItem:task]; + [(NSOutlineView*)tableView reloadItem:itemTask]; //end updates [tableView endUpdates]; @@ -408,10 +440,97 @@ } - - //TODO: bottom pane - - + //bottom pane + else + { + //bottom table view + tableView = [((id)self.bottomViewController) itemView]; + + //get segment tag + segmentTag = [[self.bottomPaneBtn selectedCell] tagForSegment:[self.bottomPaneBtn selectedSegment]]; + + //get item + // ->will bail if item isn't in (current) view, etc + switch(segmentTag) + { + //dylibs + case DYLIBS_VIEW: + + //make sure item class/type matches current view + if(YES != [item isKindOfClass:[Binary class]]) + { + //bail + goto bail; + } + + //sync + @synchronized(itemTask.dylibs) + { + //get row + row = [itemTask.dylibs indexOfObject:item]; + } + + break; + + //files + case FILES_VIEW: + + //make sure item class/type matches current view + if(YES != [item isKindOfClass:[File class]]) + { + //bail + goto bail; + } + + //sync + @synchronized(itemTask.files) + { + //get row + row = [itemTask.files indexOfObject:item]; + } + + break; + + //networking + case NETWORKING_VIEW: + + //make sure item class/type matches current view + if(YES != [item isKindOfClass:[Connection class]]) + { + //bail + goto bail; + } + //sync + @synchronized(itemTask.connections) + { + //get row + row = [itemTask.connections indexOfObject:item]; + } + + break; + + default: + break; + } + + //make sure item was found + if(NSNotFound == row) + { + //bail + goto bail; + } + + + //begin updates + [tableView beginUpdates]; + + //reload row + [tableView reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:(row)] columnIndexes:[NSIndexSet indexSetWithIndex:0]]; + + //end updates + [tableView endUpdates]; + } + //bail bail: ; @@ -472,6 +591,7 @@ bail: //set table items self.bottomViewController.tableItems = self.currentTask.connections; + break; default: @@ -486,7 +606,7 @@ bail: { //reload // ->in main UI thread - dispatch_sync(dispatch_get_main_queue(), ^{ + dispatch_async(dispatch_get_main_queue(), ^{ [self finalizeBottomReload]; }); @@ -500,7 +620,7 @@ bail: -(void)finalizeBottomReload { - //stop progress indicator + //stop progress indicator [self.bottomPaneSpinner stopAnimation:nil]; //invoke refresh @@ -1299,6 +1419,21 @@ bail: //save selected view self.taskViewFormat = [[sender selectedCell] tag]; + //flat view + // ->enable 'filter tasks' search field + if(FLAT_VIEW == self.taskViewFormat) + { + //enable + self.filterTasksBox.enabled = YES; + } + //tree view + // ->disable 'filter tasks' search field + else + { + //disable + self.filterTasksBox.enabled = NO; + } + //switch (top) view/pane [self changeViewController]; @@ -1390,4 +1525,104 @@ bail: return; } +//automatically invoked when user enters text in filter search boxes +// ->filter tasks and/or items +- (void)controlTextDidChange:(NSNotification *)aNotification +{ + //search text + NSTextView* search = nil; + + //extract search (text) view + search = aNotification.userInfo[@"NSFieldEditor"]; + + //sanity check + if(nil == search) + { + //bail + goto bail; + } + + //top pane + if(YES == [aNotification.object isEqualTo:self.filterTasksBox]) + { + //when text is reset + // ->just reset flag + if(0 == search.string.length) + { + //set flag + self.taskTableController.isFiltered = NO; + } + //filter tasks + else + { + //filter + [self filterTasks:search.string]; + + //set flag + self.taskTableController.isFiltered = YES; + } + + //always reload task table + [self.taskTableController.itemView reloadData]; + } + //bottom pane + else if(YES == [aNotification.object isEqualTo:self.filterItemsBox]) + { + + } + + + + //NSLog(@"changed!"); + +//bail +bail: + + return; + +} + +//filter tasks +// ->for now, just name & path +// TODO filter on everything +// TODO: move into task enumerator!? +-(void)filterTasks:(NSString*)filterText +{ + //task + Task* task = nil; + + //name range + NSRange nameRange = {0}; + + //path range + NSRange pathRange = {0}; + + //first reset filter'd items + [self.taskTableController.filteredItems removeAllObjects]; + + //iterate over all tasks + for(NSNumber* taskKey in self.taskEnumerator.tasks) + { + //extract task + task = self.taskEnumerator.tasks[taskKey]; + + //init name range + nameRange = [task.binary.name rangeOfString:filterText options:NSCaseInsensitiveSearch]; + + //init path range + pathRange = [task.binary.path rangeOfString:filterText options:NSCaseInsensitiveSearch]; + + //check for match + if( (NSNotFound != nameRange.location) || + (NSNotFound != pathRange.location) ) + { + //save match + [self.taskTableController.filteredItems addObject:task]; + } + + }//all tasks + + return; +} + @end diff --git a/ItemView.h b/ItemView.h index 76b1da4..f14acfd 100644 --- a/ItemView.h +++ b/ItemView.h @@ -36,4 +36,8 @@ void addTrackingArea(NSTableCellView* itemView, NSUInteger subviewTag, id owner) //set code signing image // ->either signed, unsigned, or unknown -NSImage* getCodeSigningIcon(Binary* binary); \ No newline at end of file +NSImage* getCodeSigningIcon(Binary* binary); + +//configure the VT button +// ->also set's binary name to red if known malware +void configVTButton(NSTableCellView *itemCell, id owner, Binary* binary); \ No newline at end of file diff --git a/ItemView.m b/ItemView.m index 669d621..0bcff19 100644 --- a/ItemView.m +++ b/ItemView.m @@ -573,6 +573,9 @@ NSTableCellView* createTaskView(NSTableView* tableView, id owner, Task* task) //set path [[taskCell viewWithTag:TABLE_ROW_SUB_TEXT_TAG] setStringValue:task.binary.path]; + + //config VT button + configVTButton(taskCell, owner, task.binary); //bail bail: @@ -625,6 +628,9 @@ NSTableCellView* createDylibView(NSTableView* tableView, id owner, Binary* dylib //set path [[dylibCell viewWithTag:TABLE_ROW_SUB_TEXT_TAG] setStringValue:dylib.path]; + //config VT button + configVTButton(dylibCell, owner, dylib); + //bail bail: @@ -786,6 +792,159 @@ bail: } +//configure the VT button +// ->also set's binary name to red if known malware +void configVTButton(NSTableCellView *itemCell, id owner, Binary* binary) +{ + //virus total button + // ->for File objects only... + VTButton* vtButton; + + //paragraph style + NSMutableParagraphStyle *paragraphStyle = nil; + + //attribute dictionary + NSMutableDictionary *stringAttributes = nil; + + //VT detection ratio as string + NSString* vtDetectionRatio = nil; + + //grab virus total button + vtButton = [itemCell viewWithTag:TABLE_ROW_VT_BUTTON]; + + + //[itemCell viewWithTag:TABLE_ROW_VT_BUTTON]; + + //configure/show VT info + // ->only if 'disable' preference not set + //if(YES != ((AppDelegate*)[[NSApplication sharedApplication] delegate]).prefsWindowController.disableVTQueries) + //{ + //set button delegate + vtButton.delegate = owner; + + //save file obj + vtButton.binary = binary; + + //check if have vt results + if(nil != binary.vtInfo) + { + //set font + [vtButton setFont:[NSFont fontWithName:@"Menlo-Bold" size:12]]; + + //enable + vtButton.enabled = YES; + + //got VT results + // ->check 'permalink' to determine if file is known to VT + // then, show ratio and set to red if file is flagged + if(nil != binary.vtInfo[VT_RESULTS_URL]) + { + //alloc paragraph style + paragraphStyle = [[NSMutableParagraphStyle alloc] init]; + + //center the text + [paragraphStyle setAlignment:NSCenterTextAlignment]; + + //alloc attributes dictionary + stringAttributes = [NSMutableDictionary dictionary]; + + //set underlined attribute + stringAttributes[NSUnderlineStyleAttributeName] = @(NSUnderlineStyleSingle); + + //set alignment (center) + stringAttributes[NSParagraphStyleAttributeName] = paragraphStyle; + + //set font + stringAttributes[NSFontAttributeName] = [NSFont fontWithName:@"Menlo-Bold" size:12]; + + //compute detection ratio + vtDetectionRatio = [NSString stringWithFormat:@"%lu/%lu", (unsigned long)[binary.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue], (unsigned long)[binary.vtInfo[VT_RESULTS_TOTAL] unsignedIntegerValue]]; + + //known 'good' files (0 positivies) + if(0 == [binary.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) + { + //(re)set title black + itemCell.textField.textColor = [NSColor blackColor]; + + //set color (black) + stringAttributes[NSForegroundColorAttributeName] = [NSColor blackColor]; + + //set string (vt ratio), with attributes + [vtButton setAttributedTitle:[[NSAttributedString alloc] initWithString:vtDetectionRatio attributes:stringAttributes]]; + + //set color (gray) + stringAttributes[NSForegroundColorAttributeName] = [NSColor grayColor]; + + //set selected text color + [vtButton setAttributedAlternateTitle:[[NSAttributedString alloc] initWithString:vtDetectionRatio attributes:stringAttributes]]; + } + //files flagged by VT + // ->set name and detection to red + else + { + //set title red + itemCell.textField.textColor = [NSColor redColor]; + + //set color (red) + stringAttributes[NSForegroundColorAttributeName] = [NSColor redColor]; + + //set string (vt ratio), with attributes + [vtButton setAttributedTitle:[[NSAttributedString alloc] initWithString:vtDetectionRatio attributes:stringAttributes]]; + + //set selected text color + [vtButton setAttributedAlternateTitle:[[NSAttributedString alloc] initWithString:vtDetectionRatio attributes:stringAttributes]]; + + } + + //enable + [vtButton setEnabled:YES]; + } + + //file is not known + // ->reset title to '?' + else + { + //set title + [vtButton setTitle:@"?"]; + } + } + + //no VT results (e.g. unknown file) + else + { + //set font + [vtButton setFont:[NSFont fontWithName:@"Menlo-Bold" size:8]]; + + //set title + [vtButton setTitle:@"▪ ▪ ▪"]; + + //disable + vtButton.enabled = NO; + } + + //show virus total button + vtButton.hidden = NO; + + //show virus total label + //[[itemCell viewWithTag:TABLE_ROW_VT_BUTTON+1] setHidden:NO]; + + //}//show VT info (pref not disabled) + + /* + //hide VT info + else + { + //hide virus total button + vtButton.hidden = YES; + + //hide virus total button label + [[itemCell viewWithTag:TABLE_ROW_VT_BUTTON+1] setHidden:YES]; + } + */ + + return; +} + diff --git a/Items/Binary.h b/Items/Binary.h index 6e6fca4..34cd5cb 100644 --- a/Items/Binary.h +++ b/Items/Binary.h @@ -41,6 +41,10 @@ /* VIRUS TOTAL INFO */ +//flag saying item is 'last' +// ->thus VT items should all be queried +@property BOOL lastItem; + //dictionary returned by VT @property (nonatomic, retain)NSDictionary* vtInfo; diff --git a/Items/Binary.m b/Items/Binary.m index 8dcd4fa..3a17d38 100644 --- a/Items/Binary.m +++ b/Items/Binary.m @@ -23,7 +23,7 @@ @synthesize isTaskBinary; @synthesize vtInfo; - +@synthesize lastItem; //init method -(id)initWithParams:(NSDictionary*)params diff --git a/Queue.m b/Queue.m index dd0c175..6e36b85 100644 --- a/Queue.m +++ b/Queue.m @@ -90,7 +90,6 @@ //add item for VT processing [vtObject addItem:binary]; - //unlock [self.queueCondition unlock]; diff --git a/Task.m b/Task.m index 8cfd0b6..99cda40 100644 --- a/Task.m +++ b/Task.m @@ -110,22 +110,17 @@ goto bail; } + //indicate that binary is a task (main) executable + self.binary.isTaskBinary = YES; + //add to queue // ->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; - } - //indicate that binary is a task (main) executable - //self.binary.isTaskBinary = YES; - }//init self //bail @@ -404,7 +399,7 @@ bail: //add to task's dylibs [self.dylibs addObject:dylib]; } - } + } //all dylibs //sync to sort @synchronized(self.dylibs) diff --git a/TaskExplorer.xcodeproj/project.xcworkspace/xcshareddata/TaskExplorer.xccheckout b/TaskExplorer.xcodeproj/project.xcworkspace/xcshareddata/TaskExplorer.xccheckout index a240ad9..72b1950 100644 --- a/TaskExplorer.xcodeproj/project.xcworkspace/xcshareddata/TaskExplorer.xccheckout +++ b/TaskExplorer.xcodeproj/project.xcworkspace/xcshareddata/TaskExplorer.xccheckout @@ -10,14 +10,14 @@ TaskExplorer IDESourceControlProjectOriginsDictionary - A9FFC1B124E25027A0E10FB456DBA9E2803FAE9D + 61F07AFB33748EF0C810BEEF6126283DAC63A899 https://bitbucket.org/objective-see/knockknock.git IDESourceControlProjectPath TaskExplorer.xcodeproj IDESourceControlProjectRelativeInstallPathDictionary - A9FFC1B124E25027A0E10FB456DBA9E2803FAE9D + 61F07AFB33748EF0C810BEEF6126283DAC63A899 ../.. IDESourceControlProjectURL @@ -25,14 +25,14 @@ IDESourceControlProjectVersion 111 IDESourceControlProjectWCCIdentifier - A9FFC1B124E25027A0E10FB456DBA9E2803FAE9D + 61F07AFB33748EF0C810BEEF6126283DAC63A899 IDESourceControlProjectWCConfigurations IDESourceControlRepositoryExtensionIdentifierKey public.vcs.git IDESourceControlWCCIdentifierKey - A9FFC1B124E25027A0E10FB456DBA9E2803FAE9D + 61F07AFB33748EF0C810BEEF6126283DAC63A899 IDESourceControlWCCName TaskExplorer diff --git a/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 2ddd5ee..e054392 100644 --- a/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -20,11 +20,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "TaskEnumerator.m" - timestampString = "456469787.454434" + timestampString = "456607885.071145" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "343" - endingLineNumber = "343" + startingLineNumber = "344" + endingLineNumber = "344" landmarkName = "-removeTask:" landmarkType = "5"> @@ -36,11 +36,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "456541711.785304" + timestampString = "456648442.982284" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "661" - endingLineNumber = "661" + startingLineNumber = "775" + endingLineNumber = "775" landmarkName = "-reloadTaskTable" landmarkType = "5"> @@ -52,27 +52,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "TaskTableController.m" - timestampString = "456474649.232255" + timestampString = "456648442.982284" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "711" - endingLineNumber = "711" - landmarkName = "-refresh" - landmarkType = "5"> - - - - @@ -84,11 +68,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "TaskTableController.m" - timestampString = "456474649.232255" + timestampString = "456648442.982284" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "1115" - endingLineNumber = "1115" + startingLineNumber = "1167" + endingLineNumber = "1167" landmarkName = "-outlineView:viewForTableColumn:item:" landmarkType = "5"> @@ -100,11 +84,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "TaskTableController.m" - timestampString = "456474649.232255" + timestampString = "456648442.982284" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "1105" - endingLineNumber = "1105" + startingLineNumber = "1157" + endingLineNumber = "1157" landmarkName = "-outlineViewSelectionDidChange:" landmarkType = "5"> @@ -132,11 +116,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "TaskTableController.m" - timestampString = "456521744.657881" + timestampString = "456648442.982284" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "934" - endingLineNumber = "934" + startingLineNumber = "1000" + endingLineNumber = "1000" landmarkName = "-showInfo:" landmarkType = "5"> @@ -148,11 +132,11 @@ ignoreCount = "0" continueAfterRunningActions = "No" filePath = "AppDelegate.m" - timestampString = "456521776.780775" + timestampString = "456621956.395811" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "317" - endingLineNumber = "317" + startingLineNumber = "318" + endingLineNumber = "318" landmarkName = "-changeViewController" landmarkType = "5"> @@ -167,5 +151,245 @@ moduleName = ""> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TaskTableController.h b/TaskTableController.h index 8bb5bba..c8c21a3 100644 --- a/TaskTableController.h +++ b/TaskTableController.h @@ -23,12 +23,18 @@ //flag for first time init's @property BOOL didInit; -//tasks -// ->updated by task enumerator -//@property(nonatomic, retain)OrderedDictionary* tasks; +//flag for ignoring automated row selections +@property BOOL ignoreSelection; +//flag for filtering +@property BOOL isFiltered; + +//all table items @property(nonatomic, retain)NSMutableArray* tableItems; +//filtered table items +@property(nonatomic, retain)NSMutableArray* filteredItems; + //category table view @property(weak) IBOutlet NSTableView *itemView; diff --git a/TaskTableController.m b/TaskTableController.m index ddbda8b..f4c9e5c 100644 --- a/TaskTableController.m +++ b/TaskTableController.m @@ -6,7 +6,7 @@ // Copyright (c) 2015 Objective-See. All rights reserved. // -//TODO: list of what's running on my Mac!!!! +//TODO: list of what's running on my Mac on website!!!! #import "Binary.h" #import "Consts.h" @@ -30,59 +30,39 @@ @implementation TaskTableController @synthesize itemView; +@synthesize isFiltered; @synthesize tableItems; @synthesize selectedRow; @synthesize isBottomPane; +@synthesize filteredItems; +@synthesize ignoreSelection; @synthesize vtWindowController; @synthesize infoWindowController; @synthesize didInit; -//@synthesize tasks; - -//TODO: called many timesss -(void)awakeFromNib { - + //single time init if(YES != didInit) { + //init selected row self.selectedRow = -1; - //for outline (tree) view - // ->expand - if(YES == [self.itemView isKindOfClass:[NSOutlineView class]]) - { - [(NSOutlineView*)self.itemView expandItem:nil expandChildren:YES]; - } + //alloc array for filtered items + filteredItems = [NSMutableArray array]; + + //extand tree view + if(YES == [self.itemView isKindOfClass:[NSOutlineView class]]) + { + //expand + [(NSOutlineView*)self.itemView expandItem:nil expandChildren:YES]; + } //set flag self.didInit = YES; - } - //self.selectedRow = -1; - - /* - //for outline (tree) view - // ->expand - if(YES == [self.itemView isKindOfClass:[NSOutlineView class]]) - { - [(NSOutlineView*)self.itemView expandItem:nil expandChildren:YES]; - - //TODO: do this in IB? - //[itemView setTarget:self]; - } - */ - - - /* - NSString *title = @"[ all tasks ]"; - NSTableColumn *yourColumn = self.itemView.tableColumns.lastObject; - [yourColumn.headerCell setStringValue:title]; - - [self.itemView setIndicatorImage:[NSImage imageNamed:@"NSDescendingSortIndicator"] inTableColumn:self.itemView.tableColumns.lastObject]; - - */ } @@ -115,13 +95,26 @@ // ->use tasks from enumerator if(YES != self.isBottomPane) { - //grab tasks - tasks = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks; + //when not filtered + // ->use tasks + if(YES != isFiltered) + { + //get tasks + tasks = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks; - //set row count - rows = tasks.count; + //set count + rows = tasks.count; + } + //when filtered + // ->use filtered items + else + { + //set count + rows = self.filteredItems.count; + } } - //bottom pane uses table items + //bottom pane uses 'tableItems' iVar + //TODO: filter support else { //set row count @@ -130,14 +123,19 @@ return rows; - } +} //automatically invoked when user selects row // ->only care about for top pane, trigger load bottom view -(void)tableViewSelectionDidChange:(NSNotification *)notification { - //handle selection - [self handleRowSelection]; + //only handle user selections + // ->not those from 'reloadData' + if(YES != self.ignoreSelection) + { + //handle selection + [self handleRowSelection]; + } return; } @@ -157,23 +155,45 @@ NSView* rowView = nil; //for TOP PANE - // ->use tasks from enumerator + // ->use tasks from enumerator or filtered items if(YES != self.isBottomPane) { - //grab tasks - tasks = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks; - - //sanity check - // ->make sure there is table item for row - if(tasks.count <= row) + //when not filtered + // ->use tasks + if(YES != isFiltered) { - //bail - goto bail; + //grab tasks + tasks = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks; + + //sanity check + // ->make sure there is table item for row + if(tasks.count <= row) + { + //bail + goto bail; + } + + //get task object + // ->by index to get key, then by key + item = tasks[[tasks keyAtIndex:row]]; + } - //get task object - // ->by index to get key, then by key - item = tasks[[tasks keyAtIndex:row]]; + //when filtered + // ->use filtered items + else + { + //sanity check + // ->make sure there is table item for row + if(self.filteredItems.count <= row) + { + //bail + goto bail; + } + + //get task object + item = self.filteredItems[row]; + } } //for BOTTOM PANE @@ -725,9 +745,15 @@ bail: //get task selectedTask = [self taskForRow:nil]; + //ignore selection change though + self.ignoreSelection = YES; + //always reload [self.itemView reloadData]; + //don't ignore selection + self.ignoreSelection = NO; + //when an item was selected // ->get its index and make sure that's still selected if(nil != selectedTask) @@ -749,6 +775,40 @@ bail: [self.itemView endUpdates]; } } + /* + //otherwise select first row + else + { + //selected row cell + NSTableCellView* rowView = nil; + + //default to first row + self.selectedRow = 0; + + //sanity check + if(0 == [self numberOfRowsInTableView:self.itemView]) + { + //bail + goto bail; + } + + //get first row + rowView = [self.itemView viewAtColumn:0 row:0 makeIfNecessary:YES]; + + //extract task + // ->pid of task is view's id :) + Task* task = tasks[[NSNumber numberWithInteger:(rowView.tag - PID_TAG_DELTA)]]; + + //save task + ((AppDelegate*)[[NSApplication sharedApplication] delegate]).currentTask = task; + + //reload bottom pane + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) selectBottomPaneContent:nil]; + } + */ + +//bail +bail: return; } @@ -784,28 +844,34 @@ bail: { //grab row taskRow = [self.itemView selectedRow]; - } + //TODO: add check for filterItems.count //sanity check(s) - // ->make sure row has item + // ->make sure row is decent if( (-1 == taskRow) || - (tasks.count < taskRow) ) + ((YES != self.isFiltered) && (tasks.count < taskRow)) || + ((YES == self.isFiltered) && (self.filteredItems.count < taskRow)) ) { //bail goto bail; } - //get task object - // ->by index to get key, then by key - //task = tasks[[tasks keyAtIndex:taskRow]]; - //get row that's about to be selected rowView = [self.itemView viewAtColumn:0 row:taskRow makeIfNecessary:YES]; - //extract task - // ->pid of task is view's id :) - task = tasks[[NSNumber numberWithInteger:(rowView.tag - PID_TAG_DELTA)]]; + //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[ + //} //bail bail: @@ -966,54 +1032,40 @@ bail: //invoked when the user clicks 'virus total' icon // ->launch browser and browse to virus total's page --(void)showVTInfo:(NSView*)button +-(void)showVTInfo:(id)sender { - //array backing table - NSArray* tableItems = nil; + //item + // ->task, dylib, file, etc + Binary* item = nil; - //selected item - File* selectedItem = nil; + //for top pane + // ->get task + if(YES != self.isBottomPane) + { + //get task + item = [(Task*)[self taskForRow:sender] binary]; + } + + //bottom pane + else + { + //get item + item = (Binary*)[self itemForRow:sender]; + } - //row that button was clicked on - NSUInteger rowIndex = -1; - - //get row index - rowIndex = [self.itemView rowForView:button]; - - //grab item table items - tableItems = [self getTableItems]; - - //sanity check - // ->make sure row has item - if(tableItems.count < rowIndex) + //bail on nil items + if(nil == item) { //bail goto bail; } - - //sanity check - if(-1 != rowIndex) - { - //extract selected item - // ->invoke helper function to get array backing table - selectedItem = tableItems[rowIndex]; - - //alloc/init info window - vtWindowController = [[VTInfoWindowController alloc] initWithItem:selectedItem rowIndex:rowIndex]; - - //show it - [self.vtWindowController.windowController showWindow:self]; - - /* - //make it modal - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - - //modal! - [[NSApplication sharedApplication] runModalForWindow:vtWindowController.windowController.window]; - - }); - */ - } + + //alloc/init info window + vtWindowController = [[VTInfoWindowController alloc] initWithItem:item]; + + //show it + [self.vtWindowController.windowController showWindow:self]; + //bail bail: @@ -1177,7 +1229,8 @@ bail: //sanity check if( (-1 == newlySelectedRow) || - (newlySelectedRow >= tasks.count) ) + ((YES != self.isFiltered) && (newlySelectedRow >= tasks.count)) || + ((YES == self.isFiltered) && (newlySelectedRow >= self.filteredItems.count)) ) { //bail goto bail; @@ -1241,9 +1294,9 @@ bail: } - - //ignore if row selection didn't change - if([self.itemView selectedRow] == self.selectedRow) + //ignore if row selection and task didn't change + if( ([self.itemView selectedRow] == self.selectedRow) && + (((AppDelegate*)[[NSApplication sharedApplication] delegate]).currentTask == task) ) { //ignore goto bail; @@ -1262,7 +1315,8 @@ bail: //bail bail: - ; + + return; } diff --git a/VTButton.h b/VTButton.h index a5c7f7b..4254389 100644 --- a/VTButton.h +++ b/VTButton.h @@ -24,7 +24,7 @@ @property(assign)TaskTableController *delegate; //File object -@property(nonatomic, retain)Binary* fileObj; +@property(nonatomic, retain)Binary* binary; //button's row index diff --git a/VTButton.m b/VTButton.m index 8daa00b..672499f 100644 --- a/VTButton.m +++ b/VTButton.m @@ -13,7 +13,7 @@ @implementation VTButton -@synthesize fileObj; +@synthesize binary; @synthesize delegate; @synthesize mouseDown; @synthesize mouseExit; @@ -53,8 +53,8 @@ //flagged files // ->make em red! - if( (nil != self.fileObj.vtInfo) && - (0 != [self.fileObj.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) + if( (nil != self.binary.vtInfo) && + (0 != [self.binary.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) { //set color (light red) color = [NSColor colorWithCalibratedRed:(255/255.0f) green:(1.0/255.0f) blue:(1.0/255.0f) alpha:0.5]; @@ -94,8 +94,8 @@ //flagged files // ->make em red! - if( (nil != self.fileObj.vtInfo) && - (0 != [self.fileObj.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) + if( (nil != self.binary.vtInfo) && + (0 != [self.binary.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) { //set color (light red) color = [NSColor redColor]; @@ -126,8 +126,8 @@ //flagged files // ->make em red! - if( (nil != self.fileObj.vtInfo) && - (0 != [self.fileObj.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) + if( (nil != self.binary.vtInfo) && + (0 != [self.binary.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) { //set color (lightish red) color = [NSColor colorWithCalibratedRed:(255/255.0f) green:(1.0/255.0f) blue:(1.0/255.0f) alpha:0.66]; @@ -162,8 +162,8 @@ { //flagged files // ->make em red! - if( (nil != self.fileObj.vtInfo) && - (0 != [self.fileObj.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) + if( (nil != self.binary.vtInfo) && + (0 != [self.binary.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) { //set color (lightish red) color = [NSColor colorWithCalibratedRed:(255/255.0f) green:(1.0/255.0f) blue:(1.0/255.0f) alpha:0.66]; @@ -182,8 +182,8 @@ { //flagged files // ->make em red! - if( (nil != self.fileObj.vtInfo) && - (0 != [self.fileObj.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) + if( (nil != self.binary.vtInfo) && + (0 != [self.binary.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) { //set color (light red) color = [NSColor redColor]; diff --git a/VTInfoWindowController.h b/VTInfoWindowController.h index 42bd308..c563e20 100644 --- a/VTInfoWindowController.h +++ b/VTInfoWindowController.h @@ -22,10 +22,10 @@ @property(nonatomic, strong)VTInfoWindowController *windowController; //file object -@property(nonatomic, retain)Binary* fileObj; +@property(nonatomic, retain)Binary* item; //row index -@property NSUInteger rowIndex; +//@property NSUInteger rowIndex; //properties in window @property (weak) IBOutlet NSTextField *unknownFile; @@ -50,7 +50,7 @@ //init method // ->save item and load nib --(id)initWithItem:(File*)selectedItem rowIndex:(NSUInteger)itemRowIndex; +-(id)initWithItem:(Binary*)binary; //'submit' button handler -(IBAction)vtButtonHandler:(id)sender; diff --git a/VTInfoWindowController.m b/VTInfoWindowController.m index 606cdf2..6c4f4ab 100644 --- a/VTInfoWindowController.m +++ b/VTInfoWindowController.m @@ -23,13 +23,13 @@ @implementation VTInfoWindowController -@synthesize rowIndex; +@synthesize item; @synthesize windowController; //init method // ->save item and load nib --(id)initWithItem:(File*)selectedItem rowIndex:(NSUInteger)itemRowIndex +-(id)initWithItem:(Binary*)binary { self = [super init]; if(nil != self) @@ -38,10 +38,10 @@ self.windowController = [[VTInfoWindowController alloc] initWithWindowNibName:@"VTInfoWindow"]; //save item - self.windowController.fileObj = selectedItem; + self.windowController.item = binary; //save row index - self.windowController.rowIndex = itemRowIndex; + //self.windowController.rowIndex = itemRowIndex; } return self; @@ -92,7 +92,7 @@ NSColor* textColor = nil; //get status - if(nil != self.fileObj.vtInfo[VT_RESULTS_URL]) + if(nil != self.item.vtInfo[VT_RESULTS_URL]) { //known isKnown = YES; @@ -105,17 +105,17 @@ textColor = [NSColor blackColor]; //set color to red if its flagged - if(0 != [self.fileObj.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) + if(0 != [self.item.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) { //red textColor = [NSColor redColor]; } //generate detection ratio - vtDetectionRatio = [NSString stringWithFormat:@"%lu/%lu", (unsigned long)[self.fileObj.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue], (unsigned long)[self.fileObj.vtInfo[VT_RESULTS_TOTAL] unsignedIntegerValue]]; + vtDetectionRatio = [NSString stringWithFormat:@"%lu/%lu", (unsigned long)[self.item.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue], (unsigned long)[self.item.vtInfo[VT_RESULTS_TOTAL] unsignedIntegerValue]]; //set name - [self.fileName setStringValue:self.fileObj.name]; + [self.fileName setStringValue:self.item.name]; //set color self.fileName.textColor = textColor; @@ -130,7 +130,7 @@ [self.analysisURL setStringValue:@"VirusTotal report"]; //make analyis url a hyperlink - makeTextViewHyperlink(self.analysisURL, [NSURL URLWithString:self.fileObj.vtInfo[VT_RESULTS_URL]]); + makeTextViewHyperlink(self.analysisURL, [NSURL URLWithString:self.item.vtInfo[VT_RESULTS_URL]]); //set 'submit' button text to 'rescan' self.submitButton.title = @"rescan?"; @@ -157,7 +157,7 @@ self.analysisURL.hidden = YES; //set unknown file msg - [self.unknownFile setStringValue:[NSString stringWithFormat:@"no results found for '%@'", self.fileObj.name]]; + [self.unknownFile setStringValue:[NSString stringWithFormat:@"no results found for '%@'", self.item.name]]; //show 'unknown file' msg self.unknownFile.hidden = NO; @@ -253,7 +253,7 @@ if(YES == [((NSButton*)sender).title isEqualToString:@"rescan?"]) { //set status msg - [self.statusMsg setStringValue:[NSString stringWithFormat:@"submitting re-scan request for %@", self.fileObj.name]]; + [self.statusMsg setStringValue:[NSString stringWithFormat:@"submitting re-scan request for %@", self.item.name]]; //show status msg self.statusMsg.hidden = NO; @@ -262,8 +262,8 @@ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //make request to VT - //TODO: re-enable - //result = [vtObj reScan:self.fileObj]; + // ->will also update UI to show '...' + result = [vtObj reScan:self.item]; //got result // ->update UI and launch browswer to show report @@ -273,6 +273,9 @@ // ->need this for (re)queries scanID = result[VT_RESULTS_SCANID]; + //TODO: do something w/ prev flagged files!? + // ...i don't think we'll keep a list~ + /* //if file was flagged @@ -290,29 +293,18 @@ */ - //remove file's VT info (since it'd now out of date) - self.fileObj.vtInfo = nil; - //with a scan id can re-query VT // ->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]; + [vtObj getInfoForItem:self.item scanID:scanID]; }); - - */ + } - //ask app delegate to update item in table - // ->will change the item's VT status to ... (pending) - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) itemProcessed:self.fileObj rowIndex:self.rowIndex]; - //nap so user can see msg 'submitting' msg [NSThread sleepForTimeInterval:0.5]; @@ -340,7 +332,6 @@ [self.window close]; }); - } //error @@ -359,13 +350,13 @@ } }); - } + } //rescan file //submit file else { //set status msg - [self.statusMsg setStringValue:[NSString stringWithFormat:@"submitting %@", self.fileObj.name]]; + [self.statusMsg setStringValue:[NSString stringWithFormat:@"submitting %@", self.item.name]]; //show status msg self.statusMsg.hidden = NO; @@ -374,36 +365,26 @@ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //submit file to VT - //TODO: re-enable - //result = [vtObj submit:self.fileObj]; + // ->will also update UI to show '...' + result = [vtObj submit:self.item]; // ->need this for (re)queries scanID = result[VT_RESULTS_SCANID]; - //reset file's VT info - self.fileObj.vtInfo = nil; - //with a scan id can query VT // ->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]; + [vtObj getInfoForItem:self.item scanID:scanID]; }); - */ } //got response - // ->update UI and launch browswer to show report + // ->launch browswer to show report if(nil != result) { - //ask app delegate to update item in table - // ->will change the item's VT status to ... (pending) - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) itemProcessed:self.fileObj rowIndex:self.rowIndex]; - //update status msg dispatch_sync(dispatch_get_main_queue(), ^{ @@ -447,7 +428,7 @@ } }); - } + } //submit file return; } diff --git a/VirusTotal.h b/VirusTotal.h index 8251a28..0b789a7 100644 --- a/VirusTotal.h +++ b/VirusTotal.h @@ -34,17 +34,21 @@ -(NSDictionary*)postRequest:(NSURL*)url parameters:(id)params; //submit a file to VT -//-(NSDictionary*)submit:(File*)fileObj; +-(NSDictionary*)submit:(Binary*)item; //submit a rescan request -//-(NSDictionary*)reScan:(File*)fileObj; +-(NSDictionary*)reScan:(Binary*)item; //process results // ->updates items (found, detection ratio, etc) -(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; +// ->will callback into AppDelegate to reload item +-(void)getInfoForItem:(Binary*)item scanID:(NSString*)scanID; + +//call back up to update item in UI +// ->will either reload task table (top), or just row in item (bottom) table +-(void)updateUI:(Binary*)item; @end diff --git a/VirusTotal.m b/VirusTotal.m index 207ec8c..eb94a48 100644 --- a/VirusTotal.m +++ b/VirusTotal.m @@ -30,6 +30,7 @@ return self; } +//TODO: move this in Queue? //add item // ->will query VT when 25 items are hit -(void)addItem:(Binary*)binary @@ -42,22 +43,26 @@ } //query VT once 25 items have been gathered - if(VT_MAX_QUERY_COUNT == self.items.count) + // ->or this is a 'last' item + if( (VT_MAX_QUERY_COUNT == self.items.count) || + (YES == binary.lastItem) ) { - //process - [self queryVT]; + //kick of thread to make a query to VT + [NSThread detachNewThreadSelector:@selector(queryVT) toTarget:self withObject:nil]; } + + return; } //make query to VT -(void)queryVT { - //VT query URL - NSURL* queryURL = nil; - //item data NSMutableDictionary* itemData = nil; + //VT query URL + NSURL* queryURL = nil; + //array of queried items // ->needed so can save VT results back into binaries NSMutableDictionary* queriedItems = nil; @@ -67,9 +72,6 @@ //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]; @@ -77,6 +79,9 @@ //alloc dictionary for queried items queriedItems = [NSMutableDictionary dictionary]; + //init query URL + queryURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", VT_QUERY_URL, VT_API_KEY]]; + //sync @synchronized(self.items) { @@ -84,6 +89,14 @@ //add all binaries to VT query for(Binary* item in self.items) { + //skip items with blank hashes + // ->TODO not sure why this would happen + if(nil == item.hashes[KEY_HASH_SHA1]) + { + //skip + continue; + } + //alloc item data itemData = [NSMutableDictionary dictionary]; @@ -115,6 +128,7 @@ }//sync + //make query to VT results = [self postRequest:queryURL parameters:parameters]; if(nil != results) @@ -122,6 +136,7 @@ //process results [self processResults:queriedItems results:results]; } + return; } @@ -272,9 +287,10 @@ return; } */ + //get VT info for a single item // ->will then callback into AppDelegate to reload item in UI --(void)getInfoForItem:(Binary*)fileObj scanID:(NSString*)scanID rowIndex:(NSUInteger)rowIndex +-(void)getInfoForItem:(Binary*)item scanID:(NSString*)scanID { //VT query URL NSURL* queryURL = nil; @@ -296,8 +312,9 @@ (1 == [results[VT_RESULTS_RESPONSE] integerValue]) ) { //save result - fileObj.vtInfo = results; + item.vtInfo = results; + //TODO: do something if it's flagged! //if its flagged save in File's plugin if(0 != [results[VT_RESULTS_POSITIVES] unsignedIntegerValue]) { @@ -310,10 +327,12 @@ [fileObj.plugin.flaggedItems addObject:fileObj]; } */ + } - //callback up into UI to reload item - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) itemProcessed:fileObj rowIndex:rowIndex]; + //update UI + // ->will make item in task or dylib table have updated VT results + [self updateUI:item]; //exit loop break; @@ -326,6 +345,7 @@ return; } + //make the (POST)query to VT -(NSDictionary*)postRequest:(NSURL*)url parameters:(id)params { @@ -347,7 +367,7 @@ //response (HTTP) from VT NSURLResponse* httpResponse = nil; - + //alloc/init request request = [[NSMutableURLRequest alloc] initWithURL:url]; @@ -399,8 +419,8 @@ //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); @@ -443,7 +463,7 @@ bail: } //submit a file to VT --(NSDictionary*)submit:(File*)fileObj +-(NSDictionary*)submit:(Binary*)item { //results NSDictionary* results = nil; @@ -468,15 +488,23 @@ bail: //response (HTTP) from VT NSURLResponse* httpResponse = nil; + + //remove item's vt info + // ->as its about to be outdates + item.vtInfo = nil; + + //reload UI + // ->will change item's VT button back to ... + [self updateUI:item]; //init submit URL - submitURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@?apikey=%@&resource=%@", VT_SUBMIT_URL, VT_API_KEY, fileObj.hashes[KEY_HASH_MD5]]]; + submitURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@?apikey=%@&resource=%@", VT_SUBMIT_URL, VT_API_KEY, item.hashes[KEY_HASH_MD5]]]; //init request request = [[NSMutableURLRequest alloc] initWithURL:submitURL]; //set boundary string - NSString *boundary = @"qqqq___knockknock___qqqq"; + NSString *boundary = @"qqqq___taskexplorer___qqqq"; //set HTTP method (POST) [request setHTTPMethod:@"POST"]; @@ -491,13 +519,13 @@ bail: body = [NSMutableData data]; //load file into memory - fileContents = [NSData dataWithContentsOfFile:fileObj.pathForFinder]; + fileContents = [NSData dataWithContentsOfFile:item.pathForFinder]; //sanity check if(nil == fileContents) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: failed to load %@ into memory for submission", fileObj.path); + NSLog(@"OBJECTIVE-SEE ERROR: failed to load %@ into memory for submission", item.path); //bail goto bail; @@ -507,7 +535,7 @@ bail: [body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; //append 'Content-Disposition' file name, etc - [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"file\"; filename=\"%@\"\r\n", fileObj.name] dataUsingEncoding:NSUTF8StringEncoding]]; + [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"file\"; filename=\"%@\"\r\n", item.name] dataUsingEncoding:NSUTF8StringEncoding]]; //append 'Content-Type' [body appendData:[@"Content-Type: application/octet-stream\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; @@ -576,8 +604,9 @@ bail: return results; } + //submit a rescan request --(NSDictionary*)reScan:(File*)fileObj +-(NSDictionary*)reScan:(Binary*)item { //result data NSDictionary* result = nil; @@ -585,19 +614,28 @@ bail: //scan url NSURL* reScanURL = nil; + //remove item's vt info + // ->as its about to be outdates + item.vtInfo = nil; + + //reload UI + // ->will change item's VT button back to ... + [self updateUI:item]; + //init scan url - reScanURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@?apikey=%@&resource=%@", VT_RESCAN_URL, VT_API_KEY, fileObj.hashes[KEY_HASH_MD5]]]; + reScanURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@?apikey=%@&resource=%@", VT_RESCAN_URL, VT_API_KEY, item.hashes[KEY_HASH_MD5]]]; //make request to VT result = [self postRequest:reScanURL parameters:nil]; if(nil == result) { //err msg - NSLog(@"OBJECTIVE-SEE ERROR: failed to re-scan %@", fileObj.name); + NSLog(@"OBJECTIVE-SEE ERROR: failed to re-scan %@", item.name); //bail goto bail; } + //bail bail: @@ -606,7 +644,7 @@ bail: } //process results -// ->save VT info into +// ->save VT info into Binary object & reload relevant pane -(void)processResults:(NSMutableDictionary*)queriedItems results:(NSDictionary*)results { //queried binary obj @@ -616,10 +654,6 @@ bail: // ->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]) @@ -646,10 +680,11 @@ bail: reloadTopPane = YES; } //for dylibs - // ->set flag to reload bottom + // ->no dups, update each via row reload else { - reloadBottomPane = YES; + //update + [self updateUI:queriedItem]; } //TODO: do something with detections!? @@ -657,20 +692,38 @@ bail: } //reload top pane + // ->do full since there might be dups (e.g. Chrome Helper) 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]; + [self updateUI:nil]; } return; } +//call back up to update item in UI +// ->will either reload task table (top), or just row in item (bottom) table +-(void)updateUI:(Binary*)item +{ + //handle case where item is a task (top) + // ->fully reload task table (since there can be dups) + if( (nil == item) || + (YES == item.isTaskBinary) ) + { + //reload + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadTaskTable]; + } + //handle case where item is a dylib (bottom) + // ->just reload row + else + { + //reload row + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadRow:nil item:item pane:PANE_BOTTOM]; + } + + return; +} + + @end diff --git a/en.lproj/MainMenu.xib b/en.lproj/MainMenu.xib index 46f5484..a46d4eb 100755 --- a/en.lproj/MainMenu.xib +++ b/en.lproj/MainMenu.xib @@ -1,7 +1,6 @@ - @@ -99,6 +98,9 @@ + + +