├── .gitignore ├── 3rdParty ├── HyperlinkTextField.h ├── HyperlinkTextField.m ├── OrderedDictionary.h └── OrderedDictionary.m ├── AboutWindowController.h ├── AboutWindowController.m ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets ├── AppIcon.appiconset │ ├── Contents.json │ ├── icon_128x128.png │ ├── icon_128x128@2x.png │ ├── icon_16x16.png │ ├── icon_16x16@2x.png │ ├── icon_256x256.png │ ├── icon_256x256@2x.png │ ├── icon_32x32.png │ ├── icon_32x32@2x.png │ ├── icon_512x512.png │ └── icon_512x512@2x.png ├── Contents.json ├── FriendsHuntress.imageset │ ├── Contents.json │ ├── darkMode.png │ └── lightMode.png ├── FriendsJamf.imageset │ ├── Contents.json │ ├── darkMode.png │ └── lightMode.png ├── FriendsKandji.imageset │ ├── Contents.json │ ├── darkMode.png │ └── lightMode.png ├── FriendsMacPaw.imageset │ ├── Contents.json │ ├── darkMode.png │ └── lightMode.png ├── FriendsPANW.imageset │ ├── Contents.json │ ├── darkMode.png │ └── lightMode.png └── FriendsiVerify.imageset │ ├── Contents.json │ └── iVerify.png ├── Consts.h ├── CustomTextField.h ├── CustomTextField.m ├── Filter.h ├── Filter.m ├── FlaggedItemWindowController.h ├── FlaggedItemWindowController.m ├── InfoWindowController.h ├── InfoWindowController.m ├── ItemView.h ├── ItemView.m ├── Items ├── Binary.h ├── Binary.m ├── Connection.h ├── Connection.m ├── File.h ├── File.m ├── ItemBase.h └── ItemBase.m ├── KKRow.h ├── KKRow.m ├── LICENSE ├── NSApplicationKeyEvents.h ├── NSApplicationKeyEvents.m ├── NSMutableArray+QueueAdditions.h ├── NSMutableArray+QueueAdditions.m ├── PrefsWindowController.h ├── PrefsWindowController.m ├── Queue.h ├── Queue.m ├── RequestRootWindowController.h ├── RequestRootWindowController.m ├── ResultsWindowController.h ├── ResultsWindowController.m ├── SearchWindowController.h ├── SearchWindowController.m ├── Signing.h ├── Signing.m ├── Task.h ├── Task.m ├── TaskEnumerator.h ├── TaskEnumerator.m ├── TaskExplorer-Info.plist ├── TaskExplorer-Prefix.pch ├── TaskExplorer.entitlements ├── TaskExplorer.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ ├── TaskExplorer.xccheckout │ │ └── TaskExplorer.xcscmblueprint │ └── xcuserdata │ │ ├── patrick.xcuserdatad │ │ └── WorkspaceSettings.xcsettings │ │ └── patrickw.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── WorkspaceSettings.xcsettings └── xcuserdata │ ├── patrick.xcuserdatad │ └── xcschemes │ │ ├── TaskExplorer.xcscheme │ │ ├── remoteTaskService.xcscheme │ │ └── xcschememanagement.plist │ └── patrickw.xcuserdatad │ └── xcschemes │ ├── TaskExplorer.xcscheme │ ├── remoteTaskService.xcscheme │ └── xcschememanagement.plist ├── TaskTableController.h ├── TaskTableController.m ├── UI ├── AboutWindow.xib ├── DylibInfoWindow.xib ├── FileInfoWindow.xib ├── FlaggedItems.xib ├── FlatView.xib ├── NetworkInfoWindow.xib ├── PrefsWindow.xib ├── RequestRootWindow.xib ├── ResultsWindow.xib ├── SearchWindow.xib ├── TaskInfoWindow.xib ├── TreeView.xib ├── UpdateWindow.xib └── VTInfoWindow.xib ├── Update.h ├── Update.m ├── UpdateWindowController.h ├── UpdateWindowController.m ├── Utilities.h ├── Utilities.m ├── VTButton.h ├── VTButton.m ├── VTInfoWindowController.h ├── VTInfoWindowController.m ├── VirusTotal.h ├── VirusTotal.m ├── changelog.txt ├── en.lproj ├── InfoPlist.strings └── MainMenu.xib ├── images ├── Icon Template.psd ├── arrow.png ├── authorizationIcon.png ├── browserIcon.png ├── bug.png ├── closeWait.png ├── closedIcon.png ├── connectedIcon.png ├── dhsText.png ├── dylibIcon.png ├── flagged.png ├── flaggedBG.png ├── flaggedOver.png ├── flaggedRed.png ├── flaggedRedBG.png ├── flaggedRedOver.png ├── info.png ├── infoBG.png ├── infoOver.png ├── kernelIcon.png ├── launchIcon.png ├── listeningIcon.png ├── lockIcon.png ├── loginIcon.png ├── logo.png ├── logoApple.png ├── logoAppleBG.png ├── logoAppleOver.png ├── refreshIcon.png ├── refreshIconBG.png ├── refreshIconOver.png ├── saveIcon.png ├── saveIconBG.png ├── saveIconOver.png ├── search.png ├── searchBG.png ├── searchOver.png ├── settings.png ├── settingsBG.png ├── settingsOver.png ├── show.png ├── showBG.png ├── showOver.png ├── signed.png ├── signed2.png ├── signedAppleIcon.png ├── spotlightIcon.png ├── startScan.png ├── startScanBG.png ├── startScanOver.png ├── stopScan.png ├── stopScanBG.png ├── stopScanOver.png ├── streamIcon.png ├── teIcon.png ├── teText.ai ├── teText.png ├── unknown.png ├── unsigned.png ├── unsigned2.png ├── unsignedBlack.png ├── virus.png ├── virusTotal.png ├── virusTotalBG.png └── vtLogo.png ├── kkRowCell.h ├── kkRowCell.m ├── main.h ├── main.m ├── patrons.txt ├── remoteTaskService ├── Info.plist ├── main.m ├── remoteTaskService.entitlements ├── remoteTaskService.h └── remoteTaskService.m └── serviceInterface.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## macOS 6 | .DS_Store 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xccheckout 26 | *.xcscmblueprint 27 | 28 | ## Obj-C/Swift specific 29 | *.hmap 30 | *.ipa 31 | *.dSYM.zip 32 | *.dSYM 33 | 34 | -------------------------------------------------------------------------------- /3rdParty/HyperlinkTextField.h: -------------------------------------------------------------------------------- 1 | // 2 | // HyperlinkTextField.h 3 | // NSTextFieldHyperlinks 4 | // 5 | // Created by Toomas Vahter on 25.12.12. 6 | // Copyright (c) 2012 Toomas Vahter. All rights reserved. 7 | // 8 | // This content is released under the MIT License (http://www.opensource.org/licenses/mit-license.php). 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | // THE SOFTWARE. 27 | 28 | #import 29 | 30 | @interface HyperlinkTextField : NSTextField 31 | @end 32 | -------------------------------------------------------------------------------- /3rdParty/HyperlinkTextField.m: -------------------------------------------------------------------------------- 1 | // 2 | // HyperlinkTextField.m 3 | // NSTextFieldHyperlinks 4 | // 5 | // Created by Toomas Vahter on 25.12.12. 6 | // Copyright (c) 2012 Toomas Vahter. All rights reserved. 7 | // 8 | // This content is released under the MIT License (http://www.opensource.org/licenses/mit-license.php). 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in 18 | // all copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | // THE SOFTWARE. 27 | 28 | #import "HyperlinkTextField.h" 29 | 30 | @interface HyperlinkTextField () 31 | @property (nonatomic, readonly) NSArray *hyperlinkInfos; 32 | @property (nonatomic, readonly) NSTextView *textView; 33 | 34 | - (void)_resetHyperlinkCursorRects; 35 | @end 36 | 37 | #define kHyperlinkInfoCharacterRangeKey @"range" 38 | #define kHyperlinkInfoURLKey @"url" 39 | #define kHyperlinkInfoRectKey @"rect" 40 | 41 | @implementation HyperlinkTextField 42 | 43 | - (void)_hyperlinkTextFieldInit 44 | { 45 | [self setEditable:NO]; 46 | [self setSelectable:NO]; 47 | } 48 | 49 | 50 | - (id)initWithFrame:(NSRect)frame 51 | { 52 | if ((self = [super initWithFrame:frame])) 53 | { 54 | [self _hyperlinkTextFieldInit]; 55 | } 56 | 57 | return self; 58 | } 59 | 60 | 61 | - (id)initWithCoder:(NSCoder *)coder 62 | { 63 | if ((self = [super initWithCoder:coder])) 64 | { 65 | [self _hyperlinkTextFieldInit]; 66 | } 67 | 68 | return self; 69 | } 70 | 71 | 72 | - (void)resetCursorRects 73 | { 74 | [super resetCursorRects]; 75 | [self _resetHyperlinkCursorRects]; 76 | } 77 | 78 | 79 | - (void)_resetHyperlinkCursorRects 80 | { 81 | for (NSDictionary *info in self.hyperlinkInfos) 82 | { 83 | [self addCursorRect:[[info objectForKey:kHyperlinkInfoRectKey] rectValue] cursor:[NSCursor pointingHandCursor]]; 84 | } 85 | } 86 | 87 | 88 | #pragma mark - 89 | #pragma mark Accessors 90 | 91 | - (NSArray *)hyperlinkInfos 92 | { 93 | NSMutableArray *hyperlinkInfos = [[NSMutableArray alloc] init]; 94 | NSRange stringRange = NSMakeRange(0, [self.attributedStringValue length]); 95 | __block NSTextView *textView = self.textView; 96 | [self.attributedStringValue enumerateAttribute:NSLinkAttributeName inRange:stringRange options:0 usingBlock:^(id value, NSRange range, BOOL *stop) 97 | { 98 | if (value) 99 | { 100 | NSUInteger rectCount = 0; 101 | NSRectArray rectArray = [textView.layoutManager rectArrayForCharacterRange:range withinSelectedCharacterRange:range inTextContainer:textView.textContainer rectCount:&rectCount]; 102 | for (NSUInteger i = 0; i < rectCount; i++) 103 | { 104 | [hyperlinkInfos addObject:@{kHyperlinkInfoCharacterRangeKey : [NSValue valueWithRange:range], kHyperlinkInfoURLKey : value, kHyperlinkInfoRectKey : [NSValue valueWithRect:rectArray[i]]}]; 105 | } 106 | } 107 | }]; 108 | 109 | return [hyperlinkInfos count] ? hyperlinkInfos : nil; 110 | } 111 | 112 | 113 | - (NSTextView *)textView 114 | { 115 | // Font used for displaying and frame calculations must match 116 | NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedStringValue]; 117 | NSFont *font = [attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL]; 118 | 119 | if (!font) 120 | [attributedString addAttribute:NSFontAttributeName value:self.font range:NSMakeRange(0, [attributedString length])]; 121 | 122 | NSRect textViewFrame = [self.cell titleRectForBounds:self.bounds]; 123 | NSTextView *textView = [[NSTextView alloc] initWithFrame:textViewFrame]; 124 | [textView.textStorage setAttributedString:attributedString]; 125 | 126 | return textView; 127 | } 128 | 129 | 130 | #pragma mark - 131 | #pragma mark Mouse Events 132 | 133 | - (void)mouseUp:(NSEvent *)theEvent 134 | { 135 | NSTextView *textView = self.textView; 136 | NSPoint localPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil]; 137 | NSUInteger index = [textView.layoutManager characterIndexForPoint:localPoint inTextContainer:textView.textContainer fractionOfDistanceBetweenInsertionPoints:NULL]; 138 | 139 | if(index != NSNotFound) 140 | { 141 | for (NSDictionary *info in self.hyperlinkInfos) 142 | { 143 | NSRange range = [[info objectForKey:kHyperlinkInfoCharacterRangeKey] rangeValue]; 144 | if (NSLocationInRange(index, range)) 145 | { 146 | NSURL *url = [info objectForKey:kHyperlinkInfoURLKey]; 147 | [[NSWorkspace sharedWorkspace] openURL:url]; 148 | } 149 | } 150 | } 151 | } 152 | 153 | @end 154 | -------------------------------------------------------------------------------- /3rdParty/OrderedDictionary.h: -------------------------------------------------------------------------------- 1 | // 2 | // OrderedDictionary.h 3 | // OrderedDictionary 4 | // 5 | // Created by Matt Gallagher on 19/12/08. 6 | // Copyright 2008 Matt Gallagher. All rights reserved. 7 | // 8 | // This software is provided 'as-is', without any express or implied 9 | // warranty. In no event will the authors be held liable for any damages 10 | // arising from the use of this software. Permission is granted to anyone to 11 | // use this software for any purpose, including commercial applications, and to 12 | // alter it and redistribute it freely, subject to the following restrictions: 13 | // 14 | // 1. The origin of this software must not be misrepresented; you must not 15 | // claim that you wrote the original software. If you use this software 16 | // in a product, an acknowledgment in the product documentation would be 17 | // appreciated but is not required. 18 | // 2. Altered source versions must be plainly marked as such, and must not be 19 | // misrepresented as being the original software. 20 | // 3. This notice may not be removed or altered from any source 21 | // distribution. 22 | // 23 | 24 | #import 25 | 26 | @interface OrderedDictionary : NSMutableDictionary 27 | { 28 | NSMutableDictionary *dictionary; 29 | NSMutableArray *array; 30 | } 31 | 32 | /* PROPERTIES */ 33 | //@property(nonatomic, retain)NSMutableArray *array; 34 | 35 | /* METHODS */ 36 | - (void)insertObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)anIndex; 37 | - (id)keyAtIndex:(NSUInteger)anIndex; 38 | - (NSUInteger)indexOfKey:(id)aKey; 39 | 40 | //sort 41 | // ->by pid, name, etc 42 | -(void)sort:(NSUInteger)sortBy; 43 | 44 | 45 | 46 | @end 47 | -------------------------------------------------------------------------------- /3rdParty/OrderedDictionary.m: -------------------------------------------------------------------------------- 1 | // 2 | // OrderedDictionary.m 3 | // OrderedDictionary 4 | // 5 | // Created by Matt Gallagher on 19/12/08. 6 | // Copyright 2008 Matt Gallagher. All rights reserved. 7 | // 8 | // This software is provided 'as-is', without any express or implied 9 | // warranty. In no event will the authors be held liable for any damages 10 | // arising from the use of this software. Permission is granted to anyone to 11 | // use this software for any purpose, including commercial applications, and to 12 | // alter it and redistribute it freely, subject to the following restrictions: 13 | // 14 | // 1. The origin of this software must not be misrepresented; you must not 15 | // claim that you wrote the original software. If you use this software 16 | // in a product, an acknowledgment in the product documentation would be 17 | // appreciated but is not required. 18 | // 2. Altered source versions must be plainly marked as such, and must not be 19 | // misrepresented as being the original software. 20 | // 3. This notice may not be removed or altered from any source 21 | // distribution. 22 | // 23 | 24 | #import "Consts.h" 25 | #import "OrderedDictionary.h" 26 | 27 | 28 | NSString *DescriptionForObject(NSObject *object, id locale, NSUInteger indent) 29 | { 30 | NSString *objectString; 31 | if ([object isKindOfClass:[NSString class]]) 32 | { 33 | objectString = (NSString *)object; 34 | } 35 | else if ([object respondsToSelector:@selector(descriptionWithLocale:indent:)]) 36 | { 37 | objectString = [(NSDictionary *)object descriptionWithLocale:locale indent:indent]; 38 | } 39 | else if ([object respondsToSelector:@selector(descriptionWithLocale:)]) 40 | { 41 | objectString = [(NSSet *)object descriptionWithLocale:locale]; 42 | } 43 | else 44 | { 45 | objectString = [object description]; 46 | } 47 | return objectString; 48 | } 49 | 50 | @implementation OrderedDictionary 51 | 52 | -(id)init 53 | { 54 | self = [super init]; 55 | if (self != nil) 56 | { 57 | dictionary = [NSMutableDictionary dictionary]; 58 | array = [NSMutableArray array]; 59 | } 60 | return self; 61 | 62 | } 63 | 64 | -(id)copy 65 | { 66 | return [self mutableCopy]; 67 | } 68 | 69 | -(void)setObject:(id)anObject forKey:(id)aKey 70 | { 71 | if(![dictionary objectForKey:aKey]) 72 | { 73 | // 74 | [array addObject:aKey]; 75 | } 76 | [dictionary setObject:anObject forKey:aKey]; 77 | } 78 | 79 | -(void)removeObjectForKey:(id)aKey 80 | { 81 | [dictionary removeObjectForKey:aKey]; 82 | [array removeObject:aKey]; 83 | } 84 | 85 | - (NSUInteger)count 86 | { 87 | return [dictionary count]; 88 | } 89 | 90 | -(id)objectForKey:(id)aKey 91 | { 92 | return [dictionary objectForKey:aKey]; 93 | } 94 | 95 | -(NSEnumerator *)keyEnumerator 96 | { 97 | return [array objectEnumerator]; 98 | } 99 | 100 | 101 | -(void)insertObject:(id)anObject forKey:(id)aKey atIndex:(NSUInteger)anIndex 102 | { 103 | if([dictionary objectForKey:aKey]) 104 | { 105 | [self removeObjectForKey:aKey]; 106 | } 107 | [array insertObject:aKey atIndex:anIndex]; 108 | [dictionary setObject:anObject forKey:aKey]; 109 | } 110 | 111 | -(id)keyAtIndex:(NSUInteger)anIndex 112 | { 113 | //object 114 | id item = nil; 115 | 116 | if((nil == array) || 117 | (anIndex >= array.count)) 118 | { 119 | //bail 120 | goto bail; 121 | } 122 | 123 | //extract item 124 | item = [array objectAtIndex:anIndex]; 125 | 126 | //bail 127 | bail: 128 | 129 | return item; 130 | } 131 | 132 | //given a key 133 | // ->return its index 134 | -(NSUInteger)indexOfKey:(id)aKey 135 | { 136 | return [array indexOfObject:aKey]; 137 | } 138 | 139 | //sort 140 | // ->by pid, name, etc 141 | -(void)sort:(NSUInteger)sortBy 142 | { 143 | //task sorted by name 144 | NSArray* sortedTasks = nil; 145 | 146 | //sort by pid 147 | if(SORT_BY_PID == sortBy) 148 | { 149 | [array sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { 150 | return [obj1 compare:obj2]; 151 | }]; 152 | } 153 | //sort by name 154 | else if(SORT_BY_NAME == sortBy) 155 | { 156 | //get array of tasks, sorted by binary name 157 | // ->invokes Task class's compare method 158 | sortedTasks = [[dictionary allValues] sortedArrayUsingSelector:@selector(compare:)]; 159 | 160 | //extract sorted pids into array 161 | array = [[sortedTasks valueForKey:@"pid"] mutableCopy]; 162 | } 163 | 164 | return; 165 | } 166 | 167 | @end 168 | -------------------------------------------------------------------------------- /AboutWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // file: AboutWindowController.h 3 | // project: lulu (config) 4 | // description: about window display/controller (header) 5 | // 6 | // created by Patrick Wardle 7 | // copyright (c) 2018 Objective-See. All rights reserved. 8 | // 9 | 10 | @import Cocoa; 11 | 12 | @interface AboutWindowController : NSWindowController 13 | { 14 | 15 | } 16 | 17 | /* PROPERTIES */ 18 | 19 | //version label/string 20 | @property (weak, atomic) IBOutlet NSTextField *versionLabel; 21 | 22 | //patrons 23 | @property (unsafe_unretained, atomic) IBOutlet NSTextView *patrons; 24 | 25 | //'support us' button 26 | @property (weak, atomic) IBOutlet NSButton *supportUs; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /AboutWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // file: AboutWindowController.m 3 | // project: lulu (config) 4 | // description: about window display/controller 5 | // 6 | // created by Patrick Wardle 7 | // copyright (c) 2018 Objective-See. All rights reserved. 8 | // 9 | 10 | #import "Consts.h" 11 | #import "Utilities.h" 12 | #import "AboutWindowController.h" 13 | 14 | @implementation AboutWindowController 15 | 16 | @synthesize patrons; 17 | @synthesize supportUs; 18 | @synthesize versionLabel; 19 | 20 | //automatically called when nib is loaded 21 | // center window 22 | -(void)awakeFromNib 23 | { 24 | //center 25 | [self.window center]; 26 | } 27 | 28 | //automatically invoked when window is loaded 29 | // set to white 30 | -(void)windowDidLoad 31 | { 32 | //super 33 | [super windowDidLoad]; 34 | 35 | //not in dark mode? 36 | // make window white 37 | if(YES != isDarkMode()) 38 | { 39 | //make white 40 | self.window.backgroundColor = NSColor.whiteColor; 41 | } 42 | 43 | //set version sting 44 | self.versionLabel.stringValue = [NSString stringWithFormat:@"Version: %@", getAppVersion()]; 45 | 46 | //load patrons 47 | self.patrons.string = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"patrons" ofType:@"txt"] encoding:NSUTF8StringEncoding error:NULL]; 48 | 49 | //make 'support us' default 50 | [self.supportUs setKeyEquivalent:@"\r"]; 51 | 52 | //make first responder 53 | // calling this without a timeout sometimes fails :/ 54 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (100 * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{ 55 | 56 | //and make it first responder 57 | [self.window makeFirstResponder:self.supportUs]; 58 | 59 | }); 60 | 61 | return; 62 | } 63 | 64 | //automatically invoked when window is closing 65 | // make ourselves unmodal 66 | -(void)windowWillClose:(NSNotification *)notification 67 | { 68 | #pragma unused(notification) 69 | 70 | //make un-modal 71 | [[NSApplication sharedApplication] stopModal]; 72 | 73 | return; 74 | } 75 | 76 | //automatically invoked when user clicks any of the buttons 77 | // load patreon or products webpage in user's default browser 78 | -(IBAction)buttonHandler:(id)sender 79 | { 80 | //support us button 81 | if(((NSButton*)sender).tag == BUTTON_SUPPORT_US) 82 | { 83 | //open URL 84 | // invokes user's default browser 85 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:PATREON_URL]]; 86 | } 87 | 88 | //more info button 89 | else if(((NSButton*)sender).tag == BUTTON_MORE_INFO) 90 | { 91 | //open URL 92 | // invokes user's default browser 93 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:PRODUCT_URL]]; 94 | } 95 | 96 | return; 97 | } 98 | @end 99 | -------------------------------------------------------------------------------- /AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "Task.h" 12 | #import "Filter.h" 13 | #import "Binary.h" 14 | #import "ItemBase.h" 15 | #import "VirusTotal.h" 16 | #import "TaskEnumerator.h" 17 | #import "CustomTextField.h" 18 | #import "TaskTableController.h" 19 | #import "AboutWindowController.h" 20 | #import "PrefsWindowController.h" 21 | #import "SearchWindowController.h" 22 | #import "UpdateWindowController.h" 23 | #import "ResultsWindowController.h" 24 | #import "FlaggedItemWindowController.h" 25 | #import "RequestRootWindowController.h" 26 | 27 | /* GLOBALS */ 28 | 29 | //shared enumerator 30 | extern TaskEnumerator* taskEnumerator; 31 | 32 | //shared virustotal object 33 | extern VirusTotal* virusTotal; 34 | 35 | //network connected flag 36 | extern BOOL isConnected; 37 | 38 | @interface AppDelegate : NSObject 39 | { 40 | 41 | } 42 | 43 | //friends 44 | @property (weak) IBOutlet NSWindow *friends; 45 | 46 | //'filter task' search box 47 | // ->top pane 48 | @property (weak) IBOutlet NSSearchField *filterTasksBox; 49 | 50 | //(current) bottom view controller 51 | @property(nonatomic, retain)TaskTableController *bottomViewController; 52 | 53 | //top view/pane 54 | @property (weak) IBOutlet NSView *topPane; 55 | 56 | //task table controller object 57 | @property (nonatomic, retain)TaskTableController *taskTableController; 58 | 59 | //current task view format 60 | // ->flat or tree 61 | @property NSUInteger taskViewFormat; 62 | 63 | //drop-down view selector 64 | @property (weak) IBOutlet NSPopUpButton *viewSelector; 65 | 66 | //segmented button for button pane 67 | // ->select to view dylib, files, network, etc 68 | @property (weak) IBOutlet NSSegmentedControl *bottomPaneBtn; 69 | 70 | //action when segmented button is clicked 71 | -(IBAction)selectBottomPaneContent:(id)sender; 72 | 73 | //bottom pane view 74 | @property (weak) IBOutlet NSView *bottomPane; 75 | 76 | //'filter items' search box (bottom pane) 77 | @property (weak) IBOutlet NSSearchField *filterItemsBox; 78 | 79 | //(main) window 80 | @property (assign) IBOutlet NSWindow *window; 81 | 82 | //logo button 83 | @property (weak) IBOutlet NSButton *logoButton; 84 | 85 | //spinner 86 | @property (weak) IBOutlet NSProgressIndicator *progressIndicator; 87 | 88 | //filter object 89 | @property(nonatomic, retain)Filter* filterObj; 90 | 91 | //array for all virus total threads 92 | @property(nonatomic, retain)NSMutableArray* vtThreads; 93 | 94 | //request root window controller 95 | @property(nonatomic, retain)RequestRootWindowController* requestRootWindowController; 96 | 97 | //preferences window controller 98 | @property(nonatomic, retain)PrefsWindowController* prefsWindowController; 99 | 100 | //about window controller 101 | @property(nonatomic, retain)AboutWindowController* aboutWindowController; 102 | 103 | //search window controller 104 | @property(nonatomic, retain)SearchWindowController* searchWindowController; 105 | 106 | //results window controller 107 | @property(nonatomic, retain)ResultsWindowController* resultsWindowController; 108 | 109 | //flagged items window controller 110 | @property(nonatomic, retain)FlaggedItems* flagItemsWindowController; 111 | 112 | //currently selected task 113 | @property(nonatomic, retain)Task* currentTask; 114 | 115 | //activity indicator for bottom pane 116 | @property (weak) IBOutlet NSProgressIndicator *bottomPaneSpinner; 117 | 118 | //'no items' found label for bottom pane 119 | @property (weak) IBOutlet NSTextField *noItemsLabel; 120 | 121 | //refresh button 122 | @property (weak) IBOutlet NSButton *refreshButton; 123 | 124 | //search button 125 | @property (weak) IBOutlet NSButton *searchButton; 126 | 127 | //save button 128 | @property (weak) IBOutlet NSButton *saveButton; 129 | 130 | //flagged items button 131 | @property (weak) IBOutlet NSButton *flaggedButton; 132 | 133 | //top constraint 134 | @property(nonatomic, retain)NSLayoutConstraint* topConstraint; 135 | 136 | //bottom constraint 137 | @property(nonatomic, retain)NSLayoutConstraint* bottomConstraint; 138 | 139 | //top constraint 140 | @property(nonatomic, retain)NSLayoutConstraint* leadingConstraint; 141 | 142 | //top constraint 143 | @property(nonatomic, retain)NSLayoutConstraint* trailingConstraint; 144 | 145 | //flag for filter field (autocomplete) 146 | @property BOOL completePosting; 147 | 148 | //flag for filter field (autocomplete) 149 | @property BOOL commandHandling; 150 | 151 | //custom search field for tasks 152 | @property(nonatomic, retain)CustomTextField* customTasksFilter; 153 | 154 | //custom search field for items 155 | @property(nonatomic, retain)CustomTextField* customItemsFilter; 156 | 157 | //overlay view for filter 158 | @property (weak) IBOutlet NSView *filteringOverlay; 159 | 160 | //activity indicator for filtering 161 | @property (weak) IBOutlet NSProgressIndicator *filteringIndicator; 162 | 163 | //message for filtering 164 | @property (weak) IBOutlet NSTextField *filteringMessage; 165 | 166 | //update window controller 167 | @property(nonatomic, retain)UpdateWindowController* updateWindowController; 168 | 169 | /* METHODS */ 170 | 171 | //complete a few inits 172 | // ->then invoke helper method to start enum'ing task (in bg thread) 173 | -(void)go; 174 | 175 | //switch between flat/tree view 176 | -(IBAction)switchView:(id)sender; 177 | 178 | //init tracking areas for buttons 179 | // ->provide mouse over effects 180 | -(void)initTrackingAreas; 181 | 182 | //action for 'refresh' button 183 | // ->query OS to refresh/reload all tasks 184 | -(IBAction)refreshTasks:(id)sender; 185 | 186 | //button handler for logo 187 | -(IBAction)logoButtonHandler:(id)sender; 188 | 189 | //action for 'about' in menu/logo in UI 190 | -(IBAction)about:(id)sender; 191 | 192 | //reload (to re-draw) a specific row in table 193 | -(void)reloadRow:(id)item; 194 | 195 | //reload task table 196 | -(void)reloadTaskTable; 197 | 198 | //smartly, reload bottom pane 199 | // ->checks if task & item type (e.g. files) are both selected 200 | -(void)reloadBottomPane:(Task*)task itemView:(NSUInteger)itemView; 201 | 202 | //begin task enumeration 203 | -(void)exploreTasks; 204 | 205 | //sort tasks 206 | // ->either name (flat view) or pid (tree view) 207 | -(void)sortTasksForView:(OrderedDictionary*)tasks; 208 | 209 | //VT callback to reload a binary 210 | -(void)reloadBinary:(Binary*)binary; 211 | 212 | //save button handler 213 | -(IBAction)saveResults:(id)sender; 214 | 215 | //search button handler 216 | -(IBAction)search:(id)sender; 217 | 218 | //constrain subview to parent view 219 | -(void)constrainView:(NSView*)containerView subView:(NSView*)subView; 220 | 221 | //display (in separate popup) all flagged items 222 | -(IBAction)showFlaggedItems:(id)sender; 223 | 224 | //callback for custom search fields 225 | // ->handle auto-complete filterings 226 | -(void)filterAutoComplete:(NSTextView*)textField; 227 | 228 | //show the filter overlay, etc 229 | -(void)prepUIForFiltering:(NSUInteger)pane; 230 | 231 | //hide overlay, etc 232 | -(void)unprepUIForFiltering; 233 | 234 | //code to complete filtering/search 235 | // ->reload table/scroll to top etc 236 | -(void)finalizeFiltration:(NSUInteger)pane; 237 | 238 | @end 239 | -------------------------------------------------------------------------------- /Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "icon_16x16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "icon_16x16@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "icon_32x32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "icon_32x32@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "icon_128x128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "icon_128x128@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "icon_256x256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "icon_256x256@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "icon_512x512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "icon_512x512@2x.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /Assets.xcassets/AppIcon.appiconset/icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/AppIcon.appiconset/icon_128x128.png -------------------------------------------------------------------------------- /Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png -------------------------------------------------------------------------------- /Assets.xcassets/AppIcon.appiconset/icon_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/AppIcon.appiconset/icon_16x16.png -------------------------------------------------------------------------------- /Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png -------------------------------------------------------------------------------- /Assets.xcassets/AppIcon.appiconset/icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/AppIcon.appiconset/icon_256x256.png -------------------------------------------------------------------------------- /Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png -------------------------------------------------------------------------------- /Assets.xcassets/AppIcon.appiconset/icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/AppIcon.appiconset/icon_32x32.png -------------------------------------------------------------------------------- /Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png -------------------------------------------------------------------------------- /Assets.xcassets/AppIcon.appiconset/icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/AppIcon.appiconset/icon_512x512.png -------------------------------------------------------------------------------- /Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png -------------------------------------------------------------------------------- /Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Assets.xcassets/FriendsHuntress.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "darkMode.png", 5 | "idiom" : "mac" 6 | }, 7 | { 8 | "appearances" : [ 9 | { 10 | "appearance" : "luminosity", 11 | "value" : "light" 12 | } 13 | ], 14 | "filename" : "lightMode.png", 15 | "idiom" : "mac" 16 | }, 17 | { 18 | "appearances" : [ 19 | { 20 | "appearance" : "luminosity", 21 | "value" : "dark" 22 | } 23 | ], 24 | "filename" : "darkMode.png", 25 | "idiom" : "mac" 26 | } 27 | ], 28 | "info" : { 29 | "author" : "xcode", 30 | "version" : 1 31 | }, 32 | "properties" : { 33 | "preserves-vector-representation" : true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Assets.xcassets/FriendsHuntress.imageset/darkMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/FriendsHuntress.imageset/darkMode.png -------------------------------------------------------------------------------- /Assets.xcassets/FriendsHuntress.imageset/lightMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/FriendsHuntress.imageset/lightMode.png -------------------------------------------------------------------------------- /Assets.xcassets/FriendsJamf.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "filename" : "darkMode.png" 6 | }, 7 | { 8 | "idiom" : "mac", 9 | "filename" : "lightMode.png", 10 | "appearances" : [ 11 | { 12 | "appearance" : "luminosity", 13 | "value" : "light" 14 | } 15 | ] 16 | }, 17 | { 18 | "idiom" : "mac", 19 | "filename" : "darkMode.png", 20 | "appearances" : [ 21 | { 22 | "appearance" : "luminosity", 23 | "value" : "dark" 24 | } 25 | ] 26 | } 27 | ], 28 | "info" : { 29 | "version" : 1, 30 | "author" : "xcode" 31 | }, 32 | "properties" : { 33 | "preserves-vector-representation" : true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Assets.xcassets/FriendsJamf.imageset/darkMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/FriendsJamf.imageset/darkMode.png -------------------------------------------------------------------------------- /Assets.xcassets/FriendsJamf.imageset/lightMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/FriendsJamf.imageset/lightMode.png -------------------------------------------------------------------------------- /Assets.xcassets/FriendsKandji.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "filename" : "darkMode.png" 6 | }, 7 | { 8 | "idiom" : "mac", 9 | "filename" : "lightMode.png", 10 | "appearances" : [ 11 | { 12 | "appearance" : "luminosity", 13 | "value" : "light" 14 | } 15 | ] 16 | }, 17 | { 18 | "idiom" : "mac", 19 | "filename" : "darkMode.png", 20 | "appearances" : [ 21 | { 22 | "appearance" : "luminosity", 23 | "value" : "dark" 24 | } 25 | ] 26 | } 27 | ], 28 | "info" : { 29 | "version" : 1, 30 | "author" : "xcode" 31 | }, 32 | "properties" : { 33 | "preserves-vector-representation" : true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Assets.xcassets/FriendsKandji.imageset/darkMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/FriendsKandji.imageset/darkMode.png -------------------------------------------------------------------------------- /Assets.xcassets/FriendsKandji.imageset/lightMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/FriendsKandji.imageset/lightMode.png -------------------------------------------------------------------------------- /Assets.xcassets/FriendsMacPaw.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "filename" : "darkMode.png" 6 | }, 7 | { 8 | "idiom" : "mac", 9 | "filename" : "lightMode.png", 10 | "appearances" : [ 11 | { 12 | "appearance" : "luminosity", 13 | "value" : "light" 14 | } 15 | ] 16 | }, 17 | { 18 | "idiom" : "mac", 19 | "filename" : "darkMode.png", 20 | "appearances" : [ 21 | { 22 | "appearance" : "luminosity", 23 | "value" : "dark" 24 | } 25 | ] 26 | } 27 | ], 28 | "info" : { 29 | "version" : 1, 30 | "author" : "xcode" 31 | }, 32 | "properties" : { 33 | "preserves-vector-representation" : true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Assets.xcassets/FriendsMacPaw.imageset/darkMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/FriendsMacPaw.imageset/darkMode.png -------------------------------------------------------------------------------- /Assets.xcassets/FriendsMacPaw.imageset/lightMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/FriendsMacPaw.imageset/lightMode.png -------------------------------------------------------------------------------- /Assets.xcassets/FriendsPANW.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "darkMode.png", 5 | "idiom" : "mac" 6 | }, 7 | { 8 | "appearances" : [ 9 | { 10 | "appearance" : "luminosity", 11 | "value" : "light" 12 | } 13 | ], 14 | "filename" : "lightMode.png", 15 | "idiom" : "mac" 16 | }, 17 | { 18 | "appearances" : [ 19 | { 20 | "appearance" : "luminosity", 21 | "value" : "dark" 22 | } 23 | ], 24 | "filename" : "darkMode.png", 25 | "idiom" : "mac" 26 | } 27 | ], 28 | "info" : { 29 | "author" : "xcode", 30 | "version" : 1 31 | }, 32 | "properties" : { 33 | "preserves-vector-representation" : true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Assets.xcassets/FriendsPANW.imageset/darkMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/FriendsPANW.imageset/darkMode.png -------------------------------------------------------------------------------- /Assets.xcassets/FriendsPANW.imageset/lightMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/FriendsPANW.imageset/lightMode.png -------------------------------------------------------------------------------- /Assets.xcassets/FriendsiVerify.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "filename" : "iVerify.png" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | }, 12 | "properties" : { 13 | "preserves-vector-representation" : true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Assets.xcassets/FriendsiVerify.imageset/iVerify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/Assets.xcassets/FriendsiVerify.imageset/iVerify.png -------------------------------------------------------------------------------- /CustomTextField.h: -------------------------------------------------------------------------------- 1 | // 2 | // CustomTextField.h 3 | // SearchField 4 | // 5 | // Created by Patrick Wardle on 8/27/15. 6 | // 7 | // 8 | 9 | #import 10 | 11 | //NSTextView subclass 12 | // 1) fixes issue with non-alphanumeric characters in keyword matches 13 | // 2) triggers action when user hits enter (1x) 14 | @interface CustomTextField : NSTextView 15 | { 16 | 17 | } 18 | 19 | //'owner' 20 | @property(nonatomic, retain)id owner; 21 | 22 | //last movement 23 | // ->helps avoid 2x 'Enter' 24 | @property NSInteger lastMovement; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /CustomTextField.m: -------------------------------------------------------------------------------- 1 | // 2 | // CustomTextField.m 3 | // SearchField 4 | // 5 | // Created by Patrick Wardle on 8/27/15. 6 | // 7 | // 8 | 9 | #import "CustomTextField.h" 10 | #import "AppDelegate.h" 11 | 12 | @implementation CustomTextField 13 | 14 | @synthesize owner; 15 | @synthesize lastMovement; 16 | 17 | //subclass override 18 | // ->see: http://stackoverflow.com/questions/5163646/how-to-make-nssearchfield-send-action-upon-autocompletion/5360535#5360535 19 | -(void)insertCompletion:(NSString *)word forPartialWordRange:(NSRange)charRange movement:(NSInteger)movement isFinal:(BOOL)flag 20 | { 21 | //suppress completion if user types a space 22 | if(movement == NSRightTextMovement) 23 | { 24 | //bail 25 | goto bail; 26 | } 27 | 28 | //ignore if 2x 'enter' 29 | if( (movement == NSReturnTextMovement) && 30 | (self.lastMovement == NSReturnTextMovement) ) 31 | { 32 | //bail 33 | goto bail; 34 | } 35 | 36 | //update 37 | self.lastMovement = movement; 38 | 39 | //show full replacements 40 | if(0 != charRange.location) 41 | { 42 | //update length 43 | charRange.length += charRange.location; 44 | 45 | //reset location 46 | charRange.location = 0; 47 | } 48 | 49 | //insert completion 50 | // ->will use updated char range! 51 | [super insertCompletion:word forPartialWordRange:charRange movement:movement isFinal:flag]; 52 | 53 | //on enter 54 | // ->call up into app delegate to process (filter) 55 | if(movement == NSReturnTextMovement) 56 | { 57 | //call up into owner to process 58 | [owner filterAutoComplete:self]; 59 | } 60 | 61 | //bail 62 | bail: 63 | 64 | return; 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /Filter.h: -------------------------------------------------------------------------------- 1 | // 2 | // Filter.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 2/21/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | #import "File.h" 9 | #import "Binary.h" 10 | 11 | #import 12 | 13 | @interface Filter : NSObject 14 | { 15 | 16 | } 17 | 18 | /* METHODS */ 19 | 20 | //determine if search string is #keyword 21 | -(BOOL)isKeyword:(NSString*)searchString; 22 | 23 | //check if a binary fulfills a keyword 24 | -(BOOL)binaryFulfillsKeyword:(NSString*)keyword binary:(Binary*)binary; 25 | 26 | //keyword filter '#apple' 27 | // ->determine if binary is signed by apple 28 | -(BOOL)isApple:(Binary*)item; 29 | 30 | //keyword filter '#signed' (and indirectly #unsigned) 31 | // ->determine if binary is signed 32 | -(BOOL)isSigned:(Binary*)item; 33 | 34 | //keyword filter '#flagged' 35 | // ->determine if binary is flagged by VT 36 | -(BOOL)isFlagged:(Binary*)item; 37 | 38 | //keyword filter '#encrypted' 39 | // ->determine if binary is encrypted 40 | -(BOOL)isEncrypted:(Binary*)item; 41 | 42 | //keyword filter '#packed' 43 | // ->determine if binary is packed 44 | -(BOOL)isPacked:(Binary*)item; 45 | 46 | //keyword filter '#notfound' 47 | -(BOOL)notFound:(Binary*)item; 48 | 49 | //filter all for global search 50 | // ->tasks, dylibs, files, & connections 51 | -(void)filterAll:(NSString*)filterText items:(NSMutableDictionary*)items results:(NSMutableArray*)results; 52 | 53 | //filter tasks 54 | -(void)filterTasks:(NSString*)filterText items:(NSMutableDictionary*)items results:(NSMutableArray*)results pane:(NSUInteger)pane; 55 | 56 | //filter dylibs and files 57 | -(void)filterFiles:(NSString*)filterText items:(NSMutableArray*)items results:(NSMutableArray*)results pane:(NSUInteger)pane; 58 | 59 | //filter network connections 60 | -(void)filterConnections:(NSString*)filterText items:(NSMutableArray*)items results:(NSMutableArray*)results; 61 | 62 | /* PROPERTIES */ 63 | 64 | //binary filter keywords 65 | @property(nonatomic, retain)NSMutableArray* binaryFilters; 66 | 67 | //file filter keywords 68 | @property(nonatomic, retain)NSMutableArray* fileFilters; 69 | 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /FlaggedItemWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // FlaggedItems.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 8/14/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "InfoWindowController.h" 11 | 12 | @interface FlaggedItems : NSWindowController 13 | 14 | /* PROPERTIES */ 15 | 16 | //flag for first time init's 17 | @property BOOL didInit; 18 | 19 | //table 20 | @property (weak) IBOutlet NSTableView *flaggedItemTable; 21 | 22 | //table items 23 | @property(nonatomic, retain)NSMutableArray* flaggedItems; 24 | 25 | //vt window controller 26 | @property (nonatomic, retain)VTInfoWindowController* vtWindowController; 27 | 28 | //info window controller 29 | @property(retain, nonatomic)InfoWindowController* infoWindowController; 30 | 31 | /* METHODS */ 32 | 33 | //init/prepare 34 | // ->make sure everything is cleanly init'd before displaying 35 | -(void)prepare; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /FlaggedItemWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // FlaggedItems.m 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 8/14/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import "KKRow.h" 10 | #import "ItemView.h" 11 | #import "Utilities.h" 12 | #import "AppDelegate.h" 13 | #import "FlaggedItemWindowController.h" 14 | 15 | @interface FlaggedItems () 16 | 17 | @end 18 | 19 | @implementation FlaggedItems 20 | 21 | @synthesize didInit; 22 | @synthesize flaggedItems; 23 | @synthesize flaggedItemTable; 24 | @synthesize vtWindowController; 25 | @synthesize infoWindowController; 26 | 27 | //automatically called when nib is loaded 28 | // ->first time (when outlets aren't nil), init UI 29 | -(void)awakeFromNib 30 | { 31 | //single time init 32 | if(YES != self.didInit) 33 | { 34 | //init UI 35 | [self initUI]; 36 | 37 | //set flag 38 | self.didInit = YES; 39 | } 40 | 41 | return; 42 | } 43 | 44 | //init/prepare 45 | // ->make sure everything is cleanly init'd before displaying 46 | -(void)prepare 47 | { 48 | //array of flagged tasks 49 | NSMutableArray* flaggedTasks = nil; 50 | 51 | //init array for flagged items 52 | flaggedItems = [NSMutableArray array]; 53 | 54 | //first time outlets are nil 55 | // ->thus 'initUI' method called in 'awakeFromNib' 56 | if(nil != self.window) 57 | { 58 | //can init UI 59 | // ->center window, etc 60 | [self initUI]; 61 | 62 | //set flag 63 | self.didInit = YES; 64 | } 65 | 66 | //populate flagged items array 67 | for(Binary* flaggedItem in taskEnumerator.flaggedItems) 68 | { 69 | //when binary is task binary 70 | // ->find/save all tasks instances 71 | if(YES == flaggedItem.isTaskBinary) 72 | { 73 | //get all tasks instances 74 | flaggedTasks = [taskEnumerator tasksForBinary:flaggedItem]; 75 | 76 | //sync to save 77 | @synchronized(self.flaggedItems) 78 | { 79 | //save all tasks 80 | [self.flaggedItems addObjectsFromArray:flaggedTasks]; 81 | } 82 | } 83 | //when binary is dylib 84 | // ->just add, since will be processed as single item 85 | else 86 | { 87 | //sync to save 88 | @synchronized(self.flaggedItems) 89 | { 90 | //add 91 | [self.flaggedItems addObject:flaggedItem]; 92 | } 93 | } 94 | } 95 | 96 | //always reload 97 | [self.flaggedItemTable reloadData]; 98 | 99 | return; 100 | } 101 | 102 | 103 | //init the UI 104 | // ->each time window is shown, reset, show spinner if needed, etc 105 | -(void)initUI 106 | { 107 | //center 108 | [self.window center]; 109 | 110 | //table reload 111 | // ->make sure all is reset 112 | [self.flaggedItemTable reloadData]; 113 | 114 | return; 115 | } 116 | 117 | 118 | //table delegate 119 | // ->return number of rows, which is just number of items in the currently selected plugin 120 | -(NSInteger)numberOfRowsInTableView:(NSTableView *)tableView 121 | { 122 | return self.flaggedItems.count; 123 | } 124 | 125 | //table delegate method 126 | // ->return cell for row 127 | -(NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row 128 | { 129 | //row view 130 | NSView* rowView = nil; 131 | 132 | //sanity check 133 | // ->make sure there is table item for row 134 | if(row >= self.flaggedItems.count) 135 | { 136 | //bail 137 | goto bail; 138 | } 139 | 140 | //create the view 141 | // ->inits row w/ all required info 142 | rowView = createItemView(tableView, self, [self.flaggedItems objectAtIndex:row]); 143 | 144 | //bail 145 | bail: 146 | 147 | return rowView; 148 | } 149 | 150 | //automatically invoked 151 | // ->create custom (sub-classed) NSTableRowView 152 | -(NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row 153 | { 154 | //row view 155 | KKRow* rowView = nil; 156 | 157 | //row ID 158 | static NSString* const kRowIdentifier = @"TableRowView"; 159 | 160 | //try grab existing row view 161 | rowView = [tableView makeViewWithIdentifier:kRowIdentifier owner:self]; 162 | 163 | //make new if needed 164 | if(nil == rowView) 165 | { 166 | //create new 167 | // ->size doesn't matter 168 | rowView = [[KKRow alloc] initWithFrame:NSZeroRect]; 169 | 170 | //set row ID 171 | rowView.identifier = kRowIdentifier; 172 | } 173 | 174 | return rowView; 175 | } 176 | 177 | //automatically invoked when mouse entered 178 | // ->highlight button 179 | -(void)mouseEntered:(NSEvent*)theEvent 180 | { 181 | //mouse entered 182 | // ->highlight (visual) state 183 | buttonAppearance(self.flaggedItemTable, theEvent, NO); 184 | 185 | return; 186 | } 187 | 188 | //automatically invoked when mouse exits 189 | // ->unhighlight/reset button 190 | -(void)mouseExited:(NSEvent*)theEvent 191 | { 192 | //mouse exited 193 | // ->so reset button to original (visual) state 194 | buttonAppearance(self.flaggedItemTable, theEvent, YES); 195 | 196 | return; 197 | } 198 | 199 | //invoked when the user clicks 'virus total' icon 200 | // ->launch browser and browse to virus total's page 201 | -(void)showVTInfo:(id)sender 202 | { 203 | //item 204 | id item = nil; 205 | 206 | //binary 207 | Binary* binary = nil; 208 | 209 | //row 210 | NSInteger itemRow = 0; 211 | 212 | //grab sender's row 213 | itemRow = [self.flaggedItemTable rowForView:sender]; 214 | 215 | //sanity check(s) 216 | // ->make sure row is decent 217 | if( (-1 == itemRow) || 218 | (itemRow >= self.flaggedItems.count) ) 219 | { 220 | //bail 221 | goto bail; 222 | } 223 | 224 | //extract item for row 225 | item = self.flaggedItems[itemRow]; 226 | 227 | //tasks 228 | // ->grab binary 229 | if(YES == [item isKindOfClass:[Task class]]) 230 | { 231 | //get binary 232 | binary = ((Task*)item).binary; 233 | } 234 | //binaries 235 | // ->can use as is 236 | else if(YES == [item isKindOfClass:[Binary class]]) 237 | { 238 | //set 239 | binary = item; 240 | } 241 | 242 | //sanity check 243 | if(nil == binary) 244 | { 245 | //bail 246 | goto bail; 247 | } 248 | 249 | //alloc/init info window 250 | vtWindowController = [[VTInfoWindowController alloc] initWithItem:binary]; 251 | 252 | //show it 253 | [self.vtWindowController.windowController showWindow:self]; 254 | 255 | 256 | //bail 257 | bail: 258 | 259 | return; 260 | } 261 | 262 | //automatically invoked when user clicks the 'info' icon 263 | // ->create/configure/display info window for task/dylib/file/etc 264 | -(IBAction)showInfo:(id)sender 265 | { 266 | //item 267 | // ->task, dylib, file, etc 268 | id item = nil; 269 | 270 | //row 271 | NSInteger itemRow = 0; 272 | 273 | //grab sender's row 274 | itemRow = [self.flaggedItemTable rowForView:sender]; 275 | 276 | //sanity check(s) 277 | // ->make sure row is decent 278 | if( (-1 == itemRow) || 279 | (itemRow >= self.flaggedItems.count) ) 280 | { 281 | //bail 282 | goto bail; 283 | } 284 | 285 | //grab item 286 | item = self.flaggedItems[itemRow]; 287 | 288 | //alloc/init info window 289 | infoWindowController = [[InfoWindowController alloc] initWithItem:item]; 290 | 291 | //show it 292 | [self.infoWindowController.windowController showWindow:self]; 293 | 294 | //bail 295 | bail: 296 | 297 | return; 298 | } 299 | 300 | 301 | //automatically invoked when user clicks the 'show in finder' icon 302 | // ->open Finder to show item 303 | -(IBAction)showInFinder:(id)sender 304 | { 305 | //item 306 | // ->task, dylib, or file 307 | id item = nil; 308 | 309 | //path to item 310 | NSString* path = nil; 311 | 312 | //row 313 | NSInteger itemRow = 0; 314 | 315 | //file open error alert 316 | NSAlert* errorAlert = nil; 317 | 318 | //grab sender's row 319 | itemRow = [self.flaggedItemTable rowForView:sender]; 320 | 321 | //sanity check(s) 322 | // ->make sure row is decent 323 | if( (-1 == itemRow) || 324 | (itemRow >= self.flaggedItems.count) ) 325 | { 326 | //bail 327 | goto bail; 328 | } 329 | 330 | //extract item for row 331 | item = self.flaggedItems[itemRow]; 332 | 333 | //tasks 334 | // ->grab path from binary 335 | if(YES == [item isKindOfClass:[Task class]]) 336 | { 337 | //get path 338 | path = ((Task*)item).binary.path; 339 | } 340 | //dylib 341 | // ->use as is, to extract path 342 | else if(YES == [item isKindOfClass:[Binary class]]) 343 | { 344 | //get path 345 | path = [item path]; 346 | } 347 | 348 | //sanity check 349 | if(nil == path) 350 | { 351 | //bail 352 | goto bail; 353 | } 354 | 355 | //open item in Finder 356 | // ->error alert shown if file open fails 357 | if(YES != [[NSWorkspace sharedWorkspace] selectFile:path inFileViewerRootedAtPath:@""]) 358 | { 359 | //alloc/init alert 360 | errorAlert = [NSAlert alertWithMessageText:[NSString stringWithFormat:@"ERROR:\nfailed to open %@", path] defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"errno value: %d", errno]; 361 | 362 | //show it 363 | [errorAlert runModal]; 364 | } 365 | 366 | //bail 367 | bail: 368 | 369 | return; 370 | } 371 | 372 | @end 373 | -------------------------------------------------------------------------------- /InfoWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // InfoWindowController.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 2/21/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class ItemBase; 12 | 13 | @interface InfoWindowController : NSWindowController 14 | { 15 | 16 | } 17 | 18 | //properties in window 19 | // ->attributes about the item 20 | @property(weak)IBOutlet NSImageView *icon; 21 | @property(weak)IBOutlet NSTextField *name; 22 | @property(weak)IBOutlet NSTextField *path; 23 | @property(weak)IBOutlet NSTextField *date; 24 | 25 | 26 | //task-specific outlets 27 | @property (weak) IBOutlet NSTextField *arguments; 28 | 29 | 30 | 31 | //file window specific outlets 32 | @property(weak)IBOutlet NSTextField *hashes; 33 | @property(weak)IBOutlet NSTextField *size; 34 | @property(weak)IBOutlet NSTextField *sign; 35 | 36 | //file window specific outlets 37 | @property (weak) IBOutlet NSTextField *type; 38 | 39 | //network window specific outlets 40 | @property (weak) IBOutlet NSTextField *connection; 41 | @property (weak) IBOutlet NSTextField *protocol; 42 | @property (weak) IBOutlet NSTextField *family; 43 | @property (weak) IBOutlet NSTextField *state; 44 | 45 | 46 | //window controller 47 | @property(nonatomic, strong)InfoWindowController *windowController; 48 | 49 | //item 50 | @property(nonatomic, retain)ItemBase* itemObj; 51 | 52 | /* METHODS */ 53 | 54 | //init method 55 | // ->save item and load nib 56 | -(id)initWithItem:(id)selectedItem; 57 | 58 | //configure window 59 | // ->add item's attributes (name, path, etc.) 60 | -(void)configure; 61 | 62 | //check if something is nil 63 | // ->if so, return the default 64 | -(NSString*)valueForStringItem:(NSString*)item default:(NSString*)defaultValue; 65 | 66 | //close button handler 67 | -(IBAction)closeWindow:(id)sender; 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /ItemView.h: -------------------------------------------------------------------------------- 1 | // 2 | // ItemView.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 5/23/15. 6 | // Copyright (c) 2015 Objective-See, LLC. All rights reserved. 7 | // 8 | 9 | #import "File.h" 10 | #import "Task.h" 11 | #import "Binary.h" 12 | #import "Connection.h" 13 | 14 | #import 15 | 16 | 17 | /* METHODS */ 18 | 19 | //create customize item view 20 | NSTableCellView* createItemView(NSTableView* tableView, id owner, id item); 21 | 22 | //create & customize flagged item view 23 | NSTableCellView* createFlaggedItemView(NSTableView* tableView, id owner, id item); 24 | 25 | //create & customize global dylib/file view 26 | NSTableCellView* createLoadedItemView(NSTableView* tableView, id owner, id item); 27 | 28 | 29 | //create & customize task view 30 | NSTableCellView* createTaskView(NSTableView* tableView, id owner, id item); 31 | 32 | //create & customize dylib view 33 | NSTableCellView* createDylibView(NSTableView* tableView, id owner, Binary* dylib); 34 | 35 | //create & customize file view 36 | NSTableCellView* createFileView(NSTableView* tableView, id owner, File* file); 37 | 38 | //create & customize networking view 39 | NSTableCellView* createNetworkView(NSTableView* tableView, id owner, Connection* connection); 40 | 41 | //add a tracking area to a view within the item view 42 | void addTrackingArea(NSTableCellView* itemView, NSUInteger subviewTag, id owner); 43 | 44 | //build item + 'loaded in' string for dylibs, files, etc in search window 45 | NSAttributedString* initLoadedInString(id item); 46 | 47 | //set code signing image 48 | // ->either signed, unsigned, or unknown 49 | NSImage* getCodeSigningIcon(Binary* binary); 50 | 51 | //configure the VT button 52 | // ->also set's binary name to red if known malware 53 | void configVTButton(NSTableCellView *itemCell, id owner, Binary* binary); -------------------------------------------------------------------------------- /Items/Binary.h: -------------------------------------------------------------------------------- 1 | // 2 | // File.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 2/19/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import "MachO.h" 10 | #import "ItemBase.h" 11 | 12 | #import 13 | #import 14 | 15 | @interface Binary : ItemBase 16 | { 17 | 18 | } 19 | 20 | /* PROPERTIES */ 21 | 22 | //name 23 | @property(nonatomic, retain)NSString* name; 24 | 25 | //path 26 | @property(nonatomic, retain)NSString* path; 27 | 28 | //bundle 29 | @property(nonatomic, retain)NSBundle* bundle; 30 | 31 | //flag for task (main) executable 32 | @property BOOL isTaskBinary; 33 | 34 | //loaded in 35 | // ...for dylibs only 36 | @property(nonatomic, retain)NSArray* loadedIn; 37 | 38 | //hashes (md5, sha1) 39 | @property(nonatomic, retain)NSDictionary* hashes; 40 | 41 | //signing info 42 | @property(nonatomic, retain)NSDictionary* signingInfo; 43 | 44 | //macho parser 45 | @property(nonatomic, retain)MachO* parser; 46 | 47 | //encrypted flag 48 | @property BOOL isEncrypted; 49 | 50 | //packed flag 51 | @property BOOL isPacked; 52 | 53 | //not found 54 | @property BOOL notFound; 55 | 56 | //in dyld cache 57 | @property BOOL inCache; 58 | 59 | /* VIRUS TOTAL INFO */ 60 | 61 | //dictionary returned by VT 62 | @property (nonatomic, retain)NSDictionary* vtInfo; 63 | 64 | 65 | /* METHODS */ 66 | 67 | //init method 68 | -(id)initWithParams:(NSDictionary*)params; 69 | 70 | //machO parse 71 | -(BOOL)parse; 72 | 73 | //get task's name 74 | // ->either from bundle or path's last component 75 | -(NSString*)getName; 76 | 77 | //get an icon for a process 78 | -(NSImage*)getIcon; 79 | 80 | //get signing info (which takes a while to generate) 81 | // ->this method should be called in the background 82 | -(void)generatedSigningInfo; 83 | 84 | //get detailed info (which takes a while to generate) 85 | // ->only shown to user if they click 'info' so this method should be called in the background 86 | -(void)generateDetailedInfo; 87 | 88 | //format the signing info dictionary 89 | -(NSString*)formatSigningInfo; 90 | 91 | 92 | @end 93 | -------------------------------------------------------------------------------- /Items/Connection.h: -------------------------------------------------------------------------------- 1 | // 2 | // Extension.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 2/19/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import "ItemBase.h" 10 | 11 | #import 12 | #import 13 | #import 14 | #import 15 | 16 | //socket states 17 | // ->note, index correspondes to numberic value 18 | static const char* socketStates[] = 19 | { 20 | "closed", 21 | "listening", 22 | "syn sent", 23 | "syn received", 24 | "established", 25 | "close/wait", 26 | "fin wait 1", 27 | "closing", 28 | "last act", 29 | "fin wait 2", 30 | "time wait", 31 | }; 32 | 33 | static const char *socketFamilies[] = 34 | { 35 | "AF_UNSPEC", 36 | "AF_UNIX", 37 | "AF_INET", 38 | "AF_IMPLINK", 39 | "AF_PUP", 40 | "AF_CHAOS", 41 | "AF_NS", 42 | "AF_ISO", 43 | "AF_ECMA", 44 | "AF_DATAKIT", 45 | "AF_CCITT", 46 | "AF_SNA", 47 | "AF_DECnet", 48 | "AF_DLI", 49 | "AF_LAT", 50 | "AF_HYLINK", 51 | "AF_APPLETALK", 52 | "AF_ROUTE", 53 | "AF_LINK", 54 | "#define", 55 | "AF_COIP", 56 | "AF_CNT", 57 | "pseudo_AF_RTIP", 58 | "AF_IPX", 59 | "AF_SIP", 60 | "pseudo_AF_PIP", 61 | "pseudo_AF_BLUE", 62 | "AF_NDRV", 63 | "AF_ISDN", 64 | "pseudo_AF_KEY", 65 | "AF_INET6", 66 | "AF_NATM", 67 | "AF_SYSTEM", 68 | "AF_NETBIOS", 69 | "AF_PPP", 70 | "pseudo_AF_HDRCMPLT", 71 | "AF_RESERVED_36", 72 | }; 73 | #define SOCKET_FAMILY_MAX (int)(sizeof(socketFamilies)/sizeof(char *)) 74 | 75 | 76 | 77 | @interface Connection : ItemBase 78 | { 79 | 80 | } 81 | 82 | //local ip addr 83 | @property(nonatomic, retain)NSString* localIPAddr; 84 | 85 | //local port 86 | @property(nonatomic, retain)NSNumber* localPort; 87 | 88 | //remote ip addr 89 | @property(nonatomic, retain)NSString* remoteIPAddr; 90 | 91 | //remote port 92 | @property(nonatomic, retain)NSNumber* remotePort; 93 | 94 | //remote name (url) 95 | @property(nonatomic, retain)NSString* remoteName; 96 | 97 | //socket type 98 | @property(nonatomic, retain)NSString* type; 99 | 100 | //socket family 101 | @property(nonatomic, retain)NSString* family; 102 | 103 | //socket proto 104 | @property(nonatomic, retain)NSString* proto; 105 | 106 | //socket state 107 | @property(nonatomic, retain)NSString* state; 108 | 109 | //pretty print of connnection 110 | @property(nonatomic, retain)NSMutableString* endpoints; 111 | 112 | 113 | /* METHODS */ 114 | 115 | //set icon 116 | -(void)setConnectionIcon; 117 | 118 | //return a string representation of the connection 119 | -(void)setConnectionString; 120 | 121 | 122 | 123 | @end 124 | -------------------------------------------------------------------------------- /Items/Connection.m: -------------------------------------------------------------------------------- 1 | // 2 | // Extension.m 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 2/19/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import "Consts.h" 10 | #import "Connection.h" 11 | #import "AppDelegate.h" 12 | 13 | @implementation Connection 14 | 15 | @synthesize endpoints; 16 | @synthesize remoteName; 17 | 18 | //init method 19 | -(id)initWithParams:(NSDictionary*)params 20 | { 21 | //super 22 | //self = [super initWithParams:params]; 23 | self = [super init]; 24 | if(nil != self) 25 | { 26 | //alloc string for connection 27 | endpoints = [NSMutableString string]; 28 | 29 | //extract/save local addr 30 | self.localIPAddr = params[KEY_LOCAL_ADDR]; 31 | 32 | //extract/save local port 33 | self.localPort = params[KEY_LOCAL_PORT]; 34 | 35 | //extract/save remote addr 36 | self.remoteIPAddr = params[KEY_REMOTE_ADDR]; 37 | 38 | //extract/save remote port 39 | self.remotePort = params[KEY_REMOTE_PORT]; 40 | 41 | //extract/save type 42 | self.type = [self socketType2String:params[KEY_SOCKET_TYPE]]; 43 | 44 | //extract/save family 45 | self.family = [self socketFamily2String:params[KEY_SOCKET_FAMILY]]; 46 | 47 | //extract/save proto 48 | self.proto = [self socketProto2String:params[KEY_SOCKET_PROTO]]; 49 | 50 | //extract/save state 51 | self.state = [self socketState2String:params[KEY_SOCKET_STATE]]; 52 | 53 | //set icon 54 | [self setConnectionIcon]; 55 | 56 | //resolve DNS names 57 | if(nil != self.remoteIPAddr) 58 | { 59 | //resolve 60 | [self addressesForHost]; 61 | } 62 | 63 | //build/set connection string 64 | [self setConnectionString]; 65 | } 66 | 67 | return self; 68 | } 69 | 70 | //set icon 71 | // ->based on state 72 | -(void)setConnectionIcon 73 | { 74 | //set icon for TCP sockets 75 | if(nil != self.state) 76 | { 77 | //listening 78 | if(YES == [self.state isEqualToString:@"listening"]) 79 | { 80 | //set 81 | self.icon = [NSImage imageNamed:@"listeningIcon"]; 82 | } 83 | //connected 84 | else if(YES == [self.state isEqualToString:@"established"]) 85 | { 86 | //set 87 | self.icon = [NSImage imageNamed:@"connectedIcon"]; 88 | } 89 | //wait related states 90 | else if( (YES == [self.state isEqualToString:@"close/wait"]) || 91 | (YES == [self.state isEqualToString:@"in wait 1"]) || 92 | (YES == [self.state isEqualToString:@"closing"]) || 93 | (YES == [self.state isEqualToString:@"last act"]) || 94 | (YES == [self.state isEqualToString:@"fin wait 2"]) || 95 | (YES == [self.state isEqualToString:@"time wait"]) ) 96 | { 97 | //set 98 | self.icon = [NSImage imageNamed:@"closeWait"]; 99 | } 100 | 101 | //closed 102 | else if(YES == [self.state isEqualToString:@"closed"]) 103 | { 104 | //set 105 | self.icon = [NSImage imageNamed:@"closedIcon"]; 106 | } 107 | } 108 | 109 | //set icon for UDP sockets 110 | // ->can't listen, so just show 'em as streaming 111 | else if(YES == [self.type isEqualToString:@"SOCK_DGRAM"]) 112 | { 113 | //set 114 | self.icon = [NSImage imageNamed:@"streamIcon"]; 115 | } 116 | 117 | return; 118 | } 119 | 120 | //resolve a remote IP address to nice DNS name 121 | // ->uses address and port, which are passed to getaddrinfo 122 | // note: call from bg thread, cuz it can be slow due to DNS resolution(s) 123 | -(void)addressesForHost 124 | { 125 | //host 126 | NSHost* host = nil; 127 | 128 | //init host 129 | host = [NSHost hostWithAddress:self.remoteIPAddr]; 130 | 131 | //extract/save name 132 | self.remoteName = host.name; 133 | 134 | return; 135 | } 136 | 137 | //convert a socket type into string 138 | -(NSString*) socketType2String:(NSNumber*)type 139 | { 140 | //socket type 141 | NSString* socketType = nil; 142 | 143 | //convert 144 | switch(type.intValue) 145 | { 146 | //stream 147 | case SOCK_STREAM: 148 | socketType = @"SOCK_STREAM"; 149 | break; 150 | 151 | //dgram 152 | case SOCK_DGRAM: 153 | socketType = @"SOCK_DGRAM"; 154 | break; 155 | 156 | //raw 157 | case SOCK_RAW: 158 | socketType = @"SOCK_RAW"; 159 | break; 160 | 161 | //rdm 162 | case SOCK_RDM: 163 | socketType = @"SOCK_RDM"; 164 | break; 165 | 166 | //seq packet 167 | case SOCK_SEQPACKET: 168 | socketType = @"SOCK_SEQPACKET"; 169 | break; 170 | 171 | default: 172 | break; 173 | } 174 | 175 | return socketType; 176 | } 177 | 178 | 179 | //convert a socket family into string 180 | -(NSString*) socketFamily2String:(NSNumber*)family 181 | { 182 | //socket family 183 | NSString* socketFamily = nil; 184 | 185 | //sanity check 186 | if( (family.intValue < 0) || 187 | (family.intValue >= SOCKET_FAMILY_MAX) ) 188 | { 189 | //bail 190 | goto bail; 191 | } 192 | 193 | //init socket family string 194 | socketFamily = [NSString stringWithUTF8String:socketFamilies[family.intValue]]; 195 | 196 | //bail 197 | bail: 198 | 199 | return socketFamily; 200 | } 201 | 202 | //convert a socket protocol into string 203 | -(NSString*) socketProto2String:(NSNumber*)proto 204 | { 205 | //socket proto 206 | NSString* socketProto = nil; 207 | 208 | //proto struct 209 | struct protoent *protoInfo = NULL; 210 | 211 | //get proto info 212 | protoInfo = getprotobynumber(proto.intValue); 213 | 214 | //sanity check 215 | if(NULL == protoInfo) 216 | { 217 | //bail 218 | goto bail; 219 | } 220 | 221 | //init proto string 222 | // ->name comes from struct 223 | socketProto = [NSString stringWithUTF8String:protoInfo->p_name]; 224 | 225 | //bail 226 | bail: 227 | 228 | return socketProto; 229 | } 230 | 231 | 232 | //convert a socket state into string 233 | -(NSString*) socketState2String:(NSNumber*)state 234 | { 235 | //socket proto 236 | NSString* socketState = nil; 237 | 238 | //set state 239 | if(state.intValue < TCP_NSTATES) 240 | { 241 | //set state 242 | socketState = [NSString stringWithUTF8String:socketStates[state.intValue]]; 243 | } 244 | //invalid/unknown socket state 245 | else 246 | { 247 | socketState = [NSString stringWithFormat:@"unknown state (%d)", state.intValue]; 248 | } 249 | 250 | return socketState; 251 | } 252 | 253 | //build printable connection string 254 | -(void)setConnectionString 255 | { 256 | //add local addr/port to endpoint string 257 | [self.endpoints appendString:[NSString stringWithFormat:@"%@:%d", self.localIPAddr, [self.localPort unsignedShortValue]]]; 258 | 259 | //for remote connections 260 | // ->add remote endpoint 261 | if( (nil != self.remoteIPAddr) && 262 | (nil != self.remotePort) ) 263 | { 264 | //add remote IP:port 265 | [self.endpoints appendString:[NSString stringWithFormat:@" -> %@:%d", self.remoteIPAddr, [self.remotePort unsignedShortValue]]]; 266 | 267 | //add DNS name 268 | if(nil != self.remoteName) 269 | { 270 | //add 271 | [self.endpoints appendString:[NSString stringWithFormat:@" (%@)", self.remoteName]]; 272 | } 273 | } 274 | 275 | return; 276 | } 277 | 278 | //override method 279 | // ->hash 280 | -(NSUInteger)hash 281 | { 282 | return [self.endpoints hash]; 283 | } 284 | 285 | //override method 286 | // ->equality check 287 | -(BOOL)isEqual:(id)object 288 | { 289 | //flag 290 | BOOL objEqual = NO; 291 | 292 | //check self 293 | if(self == object) 294 | { 295 | //match 296 | objEqual = YES; 297 | 298 | //bail 299 | goto bail; 300 | } 301 | 302 | //check for type 303 | if(YES != [object isKindOfClass:[Connection class]]) 304 | { 305 | //no match 306 | objEqual = NO; 307 | 308 | //bail 309 | goto bail; 310 | } 311 | 312 | //do check 313 | if(YES == [((Connection*)object).endpoints isEqualToString:self.endpoints]) 314 | { 315 | //happy 316 | objEqual = YES; 317 | 318 | //bail 319 | goto bail; 320 | } 321 | 322 | //bail 323 | bail: 324 | 325 | return objEqual; 326 | } 327 | 328 | 329 | 330 | //convert Connection object to a JSON string 331 | -(NSString*)toJSON 332 | { 333 | //json string 334 | NSString *json = nil; 335 | 336 | //init json 337 | json = [NSString stringWithFormat:@"\"connection\": \"%@\", \"local IP\": \"%@\", \"local port\": \"%d\", \"remote IP\": \"%@\", \"remote port\": \"%d\", \"type\": \"%@\", \"family\": \"%@\", \"protocol\": \"%@\", \"state\": \"%@\"", self.endpoints, self.localIPAddr, [self.localPort unsignedShortValue], self.remoteIPAddr, [self.remotePort unsignedShortValue], self.type, self.family, self.proto, self.state]; 338 | 339 | return json; 340 | } 341 | 342 | @end 343 | -------------------------------------------------------------------------------- /Items/File.h: -------------------------------------------------------------------------------- 1 | // 2 | // File.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 2/19/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import "ItemBase.h" 10 | 11 | #import 12 | 13 | /* GLOBALS */ 14 | 15 | //(privacy) protected directories 16 | extern NSArray* protectedDirectories; 17 | 18 | @interface File : ItemBase 19 | { 20 | 21 | } 22 | 23 | /* PROPERTIES */ 24 | 25 | //type 26 | @property(nonatomic, retain)NSString* type; 27 | 28 | 29 | /* METHODS */ 30 | 31 | //init method 32 | -(id)initWithParams:(NSDictionary*)params; 33 | 34 | //get detailed info (which takes a while to generate) 35 | // ->only shown to user if they click 'info' so this method is called in the background 36 | -(void)generateDetailedInfo; 37 | 38 | //set file type 39 | // ->invokes 'file' cmd, the parses out result 40 | -(void)setFileType; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Items/File.m: -------------------------------------------------------------------------------- 1 | // 2 | // File.m 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 2/19/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | #import "File.h" 9 | #import "Consts.h" 10 | #import "Utilities.h" 11 | #import "AppDelegate.h" 12 | 13 | #import 14 | 15 | @implementation File 16 | 17 | @synthesize type; 18 | 19 | //init method 20 | -(id)initWithParams:(NSDictionary*)params 21 | { 22 | //super 23 | // saves path, etc 24 | self = [super initWithParams:params]; 25 | if(self) 26 | { 27 | //extract name 28 | self.name = [[self.path lastPathComponent] stringByDeletingPathExtension]; 29 | 30 | //set icon 31 | self.icon = [[NSWorkspace sharedWorkspace] iconForFile:self.path]; 32 | 33 | //check if protected 34 | // if not, get file attrs 35 | if(YES != [self isProtected]) 36 | { 37 | //get attrs 38 | self.attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:self.path error:nil]; 39 | } 40 | } 41 | 42 | bail: 43 | 44 | return self; 45 | } 46 | 47 | //set file type 48 | // invokes 'file' cmd, the parses out result 49 | -(void)setFileType 50 | { 51 | //results from 'file' cmd 52 | NSMutableDictionary* results = nil; 53 | 54 | //output 55 | NSString* output = nil; 56 | 57 | //array of parsed results 58 | NSArray* parsedResults = nil; 59 | 60 | //exec 'file' to get file type 61 | results = execTask(FILE, @[self.path], YES); 62 | if( (nil == results[EXIT_CODE]) || 63 | (0 != [results[EXIT_CODE] integerValue]) ) 64 | { 65 | //bail 66 | goto bail; 67 | } 68 | 69 | //convert stdout data to string 70 | output = [[NSString alloc] initWithData:results[STDOUT] encoding:NSUTF8StringEncoding]; 71 | 72 | //parse results 73 | // ->format: : 74 | parsedResults = [output componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; 75 | 76 | //sanity check 77 | // ->should be two items in array, and 78 | if(parsedResults.count < 2) 79 | { 80 | //bail 81 | goto bail; 82 | } 83 | 84 | //file type comes second 85 | // ->also trim whitespace 86 | self.type = [parsedResults[1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 87 | 88 | bail: 89 | 90 | return; 91 | } 92 | 93 | 94 | //get detailed info (which takes a while to generate) 95 | // ->only shown to user if they click 'info' so this method is called in the background 96 | -(void)generateDetailedInfo 97 | { 98 | //set type 99 | [self setFileType]; 100 | 101 | return; 102 | } 103 | 104 | //override method 105 | // hash the file 106 | -(NSUInteger)hash 107 | { 108 | return [self.path hash]; 109 | } 110 | 111 | //override method 112 | // file equality check (path) 113 | -(BOOL)isEqual:(id)object 114 | { 115 | //flag 116 | BOOL objEqual = NO; 117 | 118 | //check self 119 | if(self == object) 120 | { 121 | //match 122 | objEqual = YES; 123 | 124 | //bail 125 | goto bail; 126 | } 127 | 128 | //check for type 129 | if(YES != [object isKindOfClass:[File class]]) 130 | { 131 | //no match 132 | objEqual = NO; 133 | 134 | //bail 135 | goto bail; 136 | } 137 | 138 | //do check 139 | if(YES == [((File*)object).path isEqualToString:self.path]) 140 | { 141 | //happy 142 | objEqual = YES; 143 | 144 | //bail 145 | goto bail; 146 | } 147 | 148 | bail: 149 | 150 | return objEqual; 151 | } 152 | 153 | //check if file is protected 154 | // on mojave+, need to avoid prompts 155 | -(BOOL)isProtected 156 | { 157 | //flag 158 | BOOL protected = NO; 159 | 160 | //skip any files in (privacy) protected directories 161 | // as otherwise we will generate a privacy prompt (on Mojave) 162 | for(NSString* directory in protectedDirectories) 163 | { 164 | //check 165 | if(YES == [self.path hasPrefix:directory]) 166 | { 167 | //set flag 168 | protected = YES; 169 | 170 | //done 171 | break; 172 | } 173 | } 174 | 175 | return protected; 176 | } 177 | 178 | //convert object to JSON string 179 | -(NSString*)toJSON 180 | { 181 | //json string 182 | NSString *json = nil; 183 | 184 | //attributes 185 | NSMutableString* attributesJSON = nil; 186 | 187 | //init 188 | attributesJSON = [NSMutableString string]; 189 | 190 | //when attributes are nil 191 | // init default string, 'unknown' 192 | if(nil == self.attributes) 193 | { 194 | //init 195 | [attributesJSON appendString:@"\"unknown\""]; 196 | } 197 | 198 | //file has attributes 199 | // add each one to json 200 | else 201 | { 202 | //start 203 | [attributesJSON appendString:@"{"]; 204 | 205 | //add each attributes 206 | for(NSString* attribute in self.attributes) 207 | { 208 | //skip NSFileExtendedAttributes 209 | // ->binary format 210 | if(YES == [attribute isEqualToString:@"NSFileExtendedAttributes"]) 211 | { 212 | //skip 213 | continue; 214 | } 215 | 216 | //add 217 | [attributesJSON appendFormat:@"\"%@\":\"%@\",", attribute, self.attributes[attribute]]; 218 | } 219 | 220 | //remove last ',' 221 | if(YES == [attributesJSON hasSuffix:@","]) 222 | { 223 | //remove 224 | [attributesJSON deleteCharactersInRange:NSMakeRange([attributesJSON length]-1, 1)]; 225 | } 226 | 227 | //end 228 | [attributesJSON appendString:@"}"]; 229 | } 230 | 231 | //init json 232 | json = [NSString stringWithFormat:@"\"name\": \"%@\", \"path\": \"%@\", \"type\": \"%@\", \"attributes\": %@", self.name, self.path, self.type, attributesJSON]; 233 | 234 | return json; 235 | } 236 | 237 | @end 238 | -------------------------------------------------------------------------------- /Items/ItemBase.h: -------------------------------------------------------------------------------- 1 | // 2 | // ItemBase.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 9/25/14. 6 | // Copyright (c) 2014 Objective-See. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ItemBase : NSObject 12 | { 13 | 14 | } 15 | 16 | //name 17 | @property(retain, nonatomic)NSString* name; 18 | 19 | //path 20 | @property(retain, nonatomic)NSString* path; 21 | 22 | //icon 23 | @property(nonatomic, retain)NSImage* icon; 24 | 25 | //file attributes 26 | @property(nonatomic, retain)NSDictionary* attributes; 27 | 28 | /* METHODS */ 29 | 30 | //init method 31 | -(id)initWithParams:(NSDictionary*)params; 32 | 33 | //return a path that can be opened in Finder.app 34 | -(NSString*)pathForFinder; 35 | 36 | //convert object to JSON string 37 | -(NSString*)toJSON; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /Items/ItemBase.m: -------------------------------------------------------------------------------- 1 | // 2 | // ItemBase.m 3 | // TaskExplorer 4 | 5 | #import "Consts.h" 6 | #import "ItemBase.h" 7 | 8 | #define kErrFormat @"%@ not implemented in subclass %@" 9 | #define kExceptName @"KK Item" 10 | 11 | 12 | 13 | @implementation ItemBase 14 | 15 | @synthesize name; 16 | @synthesize path; 17 | //@synthesize isTrusted; 18 | @synthesize attributes; 19 | 20 | //init method 21 | -(id)initWithParams:(NSDictionary*)params 22 | { 23 | //super 24 | self = [super init]; 25 | if(nil != self) 26 | { 27 | //extract/save name 28 | self.name = params[KEY_RESULT_NAME]; 29 | 30 | //extract/save path 31 | self.path = params[KEY_RESULT_PATH]; 32 | } 33 | 34 | return self; 35 | } 36 | 37 | //return a path that can be opened in Finder.app 38 | -(NSString*)pathForFinder 39 | { 40 | return self.path; 41 | } 42 | 43 | //return 44 | 45 | 46 | /* OPTIONAL METHODS */ 47 | 48 | 49 | /* REQUIRED METHODS */ 50 | 51 | //stubs for inherited methods 52 | // throw exceptions as they should be implemented in sub-classes 53 | 54 | //convert object to JSON string 55 | -(NSString*)toJSON 56 | { 57 | @throw [NSException exceptionWithName:kExceptName 58 | reason:[NSString stringWithFormat:kErrFormat, NSStringFromSelector(_cmd), [self class]] 59 | userInfo:nil]; 60 | return nil; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /KKRow.h: -------------------------------------------------------------------------------- 1 | // 2 | // CategoryRow.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 4/4/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface KKRow : NSTableRowView 12 | 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /KKRow.m: -------------------------------------------------------------------------------- 1 | // 2 | // CategoryRow.m 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 4/4/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import "KKRow.h" 10 | #import "Utilities.h" 11 | 12 | @implementation KKRow 13 | 14 | //custom row selection 15 | -(void)drawSelectionInRect:(NSRect)dirtyRect 16 | { 17 | //selection rect 18 | NSRect selectionRect = {0}; 19 | 20 | //selection path 21 | NSBezierPath *selectionPath = nil; 22 | 23 | //highlight selected rows 24 | if(self.selectionHighlightStyle != NSTableViewSelectionHighlightStyleNone) 25 | { 26 | //make selection rect 27 | selectionRect = NSInsetRect(self.bounds, 2.5, 2.5); 28 | 29 | //dark mode highlight 30 | if(YES == isDarkMode()) 31 | { 32 | //set stroke 33 | [[NSColor colorWithCalibratedWhite:1.0 alpha:1.0] setStroke]; 34 | 35 | //set fill 36 | [[NSColor colorWithCalibratedWhite:.50 alpha:1.0] setFill]; 37 | } 38 | //light mode highlight 39 | else 40 | { 41 | //set stroke 42 | [[NSColor colorWithCalibratedWhite:.65 alpha:1.0] setStroke]; 43 | 44 | //set fill 45 | [[NSColor colorWithCalibratedWhite:.82 alpha:1.0] setFill]; 46 | } 47 | 48 | //create selection path 49 | // ...with rounded corners 50 | selectionPath = [NSBezierPath bezierPathWithRoundedRect:selectionRect xRadius:5 yRadius:5]; 51 | 52 | //fill 53 | [selectionPath fill]; 54 | 55 | //stroke 56 | [selectionPath stroke]; 57 | } 58 | 59 | return; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /NSApplicationKeyEvents.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSApplicationKeyEvents.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 7/11/15. 6 | // Copyright (c) 2015 Objective-See, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSApplicationKeyEvents : NSApplication 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /NSApplicationKeyEvents.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSApplicationKeyEvents.m 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 7/11/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import "NSApplicationKeyEvents.h" 10 | 11 | @implementation NSApplicationKeyEvents 12 | 13 | //to enable copy/paste etc even though we don't have an 'Edit' menu 14 | // details: http://stackoverflow.com/questions/970707/cocoa-keyboard-shortcuts-in-dialog-without-an-edit-menu 15 | -(void) sendEvent:(NSEvent *)event { 16 | if ([event type] == NSKeyDown) { 17 | if (([event modifierFlags] & NSDeviceIndependentModifierFlagsMask) == NSCommandKeyMask) { 18 | if ([[event charactersIgnoringModifiers] isEqualToString:@"x"]) { 19 | if ([self sendAction:@selector(cut:) to:nil from:self]) 20 | return; 21 | } 22 | else if ([[event charactersIgnoringModifiers] isEqualToString:@"c"]) { 23 | if ([self sendAction:@selector(copy:) to:nil from:self]) 24 | return; 25 | } 26 | else if ([[event charactersIgnoringModifiers] isEqualToString:@"v"]) { 27 | if ([self sendAction:@selector(paste:) to:nil from:self]) 28 | return; 29 | } 30 | else if ([[event charactersIgnoringModifiers] isEqualToString:@"a"]) { 31 | if ([self sendAction:@selector(selectAll:) to:nil from:self]) 32 | return; 33 | } 34 | } 35 | 36 | } 37 | [super sendEvent:event]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /NSMutableArray+QueueAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArray+QueueAdditions.h 3 | // BlockBlock 4 | // 5 | // Created by Patrick Wardle on 9/26/14. 6 | // Copyright (c) 2014 Objective-See. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSMutableArray (QueueAdditions) 12 | { 13 | 14 | } 15 | 16 | //METHODS 17 | 18 | //remove first object 19 | -(id)dequeue; 20 | 21 | //add to end 22 | -(void)enqueue:(id)obj; 23 | 24 | //check if empty 25 | -(BOOL)empty; 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /NSMutableArray+QueueAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArray+QueueAdditions.m 3 | // BlockBlock 4 | // 5 | // Created by Patrick Wardle on 9/26/14. 6 | // Copyright (c) 2014 Objective-See. All rights reserved. 7 | // 8 | 9 | #import "NSMutableArray+QueueAdditions.h" 10 | 11 | @implementation NSMutableArray (QueueAdditions) 12 | 13 | //add object to tail (end) of queue 14 | -(void)enqueue:(id)anObject 15 | { 16 | //sync 17 | @synchronized(self) 18 | { 19 | //add object 20 | [self addObject:anObject]; 21 | } 22 | } 23 | 24 | //grab next item in queue 25 | -(id)dequeue 26 | { 27 | //extract object 28 | id queueObject = nil; 29 | 30 | //sync 31 | @synchronized(self) 32 | { 33 | //check to make sure there are some items 34 | if(YES != [self empty]) 35 | { 36 | //extract first one 37 | queueObject = [self objectAtIndex: 0]; 38 | 39 | //delete it from queue 40 | [self removeObjectAtIndex: 0]; 41 | } 42 | 43 | }//sync 44 | 45 | return queueObject; 46 | } 47 | 48 | // Checks if the queue is empty 49 | -(BOOL)empty 50 | { 51 | return ([self lastObject] == nil); 52 | } 53 | 54 | @end 55 | -------------------------------------------------------------------------------- /PrefsWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PrefsWindowController.h 3 | // DHS 4 | // 5 | // Created by Patrick Wardle on 2/6/15. 6 | // Copyright (c) 2015 Objective-See, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface PrefsWindowController : NSWindowController 12 | { 13 | 14 | } 15 | 16 | //buttons 17 | 18 | //button for filtering out OS componets 19 | @property (weak) IBOutlet NSButton* showTrustedItemsBtn; 20 | 21 | //button for disabling talking to VT 22 | @property (weak) IBOutlet NSButton* disableVTQueriesBtn; 23 | 24 | //button for saving output 25 | @property (weak) IBOutlet NSButton* saveOutputBtn; 26 | 27 | //button for ok/close 28 | @property (weak) IBOutlet NSButton *okButton; 29 | 30 | //filter out OS/known items 31 | @property BOOL showTrustedItems; 32 | 33 | //disable talking to VT 34 | @property BOOL disableVTQueries; 35 | 36 | //save results (at end of scan) 37 | @property BOOL saveOutput; 38 | 39 | //save results now 40 | @property BOOL shouldSaveNow; 41 | 42 | /* METHODS */ 43 | 44 | //save existing prefs 45 | -(void)captureExistingPrefs; 46 | 47 | //'OK' button handler 48 | // ->save prefs and close window 49 | -(IBAction)closeWindow:(id)sender; 50 | 51 | 52 | /* METHODS */ 53 | 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /PrefsWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PrefsWindowController.m 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 2/6/15. 6 | // Copyright (c) 2015 Objective-See, LLC. All rights reserved. 7 | // 8 | 9 | 10 | #import "Utilities.h" 11 | #import "AppDelegate.h" 12 | #import "PrefsWindowController.h" 13 | 14 | 15 | @implementation PrefsWindowController 16 | 17 | @synthesize okButton; 18 | @synthesize saveOutput; 19 | @synthesize shouldSaveNow; 20 | @synthesize disableVTQueries; 21 | @synthesize showTrustedItems; 22 | 23 | 24 | //automatically called when nib is loaded 25 | // ->center window 26 | -(void)awakeFromNib 27 | { 28 | //center 29 | [self.window center]; 30 | } 31 | 32 | 33 | //automatically invoked when window is loaded 34 | -(void)windowDidLoad 35 | { 36 | //super 37 | [super windowDidLoad]; 38 | 39 | //no dark mode? 40 | // make window white 41 | if(YES != isDarkMode()) 42 | { 43 | //make white 44 | self.window.backgroundColor = NSColor.whiteColor; 45 | } 46 | 47 | //make button selected 48 | [self.window makeFirstResponder:self.okButton]; 49 | 50 | //capture existing prefs 51 | // needed to trigger re-saves 52 | [self captureExistingPrefs]; 53 | 54 | return; 55 | } 56 | 57 | //save existing prefs 58 | -(void)captureExistingPrefs 59 | { 60 | //save current state of 'include os/trusted' components 61 | self.showTrustedItems = self.showTrustedItemsBtn.state; 62 | 63 | //save current state of 'disable VT' 64 | self.disableVTQueries = self.disableVTQueriesBtn.state; 65 | 66 | //save current state of 'save' button 67 | self.saveOutput = self.saveOutputBtn.state; 68 | 69 | return; 70 | } 71 | 72 | //automatically invoked when window is closing 73 | // ->make ourselves unmodal 74 | -(void)windowWillClose:(NSNotification *)notification 75 | { 76 | //save prefs 77 | //[self savePrefs]; 78 | 79 | //make un-modal 80 | [[NSApplication sharedApplication] stopModal]; 81 | 82 | return; 83 | } 84 | 85 | //'OK' button handler 86 | // ->save prefs and close window 87 | -(IBAction)closeWindow:(id)sender 88 | { 89 | //close 90 | [self.window close]; 91 | 92 | return; 93 | } 94 | @end 95 | -------------------------------------------------------------------------------- /Queue.h: -------------------------------------------------------------------------------- 1 | // 2 | // Queue.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 9/26/14. 6 | // Copyright (c) 2014 Objective-See. All rights reserved. 7 | // 8 | 9 | //from: https://github.com/esromneb/ios-queue-object/blob/master/NSMutableArray%2BQueueAdditions.h 10 | 11 | #import 12 | #import "NSMutableArray+QueueAdditions.h" 13 | 14 | @interface Queue : NSObject 15 | { 16 | //the queue 17 | NSMutableArray* eventQueue; 18 | 19 | //queue processor thread 20 | NSThread* qProcessorThread; 21 | 22 | //condition for queue's status 23 | NSCondition* queueCondition; 24 | 25 | } 26 | 27 | /* PROPERTIES */ 28 | 29 | //items in 30 | @property NSUInteger itemsIn; 31 | 32 | //items out 33 | @property NSUInteger itemsOut; 34 | 35 | //event queue 36 | @property(retain, atomic)NSMutableArray* eventQueue; 37 | 38 | //thread to process events 39 | @property (nonatomic, retain)NSThread* qProcessorThread; 40 | 41 | //condition for queue 42 | @property (nonatomic, retain)NSCondition* queueCondition; 43 | 44 | //METHODS 45 | 46 | //add an object to the queue 47 | -(void)enqueue:(id)anObject; 48 | 49 | //process events from queue 50 | -(void)processQueue:(id)threadParam; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /Queue.m: -------------------------------------------------------------------------------- 1 | // 2 | // Queue.m 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 9/26/14. 6 | // Copyright (c) 2014 Objective-See. All rights reserved. 7 | // 8 | 9 | #import "Queue.h" 10 | #import "Consts.h" 11 | #import "Binary.h" 12 | #import "VirusTotal.h" 13 | #import "AppDelegate.h" 14 | 15 | @implementation Queue 16 | 17 | @synthesize itemsIn; 18 | @synthesize itemsOut; 19 | @synthesize eventQueue; 20 | @synthesize queueCondition; 21 | @synthesize qProcessorThread; 22 | 23 | -(id)init 24 | { 25 | //init super 26 | self = [super init]; 27 | if(nil != self) 28 | { 29 | //init queue 30 | eventQueue = [NSMutableArray array]; 31 | 32 | //init empty condition 33 | queueCondition = [[NSCondition alloc] init]; 34 | 35 | //spin up thread to watch/process queue 36 | self.qProcessorThread = [[NSThread alloc] initWithTarget:self selector:@selector(processQueue:) object:nil]; 37 | 38 | //start it 39 | [self.qProcessorThread start]; 40 | } 41 | 42 | return self; 43 | } 44 | 45 | //process events from Q 46 | -(void)processQueue:(id)threadParam 47 | { 48 | //Binary obj 49 | Binary* binary = nil; 50 | 51 | //nap for a bit 52 | // ->don't want UI thread, etc to suffer 53 | [NSThread sleepForTimeInterval:5.0f]; 54 | 55 | //for ever 56 | while(YES) 57 | { 58 | //pool 59 | @autoreleasepool { 60 | 61 | //lock 62 | [self.queueCondition lock]; 63 | 64 | //wait while queue is empty 65 | while(YES == [self.eventQueue empty]) 66 | { 67 | //wait 68 | [self.queueCondition wait]; 69 | } 70 | 71 | //get item off queue 72 | binary = [eventQueue dequeue]; 73 | 74 | //inc 75 | itemsOut++; 76 | 77 | //unlock 78 | [self.queueCondition unlock]; 79 | 80 | //generate hashes, etc 81 | [binary generateDetailedInfo]; 82 | 83 | //when connected 84 | // add item for VT processing 85 | if(YES == isConnected) 86 | { 87 | //add 88 | [virusTotal addItem:binary]; 89 | } 90 | 91 | } //pool 92 | 93 | }//foreverz process queue 94 | 95 | return; 96 | } 97 | 98 | //add an object to the queue 99 | -(void)enqueue:(id)anObject 100 | { 101 | //lock 102 | [self.queueCondition lock]; 103 | 104 | //add to queue 105 | [self.eventQueue enqueue:anObject]; 106 | 107 | //inc 108 | itemsIn++; 109 | 110 | //signal 111 | [self.queueCondition signal]; 112 | 113 | //unlock 114 | [self.queueCondition unlock]; 115 | 116 | return; 117 | } 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /RequestRootWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PrefsWindowController.h 3 | // DHS 4 | // 5 | // Created by Patrick Wardle on 2/6/15. 6 | // Copyright (c) 2015 Objective-See, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RequestRootWindowController : NSWindowController 12 | { 13 | 14 | } 15 | 16 | /* PROPERTIES */ 17 | 18 | //auth button 19 | @property (weak) IBOutlet NSButton *authButton; 20 | 21 | //cancel button 22 | @property (weak) IBOutlet NSButton *cancelButton; 23 | 24 | //arrow icon 25 | @property (weak) IBOutlet NSImageView *arrowIcon; 26 | 27 | //help button 28 | @property (weak) IBOutlet NSButton *helpButton; 29 | 30 | //status msg 31 | @property (weak) IBOutlet NSTextField *statusMsg; 32 | 33 | //flag indicating app should exit 34 | @property BOOL shouldExit; 35 | 36 | /* METHODS */ 37 | 38 | //invoked when user clicks 'auth' button 39 | // ->auths user! 40 | -(IBAction)authenticate:(id)sender; 41 | 42 | //invoked when user clicks 'help' button 43 | // ->open product's page w/ anchor to help 44 | -(IBAction)help:(id)sender; 45 | 46 | //invoked when user clicks 'cancel' button 47 | // ->exits app 48 | -(IBAction)close:(id)sender; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /RequestRootWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // RequestRootWindowController.m 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 2/6/15. 6 | // Copyright (c) 2015 Objective-See, LLC. All rights reserved. 7 | // 8 | 9 | 10 | #import "Utilities.h" 11 | #import "AppDelegate.h" 12 | #import "RequestRootWindowController.h" 13 | 14 | #import 15 | 16 | 17 | @implementation RequestRootWindowController 18 | 19 | @synthesize statusMsg; 20 | @synthesize authButton; 21 | @synthesize shouldExit; 22 | @synthesize cancelButton; 23 | 24 | //automatically called when nib is loaded 25 | // ->center window 26 | -(void)awakeFromNib 27 | { 28 | //center 29 | [self.window center]; 30 | 31 | //make auth button one in focus 32 | [self.window makeFirstResponder:self.authButton]; 33 | 34 | //default to exit app 35 | // ->unset if auth is succesful 36 | self.shouldExit = YES; 37 | 38 | return; 39 | } 40 | 41 | //automatically invoked when window is loaded 42 | -(void)windowDidLoad 43 | { 44 | //super 45 | [super windowDidLoad]; 46 | 47 | //no dark mode? 48 | // make window white 49 | if(YES != isDarkMode()) 50 | { 51 | //make white 52 | self.window.backgroundColor = NSColor.whiteColor; 53 | } 54 | 55 | //set version sting 56 | //[self.versionLabel setStringValue:[NSString stringWithFormat:@"version: %@", getAppVersion()]]; 57 | 58 | return; 59 | } 60 | 61 | 62 | //automatically invoked when window is closing 63 | // ->make ourselves unmodal and possibly exit app 64 | -(void)windowWillClose:(NSNotification *)notification 65 | { 66 | //make un-modal 67 | [[NSApplication sharedApplication] stopModal]; 68 | 69 | //on errors, cancels 70 | // ->exit app 71 | if(YES == self.shouldExit) 72 | { 73 | //exit 74 | [NSApp terminate:self]; 75 | } 76 | 77 | return; 78 | } 79 | 80 | //invoked when user clicks 'auth' button 81 | // ->auth user, then set XPC service as root/setuid 82 | -(IBAction)authenticate:(id)sender 83 | { 84 | //authorization ref 85 | AuthorizationRef authorizationRef = {0}; 86 | 87 | //args 88 | const char* installArgs[0x10] = {0}; 89 | 90 | //status code 91 | OSStatus osStatus = -1; 92 | 93 | //path to XPC service 94 | NSString* xpcService = nil; 95 | 96 | //hide arrow icon 97 | self.arrowIcon.hidden = YES; 98 | 99 | //hide help button 100 | self.helpButton.hidden = YES; 101 | 102 | //get path to XPC service 103 | xpcService = getPath2XPC(); 104 | if(nil == xpcService) 105 | { 106 | //bail 107 | goto bail; 108 | } 109 | 110 | /* first chown as root */ 111 | 112 | //1st arg: recursive flag 113 | installArgs[0] = "-R"; 114 | 115 | //2nd arg: group/owner 116 | installArgs[1] = "root:wheel"; 117 | 118 | //3rd arg: XPC service 119 | installArgs[2] = [xpcService UTF8String]; 120 | 121 | //end w/ NULL 122 | installArgs[3] = NULL; 123 | 124 | //reset text color 125 | self.statusMsg.textColor = NSColor.controlTextColor; 126 | 127 | //create authorization ref 128 | osStatus = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef); 129 | if(errAuthorizationSuccess != osStatus) 130 | { 131 | //err msg 132 | syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: AuthorizationCreate() failed with %d", osStatus); 133 | 134 | //bail 135 | goto bail; 136 | } 137 | 138 | //chown XPC service as r00t 139 | osStatus = AuthorizationExecuteWithPrivileges(authorizationRef, "/usr/sbin/chown", 0, (char* const*)installArgs, NULL); 140 | if(errAuthorizationSuccess != osStatus) 141 | { 142 | //err msg 143 | syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: AuthorizationExecuteWithPrivileges() failed with %d", osStatus); 144 | 145 | //set result msg 146 | [self.statusMsg setStringValue: [NSString stringWithFormat:@"error: failed with %d", osStatus]]; 147 | 148 | //set font to red 149 | self.statusMsg.textColor = [NSColor redColor]; 150 | 151 | //bail 152 | goto bail; 153 | } 154 | 155 | /* then setuid */ 156 | 157 | //1st arg: recursive flag 158 | installArgs[0] = "-R"; 159 | 160 | //2nd arg: permissions 161 | // ->note: 4 at front is setuid 162 | installArgs[1] = "4755"; 163 | 164 | //3rd arg: XPC service 165 | installArgs[2] = [xpcService UTF8String]; 166 | 167 | //end w/ NULL 168 | installArgs[3] = NULL; 169 | 170 | //chmod XPC service w/ setuid 171 | osStatus = AuthorizationExecuteWithPrivileges(authorizationRef, "/bin/chmod", 0, (char* const*)installArgs, NULL); 172 | if(errAuthorizationSuccess != osStatus) 173 | { 174 | //err msg 175 | syslog(LOG_ERR, "OBJECTIVE-SEE ERROR: AuthorizationExecuteWithPrivileges() failed with %d", osStatus); 176 | 177 | //set result msg 178 | [self.statusMsg setStringValue: [NSString stringWithFormat:@"error: failed with %d", osStatus]]; 179 | 180 | //set font to red 181 | self.statusMsg.textColor = [NSColor redColor]; 182 | 183 | //bail 184 | goto bail; 185 | } 186 | 187 | //no exit 188 | self.shouldExit = NO; 189 | 190 | //disable auth button 191 | self.authButton.enabled = NO; 192 | 193 | //disable cancel button 194 | self.cancelButton.enabled = NO; 195 | 196 | //update auth message 197 | self.statusMsg.stringValue = @"...authorization successful!"; 198 | 199 | //wait a bit 200 | // ->then hide window & kick off action 201 | { 202 | 203 | //dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.50 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 204 | 205 | [self.window close]; 206 | 207 | // Other UI updates 208 | [((AppDelegate*)[[NSApplication sharedApplication] delegate]).window makeKeyAndOrderFront:self]; 209 | [NSApp activateIgnoringOtherApps:YES]; 210 | [((AppDelegate*)[[NSApplication sharedApplication] delegate]) go]; 211 | // }); 212 | 213 | } 214 | 215 | bail: 216 | 217 | //free auth ref 218 | if(0 != authorizationRef) 219 | { 220 | //free 221 | AuthorizationFree(authorizationRef, kAuthorizationFlagDestroyRights); 222 | } 223 | 224 | 225 | 226 | return; 227 | } 228 | 229 | //invoked when user clicks 'cancel' button 230 | // ->close window, which will trigger app exit 231 | -(IBAction)close:(id)sender 232 | { 233 | //exit 234 | [self.window close]; 235 | } 236 | 237 | //invoked when user clicks 'help' button 238 | // ->open product's page w/ anchor to help 239 | -(IBAction)help:(id)sender 240 | { 241 | //open URL 242 | // ->invokes user's default browser 243 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://objective-see.com/products/taskexplorer.html#help"]]; 244 | 245 | return; 246 | } 247 | @end 248 | -------------------------------------------------------------------------------- /ResultsWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // PrefsWindowController.h 3 | // DHS 4 | // 5 | // Created by Patrick Wardle on 2/6/15. 6 | // Copyright (c) 2015 Objective-See, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ResultsWindowController : NSWindowController 12 | { 13 | 14 | } 15 | 16 | /* PROPERTIES */ 17 | 18 | //details 19 | @property(nonatomic, retain)NSString* details; 20 | 21 | //details of results label/string 22 | @property(weak) IBOutlet NSTextField *detailsLabel; 23 | 24 | 25 | /* METHODS */ 26 | 27 | //invoked when user clicks 'more info' button 28 | // ->open KK's webpage 29 | - (IBAction)close:(id)sender; 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /ResultsWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // PrefsWindowController.m 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 2/6/15. 6 | // Copyright (c) 2015 Objective-See, LLC. All rights reserved. 7 | // 8 | 9 | 10 | #import "Utilities.h" 11 | #import "AppDelegate.h" 12 | 13 | #import "ResultsWindowController.h" 14 | 15 | 16 | @implementation ResultsWindowController 17 | 18 | @synthesize details; 19 | @synthesize detailsLabel; 20 | 21 | //automatically called when nib is loaded 22 | // ->center window 23 | -(void)awakeFromNib 24 | { 25 | //center 26 | [self.window center]; 27 | } 28 | 29 | //automatically invoked when window is loaded 30 | // ->set to white 31 | -(void)windowDidLoad 32 | { 33 | //super 34 | [super windowDidLoad]; 35 | 36 | //no dark mode? 37 | // make window white 38 | if(YES != isDarkMode()) 39 | { 40 | //make white 41 | self.window.backgroundColor = NSColor.whiteColor; 42 | } 43 | 44 | //set details 45 | self.detailsLabel.stringValue = self.details; 46 | 47 | return; 48 | } 49 | 50 | //automatically invoked when user clicks 'OK' 51 | // ->close window 52 | -(IBAction)close:(id)sender 53 | { 54 | //close 55 | [[self window] close]; 56 | 57 | return; 58 | } 59 | 60 | //automatically invoked when window is closing 61 | // ->make ourselves unmodal 62 | -(void)windowWillClose:(NSNotification *)notification 63 | { 64 | //make un-modal 65 | [[NSApplication sharedApplication] stopModal]; 66 | 67 | return; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /SearchWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // FlaggedItems.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 8/14/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "CustomTextField.h" 12 | #import "InfoWindowController.h" 13 | #import "VTInfoWindowController.h" 14 | 15 | 16 | @interface SearchWindowController : NSWindowController 17 | 18 | //PROPERTIES 19 | 20 | //flag for first time init's 21 | @property BOOL didInit; 22 | 23 | //enumeration state thread 24 | @property (nonatomic, retain) NSThread* stateMonitor; 25 | 26 | //search box 27 | @property (weak) IBOutlet NSTextField *searchBox; 28 | 29 | //table 30 | @property (weak) IBOutlet NSTableView *searchTable; 31 | 32 | //table items 33 | @property (nonatomic, retain) NSMutableArray* searchResults; 34 | 35 | //activity indicator 36 | @property (weak) IBOutlet NSProgressIndicator *activityIndicator; 37 | 38 | //activity indicator label 39 | @property (weak) IBOutlet NSTextField *activityIndicatorLabel; 40 | 41 | //filter object 42 | @property (nonatomic, retain) Filter* filterObj; 43 | 44 | //search status message 45 | @property (weak) IBOutlet NSTextField *searchResultsMessage; 46 | 47 | //overlay for serach 48 | @property (weak) IBOutlet NSView *overlay; 49 | 50 | //activity indicator for overlay 51 | @property (weak) IBOutlet NSProgressIndicator *overlaySpinner; 52 | 53 | //mesasge for overlay 54 | @property (weak) IBOutlet NSTextField *overlayMessage; 55 | 56 | //vt window controller 57 | @property (nonatomic, retain) VTInfoWindowController* vtWindowController; 58 | 59 | //info window controller 60 | @property (nonatomic, retain) InfoWindowController* infoWindowController; 61 | 62 | //overlay view 63 | @property (weak) IBOutlet NSView *overlayView; 64 | 65 | //flag for filter field (autocomplete) 66 | @property BOOL completePosting; 67 | 68 | //flag for filter field (autocomplete) 69 | @property BOOL commandHandling; 70 | 71 | //custom search field 72 | @property (nonatomic, retain)CustomTextField* customSearchField; 73 | 74 | 75 | /* METHODS */ 76 | 77 | //init/prepare 78 | // ->make sure everything is cleanly init'd 79 | -(void)prepare; 80 | 81 | //search 82 | -(void)search; 83 | 84 | //callback for when searching is done 85 | // ->update UI by removing overlay and reloading table 86 | -(void)completeSearch; 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /Signing.h: -------------------------------------------------------------------------------- 1 | // 2 | // File: Signing.h 3 | // Project: Proc Info 4 | // 5 | // Created by: Patrick Wardle 6 | // Copyright: 2017 Objective-See 7 | // License: Creative Commons Attribution-NonCommercial 4.0 International License 8 | // 9 | 10 | #ifndef Signing_h 11 | #define Signing_h 12 | 13 | #import 14 | #import 15 | 16 | /* FUNCTIONS */ 17 | 18 | //get the signing info of a item 19 | // pid specified: extract dynamic code signing info 20 | // path specified: generate static code signing info 21 | NSMutableDictionary* extractSigningInfo(pid_t pid, NSString* path, SecCSFlags flags); 22 | 23 | //determine who signed item 24 | NSNumber* extractSigner(SecStaticCodeRef code, SecCSFlags flags, BOOL isDynamic); 25 | 26 | //validate a requirement 27 | OSStatus validateRequirement(SecStaticCodeRef code, SecRequirementRef requirement, SecCSFlags flags, BOOL isDynamic); 28 | 29 | //extract (names) of signing auths 30 | NSMutableArray* extractSigningAuths(NSDictionary* signingDetails); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /Task.h: -------------------------------------------------------------------------------- 1 | // 2 | // Task.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 5/2/15. 6 | // Copyright (c) 2015 Objective-See, LLC. All rights reserved. 7 | // 8 | 9 | #import "Binary.h" 10 | #import 11 | 12 | 13 | /* 14 | 15 | //32bit 16 | struct dyld_image_info_32 { 17 | int imageLoadAddress; 18 | int imageFilePath; 19 | int imageFileModDate; 20 | }; 21 | 22 | */ 23 | 24 | @interface Task : NSObject 25 | { 26 | //pid 27 | NSNumber* pid; 28 | 29 | //uid 30 | //uid_t uid; 31 | 32 | //main binary 33 | Binary* binary; 34 | 35 | //parent id 36 | NSNumber* ppid; 37 | 38 | //children (for tree view) 39 | NSMutableArray* children; 40 | } 41 | 42 | 43 | @property (nonatomic, retain)NSNumber* pid; 44 | 45 | //main binary 46 | @property(nonatomic, retain)Binary* binary; 47 | 48 | //process args 49 | @property(nonatomic, retain)NSMutableArray* arguments; 50 | 51 | //loaded dylibs 52 | @property(nonatomic, retain)NSMutableArray* dylibs; 53 | 54 | //open files 55 | @property(nonatomic, retain)NSMutableArray* files; 56 | 57 | //connections 58 | @property(nonatomic, retain)NSMutableArray* connections; 59 | 60 | //uid 61 | @property uid_t uid; 62 | 63 | //parent's pid 64 | @property (nonatomic, retain)NSNumber* ppid; 65 | 66 | //children 67 | @property (nonatomic, retain)NSMutableArray* children; 68 | 69 | 70 | /* METHODS */ 71 | 72 | //init w/ a pid + path 73 | // note: icons are dynamically determined only when process is shown in alert 74 | -(id)initWithPID:(NSNumber*)taskPID; 75 | 76 | //get task's path 77 | // ->via 'proc_pidpath()' or via task's args ('KERN_PROCARGS2') 78 | -(NSString*)getPath; 79 | 80 | //get command-line args 81 | -(void)getArguments; 82 | 83 | //enumerate all dylibs 84 | // ->new ones are added to 'existingDylibs' (global) dictionary 85 | -(void)enumerateDylibs:(NSMutableDictionary*)allDylibs shouldWait:(BOOL)shouldWait; 86 | 87 | //enumerate all open files 88 | -(void)enumerateFiles:(BOOL)shouldWait; 89 | 90 | //enumerate network sockets/connections 91 | -(void)enumerateNetworking:(BOOL)shouldWait; 92 | 93 | //convert self to JSON string 94 | -(NSString*)toJSON:(BOOL)detailed; 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /TaskEnumerator.h: -------------------------------------------------------------------------------- 1 | // 2 | // TaskEnumerator.h 3 | // 4 | // 5 | // Created by Patrick Wardle on 5/2/15. 6 | // 7 | // 8 | 9 | #import "Task.h" 10 | #import "Queue.h" 11 | #import "Consts.h" 12 | #import "Signing.h" 13 | #import "Utilities.h" 14 | #import "Connection.h" 15 | #import "3rdParty/OrderedDictionary.h" 16 | 17 | #import 18 | #import 19 | #import 20 | #import 21 | #import 22 | #import 23 | 24 | @interface TaskEnumerator : NSObject 25 | { 26 | 27 | } 28 | 29 | /* PROPERTIES */ 30 | 31 | //enumerator thread 32 | @property(nonatomic, retain)NSThread* enumerator; 33 | 34 | //all tasks objects 35 | @property(nonatomic, retain)OrderedDictionary* tasks; 36 | 37 | //all task binaries (main executables) 38 | @property(nonatomic, retain)NSMutableDictionary* executables; 39 | 40 | //all dylibs 41 | @property(nonatomic, retain)NSMutableDictionary* dylibs; 42 | 43 | //flagged items 44 | @property(nonatomic, retain)NSMutableArray* flaggedItems; 45 | 46 | //queue 47 | // ->contains binaries that should be processed 48 | @property (nonatomic, retain)Queue* binaryQueue; 49 | 50 | //state 51 | // ->enum'ing tasks, dylibs, file, etc... 52 | @property NSUInteger state; 53 | 54 | /* METHODS */ 55 | 56 | //enumerate all tasks 57 | // ->call back into app delegate to update task (top) table 58 | -(void)enumerateTasks:(NSNumber*)pid; 59 | 60 | //get list of all pids 61 | -(OrderedDictionary*)getAllTasks; 62 | 63 | //insert tasks into appropriate parent 64 | // ->ensures order of parent's (by pid), is preserved 65 | -(void)generateAncestries:(OrderedDictionary*)newTasks; 66 | 67 | //remove a task 68 | // ->contain extra logic to remove children, etc 69 | -(void)removeTask:(Task*)task; 70 | 71 | //given a task 72 | // ->get list of all child pids 73 | -(void)getAllChildren:(Task*)parent children:(NSMutableArray*)children; 74 | 75 | //get all tasks a dylib/file is loaded into 76 | -(NSMutableArray*)loadedIn:(id)item; 77 | 78 | //get all task pids for a given binary 79 | -(NSMutableArray*)tasksForBinary:(Binary*)binary; 80 | 81 | //ensure that the list of flagged items is correctly updated 82 | // when a dead task or any of its dylibs were flagged... 83 | -(void)updateFlaggedItems:(Task*)deadTask; 84 | 85 | 86 | @end 87 | -------------------------------------------------------------------------------- /TaskExplorer-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | icon 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 2.1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 2.1.0 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSHumanReadableCopyright 28 | Copyright © 2025 Objective-See, LLC. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplicationKeyEvents 33 | LSUIElement 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /TaskExplorer-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'TaskExplorer' target in the 'TaskExplorer' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /TaskExplorer.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /TaskExplorer.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TaskExplorer.xcodeproj/project.xcworkspace/xcshareddata/TaskExplorer.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | FE4103FE-6F26-4639-8C9F-D8D32C76D6A9 9 | IDESourceControlProjectName 10 | TaskExplorer 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 61F07AFB33748EF0C810BEEF6126283DAC63A899 14 | https://bitbucket.org/objective-see/taskexplorer.git 15 | 16 | IDESourceControlProjectPath 17 | TaskExplorer.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 61F07AFB33748EF0C810BEEF6126283DAC63A899 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://bitbucket.org/objective-see/taskexplorer.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 61F07AFB33748EF0C810BEEF6126283DAC63A899 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 61F07AFB33748EF0C810BEEF6126283DAC63A899 36 | IDESourceControlWCCName 37 | TaskExplorer 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /TaskExplorer.xcodeproj/project.xcworkspace/xcshareddata/TaskExplorer.xcscmblueprint: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "61F07AFB33748EF0C810BEEF6126283DAC63A899", 3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { 4 | 5 | }, 6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { 7 | "61F07AFB33748EF0C810BEEF6126283DAC63A899" : 0, 8 | "7564E3FECD3AE625755C217749E31B3EE2B69E20" : 0 9 | }, 10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "FE4103FE-6F26-4639-8C9F-D8D32C76D6A9", 11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { 12 | "61F07AFB33748EF0C810BEEF6126283DAC63A899" : "TaskExplorer\/", 13 | "7564E3FECD3AE625755C217749E31B3EE2B69E20" : "MachO\/" 14 | }, 15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "TaskExplorer", 16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204, 17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "TaskExplorer.xcodeproj", 18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ 19 | { 20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/bitbucket.org\/objective-see\/taskexplorer.git", 21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "61F07AFB33748EF0C810BEEF6126283DAC63A899" 23 | }, 24 | { 25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/bitbucket.org\/objective-see\/macho.git", 26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "7564E3FECD3AE625755C217749E31B3EE2B69E20" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /TaskExplorer.xcodeproj/project.xcworkspace/xcuserdata/patrick.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges 6 | 7 | SnapshotAutomaticallyBeforeSignificantChanges 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /TaskExplorer.xcodeproj/project.xcworkspace/xcuserdata/patrickw.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/TaskExplorer.xcodeproj/project.xcworkspace/xcuserdata/patrickw.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /TaskExplorer.xcodeproj/project.xcworkspace/xcuserdata/patrickw.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges 6 | 7 | SnapshotAutomaticallyBeforeSignificantChanges 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcschemes/TaskExplorer.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 16 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 39 | 41 | 47 | 48 | 49 | 50 | 53 | 54 | 55 | 56 | 60 | 61 | 62 | 63 | 64 | 65 | 71 | 73 | 79 | 80 | 81 | 82 | 84 | 85 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcschemes/remoteTaskService.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 56 | 59 | 60 | 61 | 67 | 68 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /TaskExplorer.xcodeproj/xcuserdata/patrick.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | TaskExplorer.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | remoteTaskService.xcscheme 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 1D21BC4A172AF43D009D1CFD 21 | 22 | primary 23 | 24 | 25 | CDA5F6AC1B16D805003CE340 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /TaskExplorer.xcodeproj/xcuserdata/patrickw.xcuserdatad/xcschemes/TaskExplorer.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 55 | 57 | 63 | 64 | 65 | 66 | 70 | 71 | 72 | 73 | 74 | 75 | 81 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /TaskExplorer.xcodeproj/xcuserdata/patrickw.xcuserdatad/xcschemes/remoteTaskService.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /TaskExplorer.xcodeproj/xcuserdata/patrickw.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | TaskExplorer.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | remoteTaskService.xcscheme 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 1D21BC4A172AF43D009D1CFD 21 | 22 | primary 23 | 24 | 25 | CDA5F6AC1B16D805003CE340 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /TaskTableController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ItemTableController.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 2/18/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import "Task.h" 10 | #import "InfoWindowController.h" 11 | #import "VTInfoWindowController.h" 12 | #import "3rdParty/OrderedDictionary.h" 13 | 14 | #import 15 | 16 | @interface TaskTableController : NSViewController 17 | { 18 | 19 | } 20 | 21 | //flag for first time init's 22 | @property BOOL didInit; 23 | 24 | //flag for ignoring automated row selections 25 | @property BOOL ignoreSelection; 26 | 27 | //flag for filtering 28 | @property BOOL isFiltered; 29 | 30 | //all table items 31 | @property(nonatomic, retain)NSMutableArray* tableItems; 32 | 33 | //filtered table items 34 | @property(nonatomic, retain)NSMutableArray* filteredItems; 35 | 36 | //category table view 37 | @property(weak) IBOutlet NSTableView *itemView; 38 | 39 | //info window controller 40 | @property(retain, nonatomic)InfoWindowController* infoWindowController; 41 | 42 | //virus total window controller 43 | @property (nonatomic, retain)VTInfoWindowController* vtWindowController; 44 | 45 | //currently selected row 46 | // ->can help determine if newly selected row is really new 47 | @property NSUInteger selectedRow; 48 | 49 | //flag to differentiate between top/bottom view 50 | @property BOOL isBottomPane; 51 | 52 | /* METHODS */ 53 | 54 | //reload table 55 | -(void)reloadTable; 56 | 57 | //custom reload 58 | // ->ensures selected row remains selected 59 | -(void)refresh; 60 | 61 | //grab a task at a row 62 | -(Task*)taskForRow:(id)sender; 63 | 64 | //button handler 65 | // ->show item in finder 66 | - (IBAction)showInFinder:(id)sender; 67 | 68 | //button handler 69 | // ->show info window 70 | - (IBAction)showInfo:(id)sender; 71 | 72 | //button handler 73 | // ->show virus total info window 74 | -(void)showVTInfo:(NSView*)button; 75 | 76 | //scroll back up to top of table 77 | -(void)scrollToTop; 78 | 79 | //handle when user clicks row 80 | // ->update bottom pane w/ task's dylibs/files/etc 81 | -(void)handleRowSelection; 82 | 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /UI/PrefsWindow.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 41 | 48 | 55 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /UI/RequestRootWindow.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 57 | 58 | 59 | 60 | 61 | 62 | 73 | 74 | 75 | 76 | 77 | 78 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /UI/ResultsWindow.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /UI/UpdateWindow.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 58 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Update.h: -------------------------------------------------------------------------------- 1 | // 2 | // file: Update.h 3 | // project: lulu (shared) 4 | // description: checks for new versions of LuLu (header) 5 | // 6 | // created by Patrick Wardle 7 | // copyright (c) 2017 Objective-See. All rights reserved. 8 | // 9 | 10 | 11 | #ifndef Update_h 12 | #define Update_h 13 | 14 | @import Cocoa; 15 | @import Foundation; 16 | 17 | @interface Update : NSObject 18 | 19 | //check for an update 20 | // will invoke app delegate method to update UI when check completes 21 | -(void)checkForUpdate:(void (^)(NSUInteger result, NSString* latestVersion))completionHandler; 22 | 23 | @end 24 | 25 | 26 | #endif /* Update_h */ 27 | -------------------------------------------------------------------------------- /Update.m: -------------------------------------------------------------------------------- 1 | // 2 | // file: Update.m 3 | // project: lulu (shared) 4 | // description: checks for new versions of LuLu 5 | // 6 | // created by Patrick Wardle 7 | // copyright (c) 2017 Objective-See. All rights reserved. 8 | // 9 | 10 | #import "Consts.h" 11 | #import "Update.h" 12 | #import "Utilities.h" 13 | #import "AppDelegate.h" 14 | 15 | 16 | @implementation Update 17 | 18 | //check for an update 19 | // ->will invoke app delegate method to update UI when check completes 20 | -(void)checkForUpdate:(void (^)(NSUInteger result, NSString* latestVersion))completionHandler 21 | { 22 | //latest version 23 | __block NSString* latestVersion = nil; 24 | 25 | //result 26 | __block NSInteger result = -1; 27 | 28 | //get latest version in background 29 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 30 | 31 | //grab latest version 32 | latestVersion = [self getLatestVersion]; 33 | if(nil != latestVersion) 34 | { 35 | //check 36 | result = (NSOrderedAscending == [getAppVersion() compare:latestVersion options:NSNumericSearch]); 37 | } 38 | 39 | //invoke app delegate method 40 | // ->will update UI/show popup if necessart 41 | dispatch_async(dispatch_get_main_queue(), 42 | ^{ 43 | completionHandler(result, latestVersion); 44 | }); 45 | 46 | }); 47 | 48 | return; 49 | } 50 | 51 | //query interwebz to get latest version 52 | -(NSString*)getLatestVersion 53 | { 54 | //product version(s) data 55 | NSData* productsVersionData = nil; 56 | 57 | //version dictionary 58 | NSDictionary* productsVersionDictionary = nil; 59 | 60 | //latest version 61 | NSString* latestVersion = nil; 62 | 63 | //get version from remote URL 64 | productsVersionData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:PRODUCT_VERSIONS_URL]]; 65 | if(nil == productsVersionData) 66 | { 67 | //bail 68 | goto bail; 69 | } 70 | 71 | //convert JSON to dictionary 72 | // ->wrap as may throw exception 73 | @try 74 | { 75 | //convert 76 | productsVersionDictionary = [NSJSONSerialization JSONObjectWithData:productsVersionData options:0 error:nil]; 77 | if(nil == productsVersionDictionary) 78 | { 79 | //bail 80 | goto bail; 81 | } 82 | } 83 | @catch(NSException* exception) 84 | { 85 | //bail 86 | goto bail; 87 | } 88 | 89 | //extract latest version 90 | latestVersion = [[productsVersionDictionary objectForKey:PRODUCT_NAME] objectForKey:@"version"]; 91 | 92 | bail: 93 | 94 | return latestVersion; 95 | } 96 | 97 | @end 98 | -------------------------------------------------------------------------------- /UpdateWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // file: UpdateWindowController.m 3 | // project: KnockKnock 4 | // description: window handler for update window/popup (header) 5 | // 6 | // created by Patrick Wardle 7 | // copyright (c) 2017 Objective-See. All rights reserved. 8 | // 9 | 10 | @import Cocoa; 11 | 12 | @interface UpdateWindowController : NSWindowController 13 | { 14 | 15 | } 16 | 17 | /* PROPERTIES */ 18 | 19 | //version label/string 20 | @property(weak)IBOutlet NSTextField *infoLabel; 21 | 22 | //action button 23 | @property(weak)IBOutlet NSButton *actionButton; 24 | 25 | //label string 26 | @property(nonatomic, retain)NSString* infoLabelString; 27 | 28 | //first button ('update check') 29 | @property(weak)IBOutlet NSView *firstButton; 30 | 31 | //button title 32 | @property(nonatomic, retain)NSString* actionButtonTitle; 33 | 34 | //overlay view 35 | @property(weak)IBOutlet NSView *overlayView; 36 | 37 | //spinner 38 | @property(weak)IBOutlet NSProgressIndicator *progressIndicator; 39 | 40 | /* METHODS */ 41 | 42 | //save the main label's & button title's text 43 | -(void)configure:(NSString*)label buttonTitle:(NSString*)buttonTitle; 44 | 45 | //invoked when user clicks button 46 | // ->trigger action such as opening product website, updating, etc 47 | -(IBAction)buttonHandler:(id)sender; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /UpdateWindowController.m: -------------------------------------------------------------------------------- 1 | // 2 | // file: UpdateWindowController.m 3 | // project: KnockKnock 4 | // description: window handler for update window/popup 5 | // 6 | // created by Patrick Wardle 7 | // copyright (c) 2017 Objective-See. All rights reserved. 8 | // 9 | 10 | #import "Consts.h" 11 | #import "Utilities.h" 12 | #import "AppDelegate.h" 13 | #import "UpdateWindowController.h" 14 | 15 | 16 | @implementation UpdateWindowController 17 | 18 | @synthesize infoLabel; 19 | @synthesize overlayView; 20 | @synthesize firstButton; 21 | @synthesize actionButton; 22 | @synthesize infoLabelString; 23 | @synthesize actionButtonTitle; 24 | @synthesize progressIndicator; 25 | 26 | //automatically called when nib is loaded 27 | // ->center window 28 | -(void)awakeFromNib 29 | { 30 | //center 31 | [self.window center]; 32 | 33 | return; 34 | } 35 | 36 | //automatically invoked when window is loaded 37 | // ->set to white 38 | -(void)windowDidLoad 39 | { 40 | //super 41 | [super windowDidLoad]; 42 | 43 | //not in dark mode? 44 | // make window white 45 | if(YES != isDarkMode()) 46 | { 47 | //make white 48 | self.window.backgroundColor = NSColor.whiteColor; 49 | } 50 | 51 | //indicated title bar is tranparent (too) 52 | self.window.titlebarAppearsTransparent = YES; 53 | 54 | //set main label 55 | [self.infoLabel setStringValue:self.infoLabelString]; 56 | 57 | //set button text 58 | self.actionButton.title = self.actionButtonTitle; 59 | 60 | //hide first button when action is 'update' 61 | // ->don't need update check button ;) 62 | if(YES == [self.actionButton.title isEqualToString:@"Update"]) 63 | { 64 | //hide 65 | self.firstButton.hidden = YES; 66 | 67 | //then make action button first responder 68 | [self.window makeFirstResponder:self.actionButton]; 69 | } 70 | 71 | //make it key window 72 | [self.window makeKeyAndOrderFront:self]; 73 | 74 | //make window front 75 | [NSApp activateIgnoringOtherApps:YES]; 76 | 77 | return; 78 | } 79 | 80 | //automatically invoked when window is closing 81 | // ->make ourselves unmodal 82 | -(void)windowWillClose:(NSNotification *)notification 83 | { 84 | //make un-modal 85 | [[NSApplication sharedApplication] stopModal]; 86 | 87 | return; 88 | } 89 | 90 | //save the main label's & button title's text 91 | // ->invoked before window is loaded (and thus buttons, etc are nil) 92 | -(void)configure:(NSString*)label buttonTitle:(NSString*)buttonTitle 93 | { 94 | //save label's string 95 | self.infoLabelString = label; 96 | 97 | //save button's title 98 | self.actionButtonTitle = buttonTitle; 99 | 100 | return; 101 | } 102 | 103 | //invoked when user clicks button 104 | // trigger action such as opening product website, updating, etc 105 | -(IBAction)buttonHandler:(id)sender 106 | { 107 | //handle 'update' / 'more info', etc 108 | // ->open LuLu's webpage, if they *didn't* click 'Close' 109 | if(YES != [((NSButton*)sender).title isEqualToString:@"Close"]) 110 | { 111 | //open URL 112 | // ->invokes user's default browser 113 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:PRODUCT_URL]]; 114 | } 115 | 116 | //always close window 117 | [[self window] close]; 118 | 119 | return; 120 | } 121 | @end 122 | -------------------------------------------------------------------------------- /Utilities.h: -------------------------------------------------------------------------------- 1 | // 2 | // Utilities.h 3 | // DHS 4 | // 5 | // Created by Patrick Wardle on 2/7/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #ifndef DHS_Utilities_h 10 | #define DHS_Utilities_h 11 | 12 | #import 13 | #import 14 | 15 | extern bool _dyld_shared_cache_contains_path(const char* path); 16 | 17 | 18 | /* FUNCTIONS */ 19 | 20 | //TODO: remove unneeded items 21 | 22 | //loads a framework 23 | // note: assumes it is in 'Framework' dir 24 | NSBundle* loadFramework(NSString* name); 25 | 26 | 27 | //get OS's major or minor version 28 | SInt32 getVersion(OSType selector); 29 | 30 | //given a path to binary 31 | // parse it back up to find app's bundle 32 | NSBundle* findAppBundle(NSString* binaryPath); 33 | 34 | //given a directory and a filter predicate 35 | // ->return all matches 36 | NSArray* directoryContents(NSString* directory, NSString* predicate); 37 | 38 | //hash (sha1/md5) a file 39 | NSDictionary* hashFile(NSString* filePath); 40 | 41 | //get app's version 42 | // ->extracted from Info.plist 43 | NSString* getAppVersion(); 44 | 45 | //convert a textview to a clickable hyperlink 46 | void makeTextViewHyperlink(NSTextField* textField, NSURL* url); 47 | 48 | //determine if a file is signed by Apple proper 49 | BOOL isApple(NSString* path); 50 | 51 | //set the color of an attributed string 52 | NSMutableAttributedString* setStringColor(NSAttributedString* string, NSColor* color); 53 | 54 | //exec a process and grab it's output 55 | NSMutableDictionary* execTask(NSString* binaryPath, NSArray* arguments, BOOL shouldWait); 56 | 57 | //wait until a window is non nil 58 | // ->then make it modal 59 | void makeModal(NSWindowController* windowController); 60 | 61 | //given a pid, get its parent (ppid) 62 | pid_t getParentID(int pid); 63 | 64 | //get path to XPC service 65 | NSString* getPath2XPC(); 66 | 67 | //get path to kernel 68 | NSString* path2Kernel(); 69 | 70 | //determine if process is (still) alive 71 | BOOL isAlive(pid_t targetPID); 72 | 73 | //check if computer has network connection 74 | BOOL isNetworkConnected(); 75 | 76 | //set or unset button's highlight 77 | void buttonAppearance(NSTableView* table, NSEvent* event, BOOL shouldReset); 78 | 79 | //check if remote process is i386 80 | BOOL Is32Bit(pid_t targetPID); 81 | 82 | //check if app is translocated 83 | // ->based on http://lapcatsoftware.com/articles/detect-app-translocation.html 84 | NSURL* getUnTranslocatedURL(); 85 | 86 | //give a list of paths 87 | // convert any `~` to all or current user 88 | NSMutableArray* expandPaths(const __strong NSString* const paths[], int count); 89 | 90 | //check if (full) dark mode 91 | // meaning, Mojave+ and dark mode enabled 92 | BOOL isDarkMode(); 93 | 94 | //bring an app to foreground (to get an icon in the dock) or background 95 | void transformProcess(ProcessApplicationTransformState location); 96 | 97 | //check if file is in shared cache 98 | // uses private _dyld_shared_cache_contains_path API 99 | BOOL isInSharedCache(NSString* path); 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /VTButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // vtButton.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 3/26/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import "Binary.h" 10 | #import 11 | 12 | //#import "ItemTableController.h" 13 | 14 | @class TaskTableController; 15 | 16 | @interface VTButton : NSButton 17 | { 18 | 19 | } 20 | 21 | //properties 22 | 23 | //parent object 24 | @property(assign)TaskTableController *delegate; 25 | 26 | //File object 27 | @property(nonatomic, retain)Binary* binary; 28 | 29 | //button's row index 30 | 31 | //flag indicating press 32 | @property BOOL mouseDown; 33 | 34 | //flag indicating exit 35 | @property BOOL mouseExit; 36 | 37 | 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /VTButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // vtButton.m 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 3/26/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import "Consts.h" 10 | #import "VTButton.h" 11 | #import "Utilities.h" 12 | #import "TaskTableController.h" 13 | 14 | @implementation VTButton 15 | 16 | @synthesize binary; 17 | @synthesize delegate; 18 | @synthesize mouseDown; 19 | @synthesize mouseExit; 20 | 21 | //automatically invoked 22 | // ->create tracking area for mouse events 23 | -(void)awakeFromNib 24 | { 25 | //create the mouse-over tracking area 26 | [self createTrackingArea]; 27 | } 28 | 29 | //create the mouse-over tracking area 30 | -(void)createTrackingArea 31 | { 32 | //tracking area 33 | NSTrackingArea *trackingArea = nil; 34 | 35 | //alloc/init tracking area 36 | trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:(NSTrackingInVisibleRect | NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways) owner:self userInfo:nil]; 37 | 38 | //add tracking area 39 | [self addTrackingArea:trackingArea]; 40 | 41 | return; 42 | } 43 | 44 | //automatically invoked when mouse-down occurs 45 | // ->set color to default or red 46 | -(void)mouseDown:(NSEvent *)theEvent; 47 | { 48 | //mouse down/over color 49 | NSColor* color = nil; 50 | 51 | //set flag 52 | self.mouseDown = YES; 53 | 54 | //flagged files 55 | // ->make em red! 56 | if( (nil != self.binary.vtInfo) && 57 | (0 != [self.binary.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) 58 | { 59 | //set color (light red) 60 | color = [NSColor colorWithCalibratedRed:(255/255.0f) green:(1.0/255.0f) blue:(1.0/255.0f) alpha:0.5]; 61 | } 62 | //non-flagged files 63 | else 64 | { 65 | //default 66 | color = NSColor.controlTextColor; 67 | } 68 | 69 | //set string 70 | [self setAttributedTitle:setStringColor(self.attributedTitle, color)]; 71 | 72 | return; 73 | } 74 | 75 | //automatically invoked when mouse-up occurs 76 | // ->reset color to default or red and trigger mouse click logic (if necessary) 77 | -(void)mouseUp:(NSEvent *)theEvent; 78 | { 79 | //mouse up color 80 | NSColor* color = nil; 81 | 82 | //shoud treat at click? 83 | // ->mouse up inside in non-disabled button 84 | if( (YES == self.isEnabled) && 85 | (YES != self.mouseExit) ) 86 | { 87 | //show virus total window 88 | [self.delegate performSelector:@selector(showVTInfo:) withObject:self]; 89 | } 90 | 91 | //reset flag 92 | self.mouseDown = NO; 93 | 94 | //flagged files 95 | // ->make em red! 96 | if( (nil != self.binary.vtInfo) && 97 | (0 != [self.binary.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) 98 | { 99 | //set color 100 | color = [NSColor redColor]; 101 | } 102 | //non-flagged files 103 | // set (back) to default 104 | else 105 | { 106 | color = NSColor.controlTextColor; 107 | } 108 | 109 | //set string 110 | [self setAttributedTitle:setStringColor(self.attributedTitle, color)]; 111 | 112 | return; 113 | } 114 | 115 | //automatically invoked when mouse enters 116 | // ->set mouse over color 117 | -(void)mouseEntered:(NSEvent*)theEvent 118 | { 119 | //mouse entered color 120 | NSColor* color = nil; 121 | 122 | //set flag 123 | self.mouseExit = NO; 124 | 125 | //flagged files 126 | // ->make em red! 127 | if( (nil != self.binary.vtInfo) && 128 | (0 != [self.binary.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) 129 | { 130 | //set color (lightish red) 131 | color = [NSColor colorWithCalibratedRed:(255/255.0f) green:(1.0/255.0f) blue:(1.0/255.0f) alpha:0.66]; 132 | } 133 | //non-flagged files 134 | // set (back) to default 135 | else 136 | { 137 | //default 138 | color = NSColor.controlTextColor; 139 | } 140 | 141 | //set string 142 | [self setAttributedTitle:setStringColor(self.attributedTitle, color)]; 143 | 144 | return; 145 | } 146 | 147 | //automatically invoked when mouse exits 148 | // ->reset color to black or red 149 | -(void)mouseExited:(NSEvent*)theEvent 150 | { 151 | //mouse exit color 152 | NSColor* color = nil; 153 | 154 | //set flag 155 | self.mouseExit = YES; 156 | 157 | //check if mouse is down 158 | // ->set color to default/red 159 | if(YES == self.mouseDown) 160 | { 161 | //flagged files 162 | // ->make em red! 163 | if( (nil != self.binary.vtInfo) && 164 | (0 != [self.binary.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) 165 | { 166 | //set color (lightish red) 167 | color = [NSColor colorWithCalibratedRed:(255/255.0f) green:(1.0/255.0f) blue:(1.0/255.0f) alpha:0.66]; 168 | } 169 | //non-flagged files 170 | else 171 | { 172 | //default 173 | color = NSColor.controlTextColor; 174 | } 175 | } 176 | //mouse is up 177 | // ->reset color to black/red 178 | else 179 | { 180 | //flagged files 181 | // ->make em red! 182 | if( (nil != self.binary.vtInfo) && 183 | (0 != [self.binary.vtInfo[VT_RESULTS_POSITIVES] unsignedIntegerValue]) ) 184 | { 185 | //set color (light red) 186 | color = [NSColor redColor]; 187 | } 188 | //non-flagged files 189 | // set (back) to default 190 | else 191 | { 192 | //default 193 | color = NSColor.controlTextColor; 194 | } 195 | } 196 | 197 | //set string 198 | [self setAttributedTitle:setStringColor(self.attributedTitle, color)]; 199 | 200 | return; 201 | } 202 | 203 | @end 204 | -------------------------------------------------------------------------------- /VTInfoWindowController.h: -------------------------------------------------------------------------------- 1 | // 2 | // VTInfoWindow.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 3/29/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | @class File; 10 | @class HyperlinkTextField; 11 | 12 | #import 13 | 14 | @interface VTInfoWindowController : NSWindowController 15 | { 16 | 17 | } 18 | 19 | /* PROPERTIES */ 20 | 21 | //window controller 22 | @property(nonatomic, strong)VTInfoWindowController *windowController; 23 | 24 | //file object 25 | @property(nonatomic, retain)Binary* item; 26 | 27 | //row index 28 | //@property NSUInteger rowIndex; 29 | 30 | //properties in window 31 | @property (weak) IBOutlet NSTextField *unknownFile; 32 | 33 | @property (weak) IBOutlet NSTextField *fileNameLabel; 34 | @property (weak) IBOutlet HyperlinkTextField *fileName; 35 | 36 | @property (weak) IBOutlet NSTextField *detectionRatioLabel; 37 | @property (weak) IBOutlet NSTextField *detectionRatio; 38 | 39 | @property (weak) IBOutlet NSTextField *analysisURLLabel; 40 | @property (weak) IBOutlet NSTextField *analysisURL; 41 | 42 | @property (weak) IBOutlet NSButton *closeButton; 43 | 44 | @property (weak) IBOutlet NSButton *submitButton; 45 | @property (weak) IBOutlet NSProgressIndicator *progressIndicator; 46 | @property (strong) IBOutlet NSView *overlayView; 47 | @property (weak) IBOutlet NSTextField *statusMsg; 48 | 49 | /* METHODS */ 50 | 51 | //init method 52 | // ->save item and load nib 53 | -(id)initWithItem:(Binary*)binary; 54 | 55 | //'submit' button handler 56 | -(IBAction)vtButtonHandler:(id)sender; 57 | 58 | //'close' button handler 59 | -(IBAction)closeButtonHandler:(id)sender; 60 | 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /VirusTotal.h: -------------------------------------------------------------------------------- 1 | // 2 | // VirusTotal.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 3/8/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import "Binary.h" 10 | #import 11 | 12 | /* GLOBALS */ 13 | 14 | //cmdline flag 15 | extern BOOL cmdlineMode; 16 | 17 | @interface VirusTotal : NSObject 18 | { 19 | 20 | } 21 | 22 | /* PROPERTIES */ 23 | 24 | //array for (up to 25) items 25 | @property(nonatomic, retain)NSMutableArray* items; 26 | 27 | //array for all threads 28 | @property(nonatomic, retain)NSMutableArray* vtThreads; 29 | 30 | /* METHODS */ 31 | 32 | //add item 33 | // ->will query VT when 25 items are hit 34 | -(void)addItem:(Binary*)binary; 35 | 36 | //make the (POST)query to VT 37 | -(NSDictionary*)postRequest:(NSURL*)url parameters:(id)params; 38 | 39 | //submit a file to VT 40 | -(NSDictionary*)submit:(Binary*)item; 41 | 42 | //submit a rescan request 43 | -(NSDictionary*)reScan:(Binary*)item; 44 | 45 | //process results 46 | // ->updates items (found, detection ratio, etc) 47 | -(void)processResults:(NSMutableDictionary*)queriedItems results:(NSDictionary*)results; 48 | 49 | //get info for a single item 50 | // ->will callback into AppDelegate to reload item 51 | -(void)getInfoForItem:(Binary*)item scanID:(NSString*)scanID; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | TASKEXPLORER CHANGELOG 2 | 3 | 4 | VERSION 1.2.0 (10/11/2015) 5 | el capitan/rootless compatibility 6 | autocomplete search/filter 7 | keyboard shortcuts (cmd+s, cmd+r, cmd+f, cmd+w) 8 | network results included in global search results 9 | xpc-comms refactor (major speed improvement!) 10 | ui fixes/improvements 11 | 12 | 13 | VERSION 1.1.0 (8/23/2015) 14 | added a global search window (tasks, dylibs, files) 15 | added a flagged items window for any flagged tasks or dylibs 16 | xpc-helper's security improved by allowing only Objective-See binary to connect 17 | ui fixes/improvements 18 | 19 | 20 | VERSION 1.0.1 (8/5/2015) 21 | fixed crash on Mavericks (NSSearchField/setPlaceholderString:) 22 | 23 | 24 | VERSION 1.0.0 (8/5/2015) 25 | initial release -------------------------------------------------------------------------------- /en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /images/Icon Template.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/Icon Template.psd -------------------------------------------------------------------------------- /images/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/arrow.png -------------------------------------------------------------------------------- /images/authorizationIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/authorizationIcon.png -------------------------------------------------------------------------------- /images/browserIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/browserIcon.png -------------------------------------------------------------------------------- /images/bug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/bug.png -------------------------------------------------------------------------------- /images/closeWait.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/closeWait.png -------------------------------------------------------------------------------- /images/closedIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/closedIcon.png -------------------------------------------------------------------------------- /images/connectedIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/connectedIcon.png -------------------------------------------------------------------------------- /images/dhsText.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/dhsText.png -------------------------------------------------------------------------------- /images/dylibIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/dylibIcon.png -------------------------------------------------------------------------------- /images/flagged.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/flagged.png -------------------------------------------------------------------------------- /images/flaggedBG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/flaggedBG.png -------------------------------------------------------------------------------- /images/flaggedOver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/flaggedOver.png -------------------------------------------------------------------------------- /images/flaggedRed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/flaggedRed.png -------------------------------------------------------------------------------- /images/flaggedRedBG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/flaggedRedBG.png -------------------------------------------------------------------------------- /images/flaggedRedOver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/flaggedRedOver.png -------------------------------------------------------------------------------- /images/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/info.png -------------------------------------------------------------------------------- /images/infoBG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/infoBG.png -------------------------------------------------------------------------------- /images/infoOver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/infoOver.png -------------------------------------------------------------------------------- /images/kernelIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/kernelIcon.png -------------------------------------------------------------------------------- /images/launchIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/launchIcon.png -------------------------------------------------------------------------------- /images/listeningIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/listeningIcon.png -------------------------------------------------------------------------------- /images/lockIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/lockIcon.png -------------------------------------------------------------------------------- /images/loginIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/loginIcon.png -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/logo.png -------------------------------------------------------------------------------- /images/logoApple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/logoApple.png -------------------------------------------------------------------------------- /images/logoAppleBG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/logoAppleBG.png -------------------------------------------------------------------------------- /images/logoAppleOver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/logoAppleOver.png -------------------------------------------------------------------------------- /images/refreshIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/refreshIcon.png -------------------------------------------------------------------------------- /images/refreshIconBG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/refreshIconBG.png -------------------------------------------------------------------------------- /images/refreshIconOver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/refreshIconOver.png -------------------------------------------------------------------------------- /images/saveIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/saveIcon.png -------------------------------------------------------------------------------- /images/saveIconBG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/saveIconBG.png -------------------------------------------------------------------------------- /images/saveIconOver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/saveIconOver.png -------------------------------------------------------------------------------- /images/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/search.png -------------------------------------------------------------------------------- /images/searchBG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/searchBG.png -------------------------------------------------------------------------------- /images/searchOver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/searchOver.png -------------------------------------------------------------------------------- /images/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/settings.png -------------------------------------------------------------------------------- /images/settingsBG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/settingsBG.png -------------------------------------------------------------------------------- /images/settingsOver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/settingsOver.png -------------------------------------------------------------------------------- /images/show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/show.png -------------------------------------------------------------------------------- /images/showBG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/showBG.png -------------------------------------------------------------------------------- /images/showOver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/showOver.png -------------------------------------------------------------------------------- /images/signed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/signed.png -------------------------------------------------------------------------------- /images/signed2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/signed2.png -------------------------------------------------------------------------------- /images/signedAppleIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/signedAppleIcon.png -------------------------------------------------------------------------------- /images/spotlightIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/spotlightIcon.png -------------------------------------------------------------------------------- /images/startScan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/startScan.png -------------------------------------------------------------------------------- /images/startScanBG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/startScanBG.png -------------------------------------------------------------------------------- /images/startScanOver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/startScanOver.png -------------------------------------------------------------------------------- /images/stopScan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/stopScan.png -------------------------------------------------------------------------------- /images/stopScanBG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/stopScanBG.png -------------------------------------------------------------------------------- /images/stopScanOver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/stopScanOver.png -------------------------------------------------------------------------------- /images/streamIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/streamIcon.png -------------------------------------------------------------------------------- /images/teIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/teIcon.png -------------------------------------------------------------------------------- /images/teText.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/teText.ai -------------------------------------------------------------------------------- /images/teText.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/teText.png -------------------------------------------------------------------------------- /images/unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/unknown.png -------------------------------------------------------------------------------- /images/unsigned.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/unsigned.png -------------------------------------------------------------------------------- /images/unsigned2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/unsigned2.png -------------------------------------------------------------------------------- /images/unsignedBlack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/unsignedBlack.png -------------------------------------------------------------------------------- /images/virus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/virus.png -------------------------------------------------------------------------------- /images/virusTotal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/virusTotal.png -------------------------------------------------------------------------------- /images/virusTotalBG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/virusTotalBG.png -------------------------------------------------------------------------------- /images/vtLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/objective-see/TaskExplorer/c8a0f6498f27ede99613824c6ad0880e6e4159e7/images/vtLogo.png -------------------------------------------------------------------------------- /kkRowCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // kkRowCell.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 4/6/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface kkRowCell : NSTableCellView 12 | { 13 | 14 | } 15 | 16 | /* PROPERTIES */ 17 | 18 | //tag 19 | @property NSInteger tag; 20 | 21 | //color 22 | @property(nonatomic, retain)NSColor* color; 23 | 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /kkRowCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // kkRowCell.m 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 4/6/15. 6 | // Copyright (c) 2015 Objective-See. All rights reserved. 7 | // 8 | 9 | #import "kkRowCell.h" 10 | 11 | @implementation kkRowCell 12 | 13 | @synthesize tag; 14 | 15 | //draw method 16 | - (void)drawRect:(NSRect)dirtyRect 17 | { 18 | //draw custom color 19 | if(nil != self.color) 20 | { 21 | //set color 22 | [self.color set]; 23 | 24 | //fill 25 | NSRectFill([self bounds]); 26 | } 27 | } 28 | 29 | //set background color 30 | // ->always light! 31 | - (void)setBackgroundStyle:(NSBackgroundStyle)backgroundStyle 32 | { 33 | [super setBackgroundStyle: NSBackgroundStyleLight]; 34 | } 35 | 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | // 2 | // main.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 12/16/18. 6 | // 7 | 8 | #ifndef main_h 9 | #define main_h 10 | 11 | #import "Consts.h" 12 | #import "Filter.h" 13 | #import "Utilities.h" 14 | #import "VirusTotal.h" 15 | #import "AppDelegate.h" 16 | 17 | #import "TaskEnumerator.h" 18 | 19 | #import 20 | 21 | //(privacy) protected directories 22 | 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"}; 23 | 24 | /* GLOBALS */ 25 | 26 | //task enumerator obj 27 | TaskEnumerator* taskEnumerator = nil; 28 | 29 | //virustotal obj 30 | VirusTotal* virusTotal = nil; 31 | 32 | //network connected flag 33 | BOOL isConnected = NO; 34 | 35 | //cmdline flag 36 | BOOL cmdlineMode = NO; 37 | 38 | //(privacy) protected directories 39 | NSArray* protectedDirectories = nil; 40 | 41 | /* FUNCTIONS */ 42 | 43 | //print usage 44 | void usage(void); 45 | 46 | //perform a cmdline actions 47 | void cmdlineInterface(void); 48 | 49 | //block until vt queries are done 50 | void completeVTQuery(void); 51 | 52 | //pretty print JSON 53 | void prettyPrintJSON(NSString* output); 54 | 55 | #endif /* main_h */ 56 | -------------------------------------------------------------------------------- /patrons.txt: -------------------------------------------------------------------------------- 1 | Patrons (2^6+): 2 | Jan Koum, Matt Mullenweg, Christian Blümlein, Shain Singh, Andreas Fink, Nuno, Rabi Rob Thomas, Mikhail S. 3 | 4 | Friends of Objective-See: 5 | Kandji, Jamf, CleanMyMac X, Palo Alto Networks, iVerify, Huntress, SmugMug, Guardian Mobile Firewall, Halo Privacy, The Mitten Mac 6 | -------------------------------------------------------------------------------- /remoteTaskService/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | remoteTaskService 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 2.1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 2.1.0 25 | NSHumanReadableCopyright 26 | Copyright © 2025 Objective-See, LLC. All rights reserved. 27 | XPCService 28 | 29 | ServiceType 30 | Application 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /remoteTaskService/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // remoteTaskService 4 | // 5 | // Created by Patrick Wardle on 5/27/15. 6 | // Copyright (c) 2015 Objective-See, LLC. All rights reserved. 7 | // 8 | 9 | #import "../Utilities.h" 10 | #import "serviceInterface.h" 11 | #import "remoteTaskService.h" 12 | 13 | 14 | #import 15 | #import 16 | #import 17 | #import 18 | 19 | //interface for 'extension' to NSXPCConnection 20 | // ->allows us to access the 'private' auditToken iVar 21 | @interface ExtendedNSXPCConnection : NSXPCConnection 22 | { 23 | //private iVar 24 | audit_token_t auditToken; 25 | } 26 | //private iVar 27 | @property audit_token_t auditToken; 28 | 29 | @end 30 | 31 | //implementation for 'extension' to NSXPCConnection 32 | // ->allows us to access the 'private' auditToken iVar 33 | @implementation ExtendedNSXPCConnection 34 | 35 | //private iVar 36 | @synthesize auditToken; 37 | 38 | @end 39 | 40 | //function def 41 | OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement); 42 | 43 | //signing auth 44 | #define SIGNING_AUTH @"Developer ID Application: Objective-See, LLC (VBG97UB4TA)" 45 | 46 | //skeleton interface 47 | @interface ServiceDelegate : NSObject 48 | @end 49 | 50 | @implementation ServiceDelegate 51 | 52 | //automatically invoked 53 | //->allows NSXPCListener to configure/accept/resume a new incoming NSXPCConnection 54 | // note: we only allow binaries signed by Objective-See to talk to this! 55 | -(BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection 56 | { 57 | //flag 58 | BOOL shouldAccept = NO; 59 | 60 | //task ref 61 | SecTaskRef taskRef = 0; 62 | 63 | //signing req string 64 | NSString *requirementString = nil; 65 | 66 | //init signing req string 67 | requirementString = [NSString stringWithFormat:@"anchor trusted and certificate leaf [subject.CN] = \"%@\"", SIGNING_AUTH]; 68 | 69 | //step 1: create task ref 70 | // ->uses NSXPCConnection's (private) 'auditToken' iVar 71 | taskRef = SecTaskCreateWithAuditToken(NULL, ((ExtendedNSXPCConnection*)newConnection).auditToken); 72 | if(NULL == taskRef) 73 | { 74 | //bail 75 | goto bail; 76 | } 77 | 78 | //step 2: validate 79 | // ->check that client is signed with Objective-See's dev cert 80 | if(0 != SecTaskValidateForRequirement(taskRef, (__bridge CFStringRef)(requirementString))) 81 | { 82 | //bail 83 | goto bail; 84 | } 85 | 86 | //set the interface that the exported object implements 87 | newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(remoteTaskProto)]; 88 | 89 | //set object exported by connection 90 | newConnection.exportedObject = [remoteTaskService new]; 91 | 92 | //resume 93 | [newConnection resume]; 94 | 95 | //happy 96 | shouldAccept = YES; 97 | 98 | //bail 99 | bail: 100 | 101 | //release task ref object 102 | if(NULL != taskRef) 103 | { 104 | //release 105 | CFRelease(taskRef); 106 | 107 | //unset 108 | taskRef = NULL; 109 | } 110 | 111 | return shouldAccept; 112 | } 113 | 114 | @end 115 | 116 | //main entrypoint 117 | // ->install exception handlers & setup/kickoff listener 118 | int main(int argc, const char *argv[]) 119 | { 120 | //ret var 121 | int status = -1; 122 | 123 | //service delegate 124 | ServiceDelegate* delegate = nil; 125 | 126 | //listener 127 | NSXPCListener* listener = nil; 128 | 129 | //make really r00t 130 | // ->needed for exec'ing vmmap, etc 131 | if(0 != setuid(0)) 132 | { 133 | //err msg 134 | syslog(LOG_ERR, "OBJECTIVE-SEE TASKEXPLORER ERROR: setuid() failed with: %d\n", errno); 135 | 136 | //bail 137 | goto bail; 138 | } 139 | 140 | //create the delegate for the service. 141 | delegate = [ServiceDelegate new]; 142 | 143 | //set up the one NSXPCListener for this service 144 | // ->handles incoming connections 145 | listener = [NSXPCListener serviceListener]; 146 | 147 | //set delegate 148 | listener.delegate = delegate; 149 | 150 | //resuming the listener starts this service 151 | // ->method does not return 152 | [listener resume]; 153 | 154 | //happy 155 | status = 0; 156 | 157 | //bail 158 | bail: 159 | 160 | return status; 161 | } 162 | -------------------------------------------------------------------------------- /remoteTaskService/remoteTaskService.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /remoteTaskService/remoteTaskService.h: -------------------------------------------------------------------------------- 1 | // 2 | // remoteTaskService.h 3 | // remoteTaskService 4 | // 5 | // Created by Patrick Wardle on 5/27/15. 6 | // Copyright (c) 2015 Objective-See, LLC. All rights reserved. 7 | // 8 | 9 | #import "serviceInterface.h" 10 | 11 | #import 12 | 13 | 14 | // This object implements the protocol which we have defined. It provides the actual behavior for the service. It is 'exported' by the service to make it available to the process hosting the service over an NSXPCConnection. 15 | @interface remoteTaskService : NSObject 16 | 17 | //default service 18 | +(remoteTaskService *)defaultService; 19 | 20 | @end 21 | 22 | -------------------------------------------------------------------------------- /serviceInterface.h: -------------------------------------------------------------------------------- 1 | // 2 | // serviceInterface.h 3 | // TaskExplorer 4 | // 5 | // Created by Patrick Wardle on 5/27/15. 6 | // Copyright (c) 2015 Objective-See, LLC. All rights reserved. 7 | // 8 | 9 | #ifndef TaskExplorer_serviceInterface_h 10 | #define TaskExplorer_serviceInterface_h 11 | 12 | #import "Task.h" 13 | 14 | @protocol remoteTaskProto 15 | 16 | //get task's commandline args 17 | // ->args returned in array arg 18 | -(void)getTaskArgs:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply; 19 | 20 | //enumerate loaded dylibs in a task 21 | -(void)enumerateDylibs:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply; 22 | 23 | //enumerate open files in a task 24 | -(void)enumerateFiles:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply; 25 | 26 | //enumerate network connections 27 | -(void)enumerateNetwork:(NSNumber*)taskPID withReply:(void (^)(NSMutableArray *))reply; 28 | 29 | @end 30 | 31 | #endif 32 | --------------------------------------------------------------------------------