From 205e24d3097d89c08823ae7789c148c79ab2b514 Mon Sep 17 00:00:00 2001 From: Patrick Wardle Date: Fri, 21 Dec 2018 20:19:09 -1000 Subject: [PATCH] version 2.0.0 -avoid UI calls when run in cmdline mode -hide icon when run in cmdline mode -option to scan single process --- AboutWindowController.h | 1 - AppDelegate.m | 32 +++-- Items/ItemBase.h | 11 +- KKRow.m | 2 +- NSMutableArray+QueueAdditions.h | 2 +- NSMutableArray+QueueAdditions.m | 2 +- Queue.h | 4 +- Queue.m | 4 +- RequestRootWindowController.m | 2 +- Task.m | 37 ++++-- TaskEnumerator.h | 2 +- TaskEnumerator.m | 116 ++++++++++++++---- TaskExplorer-Info.plist | 2 +- TaskExplorer.entitlements | 5 + TaskExplorer.xcodeproj/project.pbxproj | 18 +++ .../UserInterfaceState.xcuserstate | Bin 65313 -> 0 bytes .../xcschemes/TaskExplorer.xcscheme | 10 +- Utilities.m | 10 +- VTInfoWindowController.m | 33 ++++- VirusTotal.h | 5 + VirusTotal.m | 7 +- main.h | 4 +- main.m | 67 ++++++++-- patrons.txt | 2 +- remoteTaskService/Info.plist | 2 +- .../remoteTaskService.entitlements | 5 + 26 files changed, 291 insertions(+), 94 deletions(-) create mode 100644 TaskExplorer.entitlements delete mode 100644 TaskExplorer.xcodeproj/project.xcworkspace/xcuserdata/patrick.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 remoteTaskService/remoteTaskService.entitlements diff --git a/AboutWindowController.h b/AboutWindowController.h index e04d687..e4f6245 100644 --- a/AboutWindowController.h +++ b/AboutWindowController.h @@ -25,5 +25,4 @@ //'support us' button @property (weak, atomic) IBOutlet NSButton *supportUs; - @end diff --git a/AppDelegate.m b/AppDelegate.m index 5845505..92cce31 100755 --- a/AppDelegate.m +++ b/AppDelegate.m @@ -53,9 +53,6 @@ //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"]; @@ -115,15 +112,21 @@ [self registerKeypressHandler]; //check if authenticated - // ->display authentication request if needed + // display authentication request if needed if(YES != [self isAuthenticated]) { - //display auth popup - // will invoke 'go' method on successful auth - [self askForRoot]; + //wait to allow app to become front + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, .33 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + + //display auth popup + // will invoke 'go' method on successful auth + [self askForRoot]; + + }); } + //go! - // ->setup tracking areas and begin thread that explores tasks + // setup tracking areas and begin thread that explores tasks else { //go! @@ -161,12 +164,23 @@ //set delegate // ->ensures our 'windowWillClose' method, which has logic to fully exit app self.window.delegate = self; + + return; } //automatically invoked by OS // ->main entry point -(void)applicationDidFinishLaunching:(NSNotification *)notification { + + //toggle away + [[[NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.loginwindow"] firstObject] activateWithOptions:NSApplicationActivateIgnoringOtherApps]; + + //toggle back + // work-around for menu not showing since we set Application is agent(UIElement): YES + [[[NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.objective-see.TaskExplorer"] firstObject] activateWithOptions:NSApplicationActivateIgnoringOtherApps]; + + //first time run? // show thanks to friends window! // note: on close, invokes method to show main window @@ -443,7 +457,7 @@ bail: //kick off thread to enum task // ->will update table as results come in - [NSThread detachNewThreadSelector:@selector(enumerateTasks) toTarget:taskEnumerator withObject:nil]; + [NSThread detachNewThreadSelector:@selector(enumerateTasks:) toTarget:taskEnumerator withObject:nil]; return; } diff --git a/Items/ItemBase.h b/Items/ItemBase.h index 730a49a..888ce4d 100644 --- a/Items/ItemBase.h +++ b/Items/ItemBase.h @@ -1,9 +1,9 @@ // // ItemBase.h -// BlockBlock +// TaskExplorer // // Created by Patrick Wardle on 9/25/14. -// Copyright (c) 2014 Synack. All rights reserved. +// Copyright (c) 2014 Objective-See. All rights reserved. // #import @@ -25,13 +25,6 @@ //file attributes @property(nonatomic, retain)NSDictionary* attributes; -//flag if known -// ->signed by apple and/or whitelisted -//@property BOOL isTrusted; - - - - /* METHODS */ //init method diff --git a/KKRow.m b/KKRow.m index 6e6ef6c..10c4eda 100644 --- a/KKRow.m +++ b/KKRow.m @@ -33,7 +33,7 @@ [[NSColor colorWithCalibratedWhite:1.0 alpha:1.0] setStroke]; //set fill - [[NSColor colorWithCalibratedWhite:.65 alpha:1.0] setFill]; + [[NSColor colorWithCalibratedWhite:.50 alpha:1.0] setFill]; } //light mode highlight else diff --git a/NSMutableArray+QueueAdditions.h b/NSMutableArray+QueueAdditions.h index aebd0ab..a234869 100644 --- a/NSMutableArray+QueueAdditions.h +++ b/NSMutableArray+QueueAdditions.h @@ -3,7 +3,7 @@ // BlockBlock // // Created by Patrick Wardle on 9/26/14. -// Copyright (c) 2014 Synack. All rights reserved. +// Copyright (c) 2014 Objective-See. All rights reserved. // #import diff --git a/NSMutableArray+QueueAdditions.m b/NSMutableArray+QueueAdditions.m index 5d5fb54..fa30b01 100644 --- a/NSMutableArray+QueueAdditions.m +++ b/NSMutableArray+QueueAdditions.m @@ -3,7 +3,7 @@ // BlockBlock // // Created by Patrick Wardle on 9/26/14. -// Copyright (c) 2014 Synack. All rights reserved. +// Copyright (c) 2014 Objective-See. All rights reserved. // #import "NSMutableArray+QueueAdditions.h" diff --git a/Queue.h b/Queue.h index bae2edb..980e42b 100644 --- a/Queue.h +++ b/Queue.h @@ -1,9 +1,9 @@ // // Queue.h -// BlockBlock +// TaskExplorer // // Created by Patrick Wardle on 9/26/14. -// Copyright (c) 2014 Synack. All rights reserved. +// Copyright (c) 2014 Objective-See. All rights reserved. // //from: https://github.com/esromneb/ios-queue-object/blob/master/NSMutableArray%2BQueueAdditions.h diff --git a/Queue.m b/Queue.m index 8d6954c..22b9167 100644 --- a/Queue.m +++ b/Queue.m @@ -1,9 +1,9 @@ // // Queue.m -// BlockBlock +// TaskExplorer // // Created by Patrick Wardle on 9/26/14. -// Copyright (c) 2014 Synack. All rights reserved. +// Copyright (c) 2014 Objective-See. All rights reserved. // #import "Queue.h" diff --git a/RequestRootWindowController.m b/RequestRootWindowController.m index 4c42722..85e021e 100644 --- a/RequestRootWindowController.m +++ b/RequestRootWindowController.m @@ -195,7 +195,7 @@ self.cancelButton.enabled = NO; //update auth message - self.statusMsg.stringValue = @"ok: authorization successful"; + self.statusMsg.stringValue = @"...authorization successful!"; //wait a bit // ->then hide window & kick off action diff --git a/Task.m b/Task.m index 3b7d123..19d6580 100644 --- a/Task.m +++ b/Task.m @@ -394,7 +394,11 @@ bail: //reload bottom pane now // note: this will only reload if new task is the currently selected one, etc - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadBottomPane:self itemView:DYLIBS_VIEW]; + if(YES != cmdlineMode) + { + //reload + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadBottomPane:self itemView:DYLIBS_VIEW]; + } //complete dylib processing for new dylib // ->get signing info, hash, etc, & save into global list @@ -415,15 +419,19 @@ bail: newDylib.isPacked = [newDylib.parser.binaryInfo[KEY_IS_PACKED] boolValue]; } - //no need to reload if task is now longer current/selected - if(((AppDelegate*)[[NSApplication sharedApplication] delegate]).currentTask != self) + //need to load? + if(YES != cmdlineMode) { - //skip reload - continue; + //no need to reload if task is now longer current/selected + if(((AppDelegate*)[[NSApplication sharedApplication] delegate]).currentTask != self) + { + //skip reload + continue; + } + + //reload row + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadRow:newDylib]; } - - //reload row - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadRow:newDylib]; } //signal sema @@ -519,7 +527,12 @@ bail: }] mutableCopy]; //reload bottom pane - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadBottomPane:self itemView:FILES_VIEW]; + if(YES != cmdlineMode) + { + //reload + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadBottomPane:self itemView:FILES_VIEW]; + } + }//sync @@ -603,7 +616,11 @@ bail: } //reload bottom pane - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadBottomPane:self itemView:NETWORKING_VIEW]; + if(YES != cmdlineMode) + { + //reload + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadBottomPane:self itemView:NETWORKING_VIEW]; + } }//sync diff --git a/TaskEnumerator.h b/TaskEnumerator.h index 5940905..ddeabce 100644 --- a/TaskEnumerator.h +++ b/TaskEnumerator.h @@ -52,7 +52,7 @@ //enumerate all tasks // ->call back into app delegate to update task (top) table --(void)enumerateTasks; +-(void)enumerateTasks:(NSNumber*)pid; //get list of all pids -(OrderedDictionary*)getAllTasks; diff --git a/TaskEnumerator.m b/TaskEnumerator.m index b1e25b1..aee09bd 100644 --- a/TaskEnumerator.m +++ b/TaskEnumerator.m @@ -44,10 +44,9 @@ return self; } - //enumerate all tasks // calls back into app delegate to update task (top) table when pau --(void)enumerateTasks +-(void)enumerateTasks:(NSNumber*)pid { //(new) task item Task* newTask = nil; @@ -65,18 +64,28 @@ self.state = ENUMERATION_STATE_TASKS; //get all tasks - // ->pids and binary obj with just path/name + // pids and binary obj with just path/name newTasks = [self getAllTasks]; //build ancestries // do here, and use 'new tasks' since there might be new parents too [self generateAncestries:newTasks]; + //only interested in one task? + if(nil != pid) + { + //enumerate task + [self enumerateTask:newTasks[pid]]; + + //done + goto bail; + } + //get all tasks that are pau deadTasks = [self.tasks.allKeys filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"NOT SELF IN %@", newTasks.allKeys]]; //remove any old tasks that have exited/died - // ->invoke custom method to handle kids too... + // invoke custom method to handle kids too... for(NSNumber* key in deadTasks) { //sync to remove @@ -121,23 +130,26 @@ }//add new tasks //sort tasks - // ->ensures that signing info etc w/ be generated for (top) visible tasks - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) sortTasksForView:newTasks]; - - //reload task table - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadTaskTable]; - - //reload bottom pain - // call on main thread - if(YES != [NSThread isMainThread]) + if(YES != cmdlineMode) { - //main thread - dispatch_sync(dispatch_get_main_queue(), ^{ - - //reload bottom pane - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) selectBottomPaneContent:nil]; + //sort + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) sortTasksForView:newTasks]; + + //reload task table + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadTaskTable]; + + //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]; + + }); + } } //for new tasks @@ -173,12 +185,16 @@ newTask.binary.isPacked = [newTask.binary.parser.binaryInfo[KEY_IS_PACKED] boolValue]; } - //reload task (row) in table - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadRow:newTask]; - - //reload bottom pane - // ->this will only reload if new task is the currently selected one, etc - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadBottomPane:newTask itemView:CURRENT_VIEW]; + //reload UI + if(YES != cmdlineMode) + { + //reload task (row) in table + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadRow:newTask]; + + //reload bottom pane + // ->this will only reload if new task is the currently selected one, etc + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadBottomPane:newTask itemView:CURRENT_VIEW]; + } }//signing info for all new tasks @@ -298,6 +314,53 @@ //set state self.state = ENUMERATION_STATE_COMPLETE; +bail: + + return; +} + +//scan a single task +-(void)enumerateTask:(Task*)task +{ + //sanity check + if(nil == task) + { + //bail + goto bail; + } + + //generate signing info dynamically + task.binary.signingInfo = extractSigningInfo(task.pid.intValue, nil, kSecCSDefaultFlags); + if(nil == task.binary.signingInfo) + { + //extract signing info statically + task.binary.signingInfo = extractSigningInfo(0, task.binary.path, kSecCSCheckAllArchitectures | kSecCSCheckNestedCode | kSecCSDoNotValidateResources); + } + + //parse + if(YES == [task.binary parse]) + { + //save encrypted flag + task.binary.isEncrypted = [task.binary.parser.binaryInfo[KEY_IS_ENCRYPTED] boolValue]; + + //save packed flag + task.binary.isPacked = [task.binary.parser.binaryInfo[KEY_IS_PACKED] boolValue]; + } + + //enumerate dylibs + [task enumerateDylibs:self.dylibs shouldWait:YES]; + + //enumerate files + [task enumerateFiles:YES]; + + //enumerate networking + [task enumerateNetworking:YES]; + + //save task + self.tasks[task.pid] = task; + +bail: + return; } @@ -627,7 +690,8 @@ bail: //when there are no flagged items // ->(re)set flagged icon to black - if(0 == taskEnumerator.flaggedItems.count) + if( (YES != cmdlineMode) && + (0 == taskEnumerator.flaggedItems.count) ) { //set main image [((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedButton setImage:[NSImage imageNamed:@"flagged"]]; diff --git a/TaskExplorer-Info.plist b/TaskExplorer-Info.plist index 8d20bfe..ebea5c7 100755 --- a/TaskExplorer-Info.plist +++ b/TaskExplorer-Info.plist @@ -25,7 +25,7 @@ LSMinimumSystemVersion ${MACOSX_DEPLOYMENT_TARGET} NSHumanReadableCopyright - Copyright © 2018 Objective-See, LLC. All rights reserved. + Copyright © 2019 Objective-See, LLC. All rights reserved. NSMainNibFile MainMenu NSPrincipalClass diff --git a/TaskExplorer.entitlements b/TaskExplorer.entitlements new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/TaskExplorer.entitlements @@ -0,0 +1,5 @@ + + + + + diff --git a/TaskExplorer.xcodeproj/project.pbxproj b/TaskExplorer.xcodeproj/project.pbxproj index 9814197..21f8cff 100755 --- a/TaskExplorer.xcodeproj/project.pbxproj +++ b/TaskExplorer.xcodeproj/project.pbxproj @@ -238,6 +238,8 @@ CD3F4CFE1AF72BC4002A2647 /* TaskInfoWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = TaskInfoWindow.xib; path = UI/TaskInfoWindow.xib; sourceTree = ""; }; CD3F4D131AF85088002A2647 /* FlatView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FlatView.xib; path = UI/FlatView.xib; sourceTree = ""; }; CD3F4D151AF89066002A2647 /* TreeView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = TreeView.xib; path = UI/TreeView.xib; sourceTree = ""; }; + CD42FF8F21CE0CED000A0461 /* TaskExplorer.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TaskExplorer.entitlements; sourceTree = SOURCE_ROOT; }; + CD42FF9021CE0D05000A0461 /* remoteTaskService.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = remoteTaskService.entitlements; sourceTree = ""; }; CD4D53C91B20296E00008030 /* unknown.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = unknown.png; path = images/unknown.png; sourceTree = SOURCE_ROOT; }; CD4D53CF1B23ED3900008030 /* connectedIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = connectedIcon.png; path = images/connectedIcon.png; sourceTree = SOURCE_ROOT; }; CD4D53D11B23ED4300008030 /* listeningIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = listeningIcon.png; path = images/listeningIcon.png; sourceTree = SOURCE_ROOT; }; @@ -444,6 +446,7 @@ 1D21BC54172AF43D009D1CFD /* TaskExplorer */ = { isa = PBXGroup; children = ( + CD42FF8F21CE0CED000A0461 /* TaskExplorer.entitlements */, CD1508DE21BB13980081F1AF /* Signing.h */, CD1508DD21BB13980081F1AF /* Signing.m */, CD1D14F221B8F2FC00FF7F4B /* Assets.xcassets */, @@ -565,6 +568,7 @@ CDA5F6AE1B16D805003CE340 /* remoteTaskService */ = { isa = PBXGroup; children = ( + CD42FF9021CE0D05000A0461 /* remoteTaskService.entitlements */, CDA5F6B21B16D805003CE340 /* remoteTaskService.h */, CDA5F6B31B16D805003CE340 /* remoteTaskService.m */, CDA5F6B51B16D805003CE340 /* main.m */, @@ -715,10 +719,20 @@ TargetAttributes = { 1D21BC4A172AF43D009D1CFD = { DevelopmentTeam = VBG97UB4TA; + SystemCapabilities = { + com.apple.HardenedRuntime = { + enabled = 1; + }; + }; }; CDA5F6AC1B16D805003CE340 = { CreatedOnToolsVersion = 6.3.2; DevelopmentTeam = VBG97UB4TA; + SystemCapabilities = { + com.apple.HardenedRuntime = { + enabled = 1; + }; + }; }; }; }; @@ -1008,6 +1022,7 @@ CODE_SIGN_IDENTITY = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)"; COMBINE_HIDPI_IMAGES = YES; + ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/Mac", @@ -1031,6 +1046,7 @@ CODE_SIGN_IDENTITY = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)"; COMBINE_HIDPI_IMAGES = YES; + ENABLE_HARDENED_RUNTIME = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Carthage/Build/Mac", @@ -1057,6 +1073,7 @@ CODE_SIGN_IDENTITY = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)"; COMBINE_HIDPI_IMAGES = YES; DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_HARDENED_RUNTIME = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -1092,6 +1109,7 @@ CODE_SIGN_IDENTITY = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)"; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + ENABLE_HARDENED_RUNTIME = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ( diff --git a/TaskExplorer.xcodeproj/project.xcworkspace/xcuserdata/patrick.xcuserdatad/UserInterfaceState.xcuserstate b/TaskExplorer.xcodeproj/project.xcworkspace/xcuserdata/patrick.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index dee3b90763f5faf8d5ea995754e4fa6944454b59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65313 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

B85>M`X)bjmLJa}BgB|DWE7A( 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 + + + + diff --git a/Utilities.m b/Utilities.m index bc92873..6bea93a 100644 --- a/Utilities.m +++ b/Utilities.m @@ -763,9 +763,12 @@ void makeModal(NSWindowController* windowController) //make modal on main thread dispatch_sync(dispatch_get_main_queue(), ^{ + //make app front + [NSApp activateIgnoringOtherApps:YES]; + //modal [[NSApplication sharedApplication] runModalForWindow:windowController.window]; - + }); //all done @@ -1375,8 +1378,3 @@ void transformProcess(ProcessApplicationTransformState location) return; } - - - - - diff --git a/VTInfoWindowController.m b/VTInfoWindowController.m index 4b77b77..2077611 100644 --- a/VTInfoWindowController.m +++ b/VTInfoWindowController.m @@ -208,6 +208,9 @@ //VT scan ID __block NSString* scanID = nil; + //new report + __block NSURL* newReport = nil; + //alloc/init VT obj vtObj = [[VirusTotal alloc] init]; @@ -313,8 +316,19 @@ //nap so user can see msg [NSThread sleepForTimeInterval:0.5]; - //launch browser to show rew report - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:result[@"permalink"]]]; + //launch browser to show new report + dispatch_sync(dispatch_get_main_queue(), ^{ + + //sanity check + // then launch browser + if( (nil != result[@"permalink"]) && + (nil != (newReport = [NSURL URLWithString:result[@"permalink"]])) ) + { + //launch browser + [[NSWorkspace sharedWorkspace] openURL:newReport]; + } + + }); //wait to browser is up and happy [NSThread sleepForTimeInterval:0.5]; @@ -390,8 +404,19 @@ //nap so user can see msg [NSThread sleepForTimeInterval:0.5]; - //launch browser to show rew report - [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:result[@"permalink"]]]; + //launch browser to show new report + dispatch_sync(dispatch_get_main_queue(), ^{ + + //sanity check + // then launch browser + if( (nil != result[@"permalink"]) && + (nil != (newReport = [NSURL URLWithString:result[@"permalink"]])) ) + { + //launch browser + [[NSWorkspace sharedWorkspace] openURL:newReport]; + } + + }); //wait to browser is up and happy [NSThread sleepForTimeInterval:0.5]; diff --git a/VirusTotal.h b/VirusTotal.h index 96c7f1a..fe9f8d7 100644 --- a/VirusTotal.h +++ b/VirusTotal.h @@ -9,6 +9,11 @@ #import "Binary.h" #import +/* GLOBALS */ + +//cmdline flag +extern BOOL cmdlineMode; + @interface VirusTotal : NSObject { diff --git a/VirusTotal.m b/VirusTotal.m index 85309b6..e572cf6 100644 --- a/VirusTotal.m +++ b/VirusTotal.m @@ -606,11 +606,14 @@ bail: } //call up into app delegate to smartly reload - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadBinary:queriedItem]; + if(YES != cmdlineMode) + { + //reload + [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadBinary:queriedItem]; + } } return; } - @end diff --git a/main.h b/main.h index a5d203e..895c8ee 100644 --- a/main.h +++ b/main.h @@ -3,7 +3,6 @@ // TaskExplorer // // Created by Patrick Wardle on 12/16/18. -// Copyright © 2018 Lucas Derraugh. All rights reserved. // #ifndef main_h @@ -33,6 +32,9 @@ VirusTotal* virusTotal = nil; //network connected flag BOOL isConnected = NO; +//cmdline flag +BOOL cmdlineMode = NO; + //(privacy) protected directories NSArray* protectedDirectories = nil; diff --git a/main.m b/main.m index ec9a248..3fd1bb0 100755 --- a/main.m +++ b/main.m @@ -80,6 +80,9 @@ int main(int argc, char *argv[]) goto bail; } + //set flag + cmdlineMode = YES; + //scan cmdlineExplore(); @@ -94,8 +97,14 @@ int main(int argc, char *argv[]) // just kick off app for UI instance else { + //set flag + cmdlineMode = NO; + + //make foreground so it has an dock icon, etc + transformProcess(kProcessTransformToForegroundApplication); + //invoke app's main - status = NSApplicationMain(argc, (const char **)argv); + status = NSApplicationMain(argc, (const char **)argv); } bail: @@ -114,8 +123,9 @@ void usage() printf(" -explore enumerate all tasks and dylibs\n"); printf("\noptions:\n"); printf(" -pretty json output is 'pretty-printed'\n"); + printf(" -pid [pid] just scan/explore the specified task'\n"); printf(" -skipVT do not query VirusTotal (when '-explore' is specified)\n"); - printf(" -full for each task; include dylibs, files, & network connections\n\n"); + printf(" -detailed for each task; include dylibs, files, & network connections\n\n"); return; } @@ -123,6 +133,9 @@ void usage() //perform a cmdline enumeration of all things void cmdlineExplore() { + //args + NSArray* arguments = nil; + //filter obj Filter* filter = nil; @@ -141,6 +154,16 @@ void cmdlineExplore() //output NSMutableString* output = nil; + //formatter + NSNumberFormatter* formatter = nil; + + //pid + // if single task was specified + NSNumber* pid = nil; + + //grab args + arguments = [[NSProcessInfo processInfo] arguments]; + //init filter obj filter = [[Filter alloc] init]; @@ -149,7 +172,7 @@ void cmdlineExplore() //set flag // skip virus total? - skipVirusTotal = [[[NSProcessInfo processInfo] arguments] containsObject:@"-skipVT"]; + skipVirusTotal = [arguments containsObject:@"-skipVT"]; //virus total? if(YES != skipVirusTotal) @@ -161,8 +184,22 @@ void cmdlineExplore() //be nice nice(15); + //scan just one pid? + if( (YES == [arguments containsObject:@"-pid"]) && + (YES != [@"-pid" isEqualToString:arguments.lastObject]) ) + { + //init formatter + formatter = [[NSNumberFormatter alloc] init]; + + //set style + formatter.numberStyle = NSNumberFormatterDecimalStyle; + + //extract/convert pid + pid = [formatter numberFromString:arguments[[arguments indexOfObject:@"-pid"] + 1]]; + } + //enumerate all tasks/dylibs/files/etc - [taskEnumerator enumerateTasks]; + [taskEnumerator enumerateTasks:pid]; //wait for items to complete processing while(taskEnumerator.binaryQueue.itemsOut != taskEnumerator.binaryQueue.itemsOut) @@ -172,7 +209,7 @@ void cmdlineExplore() } //determine what each dylib is loaded in - // do here as all tasks and all dylibs are enum'd + // do here as all tasks and all dylibs are (now) enum'd for(NSString* dylib in taskEnumerator.dylibs) { //loaded in @@ -189,21 +226,21 @@ void cmdlineExplore() //set flag // include apple items? - includeApple = [[[NSProcessInfo processInfo] arguments] containsObject:@"-apple"]; + includeApple = [arguments containsObject:@"-apple"]; //set flag // pretty print json? - prettyPrint = [[[NSProcessInfo processInfo] arguments] containsObject:@"-pretty"]; + prettyPrint = [arguments containsObject:@"-pretty"]; //set flag // full output? - detailed = [[[NSProcessInfo processInfo] arguments] containsObject:@"-detailed"]; + detailed = [arguments containsObject:@"-detailed"]; //alloc output JSON output = [NSMutableString string]; //only flagged items? - if(YES == [[[NSProcessInfo processInfo] arguments] containsObject:@"-scan"]) + if(YES == [arguments containsObject:@"-scan"]) { //start JSON [output appendString:@"{\"flagged items\":["]; @@ -225,6 +262,7 @@ void cmdlineExplore() [output appendString:@"]}"]; } + //all items else { //start JSON @@ -234,8 +272,10 @@ void cmdlineExplore() for(NSNumber* taskPid in taskEnumerator.tasks) { //skip apple? + // unless we're scanning a single proc if( (YES != includeApple) && - (YES == [filter isApple:((Task*)taskEnumerator.tasks[taskPid]).binary]) ) + (1 != taskEnumerator.tasks.count) && + (YES == [filter isApple:((Task*)taskEnumerator.tasks[taskPid]).binary]) ) { //skip continue; @@ -252,9 +292,10 @@ void cmdlineExplore() [output deleteCharactersInRange:NSMakeRange([output length]-1, 1)]; } - //not detailed? - // add separate array of dylibs - if(YES != detailed) + //not detailed or not just scanning 1 task + // add separate array of for all the dylibs + if( (YES != detailed) && + (1 != taskEnumerator.tasks.count) ) { //append [output appendString:@"],\"dylibs\":["]; diff --git a/patrons.txt b/patrons.txt index 31f7c85..e28a351 100644 --- a/patrons.txt +++ b/patrons.txt @@ -1,5 +1,5 @@ Patrons (2^6+): -Halo Privacy, Ash Morgan, Nando Mendonca, Geoffrey Weber, Randy Wong, Aaron Kiemele, Beau Galbraith, Stuart Ashenbrenner, Gamer_Bot +Halo Privacy, Ash Morgan, Nando Mendonca, Geoffrey Weber, Randy Wong, Aaron Kiemele, Beau Galbraith, Stuart Ashenbrenner, whiskerz, Gamer_Bot Friends of Objective-See: Digita Security, Sophos, Malwarebytes, SmugMug, Guardian Mobile Firewall, SecureMac diff --git a/remoteTaskService/Info.plist b/remoteTaskService/Info.plist index 72f8bbc..cc428a9 100644 --- a/remoteTaskService/Info.plist +++ b/remoteTaskService/Info.plist @@ -23,7 +23,7 @@ CFBundleVersion 2.0.0 NSHumanReadableCopyright - Copyright © 2018 Objective-See, LLC. All rights reserved. + Copyright © 2019 Objective-See, LLC. All rights reserved. XPCService ServiceType diff --git a/remoteTaskService/remoteTaskService.entitlements b/remoteTaskService/remoteTaskService.entitlements new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/remoteTaskService/remoteTaskService.entitlements @@ -0,0 +1,5 @@ + + + + +