mirror of
https://github.com/objective-see/TaskExplorer.git
synced 2026-03-22 07:02:39 +00:00
version 2.0 (beta?)
cli interface
This commit is contained in:
+11
-28
@@ -24,6 +24,16 @@
|
||||
#import "FlaggedItemWindowController.h"
|
||||
#import "RequestRootWindowController.h"
|
||||
|
||||
/* GLOBALS */
|
||||
|
||||
//shared enumerator
|
||||
extern TaskEnumerator* taskEnumerator;
|
||||
|
||||
//shared virustotal object
|
||||
extern VirusTotal* virusTotal;
|
||||
|
||||
//network connected flag
|
||||
extern BOOL isConnected;
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate, NSTableViewDataSource, NSTableViewDelegate, NSMenuDelegate>
|
||||
{
|
||||
@@ -36,9 +46,6 @@
|
||||
//start time
|
||||
@property NSTimeInterval startTime;
|
||||
|
||||
//connection flag
|
||||
@property BOOL isConnected;
|
||||
|
||||
//'filter task' search box
|
||||
// ->top pane
|
||||
@property (weak) IBOutlet NSSearchField *filterTasksBox;
|
||||
@@ -46,13 +53,9 @@
|
||||
//(current) bottom view controller
|
||||
@property(nonatomic, retain)TaskTableController *bottomViewController;
|
||||
|
||||
//top view/pane
|
||||
@property (weak) IBOutlet NSView *topPane;
|
||||
|
||||
//@property (weak) IBOutlet NSScrollView *taskScrollView;
|
||||
|
||||
//task enumerator object
|
||||
@property(nonatomic, retain)TaskEnumerator* taskEnumerator;
|
||||
|
||||
//task table controller object
|
||||
@property (nonatomic, retain)TaskTableController *taskTableController;
|
||||
|
||||
@@ -83,9 +86,6 @@
|
||||
|
||||
@property (weak) IBOutlet NSButton *logoButton;
|
||||
|
||||
|
||||
//@property (weak) IBOutlet NSButton *showPreferencesButton;
|
||||
|
||||
//spinner
|
||||
@property (weak) IBOutlet NSProgressIndicator *progressIndicator;
|
||||
|
||||
@@ -95,9 +95,6 @@
|
||||
//filter object
|
||||
@property(nonatomic, retain)Filter* filterObj;
|
||||
|
||||
//virus total object
|
||||
@property(nonatomic, retain)VirusTotal* virusTotalObj;
|
||||
|
||||
//array for all virus total threads
|
||||
@property(nonatomic, retain)NSMutableArray* vtThreads;
|
||||
|
||||
@@ -152,9 +149,6 @@
|
||||
//top constraint
|
||||
@property(nonatomic, retain)NSLayoutConstraint* trailingConstraint;
|
||||
|
||||
//flagged items
|
||||
@property(nonatomic, retain)NSMutableArray* flaggedItems;
|
||||
|
||||
//flag for filter field (autocomplete)
|
||||
@property BOOL completePosting;
|
||||
|
||||
@@ -196,13 +190,6 @@
|
||||
// ->query OS to refresh/reload all tasks
|
||||
-(IBAction)refreshTasks:(id)sender;
|
||||
|
||||
//callback when user has updated prefs
|
||||
// ->reload table, etc
|
||||
//-(void)applyPreferences;
|
||||
|
||||
//button handler for when settings icon (gear) is clicked
|
||||
//-(IBAction)showPreferences:(id)sender;
|
||||
|
||||
//button handler for logo
|
||||
-(IBAction)logoButtonHandler:(id)sender;
|
||||
|
||||
@@ -241,10 +228,6 @@
|
||||
//display (in separate popup) all flagged items
|
||||
-(IBAction)showFlaggedItems:(id)sender;
|
||||
|
||||
//save a flagged binary
|
||||
// ->also set text flagged items button label to red
|
||||
-(void)saveFlaggedBinary:(Binary*)binary;
|
||||
|
||||
//callback for custom search fields
|
||||
// ->handle auto-complete filterings
|
||||
-(void)filterAutoComplete:(NSTextView*)textField;
|
||||
|
||||
+70
-128
@@ -10,8 +10,8 @@
|
||||
#import "Consts.h"
|
||||
#import "Binary.h"
|
||||
#import "Update.h"
|
||||
#import "Connection.h"
|
||||
#import "Utilities.h"
|
||||
#import "Connection.h"
|
||||
#import "AppDelegate.h"
|
||||
#import "serviceInterface.h"
|
||||
#import "TaskTableController.h"
|
||||
@@ -29,13 +29,9 @@
|
||||
@synthesize bottomPane;
|
||||
@synthesize saveButton;
|
||||
@synthesize currentTask;
|
||||
@synthesize isConnected;
|
||||
@synthesize flaggedItems;
|
||||
@synthesize searchButton;
|
||||
@synthesize viewSelector;
|
||||
@synthesize scannerThread;
|
||||
@synthesize virusTotalObj;
|
||||
@synthesize taskEnumerator;
|
||||
@synthesize taskViewFormat;
|
||||
@synthesize commandHandling;
|
||||
@synthesize completePosting;
|
||||
@@ -57,18 +53,18 @@
|
||||
//kick off main window/logic
|
||||
-(void)taskExplore
|
||||
{
|
||||
//make foreground so it has an dock icon, etc
|
||||
transformProcess(kProcessTransformToForegroundApplication);
|
||||
|
||||
//for autolayout
|
||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints"];
|
||||
|
||||
//init virus total object
|
||||
virusTotalObj = [[VirusTotal alloc] init];
|
||||
virusTotal = [[VirusTotal alloc] init];
|
||||
|
||||
//init filter obj
|
||||
filterObj = [[Filter alloc] init];
|
||||
|
||||
//alloc flagged items
|
||||
flaggedItems = [NSMutableArray array];
|
||||
|
||||
//alloc/init custom search field for tasks
|
||||
customTasksFilter = [[CustomTextField alloc] init];
|
||||
|
||||
@@ -123,7 +119,7 @@
|
||||
if(YES != [self isAuthenticated])
|
||||
{
|
||||
//display auth popup
|
||||
// ->will invoke 'go' method on successful auth
|
||||
// will invoke 'go' method on successful auth
|
||||
[self askForRoot];
|
||||
}
|
||||
//go!
|
||||
@@ -171,12 +167,9 @@
|
||||
// ->main entry point
|
||||
-(void)applicationDidFinishLaunching:(NSNotification *)notification
|
||||
{
|
||||
//init crash reporting
|
||||
initCrashReporting();
|
||||
|
||||
//first time run?
|
||||
// show thanks to friends window!
|
||||
// note: on close invokes method to show main window
|
||||
// note: on close, invokes method to show main window
|
||||
if(YES != [[NSUserDefaults standardUserDefaults] boolForKey:NOT_FIRST_TIME])
|
||||
{
|
||||
//set key
|
||||
@@ -209,8 +202,10 @@
|
||||
//register handler for hot keys
|
||||
-(void)registerKeypressHandler
|
||||
{
|
||||
NSEvent * (^keypressHandler)(NSEvent *);
|
||||
//handler
|
||||
NSEvent * (^keypressHandler)(NSEvent *) = nil;
|
||||
|
||||
//handler block
|
||||
keypressHandler = ^NSEvent * (NSEvent * theEvent){
|
||||
|
||||
return [self handleKeypress:theEvent];
|
||||
@@ -440,7 +435,7 @@ bail:
|
||||
-(void)exploreTasks
|
||||
{
|
||||
//alloc task enumerator
|
||||
if(nil == self.taskEnumerator)
|
||||
if(nil == taskEnumerator)
|
||||
{
|
||||
//alloc
|
||||
taskEnumerator = [[TaskEnumerator alloc] init];
|
||||
@@ -448,7 +443,7 @@ bail:
|
||||
|
||||
//kick off thread to enum task
|
||||
// ->will update table as results come in
|
||||
[NSThread detachNewThreadSelector:@selector(enumerateTasks) toTarget:self.taskEnumerator withObject:nil];
|
||||
[NSThread detachNewThreadSelector:@selector(enumerateTasks) toTarget:taskEnumerator withObject:nil];
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -532,10 +527,10 @@ bail:
|
||||
if(YES != self.taskTableController.isFiltered)
|
||||
{
|
||||
//sync to get row
|
||||
@synchronized (self.taskEnumerator.tasks)
|
||||
@synchronized (taskEnumerator.tasks)
|
||||
{
|
||||
//get row
|
||||
row = [self.taskEnumerator.tasks indexOfKey:((Task*)item).pid];
|
||||
row = [taskEnumerator.tasks indexOfKey:((Task*)item).pid];
|
||||
}
|
||||
}
|
||||
//filtering
|
||||
@@ -816,11 +811,11 @@ bail:
|
||||
if(YES != self.taskTableController.isFiltered)
|
||||
{
|
||||
//sync
|
||||
@synchronized(self.taskEnumerator.tasks)
|
||||
@synchronized(taskEnumerator.tasks)
|
||||
{
|
||||
|
||||
//get tasks
|
||||
tasks = self.taskEnumerator.tasks;
|
||||
tasks = taskEnumerator.tasks;
|
||||
|
||||
//reload each row w/ new VT info
|
||||
for(NSNumber* taskPid in tasks)
|
||||
@@ -871,6 +866,35 @@ bail:
|
||||
[self reloadRow:binary];
|
||||
}
|
||||
|
||||
//main thread
|
||||
//update 'flagged' item
|
||||
// ...method might be invoked by VT callback, etc
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
//flagged items?
|
||||
// set icon to red
|
||||
if(0 != taskEnumerator.flaggedItems.count)
|
||||
{
|
||||
//set main image
|
||||
self.flaggedButton.image = [NSImage imageNamed:@"flaggedRed"];
|
||||
|
||||
//set alternate image
|
||||
self.flaggedButton.alternateImage = [NSImage imageNamed:@"flaggedRedBG"];
|
||||
}
|
||||
//no flagged items
|
||||
// set icon back to default
|
||||
else
|
||||
{
|
||||
//set main image
|
||||
self.flaggedButton.image = [NSImage imageNamed:@"flagged"];
|
||||
|
||||
//set alternate image
|
||||
self.flaggedButton.alternateImage = [NSImage imageNamed:@"flaggedBG"];
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -965,7 +989,7 @@ bail:
|
||||
-(void)reloadTaskTable
|
||||
{
|
||||
//sort tasks
|
||||
[self sortTasksForView:self.taskEnumerator.tasks];
|
||||
[self sortTasksForView:taskEnumerator.tasks];
|
||||
|
||||
//when exec'ing on background thread
|
||||
// ->exec on main thread
|
||||
@@ -991,62 +1015,6 @@ bail:
|
||||
return;
|
||||
}
|
||||
|
||||
//callback when user has updated prefs
|
||||
// ->reload table, etc
|
||||
-(void)applyPreferences
|
||||
{
|
||||
//currently selected category
|
||||
//NSUInteger selectedCategory = 0;
|
||||
|
||||
/*
|
||||
|
||||
//get currently selected category
|
||||
//selectedCategory = self.categoryTableController.categoryTableView.selectedRow;
|
||||
|
||||
//reload category table
|
||||
[self.categoryTableController customReload];
|
||||
|
||||
//reloading the category table resets the selected plugin
|
||||
// ->so manually (re)set it here
|
||||
self.selectedPlugin = self.plugins[selectedCategory];
|
||||
|
||||
//reload item table
|
||||
[self.taskTableController.itemTableView reloadData];
|
||||
|
||||
//if VT query was never done (e.g. scan was started w/ pref disabled)
|
||||
// ->kick off VT queries now
|
||||
if( (0 == self.vtThreads.count) &&
|
||||
(YES != self.prefsWindowController.disableVTQueries) )
|
||||
{
|
||||
//iterate over all plugins
|
||||
// ->do VT query for each
|
||||
for(PluginBase* plugin in self.plugins)
|
||||
{
|
||||
//do query
|
||||
[self queryVT:plugin];
|
||||
}
|
||||
}
|
||||
|
||||
//save results?
|
||||
// ->if there was a previous scan
|
||||
if( (nil != self.scannerThread) &&
|
||||
(YES == self.prefsWindowController.shouldSaveNow))
|
||||
{
|
||||
//save
|
||||
[self saveResults];
|
||||
|
||||
//alloc/init alert
|
||||
saveAlert = [NSAlert alertWithMessageText:[NSString stringWithFormat:@"current results saved to %@", OUTPUT_FILE] defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"subsequent scans will overwrite this file"];
|
||||
|
||||
//show it
|
||||
[saveAlert runModal];
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//automatically invoked when window is closing
|
||||
// ->terminate app
|
||||
-(void)windowWillClose:(NSNotification *)notification
|
||||
@@ -1128,7 +1096,7 @@ bail:
|
||||
else if(FLAGGED_BUTTON_TAG == tag)
|
||||
{
|
||||
//when no flagged items
|
||||
if(0 == self.flaggedItems.count)
|
||||
if(0 == taskEnumerator.flaggedItems.count)
|
||||
{
|
||||
//set
|
||||
imageName = @"flagged";
|
||||
@@ -1175,7 +1143,7 @@ bail:
|
||||
else if(FLAGGED_BUTTON_TAG == tag)
|
||||
{
|
||||
//when no flagged items
|
||||
if(0 == self.flaggedItems.count)
|
||||
if(0 == taskEnumerator.flaggedItems.count)
|
||||
{
|
||||
//set
|
||||
imageName = @"flaggedOver";
|
||||
@@ -1250,14 +1218,14 @@ bail:
|
||||
[output appendString:@"{\"tasks:\":["];
|
||||
|
||||
//sync
|
||||
@synchronized(self.taskEnumerator.tasks)
|
||||
@synchronized(taskEnumerator.tasks)
|
||||
{
|
||||
|
||||
//get tasks
|
||||
for(NSNumber* taskPid in self.taskEnumerator.tasks)
|
||||
for(NSNumber* taskPid in taskEnumerator.tasks)
|
||||
{
|
||||
//append task JSON
|
||||
[output appendFormat:@"{%@},", [self.taskEnumerator.tasks[taskPid] toJSON]];
|
||||
[output appendFormat:@"{%@},", [taskEnumerator.tasks[taskPid] toJSON]];
|
||||
}
|
||||
|
||||
}//sync
|
||||
@@ -1281,7 +1249,6 @@ bail:
|
||||
|
||||
//init popup w/ error msg
|
||||
saveResultPopup = [NSAlert alertWithMessageText:@"ERROR: failed to save output" defaultButton:@"Ok" alternateButton:nil otherButton:nil informativeTextWithFormat:@"details: %@", error];
|
||||
|
||||
}
|
||||
//happy
|
||||
// ->set result msg
|
||||
@@ -1506,7 +1473,7 @@ bail:
|
||||
( (YES == self.taskTableController.isFiltered) && (0 != self.taskTableController.filteredItems.count) ) )
|
||||
{
|
||||
//set to first
|
||||
self.currentTask = self.taskEnumerator.tasks[[self.taskEnumerator.tasks keyAtIndex:0]];
|
||||
self.currentTask = taskEnumerator.tasks[[taskEnumerator.tasks keyAtIndex:0]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1576,7 +1543,7 @@ bail:
|
||||
|
||||
//(re)enumerate dylibs via XPC
|
||||
// ->triggers table reload when done
|
||||
[self.currentTask enumerateDylibs:self.taskEnumerator.dylibs shouldWait:NO];
|
||||
[self.currentTask enumerateDylibs:taskEnumerator.dylibs shouldWait:NO];
|
||||
|
||||
break;
|
||||
|
||||
@@ -1731,7 +1698,7 @@ bail:
|
||||
[self.taskTableController.filteredItems removeAllObjects];
|
||||
|
||||
//normal filter
|
||||
[self.filterObj filterTasks:search.string items:self.taskEnumerator.tasks results:self.taskTableController.filteredItems pane:PANE_TOP];
|
||||
[self.filterObj filterTasks:search.string items:taskEnumerator.tasks results:self.taskTableController.filteredItems pane:PANE_TOP];
|
||||
|
||||
}//sync
|
||||
|
||||
@@ -1835,8 +1802,20 @@ bail:
|
||||
//maks
|
||||
self.filteringOverlay.layer.masksToBounds = YES;
|
||||
|
||||
//set overlay's view color to gray
|
||||
self.filteringOverlay.layer.backgroundColor = NSColor.grayColor.CGColor;
|
||||
//dark mode
|
||||
// set overlay to light
|
||||
if(YES == isDarkMode())
|
||||
{
|
||||
//light gray
|
||||
self.filteringOverlay.layer.backgroundColor = NSColor.lightGrayColor.CGColor;
|
||||
}
|
||||
//light mode
|
||||
// set overlay to gray
|
||||
else
|
||||
{
|
||||
//gray
|
||||
self.filteringOverlay.layer.backgroundColor = NSColor.grayColor.CGColor;
|
||||
}
|
||||
|
||||
//make it semi-transparent
|
||||
self.filteringOverlay.alphaValue = 0.95;
|
||||
@@ -2084,43 +2063,6 @@ bail:
|
||||
return;
|
||||
}
|
||||
|
||||
//save a flagged item
|
||||
// ->also set text flagged items button label to red
|
||||
-(void)saveFlaggedBinary:(Binary*)binary
|
||||
{
|
||||
//first check if item is already flagged
|
||||
if(YES == [self.flaggedItems containsObject:binary])
|
||||
{
|
||||
//no need to add
|
||||
// ->so bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//sync to save
|
||||
@synchronized(self.flaggedItems)
|
||||
{
|
||||
//save
|
||||
[self.flaggedItems addObject:binary];
|
||||
}
|
||||
|
||||
//when count is 1
|
||||
// ->means first flagged file so set image to red
|
||||
if(1 == self.flaggedItems.count)
|
||||
{
|
||||
//set main image
|
||||
[self.flaggedButton setImage:[NSImage imageNamed:@"flaggedRed"]];
|
||||
|
||||
//set alternate image
|
||||
[self.flaggedButton setAlternateImage:[NSImage imageNamed:@"flaggedRedBG"]];
|
||||
|
||||
}
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//button handle for 'flagged items' button
|
||||
// ->display (in separate popup) all flagged items
|
||||
-(IBAction)showFlaggedItems:(id)sender
|
||||
@@ -2130,7 +2072,7 @@ bail:
|
||||
|
||||
//handle case where there aren't any flagged items
|
||||
// ->just show alert
|
||||
if(0 == self.flaggedItems.count)
|
||||
if(0 == taskEnumerator.flaggedItems.count)
|
||||
{
|
||||
//alloc/init alert
|
||||
alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:@"No items flagged by VirusTotal"] defaultButton:@"Ok" alternateButton:nil otherButton:nil informativeTextWithFormat:@"hooray! 😇"];
|
||||
@@ -2321,7 +2263,7 @@ bail:
|
||||
[self finalizeFiltration:PANE_TOP];
|
||||
|
||||
//filter
|
||||
[self.filterObj filterTasks:filterString items:self.taskEnumerator.tasks results:self.taskTableController.filteredItems pane:PANE_TOP];
|
||||
[self.filterObj filterTasks:filterString items:taskEnumerator.tasks results:self.taskTableController.filteredItems pane:PANE_TOP];
|
||||
}
|
||||
|
||||
//refresh/update UI when not a keyword search
|
||||
|
||||
@@ -30,6 +30,11 @@
|
||||
//success
|
||||
#define STATUS_SUCCESS 0
|
||||
|
||||
//user name
|
||||
#define USER_NAME @"userName"
|
||||
|
||||
//user (home) directory
|
||||
#define USER_DIRECTORY @"userDirectory"
|
||||
|
||||
//signers
|
||||
enum Signer{None, Apple, AppStore, DevID, AdHoc};
|
||||
|
||||
@@ -64,14 +64,14 @@
|
||||
}
|
||||
|
||||
//populate flagged items array
|
||||
for(Binary* flaggedItem in ((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedItems)
|
||||
for(Binary* flaggedItem in taskEnumerator.flaggedItems)
|
||||
{
|
||||
//when binary is task binary
|
||||
// ->find/save all tasks instances
|
||||
if(YES == flaggedItem.isTaskBinary)
|
||||
{
|
||||
//get all tasks instances
|
||||
flaggedTasks = [((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator tasksForBinary:flaggedItem];
|
||||
flaggedTasks = [taskEnumerator tasksForBinary:flaggedItem];
|
||||
|
||||
//sync to save
|
||||
@synchronized(self.flaggedItems)
|
||||
|
||||
+13
-18
@@ -306,8 +306,8 @@ NSAttributedString* initBinaryString(id item, BOOL isSearchWindow)
|
||||
//add name
|
||||
[taskString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:binary.name]];
|
||||
|
||||
//init gray color for pid
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.grayColor forKey:NSForegroundColorAttributeName];
|
||||
//init default color for pid
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.controlTextColor forKey:NSForegroundColorAttributeName];
|
||||
|
||||
//search window
|
||||
// ->only for tasks, since dylibs in search window are handled elsewhere ('loaded in')
|
||||
@@ -334,8 +334,7 @@ NSAttributedString* initBinaryString(id item, BOOL isSearchWindow)
|
||||
(YES == binary.isPacked) )
|
||||
{
|
||||
//init color for comma, etc
|
||||
// ->light gray
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.grayColor forKey:NSForegroundColorAttributeName];
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.controlTextColor forKey:NSForegroundColorAttributeName];
|
||||
|
||||
//tasks
|
||||
//add comma string
|
||||
@@ -376,7 +375,7 @@ NSAttributedString* initBinaryString(id item, BOOL isSearchWindow)
|
||||
(YES != binary.notFound))
|
||||
{
|
||||
//init color for closing
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.grayColor forKey:NSForegroundColorAttributeName];
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.controlTextColor forKey:NSForegroundColorAttributeName];
|
||||
|
||||
//close string
|
||||
[taskString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:@")" attributes:attributes]];
|
||||
@@ -392,8 +391,7 @@ NSAttributedString* initBinaryString(id item, BOOL isSearchWindow)
|
||||
(YES == binary.isPacked) )
|
||||
{
|
||||
//init color for comma,
|
||||
// ->light gray
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.grayColor forKey:NSForegroundColorAttributeName];
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.controlTextColor forKey:NSForegroundColorAttributeName];
|
||||
|
||||
//add
|
||||
[taskString appendAttributedString:[[NSAttributedString alloc] initWithString:@", " attributes:attributes]];
|
||||
@@ -402,8 +400,7 @@ NSAttributedString* initBinaryString(id item, BOOL isSearchWindow)
|
||||
else
|
||||
{
|
||||
//init color for comma, etc
|
||||
// ->light gray
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.grayColor forKey:NSForegroundColorAttributeName];
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.controlTextColor forKey:NSForegroundColorAttributeName];
|
||||
|
||||
//tasks
|
||||
//add comma string
|
||||
@@ -427,14 +424,14 @@ NSAttributedString* initBinaryString(id item, BOOL isSearchWindow)
|
||||
attributes = [NSDictionary dictionaryWithObject:[NSColor redColor] forKey:NSForegroundColorAttributeName];
|
||||
|
||||
//add
|
||||
[taskString appendAttributedString:[[NSAttributedString alloc] initWithString:@"not found" attributes:attributes]];
|
||||
[taskString appendAttributedString:[[NSAttributedString alloc] initWithString:@"deleted" attributes:attributes]];
|
||||
|
||||
//dylib, need to close string here
|
||||
// ->normally it doesn't have anything after...
|
||||
if(YES != [item isKindOfClass:[Task class]])
|
||||
{
|
||||
//init color for closing
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.grayColor forKey:NSForegroundColorAttributeName];
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.controlTextColor forKey:NSForegroundColorAttributeName];
|
||||
|
||||
//close string
|
||||
[taskString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:@")" attributes:attributes]];
|
||||
@@ -448,8 +445,7 @@ NSAttributedString* initBinaryString(id item, BOOL isSearchWindow)
|
||||
if(YES == [item isKindOfClass:[Task class]])
|
||||
{
|
||||
//init color for closing
|
||||
// ->light gray
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.grayColor forKey:NSForegroundColorAttributeName];
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.controlTextColor forKey:NSForegroundColorAttributeName];
|
||||
|
||||
//close string
|
||||
[taskString appendAttributedString:[[NSMutableAttributedString alloc] initWithString:@")" attributes:attributes]];
|
||||
@@ -478,7 +474,7 @@ NSAttributedString* initLoadedInString(id item)
|
||||
|
||||
//get host tasks
|
||||
// ->works with dylibs or files
|
||||
tasks = [((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator loadedIn:item];
|
||||
tasks = [taskEnumerator loadedIn:item];
|
||||
|
||||
//dylibs/files
|
||||
// ->add name
|
||||
@@ -497,8 +493,7 @@ NSAttributedString* initLoadedInString(id item)
|
||||
}
|
||||
|
||||
//init color for 'loaded in...'
|
||||
// ->light gray
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.grayColor forKey:NSForegroundColorAttributeName];
|
||||
attributes = [NSDictionary dictionaryWithObject:NSColor.controlTextColor forKey:NSForegroundColorAttributeName];
|
||||
|
||||
//add dylib indicator
|
||||
//-> '(dylib, loaded in: ... '
|
||||
@@ -851,8 +846,8 @@ void configVTButton(NSTableCellView *itemCell, id owner, Binary* binary)
|
||||
//set string (vt ratio), with attributes
|
||||
[vtButton setAttributedTitle:[[NSAttributedString alloc] initWithString:vtDetectionRatio attributes:stringAttributes]];
|
||||
|
||||
//set color (gray)
|
||||
stringAttributes[NSForegroundColorAttributeName] = NSColor.grayColor;
|
||||
//set color
|
||||
stringAttributes[NSForegroundColorAttributeName] = NSColor.controlTextColor;
|
||||
|
||||
//set selected text color
|
||||
[vtButton setAttributedAlternateTitle:[[NSAttributedString alloc] initWithString:vtDetectionRatio attributes:stringAttributes]];
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
//flag for task (main) executable
|
||||
@property BOOL isTaskBinary;
|
||||
|
||||
//loaded in
|
||||
// ...for dylibs only
|
||||
@property(nonatomic, retain)NSArray* loadedIn;
|
||||
|
||||
//hashes (md5, sha1)
|
||||
@property(nonatomic, retain)NSDictionary* hashes;
|
||||
|
||||
@@ -49,6 +53,7 @@
|
||||
//not found
|
||||
@property BOOL notFound;
|
||||
|
||||
|
||||
/* VIRUS TOTAL INFO */
|
||||
|
||||
//dictionary returned by VT
|
||||
|
||||
+59
-15
@@ -22,6 +22,7 @@
|
||||
@synthesize parser;
|
||||
@synthesize vtInfo;
|
||||
@synthesize isPacked;
|
||||
@synthesize loadedIn;
|
||||
@synthesize notFound;
|
||||
@synthesize isEncrypted;
|
||||
@synthesize signingInfo;
|
||||
@@ -52,6 +53,8 @@
|
||||
//determine if its on disk
|
||||
self.notFound = ![[NSFileManager defaultManager] fileExistsAtPath:self.path];
|
||||
|
||||
//get attributes
|
||||
self.attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:self.path error:nil];
|
||||
}
|
||||
|
||||
//bail
|
||||
@@ -217,13 +220,8 @@ bail:
|
||||
//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];
|
||||
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
//format the signing info dictionary
|
||||
@@ -356,10 +354,10 @@ bail:
|
||||
-(NSString*)toJSON
|
||||
{
|
||||
//json string
|
||||
NSString *json = nil;
|
||||
NSMutableString *json = nil;
|
||||
|
||||
//json data
|
||||
// ->for intermediate conversions
|
||||
// for intermediate conversions
|
||||
NSData *jsonData = nil;
|
||||
|
||||
//hashes
|
||||
@@ -371,19 +369,25 @@ bail:
|
||||
//VT detection ratio
|
||||
NSString* vtDetectionRatio = nil;
|
||||
|
||||
//tasks loaded in
|
||||
NSMutableArray* taskPids = nil;
|
||||
|
||||
//'loaded in' list
|
||||
NSString* tasks = nil;
|
||||
|
||||
//init file hash to default string
|
||||
// ->used when hashes are nil, or serialization fails
|
||||
// used when hashes are nil, or serialization fails
|
||||
fileHashes = @"\"unknown\"";
|
||||
|
||||
//init file signature to default string
|
||||
// ->used when signatures are nil, or serialization fails
|
||||
// used when signatures are nil, or serialization fails
|
||||
fileSigs = @"\"unknown\"";
|
||||
|
||||
//convert hashes to JSON
|
||||
if(nil != self.hashes)
|
||||
{
|
||||
//convert hash dictionary
|
||||
// ->wrap since we are serializing JSON
|
||||
// wrap since we are serializing JSON
|
||||
@try
|
||||
{
|
||||
//convert
|
||||
@@ -395,7 +399,7 @@ bail:
|
||||
}
|
||||
}
|
||||
//ignore exceptions
|
||||
// ->file hashes will just be 'unknown'
|
||||
// file hashes will just be 'unknown'
|
||||
@catch(NSException *exception)
|
||||
{
|
||||
;
|
||||
@@ -406,7 +410,7 @@ bail:
|
||||
if(nil != self.signingInfo)
|
||||
{
|
||||
//convert signing dictionary
|
||||
// ->wrap since we are serializing JSON
|
||||
// wrap since we are serializing JSON
|
||||
@try
|
||||
{
|
||||
//convert
|
||||
@@ -424,15 +428,55 @@ bail:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
//dylibs
|
||||
//covert 'loaded in' array to JSON
|
||||
if(YES != isTaskBinary)
|
||||
{
|
||||
//init
|
||||
taskPids = [NSMutableArray array];
|
||||
|
||||
//tasks loaded in
|
||||
for(Task* task in self.loadedIn)
|
||||
{
|
||||
//add pid
|
||||
[taskPids addObject:task.pid];
|
||||
}
|
||||
|
||||
//wrap since we are serializing JSON
|
||||
@try
|
||||
{
|
||||
//convert
|
||||
jsonData = [NSJSONSerialization dataWithJSONObject:taskPids options:kNilOptions error:NULL];
|
||||
if(nil != jsonData)
|
||||
{
|
||||
//convert data to string
|
||||
tasks = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
}
|
||||
//ignore exceptions
|
||||
// ->file sigs will just be 'unknown'
|
||||
@catch(NSException *exception)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
//init VT detection ratio
|
||||
vtDetectionRatio = [NSString stringWithFormat:@"%lu/%lu", (unsigned long)[self.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue], (unsigned long)[self.vtInfo[VT_RESULTS_TOTAL] unsignedIntegerValue]];
|
||||
|
||||
//init json
|
||||
json = [NSString stringWithFormat:@"\"name\": \"%@\", \"path\": \"%@\", \"hashes\": %@, \"signature(s)\": %@, \"VT detection\": \"%@\", \"encrypted\": %d, \"packed\": %d, \"not found\": %d", self.name, self.path, fileHashes, fileSigs, vtDetectionRatio, self.isEncrypted, self.isPacked, self.notFound];
|
||||
json = [NSMutableString stringWithFormat:@"\"name\": \"%@\", \"path\": \"%@\", \"hashes\": %@, \"signature(s)\": %@, \"VT detection\": \"%@\", \"encrypted\": %d, \"packed\": %d, \"deleted\": %d", self.name, self.path, fileHashes, fileSigs, vtDetectionRatio, self.isEncrypted, self.isPacked, self.notFound];
|
||||
|
||||
//dylibs
|
||||
// add tasks they are loaded in
|
||||
if(YES != self.isTaskBinary)
|
||||
{
|
||||
//add
|
||||
[json appendString:[NSString stringWithFormat:@", \"loaded in\": \"%@\"", tasks]];
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
+4
-2
@@ -10,6 +10,10 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/* GLOBALS */
|
||||
|
||||
//(privacy) protected directories
|
||||
extern NSArray* protectedDirectories;
|
||||
|
||||
@interface File : ItemBase
|
||||
{
|
||||
@@ -35,6 +39,4 @@
|
||||
// ->invokes 'file' cmd, the parses out result
|
||||
-(void)setFileType;
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
+39
-14
@@ -16,12 +16,11 @@
|
||||
|
||||
@synthesize type;
|
||||
|
||||
|
||||
//init method
|
||||
-(id)initWithParams:(NSDictionary*)params
|
||||
{
|
||||
//super
|
||||
// ->saves path, etc
|
||||
// saves path, etc
|
||||
self = [super initWithParams:params];
|
||||
if(self)
|
||||
{
|
||||
@@ -31,18 +30,22 @@
|
||||
//set icon
|
||||
self.icon = [[NSWorkspace sharedWorkspace] iconForFile:self.path];
|
||||
|
||||
//grab attributes
|
||||
self.attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:self.path error:nil];
|
||||
//check if protected
|
||||
// if not, get file attrs
|
||||
if(YES != [self isProtected])
|
||||
{
|
||||
//get attrs
|
||||
self.attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:self.path error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
//bail
|
||||
|
||||
bail:
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//set file type
|
||||
// ->invokes 'file' cmd, the parses out result
|
||||
// invokes 'file' cmd, the parses out result
|
||||
-(void)setFileType
|
||||
{
|
||||
//results from 'file' cmd
|
||||
@@ -82,7 +85,6 @@ bail:
|
||||
// ->also trim whitespace
|
||||
self.type = [parsedResults[1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
return;
|
||||
@@ -100,14 +102,14 @@ bail:
|
||||
}
|
||||
|
||||
//override method
|
||||
// ->hash
|
||||
// hash the file
|
||||
-(NSUInteger)hash
|
||||
{
|
||||
return [self.path hash];
|
||||
}
|
||||
|
||||
//override method
|
||||
// ->equality check
|
||||
// file equality check (path)
|
||||
-(BOOL)isEqual:(id)object
|
||||
{
|
||||
//flag
|
||||
@@ -143,12 +145,36 @@ bail:
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
return objEqual;
|
||||
}
|
||||
|
||||
//check if file is protected
|
||||
// on mojave+, need to avoid prompts
|
||||
-(BOOL)isProtected
|
||||
{
|
||||
//flag
|
||||
BOOL protected = NO;
|
||||
|
||||
//skip any files in (privacy) protected directories
|
||||
// as otherwise we will generate a privacy prompt (on Mojave)
|
||||
for(NSString* directory in protectedDirectories)
|
||||
{
|
||||
//check
|
||||
if(YES == [self.path hasPrefix:directory])
|
||||
{
|
||||
//set flag
|
||||
protected = YES;
|
||||
|
||||
//done
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return protected;
|
||||
}
|
||||
|
||||
//convert object to JSON string
|
||||
-(NSString*)toJSON
|
||||
{
|
||||
@@ -162,7 +188,7 @@ bail:
|
||||
attributesJSON = [NSMutableString string];
|
||||
|
||||
//when attributes are nil
|
||||
// ->init default string
|
||||
// init default string, 'unknown'
|
||||
if(nil == self.attributes)
|
||||
{
|
||||
//init
|
||||
@@ -170,7 +196,7 @@ bail:
|
||||
}
|
||||
|
||||
//file has attributes
|
||||
// ->add each
|
||||
// add each one to json
|
||||
else
|
||||
{
|
||||
//start
|
||||
@@ -208,5 +234,4 @@ bail:
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
+2
-16
@@ -29,10 +29,6 @@
|
||||
|
||||
//extract/save path
|
||||
self.path = params[KEY_RESULT_PATH];
|
||||
|
||||
//get attributes
|
||||
// ->based off path
|
||||
self.attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:self.path error:nil];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -50,20 +46,10 @@
|
||||
/* OPTIONAL METHODS */
|
||||
|
||||
|
||||
|
||||
/* REQUIRED METHODS */
|
||||
|
||||
//stubs for inherited methods
|
||||
// ->all just throw exceptions as they should be implemented in sub-classes
|
||||
|
||||
//scan
|
||||
-(void)scan:(NSDictionary*)scanOptions
|
||||
{
|
||||
@throw [NSException exceptionWithName:kExceptName
|
||||
reason:[NSString stringWithFormat:kErrFormat, NSStringFromSelector(_cmd), [self class]]
|
||||
userInfo:nil];
|
||||
return;
|
||||
}
|
||||
// throw exceptions as they should be implemented in sub-classes
|
||||
|
||||
//convert object to JSON string
|
||||
-(NSString*)toJSON
|
||||
@@ -74,4 +60,4 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@@ -82,44 +82,6 @@
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
//save prefs
|
||||
-(void)savePrefs
|
||||
{
|
||||
//first, any prefs changed, a 'save' set
|
||||
// ->set 'save now' flag
|
||||
if( ((self.showTrustedItems != self.showTrustedItemsBtn.state) ||
|
||||
(self.disableVTQueries != self.disableVTQueriesBtn.state) ||
|
||||
(self.saveOutput != self.saveOutputBtn.state) ) &&
|
||||
(YES == self.saveOutputBtn.state) )
|
||||
{
|
||||
//set
|
||||
self.shouldSaveNow = YES;
|
||||
}
|
||||
//don't save
|
||||
else
|
||||
{
|
||||
//unset
|
||||
self.shouldSaveNow = NO;
|
||||
}
|
||||
|
||||
//save hiding OS components flag
|
||||
self.showTrustedItems = self.showTrustedItemsBtn.state;
|
||||
|
||||
//save disabling VT flag
|
||||
self.disableVTQueries = self.disableVTQueriesBtn.state;
|
||||
|
||||
//save save output flag
|
||||
self.saveOutput = self.saveOutputBtn.state;
|
||||
|
||||
//call back up into app delegate for filtering/hiding OS components
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) applyPreferences];
|
||||
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
//'OK' button handler
|
||||
// ->save prefs and close window
|
||||
-(IBAction)closeWindow:(id)sender
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
// Copyright (c) 2014 Synack. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
//from: https://github.com/esromneb/ios-queue-object/blob/master/NSMutableArray%2BQueueAdditions.h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -27,20 +26,27 @@
|
||||
|
||||
/* PROPERTIES */
|
||||
|
||||
//items in
|
||||
@property NSUInteger itemsIn;
|
||||
|
||||
//items out
|
||||
@property NSUInteger itemsOut;
|
||||
|
||||
//event queue
|
||||
@property(retain, atomic)NSMutableArray* eventQueue;
|
||||
|
||||
|
||||
//thread to process events
|
||||
@property (nonatomic, retain)NSThread* qProcessorThread;
|
||||
@property (nonatomic, retain)NSCondition* queueCondition;
|
||||
|
||||
//condition for queue
|
||||
@property (nonatomic, retain)NSCondition* queueCondition;
|
||||
|
||||
//METHODS
|
||||
|
||||
//add an object to the queue
|
||||
-(void)enqueue:(id)anObject;
|
||||
|
||||
|
||||
//process events from queue
|
||||
-(void)processQueue:(id)threadParam;
|
||||
|
||||
@end
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
@implementation Queue
|
||||
|
||||
@synthesize itemsIn;
|
||||
@synthesize itemsOut;
|
||||
@synthesize eventQueue;
|
||||
@synthesize queueCondition;
|
||||
@synthesize qProcessorThread;
|
||||
@@ -50,12 +52,6 @@
|
||||
// ->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)
|
||||
{
|
||||
@@ -75,37 +71,30 @@
|
||||
//get item off queue
|
||||
binary = [eventQueue dequeue];
|
||||
|
||||
//sanity check
|
||||
if(YES != [binary isKindOfClass:[Binary class]])
|
||||
{
|
||||
//ignore
|
||||
continue;
|
||||
}
|
||||
|
||||
//process
|
||||
//->for now, just hash, etc
|
||||
[binary generateDetailedInfo];
|
||||
|
||||
//when connected
|
||||
// ->add item for VT processing
|
||||
if(YES == ((AppDelegate*)[[NSApplication sharedApplication] delegate]).isConnected)
|
||||
{
|
||||
//add
|
||||
[vtObject addItem:binary];
|
||||
}
|
||||
//inc
|
||||
itemsOut++;
|
||||
|
||||
//unlock
|
||||
[self.queueCondition unlock];
|
||||
|
||||
//pool
|
||||
//generate hashes, etc
|
||||
[binary generateDetailedInfo];
|
||||
|
||||
//when connected
|
||||
// add item for VT processing
|
||||
if(YES == isConnected)
|
||||
{
|
||||
//add
|
||||
[virusTotal addItem:binary];
|
||||
}
|
||||
|
||||
} //pool
|
||||
|
||||
}//foreverz process queue
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//add an object to the queue
|
||||
-(void)enqueue:(id)anObject
|
||||
{
|
||||
@@ -115,6 +104,9 @@
|
||||
//add to queue
|
||||
[self.eventQueue enqueue:anObject];
|
||||
|
||||
//inc
|
||||
itemsIn++;
|
||||
|
||||
//signal
|
||||
[self.queueCondition signal];
|
||||
|
||||
@@ -124,10 +116,4 @@
|
||||
return;
|
||||
}
|
||||
|
||||
//process binary
|
||||
-(void)processBinary:(Binary*)binary
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
{
|
||||
//not done?
|
||||
// ->make spinner keep spinning
|
||||
if(ENUMERATION_STATE_COMPLETE != ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.state)
|
||||
if(ENUMERATION_STATE_COMPLETE != taskEnumerator.state)
|
||||
{
|
||||
//(re)start
|
||||
[self.activityIndicator startAnimation:nil];
|
||||
@@ -115,7 +115,7 @@
|
||||
|
||||
//still enumerating?
|
||||
// ->show the spinner
|
||||
if(ENUMERATION_STATE_COMPLETE != ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.state)
|
||||
if(ENUMERATION_STATE_COMPLETE != taskEnumerator.state)
|
||||
{
|
||||
//show
|
||||
self.activityIndicator.hidden = NO;
|
||||
@@ -163,7 +163,7 @@
|
||||
});
|
||||
|
||||
//done?
|
||||
if(ENUMERATION_STATE_COMPLETE == ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.state)
|
||||
if(ENUMERATION_STATE_COMPLETE == taskEnumerator.state)
|
||||
{
|
||||
//stop spinner
|
||||
[self.activityIndicator stopAnimation:nil];
|
||||
@@ -180,7 +180,7 @@
|
||||
-(void)showEnumerationState
|
||||
{
|
||||
//set status
|
||||
switch(((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.state) {
|
||||
switch(taskEnumerator.state) {
|
||||
|
||||
//tasks
|
||||
case ENUMERATION_STATE_TASKS:
|
||||
@@ -533,8 +533,20 @@ bail:
|
||||
//maks
|
||||
self.overlay.layer.masksToBounds = YES;
|
||||
|
||||
//set overlay's view color to gray
|
||||
self.overlay.layer.backgroundColor = NSColor.grayColor.CGColor;
|
||||
//dark mode
|
||||
// set overlay to light
|
||||
if(YES == isDarkMode())
|
||||
{
|
||||
//set overlay's view color to gray
|
||||
self.overlay.layer.backgroundColor = NSColor.lightGrayColor.CGColor;
|
||||
}
|
||||
//light mode
|
||||
// set overlay to gray
|
||||
else
|
||||
{
|
||||
//set to gray
|
||||
self.overlay.layer.backgroundColor = NSColor.grayColor.CGColor;
|
||||
}
|
||||
|
||||
//make it semi-transparent
|
||||
self.overlay.alphaValue = 0.95;
|
||||
@@ -553,7 +565,7 @@ bail:
|
||||
|
||||
//grab all tasks
|
||||
// make copy to avoid threading issues
|
||||
allTasks = [((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks copy];
|
||||
allTasks = [taskEnumerator.tasks copy];
|
||||
|
||||
//kick off filtering in background
|
||||
// will call back into to refresh UI when done
|
||||
|
||||
@@ -91,6 +91,6 @@ struct dyld_image_info_32 {
|
||||
-(void)enumerateNetworking:(BOOL)shouldWait;
|
||||
|
||||
//convert self to JSON string
|
||||
-(NSString*)toJSON;
|
||||
-(NSString*)toJSON:(BOOL)detailed;
|
||||
|
||||
@end
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
if(nil != self)
|
||||
{
|
||||
//grab existings binaries
|
||||
existingBinaries = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.executables;
|
||||
existingBinaries = taskEnumerator.executables;
|
||||
|
||||
//since root UID is zero
|
||||
// ->init UID to -1
|
||||
@@ -111,7 +111,7 @@
|
||||
|
||||
//add to queue
|
||||
// ->this will process in background
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.binaryQueue enqueue:self.binary];
|
||||
[taskEnumerator.binaryQueue enqueue:self.binary];
|
||||
|
||||
//sync
|
||||
@synchronized(existingBinaries)
|
||||
@@ -362,7 +362,7 @@ bail:
|
||||
|
||||
//add to queue
|
||||
// ->this will trigger background processing
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.binaryQueue enqueue:dylib];
|
||||
[taskEnumerator.binaryQueue enqueue:dylib];
|
||||
|
||||
//add to list of new dylibs
|
||||
// ->will allow for post processing
|
||||
@@ -492,7 +492,7 @@ bail:
|
||||
|
||||
//reset existing files
|
||||
[self.files removeAllObjects];
|
||||
|
||||
|
||||
//create/add all files
|
||||
for(NSMutableDictionary* fileDescriptor in fileDescriptors)
|
||||
{
|
||||
@@ -509,7 +509,7 @@ bail:
|
||||
[self.files addObject:file];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//sort by name
|
||||
self.files = [[self.files sortedArrayUsingComparator:^NSComparisonResult(id a, id b)
|
||||
{
|
||||
@@ -630,7 +630,7 @@ bail:
|
||||
}
|
||||
|
||||
//convert self to JSON string
|
||||
-(NSString*)toJSON
|
||||
-(NSString*)toJSON:(BOOL)detailed
|
||||
{
|
||||
//json string
|
||||
NSString *json = nil;
|
||||
@@ -738,63 +738,75 @@ bail:
|
||||
//init VT detection ratio
|
||||
vtDetectionRatio = [NSString stringWithFormat:@"%lu/%lu", (unsigned long)[self.binary.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue], (unsigned long)[self.binary.vtInfo[VT_RESULTS_TOTAL] unsignedIntegerValue]];
|
||||
|
||||
//sync
|
||||
@synchronized(self.dylibs)
|
||||
//detailed?
|
||||
// generate full json
|
||||
if(YES == detailed)
|
||||
{
|
||||
//convert all dylibs and add
|
||||
for(Binary* dylib in self.dylibs)
|
||||
//sync
|
||||
@synchronized(self.dylibs)
|
||||
{
|
||||
//convert/add
|
||||
[dylibsJSON appendFormat:@"{%@},", [dylib toJSON]];
|
||||
//convert all dylibs and add
|
||||
for(Binary* dylib in self.dylibs)
|
||||
{
|
||||
//convert/add
|
||||
[dylibsJSON appendFormat:@"{%@},", [dylib toJSON]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//remove last ','
|
||||
if(YES == [dylibsJSON hasSuffix:@","])
|
||||
{
|
||||
//remove
|
||||
[dylibsJSON deleteCharactersInRange:NSMakeRange([dylibsJSON length]-1, 1)];
|
||||
}
|
||||
|
||||
//sync
|
||||
@synchronized(self.files)
|
||||
{
|
||||
//convert all file and add
|
||||
for(File* file in self.files)
|
||||
|
||||
//remove last ','
|
||||
if(YES == [dylibsJSON hasSuffix:@","])
|
||||
{
|
||||
//convert/add
|
||||
[filesJSON appendFormat:@"{%@},", [file toJSON]];
|
||||
//remove
|
||||
[dylibsJSON deleteCharactersInRange:NSMakeRange([dylibsJSON length]-1, 1)];
|
||||
}
|
||||
}
|
||||
|
||||
//remove last ','
|
||||
if(YES == [filesJSON hasSuffix:@","])
|
||||
{
|
||||
//remove
|
||||
[filesJSON deleteCharactersInRange:NSMakeRange([filesJSON length]-1, 1)];
|
||||
}
|
||||
|
||||
//sync
|
||||
@synchronized(self.connections)
|
||||
{
|
||||
//convert all connections and add
|
||||
for(Connection* connection in self.connections)
|
||||
|
||||
//sync
|
||||
@synchronized(self.files)
|
||||
{
|
||||
//convert/add
|
||||
[connectionsJSON appendFormat:@"{%@},", [connection toJSON]];
|
||||
//convert all file and add
|
||||
for(File* file in self.files)
|
||||
{
|
||||
//convert/add
|
||||
[filesJSON appendFormat:@"{%@},", [file toJSON]];
|
||||
}
|
||||
}
|
||||
|
||||
//remove last ','
|
||||
if(YES == [filesJSON hasSuffix:@","])
|
||||
{
|
||||
//remove
|
||||
[filesJSON deleteCharactersInRange:NSMakeRange([filesJSON length]-1, 1)];
|
||||
}
|
||||
|
||||
//sync
|
||||
@synchronized(self.connections)
|
||||
{
|
||||
//convert all connections and add
|
||||
for(Connection* connection in self.connections)
|
||||
{
|
||||
//convert/add
|
||||
[connectionsJSON appendFormat:@"{%@},", [connection toJSON]];
|
||||
}
|
||||
}
|
||||
|
||||
//remove last ','
|
||||
if(YES == [connectionsJSON hasSuffix:@","])
|
||||
{
|
||||
//remove
|
||||
[connectionsJSON deleteCharactersInRange:NSMakeRange([connectionsJSON length]-1, 1)];
|
||||
}
|
||||
|
||||
//init json
|
||||
json = [NSString stringWithFormat:@"\"name\": \"%@\", \"path\": \"%@\", \"pid\": \"%@\", \"command line\": \"%@\", \"hashes\": %@, \"signature(s)\": %@, \"VT detection\": \"%@\", \"encrypted\": %d, \"packed\": %d, \"not found\": %d, \"dylibs\": [%@], \"files\": [%@], \"connections\": [%@]", self.binary.name, self.binary.path, self.pid, taskCommandLine, fileHashes, fileSigs, vtDetectionRatio, self.binary.isEncrypted, self.binary.isPacked, self.binary.notFound, dylibsJSON, filesJSON, connectionsJSON];
|
||||
}
|
||||
|
||||
//remove last ','
|
||||
if(YES == [connectionsJSON hasSuffix:@","])
|
||||
//basic
|
||||
else
|
||||
{
|
||||
//remove
|
||||
[connectionsJSON deleteCharactersInRange:NSMakeRange([connectionsJSON length]-1, 1)];
|
||||
//init json
|
||||
json = [NSString stringWithFormat:@"\"name\": \"%@\", \"path\": \"%@\", \"pid\": \"%@\", \"command line\": \"%@\", \"hashes\": %@, \"signature(s)\": %@, \"VT detection\": \"%@\", \"encrypted\": %d, \"packed\": %d, \"not found\": %d", self.binary.name, self.binary.path, self.pid, taskCommandLine, fileHashes, fileSigs, vtDetectionRatio, self.binary.isEncrypted, self.binary.isPacked, self.binary.notFound];
|
||||
}
|
||||
|
||||
//init json
|
||||
json = [NSString stringWithFormat:@"\"name\": \"%@\", \"path\": \"%@\", \"pid\": \"%@\", \"command line\": \"%@\", \"hashes\": %@, \"signature(s)\": %@, \"VT detection\": \"%@\", \"encrypted\": %d, \"packed\": %d, \"not found\": %d, \"dylibs\": [%@], \"files\": [%@], \"connections\": [%@]", self.binary.name, self.binary.path, self.pid, taskCommandLine, fileHashes, fileSigs, vtDetectionRatio, self.binary.isEncrypted, self.binary.isPacked, self.binary.notFound, dylibsJSON, filesJSON, connectionsJSON];
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
+13
-1
@@ -6,12 +6,21 @@
|
||||
//
|
||||
//
|
||||
|
||||
#import "Task.h"
|
||||
#import "Queue.h"
|
||||
#import "Consts.h"
|
||||
#import "Signing.h"
|
||||
#import "Utilities.h"
|
||||
#import "Connection.h"
|
||||
#import "3rdParty/OrderedDictionary.h"
|
||||
|
||||
#import <syslog.h>
|
||||
#import <signal.h>
|
||||
#import <unistd.h>
|
||||
#import <libproc.h>
|
||||
#import <sys/proc_info.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
@interface TaskEnumerator : NSObject
|
||||
{
|
||||
|
||||
@@ -28,6 +37,9 @@
|
||||
//all dylibs
|
||||
@property(nonatomic, retain)NSMutableDictionary* dylibs;
|
||||
|
||||
//flagged items
|
||||
@property(nonatomic, retain)NSMutableArray* flaggedItems;
|
||||
|
||||
//queue
|
||||
// ->contains binaries that should be processed
|
||||
@property (nonatomic, retain)Queue* binaryQueue;
|
||||
|
||||
+37
-49
@@ -6,17 +6,6 @@
|
||||
//
|
||||
//
|
||||
|
||||
#import <syslog.h>
|
||||
#import <signal.h>
|
||||
#import <unistd.h>
|
||||
#import <libproc.h>
|
||||
#import <sys/proc_info.h>
|
||||
|
||||
#import "Task.h"
|
||||
#import "Consts.h"
|
||||
#import "Signing.h"
|
||||
#import "Utilities.h"
|
||||
#import "Connection.h"
|
||||
#import "AppDelegate.h"
|
||||
#import "TaskEnumerator.h"
|
||||
|
||||
@@ -27,6 +16,7 @@
|
||||
@synthesize dylibs;
|
||||
@synthesize binaryQueue;
|
||||
@synthesize executables;
|
||||
@synthesize flaggedItems;
|
||||
|
||||
//init
|
||||
-(id)init
|
||||
@@ -44,6 +34,9 @@
|
||||
//alloc dylibs dictionary
|
||||
dylibs = [NSMutableDictionary dictionary];
|
||||
|
||||
//alloc flagged items
|
||||
flaggedItems = [NSMutableArray array];
|
||||
|
||||
//init binary processing queue
|
||||
binaryQueue = [[Queue alloc] init];
|
||||
}
|
||||
@@ -53,7 +46,7 @@
|
||||
|
||||
|
||||
//enumerate all tasks
|
||||
// ->calls back into app delegate to update task (top) table when pau
|
||||
// calls back into app delegate to update task (top) table when pau
|
||||
-(void)enumerateTasks
|
||||
{
|
||||
//(new) task item
|
||||
@@ -71,10 +64,6 @@
|
||||
//set state
|
||||
self.state = ENUMERATION_STATE_TASKS;
|
||||
|
||||
//determine if network is connected
|
||||
// ->sets 'isConnected' flag
|
||||
((AppDelegate*)[[NSApplication sharedApplication] delegate]).isConnected = isNetworkConnected();
|
||||
|
||||
//get all tasks
|
||||
// ->pids and binary obj with just path/name
|
||||
newTasks = [self getAllTasks];
|
||||
@@ -138,13 +127,18 @@
|
||||
//reload task table
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadTaskTable];
|
||||
|
||||
//call on main thread
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
//reload bottom pain
|
||||
// call on main thread
|
||||
if(YES != [NSThread isMainThread])
|
||||
{
|
||||
//main thread
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
|
||||
//reload bottom pane
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) selectBottomPaneContent:nil];
|
||||
|
||||
});
|
||||
//reload bottom pane
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) selectBottomPaneContent:nil];
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
//for new tasks
|
||||
// now generate signing info/encryption check/packer check
|
||||
@@ -226,7 +220,7 @@
|
||||
//nap
|
||||
[NSThread sleepForTimeInterval:0.01];
|
||||
}
|
||||
|
||||
|
||||
//set state
|
||||
self.state = ENUMERATION_STATE_FILES;
|
||||
|
||||
@@ -234,7 +228,6 @@
|
||||
count = 0;
|
||||
|
||||
//begin file enumeration
|
||||
// ->for search view
|
||||
for(NSNumber* key in newTasks)
|
||||
{
|
||||
//get task
|
||||
@@ -264,7 +257,7 @@
|
||||
//nap
|
||||
[NSThread sleepForTimeInterval:0.01];
|
||||
}
|
||||
|
||||
|
||||
//set state
|
||||
self.state = ENUMERATION_STATE_NETWORK;
|
||||
|
||||
@@ -272,7 +265,6 @@
|
||||
count = 0;
|
||||
|
||||
//begin network enumeration
|
||||
// ->for search view
|
||||
for(NSNumber* key in newTasks)
|
||||
{
|
||||
//get task
|
||||
@@ -544,10 +536,10 @@ bail:
|
||||
}
|
||||
|
||||
//sync to remove from all executables
|
||||
@synchronized(((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.executables)
|
||||
@synchronized(taskEnumerator.executables)
|
||||
{
|
||||
//remove dead executables
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.executables removeObjectForKey:deadTask.binary.path];
|
||||
[taskEnumerator.executables removeObjectForKey:deadTask.binary.path];
|
||||
}
|
||||
|
||||
//get parent
|
||||
@@ -576,7 +568,7 @@ bail:
|
||||
for(Binary* dylib in deadTask.dylibs)
|
||||
{
|
||||
//skip dylibs that aren't flagged
|
||||
if(YES != [((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedItems containsObject:dylib])
|
||||
if(YES != [taskEnumerator.flaggedItems containsObject:dylib])
|
||||
{
|
||||
//skip
|
||||
continue;
|
||||
@@ -596,15 +588,15 @@ bail:
|
||||
|
||||
//dylib is flagged and only hosted in dead task
|
||||
// ->remove it from flaggedItems
|
||||
@synchronized(((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedItems)
|
||||
@synchronized(taskEnumerator.flaggedItems)
|
||||
{
|
||||
//remove
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedItems removeObject:dylib];
|
||||
[taskEnumerator.flaggedItems removeObject:dylib];
|
||||
}
|
||||
}
|
||||
|
||||
//also remove task if its flagged and only instance
|
||||
if(YES == [((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedItems containsObject:deadTask.binary])
|
||||
if(YES == [taskEnumerator.flaggedItems containsObject:deadTask.binary])
|
||||
{
|
||||
//get number of task instances
|
||||
// ->might be more (flagged) instances that are still alive
|
||||
@@ -625,17 +617,17 @@ bail:
|
||||
if(1 == taskInstances)
|
||||
{
|
||||
//sync and remove
|
||||
@synchronized(((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedItems)
|
||||
@synchronized(taskEnumerator.flaggedItems)
|
||||
{
|
||||
//remove
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedItems removeObject:deadTask.binary];
|
||||
[taskEnumerator.flaggedItems removeObject:deadTask.binary];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//when there are no flagged items
|
||||
// ->(re)set flagged icon to black
|
||||
if(0 == ((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedItems.count)
|
||||
if(0 == taskEnumerator.flaggedItems.count)
|
||||
{
|
||||
//set main image
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedButton setImage:[NSImage imageNamed:@"flagged"]];
|
||||
@@ -706,7 +698,6 @@ bail:
|
||||
return matchingTasks;
|
||||
}
|
||||
|
||||
|
||||
//get all tasks a dylib/file is loaded into
|
||||
-(NSMutableArray*)loadedIn:(id)item
|
||||
{
|
||||
@@ -771,8 +762,8 @@ bail:
|
||||
if(YES == isDylib)
|
||||
{
|
||||
//sync
|
||||
//@synchronized(task.dylibs)
|
||||
//{
|
||||
@synchronized(task.dylibs)
|
||||
{
|
||||
//check if dylib is loaded in task
|
||||
for(Binary* taskDylib in task.dylibs)
|
||||
{
|
||||
@@ -787,7 +778,7 @@ bail:
|
||||
}
|
||||
}
|
||||
|
||||
//}//sync
|
||||
}//sync
|
||||
|
||||
}//dylibs
|
||||
|
||||
@@ -795,8 +786,8 @@ bail:
|
||||
else if(YES == isFile)
|
||||
{
|
||||
//sync
|
||||
//@synchronized(task.files)
|
||||
//{
|
||||
@synchronized(task.files)
|
||||
{
|
||||
//check if file is loaded in task
|
||||
for(File* taskFile in task.files)
|
||||
{
|
||||
@@ -810,7 +801,7 @@ bail:
|
||||
break;
|
||||
}
|
||||
}
|
||||
//}//sync
|
||||
}//sync
|
||||
|
||||
}//files
|
||||
|
||||
@@ -818,8 +809,8 @@ bail:
|
||||
else if(YES == isConnection)
|
||||
{
|
||||
//sync
|
||||
//@synchronized(task.connections)
|
||||
//{
|
||||
@synchronized(task.connections)
|
||||
{
|
||||
//check if connection is 'in' task
|
||||
for(Connection* taskConnection in task.connections)
|
||||
{
|
||||
@@ -834,12 +825,10 @@ bail:
|
||||
break;
|
||||
}
|
||||
}
|
||||
//}//sync
|
||||
}//sync
|
||||
|
||||
}//connections
|
||||
|
||||
|
||||
|
||||
|
||||
}//all tasks
|
||||
|
||||
}//sync
|
||||
@@ -850,5 +839,4 @@ bail:
|
||||
return hostTasks;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.7.0</string>
|
||||
<string>2.0.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.7.0</string>
|
||||
<string>2.0.0</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
@@ -30,5 +30,7 @@
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplicationKeyEvents</string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -222,6 +222,7 @@
|
||||
CD1508DE21BB13980081F1AF /* Signing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Signing.h; sourceTree = SOURCE_ROOT; };
|
||||
CD1D14EB21B7A5DB00FF7F4B /* Sentry.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sentry.framework; path = Carthage/Build/Mac/Sentry.framework; sourceTree = "<group>"; };
|
||||
CD1D14F221B8F2FC00FF7F4B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = SOURCE_ROOT; };
|
||||
CD24FE7621C6FC3D00900B61 /* main.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = main.h; sourceTree = SOURCE_ROOT; };
|
||||
CD3F4CE61AF5CF68002A2647 /* TaskEnumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TaskEnumerator.m; sourceTree = SOURCE_ROOT; };
|
||||
CD3F4CE71AF5CF68002A2647 /* TaskEnumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TaskEnumerator.h; sourceTree = SOURCE_ROOT; };
|
||||
CD3F4CE91AF6D948002A2647 /* OrderedDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OrderedDictionary.h; path = 3rdParty/OrderedDictionary.h; sourceTree = "<group>"; };
|
||||
@@ -479,6 +480,7 @@
|
||||
CDA81D581A95B4B4009790E2 /* TaskExplorer-Info.plist */,
|
||||
CDA81D591A95B4B4009790E2 /* TaskExplorer-Prefix.pch */,
|
||||
CDA81D5A1A95B4B4009790E2 /* main.m */,
|
||||
CD24FE7621C6FC3D00900B61 /* main.h */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
|
||||
BIN
Binary file not shown.
@@ -46,6 +46,12 @@
|
||||
ReferencedContainer = "container:TaskExplorer.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "-scan"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
<EnvironmentVariables>
|
||||
<EnvironmentVariable
|
||||
key = "CA_DEBUG_TRANSACTIONS"
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
if(YES != isFiltered)
|
||||
{
|
||||
//get tasks
|
||||
tasks = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks;
|
||||
tasks = taskEnumerator.tasks;
|
||||
|
||||
//set count
|
||||
rows = tasks.count;
|
||||
@@ -150,7 +150,7 @@
|
||||
if(YES != isFiltered)
|
||||
{
|
||||
//grab tasks
|
||||
tasks = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks;
|
||||
tasks = taskEnumerator.tasks;
|
||||
|
||||
//sanity check
|
||||
// ->make sure there is table item for row
|
||||
@@ -325,7 +325,7 @@ bail:
|
||||
NSUInteger taskIndex = 0;
|
||||
|
||||
//grab tasks
|
||||
tasks = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks;
|
||||
tasks = taskEnumerator.tasks;
|
||||
|
||||
//get task
|
||||
selectedTask = [self taskForRow:nil];
|
||||
@@ -427,7 +427,7 @@ bail:
|
||||
Task* task = nil;
|
||||
|
||||
//grab tasks
|
||||
tasks = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks;
|
||||
tasks = taskEnumerator.tasks;
|
||||
|
||||
//use sender if provided
|
||||
if(nil != sender)
|
||||
@@ -658,7 +658,7 @@ bail:
|
||||
OrderedDictionary* tasks = nil;
|
||||
|
||||
//grab tasks
|
||||
tasks = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks;
|
||||
tasks = taskEnumerator.tasks;
|
||||
|
||||
//number of children
|
||||
NSUInteger numberOfChildren = 0;
|
||||
@@ -708,7 +708,7 @@ bail:
|
||||
Task* task = nil;
|
||||
|
||||
//grab all tasks
|
||||
tasks = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks;
|
||||
tasks = taskEnumerator.tasks;
|
||||
|
||||
//root item
|
||||
if(nil == item)
|
||||
@@ -797,7 +797,7 @@ bail:
|
||||
}
|
||||
|
||||
//grab tasks
|
||||
tasks = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.tasks;
|
||||
tasks = taskEnumerator.tasks;
|
||||
|
||||
//get index of newly selected row
|
||||
newlySelectedRow = [self.itemView selectedRow];
|
||||
@@ -834,7 +834,7 @@ bail:
|
||||
((kkRowCell*)selectedView).color = nil;
|
||||
|
||||
//remove task from task emumerator
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator removeTask:task];
|
||||
[taskEnumerator removeTask:task];
|
||||
|
||||
//when filtering
|
||||
// ->remove deceased task from filter tasks
|
||||
|
||||
+11
-13
@@ -1,10 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11542" systemVersion="16B2555" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11542"/>
|
||||
<capability name="Alignment constraints to the first baseline" minToolsVersion="6.0"/>
|
||||
<capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@@ -75,7 +73,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button toolTip="show in finder" verticalHuggingPriority="750" tag="107" translatesAutoresizingMaskIntoConstraints="NO" id="Y4J-KD-8iy">
|
||||
<rect key="frame" x="1313" y="15" width="18" height="18"/>
|
||||
<rect key="frame" x="1313" y="16" width="18" height="18"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="18" id="3hg-GE-xpO"/>
|
||||
<constraint firstAttribute="width" constant="18" id="ap5-un-UhM"/>
|
||||
@@ -107,7 +105,7 @@
|
||||
</constraints>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="item path" id="7xA-XP-LK6">
|
||||
<font key="font" size="11" name="Menlo-Regular"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
@@ -211,7 +209,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button toolTip="show in finder" verticalHuggingPriority="750" tag="107" translatesAutoresizingMaskIntoConstraints="NO" id="bau-Td-7ST">
|
||||
<rect key="frame" x="1313" y="15" width="18" height="18"/>
|
||||
<rect key="frame" x="1313" y="16" width="18" height="18"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="18" id="DPj-gb-dIB"/>
|
||||
<constraint firstAttribute="width" constant="18" id="oNg-zE-nJv"/>
|
||||
@@ -243,7 +241,7 @@
|
||||
</constraints>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="item path" id="efL-9x-1ue">
|
||||
<font key="font" size="11" name="Menlo-Regular"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
@@ -369,7 +367,7 @@
|
||||
</constraints>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="item path" id="1WZ-C2-VvW">
|
||||
<font key="font" size="11" name="Menlo-Regular"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
@@ -439,7 +437,7 @@
|
||||
</constraints>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="item path" id="DlS-aR-tJt">
|
||||
<font key="font" size="11" name="Menlo-Regular"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
@@ -506,13 +504,13 @@
|
||||
</connections>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="backgroundColor"/>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" controlSize="mini" horizontal="YES" id="gmJ-1g-Tah">
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" controlSize="mini" horizontal="YES" id="gmJ-1g-Tah">
|
||||
<rect key="frame" x="1" y="279" width="1354" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" controlSize="mini" horizontal="NO" id="cwi-rA-cqP">
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" controlSize="mini" horizontal="NO" id="cwi-rA-cqP">
|
||||
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
|
||||
+5
-5
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11542" systemVersion="16B2555" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11542"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@@ -105,7 +105,7 @@
|
||||
</constraints>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="item path" id="F8I-N5-f4N">
|
||||
<font key="font" size="11" name="Menlo-Regular"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
@@ -199,11 +199,11 @@
|
||||
</outlineView>
|
||||
</subviews>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" controlSize="mini" horizontal="YES" id="7QY-ke-KIc">
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" controlSize="mini" horizontal="YES" id="7QY-ke-KIc">
|
||||
<rect key="frame" x="0.0" y="280" width="1356" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" controlSize="mini" horizontal="NO" id="ifK-Bg-0Ke">
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" controlSize="mini" horizontal="NO" id="ifK-Bg-0Ke">
|
||||
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
|
||||
+24
-10
@@ -1,8 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="14C1514" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6751"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="VTInfoWindowController">
|
||||
@@ -24,17 +25,18 @@
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="VirusTotal Information" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
|
||||
<window title="VirusTotal Information" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="534" height="144"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||
<view key="contentView" id="se5-gp-TjO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="534" height="144"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uJq-Bw-lDQ">
|
||||
<rect key="frame" x="430" y="13" width="90" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="close" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="RwO-yA-Xwk">
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Close" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="RwO-yA-Xwk">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
</buttonCell>
|
||||
@@ -43,7 +45,8 @@
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="hLU-fi-qXH">
|
||||
<rect key="frame" x="189" y="81" width="650" height="19"/>
|
||||
<rect key="frame" x="189" y="81" width="327" height="19"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="ratio" id="XJ7-Go-bkG">
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -52,6 +55,7 @@
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Wbv-SK-w53" customClass="HyperlinkTextField">
|
||||
<rect key="frame" x="189" y="53" width="650" height="19"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="report url" id="hR8-Wz-esN">
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -60,6 +64,7 @@
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DFa-vc-wFY">
|
||||
<rect key="frame" x="99" y="81" width="83" height="19"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="detection:" id="iBS-9J-ok9">
|
||||
<font key="font" size="13" name="Menlo-Bold"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -68,6 +73,7 @@
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tM6-Nq-1Rh">
|
||||
<rect key="frame" x="57" y="53" width="125" height="19"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="more info:" id="U3V-A7-jO8">
|
||||
<font key="font" size="13" name="Menlo-Bold"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -76,6 +82,7 @@
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="hcM-hb-3Lz" customClass="HyperlinkTextField">
|
||||
<rect key="frame" x="189" y="106" width="650" height="19"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" title="file name" id="3TS-NZ-XYE">
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -84,6 +91,7 @@
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Plg-lF-OEE">
|
||||
<rect key="frame" x="99" y="107" width="83" height="19"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="file name:" id="HO9-t1-yCi">
|
||||
<font key="font" size="13" name="Menlo-Bold"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -92,11 +100,13 @@
|
||||
</textField>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vO5-mf-hOM">
|
||||
<rect key="frame" x="20" y="56" width="68" height="68"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="vtLogo" id="qFc-P8-Wm8"/>
|
||||
</imageView>
|
||||
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eEi-hA-pdx">
|
||||
<rect key="frame" x="18" y="81" width="498" height="21"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="unknown file" id="2nj-QY-6ot">
|
||||
<rect key="frame" x="99" y="80" width="417" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="unknown file" id="2nj-QY-6ot">
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -104,7 +114,8 @@
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Sxd-Ob-1Af">
|
||||
<rect key="frame" x="340" y="13" width="90" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="submit?" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="JAW-nH-eRf">
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Submit?" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="JAW-nH-eRf">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
</buttonCell>
|
||||
@@ -114,9 +125,11 @@
|
||||
</button>
|
||||
<customView hidden="YES" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="S16-Le-yTU">
|
||||
<rect key="frame" x="0.0" y="-12" width="534" height="156"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Hew-Ee-HGX">
|
||||
<rect key="frame" x="18" y="52" width="498" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="status msg" id="lxM-ES-i7K">
|
||||
<font key="font" size="13" name="Menlo-Regular"/>
|
||||
<color key="textColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
@@ -125,6 +138,7 @@
|
||||
</textField>
|
||||
<progressIndicator hidden="YES" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" maxValue="100" bezeled="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="egv-M2-RqY">
|
||||
<rect key="frame" x="251" y="74" width="32" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</progressIndicator>
|
||||
</subviews>
|
||||
</customView>
|
||||
|
||||
+10
@@ -15,6 +15,9 @@
|
||||
|
||||
/* FUNCTIONS */
|
||||
|
||||
//disable std err
|
||||
void disableSTDERR(void);
|
||||
|
||||
//loads a framework
|
||||
// note: assumes it is in 'Framework' dir
|
||||
NSBundle* loadFramework(NSString* name);
|
||||
@@ -84,8 +87,15 @@ BOOL Is32Bit(pid_t targetPID);
|
||||
// ->based on http://lapcatsoftware.com/articles/detect-app-translocation.html
|
||||
NSURL* getUnTranslocatedURL();
|
||||
|
||||
//give a list of paths
|
||||
// convert any `~` to all or current user
|
||||
NSMutableArray* expandPaths(const __strong NSString* const paths[], int count);
|
||||
|
||||
//check if (full) dark mode
|
||||
// meaning, Mojave+ and dark mode enabled
|
||||
BOOL isDarkMode();
|
||||
|
||||
//bring an app to foreground (to get an icon in the dock) or background
|
||||
void transformProcess(ProcessApplicationTransformState location);
|
||||
|
||||
#endif
|
||||
|
||||
+161
@@ -20,8 +20,28 @@
|
||||
#import <Security/Security.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
#import <CoreServices/CoreServices.h>
|
||||
#import <Collaboration/Collaboration.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
|
||||
//disable std err
|
||||
void disableSTDERR()
|
||||
{
|
||||
//file handle
|
||||
int devNull = -1;
|
||||
|
||||
//open /dev/null
|
||||
devNull = open("/dev/null", O_RDWR);
|
||||
|
||||
//dup
|
||||
dup2(devNull, STDERR_FILENO);
|
||||
|
||||
//close
|
||||
close(devNull);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//init crash reporting
|
||||
void initCrashReporting()
|
||||
{
|
||||
@@ -1182,7 +1202,128 @@ bail:
|
||||
}
|
||||
|
||||
return untranslocatedURL;
|
||||
}
|
||||
|
||||
//get all user
|
||||
// includes name/home directory
|
||||
NSMutableDictionary* allUsers()
|
||||
{
|
||||
//users
|
||||
NSMutableDictionary* users = nil;
|
||||
|
||||
//query
|
||||
CSIdentityQueryRef query = nil;
|
||||
|
||||
//query results
|
||||
CFArrayRef results = NULL;
|
||||
|
||||
//error
|
||||
CFErrorRef error = NULL;
|
||||
|
||||
//identiry
|
||||
CBIdentity* identity = NULL;
|
||||
|
||||
//alloc dictionary
|
||||
users = [NSMutableDictionary dictionary];
|
||||
|
||||
//init query
|
||||
query = CSIdentityQueryCreate(NULL, kCSIdentityClassUser, CSGetLocalIdentityAuthority());
|
||||
|
||||
//exec query
|
||||
if(true != CSIdentityQueryExecute(query, 0, &error))
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//grab results
|
||||
results = CSIdentityQueryCopyResults(query);
|
||||
|
||||
//process all results
|
||||
// add user and home directory
|
||||
for (int i = 0; i < CFArrayGetCount(results); ++i)
|
||||
{
|
||||
//grab identity
|
||||
identity = [CBIdentity identityWithCSIdentity:(CSIdentityRef)CFArrayGetValueAtIndex(results, i)];
|
||||
|
||||
//add user
|
||||
users[identity.UUIDString] = @{USER_NAME:identity.posixName, USER_DIRECTORY:NSHomeDirectoryForUser(identity.posixName)};
|
||||
}
|
||||
|
||||
bail:
|
||||
|
||||
//release results
|
||||
if(NULL != results)
|
||||
{
|
||||
//release
|
||||
CFRelease(results);
|
||||
}
|
||||
|
||||
//release query
|
||||
if(NULL != query)
|
||||
{
|
||||
//release
|
||||
CFRelease(query);
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
|
||||
//give a list of paths
|
||||
// convert any `~` to all or current user
|
||||
NSMutableArray* expandPaths(const __strong NSString* const paths[], int count)
|
||||
{
|
||||
//expanded paths
|
||||
NSMutableArray* expandedPaths = nil;
|
||||
|
||||
//(current) path
|
||||
const NSString* path = nil;
|
||||
|
||||
//all users
|
||||
NSMutableDictionary* users = nil;
|
||||
|
||||
//grab all users
|
||||
users = allUsers();
|
||||
|
||||
//alloc list
|
||||
expandedPaths = [NSMutableArray array];
|
||||
|
||||
//iterate/expand
|
||||
for(NSInteger i = 0; i < count; i++)
|
||||
{
|
||||
//grab path
|
||||
path = paths[i];
|
||||
|
||||
//no `~`?
|
||||
// just add and continue
|
||||
if(YES != [path hasPrefix:@"~"])
|
||||
{
|
||||
//add as is
|
||||
[expandedPaths addObject:path];
|
||||
|
||||
//next
|
||||
continue;
|
||||
}
|
||||
|
||||
//handle '~' case
|
||||
// root? add each user
|
||||
if(0 == geteuid())
|
||||
{
|
||||
//add each user
|
||||
for(NSString* user in users)
|
||||
{
|
||||
[expandedPaths addObject:[users[user][USER_DIRECTORY] stringByAppendingPathComponent:[path substringFromIndex:1]]];
|
||||
}
|
||||
}
|
||||
//otherwise
|
||||
// just convert to current user
|
||||
else
|
||||
{
|
||||
[expandedPaths addObject:[path stringByExpandingTildeInPath]];
|
||||
}
|
||||
}
|
||||
|
||||
return expandedPaths;
|
||||
}
|
||||
|
||||
//check if (full) dark mode
|
||||
@@ -1215,6 +1356,26 @@ bail:
|
||||
return darkMode;
|
||||
}
|
||||
|
||||
//bring an app to foreground (to get an icon in the dock) or background
|
||||
void transformProcess(ProcessApplicationTransformState location)
|
||||
{
|
||||
//process serial no
|
||||
ProcessSerialNumber processSerialNo;
|
||||
|
||||
//init process stuct
|
||||
// ->high to 0
|
||||
processSerialNo.highLongOfPSN = 0;
|
||||
|
||||
//init process stuct
|
||||
// ->low to self
|
||||
processSerialNo.lowLongOfPSN = kCurrentProcess;
|
||||
|
||||
//transform to foreground
|
||||
TransformProcessType(&processSerialNo, location);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+10
-12
@@ -42,7 +42,7 @@
|
||||
}
|
||||
|
||||
//automatically invoked when mouse-down occurs
|
||||
// ->set color to light gray or light red
|
||||
// ->set color to default or red
|
||||
-(void)mouseDown:(NSEvent *)theEvent;
|
||||
{
|
||||
//mouse down/over color
|
||||
@@ -60,11 +60,10 @@
|
||||
color = [NSColor colorWithCalibratedRed:(255/255.0f) green:(1.0/255.0f) blue:(1.0/255.0f) alpha:0.5];
|
||||
}
|
||||
//non-flagged files
|
||||
//gray
|
||||
else
|
||||
{
|
||||
//gray
|
||||
color = NSColor.grayColor;
|
||||
//default
|
||||
color = NSColor.controlTextColor;
|
||||
}
|
||||
|
||||
//set string
|
||||
@@ -74,7 +73,7 @@
|
||||
}
|
||||
|
||||
//automatically invoked when mouse-up occurs
|
||||
// ->reset color to gray or red and trigger mouse click logic (if necessary)
|
||||
// ->reset color to default or red and trigger mouse click logic (if necessary)
|
||||
-(void)mouseUp:(NSEvent *)theEvent;
|
||||
{
|
||||
//mouse up color
|
||||
@@ -135,8 +134,8 @@
|
||||
// set (back) to default
|
||||
else
|
||||
{
|
||||
//gray
|
||||
color = NSColor.grayColor;
|
||||
//default
|
||||
color = NSColor.controlTextColor;
|
||||
}
|
||||
|
||||
//set string
|
||||
@@ -156,7 +155,7 @@
|
||||
self.mouseExit = YES;
|
||||
|
||||
//check if mouse is down
|
||||
// ->set color to gray/lightish red
|
||||
// ->set color to default/red
|
||||
if(YES == self.mouseDown)
|
||||
{
|
||||
//flagged files
|
||||
@@ -168,11 +167,10 @@
|
||||
color = [NSColor colorWithCalibratedRed:(255/255.0f) green:(1.0/255.0f) blue:(1.0/255.0f) alpha:0.66];
|
||||
}
|
||||
//non-flagged files
|
||||
// ->just black
|
||||
else
|
||||
{
|
||||
//gray
|
||||
color = NSColor.grayColor;
|
||||
//default
|
||||
color = NSColor.controlTextColor;
|
||||
}
|
||||
}
|
||||
//mouse is up
|
||||
@@ -191,7 +189,7 @@
|
||||
// set (back) to default
|
||||
else
|
||||
{
|
||||
//gray
|
||||
//default
|
||||
color = NSColor.controlTextColor;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
makeTextViewHyperlink(self.analysisURL, [NSURL URLWithString:self.item.vtInfo[VT_RESULTS_URL]]);
|
||||
|
||||
//set 'submit' button text to 'rescan'
|
||||
self.submitButton.title = @"rescan?";
|
||||
self.submitButton.title = @"Rescan?";
|
||||
}
|
||||
//unknown file
|
||||
else
|
||||
@@ -236,9 +236,21 @@
|
||||
//pre-req
|
||||
[self.overlayView setWantsLayer:YES];
|
||||
|
||||
//set overlay's view color to white
|
||||
self.overlayView.layer.backgroundColor = [NSColor whiteColor].CGColor;
|
||||
|
||||
//dark mode
|
||||
// set overlay to light
|
||||
if(YES == isDarkMode())
|
||||
{
|
||||
//set overlay's view color to gray
|
||||
self.overlayView.layer.backgroundColor = NSColor.lightGrayColor.CGColor;
|
||||
}
|
||||
//light mode
|
||||
// set overlay to gray
|
||||
else
|
||||
{
|
||||
//set to gray
|
||||
self.overlayView.layer.backgroundColor = NSColor.grayColor.CGColor;
|
||||
}
|
||||
|
||||
//make it semi-transparent
|
||||
self.overlayView.alphaValue = 0.85;
|
||||
|
||||
@@ -252,7 +264,7 @@
|
||||
[self.progressIndicator startAnimation:nil];
|
||||
|
||||
//rescan file?
|
||||
if(YES == [((NSButton*)sender).title isEqualToString:@"rescan?"])
|
||||
if(YES == [((NSButton*)sender).title isEqualToString:@"Rescan?"])
|
||||
{
|
||||
//set status msg
|
||||
[self.statusMsg setStringValue:[NSString stringWithFormat:@"submitting re-scan request for %@", self.item.name]];
|
||||
|
||||
+2
-4
@@ -19,6 +19,8 @@
|
||||
//array for (up to 25) items
|
||||
@property(nonatomic, retain)NSMutableArray* items;
|
||||
|
||||
//array for all threads
|
||||
@property(nonatomic, retain)NSMutableArray* vtThreads;
|
||||
|
||||
/* METHODS */
|
||||
|
||||
@@ -26,10 +28,6 @@
|
||||
// ->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;
|
||||
|
||||
//make the (POST)query to VT
|
||||
-(NSDictionary*)postRequest:(NSURL*)url parameters:(id)params;
|
||||
|
||||
|
||||
+49
-33
@@ -17,6 +17,7 @@
|
||||
@implementation VirusTotal
|
||||
|
||||
@synthesize items;
|
||||
@synthesize vtThreads;
|
||||
|
||||
//init
|
||||
-(id)init
|
||||
@@ -28,8 +29,11 @@
|
||||
//alloc array for items
|
||||
items = [NSMutableArray array];
|
||||
|
||||
//init array for virus total threads
|
||||
vtThreads = [NSMutableArray array];
|
||||
|
||||
//kick of thread to watch/flush queue
|
||||
// ->will flush if item not processed in 30 seconds
|
||||
// ->will flush if item not processed in 3 seconds
|
||||
[NSThread detachNewThreadSelector:@selector(queueFlusher) toTarget:self withObject:nil];
|
||||
}
|
||||
|
||||
@@ -98,6 +102,9 @@
|
||||
//items to process
|
||||
NSMutableArray* vtItems = nil;
|
||||
|
||||
//virus total thread
|
||||
NSThread* virusTotalThread = nil;
|
||||
|
||||
//sync
|
||||
@synchronized(self.items)
|
||||
{
|
||||
@@ -108,10 +115,21 @@
|
||||
if(VT_MAX_QUERY_COUNT == self.items.count)
|
||||
{
|
||||
//make copy
|
||||
vtItems = [NSMutableArray arrayWithArray:self.items];
|
||||
|
||||
//kick of thread to make a query to VT
|
||||
[NSThread detachNewThreadSelector:@selector(queryVT:) toTarget:self withObject:vtItems];
|
||||
vtItems = [NSMutableArray arrayWithArray:self.items];
|
||||
|
||||
//alloc thread
|
||||
// ->will query virus total to get info about all detected items
|
||||
virusTotalThread = [[NSThread alloc] initWithTarget:self selector:@selector(queryVT:) object:vtItems];
|
||||
|
||||
//start thread
|
||||
[virusTotalThread start];
|
||||
|
||||
//sync
|
||||
@synchronized(self.vtThreads)
|
||||
{
|
||||
//save it into array
|
||||
[self.vtThreads addObject:virusTotalThread];
|
||||
}
|
||||
|
||||
//remove all items
|
||||
[self.items removeAllObjects];
|
||||
@@ -226,22 +244,29 @@
|
||||
//save flagged item
|
||||
if(0 != [results[VT_RESULTS_POSITIVES] unsignedIntegerValue])
|
||||
{
|
||||
//save
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) saveFlaggedBinary:item];
|
||||
//sync to check/add
|
||||
@synchronized(taskEnumerator.flaggedItems)
|
||||
{
|
||||
if(YES != [taskEnumerator.flaggedItems containsObject:item])
|
||||
{
|
||||
//save
|
||||
[taskEnumerator.flaggedItems addObject:item];
|
||||
}
|
||||
}
|
||||
}
|
||||
//for non-flagged items
|
||||
// ->remove from list, if they were previously flagged
|
||||
// remove from list, if it was previously flagged
|
||||
else
|
||||
{
|
||||
//check if previously flagged
|
||||
// ->then remove
|
||||
if(YES == [((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedItems containsObject:item])
|
||||
//sync to check/remove
|
||||
@synchronized(taskEnumerator.flaggedItems)
|
||||
{
|
||||
//sync to remove
|
||||
@synchronized(((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedItems)
|
||||
//check if previously flagged
|
||||
// ...if so, then remove
|
||||
if(YES == [taskEnumerator.flaggedItems containsObject:item])
|
||||
{
|
||||
//remove
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedItems removeObject:item];
|
||||
[taskEnumerator.flaggedItems removeObject:item];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,9 +325,6 @@
|
||||
postData = [NSJSONSerialization dataWithJSONObject:params options:kNilOptions error:nil];
|
||||
if(nil == postData)
|
||||
{
|
||||
//err msg
|
||||
syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: failed to convert request %s to JSON", [[postData description] UTF8String]);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
@@ -354,9 +376,6 @@
|
||||
//bail on any exceptions
|
||||
@catch (NSException *exception)
|
||||
{
|
||||
//err msg
|
||||
syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: converting response %s to JSON threw %s", [[vtData description] UTF8String], [[exception description] UTF8String]);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
@@ -364,9 +383,6 @@
|
||||
//sanity check
|
||||
if(nil == results)
|
||||
{
|
||||
//err msg
|
||||
syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: failed to convert response %s to JSON", [[vtData description] UTF8String]);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
@@ -513,13 +529,11 @@ bail:
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
//submit a rescan request
|
||||
-(NSDictionary*)reScan:(Binary*)item
|
||||
{
|
||||
@@ -551,7 +565,6 @@ bail:
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
return result;
|
||||
@@ -563,7 +576,7 @@ bail:
|
||||
{
|
||||
//queried binary obj
|
||||
Binary* queriedItem = nil;
|
||||
|
||||
|
||||
//process all results
|
||||
// ->save VT result dictionary into File obj
|
||||
for(NSDictionary* result in results[VT_RESULTS])
|
||||
@@ -571,8 +584,6 @@ bail:
|
||||
//extract ('match') queried item
|
||||
// ->VT gives us back a hash
|
||||
queriedItem = queriedItems[result[@"hash"]];
|
||||
|
||||
//sanity check
|
||||
if(nil == queriedItem)
|
||||
{
|
||||
//skip
|
||||
@@ -582,11 +593,16 @@ bail:
|
||||
//save VT results into item
|
||||
queriedItem.vtInfo = result;
|
||||
|
||||
//save flagged item
|
||||
if(0 != [result[VT_RESULTS_POSITIVES] unsignedIntegerValue])
|
||||
//sync to check/add
|
||||
@synchronized(taskEnumerator.flaggedItems)
|
||||
{
|
||||
//save
|
||||
[((AppDelegate*)[[NSApplication sharedApplication] delegate]) saveFlaggedBinary:queriedItem];
|
||||
//save flagged item
|
||||
if( (0 != [result[VT_RESULTS_POSITIVES] unsignedIntegerValue]) &&
|
||||
(YES != [taskEnumerator.flaggedItems containsObject:queriedItem]) )
|
||||
{
|
||||
//save
|
||||
[taskEnumerator.flaggedItems addObject:queriedItem];
|
||||
}
|
||||
}
|
||||
|
||||
//call up into app delegate to smartly reload
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// main.h
|
||||
// TaskExplorer
|
||||
//
|
||||
// Created by Patrick Wardle on 12/16/18.
|
||||
// Copyright © 2018 Lucas Derraugh. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef main_h
|
||||
#define main_h
|
||||
|
||||
#import "Consts.h"
|
||||
#import "Filter.h"
|
||||
#import "Utilities.h"
|
||||
#import "VirusTotal.h"
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import "TaskEnumerator.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
//(privacy) protected directories
|
||||
NSString * const PROTECTED_DIRECTORIES[] = {@"~/Library/Application Support/AddressBook", @"~/Library/Calendars", @"~/Pictures", @"~/Library/Mail", @"~/Library/Messages", @"~/Library/Safari", @"~/Library/Cookies", @"~/Library/HomeKit", @"~/Library/IdentityServices", @"~/Library/Metadata/CoreSpotlight", @"~/Library/PersonalizationPortrait", @"~/Library/Suggestions"};
|
||||
|
||||
/* GLOBALS */
|
||||
|
||||
//task enumerator obj
|
||||
TaskEnumerator* taskEnumerator = nil;
|
||||
|
||||
//virustotal obj
|
||||
VirusTotal* virusTotal = nil;
|
||||
|
||||
//network connected flag
|
||||
BOOL isConnected = NO;
|
||||
|
||||
//(privacy) protected directories
|
||||
NSArray* protectedDirectories = nil;
|
||||
|
||||
/* FUNCTIONS */
|
||||
|
||||
//print usage
|
||||
void usage(void);
|
||||
|
||||
//perform a cmdline enumeration
|
||||
void cmdlineExplore(void);
|
||||
|
||||
//block until vt queries are done
|
||||
void completeVTQuery(void);
|
||||
|
||||
//pretty print JSON
|
||||
void prettyPrintJSON(NSString* output);
|
||||
|
||||
#endif /* main_h */
|
||||
@@ -6,19 +6,22 @@
|
||||
// Copyright (c) 2015 Objective-See. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Consts.h"
|
||||
#import "Utilities.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "main.h"
|
||||
|
||||
//main interface
|
||||
// ->contains extra logic to handle app translocation
|
||||
// contains extra logic to handle app translocation
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
//return
|
||||
int status = -1;
|
||||
|
||||
//disable stderr
|
||||
// sentry dumps to this, and we want only JSON to output...
|
||||
disableSTDERR();
|
||||
|
||||
//init crash reporting
|
||||
initCrashReporting();
|
||||
|
||||
//untranslocated URL
|
||||
NSURL* untranslocatedURL = nil;
|
||||
|
||||
@@ -29,8 +32,11 @@ int main(int argc, char *argv[])
|
||||
//remove quarantine attributes of original
|
||||
execTask(XATTR, @[@"-cr", untranslocatedURL.path], NO);
|
||||
|
||||
//nap
|
||||
[NSThread sleepForTimeInterval:0.5];
|
||||
|
||||
//relaunch
|
||||
// ->use 'open' since allows two instances of app to be run
|
||||
// use 'open' since allows two instances of app to be run
|
||||
execTask(OPEN, @[@"-n", @"-a", untranslocatedURL.path], NO);
|
||||
|
||||
//happy
|
||||
@@ -40,17 +46,348 @@ int main(int argc, char *argv[])
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//app isn't translocated
|
||||
// ->can just run app as is
|
||||
//set network connection flag
|
||||
isConnected = isNetworkConnected();
|
||||
|
||||
//init set of (privacy) protected directories
|
||||
// these will be skipped, as otherwise we will generate a privacy prompt
|
||||
protectedDirectories = expandPaths(PROTECTED_DIRECTORIES, sizeof(PROTECTED_DIRECTORIES)/sizeof(PROTECTED_DIRECTORIES[0]));
|
||||
|
||||
//handle '-h' or '-help'
|
||||
if( (YES == [[[NSProcessInfo processInfo] arguments] containsObject:@"-h"]) ||
|
||||
(YES == [[[NSProcessInfo processInfo] arguments] containsObject:@"-help"]) )
|
||||
{
|
||||
//print usage
|
||||
usage();
|
||||
|
||||
//done
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//handle cmdline
|
||||
// scan, explore, etc
|
||||
if( (YES == [[[NSProcessInfo processInfo] arguments] containsObject:@"-scan"]) ||
|
||||
(YES == [[[NSProcessInfo processInfo] arguments] containsObject:@"-explore"]) )
|
||||
|
||||
{
|
||||
//first check rooot
|
||||
if(0 != geteuid())
|
||||
{
|
||||
//err msg
|
||||
printf("{\"ERROR\": \"TASKEXPLORER (cmdline) requires root\"}\n");
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//scan
|
||||
cmdlineExplore();
|
||||
|
||||
//happy
|
||||
status = 0;
|
||||
|
||||
//done
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//otherwise
|
||||
// just kick off app for UI instance
|
||||
else
|
||||
{
|
||||
//invoke app's main
|
||||
status = NSApplicationMain(argc, (const char **)argv);
|
||||
}
|
||||
|
||||
//bail
|
||||
bail:
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
//print usage
|
||||
void usage()
|
||||
{
|
||||
//usage
|
||||
printf("\nTASKEXPLORER USAGE:\n");
|
||||
printf(" -h or -help display this usage info\n");
|
||||
printf(" -scan scan all tasks and dylibs \n");
|
||||
printf(" -explore enumerate all tasks and dylibs\n");
|
||||
printf("\noptions:\n");
|
||||
printf(" -pretty json output is 'pretty-printed'\n");
|
||||
printf(" -skipVT do not query VirusTotal (when '-explore' is specified)\n");
|
||||
printf(" -full for each task; include dylibs, files, & network connections\n\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//perform a cmdline enumeration of all things
|
||||
void cmdlineExplore()
|
||||
{
|
||||
//filter obj
|
||||
Filter* filter = nil;
|
||||
|
||||
//flag
|
||||
BOOL includeApple = NO;
|
||||
|
||||
//flag
|
||||
BOOL skipVirusTotal = NO;
|
||||
|
||||
//flag
|
||||
BOOL prettyPrint = NO;
|
||||
|
||||
//flag
|
||||
BOOL detailed = NO;
|
||||
|
||||
//output
|
||||
NSMutableString* output = nil;
|
||||
|
||||
//init filter obj
|
||||
filter = [[Filter alloc] init];
|
||||
|
||||
//init task enumerator object
|
||||
taskEnumerator = [[TaskEnumerator alloc] init];
|
||||
|
||||
//set flag
|
||||
// skip virus total?
|
||||
skipVirusTotal = [[[NSProcessInfo processInfo] arguments] containsObject:@"-skipVT"];
|
||||
|
||||
//virus total?
|
||||
if(YES != skipVirusTotal)
|
||||
{
|
||||
//init virus total object
|
||||
virusTotal = [[VirusTotal alloc] init];
|
||||
}
|
||||
|
||||
//be nice
|
||||
nice(15);
|
||||
|
||||
//enumerate all tasks/dylibs/files/etc
|
||||
[taskEnumerator enumerateTasks];
|
||||
|
||||
//wait for items to complete processing
|
||||
while(taskEnumerator.binaryQueue.itemsOut != taskEnumerator.binaryQueue.itemsOut)
|
||||
{
|
||||
//nap
|
||||
[NSThread sleepForTimeInterval:1.0f];
|
||||
}
|
||||
|
||||
//determine what each dylib is loaded in
|
||||
// do here as all tasks and all dylibs are enum'd
|
||||
for(NSString* dylib in taskEnumerator.dylibs)
|
||||
{
|
||||
//loaded in
|
||||
((Binary*)taskEnumerator.dylibs[dylib]).loadedIn = [taskEnumerator loadedIn:taskEnumerator.dylibs[dylib]];
|
||||
|
||||
}//sync
|
||||
|
||||
//wait for all VT threads to exit
|
||||
if(YES != skipVirusTotal)
|
||||
{
|
||||
//wait
|
||||
completeVTQuery();
|
||||
}
|
||||
|
||||
//set flag
|
||||
// include apple items?
|
||||
includeApple = [[[NSProcessInfo processInfo] arguments] containsObject:@"-apple"];
|
||||
|
||||
//set flag
|
||||
// pretty print json?
|
||||
prettyPrint = [[[NSProcessInfo processInfo] arguments] containsObject:@"-pretty"];
|
||||
|
||||
//set flag
|
||||
// full output?
|
||||
detailed = [[[NSProcessInfo processInfo] arguments] containsObject:@"-detailed"];
|
||||
|
||||
//alloc output JSON
|
||||
output = [NSMutableString string];
|
||||
|
||||
//only flagged items?
|
||||
if(YES == [[[NSProcessInfo processInfo] arguments] containsObject:@"-scan"])
|
||||
{
|
||||
//start JSON
|
||||
[output appendString:@"{\"flagged items\":["];
|
||||
|
||||
//add each item
|
||||
for(Binary* flaggedItem in taskEnumerator.flaggedItems)
|
||||
{
|
||||
[output appendFormat:@"{%@},", [flaggedItem toJSON]];
|
||||
}
|
||||
|
||||
//remove last ','
|
||||
if(YES == [output hasSuffix:@","])
|
||||
{
|
||||
//remove
|
||||
[output deleteCharactersInRange:NSMakeRange([output length]-1, 1)];
|
||||
}
|
||||
|
||||
//terminate list/output
|
||||
[output appendString:@"]}"];
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
//start JSON
|
||||
[output appendString:@"{\"tasks\":["];
|
||||
|
||||
//get tasks
|
||||
for(NSNumber* taskPid in taskEnumerator.tasks)
|
||||
{
|
||||
//skip apple?
|
||||
if( (YES != includeApple) &&
|
||||
(YES == [filter isApple:((Task*)taskEnumerator.tasks[taskPid]).binary]) )
|
||||
{
|
||||
//skip
|
||||
continue;
|
||||
}
|
||||
|
||||
//append task JSON
|
||||
[output appendFormat:@"{%@},", [taskEnumerator.tasks[taskPid] toJSON:detailed]];
|
||||
}
|
||||
|
||||
//remove last ','
|
||||
if(YES == [output hasSuffix:@","])
|
||||
{
|
||||
//remove
|
||||
[output deleteCharactersInRange:NSMakeRange([output length]-1, 1)];
|
||||
}
|
||||
|
||||
//not detailed?
|
||||
// add separate array of dylibs
|
||||
if(YES != detailed)
|
||||
{
|
||||
//append
|
||||
[output appendString:@"],\"dylibs\":["];
|
||||
|
||||
//add each dylib
|
||||
for(NSString* dylib in taskEnumerator.dylibs)
|
||||
{
|
||||
//add
|
||||
[output appendFormat:@"{%@},", [((Binary*)taskEnumerator.dylibs[dylib]) toJSON]];
|
||||
}
|
||||
|
||||
//remove last ','
|
||||
if(YES == [output hasSuffix:@","])
|
||||
{
|
||||
//remove
|
||||
[output deleteCharactersInRange:NSMakeRange([output length]-1, 1)];
|
||||
}
|
||||
}
|
||||
|
||||
//terminate list/output
|
||||
[output appendString:@"]}"];
|
||||
}
|
||||
|
||||
//pretty print?
|
||||
if(YES == prettyPrint)
|
||||
{
|
||||
//make me pretty!
|
||||
prettyPrintJSON(output);
|
||||
}
|
||||
else
|
||||
{
|
||||
//output
|
||||
printf("%s\n", output.UTF8String);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//block until vt queries are done
|
||||
void completeVTQuery()
|
||||
{
|
||||
//flag
|
||||
BOOL queryingVT = NO;
|
||||
|
||||
//nap
|
||||
// VT threads take some time to spawn/process
|
||||
[NSThread sleepForTimeInterval:5.0f];
|
||||
|
||||
//wait till threads are done
|
||||
while(YES)
|
||||
{
|
||||
//reset flag
|
||||
queryingVT = NO;
|
||||
|
||||
//wait for vt to complete
|
||||
@synchronized(virusTotal.vtThreads)
|
||||
{
|
||||
//check all threads
|
||||
for(NSThread* vtThread in virusTotal.vtThreads)
|
||||
{
|
||||
//check if still running?
|
||||
if(YES == [vtThread isExecuting])
|
||||
{
|
||||
//set flag
|
||||
queryingVT = YES;
|
||||
|
||||
//bail
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//check flag
|
||||
if(YES != queryingVT)
|
||||
{
|
||||
//finally no active threads
|
||||
break;
|
||||
}
|
||||
|
||||
//nap
|
||||
[NSThread sleepForTimeInterval:5.0f];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//pretty print JSON
|
||||
void prettyPrintJSON(NSString* output)
|
||||
{
|
||||
//data
|
||||
NSData* data = nil;
|
||||
|
||||
//object
|
||||
id object = nil;
|
||||
|
||||
//pretty data
|
||||
NSData* prettyData = nil;
|
||||
|
||||
//pretty string
|
||||
NSString* prettyString = nil;
|
||||
|
||||
//covert to data
|
||||
data = [output dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
//convert to JSON
|
||||
// wrap since we are serializing JSON
|
||||
@try
|
||||
{
|
||||
//serialize
|
||||
object = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
||||
|
||||
//covert to pretty data
|
||||
prettyData = [NSJSONSerialization dataWithJSONObject:object options:NSJSONWritingPrettyPrinted error:nil];
|
||||
}
|
||||
@catch(NSException *exception)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
//covert to pretty string
|
||||
if(nil != prettyData)
|
||||
{
|
||||
//convert to string
|
||||
prettyString = [[NSString alloc] initWithData:prettyData encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
else
|
||||
{
|
||||
//error
|
||||
prettyString = @"{\"ERROR\" : \"failed to covert output to JSON\"}";
|
||||
}
|
||||
|
||||
//output
|
||||
printf("%s\n", prettyString.UTF8String);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.7.0</string>
|
||||
<string>2.0.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.7.0</string>
|
||||
<string>2.0.0</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2018 Objective-See, LLC. All rights reserved.</string>
|
||||
<key>XPCService</key>
|
||||
|
||||
Reference in New Issue
Block a user