version 2.0 (beta?)

cli interface
This commit is contained in:
Patrick Wardle
2018-12-18 21:13:23 -10:00
parent 35baa20f7c
commit 56fd7b0a07
35 changed files with 1082 additions and 510 deletions
+11 -28
View File
@@ -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
View File
@@ -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
+5
View File
@@ -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};
+2 -2
View File
@@ -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
View File
@@ -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]];
+5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
-38
View File
@@ -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
+10 -4
View File
@@ -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
+18 -32
View File
@@ -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
+19 -7
View File
@@ -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
+1 -1
View File
@@ -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
+63 -51
View File
@@ -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
View File
@@ -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
View File
@@ -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
+4 -2
View File
@@ -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>
+2
View File
@@ -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>";
@@ -46,6 +46,12 @@
ReferencedContainer = "container:TaskExplorer.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "-scan"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<EnvironmentVariables>
<EnvironmentVariable
key = "CA_DEBUG_TRANSACTIONS"
+8 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
}
+17 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
+53
View File
@@ -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 */
+347 -10
View File
@@ -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;
}
+2 -2
View File
@@ -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>