diff --git a/AppDelegate.h b/AppDelegate.h index db17fc2..d274d20 100755 --- a/AppDelegate.h +++ b/AppDelegate.h @@ -143,9 +143,6 @@ //flagged items button @property (weak) IBOutlet NSButton *flaggedButton; -//flagged items label -@property (weak) IBOutlet NSTextField *flaggedLabel; - //top constraint @property(nonatomic, retain)NSLayoutConstraint* topConstraint; @@ -158,9 +155,6 @@ //top constraint @property(nonatomic, retain)NSLayoutConstraint* trailingConstraint; -//remote XPC interface -@property(nonatomic, retain)NSXPCConnection* xpcConnection; - //flagged items @property(nonatomic, retain)NSMutableArray* flaggedItems; @@ -182,9 +176,6 @@ // ->then invoke helper method to start enum'ing task (in bg thread) -(void)go; -//init (setup) XPC connection --(BOOL)initXPC; - //switch between flat/tree view -(IBAction)switchView:(id)sender; diff --git a/AppDelegate.m b/AppDelegate.m index 7e42032..40adca5 100755 --- a/AppDelegate.m +++ b/AppDelegate.m @@ -17,31 +17,8 @@ #import "Task.h" //TODO: filter out dup'd networks (airportd 0:0..) -not sure want to do this -//TODO: add 'am i on main thread' guard and test - - //TODO: autolayout vertically -//TODO: filter VT results - HUH? -//TODO: # autocomplete - DONE - -//TODO: keyboard shortcuts - DONE! -// see: https://mail.google.com/mail/u/0/#inbox/14eeb163d4dd2852 //TODO: show 'from where' via quarantine attrz -//TODO: show user (after pid): -> (pid, user)? - -//TODO: syncronize filtered tasks - DONE! - -//TODO: add files/dylibs/connections to save - DONE! -//TODO: sync while saving - -//TODO: JavaW (iWorm) dylibs... - -//TODO: remove task, remove from taskEnum's global list for executables, and dylibs, etc -//TODO: also refresh!.... - -//TODO: search include network (and improved filtering to include state/proto/type) - DONE! - -//TODO: check all searches that use NSNotFound also check for nil - DONE @implementation AppDelegate @@ -59,7 +36,6 @@ @synthesize viewSelector; @synthesize scannerThread; @synthesize virusTotalObj; -@synthesize xpcConnection; @synthesize taskEnumerator; @synthesize taskViewFormat; @synthesize commandHandling; @@ -97,9 +73,7 @@ { //first thing... // ->install exception handlers! - - //TODO: CHANGE B4 RELEASE!! - //installExceptionHandlers(); + installExceptionHandlers(); //init virus total object virusTotalObj = [[VirusTotal alloc] init]; @@ -339,13 +313,6 @@ bail: // ->then invoke helper method to start enum'ing task (in bg thread) -(void)go { - //init XPC - if(YES != [self initXPC]) - { - //bail - goto bail; - } - //init mouse-over areas [self initTrackingAreas]; @@ -359,68 +326,6 @@ bail: } -//init (setup) XPC connection --(BOOL)initXPC -{ - //status - BOOL initialized = NO; - - //alloc XPC connection - xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.remoteTaskService"]; - - //sanity check - if(nil == self.xpcConnection) - { - //err msg - syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: failed to find/initialize XPC service"); - - //bail - goto bail; - } - - //set remote object interface - self.xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(remoteTaskProto)]; - - //set classes - // ->arrays & strings are what is ok to vend - [self.xpcConnection.remoteObjectInterface - setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], nil] - forSelector: @selector(enumerateDylibs:withReply:) - argumentIndex: 0 // the first parameter - ofReply: YES // in the method itself. - ]; - - //set classes - // ->arrays & strings are what is ok to vend - [self.xpcConnection.remoteObjectInterface - setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], nil] - forSelector: @selector(enumerateFiles:withReply:) - argumentIndex: 0 // the first parameter - ofReply: YES // in the method itself. - ]; - - //set classes - // ->arrays & strings are what is ok to vend - [self.xpcConnection.remoteObjectInterface - setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], [NSNumber class], nil] - forSelector: @selector(enumerateNetwork:withReply:) - argumentIndex: 0 // the first parameter - ofReply: YES // in the method itself. - ]; - - //resume - [self.xpcConnection resume]; - - //happy - initialized = YES; - -//bail -bail: - - return initialized; -} - - //check if app is auth'd // ->specifically, if XPC service is setuid -(BOOL)isAuthenticated @@ -766,7 +671,7 @@ bail: { //reload // ->in main UI thread - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_sync(dispatch_get_main_queue(), ^{ //set not found label self.noItemsLabel.stringValue = noItemsMsg; @@ -1137,10 +1042,22 @@ bail: } //set original flagged items image + // ->but also handle case where there are still flagged items else if(FLAGGED_BUTTON_TAG == tag) { - //set - imageName = @"flagged"; + //when no flagged items + if(0 == self.flaggedItems.count) + { + //set + imageName = @"flagged"; + } + //flagged items + else + { + //set + imageName = @"flaggedRed"; + } + } } //highlight button @@ -1172,10 +1089,21 @@ bail: } //set mouse over flagged items image + // ->also handles case where flagged items are present else if(FLAGGED_BUTTON_TAG == tag) { - //set - imageName = @"flaggedOver"; + //when no flagged items + if(0 == self.flaggedItems.count) + { + //set + imageName = @"flaggedOver"; + } + //flagged items + else + { + //set + imageName = @"flaggedRedOver"; + } } } @@ -1541,7 +1469,7 @@ bail: //(re)enumerate dylibs via XPC // ->triggers table reload when done - [self.currentTask enumerateDylibs:self.xpcConnection allDylibs:self.taskEnumerator.dylibs]; + [self.currentTask enumerateDylibs:self.taskEnumerator.dylibs]; break; @@ -1556,7 +1484,7 @@ bail: //(re)enumerate files via XPC // ->triggers table reload when done - [self.currentTask enumerateFiles:self.xpcConnection]; + [self.currentTask enumerateFiles]; break; @@ -1568,7 +1496,7 @@ bail: //(re)enumerate network connections via XPC // ->triggers table reload when done - [self.currentTask enumerateNetworking:self.xpcConnection]; + [self.currentTask enumerateNetworking]; break; @@ -1797,7 +1725,7 @@ bail: return; } -//action for 'refresh' button / cmd+r hotkey +//action for 'refresh' button/cmd+r hotkey // ->query OS to refresh/reload all tasks -(IBAction)refreshTasks:(id)sender { @@ -1813,10 +1741,8 @@ bail: //sync @synchronized(self.taskTableController.filteredItems) { - - //remove all filtered items - [self.taskTableController.filteredItems removeAllObjects]; - + //remove all filtered items + [self.taskTableController.filteredItems removeAllObjects]; } //reset filter box @@ -1827,10 +1753,6 @@ bail: //scroll to top [self.taskTableController scrollToTop]; - - //reload - // ->ensure that top row/task is correctly selected - //[self.taskTableController.itemView reloadData]; //select top row [self.taskTableController.itemView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; @@ -1900,7 +1822,6 @@ bail: return; } -//TODO: handle reset on refresh? //save a flagged item // ->also set text flagged items button label to red -(void)saveFlaggedBinary:(Binary*)binary @@ -1921,11 +1842,15 @@ bail: } //when count is 1 - // ->means first flagged file so set text to red + // ->means first flagged file so set image to red if(1 == self.flaggedItems.count) { - //set to red - self.flaggedLabel.textColor = [NSColor redColor]; + //set main image + [self.flaggedButton setImage:[NSImage imageNamed:@"flaggedRed"]]; + + //set alternate image + [self.flaggedButton setAlternateImage:[NSImage imageNamed:@"flaggedRedBG"]]; + } //bail @@ -1946,7 +1871,7 @@ bail: if(0 == self.flaggedItems.count) { //alloc/init alert - alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:@"No items flagged by VirusTotal"] defaultButton:@"Ok" alternateButton:nil otherButton:nil informativeTextWithFormat:@"horray! 😇"]; + alert = [NSAlert alertWithMessageText:[NSString stringWithFormat:@"No items flagged by VirusTotal"] defaultButton:@"Ok" alternateButton:nil otherButton:nil informativeTextWithFormat:@"hooray! 😇"]; //and show it [alert runModal]; diff --git a/Consts.h b/Consts.h index aba5ecf..0faaaf4 100644 --- a/Consts.h +++ b/Consts.h @@ -35,12 +35,15 @@ //OS version x #define OS_MAJOR_VERSION_X 10 -//OS version lion +//OS minor version lion #define OS_MINOR_VERSION_LION 8 -//OS version yosemite +//OS minor version yosemite #define OS_MINOR_VERSION_YOSEMITE 10 +//OS minor version el capitan +#define OS_MINOR_VERSION_EL_CAPITAN 11 + //executable path #define EXECUTABLE_PATH @"@executable_path" @@ -54,6 +57,15 @@ //path to LSOF #define LSOF @"/usr/sbin/lsof" +//path to vmmap +#define VMMAP @"/usr/bin/vmmap" + +//path to arch +#define ARCH @"/usr/bin/arch" + +//path to file +#define FILE @"/usr/bin/file" + //hash key, SHA1 #define KEY_HASH_SHA1 @"sha1" @@ -289,12 +301,6 @@ //socket protocol #define KEY_SOCKET_PROTO @"socketProto" -//listening socket -#define SOCKET_LISTENING @"listening" - -//connected socket -#define SOCKET_ESTABLISHED @"connected" - //sort by pid #define SORT_BY_PID 0x0 @@ -304,9 +310,8 @@ //delta for pid tag #define PID_TAG_DELTA 1000 -//TODO: CHANGE B4 RELEASE //search wait time (from app's launch) -#define SEARCH_WAIT_TIME 10 +#define SEARCH_WAIT_TIME 30 //pls wait (search) message #define PLS_WAIT_MESSAGE @"completing (intial) task/dylib/file enumeration please wait" diff --git a/Filter.m b/Filter.m index cb9da1b..6ae4363 100644 --- a/Filter.m +++ b/Filter.m @@ -92,6 +92,10 @@ NSString * const BINARY_KEYWORDS[] = {@"#apple", @"#nonapple", @"#signed", @"#un // ->note: already checked its a full/matching keyword isKeyword = [filterText hasPrefix:@"#"]; + //sync + @synchronized(items) + { + //iterate over all tasks for(NSNumber* taskKey in items) { @@ -146,10 +150,11 @@ NSString * const BINARY_KEYWORDS[] = {@"#apple", @"#nonapple", @"#signed", @"#un }//all tasks + }//sync + return; } - //filter dylibs and files // ->name and path -(void)filterFiles:(NSString*)filterText items:(NSMutableArray*)items results:(NSMutableArray*)results @@ -164,6 +169,10 @@ NSString * const BINARY_KEYWORDS[] = {@"#apple", @"#nonapple", @"#signed", @"#un // ->note: already checked its a full/matching keyword isKeyword = [filterText hasPrefix:@"#"]; + //sync + @synchronized(items) + { + //iterate over all tasks for(ItemBase* item in items) { @@ -206,6 +215,9 @@ NSString * const BINARY_KEYWORDS[] = {@"#apple", @"#nonapple", @"#signed", @"#un } }//all items + + //sync + } return; } @@ -216,87 +228,92 @@ NSString * const BINARY_KEYWORDS[] = {@"#apple", @"#nonapple", @"#signed", @"#un //first reset filter'd items [results removeAllObjects]; - //iterate over all tasks - for(Connection* item in items) + //sync + @synchronized(items) { - //check local ip - if( (nil != item.localIPAddr) && - (NSNotFound != [item.localIPAddr rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) + //iterate over all tasks + for(Connection* item in items) { - //save match - [results addObject:item]; + //check local ip + if( (nil != item.localIPAddr) && + (NSNotFound != [item.localIPAddr rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) + { + //save match + [results addObject:item]; + + //next + continue; + } - //next - continue; - } - - //check local port - if( (nil != item.localIPAddr) && - (NSNotFound != [[NSString stringWithFormat:@"%d", [item.localPort unsignedShortValue]] rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) - { - //save match - [results addObject:item]; + //check local port + if( (nil != item.localIPAddr) && + (NSNotFound != [[NSString stringWithFormat:@"%d", [item.localPort unsignedShortValue]] rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) + { + //save match + [results addObject:item]; + + //next + continue; + } - //next - continue; - } - - //check remote ip - if( (nil != item.remoteIPAddr) && - (NSNotFound != [item.remoteIPAddr rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) - { - //save match - [results addObject:item]; + //check remote ip + if( (nil != item.remoteIPAddr) && + (NSNotFound != [item.remoteIPAddr rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) + { + //save match + [results addObject:item]; + + //next + continue; + } - //next - continue; - } - - //check remote port - if( (nil != item.remoteIPAddr) && - (NSNotFound != [[NSString stringWithFormat:@"%d", [item.remotePort unsignedShortValue]] rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) - { - //save match - [results addObject:item]; + //check remote port + if( (nil != item.remoteIPAddr) && + (NSNotFound != [[NSString stringWithFormat:@"%d", [item.remotePort unsignedShortValue]] rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) + { + //save match + [results addObject:item]; + + //next + continue; + } - //next - continue; - } - - //check family - if( (nil != item.family) && - (NSNotFound != [item.family rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) - { - //save match - [results addObject:item]; + //check family + if( (nil != item.family) && + (NSNotFound != [item.family rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) + { + //save match + [results addObject:item]; + + //next + continue; + } - //next - continue; - } - - //check protocol - if( (nil != item.proto) && - (NSNotFound != [item.proto rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) - { - //save match - [results addObject:item]; + //check protocol + if( (nil != item.proto) && + (NSNotFound != [item.proto rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) + { + //save match + [results addObject:item]; + + //next + continue; + } - //next - continue; - } - - //check state - if( (nil != item.state) && - (NSNotFound != [item.state rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) - { - //save match - [results addObject:item]; + //check state + if( (nil != item.state) && + (NSNotFound != [item.state rangeOfString:filterText options:NSCaseInsensitiveSearch].location) ) + { + //save match + [results addObject:item]; + + //next + continue; + } - //next - continue; - } - - }//all connections + }//all connections + + }//sync return; } diff --git a/FlaggedItems.m b/FlaggedItems.m index bc0bf8e..71b1276 100644 --- a/FlaggedItems.m +++ b/FlaggedItems.m @@ -326,7 +326,7 @@ bail: //open Finder // ->will reveal binary - [[NSWorkspace sharedWorkspace] selectFile:path inFileViewerRootedAtPath:nil]; + [[NSWorkspace sharedWorkspace] selectFile:path inFileViewerRootedAtPath:@""]; //bail bail: diff --git a/InfoWindowController.m b/InfoWindowController.m index 826efe8..b4c9046 100644 --- a/InfoWindowController.m +++ b/InfoWindowController.m @@ -137,7 +137,7 @@ } //set args - [self.arguments setStringValue:[self valueForStringItem:[task.arguments componentsJoinedByString:@""] default:@"no arguments"]]; + [self.arguments setStringValue:[self valueForStringItem:[task.arguments componentsJoinedByString:@""] default:@"no arguments/unknown"]]; //set path [self.path setStringValue:[self valueForStringItem:task.binary.path default:@"unknown"]]; diff --git a/ItemView.m b/ItemView.m index 50d71a3..f359c71 100644 --- a/ItemView.m +++ b/ItemView.m @@ -397,8 +397,6 @@ NSTableCellView* createTaskView(NSTableView* tableView, id owner, Task* task) //set code signing icon ((NSImageView*)[taskCell viewWithTag:TABLE_ROW_SIGNATURE_ICON]).image = getCodeSigningIcon(task.binary); - //TODO: red for flagged? - //default // ->(re)set main textfield's color to black taskCell.textField.textColor = [NSColor blackColor]; @@ -563,7 +561,6 @@ NSTableCellView* createNetworkView(NSTableView* tableView, id owner, Connection* //item cell NSTableCellView* connectionCell = nil; - //TODO: don't need this to be mutable str? //connection details NSMutableString* details = nil; @@ -636,10 +633,7 @@ void configVTButton(NSTableCellView *itemCell, id owner, Binary* binary) //grab virus total button vtButton = [itemCell viewWithTag:TABLE_ROW_VT_BUTTON]; - - - //[itemCell viewWithTag:TABLE_ROW_VT_BUTTON]; - + //configure/show VT info // ->only if 'disable' preference not set //if(YES != ((AppDelegate*)[[NSApplication sharedApplication] delegate]).prefsWindowController.disableVTQueries) diff --git a/Items/Connection.m b/Items/Connection.m index 1332fe7..47a5e41 100644 --- a/Items/Connection.m +++ b/Items/Connection.m @@ -68,17 +68,19 @@ if(nil != self.state) { //listening - if(YES == [self.state isEqualToString:SOCKET_LISTENING]) + if(YES == [self.state isEqualToString:@"listening"]) { //set self.icon = [NSImage imageNamed:@"listeningIcon"]; } //connected - else if(YES == [self.state isEqualToString:SOCKET_ESTABLISHED]) + else if(YES == [self.state isEqualToString:@"established"]) { //set self.icon = [NSImage imageNamed:@"connectedIcon"]; } + + //TODO: set other icon? } //set icon for UDP sockets diff --git a/Items/File.m b/Items/File.m index 492b758..b89096b 100644 --- a/Items/File.m +++ b/Items/File.m @@ -66,7 +66,8 @@ bail: NSArray* parsedResults = nil; //exec 'file' to get file type - results = [[NSString alloc] initWithData:execTask(@"/usr/bin/file", @[self.path]) encoding:NSUTF8StringEncoding]; + //TODO: make const, and this ERRORS out a bunch? + results = [[NSString alloc] initWithData:execTask(FILE, @[self.path]) encoding:NSUTF8StringEncoding]; //sanity check if(nil == results) diff --git a/RequestRootWindowController.m b/RequestRootWindowController.m index 54db8a5..eae01c8 100644 --- a/RequestRootWindowController.m +++ b/RequestRootWindowController.m @@ -154,9 +154,9 @@ //2nd arg: permissions // ->4 at front is setuid - //TODO: CHANGE B4 RELEASE!! - //TODO: make 4755 before deploy (for testing, 777 makes XCOde be able to del it during build!) - installArgs[1] = "4777"; + //TODO: change b4 release + // ->make 4755 before deploy (for testing, 777 makes XCOde be able to del it during build!) + installArgs[1] = "4755"; //3rd arg: XPC service installArgs[2] = [xpcService UTF8String]; @@ -201,7 +201,7 @@ bail: if(0 != authorizationRef) { //free - AuthorizationFree(authorizationRef, kAuthorizationFlagDefaults); + AuthorizationFree(authorizationRef, kAuthorizationFlagDestroyRights); } //on auth/'install' success diff --git a/SearchWindowController.m b/SearchWindowController.m index 5c49ae3..8f043f1 100644 --- a/SearchWindowController.m +++ b/SearchWindowController.m @@ -7,9 +7,6 @@ // -//TODO: mouse over for info/show in finder buttons! - - #import "AppDelegate.h" #import "SearchWindowController.h" #import "ItemView.h" @@ -108,9 +105,6 @@ }); } - //TODO: - //else, hide text!!! - return; } @@ -218,7 +212,8 @@ }); } - //update UI on main thread + //timeout hit + // ->update UI on main thread dispatch_async(dispatch_get_main_queue(), ^{ //hide overlay @@ -484,8 +479,6 @@ bail: } //search -//TODO: SYNC DYLIBS etc -//TODO: search network conns -(void)search { //search string @@ -553,15 +546,9 @@ bail: } //1st: search for all matching tasks - //sync - @synchronized(allTasks) - { - //search for all matching tasks [self.filterObj filterTasks:searchString items:allTasks results:matchingTasks]; - }//sync - //add all tasks [self.searchResults addObjectsFromArray:matchingTasks]; @@ -572,40 +559,38 @@ bail: //sync @synchronized(allTasks) { - - //reset - [matchingItems removeAllObjects]; - - //TODO: B4 RELEASE! SYNC DYLIBS ARRAY!!! - //walk all tasks - // ->scan each for dylib matches, only processing first match - for(NSNumber* taskPid in allTasks) - { - //extract task - task = allTasks[taskPid]; - - //filter - [self.filterObj filterFiles:searchString items:task.dylibs results:matchingItems]; - - //process all matching dylibs - // ->but first check if processed due to matching in another task already - for(Binary* dylib in matchingItems) + //reset + [matchingItems removeAllObjects]; + + //walk all tasks + // ->scan each for dylib matches, only processing first match + for(NSNumber* taskPid in allTasks) { - //ignore if already seen/processed - if(nil != matchingDylibs[dylib.path]) + //extract task + task = allTasks[taskPid]; + + //filter + [self.filterObj filterFiles:searchString items:task.dylibs results:matchingItems]; + + //process all matching dylibs + // ->but first check if processed due to matching in another task already + for(Binary* dylib in matchingItems) { - //skip - continue; + //ignore if already seen/processed + if(nil != matchingDylibs[dylib.path]) + { + //skip + continue; + } + + //process + [self.searchResults addObject:dylib]; + + //save + matchingDylibs[dylib.path] = dylib; } - //process - [self.searchResults addObject:dylib]; - - //save - matchingDylibs[dylib.path] = dylib; - } - - }//all tasks + }//all tasks }//sync @@ -625,10 +610,10 @@ bail: { //extract task task = allTasks[taskPid]; - + //filter [self.filterObj filterFiles:searchString items:task.files results:matchingItems]; - + //process all matching dylibs // ->but first check if processed due to matching in another task already for(File* file in matchingItems) @@ -656,7 +641,6 @@ bail: //4th: search for all matching network comms //sync - //TODO: sync network connections @synchronized(allTasks) { //reset @@ -822,7 +806,7 @@ bail: //open Finder // ->will reveal binary - [[NSWorkspace sharedWorkspace] selectFile:path inFileViewerRootedAtPath:nil]; + [[NSWorkspace sharedWorkspace] selectFile:path inFileViewerRootedAtPath:@""]; //bail bail: diff --git a/Task.h b/Task.h index 958b8db..9cc8df7 100644 --- a/Task.h +++ b/Task.h @@ -79,13 +79,13 @@ struct dyld_image_info_32 { //enumerate all dylibs // ->new ones are added to 'existingDylibs' (global) dictionary --(void)enumerateDylibs:(NSXPCConnection*)xpcConnection allDylibs:(NSMutableDictionary*)allDylibs; +-(void)enumerateDylibs:(NSMutableDictionary*)allDylibs; //enumerate all open files --(void)enumerateFiles:(NSXPCConnection*)xpcConnection; +-(void)enumerateFiles; //enumerate network sockets/connections --(void)enumerateNetworking:(NSXPCConnection*)xpcConnection; +-(void)enumerateNetworking; //convert self to JSON string -(NSString*)toJSON; diff --git a/Task.m b/Task.m index f0c3a8f..d0a1a85 100644 --- a/Task.m +++ b/Task.m @@ -26,6 +26,7 @@ #import #import #import +#import @implementation Task @@ -305,8 +306,11 @@ bail: //enumerate all dylibs // ->new ones are added to 'existingDylibs' (global) dictionary --(void)enumerateDylibs:(NSXPCConnection*)xpcConnection allDylibs:(NSMutableDictionary*)allDylibs +-(void)enumerateDylibs:(NSMutableDictionary*)allDylibs { + //xpc connection + __block NSXPCConnection* xpcConnection = nil; + //dylib instance (as Binary) obj __block Binary* dylib = nil; @@ -317,10 +321,34 @@ bail: //alloc array for new dylibs newDylibs = [NSMutableArray array]; + //alloc XPC connection + xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.remoteTaskService"]; + + //set remote object interface + xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(remoteTaskProto)]; + + //set classes + // ->arrays & strings are what is ok to vend + [xpcConnection.remoteObjectInterface + setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], nil] + forSelector: @selector(enumerateDylibs:withReply:) + argumentIndex: 0 // the first parameter + ofReply: YES // in the method itself. + ]; + + //resume + [xpcConnection resume]; + //invoke XPC service (running as r00t) // ->will enumerate dylibs, then invoke reply block to save into iVar [[xpcConnection remoteObjectProxy] enumerateDylibs:self.pid withReply:^(NSMutableArray* dylibPaths) { + //close connection + [xpcConnection invalidate]; + + //nil out + xpcConnection = nil; + //sync @synchronized(self.dylibs) { @@ -425,24 +453,51 @@ bail: } //enumerate all file descriptors --(void)enumerateFiles:(NSXPCConnection*)xpcConnection +-(void)enumerateFiles { + //xpc connection + __block NSXPCConnection* xpcConnection = nil; + //File object __block File* file = nil; //new files - NSMutableArray* newFiles = nil; + //NSMutableArray* newFiles = nil; //file path __block NSString* filePath = nil; //alloc array for new files - newFiles = [NSMutableArray array]; + //newFiles = [NSMutableArray array]; + + //alloc XPC connection + xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.remoteTaskService"]; + + //set remote object interface + xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(remoteTaskProto)]; + + //set classes + // ->arrays & strings are what is ok to vend + [xpcConnection.remoteObjectInterface + setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], nil] + forSelector: @selector(enumerateFiles:withReply:) + argumentIndex: 0 // the first parameter + ofReply: YES // in the method itself. + ]; + + //resume + [xpcConnection resume]; //invoke XPC service (running as r00t) // ->will enumerate files, then invoke reply block so can save into iVar [[xpcConnection remoteObjectProxy] enumerateFiles:self.pid withReply:^(NSMutableArray* fileDescriptors) { + //close connection + [xpcConnection invalidate]; + + //nil out + xpcConnection = nil; + //sync @synchronized(self.files) { @@ -474,13 +529,14 @@ bail: [self.files addObject:file]; } + /* //save new files - // ->will be processed below if(nil == [((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.files objectForKey:filePath]) { //save as new [newFiles addObject:file]; } + */ } //sort by name @@ -496,13 +552,11 @@ bail: }//sync + /* ...don't need to process as search is done via task iteration //process all new files // ->determine type, etc & save into global list for(File* newFile in newFiles) { - //generate detailed info - [newFile generateDetailedInfo]; - //sync @synchronized(((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.files) { @@ -510,20 +564,48 @@ bail: [((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.files setObject:newFile forKey:filePath]; } } + */ }]; return; } //enumerate network sockets/connections --(void)enumerateNetworking:(NSXPCConnection*)xpcConnection +-(void)enumerateNetworking { - //File object + //xpc connection + __block NSXPCConnection* xpcConnection = nil; + + //Connection object __block Connection* connection = nil; + //alloc XPC connection + xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.remoteTaskService"]; + + //set remote object interface + xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(remoteTaskProto)]; + + //set classes + // ->arrays & strings are what is ok to vend + [xpcConnection.remoteObjectInterface + setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSMutableDictionary class], [NSString class], [NSNumber class], nil] + forSelector: @selector(enumerateNetwork:withReply:) + argumentIndex: 0 // the first parameter + ofReply: YES // in the method itself. + ]; + + //resume + [xpcConnection resume]; + //invoke XPC service (running as r00t) // ->will enumerate network sockets/connections, then invoke reply block so can save into iVar [[xpcConnection remoteObjectProxy] enumerateNetwork:self.pid withReply:^(NSMutableArray* networkItems) { + + //close connection + [xpcConnection invalidate]; + + //nil out + xpcConnection = nil; //sync @synchronized(self.connections) @@ -615,7 +697,7 @@ bail: (0 == taskCommandLine.length)) { //default - taskCommandLine = @"no arguments"; + taskCommandLine = @"no arguments/unknown"; } //init file hash to default string @@ -676,7 +758,6 @@ bail: vtDetectionRatio = [NSString stringWithFormat:@"%lu/%lu", (unsigned long)[self.binary.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue], (unsigned long)[self.binary.vtInfo[VT_RESULTS_TOTAL] unsignedIntegerValue]]; //sync - //TODO: make sure this is sync'd elsewhere @synchronized(self.dylibs) { //convert all dylibs and add @@ -695,7 +776,6 @@ bail: } //sync - //TODO: make sure this is sync'd elsewhere @synchronized(self.files) { //convert all file and add @@ -714,7 +794,6 @@ bail: } //sync - //TODO: make sure this is sync'd elsewhere @synchronized(self.connections) { //convert all dylibs and add diff --git a/TaskEnumerator.h b/TaskEnumerator.h index df6aa35..32addca 100644 --- a/TaskEnumerator.h +++ b/TaskEnumerator.h @@ -26,7 +26,7 @@ @property(nonatomic, retain)NSMutableDictionary* executables; //all (opened) files -@property(nonatomic, retain)NSMutableDictionary* files; +//@property(nonatomic, retain)NSMutableDictionary* files; //all dylibs @property(nonatomic, retain)NSMutableDictionary* dylibs; diff --git a/TaskEnumerator.m b/TaskEnumerator.m index 461e7cc..f898afe 100644 --- a/TaskEnumerator.m +++ b/TaskEnumerator.m @@ -23,7 +23,7 @@ @implementation TaskEnumerator -@synthesize files; +//@synthesize files; @synthesize tasks; @synthesize dylibs; @synthesize binaryQueue; @@ -55,7 +55,6 @@ //enumerate all tasks // ->calls back into app delegate to update task (top) table when pau -// TODO: call every x # of seconds? // TODO: existsing tasks w/ nil vtInfo, call [vtObject addItem:binary] ? -(void)enumerateTasks { @@ -68,16 +67,13 @@ //new tasks OrderedDictionary* newTasks = nil; - //xpc connection - NSXPCConnection* xpcConnection = nil; + //thread priority + double threadPriority = 0; //determine if network is connected // ->sets 'isConnected' flag ((AppDelegate*)[[NSApplication sharedApplication] delegate]).isConnected = isNetworkConnected(); - //get xpc connection - xpcConnection = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).xpcConnection; - //get all tasks // ->pids and binary obj with just path/name newTasks = [self getAllTasks]; @@ -101,6 +97,7 @@ // ->ensures existing task and their info are reused for(NSNumber* key in newTasks.allKeys) { + //get task newTask = newTasks[key]; @@ -123,9 +120,8 @@ //sync @synchronized(self.tasks) { - - //add new task - [self.tasks setObject:newTask forKey:newTask.pid]; + //add new task + [self.tasks setObject:newTask forKey:newTask.pid]; }//sync @@ -161,10 +157,6 @@ continue; } - //nap - // ->helps with UI - [NSThread sleepForTimeInterval:0.01f]; - //generate signing info [newTask.binary generatedSigningInfo]; @@ -176,20 +168,30 @@ [((AppDelegate*)[[NSApplication sharedApplication] delegate]) reloadBottomPane:newTask itemView:CURRENT_VIEW]; }//signing info for all new tasks + + /* + begin enumeration of dylibs/files/network connections + ->this is for global search, as otherwise, each is re-gen'd per task on each bottom-pane click + */ + + //get current thread priority + threadPriority = [NSThread threadPriority]; + + //reduce thread priorty + [NSThread setThreadPriority:0.0f]; //begin dylib enumeration - // ->this is really just for search/filter views since they are re-gen'd per task on each bottom-pane click for(NSNumber* key in newTasks) { //get task newTask = newTasks[key]; //enumerate - [newTask enumerateDylibs:xpcConnection allDylibs:self.dylibs]; + [newTask enumerateDylibs:self.dylibs]; //nap // ->helps with UI - [NSThread sleepForTimeInterval:0.01f]; + [NSThread sleepForTimeInterval:0.1f]; } //begin file enumeration @@ -200,11 +202,11 @@ newTask = newTasks[key]; //enumerate - [newTask enumerateFiles:xpcConnection]; + [newTask enumerateFiles]; //nap // ->helps with UI - [NSThread sleepForTimeInterval:0.01f]; + [NSThread sleepForTimeInterval:0.1f]; } //begin network enumeration @@ -215,13 +217,16 @@ newTask = newTasks[key]; //enumerate - [newTask enumerateNetworking:xpcConnection]; + [newTask enumerateNetworking]; //nap // ->helps with UI - [NSThread sleepForTimeInterval:0.01f]; + [NSThread sleepForTimeInterval:0.1f]; } + //reset thread priority + [NSThread setThreadPriority:threadPriority]; + return; } @@ -267,7 +272,7 @@ if(status < 0) { //err - syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: proc_listpids() failed with %d", status); + //syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: proc_listpids() failed with %d", status); //bail goto bail; @@ -437,14 +442,6 @@ bail: // ->the dead task or its dylibs might have been flagged [self updateFlaggedItems:deadTask]; - //(re)set label for flagged items to black - // when there are no flagged items - if(0 == ((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedItems.count) - { - //set to gray - ((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedLabel.textColor = [NSColor lightGrayColor]; - } - //get launchd's task // ->its 'pid' is 0x1 launchdTask = self.tasks[@1]; @@ -470,8 +467,19 @@ bail: } } - //remove dead task task - [self.tasks removeObjectForKey:deadTask.pid]; + //sync to remove from all tasks + @synchronized(self.tasks) + { + //remove dead task task + [self.tasks removeObjectForKey:deadTask.pid]; + } + + //sync to remove from all executables + @synchronized(((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.executables) + { + //remove dead executables + [((AppDelegate*)[[NSApplication sharedApplication] delegate]).taskEnumerator.executables removeObjectForKey:deadTask.binary.path]; + } //get parent parent = [self.tasks objectForKey:deadTask.ppid]; @@ -556,6 +564,17 @@ bail: } } + //when there are no flagged items + // ->(re)set flagged icon to black + if(0 == ((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedItems.count) + { + //set main image + [((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedButton setImage:[NSImage imageNamed:@"flagged"]]; + + //set alternate image + [((AppDelegate*)[[NSApplication sharedApplication] delegate]).flaggedButton setAlternateImage:[NSImage imageNamed:@"flaggedBG"]]; + } + return; } @@ -671,7 +690,6 @@ bail: } //sync - //TODO: B4 RELEASE, sync files/dylibs/connections @synchronized(self.tasks) { //iterate over all tasks @@ -683,59 +701,74 @@ bail: //dylib check if(YES == isDylib) { - //check if dylib is loaded in task - for(Binary* taskDylib in task.dylibs) + //sync + @synchronized(task.dylibs) { - //check for task has dylib - if(taskDylib == (Binary*)item) + //check if dylib is loaded in task + for(Binary* taskDylib in task.dylibs) { - //save - [hostTasks addObject:task]; - - //can bail, since match was found - break; + //check for task has dylib + if(taskDylib == (Binary*)item) + { + //save + [hostTasks addObject:task]; + + //can bail, since match was found + break; + } } - } - }//dylib check + + }//sync + + }//dylibs //file check else if(YES == isFile) { - //check if file is loaded in task - for(File* taskFile in task.files) + //sync + @synchronized(task.files) { - //check for task has dylib - if(taskFile == (File*)item) + //check if file is loaded in task + for(File* taskFile in task.files) { - //save - [hostTasks addObject:task]; - - //can bail, since match was found - break; + //check for task has file + if(taskFile == (File*)item) + { + //save + [hostTasks addObject:task]; + + //can bail, since match was found + break; + } } - } + }//sync - }//file check + }//files //connection check else if(YES == isConnection) { - //check if connection is 'in' task - for(Connection* taskConnection in task.connections) + //sync + @synchronized(task.connections) { - //check for task has connection - // note: ->check via endpoints, as that a good representation of connection(?) - if(YES == [taskConnection.endpoints isEqualToString: ((Connection*)item).endpoints]) + //check if connection is 'in' task + for(Connection* taskConnection in task.connections) { - //save - [hostTasks addObject:task]; - - //can bail, since match was found - break; + //check for task has connection + // note: ->check via endpoints, as that a good representation of connection(?) + if(YES == [taskConnection.endpoints isEqualToString: ((Connection*)item).endpoints]) + { + //save + [hostTasks addObject:task]; + + //can bail, since match was found + break; + } } - } - } - + }//sync + + }//connections + }//all tasks }//sync diff --git a/TaskExplorer-Info.plist b/TaskExplorer-Info.plist index 1e89437..bf1ae32 100755 --- a/TaskExplorer-Info.plist +++ b/TaskExplorer-Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile icon CFBundleIdentifier - com.objective-see.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1.0 + 1.2.0 CFBundleSignature ???? CFBundleVersion - 1.1.0 + 1.2.0 LSMinimumSystemVersion ${MACOSX_DEPLOYMENT_TARGET} NSHumanReadableCopyright diff --git a/TaskExplorer.xcodeproj/xcuserdata/patrickw.xcuserdatad/xcschemes/TaskExplorer.xcscheme b/TaskExplorer.xcodeproj/xcuserdata/patrickw.xcuserdatad/xcschemes/TaskExplorer.xcscheme index ee52d29..30281bd 100644 --- a/TaskExplorer.xcodeproj/xcuserdata/patrickw.xcuserdatad/xcschemes/TaskExplorer.xcscheme +++ b/TaskExplorer.xcodeproj/xcuserdata/patrickw.xcuserdatad/xcschemes/TaskExplorer.xcscheme @@ -1,6 +1,6 @@ + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -38,15 +38,18 @@ ReferencedContainer = "container:TaskExplorer.xcodeproj"> + + @@ -62,10 +65,10 @@ diff --git a/TaskExplorer.xcodeproj/xcuserdata/patrickw.xcuserdatad/xcschemes/remoteTaskService.xcscheme b/TaskExplorer.xcodeproj/xcuserdata/patrickw.xcuserdatad/xcschemes/remoteTaskService.xcscheme index ae6d5ea..de85bf8 100644 --- a/TaskExplorer.xcodeproj/xcuserdata/patrickw.xcuserdatad/xcschemes/remoteTaskService.xcscheme +++ b/TaskExplorer.xcodeproj/xcuserdata/patrickw.xcuserdatad/xcschemes/remoteTaskService.xcscheme @@ -1,6 +1,6 @@ + shouldUseLaunchSchemeArgsEnv = "YES"> + + will reveal binary - [[NSWorkspace sharedWorkspace] selectFile:path inFileViewerRootedAtPath:nil]; + [[NSWorkspace sharedWorkspace] selectFile:path inFileViewerRootedAtPath:@""]; //bail bail: diff --git a/Utilities.h b/Utilities.h index ce1bc74..1e59ba6 100644 --- a/Utilities.h +++ b/Utilities.h @@ -9,6 +9,9 @@ #ifndef DHS_Utilities_h #define DHS_Utilities_h +#import +#import + //get the signing info of a file NSDictionary* extractSigningInfo(NSString* path); @@ -74,4 +77,7 @@ BOOL isNetworkConnected(); //set or unset button's highlight void buttonAppearance(NSTableView* table, NSEvent* event, BOOL shouldReset); +//check if remote process is i386 +BOOL Is32Bit(pid_t targetPID); + #endif diff --git a/Utilities.m b/Utilities.m index ddba3c8..f8144e7 100644 --- a/Utilities.m +++ b/Utilities.m @@ -128,7 +128,7 @@ NSDictionary* extractSigningInfo(NSString* path) if(STATUS_SUCCESS != status) { //err msg - syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: SecStaticCodeCreateWithPath() failed on %s with %d", [path UTF8String], status); + //syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: SecStaticCodeCreateWithPath() failed on %s with %d", [path UTF8String], status); //bail goto bail; @@ -151,7 +151,7 @@ NSDictionary* extractSigningInfo(NSString* path) if(STATUS_SUCCESS != status) { //err msg - syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: SecCodeCopySigningInformation() failed on %s with %d", [path UTF8String], status); + //syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: SecCodeCopySigningInformation() failed on %s with %d", [path UTF8String], status); //bail goto bail; @@ -547,7 +547,7 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments) //set task's output [task setStandardOutput:outPipe]; - + //wrap task launch @try { @@ -556,6 +556,9 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments) } @catch(NSException *exception) { + //err msg + //syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: taskExec(%s) failed with %s", [binaryPath UTF8String], [[exception description] UTF8String]); + //bail goto bail; } @@ -570,6 +573,8 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments) //grab any left over data [output appendData:[readHandle readDataToEndOfFile]]; + //syslog(LOG_ERR, "OBJECTIVE-SEE: results here: %s", [[output description] UTF8String]); + //bail bail: @@ -871,3 +876,35 @@ bail: return; } +//check if remote process is i386 +BOOL Is32Bit(pid_t targetPID) +{ + //info struct + struct proc_bsdshortinfo procInfo = {0}; + + //flag + BOOL isI386 = NO; + + //get proc info + if(proc_pidinfo(targetPID, PROC_PIDT_SHORTBSDINFO, 0, &procInfo, PROC_PIDT_SHORTBSDINFO_SIZE) <= 0) + { + //error + goto bail; + } + + //check 64bit process flag + if(PROC_FLAG_LP64 != (procInfo.pbsi_flags & PROC_FLAG_LP64)) + { + //not x86_64 + // ->thus, i386 + isI386 = YES; + } + +//bail +bail: + + return isI386; + +} + + diff --git a/VirusTotal.m b/VirusTotal.m index 622e18e..ddc8ea4 100644 --- a/VirusTotal.m +++ b/VirusTotal.m @@ -197,153 +197,6 @@ return; } -/* -//thread function -// ->runs in the background to get virus total info about a plugin's items --(void)getInfo:(PluginBase*)plugin -{ - //plugin file items - // ->in dictionary w/ SHA1 hash as key - NSMutableDictionary* uniqueItems = nil; - - //File object - File* item = nil; - - //item data - NSMutableDictionary* itemData = nil; - - //items - NSMutableArray* items = nil; - - //VT query URL - NSURL* queryURL = nil; - - //results - NSDictionary* results = nil; - - //alloc dictionary for plugin file items - uniqueItems = [NSMutableDictionary dictionary]; - - //alloc list for items - items = [NSMutableArray array]; - - //init query URL - queryURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@", VT_QUERY_URL, VT_API_KEY]]; - - //sync - // ->since array will be reset if user clicks 'stop' scan - @synchronized(plugin.allItems) - { - - //place all plugin file items into dictionary - // ->key: hash, filter's out dups for queries - for(ItemBase* item in plugin.allItems) - { - //skip non-file items - if(YES != [item isKindOfClass:[File class]]) - { - //skip - continue; - } - - //skip item's without hashes - // ...not sure how this could ever happen - if(nil == ((File*)item).hashes[KEY_HASH_SHA1]) - { - //skip - continue; - } - - //add item - uniqueItems[((File*)item).hashes[KEY_HASH_SHA1]] = item; - } - - }//sync - - //iterate over all hashes - // ->create item dictionary (JSON), and add it to list - for(NSString* itemKey in uniqueItems) - { - //alloc item data - itemData = [NSMutableDictionary dictionary]; - - //exit if thread was cancelled - // ->i.e. user pressed 'stop' scan - if(YES == [[NSThread currentThread] isCancelled]) - { - //exit - [NSThread exit]; - } - - //extract item - item = uniqueItems[itemKey]; - - //auto start location - itemData[@"autostart_location"] = plugin.name; - - //set item name - itemData[@"autostart_entry"] = item.name; - - //set item path - itemData[@"image_path"] = item.path; - - //set hash - itemData[@"hash"] = item.hashes[KEY_HASH_SHA1]; - - //set creation times - itemData[@"creation_datetime"] = [item.attributes.fileCreationDate description]; - - //add item info to list - [items addObject:itemData]; - - //less then 25 items - // ->just keep collecting items - if(VT_MAX_QUERY_COUNT != items.count) - { - //next - continue; - } - - //make query to VT - results = [self postRequest:queryURL parameters:items]; - if(nil != results) - { - //process results - [self processResults:plugin.allItems results:results]; - } - - //remove all items - // ->since they've been processed - [items removeAllObjects]; - } - - //process any remaining items - if(0 != items.count) - { - //query virus total - results = [self postRequest:queryURL parameters:items]; - if(nil != results) - { - //process results - [self processResults:plugin.allItems results:results]; - } - } - - //exit if thread was cancelled - // ->i.e. user pressed 'stop' scan - if(YES == [[NSThread currentThread] isCancelled]) - { - //exit - [NSThread exit]; - } - - //tell UI all plugin's items have all be processed - [((AppDelegate*)[[NSApplication sharedApplication] delegate]) itemsProcessed:plugin]; - - return; -} -*/ - //get VT info for a single item // ->will then callback into AppDelegate to reload item in UI -(void)getInfoForItem:(Binary*)item scanID:(NSString*)scanID diff --git a/changelog.txt b/changelog.txt index 400d611..6c60705 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,39 +1,25 @@ -KNOCKKNOCK CHANGELOG - -VERSION 1.0.0 (4/23/2015) - initial release +TASKEXPLORER CHANGELOG -VERSION 1.1.0 (4/24/2015) - added plugin to scan for Authorization Plugins - fixed NSJSONSerialization bug (parsing Google Chrome plugins) +VERSION 1.2.0 (10/11/2015) + el capitan/rootless compatibility + autocomplete search/filter + keyboard shortcuts (cmd+s, cmd+r, cmd+f, cmd+w) + network results included in global search results + xpc-comms refactor (major speed improvement!) + ui fixes/improvements -VERSION 1.2.0 (4/25/2015) - added DYLD_INSERT_LIBRARIES plugin - browser extensions plugin now supports enumerating Opera plugins - browser extensions plugin improved to enumerate Google Chrome with multiple profiles - increased timeouts for making a popups modal (to avoid NSInternalInconsistencyException issues) - fixed nil dictionary insertion when processing Safari extensions with missin 'Bundle Identifier' +VERSION 1.1.0 (8/23/2015) + added a global search window (tasks, dylibs, files) + added a flagged items window for any flagged tasks or dylibs + xpc-helper's security improved by allowing only Objective-See binary to connect + ui fixes/improvements -VERSION 1.2.1 (4/25/2015) - improved DYLD_INSERT_LIBRARIES plugin to report path to applications' Info.plist as string (instead of URL) - fixed issue in DYLD_INSERT_LIBRARIES plugin, where NSInvalidArgumentException would result if enviro var was string - - -VERSION 1.2.2 (4/28/2015) - browser extensions plugin now supports enumerating extensions in older versions of Safari - improved JSON output & fixed bug when saving JSON when file hash or signature was nil - recompiled with updated/improved (shared) MachO parser - fixed issue where on multiple scans, result popup was not properly updated - improved UI to display item's plist (when applicable) into the item's row - listed items in item table are now selectable - -VERSION 1.2.3 (4/30/2015) - improved VirusTotal logic (e.g. when an signed OS file was flagged) - tweaked UI to be more compatible with OS X 10.9 - - +VERSION 1.0.1 (8/5/2015) + fixed crash on Mavericks (NSSearchField/setPlaceholderString:) +VERSION 1.0.0 (8/5/2015) + initial release \ No newline at end of file diff --git a/en.lproj/MainMenu.xib b/en.lproj/MainMenu.xib index 11bc0ff..c6c8bad 100755 --- a/en.lproj/MainMenu.xib +++ b/en.lproj/MainMenu.xib @@ -1,9 +1,9 @@ - + - + @@ -40,27 +40,27 @@ - - - - + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - + - + @@ -121,7 +97,7 @@ - + @@ -135,7 +111,7 @@ - - + + @@ -176,8 +139,8 @@ - - - - - - - - - - + - + - - + @@ -234,7 +202,7 @@ - + @@ -277,7 +245,7 @@ - + @@ -292,7 +260,7 @@ - + @@ -302,7 +270,6 @@ - diff --git a/images/flaggedRed.png b/images/flaggedRed.png new file mode 100755 index 0000000..345bff1 Binary files /dev/null and b/images/flaggedRed.png differ diff --git a/images/flaggedRedBG.png b/images/flaggedRedBG.png new file mode 100755 index 0000000..2084dec Binary files /dev/null and b/images/flaggedRedBG.png differ diff --git a/images/flaggedRedOver.png b/images/flaggedRedOver.png new file mode 100755 index 0000000..2cd5043 Binary files /dev/null and b/images/flaggedRedOver.png differ diff --git a/remoteTaskService/Info.plist b/remoteTaskService/Info.plist index 2822429..f929ad7 100644 --- a/remoteTaskService/Info.plist +++ b/remoteTaskService/Info.plist @@ -9,7 +9,7 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - com.objective-see.$(PRODUCT_NAME:rfc1034identifier) + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -17,11 +17,11 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 1.1.0 + 1.2.0 CFBundleSignature ???? CFBundleVersion - 1.1.0 + 1.2.0 NSHumanReadableCopyright Copyright © 2015 Objective-See, LLC. All rights reserved. XPCService diff --git a/remoteTaskService/main.m b/remoteTaskService/main.m index ec6370f..adb61a6 100644 --- a/remoteTaskService/main.m +++ b/remoteTaskService/main.m @@ -41,7 +41,7 @@ OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement) //TODO: CHANGE B4 RELEASE!! //-> for testing: @"Mac Developer: patrick wardle (5SKKU32KLJ)" -#define SIGNING_AUTH @"Mac Developer: patrick wardle (5SKKU32KLJ)"//@"Developer ID Application: Objective-See, LLC (VBG97UB4TA)" +#define SIGNING_AUTH @"Developer ID Application: Objective-See, LLC (VBG97UB4TA)" //skeleton interface @interface ServiceDelegate : NSObject @@ -105,6 +105,10 @@ bail: int main(int argc, const char *argv[]) { + //make really r00t + // ->needed for exec'ing vmmap + setuid(0); + //create the delegate for the service. ServiceDelegate *delegate = [ServiceDelegate new]; diff --git a/remoteTaskService/remoteTaskService.m b/remoteTaskService/remoteTaskService.m index 8a96c42..f97f0ee 100644 --- a/remoteTaskService/remoteTaskService.m +++ b/remoteTaskService/remoteTaskService.m @@ -5,8 +5,11 @@ // Created by Patrick Wardle on 5/27/15. // Copyright (c) 2015 Patrick Wardle. All rights reserved. // -#import "remoteTaskService.h" + #import "Consts.h" +#import "Utilities.h" +#import "remoteTaskService.h" + #import #import @@ -23,6 +26,22 @@ #import #import +//socket states +// ->note, index correspondes to numberic value +static const char* socketStates[] = +{ + "closed", + "listening", + "syn sent", + "syn received", + "established", + "close/wait", + "fin wait 1", + "closing", + "last act", + "fin wait 2", + "time wait", +}; static const char *socketFamilies[] = { @@ -89,6 +108,41 @@ struct dyld_image_info_32 { //enumerate dylibs for a specified task // ->dylibs returned in array arg -(void)enumerateDylibs:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply; +{ + //dylibs + NSMutableArray* dylibPaths = nil; + + //minor OS X version + SInt32 versionMinor = 0; + + //get minor version + versionMinor = getVersion(gestaltSystemVersionMinor); + + //when OS version is older then el capitan + // ->read memory directly + if(versionMinor < OS_MINOR_VERSION_EL_CAPITAN) + { + //enum dylibs + dylibPaths = [self enumerateDylibsOld:(NSNumber*)taskPID]; + + } + //OS version is el capitan + // ->have to use vmmap, since we don't com.apple.system-task-ports entitlement + else + { + //enum dylibs + dylibPaths = [self enumerateDylibsNew:(NSNumber*)taskPID]; + } + + //invoke reply block + reply(dylibPaths); + + return; +} + +//enumerate dylibs via direct memory reading +// ->can only do this pre-el capitan +-(NSMutableArray*)enumerateDylibsOld:(NSNumber*)pid { //status kern_return_t status = !KERN_SUCCESS; @@ -133,14 +187,14 @@ struct dyld_image_info_32 { mach_msg_type_number_t dpBytesRead = 0; //dylibs - NSMutableArray* dylibPaths = nil; + NSMutableArray* dylibs = nil; //alloc array for dylibs - dylibPaths = [NSMutableArray array]; + dylibs = [NSMutableArray array]; //get task for pid // ->allows access to read remote process memory - status = task_for_pid(mach_task_self(), [taskPID intValue], &remoteTask); + status = task_for_pid(mach_task_self(), [pid intValue], &remoteTask); if(KERN_SUCCESS != status) { //err msg @@ -162,9 +216,6 @@ struct dyld_image_info_32 { goto bail; } - //dbg msg - //NSLog(@"got dyld_info: %#llx : %llx - %d", dyldInfo.all_image_info_addr, dyldInfo.all_image_info_size, dyldInfo.all_image_info_format); - //remotely read dyld_all_image_infos status = mach_vm_read(remoteTask, (vm_address_t)dyldInfo.all_image_info_addr, dyldInfo.all_image_info_size, (vm_offset_t*)&allImageInfo, &aifBytesRead); if(KERN_SUCCESS != status) @@ -247,22 +298,16 @@ struct dyld_image_info_32 { if( (KERN_SUCCESS != status) || (NULL == dylibPath) ) { - //err msg - //NSLog(@"ERROR: mach_vm_read() failed w/ %d", status); - //try next continue; } - //dbg msg - //NSLog(@"path (%d): %s", dpBytesRead, dylibPath); - //save it - [dylibPaths addObject:[NSString stringWithUTF8String:dylibPath]]; + [dylibs addObject:[NSString stringWithUTF8String:dylibPath]]; //dealloc mach_vm_deallocate(mach_task_self(), (vm_offset_t)dylibPath, dpBytesRead); - + }//for all dyld_image_info_32/dyld_image_info structs //bail @@ -283,25 +328,106 @@ bail: } //remove dups - //TODO: this isn't mutable - which is ok, but maybe change reply method def? - [dylibPaths setArray:[[NSSet setWithArray:dylibPaths] allObjects]]; + [dylibs setArray:[[[NSSet setWithArray:dylibs] allObjects] mutableCopy]]; - //invoke reply block - reply(dylibPaths); + return dylibs; + +} + +//enumerate dylibs via vmmap +// ->OS version is el capitan, and we don't have the com.apple.system-task-ports entitlement :/ +-(NSMutableArray*)enumerateDylibsNew:(NSNumber*)pid +{ + //dylibs + NSMutableArray* dylibs = nil; + + //results from 'file' cmd + NSString* results = nil; + + //path offset + NSRange pathOffset = {0}; + + //dylib + NSString* dylib = nil; + + //alloc array for dylibs + dylibs = [NSMutableArray array]; + + //vmmap can't directly handle 32bit procs + // ->so exec via 'arch -i386 vmmap <32bit pid>' ...though El Capitan doesn't have i386 version :/ + if(YES == Is32Bit(pid.unsignedIntValue)) + { + //exec 'file' to get file type + results = [[NSString alloc] initWithData:execTask(ARCH, @[@"-i386", VMMAP, [pid stringValue]]) encoding:NSUTF8StringEncoding]; + } + //for 64bit procs + // ->just exec vmmap directly + else + { + //exec 'file' to get file type + results = [[NSString alloc] initWithData:execTask(VMMAP, @[@"-w", [pid stringValue]]) encoding:NSUTF8StringEncoding]; + } + + //sanity check + if( (nil == results) || + (0 == results.length)) + { + //bail + goto bail; + } + + //iterate over all results + // ->line by line, looking for '__TEXT' + for(NSString* line in [results componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\n"]]) + { + //ignore any line that doesn't start with '__TEXT' + if(YES != [line hasPrefix:@"__TEXT"]) + { + //skip + continue; + } + + //format of line is: __TEXT 00007fff63564000-00007fff6359b000 [ 220K] r-x/rwx SM=COW /usr/lib/dyld + // ->grab path, by finding: ' /' + pathOffset = [line rangeOfString:@" /"]; + + //sanity check + // ->make sure path was found + if(NSNotFound == pathOffset.location) + { + //not found + continue; + } + + //extract dylib's path + // ->trim leading whitespace + dylib = [[line substringFromIndex:pathOffset.location] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; + + //sanity check + if(nil == dylib) + { + //skip + continue; + } + + //add to results array + [dylibs addObject:dylib]; + } + + //remove dups + [dylibs setArray:[[[NSSet setWithArray:dylibs] allObjects] mutableCopy]]; + +//bail +bail: + + return dylibs; - return; } //enumerate open files // ->accomplish this via lsof, since proc_pidinfo() misses some files... -(void)enumerateFiles:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply; { - //task - NSTask* task = nil; - - //pipe - NSPipe* pipe = nil; - //results NSData* results = nil; @@ -321,36 +447,14 @@ bail: //unique files NSMutableArray* files = nil; - //init task - task = [[NSTask alloc] init]; - - //init pipe - pipe = [NSPipe pipe]; - //init array for file paths filePaths = [NSMutableArray array]; //init array for unqiue files files = [NSMutableArray array]; - - //set task's stdout to pipe - [task setStandardOutput:pipe]; - - //set task's stderr to pipe - [task setStandardError:pipe]; - - //set launch path - [task setLaunchPath:LSOF]; - - //set tasks's args - // -> -Fn -p - [task setArguments:@[@"-Fn", @"-p", taskPID.stringValue]]; - - //launch - [task launch]; - - //get results - results = [[[task standardOutput] fileHandleForReading] readDataToEndOfFile]; + + //exec 'file' to get file type + results = execTask(LSOF, @[@"-Fn", @"-p", taskPID.stringValue]); //sanity check(s) if( (nil == results) || @@ -600,9 +704,7 @@ bail: if(SOCK_STREAM == socketInfo.psi.soi_type) { //set state - // ->for now, this will only be 'listening' or 'established' socket[KEY_SOCKET_STATE] = socketState2String(socketInfo.psi.soi_proto.pri_tcp.tcpsi_state); - } //add @@ -731,35 +833,17 @@ NSString* socketState2String(int state) //socket proto NSString* socketState = nil; - //convert to string - switch(state) + //set state + if(state < TCP_NSTATES) { - //listening - case TCPS_LISTEN: - - //set - socketState = SOCKET_LISTENING; - break; - - //established - case TCPS_ESTABLISHED: - - //set - socketState = SOCKET_ESTABLISHED; - break; - - default: - - //TODO: what states? - NSLog(@"unknown state: %d", state); - - //can't return nil - //TODO: hrmm - socketState = @"unknown"; - - break; + //set state + socketState = [NSString stringWithUTF8String:socketStates[state]]; + } + //invalid/unknown socket state + else + { + socketState = [NSString stringWithFormat:@"unknown state (%d)", state]; } - return socketState; }