From 78ea2d11c4d7ea674e4db2aeb287eeff74153cf7 Mon Sep 17 00:00:00 2001
From: Patrick Wardle <patrick@objective-see.com>
Date: Sun, 18 Sep 2016 11:10:44 -1000
Subject: [PATCH] get process name (from app bundle) delete sample's output
 file made find camera/mic instance methods fixed icons for main app code
 cleanup/TODOs

---
 Installer/Configure.m               |  38 +++-----
 LoginItem/AVMonitor.m               | 135 +++++++++++++---------------
 MainApp/Info.plist                  |   6 +-
 OverSight.xcodeproj/project.pbxproj |  12 ++-
 OverSightXPC/Enumerator.m           |  69 +++++++++++++-
 Shared/Exception.m                  |  10 +--
 Shared/Logging.m                    |   3 -
 Shared/Utilities.h                  |  17 +---
 Shared/Utilities.m                  |  49 +++++++++-
 9 files changed, 202 insertions(+), 137 deletions(-)

diff --git a/Installer/Configure.m b/Installer/Configure.m
index 5e19586..63a9935 100644
--- a/Installer/Configure.m
+++ b/Installer/Configure.m
@@ -34,7 +34,11 @@
         if(YES == [self isInstalled])
         {
             //dbg msg
-            logMsg(LOG_DEBUG, @"already installed, so uninstalling...");
+            logMsg(LOG_DEBUG, @"already installed, so stopping/uninstalling...");
+            
+            //stop
+            // ->kill login item/XPC service
+            [self stop];
             
             //uninstall
             if(YES != [self uninstall])
@@ -43,10 +47,6 @@
                 goto bail;
             }
             
-            //and stop
-            //TODO: erorr checking
-            [self stop];
-            
             //dbg msg
             logMsg(LOG_DEBUG, @"uninstalled");
         }
@@ -81,16 +81,9 @@
         //dbg msg
         logMsg(LOG_DEBUG, @"stopping login item");
         
-        //stop login item/XPC service
-        if(YES != [self stop])
-        {
-            //err msg
-            logMsg(LOG_ERR, @"stopping failed");
-            
-            //bail
-            goto bail;
-        }
-    
+        //stop
+        // ->kill login item/XPC service
+        [self stop];
          
         //dbg msg
         logMsg(LOG_DEBUG, @"uninstalling...");
@@ -228,22 +221,13 @@ bail:
 }
 
 //stop
--(BOOL)stop
+-(void)stop
 {
-    //flag
-    BOOL bStopped = NO;
-    
     //kill it
     // pkill doesn't provide error info, so...
     execTask(PKILL, @[APP_HELPER_NAME]);
-    
-    //happy
-    bStopped = YES;
-    
-//bail
-bail:
-    
-    return bStopped;
+
+    return;
 }
 
 //uninstall
diff --git a/LoginItem/AVMonitor.m b/LoginItem/AVMonitor.m
index 82e593f..3ede48f 100644
--- a/LoginItem/AVMonitor.m
+++ b/LoginItem/AVMonitor.m
@@ -14,67 +14,6 @@
 
 #import "../Shared/XPCProtocol.h"
 
-//TODO: make instance methods?!
-
-//grab first apple camera
-AVCaptureDevice* findAppleCamera()
-{
-    //apple camera
-    // ->likely FaceTime camera
-    AVCaptureDevice* appleCamera = nil;
-    
-    //list of cameras
-    NSArray *cameras = nil;
-    
-    //get cameras
-    cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
-    for(AVCaptureDevice* camera in cameras)
-    {
-        //check if apple
-        if(YES == [camera.manufacturer isEqualToString:@"Apple Inc."])
-        {
-            //save
-            appleCamera = camera;
-            
-            //exit loop
-            break;
-        }
-    }
-    
-    return appleCamera;
-}
-
-//grab built-in mic
-AVCaptureDevice* findAppleMic()
-{
-    //built-in mic
-    AVCaptureDevice* appleMic = nil;
-    
-    //list of mics
-    NSArray *mics = nil;
-    
-    //get mics
-    mics = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio];
-    for(AVCaptureDevice* mic in mics)
-    {
-        //check if apple
-        // ->also check input source
-        if( (YES == [mic.manufacturer isEqualToString:@"Apple Inc."]) &&
-            (YES == [[[mic activeInputSource] inputSourceID] isEqualToString:@"imic"]) )
-        {
-            //save
-            appleMic = mic;
-            
-            //exit loop
-            break;
-        }
-        
-    }
-    
-    return appleMic;
-}
-
-
 @implementation AVMonitor
 
 @synthesize mic;
@@ -96,6 +35,54 @@ AVCaptureDevice* findAppleMic()
     return self;
 }
 
+//grab first apple camera
+// ->saves into iVar 'camera'
+// note: could maybe use defaultDeviceWithDeviceType method() to default device...
+-(void)findAppleCamera
+{
+    //get cameras
+    // ->look for one that belongs to apple
+    for(AVCaptureDevice* currentCamera in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo])
+    {
+        //check if apple
+        if(YES == [currentCamera.manufacturer isEqualToString:@"Apple Inc."])
+        {
+            //save
+            self.camera = currentCamera;
+            
+            //exit loop
+            break;
+        }
+    }
+    
+    return;
+}
+
+//grab first apple mic
+// ->saves into iVar 'mic'
+-(void)findAppleMic
+{
+    //get mics
+    // ->loof for one that belongs to app
+    for(AVCaptureDevice* currentMic in [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio])
+    {
+        //check if apple
+        // ->also check input source
+        if( (YES == [currentMic.manufacturer isEqualToString:@"Apple Inc."]) &&
+            (YES == [[[currentMic activeInputSource] inputSourceID] isEqualToString:@"imic"]) )
+        {
+            //save
+            self.mic = currentMic;
+            
+            //exit loop
+            break;
+        }
+        
+    }
+    
+    return;
+}
+
 //initialiaze AV notifcations/callbacks
 -(BOOL)monitor
 {
@@ -155,13 +142,12 @@ AVCaptureDevice* findAppleMic()
     methodSelector = NSSelectorFromString(@"connectionID");
     
     //find (first) apple camera
-    self.camera = findAppleCamera();
+    // ->saves camera into iVar, 'camera'
+    [self findAppleCamera];
     
-    //find built in mic
-    self.mic = findAppleMic();
-    
-    //dbg msg
-    logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found mic: %@", self.mic]);
+    //find (first) apple mic
+    // ->saves mic into iVar, 'mic'
+    [self findAppleMic];
     
     //got camera
     // ->grab connection ID and invoke helper functions
@@ -522,7 +508,7 @@ bail:
     if(noErr != status)
     {
         //err msg
-        //TODO: add
+        logMsg(LOG_ERR, [NSString stringWithFormat:@"CMIOObjectAddPropertyListenerBlock() failed with %d", status]);
         
         //bail
         goto bail;
@@ -737,23 +723,22 @@ bail:
     // ->for activatated video; allow/block
     else
     {
+        //get process name
+        processName = getProcessName([event[EVENT_PROCESS_ID] intValue]);
+        
         //set other button title
         notification.otherButtonTitle = @"allow";
         
         //set action title
         notification.actionButtonTitle = @"block";
         
-        //get process name
-        // TODO: see 'determineName' in BB (to get name from bundle, etc)
-        processName = [getProcessPath([event[EVENT_PROCESS_ID] intValue]) lastPathComponent];
-        
         //set pid in user info
         // ->allows code to try kill proc (later) if user clicks 'block'
         notification.userInfo = @{EVENT_PROCESS_ID:event[EVENT_PROCESS_ID]};
         
         //set details
         // ->name of process using it / icon too?
-        [notification setInformativeText:[NSString stringWithFormat:@"%@ (%@)", processName, event[EVENT_PROCESS_ID]]];
+        [notification setInformativeText:[NSString stringWithFormat:@"process: %@ (%@)", processName, event[EVENT_PROCESS_ID]]];
     }
 
     //log event?
@@ -772,11 +757,11 @@ bail:
         }
         
         //process
-        // ->add title / details / process
+        // ->add title / details / process path
         else
         {
             //add
-            [logMsg appendFormat:@"%@ (%@, %@)", title, details, processName];
+            [logMsg appendFormat:@"%@ (process: %@, %@)", title, details, getProcessPath([event[EVENT_PROCESS_ID] intValue])];
         }
         
         //write it out to syslog
diff --git a/MainApp/Info.plist b/MainApp/Info.plist
index 79b6bf7..42e9bed 100644
--- a/MainApp/Info.plist
+++ b/MainApp/Info.plist
@@ -6,8 +6,6 @@
 	<string>en</string>
 	<key>CFBundleExecutable</key>
 	<string>$(EXECUTABLE_NAME)</string>
-	<key>CFBundleIconFile</key>
-	<string></string>
 	<key>CFBundleIdentifier</key>
 	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
 	<key>CFBundleInfoDictionaryVersion</key>
@@ -24,12 +22,12 @@
 	<string>1.0.0</string>
 	<key>LSMinimumSystemVersion</key>
 	<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
+	<key>LSUIElement</key>
+	<true/>
 	<key>NSHumanReadableCopyright</key>
 	<string>Copyright (c) 2016 Objective-See. All rights reserved.</string>
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
-	<key>LSUIElement</key>
-	<true/>
 	<key>NSPrincipalClass</key>
 	<string>NSApplication</string>
 </dict>
diff --git a/OverSight.xcodeproj/project.pbxproj b/OverSight.xcodeproj/project.pbxproj
index 6fe27c1..27dec0d 100644
--- a/OverSight.xcodeproj/project.pbxproj
+++ b/OverSight.xcodeproj/project.pbxproj
@@ -33,6 +33,7 @@
 		7D9A7DE81D893E4F0091C1AF /* InfoWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D62458C1D87D38400870565 /* InfoWindow.xib */; };
 		7D9A7DEC1D8BE1E00091C1AF /* Exception.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C5311D659E580066232A /* Exception.m */; };
 		7D9A7DEE1D8CACE30091C1AF /* libbsm.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D9A7DED1D8CACE30091C1AF /* libbsm.tbd */; };
+		7D9A7DF21D8F2C900091C1AF /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7D6245841D87C43900870565 /* Images.xcassets */; };
 		7DAF4B7F1D657192000DA31A /* StatusBarMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DAF4B7D1D656FD3000DA31A /* StatusBarMenu.m */; };
 		7DC9C8171D641A350017D143 /* OverSightXPC.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DC9C8161D641A350017D143 /* OverSightXPC.m */; };
 		7DC9C8191D641A350017D143 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DC9C8181D641A350017D143 /* main.m */; };
@@ -166,7 +167,7 @@
 		7D17C5131D658FE20066232A /* Images */ = {
 			isa = PBXGroup;
 			children = (
-				7D17CFE21D81121E0017B475 /* AVMonitor.h */,
+				8B5755C819DA3F9300799E6B /* AppDelegate.h */,
 				7D17C5141D658FEB0066232A /* statusIcon.png */,
 				7D17C5151D658FEB0066232A /* statusIcon@2x.png */,
 			);
@@ -276,11 +277,11 @@
 			isa = PBXGroup;
 			children = (
 				7D17C5131D658FE20066232A /* Images */,
+				8B5755C919DA3F9300799E6B /* AppDelegate.m */,
+				7D17CFE21D81121E0017B475 /* AVMonitor.h */,
+				7D17CFE11D81121E0017B475 /* AVMonitor.m */,
 				7DAF4B7C1D656FD3000DA31A /* StatusBarMenu.h */,
 				7DAF4B7D1D656FD3000DA31A /* StatusBarMenu.m */,
-				8B5755C819DA3F9300799E6B /* AppDelegate.h */,
-				8B5755C919DA3F9300799E6B /* AppDelegate.m */,
-				7D17CFE11D81121E0017B475 /* AVMonitor.m */,
 				8B5755CD19DA3F9300799E6B /* MainMenu.xib */,
 				8B5755C419DA3F9300799E6B /* Supporting Files */,
 			);
@@ -419,6 +420,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				7D9A7DF21D8F2C900091C1AF /* Images.xcassets in Resources */,
 				7D6245921D87D46800870565 /* InfoWindow.xib in Resources */,
 				7D17C53C1D659E580066232A /* icon.png in Resources */,
 				8B5755A919DA3E9500799E6B /* MainMenu.xib in Resources */,
@@ -629,6 +631,7 @@
 		8B5755B919DA3E9500799E6B /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CODE_SIGN_IDENTITY = "Developer ID Application";
 				"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
 				COMBINE_HIDPI_IMAGES = YES;
@@ -644,6 +647,7 @@
 		8B5755BA19DA3E9500799E6B /* Release */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CODE_SIGN_IDENTITY = "Developer ID Application";
 				"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
 				COMBINE_HIDPI_IMAGES = YES;
diff --git a/OverSightXPC/Enumerator.m b/OverSightXPC/Enumerator.m
index b0d1189..a9e7f39 100644
--- a/OverSightXPC/Enumerator.m
+++ b/OverSightXPC/Enumerator.m
@@ -309,7 +309,6 @@ bail:
         }
         
         //parse on '()'
-        // TODO: improve this!
         subStrings = [line componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"()"]];
         if(subStrings.count < 3)
         {
@@ -371,10 +370,10 @@ bail:
     videoProcs = [NSMutableArray array];
     
     //invoke 'sample' on each
-    // TODO: delete tmp file? 'Sample analysis of process 37370 written to file /tmp/FaceTime_2016-09-10_081703_TAwB.sample.txt' (written 2 std err?)
     for(NSNumber* processID in currentSenders)
     {
         //exec 'sample' to get threads/dylibs
+        // ->uses 1.0 seconds for sampling time
         results = [[NSString alloc] initWithData:execTask(SAMPLE, @[processID.stringValue, @"1"]) encoding:NSUTF8StringEncoding];
         if( (nil == results) ||
             (0 == results.length) )
@@ -382,7 +381,11 @@ bail:
             //skip
             continue;
         }
-
+        
+        //sampling a process creates a temp file
+        //->delete it!
+        [self deleteSampleFile:getProcessPath(processID.intValue)];
+        
         //for now, just check for 'CMIOGraph::DoWork'
         // ->TODO: could look for dylibs, other calls, etc
         if(YES != [results containsString:@"CMIOGraph::DoWork"])
@@ -398,6 +401,66 @@ bail:
     return videoProcs;
 }
 
+//'sample' binary creates a file
+// ->this looks for that file and deletes it
+-(void)deleteSampleFile:(NSString*)processPath
+{
+    //error
+    NSError* error = nil;
+    
+    //files
+    NSArray* files = nil;
+    
+    //grab all files in /tmp
+    files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"/tmp/" error:&error];
+    if(nil != error)
+    {
+        //err msg
+        logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to enumerate files in /tmp, %@", error]);
+        
+        //bail
+        goto bail;
+    }
+    
+    //find/delete file
+    for(NSString* file in files)
+    {
+        //skip non-sample files
+        if(YES != [file hasSuffix:@".sample.txt"])
+        {
+            //skip
+            continue;
+        }
+        
+        //ignore files that don't contain process name
+        if(YES != [file containsString:[processPath lastPathComponent]])
+        {
+            //skip
+            continue;
+        }
+        
+        //dbg msg
+        logMsg(LOG_DEBUG, [NSString stringWithFormat:@"deleting sample file: %@", file]);
+        
+        //delete
+        if(YES != [[NSFileManager defaultManager] removeItemAtPath:[@"/tmp" stringByAppendingPathComponent:file] error:&error])
+        {
+            //err msg
+            logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete %@ (%@)", file, error]);
+            
+            //bail
+            goto bail;
+        }
+    
+    }//all files
+    
+//bail
+bail:
+    
+    
+    return;
+}
+
 //set status of video
 -(void)updateVideoStatus:(BOOL)isEnabled
 {
diff --git a/Shared/Exception.m b/Shared/Exception.m
index 140bcb1..7f7d035 100755
--- a/Shared/Exception.m
+++ b/Shared/Exception.m
@@ -11,12 +11,10 @@
 #import "Exception.h"
 #import "Utilities.h"
 
-//TODO: renenable
-/*
 #ifdef IS_INSTALLER_APP
 #import "AppDelegate.h"
 #endif
-*/
+
 
 //global
 // ->only report an fatal exception once
@@ -98,9 +96,8 @@ void exceptionHandler(NSException *exception)
     // ->agent should exit
     errorInfo[KEY_ERROR_SHOULD_EXIT] = [NSNumber numberWithBool:YES];
     
-    //TODO: renable
     //display error msg
-    //[((AppDelegate*)[[NSApplication sharedApplication] delegate]) displayErrorWindow:errorInfo];
+    [((AppDelegate*)[[NSApplication sharedApplication] delegate]) displayErrorWindow:errorInfo];
     
     //need to sleep, otherwise returning from this function will cause OS to kill agent
     //  ->instead, we want error popup to be displayed (which will exit agent when closed)
@@ -178,9 +175,8 @@ void signalHandler(int signal, siginfo_t *info, void *context)
     // ->agent should exit
     errorInfo[KEY_ERROR_SHOULD_EXIT] = [NSNumber numberWithBool:YES];
     
-    //TODO: renable
     //display error msg
-    //[((AppDelegate*)[[NSApplication sharedApplication] delegate]) displayErrorWindow:errorInfo];
+    [((AppDelegate*)[[NSApplication sharedApplication] delegate]) displayErrorWindow:errorInfo];
     
     //end app-specific code
     #endif
diff --git a/Shared/Logging.m b/Shared/Logging.m
index fb79fbb..d48badb 100644
--- a/Shared/Logging.m
+++ b/Shared/Logging.m
@@ -42,8 +42,5 @@ void logMsg(int level, NSString* msg)
     //log to syslog
     syslog(level, "%s: %s\n", [logPrefix UTF8String], [msg UTF8String]);
     
-    //TODO: remove
-    NSLog(@"%s: %s", [logPrefix UTF8String], [msg UTF8String]);
-    
     return;
 }
diff --git a/Shared/Utilities.h b/Shared/Utilities.h
index 3c73874..4f17046 100644
--- a/Shared/Utilities.h
+++ b/Shared/Utilities.h
@@ -14,8 +14,6 @@
 
 /* FUNCTIONS */
 
-//TODO: cleanup/remove un-needed
-
 //get app's version
 // ->extracted from Info.plist
 NSString* getAppVersion();
@@ -36,13 +34,6 @@ SInt32 getVersion(OSType selector);
 // parse it back up to find app's bundle
 NSBundle* findAppBundle(NSString* binaryPath);
 
-//given a directory and a filter predicate
-// ->return all matches
-NSArray* directoryContents(NSString* directory, NSString* predicate);
-
-//hash (sha1/md5) a file
-NSDictionary* hashFile(NSString* filePath);
-
 //get app's version
 // ->extracted from Info.plist
 NSString* getAppVersion();
@@ -54,15 +45,15 @@ NSString* getLatestVersion();
 // -1, YES or NO
 NSInteger isNewVersion(NSMutableString* versionString);
 
-//exec a process and grab it's output
-NSData* execTask(NSString* binaryPath, NSArray* arguments);
-
 //get process's path
 NSString* getProcessPath(pid_t pid);
 
+//given a pid
+// ->get the name of the process
+NSString* getProcessName(pid_t pid);
+
 //wait until a window is non nil
 // ->then make it modal
 void makeModal(NSWindowController* windowController);
 
-
 #endif
diff --git a/Shared/Utilities.m b/Shared/Utilities.m
index cc7d138..4edb730 100644
--- a/Shared/Utilities.m
+++ b/Shared/Utilities.m
@@ -254,7 +254,7 @@ NSData* execTask(NSString* binaryPath, NSArray* arguments)
         //launch
         [task launch];
     }
-    @catch(NSException *exception)
+    @catch(NSException* exception)
     {
         //bail
         goto bail;
@@ -412,6 +412,53 @@ bail:
     return taskPath;
 }
 
+//given a pid
+// ->get the name of the process
+NSString* getProcessName(pid_t pid)
+{
+    //task path
+    NSString* processName = nil;
+    
+    //process path
+    NSString* processPath = nil;
+    
+    //app's bundle
+    NSBundle* appBundle = nil;
+    
+    //get process path
+    processPath = getProcessPath(pid);
+    if( (nil == processPath) ||
+        (0 == processPath.length) )
+    {
+        //default to 'unknown'
+        processName = @"<unknown>";
+        
+        //bail
+        goto bail;
+    }
+    
+    //try find an app bundle
+    appBundle = findAppBundle(processPath);
+    if(nil != appBundle)
+    {
+        //grab name from app's bundle
+        processName = [appBundle infoDictionary][@"CFBundleName"];
+    }
+    
+    //still nil?
+    // ->just grab from path
+    if(nil == processName)
+    {
+        //from path
+        processName = [processPath lastPathComponent];
+    }
+    
+//bail
+bail:
+    
+    return processName;
+}
+
 //determine if there is a new version
 // -1, YES or NO
 NSInteger isNewVersion(NSMutableString* versionString)