initial checkin!

This commit is contained in:
Patrick Wardle 2016-09-11 14:21:14 -10:00
commit d63410b772
86 changed files with 7928 additions and 0 deletions

View File

@ -0,0 +1,414 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
7D17C4E51D658D9F0066232A /* AboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C4D81D658D9F0066232A /* AboutWindow.xib */; };
7D17C4E61D658D9F0066232A /* AboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4DA1D658D9F0066232A /* AboutWindowController.m */; };
7D17C4E71D658D9F0066232A /* Exception.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4DD1D658D9F0066232A /* Exception.m */; };
7D17C4E81D658D9F0066232A /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C4DF1D658D9F0066232A /* icon.png */; };
7D17C4E91D658D9F0066232A /* overSight.png in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C4E01D658D9F0066232A /* overSight.png */; };
7D17C4EA1D658D9F0066232A /* Logging.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4E21D658D9F0066232A /* Logging.m */; };
7D17C4EB1D658D9F0066232A /* Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4E41D658D9F0066232A /* Utilities.m */; };
7D17C4F91D658DB90066232A /* Configure.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4EC1D658DB90066232A /* Configure.m */; };
7D17C4FA1D658DB90066232A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4ED1D658DB90066232A /* AppDelegate.m */; };
7D17C4FB1D658DB90066232A /* ConfigureWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4EE1D658DB90066232A /* ConfigureWindowController.m */; };
7D17C4FC1D658DB90066232A /* ConfigureWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C4EF1D658DB90066232A /* ConfigureWindowController.xib */; };
7D17C4FD1D658DB90066232A /* ErrorWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4F31D658DB90066232A /* ErrorWindowController.m */; };
7D17C4FE1D658DB90066232A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4F41D658DB90066232A /* main.m */; };
7D17C4FF1D658DB90066232A /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C4F51D658DB90066232A /* Info.plist */; };
7D17C5001D658DB90066232A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C4F71D658DB90066232A /* Assets.xcassets */; };
7D17C5011D658DB90066232A /* ErrorWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C4F81D658DB90066232A /* ErrorWindowController.xib */; };
7D17C5041D658DEC0066232A /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C5021D658DEC0066232A /* MainMenu.xib */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
7D17C4D81D658D9F0066232A /* AboutWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AboutWindow.xib; sourceTree = "<group>"; };
7D17C4D91D658D9F0066232A /* AboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutWindowController.h; sourceTree = "<group>"; };
7D17C4DA1D658D9F0066232A /* AboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AboutWindowController.m; sourceTree = "<group>"; };
7D17C4DB1D658D9F0066232A /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Consts.h; sourceTree = "<group>"; };
7D17C4DC1D658D9F0066232A /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Exception.h; sourceTree = "<group>"; };
7D17C4DD1D658D9F0066232A /* Exception.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Exception.m; sourceTree = "<group>"; };
7D17C4DF1D658D9F0066232A /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = "<group>"; };
7D17C4E01D658D9F0066232A /* overSight.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = overSight.png; sourceTree = "<group>"; };
7D17C4E11D658D9F0066232A /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = "<group>"; };
7D17C4E21D658D9F0066232A /* Logging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Logging.m; sourceTree = "<group>"; };
7D17C4E31D658D9F0066232A /* Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utilities.h; sourceTree = "<group>"; };
7D17C4E41D658D9F0066232A /* Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Utilities.m; sourceTree = "<group>"; };
7D17C4EC1D658DB90066232A /* Configure.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Configure.m; path = Installer/Configure.m; sourceTree = SOURCE_ROOT; };
7D17C4ED1D658DB90066232A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Installer/AppDelegate.m; sourceTree = SOURCE_ROOT; };
7D17C4EE1D658DB90066232A /* ConfigureWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ConfigureWindowController.m; path = Installer/ConfigureWindowController.m; sourceTree = SOURCE_ROOT; };
7D17C4EF1D658DB90066232A /* ConfigureWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ConfigureWindowController.xib; path = Installer/ConfigureWindowController.xib; sourceTree = SOURCE_ROOT; };
7D17C4F01D658DB90066232A /* Configure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Configure.h; path = Installer/Configure.h; sourceTree = SOURCE_ROOT; };
7D17C4F11D658DB90066232A /* ConfigureWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConfigureWindowController.h; path = Installer/ConfigureWindowController.h; sourceTree = SOURCE_ROOT; };
7D17C4F21D658DB90066232A /* ErrorWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ErrorWindowController.h; path = Installer/ErrorWindowController.h; sourceTree = SOURCE_ROOT; };
7D17C4F31D658DB90066232A /* ErrorWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ErrorWindowController.m; path = Installer/ErrorWindowController.m; sourceTree = SOURCE_ROOT; };
7D17C4F41D658DB90066232A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Installer/main.m; sourceTree = SOURCE_ROOT; };
7D17C4F51D658DB90066232A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Installer/Info.plist; sourceTree = SOURCE_ROOT; };
7D17C4F61D658DB90066232A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Installer/AppDelegate.h; sourceTree = SOURCE_ROOT; };
7D17C4F71D658DB90066232A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Installer/Assets.xcassets; sourceTree = SOURCE_ROOT; };
7D17C4F81D658DB90066232A /* ErrorWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = ErrorWindowController.xib; path = Installer/ErrorWindowController.xib; sourceTree = SOURCE_ROOT; };
7D17C5031D658DEC0066232A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Installer/Base.lproj/MainMenu.xib; sourceTree = SOURCE_ROOT; };
7D24C8651D2CDEA7009932EE /* Installer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Installer.app; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
7D24C8621D2CDEA7009932EE /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
7D17C4D71D658D9F0066232A /* Shared */ = {
isa = PBXGroup;
children = (
7D17C4D81D658D9F0066232A /* AboutWindow.xib */,
7D17C4D91D658D9F0066232A /* AboutWindowController.h */,
7D17C4DA1D658D9F0066232A /* AboutWindowController.m */,
7D17C4DB1D658D9F0066232A /* Consts.h */,
7D17C4DC1D658D9F0066232A /* Exception.h */,
7D17C4DD1D658D9F0066232A /* Exception.m */,
7D17C4DE1D658D9F0066232A /* Images */,
7D17C4E11D658D9F0066232A /* Logging.h */,
7D17C4E21D658D9F0066232A /* Logging.m */,
7D17C4E31D658D9F0066232A /* Utilities.h */,
7D17C4E41D658D9F0066232A /* Utilities.m */,
);
path = Shared;
sourceTree = "<group>";
};
7D17C4DE1D658D9F0066232A /* Images */ = {
isa = PBXGroup;
children = (
7D17C4DF1D658D9F0066232A /* icon.png */,
7D17C4E01D658D9F0066232A /* overSight.png */,
);
path = Images;
sourceTree = "<group>";
};
7D17C5051D658F3E0066232A /* Images */ = {
isa = PBXGroup;
children = (
);
name = Images;
sourceTree = "<group>";
};
7D24C85C1D2CDEA7009932EE = {
isa = PBXGroup;
children = (
7D17C5051D658F3E0066232A /* Images */,
7D17C4D71D658D9F0066232A /* Shared */,
7D24C8671D2CDEA7009932EE /* Source */,
7D24C8661D2CDEA7009932EE /* Products */,
);
sourceTree = "<group>";
};
7D24C8661D2CDEA7009932EE /* Products */ = {
isa = PBXGroup;
children = (
7D24C8651D2CDEA7009932EE /* Installer.app */,
);
name = Products;
sourceTree = "<group>";
};
7D24C8671D2CDEA7009932EE /* Source */ = {
isa = PBXGroup;
children = (
7D17C5021D658DEC0066232A /* MainMenu.xib */,
7D17C4EC1D658DB90066232A /* Configure.m */,
7D17C4ED1D658DB90066232A /* AppDelegate.m */,
7D17C4EE1D658DB90066232A /* ConfigureWindowController.m */,
7D17C4EF1D658DB90066232A /* ConfigureWindowController.xib */,
7D17C4F01D658DB90066232A /* Configure.h */,
7D17C4F11D658DB90066232A /* ConfigureWindowController.h */,
7D17C4F21D658DB90066232A /* ErrorWindowController.h */,
7D17C4F31D658DB90066232A /* ErrorWindowController.m */,
7D17C4F61D658DB90066232A /* AppDelegate.h */,
7D17C4F71D658DB90066232A /* Assets.xcassets */,
7D17C4F81D658DB90066232A /* ErrorWindowController.xib */,
7D24C86B1D2CDEA7009932EE /* Supporting Files */,
);
name = Source;
path = WhatsYourSign;
sourceTree = "<group>";
};
7D24C86B1D2CDEA7009932EE /* Supporting Files */ = {
isa = PBXGroup;
children = (
7D17C4F51D658DB90066232A /* Info.plist */,
7D17C4F41D658DB90066232A /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
7D24C8641D2CDEA7009932EE /* Installer */ = {
isa = PBXNativeTarget;
buildConfigurationList = 7D24C8761D2CDEA7009932EE /* Build configuration list for PBXNativeTarget "Installer" */;
buildPhases = (
7D24C8611D2CDEA7009932EE /* Sources */,
7D24C8621D2CDEA7009932EE /* Frameworks */,
7D24C8631D2CDEA7009932EE /* Resources */,
7D1A49C71D502D4A000E2BEB /* ShellScript */,
);
buildRules = (
);
dependencies = (
);
name = Installer;
productName = WhatsYourSign;
productReference = 7D24C8651D2CDEA7009932EE /* Installer.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
7D24C85D1D2CDEA7009932EE /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0730;
ORGANIZATIONNAME = "Objective-See";
TargetAttributes = {
7D24C8641D2CDEA7009932EE = {
CreatedOnToolsVersion = 7.3;
DevelopmentTeam = VBG97UB4TA;
};
};
};
buildConfigurationList = 7D24C8601D2CDEA7009932EE /* Build configuration list for PBXProject "Installer" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 7D24C85C1D2CDEA7009932EE;
productRefGroup = 7D24C8661D2CDEA7009932EE /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
7D24C8641D2CDEA7009932EE /* Installer */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
7D24C8631D2CDEA7009932EE /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7D17C4E91D658D9F0066232A /* overSight.png in Resources */,
7D17C4FF1D658DB90066232A /* Info.plist in Resources */,
7D17C5011D658DB90066232A /* ErrorWindowController.xib in Resources */,
7D17C5001D658DB90066232A /* Assets.xcassets in Resources */,
7D17C4E81D658D9F0066232A /* icon.png in Resources */,
7D17C4FC1D658DB90066232A /* ConfigureWindowController.xib in Resources */,
7D17C4E51D658D9F0066232A /* AboutWindow.xib in Resources */,
7D17C5041D658DEC0066232A /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
7D1A49C71D502D4A000E2BEB /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "#cp -R -f $BUILT_PRODUCTS_DIR/WhatsYourSign.appex $BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Resources/WhatsYourSign.appex\n\n#for achiving\n# ->first build finder sync in release mode (don't archive it)\n# then archive (this) installer app\n#cp -R -f ~/objective-see/WhatsYourSign/DerivedData/WhatsYourSign/Build/Products/Release/WhatsYourSign.appex ~/objective-see/WhatsYourSign/DerivedData/WhatsYourSign/Build/Intermediates/ArchiveIntermediates/WhatsYourSign/BuildProductsPath/Release/WhatsYourSign.app/Contents/Resources/WhatsYourSign.appex\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
7D24C8611D2CDEA7009932EE /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7D17C4FB1D658DB90066232A /* ConfigureWindowController.m in Sources */,
7D17C4FA1D658DB90066232A /* AppDelegate.m in Sources */,
7D17C4E71D658D9F0066232A /* Exception.m in Sources */,
7D17C4EB1D658D9F0066232A /* Utilities.m in Sources */,
7D17C4F91D658DB90066232A /* Configure.m in Sources */,
7D17C4EA1D658D9F0066232A /* Logging.m in Sources */,
7D17C4FD1D658DB90066232A /* ErrorWindowController.m in Sources */,
7D17C4FE1D658DB90066232A /* main.m in Sources */,
7D17C4E61D658D9F0066232A /* AboutWindowController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
7D17C5021D658DEC0066232A /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
7D17C5031D658DEC0066232A /* Base */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
7D24C8741D2CDEA7009932EE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
7D24C8751D2CDEA7009932EE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
};
name = Release;
};
7D24C8771D2CDEA7009932EE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Developer ID Application";
COMBINE_HIDPI_IMAGES = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"IS_INSTALLER_APP=1",
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
INFOPLIST_FILE = Installer/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.OverSightInstaller";
PRODUCT_NAME = Installer;
USER_HEADER_SEARCH_PATHS = ../Shared;
};
name = Debug;
};
7D24C8781D2CDEA7009932EE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Developer ID Application";
COMBINE_HIDPI_IMAGES = YES;
GCC_PREPROCESSOR_DEFINITIONS = "IS_INSTALLER_APP=1";
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO;
INFOPLIST_FILE = Installer/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.OverSightInstaller";
PRODUCT_NAME = Installer;
USER_HEADER_SEARCH_PATHS = ../Shared;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
7D24C8601D2CDEA7009932EE /* Build configuration list for PBXProject "Installer" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7D24C8741D2CDEA7009932EE /* Debug */,
7D24C8751D2CDEA7009932EE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
7D24C8761D2CDEA7009932EE /* Build configuration list for PBXNativeTarget "Installer" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7D24C8771D2CDEA7009932EE /* Debug */,
7D24C8781D2CDEA7009932EE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 7D24C85D1D2CDEA7009932EE /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:WhatsYourSign.xcodeproj">
</FileRef>
</Workspace>

42
Installer/AppDelegate.h Normal file
View File

@ -0,0 +1,42 @@
//
// AppDelegate.h
// OverSight
//
// Created by Patrick Wardle on 9/01/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "AboutWindowController.h"
#import "ErrorWindowController.h"
#import "ConfigureWindowController.h"
@interface AppDelegate : NSObject <NSApplicationDelegate, NSMenuDelegate, NSWindowDelegate>
{
}
/* PROPERTIES */
//about window controller
@property(nonatomic, retain)AboutWindowController* aboutWindowController;
//configure window controller
@property(nonatomic, retain)ConfigureWindowController* configureWindowController;
//error window controller
@property(nonatomic, retain)ErrorWindowController* errorWindowController;
/* METHODS */
//display configuration window w/ 'install' || 'uninstall' button
-(void)displayConfigureWindow:(BOOL)isInstalled;
//display error window
-(void)displayErrorWindow:(NSDictionary*)errorInfo;
@end

141
Installer/AppDelegate.m Normal file
View File

@ -0,0 +1,141 @@
//
// AppDelegate.m
// OverSight
//
// Created by Patrick Wardle on 9/01/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
//TODO: remove:
// sudo lsmp -p 13513
#import "Consts.h"
#import "Configure.h"
#import "Exception.h"
#import "Utilities.h"
#import "AppDelegate.h"
@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
@end
@implementation AppDelegate
@synthesize aboutWindowController;
@synthesize errorWindowController;
@synthesize configureWindowController;
//main app interface
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
//config object
Configure* configureObj = nil;
//first thing...
// ->install exception handlers
installExceptionHandlers();
//alloc/init Config obj
configureObj = [[Configure alloc] init];
//show window
[self displayConfigureWindow:[configureObj isInstalled]];
//bail
bail:
return;
}
//automatically invoked when user clicks 'About/Info'
// ->show about window
-(IBAction)about:(id)sender
{
//alloc/init settings window
if(nil == self.aboutWindowController)
{
//alloc/init
aboutWindowController = [[AboutWindowController alloc] initWithWindowNibName:@"AboutWindow"];
}
//center window
[[self.aboutWindowController window] center];
//show it
[self.aboutWindowController showWindow:self];
return;
}
//display configuration window w/ 'install' || 'uninstall' button
-(void)displayConfigureWindow:(BOOL)isInstalled
{
//alloc/init
configureWindowController = [[ConfigureWindowController alloc] initWithWindowNibName:@"ConfigureWindowController"];
//display it
// ->call this first to so that outlets are connected
[self.configureWindowController display];
//configure it
[self.configureWindowController configure:isInstalled];
return;
}
//display error window
-(void)displayErrorWindow:(NSDictionary*)errorInfo
{
//alloc error window
errorWindowController = [[ErrorWindowController alloc] initWithWindowNibName:@"ErrorWindowController"];
//main thread
// ->just show UI alert, unless its fatal (then load URL)
if(YES == [NSThread isMainThread])
{
//non-fatal errors
// ->show error popup
if(YES != [errorInfo[KEY_ERROR_URL] isEqualToString:FATAL_ERROR_URL])
{
//display it
// ->call this first to so that outlets are connected
[self.errorWindowController display];
//configure it
[self.errorWindowController configure:errorInfo];
}
//fatal error
// ->launch browser to go to fatal error page, then exit
else
{
//launch browser
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:errorInfo[KEY_ERROR_URL]]];
//then exit
[NSApp terminate:self];
}
}
//background thread
// ->have to show error window on main thread
else
{
//show alert
// ->in main UI thread
dispatch_sync(dispatch_get_main_queue(), ^{
//display it
// ->call this first to so that outlets are connected
[self.errorWindowController display];
//configure it
[self.errorWindowController configure:errorInfo];
});
}
return;
}
@end

View File

@ -0,0 +1,68 @@
{
"images" : [
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "icon_16x16.png",
"scale" : "1x"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "icon_16x16@2x.png",
"scale" : "2x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "icon_32x32.png",
"scale" : "1x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "icon_32x32@2x.png",
"scale" : "2x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "icon_128x128.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "icon_128x128@2x.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "icon_256x256.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "icon_256x256@2x.png",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "icon_512x512.png",
"scale" : "1x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "icon_512x512@2x.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate"/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="WhatsYourSign" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="WhatsYourSign" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="about:" target="Voe-Tx-rLC" id="Hep-HT-rEM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</objects>
</document>

36
Installer/Configure.h Normal file
View File

@ -0,0 +1,36 @@
//
// Configure.h
// OverSight
//
// Created by Patrick Wardle on 9/01/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#ifndef __WYS_Configure_h
#define __WYS_Configure_h
#import <Foundation/Foundation.h>
@interface Configure : NSObject
{
}
/* METHODS */
//determine if extension is installed
-(BOOL)isInstalled;
//invokes appropriate install || uninstall logic
-(BOOL)configure:(NSUInteger)parameter;
//install
-(BOOL)install;
//uninstall
-(BOOL)uninstall;
@end
#endif

233
Installer/Configure.m Normal file
View File

@ -0,0 +1,233 @@
//
// Configure.m
// OverSight
//
// Created by Patrick Wardle on 9/01/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Consts.h"
#import "Logging.h"
#import "Utilities.h"
#import "Configure.h"
@implementation Configure
//invokes appropriate install || uninstall logic
-(BOOL)configure:(NSUInteger)parameter
{
//return var
BOOL wasConfigured = NO;
//install extension
if(ACTION_INSTALL_FLAG == parameter)
{
//dbg msg
logMsg(LOG_DEBUG, @"installing...");
//if already installed though
// ->uninstall everything first
if(YES == [self isInstalled])
{
//dbg msg
logMsg(LOG_DEBUG, @"already installed, so uninstalling...");
//uninstall
if(YES != [self uninstall])
{
//bail
goto bail;
}
//dbg msg
logMsg(LOG_DEBUG, @"uninstalled");
}
//install
if(YES != [self install])
{
//bail
goto bail;
}
//dbg msg
logMsg(LOG_DEBUG, @"installed!");
}
//uninstall extension
else if(ACTION_UNINSTALL_FLAG == parameter)
{
//dbg msg
logMsg(LOG_DEBUG, @"uninstalling...");
//uninstall
if(YES != [self uninstall])
{
//bail
goto bail;
}
//dbg msg
logMsg(LOG_DEBUG, @"uninstalled!");
}
//no errors
wasConfigured = YES;
//bail
bail:
return wasConfigured;
}
//determine if installed
// ->simply checks if extension binary exists
-(BOOL)isInstalled
{
//check if extension exists
return [[NSFileManager defaultManager] fileExistsAtPath:[[EXTENSION_FOLDER stringByExpandingTildeInPath] stringByAppendingPathComponent:EXTENSION_NAME]];
}
//install
// a) create and copy extension to ~/Library/WhatsYourSign
// b) add extension: 'pluginkit -a /path/2/WhatsYourSign.appex'
// c) enable extension: 'pluginkit -e use -i com.objective-see.WhatsYourSignExt.FinderSync'
-(BOOL)install
{
//return/status var
BOOL wasInstalled = NO;
//error
NSError* error = nil;
//path to finder sync (src)
NSString* extensionPathSrc = nil;
//path to finder sync (dest)
NSString* extensionPathDest = nil;
//results from 'pluginkit' cmd
NSData* results = nil;
//set src path
// ->orginally stored in installer app's /Resource bundle
extensionPathSrc = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:EXTENSION_NAME];
//set dest path
extensionPathDest = [[EXTENSION_FOLDER stringByExpandingTildeInPath] stringByAppendingPathComponent:EXTENSION_NAME];
//check if extension folder needs to be created
if(YES != [[NSFileManager defaultManager] fileExistsAtPath:[EXTENSION_FOLDER stringByExpandingTildeInPath]])
{
//create it
if(YES != [[NSFileManager defaultManager] createDirectoryAtPath:[EXTENSION_FOLDER stringByExpandingTildeInPath] withIntermediateDirectories:YES attributes:nil error:&error])
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to create extension's directory %@ (%@)", EXTENSION_FOLDER, error]);
//bail
goto bail;
}
}
//move extension into persistent location
// ->'/Library/WhatsYourSign/' + extension name
if(YES != [[NSFileManager defaultManager] copyItemAtPath:extensionPathSrc toPath:extensionPathDest error:&error])
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to copy %@ -> %@ (%@)", extensionPathSrc, extensionPathDest, error]);
//bail
goto bail;
}
//dbg msg
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"copied %@ -> %@", extensionPathSrc, extensionPathDest]);
//install extension via 'pluginkit -a <path 2 ext>
results = execTask(PLUGIN_KIT, @[@"-a", extensionPathDest]);
if(0 != results.length)
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"pluginkit failed to install extension (%@)", [[NSString alloc] initWithData:results encoding:NSUTF8StringEncoding]]);
//bail
goto bail;
}
//nap
// ->VM sometimes didn't enable
[NSThread sleepForTimeInterval:0.5];
//enable extension via 'pluginkit -e use -i <ext bundle id>
results = execTask(PLUGIN_KIT, @[@"-e", @"use", @"-i", EXTENSION_BUNDLE_ID]);
if(0 != results.length)
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"pluginkit failed to enable extension (%@)", [[NSString alloc] initWithData:results encoding:NSUTF8StringEncoding]]);
//bail
goto bail;
}
//no error
wasInstalled = YES;
//bail
bail:
return wasInstalled;
}
//uninstall
// a) remove it (pluginkit -r <path 2 ext>)
// b) delete binary & folder; /Library/WhatsYourSign
-(BOOL)uninstall
{
//return/status var
BOOL wasUninstalled = NO;
//status var
// ->since want to try all uninstall steps, but record if any fail
BOOL bAnyErrors = NO;
//path to finder sync
NSString* extensionPath = nil;
//error
NSError* error = nil;
//init path
extensionPath = [[EXTENSION_FOLDER stringByExpandingTildeInPath] stringByAppendingPathComponent:EXTENSION_NAME];
//this always seem to 'fail' with 'remove: no plugin at <path/2/FinderSync.appex>'
// ->but yet works, so just ignore any return from this invocation of execTask()
execTask(PLUGIN_KIT, @[@"-r", extensionPath]);
//delete folder
if(YES != [[NSFileManager defaultManager] removeItemAtPath:[EXTENSION_FOLDER stringByExpandingTildeInPath] error:&error])
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to delete extension directory %@ (%@)", EXTENSION_FOLDER, error]);
//set flag
bAnyErrors = YES;
//keep uninstalling...
}
//only success when there were no errors
if(YES != bAnyErrors)
{
//happy
wasUninstalled = YES;
}
return wasUninstalled;
}
@end

View File

@ -0,0 +1,40 @@
//
// ConfigureWindowController.h
// OverSight
//
// Created by Patrick Wardle on 9/01/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface ConfigureWindowController : NSWindowController <NSWindowDelegate>
{
}
/* PROPERTIES */
@property (weak) IBOutlet NSProgressIndicator *activityIndicator;
@property (weak) IBOutlet NSTextField *statusMsg;
@property (weak) IBOutlet NSButton *installButton;
@property (weak) IBOutlet NSButton *uninstallButton;
@property (weak) IBOutlet NSButton *moreInfoButton;
/* METHODS */
//install/uninstall button handler
-(IBAction)buttonHandler:(id)sender;
//(more) info button handler
-(IBAction)info:(id)sender;
//configure window/buttons
// ->also brings to front
-(void)configure:(BOOL)isInstalled;
//display (show) window
-(void)display;
@end

View File

@ -0,0 +1,406 @@
//
// ConfigureWindowController.m
// OverSight
//
// Created by Patrick Wardle on 9/01/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import "Consts.h"
#import "Configure.h"
#import "Utilities.h"
#import "ConfigureWindowController.h"
@implementation ConfigureWindowController
@synthesize statusMsg;
@synthesize moreInfoButton;
//automatically called when nib is loaded
// ->just center window
-(void)awakeFromNib
{
//center
[self.window center];
return;
}
//configure window/buttons
// ->also brings window to front
-(void)configure:(BOOL)isInstalled
{
//set window title
[self window].title = [NSString stringWithFormat:@"version %@", getAppVersion()];
//yosemite doesn't support emojis :P
if(getVersion(gestaltSystemVersionMinor) <= OS_MINOR_VERSION_YOSEMITE)
{
//init status msg
[self.statusMsg setStringValue:@"monitor audio & video access"];
}
//el capitan supports emojis
else
{
//init status msg
[self.statusMsg setStringValue:@"monitor 🎤 and 📸 access"];
}
//enable 'uninstall' button when app is installed already
if(YES == isInstalled)
{
//enable
self.uninstallButton.enabled = YES;
}
//otherwise disable
else
{
//disable
self.uninstallButton.enabled = NO;
}
//make 'install' have focus
// ->more likely they'll be upgrading, but do after a delay so it sticks...
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.25 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
//set responder
[self.window makeFirstResponder:self.installButton];
});
//set delegate
[self.window setDelegate:self];
return;
}
//display (show) window
// ->center, make front, set bg to white, etc
-(void)display
{
//center window
[[self window] center];
//show (now configured) windows
[self showWindow:self];
//make it key window
[self.window makeKeyAndOrderFront:self];
//make window front
[NSApp activateIgnoringOtherApps:YES];
//make white
[self.window setBackgroundColor: NSColor.whiteColor];
return;
}
//button handler for uninstall/install
-(IBAction)buttonHandler:(id)sender
{
//button title
NSString* buttonTitle = nil;
//extact button title
buttonTitle = ((NSButton*)sender).title;
//action
NSUInteger action = 0;
//hide 'get more info' button
self.moreInfoButton.hidden = YES;
//set action
// ->install daemon
if(YES == [buttonTitle isEqualToString:ACTION_INSTALL])
{
//set
action = ACTION_INSTALL_FLAG;
}
//set action
// ->uninstall daemon
else
{
//set
action = ACTION_UNINSTALL_FLAG;
}
//disable 'x' button
// ->don't want user killing app during install/upgrade
[[self.window standardWindowButton:NSWindowCloseButton] setEnabled:NO];
//clear status msg
[self.statusMsg setStringValue:@""];
//force redraw of status msg
// ->sometime doesn't refresh (e.g. slow VM)
[self.statusMsg setNeedsDisplay:YES];
//invoke logic to install/uninstall
// ->do in background so UI doesn't block
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),
^{
//install/uninstall
[self lifeCycleEvent:action];
});
//bail
bail:
return;
}
//button handler for '?' button (on an error)
// ->load objective-see's documentation for error(s) in default browser
-(IBAction)info:(id)sender
{
//url
NSURL *helpURL = nil;
//build help URL
helpURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@#errors", PRODUCT_URL]];
//open URL
// ->invokes user's default browser
[[NSWorkspace sharedWorkspace] openURL:helpURL];
return;
}
//perform install | uninstall via Control obj
// ->invoked on background thread so that UI doesn't block
-(void)lifeCycleEvent:(NSUInteger)event
{
//status var
BOOL status = NO;
//configure object
Configure* configureObj = nil;
//dbg msg
//logMsg(LOG_DEBUG, [NSString stringWithFormat:@"handling life cycle event, %lu", (unsigned long)event]);
//alloc control object
configureObj = [[Configure alloc] init];
//begin event
// ->updates ui on main thread
dispatch_sync(dispatch_get_main_queue(),
^{
//complete
[self beginEvent:event];
});
//sleep
// ->allow 'install' || 'uninstall' msg to show up
sleep(1);
//perform action (install | uninstall)
// ->perform background actions
if(YES == [configureObj configure:event])
{
//set flag
status = YES;
}
//error occurred
else
{
//err msg
//logMsg(LOG_ERR, @"ERROR: failed to perform life cycle event");
//set flag
status = NO;
}
//complet event
// ->updates ui on main thread
dispatch_async(dispatch_get_main_queue(),
^{
//complete
[self completeEvent:status event:event];
});
return;
}
//begin event
// ->basically just update UI
-(void)beginEvent:(NSUInteger)event
{
//status msg frame
CGRect statusMsgFrame = {0};
//grab exiting frame
statusMsgFrame = self.statusMsg.frame;
//avoid activity indicator
// ->shift frame shift delta
statusMsgFrame.origin.x += FRAME_SHIFT;
//update frame to align
self.statusMsg.frame = statusMsgFrame;
//align text left
[self.statusMsg setAlignment:NSLeftTextAlignment];
//install msg
if(ACTION_INSTALL_FLAG == event)
{
//update status msg
[self.statusMsg setStringValue:@"Installing..."];
}
//uninstall msg
else
{
//update status msg
[self.statusMsg setStringValue:@"Uninstalling..."];
}
//disable action button
self.uninstallButton.enabled = NO;
//disable cancel button
self.installButton.enabled = NO;
//show spinner
[self.activityIndicator setHidden:NO];
//start spinner
[self.activityIndicator startAnimation:nil];
return;
}
//complete event
// ->update UI after background event has finished
-(void)completeEvent:(BOOL)success event:(NSUInteger)event
{
//status msg frame
CGRect statusMsgFrame = {0};
//action
NSString* action = nil;
//result msg
NSMutableString* resultMsg = nil;
//msg font
NSColor* resultMsgColor = nil;
//generally want centered text
[self.statusMsg setAlignment:NSCenterTextAlignment];
//set action msg for install
if(ACTION_INSTALL_FLAG == event)
{
//set msg
action = @"install";
}
//set action msg for uninstall
else
{
//set msg
action = @"uninstall";
}
//success
if(YES == success)
{
//set result msg
resultMsg = [NSMutableString stringWithFormat:@"WhatsYourSign %@ed!", action];
//add extra info when installed
if(ACTION_INSTALL_FLAG == event)
{
//append
[resultMsg appendString:@"\nany file: ctl+click->'Signing Info'"];
}
//set font to black
resultMsgColor = [NSColor blackColor];
}
//failure
else
{
//set result msg
resultMsg = [NSMutableString stringWithFormat:@"error: %@ failed", action];
//set font to red
resultMsgColor = [NSColor redColor];
//show 'get more info' button
self.moreInfoButton.hidden = NO;
}
//stop/hide spinner
[self.activityIndicator stopAnimation:nil];
//hide spinner
[self.activityIndicator setHidden:YES];
//grab exiting frame
statusMsgFrame = self.statusMsg.frame;
//shift back since activity indicator is gone
statusMsgFrame.origin.x -= FRAME_SHIFT;
//update frame to align
self.statusMsg.frame = statusMsgFrame;
//set font to bold
[self.statusMsg setFont:[NSFont fontWithName:@"Menlo-Bold" size:13]];
//set msg color
[self.statusMsg setTextColor:resultMsgColor];
//set status msg
[self.statusMsg setStringValue:resultMsg];
//toggle buttons
// ->after install turn on 'uninstall' and off 'install'
if(ACTION_INSTALL_FLAG == event)
{
//enable uninstall
self.uninstallButton.enabled = YES;
//disable install
self.installButton.enabled = NO;
}
//toggle buttons
// ->after uninstall turn off 'uninstall' and on 'install'
else
{
//disable
self.uninstallButton.enabled = NO;
//enable close button
self.installButton.enabled = YES;
}
//ok to re-enable 'x' button
[[self.window standardWindowButton:NSWindowCloseButton] setEnabled:YES];
//(re)make window window key
[self.window makeKeyAndOrderFront:self];
//(re)make window front
[NSApp activateIgnoringOtherApps:YES];
return;
}
//automatically invoked when window is closing
// ->just exit application
-(void)windowWillClose:(NSNotification *)notification
{
//exit
[NSApp terminate:self];
return;
}
@end

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15G31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="ConfigureWindowController">
<connections>
<outlet property="activityIndicator" destination="b1w-5W-ayX" id="P5e-PO-Ozi"/>
<outlet property="installButton" destination="553-2y-kvm" id="nxf-wO-EI3"/>
<outlet property="moreInfoButton" destination="QWu-qZ-Za2" id="8kC-Ga-5oJ"/>
<outlet property="statusMsg" destination="SpB-Xc-WlB" id="jlm-ke-XfM"/>
<outlet property="uninstallButton" destination="XrB-7g-xya" id="0Hc-Xn-STu"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES" unifiedTitleAndToolbar="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="460" height="176"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<view key="contentView" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="460" height="176"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<progressIndicator hidden="YES" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" maxValue="100" bezeled="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="b1w-5W-ayX">
<rect key="frame" x="144" y="31" width="32" height="32"/>
</progressIndicator>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="XrB-7g-xya">
<rect key="frame" x="138" y="76" width="115" height="32"/>
<buttonCell key="cell" type="push" title="Uninstall" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="G7H-Q1-04P">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="13" name="Menlo-Regular"/>
</buttonCell>
<connections>
<action selector="buttonHandler:" target="-2" id="GAh-S3-3e6"/>
</connections>
</button>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fgM-lv-GeJ">
<rect key="frame" x="13" y="43" width="114" height="123"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="icon" id="bCU-0f-ff8"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" preferredMaxLayoutWidth="293" translatesAutoresizingMaskIntoConstraints="NO" id="SpB-Xc-WlB">
<rect key="frame" x="142" y="10" width="297" height="47"/>
<textFieldCell key="cell" truncatesLastVisibleLine="YES" enabled="NO" sendsActionOnEndEditing="YES" alignment="center" id="Rib-WU-Syl">
<font key="font" size="13" name="Menlo-Regular"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="553-2y-kvm">
<rect key="frame" x="331" y="76" width="115" height="32"/>
<buttonCell key="cell" type="push" title="Install" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="1hy-8F-qxt">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="13" name="Menlo-Regular"/>
</buttonCell>
<connections>
<action selector="buttonHandler:" target="-2" id="1wC-Ls-D2F"/>
</connections>
</button>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="K52-9g-sjn">
<rect key="frame" x="138" y="109" width="243" height="67"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="overSight" id="WRg-I2-vkK"/>
</imageView>
<button hidden="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="QWu-qZ-Za2">
<rect key="frame" x="414" y="33" width="25" height="25"/>
<buttonCell key="cell" type="help" bezelStyle="helpButton" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="v1Z-aX-Wlm">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="info:" target="-2" id="tg3-SV-AH9"/>
</connections>
</button>
</subviews>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
<point key="canvasLocation" x="185" y="-256"/>
</window>
</objects>
<resources>
<image name="icon" width="512" height="512"/>
<image name="overSight" width="1396" height="286"/>
</resources>
</document>

View File

@ -0,0 +1,42 @@
//
// ErrorWindowController.h
// OverSight
//
// Created by Patrick Wardle on 7/15/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface ErrorWindowController : NSWindowController <NSWindowDelegate>
{
}
//main msg in window
@property (weak) IBOutlet NSTextField *errMsg;
//sub msg in window
@property (weak) IBOutlet NSTextField *errSubMsg;
//info/help/fix button
@property (weak) IBOutlet NSButton *infoButton;
//close button
@property (weak) IBOutlet NSButton *closeButton;
//(optional) url for 'Info' button
@property(nonatomic, retain) NSURL* errorURL;
//flag indicating close button should exit app
@property BOOL shouldExit;
/* METHODS */
//configure the object/window
-(void)configure:(NSDictionary*)errorInfo;
//display (show) window
-(void)display;
@end

View File

@ -0,0 +1,150 @@
//
// ErrorWindowController.m
// OverSight
//
// Created by Patrick Wardle on 7/15/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import "Consts.h"
#import "ErrorWindowController.h"
@interface ErrorWindowController ()
@end
@implementation ErrorWindowController
@synthesize errorURL;
@synthesize shouldExit;
@synthesize closeButton;
//automatically called when nib is loaded
// ->center window
-(void)awakeFromNib
{
//center
[self.window center];
return;
}
//configure the object/window
-(void)configure:(NSDictionary*)errorInfo
{
//set error msg
self.errMsg.stringValue = errorInfo[KEY_ERROR_MSG];
//set error sub msg
self.errSubMsg.stringValue = errorInfo[KEY_ERROR_SUB_MSG];
//save exit
self.shouldExit = [errorInfo[KEY_ERROR_SHOULD_EXIT] boolValue];
//grab optional error url
if(nil != errorInfo[KEY_ERROR_URL])
{
//extract/convert
self.errorURL = [NSURL URLWithString:errorInfo[KEY_ERROR_URL]];
}
//when exiting
// ->change 'close' to 'exit'
if(YES == self.shouldExit)
{
//change title
self.closeButton.title = @"Exit";
}
//for fatal errors
// ->change 'Info' to 'help fix'
if(YES == [[self.errorURL absoluteString] isEqualToString:FATAL_ERROR_URL])
{
//change title
self.infoButton.title = @"Help Fix";
}
//set delegate
[self.window setDelegate:self];
return;
}
//display (show) window
-(void)display
{
//show (now configured), alert
[self showWindow:self];
//make it key window
[self.window makeKeyAndOrderFront:self];
//make window front
[NSApp activateIgnoringOtherApps:YES];
//make 'close' have focus
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.25 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
//make close button active
[self.window makeFirstResponder:closeButton];
});
//make white
[self.window setBackgroundColor: NSColor.whiteColor];
return;
}
//invoked when user clicks '?' (help button)
// ->open url with more info about the error(s)
-(IBAction)help:(id)sender
{
//if a url was specified
// ->use that one
if(nil != self.errorURL)
{
//open URL
// ->invokes user's default browser
[[NSWorkspace sharedWorkspace] openURL:self.errorURL];
}
//use default URL
else
{
//open URL
// ->invokes user's default browser
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:ERRORS_URL]];
}
return;
}
//invoked when user clicks 'close'
// ->just close window
-(IBAction)close:(id)sender
{
//close
[self.window close];
return;
}
//automatically invoked when window is closing
// ->exit the app if specified...
-(void)windowWillClose:(NSNotification *)notification
{
//check if should exit process
// ->e.g. an error during install, etc
if(YES == self.shouldExit)
{
//dbg msg
//logMsg(LOG_DEBUG, @"exiting application");
//exit
[NSApp terminate:self];
}
return;
}
@end

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="ErrorWindowController">
<connections>
<outlet property="closeButton" destination="zbs-ul-WTJ" id="1fu-zv-brA"/>
<outlet property="errMsg" destination="Act-U9-8G9" id="PMa-6M-gwu"/>
<outlet property="errSubMsg" destination="tQ0-f2-G4C" id="4hz-9M-Gub"/>
<outlet property="infoButton" destination="sNV-Jw-9dZ" id="qdf-xQ-ubf"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="WHATSYOURSIGN? Error" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES" unifiedTitleAndToolbar="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="744" y="427" width="380" height="137"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<view key="contentView" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="380" height="137"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zbs-ul-WTJ">
<rect key="frame" x="284" y="13" width="82" height="32"/>
<buttonCell key="cell" type="push" title="Close" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Pjp-Se-SfK">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="close:" target="-2" id="sez-MW-4AE"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" preferredMaxLayoutWidth="0.0" translatesAutoresizingMaskIntoConstraints="NO" id="tQ0-f2-G4C">
<rect key="frame" x="114" y="49" width="248" height="36"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="left" title="Label" id="sk0-EF-ZfG">
<font key="font" size="11" name="Menlo-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" preferredMaxLayoutWidth="0.0" translatesAutoresizingMaskIntoConstraints="NO" id="Act-U9-8G9">
<rect key="frame" x="114" y="88" width="248" height="20"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="ERROR msg" id="VCu-zh-Vcs">
<font key="font" size="13" name="Menlo-Bold"/>
<color key="textColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="sNV-Jw-9dZ">
<rect key="frame" x="202" y="13" width="82" height="32"/>
<buttonCell key="cell" type="push" title="Info" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="L1w-RB-EC4">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="help:" target="-2" id="gCm-No-dHq"/>
</connections>
</button>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="sRj-I0-15e">
<rect key="frame" x="12" y="35" width="92" height="92"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="icon" id="9iG-ot-b7i"/>
</imageView>
</subviews>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
<point key="canvasLocation" x="257" y="269.5"/>
</window>
</objects>
<resources>
<image name="icon" width="512" height="512"/>
</resources>
</document>

34
Installer/Info.plist Normal file
View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<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>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright (c) 2016 Objective-See. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

13
Installer/main.m Normal file
View File

@ -0,0 +1,13 @@
//
// main.m
// OverSight
//
// Created by Patrick Wardle on 7/15/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[]) {
return NSApplicationMain(argc, argv);
}

70
LoginItem/AVMonitor.h Normal file
View File

@ -0,0 +1,70 @@
//
// ProcessMonitor.h
// OverSight
//
// Created by Patrick Wardle on 9/01/16.
// Copyright (c) 2015 Objective-See. All rights reserved.
//
#import <CoreAudio/CoreAudio.h>
#import <CoreMedia/CoreMedia.h>
#import <Foundation/Foundation.h>
#import <CoreMediaIO/CMIOHardware.h>
#import <AVFoundation/AVFoundation.h>
/* DEFINES */
#define VIDEO_DISABLED 0x0
#define VIDEO_ENABLED 0x1
#define EVENT_SOURCE @"source"
#define EVENT_DEVICE @"device"
#define EVENT_DEVICE_STATUS @"status"
#define EVENT_PROCESS_ID @"processID"
#define SOURCE_AUDIO @0x1
#define SOURCE_VIDEO @0x2
#define DEVICE_INACTIVE @0x0
#define DEVICE_ACTIVE @0x1
@interface AVMonitor : NSObject <NSUserNotificationCenterDelegate>
{
}
/* PROPERTIES */
//apple mic (AVCaptureHALDevice)
@property(nonatomic, retain)AVCaptureDevice* mic;
//apple camera (AVCaptureDALDevice)
@property(nonatomic, retain)AVCaptureDevice* camera;
//flag indicating audio (mic) is active
@property BOOL audioActive;
//flag indicating video (camera) is active
@property BOOL videoActive;
//monitor thread
@property(nonatomic, retain)NSThread* videoMonitorThread;
/* METHODS */
//kicks off thread to monitor
-(BOOL)monitor;
//monitor for new procs
-(void)monitor4Procs;
//determine if audio is active
-(void)setAudioDevStatus:(AudioObjectID)deviceID;
//determine if video is active
-(void)setVideoDevStatus:(CMIOObjectID)deviceID;
@end

941
LoginItem/AVMonitor.m Normal file
View File

@ -0,0 +1,941 @@
//
// AVMonitor.m
// OverSight
//
// Created by Patrick Wardle on 9/01/16.
// Copyright (c) 2015 Objective-See. All rights reserved.
//
//TODO: NSLOg -> logmSg
#import "Consts.h"
#import "Logging.h"
#import "Utilities.h"
#import "AVMonitor.h"
#import "AppDelegate.h"
#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;
@synthesize camera;
@synthesize audioActive;
@synthesize videoActive;
@synthesize videoMonitorThread;
//init
-(id)init
{
//init super
self = [super init];
if(nil != self)
{
}
return self;
}
//initialiaze AV notifcations/callbacks
-(BOOL)monitor
{
//return var
BOOL bRet = NO;
//xpc connection
__block NSXPCConnection* xpcConnection = nil;
//device's connection id
unsigned int connectionID = 0;
//selector for getting device id
SEL methodSelector = nil;
//array for devices + status
NSMutableArray* devices = nil;
//wait semaphore
dispatch_semaphore_t waitSema = nil;
//alloc XPC connection
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"];
//alloc device array
devices = [NSMutableArray array];
//set remote object interface
xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)];
//resume
[xpcConnection resume];
//dbg msg
logMsg(LOG_DEBUG, @"telling XPC service to begin base-lining mach messages");
//init wait semaphore
waitSema = dispatch_semaphore_create(0);
//XPC service to begin baselining mach messages
// ->wait, since want this to compelete before doing other things!
[[xpcConnection remoteObjectProxy] initialize:^
{
//signal sema
dispatch_semaphore_signal(waitSema);
}];
//wait until XPC is done
// ->XPC reply block will signal semaphore
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
//init selector
methodSelector = NSSelectorFromString(@"connectionID");
//find (first) apple camera
self.camera = findAppleCamera();
//find built in mic
self.mic = findAppleMic();
//dbg msg
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"found mic: %@", self.mic]);
//got camera
// ->grab connection ID and invoke helper functions
if( (nil != self.camera) &&
(YES == [self.camera respondsToSelector:methodSelector]) )
{
//ignore leak warning
// ->we know what we're doing via this 'performSelector'
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
//grab connection ID
connectionID = (unsigned int)[self.camera performSelector:methodSelector withObject:nil];
//restore
#pragma clang diagnostic pop
//set status
// ->will set 'videoActive' iVar
[self setVideoDevStatus:connectionID];
//if video is already active
// ->start monitoring thread
if(YES == self.videoActive)
{
//tell XPC video is active
[[xpcConnection remoteObjectProxy] updateVideoStatus:self.videoActive reply:^{
//signal sema
dispatch_semaphore_signal(waitSema);
}];
//wait until XPC is done
// ->XPC reply block will signal semaphore
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
//alloc
videoMonitorThread = [[NSThread alloc] initWithTarget:self selector:@selector(monitor4Procs) object:nil];
//start
[self.videoMonitorThread start];
}
//save camera/status into device array
[devices addObject:@{EVENT_DEVICE:self.camera, EVENT_DEVICE_STATUS:@(self.videoActive)}];
//register for video events
if(YES != [self watchVideo:connectionID])
{
//err msg
}
//dbg msg
logMsg(LOG_DEBUG, @"registerd for video events");
}
//err msg
else
{
//err msg
logMsg(LOG_ERR, @"failed to find (apple) camera :(");
}
//watch mic
// ->grab connection ID and invoke helper function
if( (nil != self.mic) &&
(YES == [self.mic respondsToSelector:methodSelector]) )
{
//ignore leak warning
// ->we know what we're doing via this 'performSelector'
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
//grab connection ID
connectionID = (unsigned int)[self.mic performSelector:NSSelectorFromString(@"connectionID") withObject:nil];
//restore
#pragma clang diagnostic pop
//save camera/status into device array
[devices addObject:@{EVENT_DEVICE:self.mic, EVENT_DEVICE_STATUS:@(self.audioActive)}];
//register for audio events
if(YES != [self watchAudio:connectionID])
{
//err msg
}
//dbg msg
logMsg(LOG_DEBUG, @"registerd for audio events");
}
//err msg
else
{
//err msg
logMsg(LOG_ERR, @"failed to find (apple) mic :(");
}
//send msg to status menu
// ->update menu to show devices & their status
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:devices];
//no errors
bRet = YES;
//TODO: not needed?
//bail
bail:
//cleanup XPC
if(nil != xpcConnection)
{
//close connection
[xpcConnection invalidate];
//nil out
xpcConnection = nil;
}
return bRet;
}
//determine if video is active
// ->sets 'videoActive' iVar
-(void)setVideoDevStatus:(CMIODeviceID)deviceID
{
//status var
OSStatus status = -1;
//running flag
UInt32 isRunning = -1;
//size of query flag
UInt32 propertySize = 0;
//property address struct
CMIOObjectPropertyAddress propertyStruct = {0};
//init size
propertySize = sizeof(isRunning);
//init property struct's selector
propertyStruct.mSelector = kAudioDevicePropertyDeviceIsRunningSomewhere;
//init property struct's scope
propertyStruct.mScope = kCMIOObjectPropertyScopeGlobal;
//init property struct's element
propertyStruct.mElement = 0;
//query to get 'kAudioDevicePropertyDeviceIsRunningSomewhere' status
status = CMIOObjectGetPropertyData(deviceID, &propertyStruct, 0, NULL, sizeof(kAudioDevicePropertyDeviceIsRunningSomewhere), &propertySize, &isRunning);
if(noErr != status)
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"getting status of video device failed with %d", status]);
//set error
isRunning = -1;
//bail
goto bail;
}
//set iVar
self.videoActive = isRunning;
//bail
bail:
return;
}
//helper function
// ->determines if video went active/inactive then invokes notification generator method
-(void)handleVideoNotification:(CMIOObjectID)deviceID addresses:(const CMIOObjectPropertyAddress[]) addresses
{
//event dictionary
NSMutableDictionary* event = nil;
//xpc connection
__block NSXPCConnection* xpcConnection = nil;
//wait semaphore
dispatch_semaphore_t waitSema = nil;
//init dictionary
event = [NSMutableDictionary dictionary];
//sync?
//TODO: is this a good idea?
@synchronized (self)
{
//set status
[self setVideoDevStatus:deviceID];
//send msg to status menu
// ->update menu to show (all) devices & their status
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:@[@{EVENT_DEVICE:self.camera, EVENT_DEVICE_STATUS:@(self.videoActive)},@{EVENT_DEVICE:self.mic, EVENT_DEVICE_STATUS:@(self.audioActive)}]];
//add device
event[EVENT_DEVICE] = self.camera;
//set device status
event[EVENT_DEVICE_STATUS] = [NSNumber numberWithInt:self.videoActive];
//dbg msg
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"got video change notification; is running? %x", self.videoActive]);
//alloc XPC connection
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"];
//set remote object interface
xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)];
//resume
[xpcConnection resume];
//init wait semaphore
waitSema = dispatch_semaphore_create(0);
//tell XPC about video status
// ->for example, when video is active, will stop baselining
[[xpcConnection remoteObjectProxy] updateVideoStatus:self.videoActive reply:^{
//signal sema
dispatch_semaphore_signal(waitSema);
}];
//wait until XPC is done
// ->XPC reply block will signal semaphore
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
//if video just started
// ->ask for video procs from XPC
if(YES == self.videoActive)
{
//dbg msg
logMsg(LOG_DEBUG, @"querying XPC to get video process(s)");
//set allowed classes
[xpcConnection.remoteObjectInterface setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSNumber class], nil]
forSelector: @selector(getVideoProcs:) argumentIndex: 0 ofReply: YES];
//invoke XPC service
[[xpcConnection remoteObjectProxy] getVideoProcs:^(NSMutableArray* videoProcesses)
{
//close connection
[xpcConnection invalidate];
//nil out
xpcConnection = nil;
//dbg msg
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"video procs from XPC: %@", videoProcesses]);
//generate notification for each process
for(NSNumber* processID in videoProcesses)
{
//set pid
event[EVENT_PROCESS_ID] = processID;
//generate notification
[self generateNotification:event];
}
//signal sema
dispatch_semaphore_signal(waitSema);
}];
//wait until XPC is done
// ->XPC reply block will signal semaphore
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
}
//video deactivated
// ->close XPC connection and alert user
else
{
//close connection
[xpcConnection invalidate];
//nil out
xpcConnection = nil;
//generate notification
[self generateNotification:event];
}
//poll for new video procs
// ->this thread will exit itself as its checks the 'videoActive' iVar
if(YES == self.videoActive)
{
//start monitor thread if needed
if(YES != videoMonitorThread.isExecuting)
{
//alloc
videoMonitorThread = [[NSThread alloc] initWithTarget:self selector:@selector(monitor4Procs) object:nil];
//start
[self.videoMonitorThread start];
}
}
}//sync
//bail
bail:
return;
}
//register for video notifcations
// ->block will invoke method on event
-(BOOL)watchVideo:(CMIOObjectID)deviceID
{
//ret var
BOOL bRegistered = NO;
//status var
OSStatus status = -1;
//property struct
CMIOObjectPropertyAddress propertyStruct = {0};
//init property struct's selector
propertyStruct.mSelector = kAudioDevicePropertyDeviceIsRunningSomewhere;
//init property struct's scope
propertyStruct.mScope = kAudioObjectPropertyScopeGlobal;
//init property struct's element
propertyStruct.mElement = kAudioObjectPropertyElementMaster;
//block
// ->invoked when video changes & just calls helper function
CMIOObjectPropertyListenerBlock listenerBlock = ^(UInt32 inNumberAddresses, const CMIOObjectPropertyAddress addresses[])
{
//invoke helper function
[self handleVideoNotification:deviceID addresses:addresses];
};
//register (add) property block listener
status = CMIOObjectAddPropertyListenerBlock(deviceID, &propertyStruct, dispatch_get_main_queue(), listenerBlock);
if(noErr != status)
{
//err msg
//TODO: add
//bail
goto bail;
}
//happy
bRegistered = YES;
//bail
bail:
return bRegistered;
}
//determine if audio is active
// ->sets 'audioActive' iVar
-(void)setAudioDevStatus:(AudioObjectID)deviceID
{
//status var
OSStatus status = -1;
//running flag
UInt32 isRunning = -1;
//size of query flag
UInt32 propertySize = 0;
//init size
propertySize = sizeof(isRunning);
//query to get 'kAudioDevicePropertyDeviceIsRunningSomewhere' status
status = AudioDeviceGetProperty(deviceID, 0, false, kAudioDevicePropertyDeviceIsRunningSomewhere, &propertySize, &isRunning);
if(noErr != status)
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"getting status of audio device failed with %d", status]);
//set error
isRunning = -1;
//bail
goto bail;
}
//set iVar
self.audioActive = isRunning;
//bail
bail:
return;
}
//helper function
// ->determines if audio went active/inactive then invokes notification generator method
-(void)handleAudioNotification:(AudioObjectID)deviceID
{
//event dictionary
NSMutableDictionary* event = nil;
//init dictionary
event = [NSMutableDictionary dictionary];
//sync
@synchronized (self)
{
//set status
[self setAudioDevStatus:deviceID];
//send msg to status menu
// ->update menu to show (all) devices & their status
[((AppDelegate*)[[NSApplication sharedApplication] delegate]).statusBarMenuController updateStatusItemMenu:@[@{EVENT_DEVICE:self.mic, EVENT_DEVICE_STATUS:@(self.audioActive)},@{EVENT_DEVICE:self.camera, EVENT_DEVICE_STATUS:@(self.videoActive)}]];
//add device
event[EVENT_DEVICE] = self.mic;
//set device status
event[EVENT_DEVICE_STATUS] = [NSNumber numberWithInt:self.audioActive];
//dbg msg
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"got audio change notification; is running? %x", self.audioActive]);
//generate notification
[self generateNotification:event];
}
//bail
bail:
return;
}
//register for audio notifcations
// ->block will invoke method on event
-(BOOL)watchAudio:(AudioObjectID)deviceID
{
//ret var
BOOL bRegistered = NO;
//property struct
AudioObjectPropertyAddress propertyStruct = {0};
//init property struct's selector
propertyStruct.mSelector = kAudioDevicePropertyDeviceIsRunningSomewhere;
//init property struct's scope
propertyStruct.mScope = kAudioObjectPropertyScopeGlobal;
//init property struct's element
propertyStruct.mElement = kAudioObjectPropertyElementMaster;
//block
// ->invoked when audio changes & just calls helper function
AudioObjectPropertyListenerBlock listenerBlock = ^(UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses)
{
[self handleAudioNotification:deviceID];
};
OSStatus ret = AudioObjectAddPropertyListenerBlock(deviceID, &propertyStruct, dispatch_get_main_queue(), listenerBlock);
if (ret)
{
abort(); // FIXME
}
//happy
bRegistered = YES;
//bail
bail:
return bRegistered;
}
//build and display notification
-(void)generateNotification:(NSDictionary*)event
{
//notification
NSUserNotification* notification = nil;
//title
NSMutableString* title = nil;
//details
NSMutableString* details = nil;
//process name
NSString* processName = nil;
//alloc notificaiton
notification = [[NSUserNotification alloc] init];
//alloc title
title = [NSMutableString string];
//alloc details
details = [NSMutableString string];
//set title
// ->audio device
if(YES == [event[EVENT_DEVICE] isKindOfClass:NSClassFromString(@"AVCaptureHALDevice")])
{
//add
[title appendString:@"Audio Device"];
}
//add source
// ->video device
else
{
//add
[title appendString:@"Video Device"];
}
//add action
// ->device went inactive
if(YES == [DEVICE_INACTIVE isEqual:event[EVENT_DEVICE_STATUS]])
{
//add
[title appendString:@" became inactive"];
}
//add action
// ->device went active
else
{
//add
[title appendString:@" became active"];
}
//customize buttons
// ->for mic or inactive events, just say 'ok'
if( (YES == [event[EVENT_DEVICE] isKindOfClass:NSClassFromString(@"AVCaptureHALDevice")]) ||
(YES == [DEVICE_INACTIVE isEqual:event[EVENT_DEVICE_STATUS]]) )
{
//set other button title
notification.otherButtonTitle = @"ok";
//remove action button
notification.hasActionButton = NO;
}
//customize buttons
// ->for activatated video; allow/block
else
{
//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]]];
}
//icon issues
//http://stackoverflow.com/questions/11856766/osx-notification-center-icon
//contentImage for process icon?
//custom icon?
//http://stackoverflow.com/questions/24923979/nsusernotification-customisable-app-icon
//icon set automatically?
//[notification set]
//set title
[notification setTitle:title];
//set subtitle
[notification setSubtitle:((AVCaptureDevice*)event[EVENT_DEVICE]).localizedName];
//set details
// ->name of process using it / icon too?
//[notification setInformativeText:@"some process (1337)"];
//set notification
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
//deliver notification
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
//bail
bail:
return;
}
//always present notifications
-(BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification
{
return YES;
}
//automatically invoked when user interacts w/ the notification popup
// ->only action we care about, is killing the process if they click 'block'
-(void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification
{
//xpc connection
__block NSXPCConnection* xpcConnection = nil;
//process id
NSNumber* processID = nil;
//dbg msg
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"use responded to notification: %@", notification]);
//for video
// ->kill process if user clicked 'block'
if( (YES == [notification.actionButtonTitle isEqualToString:@"block"]) &&
(notification.activationType == NSUserNotificationActivationTypeActionButtonClicked))
{
//extract process id
processID = notification.userInfo[EVENT_PROCESS_ID];
if(nil == processID)
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to extract process id from notification, %@", notification.userInfo]);
//bail
goto bail;
}
//alloc XPC connection
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"];
//set remote object interface
xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)];
//resume
[xpcConnection resume];
//dbg msg
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"invoking XPC method to kill: %@", processID]);
//invoke XPC method 'killProcess' to terminate
[[xpcConnection remoteObjectProxy] killProcess:processID reply:^(BOOL wasKilled)
{
//check for err
if(YES != wasKilled)
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to kill/block: %@", processID]);
}
//close connection
[xpcConnection invalidate];
//nil out
xpcConnection = nil;
}];
}//user clicked 'block'
//bail
bail:
return;
}
//monitor for new procs (video only at the moment)
// ->runs until video is no longer in use (set elsewhere)
-(void)monitor4Procs
{
//xpc connection
NSXPCConnection* xpcConnection = nil;
//wait semaphore
dispatch_semaphore_t waitSema = nil;
//dbg msg
logMsg(LOG_DEBUG, @"video is active, so polling for new procs");
//alloc XPC connection
xpcConnection = [[NSXPCConnection alloc] initWithServiceName:@"com.objective-see.OverSightXPC"];
//set remote object interface
xpcConnection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)];
//set classes
// ->arrays/numbers ok to vend
[xpcConnection.remoteObjectInterface setClasses: [NSSet setWithObjects: [NSMutableArray class], [NSNumber class], nil]
forSelector: @selector(getVideoProcs:) argumentIndex: 0 ofReply: YES];
//resume
[xpcConnection resume];
//poll while video is active
while(YES == self.videoActive)
{
//init wait semaphore
waitSema = dispatch_semaphore_create(0);
//dbg msg
logMsg(LOG_DEBUG, @"asking XPC for (new) video procs");
//invoke XPC service to get (new) video procs
// ->will generate user notifications for any new processes
[[xpcConnection remoteObjectProxy] getVideoProcs:^(NSMutableArray* videoProcesses)
{
//dbg msg
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"new video procs: %@", videoProcesses]);
//generate a notification for each process
// ->double check video is still active though...
for(NSNumber* processID in videoProcesses)
{
//check video
if(YES != self.videoActive)
{
//exit loop
break;
}
//generate notification
[self generateNotification:@{EVENT_DEVICE:self.camera, EVENT_DEVICE_STATUS:DEVICE_ACTIVE, EVENT_PROCESS_ID:processID}];
}
//signal sema
dispatch_semaphore_signal(waitSema);
}];
//wait until XPC is done
// ->XPC reply block will signal semaphore
dispatch_semaphore_wait(waitSema, DISPATCH_TIME_FOREVER);
//nap
[NSThread sleepForTimeInterval:5.0f];
}//run until video (camera) is off
//bail
bail:
//close connection
[xpcConnection invalidate];
//nil out
xpcConnection = nil;
//dbg msg
logMsg(LOG_DEBUG, @"exiting monitor thread");
return;
}
@end

28
LoginItem/AppDelegate.h Normal file
View File

@ -0,0 +1,28 @@
//
// AppDelegate.h
// Test Application Helper
//
// Created by Patrick Wardle on 9/10/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import "AVMonitor.h"
#import "EventMonitor.h"
#import "StatusBarMenu.h"
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
/* PROPERTIES */
//status bar menu
@property(nonatomic, retain)StatusBarMenu* statusBarMenuController;
//(camera event) monitor class
@property(nonatomic, retain)EventMonitor* monitor;
@property(nonatomic, retain)AVMonitor* avMonitor;
@end

68
LoginItem/AppDelegate.m Normal file
View File

@ -0,0 +1,68 @@
//
// AppDelegate.m
// Test Application Helper
//
// Created by Patrick Wardle on 9/10/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import "Logging.h"
#import "AppDelegate.h"
@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
@end
@implementation AppDelegate
@synthesize monitor;
@synthesize avMonitor;
@synthesize statusBarMenuController;
//app's main interface
// ->load status bar and kick off monitor
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
//dbg msg
logMsg(LOG_DEBUG, @"starting login item");
//init/load status bar
[self loadStatusBar];
//dbg msg
logMsg(LOG_DEBUG, @"initialized/loaded status bar (icon/menu)");
//create/init av event monitor
avMonitor = [[AVMonitor alloc] init];
//dbg msg
logMsg(LOG_DEBUG, @"alloc/init'd AV monitor");
//start monitoring
// ->sets up audio/video callbacks
[avMonitor monitor];
//dbg msg
logMsg(LOG_DEBUG, @"AV monitor off and running");
return;
}
//initialize status menu bar
-(void)loadStatusBar
{
//alloc/load nib
statusBarMenuController = [[StatusBarMenu alloc] init];
//init menu
[self.statusBarMenuController setupStatusItem];
return;
}
@end

View File

@ -0,0 +1,666 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6245" systemVersion="13F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6245"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate"/>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="Test Application Helper" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Test Application Helper" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About Test Application Helper" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide Test Application Helper" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit Test Application Helper" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="dMs-cI-mzQ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="bib-Uj-vzu">
<items>
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
<connections>
<action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
<connections>
<action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="tXI-mr-wws">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
<items>
<menuItem title="Clear Menu" id="vNY-rz-j42">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
<connections>
<action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
</connections>
</menuItem>
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
<connections>
<action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
</connections>
</menuItem>
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
<connections>
<action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" id="KaW-ft-85H">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
<connections>
<action selector="print:" target="-1" id="qaZ-4w-aoO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="jxT-CU-nIS">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
<items>
<menuItem title="Font" id="Gi5-1S-RQB">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
<connections>
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
<connections>
<action selector="underline:" target="-1" id="FYS-2b-JAY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
<menuItem title="Kern" id="jBQ-r6-VK2">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
<items>
<menuItem title="Use Default" id="GUa-eO-cwY">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
</connections>
</menuItem>
<menuItem title="Use None" id="cDB-IK-hbR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="46P-cB-AYj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="ogc-rX-tC1">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligatures" id="o6e-r0-MWq">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
<items>
<menuItem title="Use Default" id="agt-UL-0e3">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
</connections>
</menuItem>
<menuItem title="Use None" id="J7y-lM-qPV">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
</connections>
</menuItem>
<menuItem title="Use All" id="xQD-1f-W4t">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="OaQ-X3-Vso">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
<items>
<menuItem title="Use Default" id="3Om-Ey-2VK">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="Rqc-34-cIF">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="I0S-gh-46l">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
</connections>
</menuItem>
<menuItem title="Raise" id="2h7-ER-AoG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
</connections>
</menuItem>
<menuItem title="Lower" id="1tx-W0-xDw">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="Fal-I4-PZk">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="d9c-me-L2H">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
<connections>
<action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
<connections>
<action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
</connections>
</menuItem>
<menuItem title="Justify" id="J5U-5w-g23">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
<connections>
<action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
<menuItem title="Writing Direction" id="H1b-Si-o9J">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
<items>
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="YGs-j5-SAR">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
</connections>
</menuItem>
<menuItem id="Lbh-J2-qVU">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
</connections>
</menuItem>
<menuItem id="jFq-tB-4Kx">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="Nop-cj-93Q">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
</connections>
</menuItem>
<menuItem id="BgM-ve-c93">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
</connections>
</menuItem>
<menuItem id="RB4-Sm-HuC">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
<menuItem title="Show Ruler" id="vLm-3I-IUL">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="Test Application Helper Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</objects>
</document>

57
LoginItem/EventMonitor.h Normal file
View File

@ -0,0 +1,57 @@
//
// ProcessMonitor.h
// BlockBlock
//
// Created by Patrick Wardle on 10/19/14.
// Copyright (c) 2015 Objective-See. All rights reserved.
//
#import <Foundation/Foundation.h>
//#import "OrderedDictionary.h"
/*
//custom struct
// format of data that's broadcast from kext
struct processStartEvent
{
//process pid
// ->id's all chunks
pid_t pid;
//process uid
uid_t uid;
//process ppid
pid_t ppid;
//process path
char path[0];
};
*/
@interface EventMonitor : NSObject
{
}
/* PROPERTIES */
//process list
//@property(nonatomic, retain)OrderedDictionary* processList;
//dictionary of partial events
//@property(nonatomic, retain)NSMutableDictionary* partialProcessEvents;
/* METHODS */
//kicks off thread to monitor
-(BOOL)monitor;
//thread function
// ->recv() process creation notification events from kext
-(void)recvProcNotifications;
@end

351
LoginItem/EventMonitor.m Normal file
View File

@ -0,0 +1,351 @@
//
// ProcessMonitor.m
// BlockBlock
//
// Created by Patrick Wardle on 10/19/14.
// Copyright (c) 2015 Objective-See. All rights reserved.
//
#import "Consts.h"
#import "Logging.h"
//#import "Process.h"
#import "Utilities.h"
#import "EventMonitor.h"
//#import "OrderedDictionary.h"
#import <stdio.h>
#import <stdlib.h>
#import <arpa/inet.h>
#import <sys/ioctl.h>
#import <sys/socket.h>
#import <sys/kern_event.h>
//TODO: move into shared file
//vendor id string
#define OBJECTIVE_SEE_VENDOR "com.objectiveSee"
//process camera event
#define PROCESS_CAMERA_EVENT 0x1
@implementation EventMonitor
//@synthesize processList;
//@synthesize partialProcessEvents;
//init
-(id)init
{
//init super
self = [super init];
if(nil != self)
{
//init process list
//processList = [[OrderedDictionary alloc] init];
//init partial process event dictionary
//partialProcessEvents = [NSMutableDictionary dictionary];
}
return self;
}
//kick off threads to monitor
// ->dtrace/audit pipe/app callback
-(BOOL)monitor
{
//return var
BOOL bRet = NO;
//start thread to get process creation notifications from kext
[NSThread detachNewThreadSelector:@selector(recvProcNotifications) toTarget:self withObject:nil];
//no errors
bRet = YES;
//bail
bail:
return bRet;
}
//thread function
// ->recv() process creation notification events
-(void)recvProcNotifications
{
//status var
int status = -1;
//system socket
int systemSocket = -1;
//struct for vendor code
// ->set via call to ioctl/SIOCGKEVVENDOR
struct kev_vendor_code vendorCode = {0};
//struct for kernel request
// ->set filtering options
struct kev_request kevRequest = {0};
//struct for broadcast data from the kext
struct kern_event_msg *kernEventMsg = {0};
//message from kext
// ->size is cumulation of header, pid
char kextMsg[KEV_MSG_HEADER_SIZE + sizeof(pid_t)] = {0};
//bytes received from system socket
ssize_t bytesReceived = -1;
//process path
// ->could be partial...
char path[MAXPATHLEN+1] = {0};
//length of path bytes
// ->might not be NULL terminated, so have to calc manually
int pathLength = 0;
//cumulative path
// ->when path is long & thus chunked
NSMutableString* cumulativePath = nil;
//custom struct
// ->process data from kext
//struct processStartEvent* procStartEvent = NULL;
//pid of process that triggered alert
pid_t triggerProcess = -1;
//process info
//NSMutableDictionary* procInfo = nil;
//process object
//Process* processObj = nil;
//create system socket
systemSocket = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT);
if(-1 == systemSocket)
{
//set status var
status = errno;
//err msg
//logMsg(LOG_ERR, [NSString stringWithFormat:@"socket() failed with %d", status]);
//bail
goto bail;
}
//set vendor name string
strncpy(vendorCode.vendor_string, OBJECTIVE_SEE_VENDOR, KEV_VENDOR_CODE_MAX_STR_LEN);
//get vendor name -> vendor code mapping
status = ioctl(systemSocket, SIOCGKEVVENDOR, &vendorCode);
if(0 != status)
{
//err msg
//logMsg(LOG_ERR, [NSString stringWithFormat:@"ioctl(...,SIOCGKEVVENDOR,...) failed with %d", status]);
//goto bail;
goto bail;
}
//init filtering options
// ->only interested in objective-see's events
kevRequest.vendor_code = vendorCode.vendor_code;
//...any class
kevRequest.kev_class = KEV_ANY_CLASS;
//...any subclass
kevRequest.kev_subclass = KEV_ANY_SUBCLASS;
//tell kernel what we want to filter on
status = ioctl(systemSocket, SIOCSKEVFILT, &kevRequest);
if(0 != status)
{
//err msg
//logMsg(LOG_ERR, [NSString stringWithFormat:@"ioctl(...,SIOCSKEVFILT,...) failed with %d", status]);
//goto bail;
goto bail;
}
//dbg msg
//logMsg(LOG_DEBUG, @"created system socket & set options, now entering recv() loop");
//foreverz
// ->listen/parse process creation events from kext
while(YES)
{
//ask the kext for process began events
// ->will block until event is ready
bytesReceived = recv(systemSocket, kextMsg, sizeof(kextMsg), 0);
//type cast
// ->to access kev_event_msg header
kernEventMsg = (struct kern_event_msg*)kextMsg;
//sanity check
// ->make sure data recv'd looks ok, sizewise
if( (bytesReceived < KEV_MSG_HEADER_SIZE) ||
(bytesReceived != kernEventMsg->total_size))
{
//dbg msg
//logMsg(LOG_DEBUG, [NSString stringWithFormat:@"recv count: %d, wanted: %d", (int)bytesReceived, kernEventMsg->total_size]);
//ignore
continue;
}
//only care about 'process began' events
if(PROCESS_CAMERA_EVENT != kernEventMsg->event_code)
{
//skip
continue;
}
//dbg msg
//logMsg(LOG_DEBUG, [NSString stringWithFormat:@"got msg from kernel, size: %d", kernEventMsg->total_size]);
//zero out process path
bzero(path, sizeof(path));
//typecast custom data
// ->begins right after header
//procStartEvent = (struct processStartEvent*)&kernEventMsg->event_data[0];
//dbg msg(s)
//logMsg(LOG_DEBUG, [NSString stringWithFormat:@" path (in this chunk): %s \n", procStartEvent->path]);
//logMsg(LOG_DEBUG, [NSString stringWithFormat:@" pid: %d ppid: %d uid: %d\n\n", procStartEvent->pid, procStartEvent->ppid, procStartEvent->uid]);
//init proc info dictionary
//procInfo = [NSMutableDictionary dictionary];
triggerProcess = (pid_t)kernEventMsg->event_data[0];
NSLog(@"proc %d triggered alert", triggerProcess);
/*
//save pid
procInfo[@"pid"] = [NSNumber numberWithInt:procStartEvent->pid];
//save uid
procInfo[@"uid"] = [NSNumber numberWithInt:procStartEvent->uid];
//save ppid
procInfo[@"ppid"] = [NSNumber numberWithInt:procStartEvent->ppid];
//calc number of bytes in path
// ->might not be NULL terminated, so have to do it manually
pathLength = kernEventMsg->total_size - KEV_MSG_HEADER_SIZE - sizeof(pid_t) - sizeof(uid_t) - sizeof(pid_t);
//sanity check
// ->should never happen...
if(pathLength > MAXPATHLEN)
{
//ignore
continue;
}
//copy path into buffer
memcpy(path, (const char*)procStartEvent->path, pathLength);
//NULL terminate it
path[pathLength] = 0x0;
//final chunk will end in NULL
if(0x0 == *(((char*)kernEventMsg->event_data) + kernEventMsg->total_size-KEV_MSG_HEADER_SIZE-1))
{
//check if there are path components
// ->pid is key into partial process list
cumulativePath = self.partialProcessEvents[procInfo[@"pid"]];
//finalize path
// ->append this last path component chunk
if(nil != cumulativePath)
{
//append
[cumulativePath appendString:[NSString stringWithUTF8String:path]];
//set final path
procInfo[@"path"] = cumulativePath;
//remove entry from partial list
[self.partialProcessEvents removeObjectForKey:procInfo[@"pid"]];
}
//path wasn't chunked
// ->can just use path as is
else
{
//set final path
procInfo[@"path"] = [NSString stringWithUTF8String:path];
}
//dbg msg
//logMsg(LOG_DEBUG, [NSString stringWithFormat:@"new process: %@", procInfo[@"path"]]);
//now, create process object
processObj = [[Process alloc] initWithPid:procStartEvent->pid infoDictionary:procInfo];
//sync process list
// ->then insert it into list
@synchronized(self.processList)
{
//trim list if needed
if(self.processList.count >= PROCESS_LIST_MAX_SIZE)
{
//toss first (oldest) item
[self.processList removeObjectForKey:[self.processList keyAtIndex:0]];
}
//insert process at end
[self.processList addObject:processObj forKey:procInfo[@"pid"] atStart:NO];
}//sync
}//final chunk
//non-final chunk
// ->insert it, or append to partial process list
else
{
//try grab existing (partial) path
cumulativePath = self.partialProcessEvents[procInfo[@"pid"]];
//check if there are path components already...
// ->i.e. this is just another chunk, so append
if(nil != cumulativePath)
{
//append
[cumulativePath appendString:[NSString stringWithUTF8String:path]];
}
//new process
// ->insert into partial process list
else
{
//insert
self.partialProcessEvents[procInfo[@"pid"]] = [NSMutableString stringWithUTF8String:path];
}
}//non-final chunk
*/
}//while(YES)
//bail
bail:
//close socket
if(-1 != systemSocket)
{
//close
close(systemSocket);
}
return;
}
@end

View File

@ -0,0 +1,68 @@
{
"images" : [
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "icon_16x16.png",
"scale" : "1x"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "icon_16x16@2x.png",
"scale" : "2x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "icon_32x32.png",
"scale" : "1x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "icon_32x32@2x.png",
"scale" : "2x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "icon_128x128.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "icon_128x128@2x.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "icon_256x256.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "icon_256x256@2x.png",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "icon_512x512.png",
"scale" : "1x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "icon_512x512@2x.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

38
LoginItem/Info.plist Normal file
View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<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>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
<key>LSBackgroundOnly</key>
<true/>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright (c) 2016 Objective-See. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSUserNotificationAlertStyle</key>
<string>alert</string>
</dict>
</plist>

View File

@ -0,0 +1,18 @@
{
"images" : [
{
"idiom" : "mac",
"filename" : "statusIcon.png",
"scale" : "1x"
},
{
"idiom" : "mac",
"filename" : "statusIcon@2x.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

49
LoginItem/StatusBarMenu.h Normal file
View File

@ -0,0 +1,49 @@
//
// StatusBarMenu.h
// OverSight
//
// Created by Patrick Wardle on 1/4/15.
// Copyright (c) 2015 Objective-See. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface StatusBarMenu : NSObject
{
}
//status item
@property (nonatomic, strong, readwrite) NSStatusItem *statusItem;
//popover
@property (retain, nonatomic)NSPopover *popover;
//enabled flag
@property BOOL isEnabled;
/* METHODS */
//setup status item
// ->init button, show popover, etc
-(void)setupStatusItem;
//create/update status item menu
-(void)updateStatusItemMenu:(NSArray*)devices;
//init a menu item
-(NSMenuItem*)initializeMenuItem:(NSString*)title action:(SEL)action;
//menu handler for 'enable'/'disable'
// ->toggle blockblock & update menu
//-(void)toggle:(id)sender;
//menu handler for 'uninstall'
// ->kick off uninstall
//-(void)uninstall:(id)sender;
//menu handler for 'perferences'
// ->show preferences window
-(void)preferences:(id)sender;
@end

295
LoginItem/StatusBarMenu.m Normal file
View File

@ -0,0 +1,295 @@
//
// StatusBarMenu.m
// OverSight
//
// Created by Patrick Wardle on 1/4/15.
// Copyright (c) 2015 Objective-See. All rights reserved.
//
#import "Logging.h"
#import "AppDelegate.h"
#import "StatusBarMenu.h"
#import <CoreMediaIO/CMIOHardware.h>
#import <AVFoundation/AVFoundation.h>
@implementation StatusBarMenu
@synthesize isEnabled;
//init method
// ->set some intial flags, etc
-(id)init
{
//load from nib
self = [super init];
if(self != nil)
{
//set flag
self.isEnabled = YES;
}
return self;
}
//setup status item
// ->init button, show popover, etc
-(void)setupStatusItem
{
//status bar image
NSImage *image = nil;
//init status item
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
//init image
image = [NSImage imageNamed:@"statusIcon"];
//tell OS to handle image
// ->dark mode, etc
[image setTemplate:YES];
//set image
self.statusItem.image = image;
//init menu
// ->enumerator will re-invoke with devices and their status
[self updateStatusItemMenu:nil];
return;
}
//create/update status item menu
-(void)updateStatusItemMenu:(NSArray*)devices
{
//menu
NSMenu* menu = nil;
//array of active devices
NSMutableArray* activeDevices = nil;
//array of inactive devices
NSMutableArray* inactiveDevices = nil;
//string for device name/emoji
NSMutableString* deviceDetails = nil;
//alloc/init window
menu = [[NSMenu alloc] init];
//alloc array for active devices
activeDevices = [NSMutableArray array];
//alloc array for inactive devices
inactiveDevices = [NSMutableArray array];
//add status msg
[menu addItemWithTitle:@"OVERSIGHT: monitoring 🎤 + 📸" action:NULL keyEquivalent:@""];
//add top separator
[menu addItem:[NSMenuItem separatorItem]];
//iterate over all devices
// ->classify each, and built details string for menu
for(NSDictionary* device in devices)
{
//init string for name/etc
deviceDetails = [NSMutableString string];
//add device emoji
// ->audio device
if(YES == [device[EVENT_DEVICE] isKindOfClass:NSClassFromString(@"AVCaptureHALDevice")])
{
//add
[deviceDetails appendString:@" 🎤 "];
}
//add device emoji
// ->video device
else
{
//add
[deviceDetails appendString:@" 📸 "];
}
//add device name
[deviceDetails appendString:((AVCaptureDevice*)device[EVENT_DEVICE]).localizedName];
//classify
// ->active devices
if(DEVICE_ACTIVE.intValue == [device[EVENT_DEVICE_STATUS] intValue])
{
//add
[activeDevices addObject:deviceDetails];
}
//classify
// ->inactive devices
else
{
//add
[inactiveDevices addObject:deviceDetails];
}
}
//add active devices to menu
if(0 != activeDevices.count)
{
//add title
[menu addItem:[self initializeMenuItem:@"Active Devices" action:NULL]];
//add each
for(NSString* deviceDetails in activeDevices)
{
[menu addItem:[self initializeMenuItem:deviceDetails action:NULL]];
}
}
//when no active devices
// ->add title to reflect that fact
else
{
//add
[menu addItem:[self initializeMenuItem:@"No Active Devices" action:NULL]];
}
//add item separator
[menu addItem:[NSMenuItem separatorItem]];
//add inactive devices to menu
if(0 != inactiveDevices.count)
{
//add title
[menu addItem:[self initializeMenuItem:@"Inactive Devices" action:NULL]];
//add each
for(NSString* deviceDetails in inactiveDevices)
{
[menu addItem:[self initializeMenuItem:deviceDetails action:NULL]];
}
}
//when no inactive devices
// ->add title to reflect that fact
else
{
//add
[menu addItem:[self initializeMenuItem:@"No Inactive Devices" action:NULL]];
}
//add item separator
[menu addItem:[NSMenuItem separatorItem]];
//create/add menu item
// ->'preferences'
[menu addItem:[self initializeMenuItem:@"Preferences" action:@selector(preferences:)]];
//add bottom separator
[menu addItem:[NSMenuItem separatorItem]];
//create/add menu item
// ->'about'
[menu addItem:[self initializeMenuItem:@"Quit" action:@selector(quit:)]];
//tie menu to status item
self.statusItem.menu = menu;
return;
}
//init a menu item
-(NSMenuItem*)initializeMenuItem:(NSString*)title action:(SEL)action
{
//menu item
NSMenuItem* menuItem = nil;
//alloc menu item
// ->toggle ('enable'/'disable')
menuItem = [[NSMenuItem alloc] initWithTitle:title action:action keyEquivalent:@""];
//enabled
menuItem.enabled = YES;
//self
menuItem.target = self;
return menuItem;
}
#pragma mark - Menu actions
//handler for 'quit'
// ->just exit the application
-(void)quit:(id)sender
{
//dbg msg
logMsg(LOG_DEBUG, @"user clicked 'exit', so goodbye!");
//exit
[[NSApplication sharedApplication] terminate:nil];
return;
}
//handler for 'preferences' menu item
// ->launch main application which will show prefs
-(void)preferences:(id)sender
{
//this works when packaged into Login Item into top-level app
NSArray *pathComponents = [[[NSBundle mainBundle] bundlePath] pathComponents];
pathComponents = [pathComponents subarrayWithRange:NSMakeRange(0, [pathComponents count] - 4)];
NSString *path = [NSString pathWithComponents:pathComponents];
[[NSWorkspace sharedWorkspace] launchApplication:path];
/*
//controller for preferences window
PrefsWindowController* prefsWindowController = nil;
//dbg msg
logMsg(LOG_DEBUG, @"displaying preferences window");
//grab controller
prefsWindowController = ((AppDelegate*)[[NSApplication sharedApplication] delegate]).prefsWindowController;
//show pref window
[prefsWindowController showWindow:sender];
//invoke function in background that will make window modal
// ->waits until window is non-nil
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//make modal
makeModal(prefsWindowController);
});
*/
return;
}
//menu handler that's automatically invoked when user clicks 'about'
// ->load objective-see's documentation for BlockBlock
-(void)about:(id)sender
{
/*
//alloc/init about window
infoWindowController = [[InfoWindowController alloc] initWithWindowNibName:@"InfoWindow"];
//configure label and button
[self.infoWindowController configure:[NSString stringWithFormat:@"version: %@", getAppVersion()] buttonTitle:@"more info"];
//center window
[[self.infoWindowController window] center];
//show it
[self.infoWindowController showWindow:self];
*/
return;
}
@end

13
LoginItem/main.m Normal file
View File

@ -0,0 +1,13 @@
//
// main.m
// Test Application Helper
//
// Created by Patrick Wardle on 9/10/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[]) {
return NSApplicationMain(argc, argv);
}

16
MainApp/AppDelegate.h Normal file
View File

@ -0,0 +1,16 @@
//
// AppDelegate.h
// Test Application
//
// Created by Patrick Wardle on 9/10/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
@property (weak) IBOutlet NSButton *loginButton;
@end

49
MainApp/AppDelegate.m Normal file
View File

@ -0,0 +1,49 @@
//
// AppDelegate.m
// Test Application
//
// Created by Patrick Wardle on 9/10/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import "AppDelegate.h"
#import <ServiceManagement/ServiceManagement.h>
@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
@end
//TODO: add uninstall?
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
self.loginButton.state = [[NSUserDefaults standardUserDefaults] boolForKey:@"LoginEnabled"];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification
{
// Insert code here to tear down your application
}
//automatically close when user closes window
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication
{
return YES;
}
- (IBAction)loginButtonPressed:(NSButton *)sender
{
if (!SMLoginItemSetEnabled((__bridge CFStringRef)@"com.objective-see.OverSightHelper", [sender state])) {
NSLog(@"Login Item Was Not Successful");
}
[[NSUserDefaults standardUserDefaults] setBool:[sender state] forKey:@"LoginEnabled"];
}
@end

View File

@ -0,0 +1,714 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15G1004" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate">
<connections>
<outlet property="loginButton" destination="rVS-uv-DNf" id="uR0-zX-h7m"/>
<outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
<items>
<menuItem title="OverSight" id="1Xt-HY-uBw">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="OverSight" systemMenu="apple" id="uQy-DD-JDr">
<items>
<menuItem title="About OverSight" id="5kV-Vb-QxS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
<menuItem title="Services" id="NMo-om-nkz">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
<menuItem title="Hide OverSight" keyEquivalent="h" id="Olw-nP-bQN">
<connections>
<action selector="hide:" target="-1" id="PnN-Uc-m68"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
</connections>
</menuItem>
<menuItem title="Show All" id="Kd2-mp-pUS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit OverSight" keyEquivalent="q" id="4sb-4s-VLi">
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="dMs-cI-mzQ">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="File" id="bib-Uj-vzu">
<items>
<menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
<connections>
<action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
<connections>
<action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="tXI-mr-wws">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="oas-Oc-fiZ">
<items>
<menuItem title="Clear Menu" id="vNY-rz-j42">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="clearRecentDocuments:" target="-1" id="Daa-9d-B3U"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
<menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
<connections>
<action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
</connections>
</menuItem>
<menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
<connections>
<action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
</connections>
</menuItem>
<menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
<connections>
<action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" id="KaW-ft-85H">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
<menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
<connections>
<action selector="print:" target="-1" id="qaZ-4w-aoO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="5QF-Oa-p0T">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Edit" id="W48-6f-4Dl">
<items>
<menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
<connections>
<action selector="undo:" target="-1" id="M6e-cu-g7V"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
<connections>
<action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
<menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
<connections>
<action selector="cut:" target="-1" id="YJe-68-I9s"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
<connections>
<action selector="copy:" target="-1" id="G1f-GL-Joy"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
<connections>
<action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
</connections>
</menuItem>
<menuItem title="Delete" id="pa3-QI-u2k">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
<connections>
<action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
<menuItem title="Find" id="4EN-yA-p0u">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Find" id="1b7-l0-nxx">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
<connections>
<action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
<connections>
<action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
</connections>
</menuItem>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
<connections>
<action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
<connections>
<action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
<connections>
<action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
<connections>
<action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
<menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="9ic-FL-obx">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
<items>
<menuItem title="Show Substitutions" id="z6F-FW-3nz">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
<menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" id="hQb-2v-fYv">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="rgM-f4-ycn">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
</connections>
</menuItem>
<menuItem title="Smart Links" id="cwL-P1-jid">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
</connections>
</menuItem>
<menuItem title="Data Detectors" id="tRr-pd-1PS">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="HFQ-gK-NFA">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Transformations" id="2oI-Rn-ZJC">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Transformations" id="c8a-y6-VQd">
<items>
<menuItem title="Make Upper Case" id="vmV-6d-7jI">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="d9M-CD-aMd">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="UEZ-Bs-lqG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="xrE-MZ-jX0">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Speech" id="3rS-ZA-NoH">
<items>
<menuItem title="Start Speaking" id="Ynk-f8-cLZ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="Oyz-dy-DGm">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="jxT-CU-nIS">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="GEO-Iw-cKr">
<items>
<menuItem title="Font" id="Gi5-1S-RQB">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
<connections>
<action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
<connections>
<action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
<connections>
<action selector="underline:" target="-1" id="FYS-2b-JAY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
<connections>
<action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
<menuItem title="Kern" id="jBQ-r6-VK2">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="tlD-Oa-oAM">
<items>
<menuItem title="Use Default" id="GUa-eO-cwY">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
</connections>
</menuItem>
<menuItem title="Use None" id="cDB-IK-hbR">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="46P-cB-AYj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="ogc-rX-tC1">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligatures" id="o6e-r0-MWq">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
<items>
<menuItem title="Use Default" id="agt-UL-0e3">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
</connections>
</menuItem>
<menuItem title="Use None" id="J7y-lM-qPV">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
</connections>
</menuItem>
<menuItem title="Use All" id="xQD-1f-W4t">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="OaQ-X3-Vso">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="ijk-EB-dga">
<items>
<menuItem title="Use Default" id="3Om-Ey-2VK">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="Rqc-34-cIF">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="I0S-gh-46l">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
</connections>
</menuItem>
<menuItem title="Raise" id="2h7-ER-AoG">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
</connections>
</menuItem>
<menuItem title="Lower" id="1tx-W0-xDw">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
<menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
<menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="Fal-I4-PZk">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="d9c-me-L2H">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
<connections>
<action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
<connections>
<action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
</connections>
</menuItem>
<menuItem title="Justify" id="J5U-5w-g23">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
<connections>
<action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
<menuItem title="Writing Direction" id="H1b-Si-o9J">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
<items>
<menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="YGs-j5-SAR">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
</connections>
</menuItem>
<menuItem id="Lbh-J2-qVU">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
</connections>
</menuItem>
<menuItem id="jFq-tB-4Kx">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
<menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem id="Nop-cj-93Q">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
</connections>
</menuItem>
<menuItem id="BgM-ve-c93">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
</connections>
</menuItem>
<menuItem id="RB4-Sm-HuC">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
<menuItem title="Show Ruler" id="vLm-3I-IUL">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="H8h-7b-M4v">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="View" id="HyV-fh-RgO">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="aUF-d1-5bR">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
<connections>
<action selector="performMiniaturize:" target="-1" id="VwT-WD-YPe"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="R4o-n2-Eq4">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="arrangeInFront:" target="-1" id="DRN-fu-gQh"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="wpr-3q-Mcd">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
<items>
<menuItem title="OverSight Help" keyEquivalent="?" id="FKE-Sm-Kum">
<connections>
<action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
<window title="OverSight Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="390" width="480" height="360"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1057"/>
<view key="contentView" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rVS-uv-DNf">
<rect key="frame" x="307" y="61" width="119" height="18"/>
<buttonCell key="cell" type="check" title="Launch at login" bezelStyle="regularSquare" imagePosition="left" inset="2" id="3gA-J8-849">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="loginButtonPressed:" target="Voe-Tx-rLC" id="rb1-Gh-rcU"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nf3-YB-f25">
<rect key="frame" x="0.0" y="0.0" width="201" height="360"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" drawsBackground="YES" id="zOz-NV-pht">
<font key="font" metaFont="system"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<imageView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kVc-EA-5LL">
<rect key="frame" x="46" y="0.0" width="325" height="65"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" image="overSight" id="WbC-HV-n62"/>
</imageView>
<imageView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ks1-o6-l6E">
<rect key="frame" x="9" y="169" width="183" height="183"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" image="icon" id="Cv4-hx-zrt"/>
</imageView>
</subviews>
</view>
</window>
</objects>
<resources>
<image name="icon" width="512" height="512"/>
<image name="overSight" width="1396" height="286"/>
</resources>
</document>

View File

@ -0,0 +1,58 @@
{
"images" : [
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "16x16",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "32x32",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "128x128",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "256x256",
"scale" : "2x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "1x"
},
{
"idiom" : "mac",
"size" : "512x512",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

34
MainApp/Info.plist Normal file
View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<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>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright (c) 2016 Objective-See. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

13
MainApp/main.m Normal file
View File

@ -0,0 +1,13 @@
//
// main.m
// Test Application
//
// Created by Patrick Wardle on 9/10/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[]) {
return NSApplicationMain(argc, argv);
}

View File

@ -0,0 +1,740 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
7D17C4941D658A790066232A /* AboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C48C1D658A790066232A /* AboutWindow.xib */; };
7D17C4951D658A790066232A /* AboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C48E1D658A790066232A /* AboutWindowController.m */; };
7D17C4971D658A790066232A /* Logging.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4931D658A790066232A /* Logging.m */; };
7D17C5271D6592FE0066232A /* statusIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C5141D658FEB0066232A /* statusIcon.png */; };
7D17C5281D6592FE0066232A /* statusIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C5151D658FEB0066232A /* statusIcon@2x.png */; };
7D17C5391D659E580066232A /* AboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C52C1D659E580066232A /* AboutWindow.xib */; };
7D17C53B1D659E580066232A /* Exception.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C5311D659E580066232A /* Exception.m */; };
7D17C53C1D659E580066232A /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C5331D659E580066232A /* icon.png */; };
7D17C53D1D659E580066232A /* overSight.png in Resources */ = {isa = PBXBuildFile; fileRef = 7D17C5341D659E580066232A /* overSight.png */; };
7D17C53F1D659E580066232A /* Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C5381D659E580066232A /* Utilities.m */; };
7D17CFE41D811B4E0017B475 /* Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C5381D659E580066232A /* Utilities.m */; };
7D17CFE51D8133840017B475 /* AVMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17CFE11D81121E0017B475 /* AVMonitor.m */; };
7D17D0101D8136C60017B475 /* OverSightXPC.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 7DC9C8121D641A350017D143 /* OverSightXPC.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
7D62457C1D84FB8900870565 /* Enumerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D62457B1D84FB8900870565 /* Enumerator.m */; };
7D62457D1D8517F500870565 /* Logging.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4931D658A790066232A /* Logging.m */; };
7D6245801D85348E00870565 /* Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D62457F1D85348E00870565 /* Utilities.m */; };
7D6245811D8537CF00870565 /* Logging.m in Sources */ = {isa = PBXBuildFile; fileRef = 7D17C4931D658A790066232A /* Logging.m */; };
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 */; };
7DC9C81D1D641A350017D143 /* OverSightXPC.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 7DC9C8121D641A350017D143 /* OverSightXPC.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
8B5755A119DA3E9500799E6B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B5755A019DA3E9500799E6B /* main.m */; };
8B5755A419DA3E9500799E6B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B5755A319DA3E9500799E6B /* AppDelegate.m */; };
8B5755A619DA3E9500799E6B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8B5755A519DA3E9500799E6B /* Images.xcassets */; };
8B5755A919DA3E9500799E6B /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8B5755A719DA3E9500799E6B /* MainMenu.xib */; };
8B5755C719DA3F9300799E6B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B5755C619DA3F9300799E6B /* main.m */; };
8B5755CA19DA3F9300799E6B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B5755C919DA3F9300799E6B /* AppDelegate.m */; };
8B5755CC19DA3F9300799E6B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8B5755CB19DA3F9300799E6B /* Images.xcassets */; };
8B5755CF19DA3F9300799E6B /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8B5755CD19DA3F9300799E6B /* MainMenu.xib */; };
8B5755E319DA41D300799E6B /* ServiceManagement.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B5755E219DA41D300799E6B /* ServiceManagement.framework */; };
8B5755E619DA432000799E6B /* OverSight Helper.app in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8B5755C219DA3F9300799E6B /* OverSight Helper.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
7D17CFE61D81349D0017B475 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 8B57559319DA3E9500799E6B /* Project object */;
proxyType = 1;
remoteGlobalIDString = 7DC9C8111D641A350017D143;
remoteInfo = OverSightXPC;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
7D17D00B1D81369C0017B475 /* Embed XPC Services */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices";
dstSubfolderSpec = 16;
files = (
7D17D0101D8136C60017B475 /* OverSightXPC.xpc in Embed XPC Services */,
);
name = "Embed XPC Services";
runOnlyForDeploymentPostprocessing = 0;
};
7DC9C8211D641A350017D143 /* Embed XPC Services */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "$(CONTENTS_FOLDER_PATH)/XPCServices";
dstSubfolderSpec = 16;
files = (
7DC9C81D1D641A350017D143 /* OverSightXPC.xpc in Embed XPC Services */,
);
name = "Embed XPC Services";
runOnlyForDeploymentPostprocessing = 0;
};
8B5755E519DA42C100799E6B /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = Contents/Library/LoginItems;
dstSubfolderSpec = 1;
files = (
8B5755E619DA432000799E6B /* OverSight Helper.app in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
7D17C48C1D658A790066232A /* AboutWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = AboutWindow.xib; path = Shared/AboutWindow.xib; sourceTree = SOURCE_ROOT; };
7D17C48D1D658A790066232A /* AboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AboutWindowController.h; path = Shared/AboutWindowController.h; sourceTree = SOURCE_ROOT; };
7D17C48E1D658A790066232A /* AboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AboutWindowController.m; path = Shared/AboutWindowController.m; sourceTree = SOURCE_ROOT; };
7D17C48F1D658A790066232A /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Consts.h; path = Shared/Consts.h; sourceTree = SOURCE_ROOT; };
7D17C4901D658A790066232A /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Exception.h; path = Shared/Exception.h; sourceTree = SOURCE_ROOT; };
7D17C4911D658A790066232A /* Exception.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Exception.m; path = Shared/Exception.m; sourceTree = SOURCE_ROOT; };
7D17C4921D658A790066232A /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = Shared/Logging.h; sourceTree = SOURCE_ROOT; };
7D17C4931D658A790066232A /* Logging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Logging.m; path = Shared/Logging.m; sourceTree = SOURCE_ROOT; };
7D17C5141D658FEB0066232A /* statusIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = statusIcon.png; path = Images/statusIcon.png; sourceTree = "<group>"; };
7D17C5151D658FEB0066232A /* statusIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "statusIcon@2x.png"; path = "Images/statusIcon@2x.png"; sourceTree = "<group>"; };
7D17C52C1D659E580066232A /* AboutWindow.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AboutWindow.xib; sourceTree = "<group>"; };
7D17C52D1D659E580066232A /* AboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutWindowController.h; sourceTree = "<group>"; };
7D17C52E1D659E580066232A /* AboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AboutWindowController.m; sourceTree = "<group>"; };
7D17C52F1D659E580066232A /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Consts.h; sourceTree = "<group>"; };
7D17C5301D659E580066232A /* Exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Exception.h; sourceTree = "<group>"; };
7D17C5311D659E580066232A /* Exception.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Exception.m; sourceTree = "<group>"; };
7D17C5331D659E580066232A /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = icon.png; sourceTree = "<group>"; };
7D17C5341D659E580066232A /* overSight.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = overSight.png; sourceTree = "<group>"; };
7D17C5351D659E580066232A /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = "<group>"; };
7D17C5361D659E580066232A /* Logging.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Logging.m; sourceTree = "<group>"; };
7D17C5371D659E580066232A /* Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utilities.h; sourceTree = "<group>"; };
7D17C5381D659E580066232A /* Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Utilities.m; sourceTree = "<group>"; };
7D17CFDE1D810D6D0017B475 /* XPCProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XPCProtocol.h; path = Shared/XPCProtocol.h; sourceTree = SOURCE_ROOT; };
7D17CFE11D81121E0017B475 /* AVMonitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AVMonitor.m; sourceTree = "<group>"; };
7D17CFE21D81121E0017B475 /* AVMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AVMonitor.h; sourceTree = "<group>"; };
7D62457A1D84FB8900870565 /* Enumerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Enumerator.h; sourceTree = "<group>"; };
7D62457B1D84FB8900870565 /* Enumerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Enumerator.m; sourceTree = "<group>"; };
7D62457E1D85348E00870565 /* Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utilities.h; sourceTree = "<group>"; };
7D62457F1D85348E00870565 /* Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Utilities.m; sourceTree = "<group>"; };
7DAF4B7C1D656FD3000DA31A /* StatusBarMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StatusBarMenu.h; sourceTree = "<group>"; };
7DAF4B7D1D656FD3000DA31A /* StatusBarMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StatusBarMenu.m; sourceTree = "<group>"; };
7DC9C8121D641A350017D143 /* OverSightXPC.xpc */ = {isa = PBXFileReference; explicitFileType = "wrapper.xpc-service"; includeInIndex = 0; path = OverSightXPC.xpc; sourceTree = BUILT_PRODUCTS_DIR; };
7DC9C8151D641A350017D143 /* OverSightXPC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OverSightXPC.h; sourceTree = "<group>"; };
7DC9C8161D641A350017D143 /* OverSightXPC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OverSightXPC.m; sourceTree = "<group>"; };
7DC9C8181D641A350017D143 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
7DC9C81A1D641A350017D143 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
8B57559B19DA3E9500799E6B /* OverSight.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OverSight.app; sourceTree = BUILT_PRODUCTS_DIR; };
8B57559F19DA3E9500799E6B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
8B5755A019DA3E9500799E6B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
8B5755A219DA3E9500799E6B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
8B5755A319DA3E9500799E6B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
8B5755A519DA3E9500799E6B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
8B5755A819DA3E9500799E6B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
8B5755C219DA3F9300799E6B /* OverSight Helper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "OverSight Helper.app"; sourceTree = BUILT_PRODUCTS_DIR; };
8B5755C519DA3F9300799E6B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
8B5755C619DA3F9300799E6B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
8B5755C819DA3F9300799E6B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
8B5755C919DA3F9300799E6B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
8B5755CB19DA3F9300799E6B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
8B5755CE19DA3F9300799E6B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
8B5755E219DA41D300799E6B /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
7DC9C80F1D641A350017D143 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
8B57559819DA3E9500799E6B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8B5755E319DA41D300799E6B /* ServiceManagement.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
8B5755BF19DA3F9300799E6B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
7D17C48B1D658A670066232A /* Shared */ = {
isa = PBXGroup;
children = (
7D17C48C1D658A790066232A /* AboutWindow.xib */,
7D17C48D1D658A790066232A /* AboutWindowController.h */,
7D17C48E1D658A790066232A /* AboutWindowController.m */,
7D17C48F1D658A790066232A /* Consts.h */,
7D17C4901D658A790066232A /* Exception.h */,
7D17C4911D658A790066232A /* Exception.m */,
7D17C4921D658A790066232A /* Logging.h */,
7D17C4931D658A790066232A /* Logging.m */,
);
name = Shared;
sourceTree = "<group>";
};
7D17C5131D658FE20066232A /* Images */ = {
isa = PBXGroup;
children = (
7D17C5141D658FEB0066232A /* statusIcon.png */,
7D17C5151D658FEB0066232A /* statusIcon@2x.png */,
);
name = Images;
sourceTree = "<group>";
};
7D17C52B1D659E580066232A /* Shared */ = {
isa = PBXGroup;
children = (
7D17C52C1D659E580066232A /* AboutWindow.xib */,
7D17C52D1D659E580066232A /* AboutWindowController.h */,
7D17C52E1D659E580066232A /* AboutWindowController.m */,
7D17C52F1D659E580066232A /* Consts.h */,
7D17C5301D659E580066232A /* Exception.h */,
7D17C5311D659E580066232A /* Exception.m */,
7D62457E1D85348E00870565 /* Utilities.h */,
7D62457F1D85348E00870565 /* Utilities.m */,
7D17C5321D659E580066232A /* Images */,
7D17C5351D659E580066232A /* Logging.h */,
7D17C5361D659E580066232A /* Logging.m */,
7D17C5371D659E580066232A /* Utilities.h */,
7D17C5381D659E580066232A /* Utilities.m */,
);
path = Shared;
sourceTree = "<group>";
};
7D17C5321D659E580066232A /* Images */ = {
isa = PBXGroup;
children = (
7D17C5331D659E580066232A /* icon.png */,
7D17C5341D659E580066232A /* overSight.png */,
);
path = Images;
sourceTree = "<group>";
};
7DC9C8131D641A350017D143 /* OverSightXPC */ = {
isa = PBXGroup;
children = (
7D62457A1D84FB8900870565 /* Enumerator.h */,
7D62457B1D84FB8900870565 /* Enumerator.m */,
7D17CFDE1D810D6D0017B475 /* XPCProtocol.h */,
7DC9C8151D641A350017D143 /* OverSightXPC.h */,
7DC9C8161D641A350017D143 /* OverSightXPC.m */,
7DC9C8181D641A350017D143 /* main.m */,
7DC9C81A1D641A350017D143 /* Info.plist */,
);
path = OverSightXPC;
sourceTree = "<group>";
};
8B57559219DA3E9500799E6B = {
isa = PBXGroup;
children = (
7D17C52B1D659E580066232A /* Shared */,
8B5755E219DA41D300799E6B /* ServiceManagement.framework */,
8B57559D19DA3E9500799E6B /* MainApp */,
8B5755C319DA3F9300799E6B /* LoginItem */,
7DC9C8131D641A350017D143 /* OverSightXPC */,
8B57559C19DA3E9500799E6B /* Products */,
);
sourceTree = "<group>";
};
8B57559C19DA3E9500799E6B /* Products */ = {
isa = PBXGroup;
children = (
8B57559B19DA3E9500799E6B /* OverSight.app */,
8B5755C219DA3F9300799E6B /* OverSight Helper.app */,
7DC9C8121D641A350017D143 /* OverSightXPC.xpc */,
);
name = Products;
sourceTree = "<group>";
};
8B57559D19DA3E9500799E6B /* MainApp */ = {
isa = PBXGroup;
children = (
8B5755A219DA3E9500799E6B /* AppDelegate.h */,
8B5755A319DA3E9500799E6B /* AppDelegate.m */,
8B5755A519DA3E9500799E6B /* Images.xcassets */,
8B5755A719DA3E9500799E6B /* MainMenu.xib */,
8B57559E19DA3E9500799E6B /* Supporting Files */,
);
path = MainApp;
sourceTree = "<group>";
};
8B57559E19DA3E9500799E6B /* Supporting Files */ = {
isa = PBXGroup;
children = (
8B57559F19DA3E9500799E6B /* Info.plist */,
8B5755A019DA3E9500799E6B /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
8B5755C319DA3F9300799E6B /* LoginItem */ = {
isa = PBXGroup;
children = (
7D17C5131D658FE20066232A /* Images */,
7D17C48B1D658A670066232A /* Shared */,
7DAF4B7C1D656FD3000DA31A /* StatusBarMenu.h */,
7DAF4B7D1D656FD3000DA31A /* StatusBarMenu.m */,
8B5755C819DA3F9300799E6B /* AppDelegate.h */,
8B5755C919DA3F9300799E6B /* AppDelegate.m */,
7D17CFE11D81121E0017B475 /* AVMonitor.m */,
7D17CFE21D81121E0017B475 /* AVMonitor.h */,
8B5755CB19DA3F9300799E6B /* Images.xcassets */,
8B5755CD19DA3F9300799E6B /* MainMenu.xib */,
8B5755C419DA3F9300799E6B /* Supporting Files */,
);
path = LoginItem;
sourceTree = "<group>";
};
8B5755C419DA3F9300799E6B /* Supporting Files */ = {
isa = PBXGroup;
children = (
8B5755C519DA3F9300799E6B /* Info.plist */,
8B5755C619DA3F9300799E6B /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
7DC9C8111D641A350017D143 /* OverSightXPC */ = {
isa = PBXNativeTarget;
buildConfigurationList = 7DC9C81E1D641A350017D143 /* Build configuration list for PBXNativeTarget "OverSightXPC" */;
buildPhases = (
7DC9C80E1D641A350017D143 /* Sources */,
7DC9C80F1D641A350017D143 /* Frameworks */,
7DC9C8101D641A350017D143 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = OverSightXPC;
productName = OverSightXPC;
productReference = 7DC9C8121D641A350017D143 /* OverSightXPC.xpc */;
productType = "com.apple.product-type.xpc-service";
};
8B57559A19DA3E9500799E6B /* OverSight */ = {
isa = PBXNativeTarget;
buildConfigurationList = 8B5755B819DA3E9500799E6B /* Build configuration list for PBXNativeTarget "OverSight" */;
buildPhases = (
8B57559719DA3E9500799E6B /* Sources */,
8B57559819DA3E9500799E6B /* Frameworks */,
8B57559919DA3E9500799E6B /* Resources */,
8B5755E519DA42C100799E6B /* CopyFiles */,
7DC9C8211D641A350017D143 /* Embed XPC Services */,
);
buildRules = (
);
dependencies = (
);
name = OverSight;
productName = "Test Application";
productReference = 8B57559B19DA3E9500799E6B /* OverSight.app */;
productType = "com.apple.product-type.application";
};
8B5755C119DA3F9300799E6B /* OverSight Helper */ = {
isa = PBXNativeTarget;
buildConfigurationList = 8B5755DC19DA3F9300799E6B /* Build configuration list for PBXNativeTarget "OverSight Helper" */;
buildPhases = (
8B5755BE19DA3F9300799E6B /* Sources */,
8B5755BF19DA3F9300799E6B /* Frameworks */,
8B5755C019DA3F9300799E6B /* Resources */,
7D17D00B1D81369C0017B475 /* Embed XPC Services */,
);
buildRules = (
);
dependencies = (
7D17CFE71D81349D0017B475 /* PBXTargetDependency */,
);
name = "OverSight Helper";
productName = "Test Application Helper";
productReference = 8B5755C219DA3F9300799E6B /* OverSight Helper.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
8B57559319DA3E9500799E6B /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0730;
ORGANIZATIONNAME = "Cory Bohon";
TargetAttributes = {
7DC9C8111D641A350017D143 = {
CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = VBG97UB4TA;
};
8B57559A19DA3E9500799E6B = {
CreatedOnToolsVersion = 6.0.1;
DevelopmentTeam = VBG97UB4TA;
SystemCapabilities = {
com.apple.Sandbox = {
enabled = 0;
};
};
};
8B5755C119DA3F9300799E6B = {
CreatedOnToolsVersion = 6.0.1;
DevelopmentTeam = VBG97UB4TA;
SystemCapabilities = {
com.apple.Sandbox = {
enabled = 0;
};
};
};
};
};
buildConfigurationList = 8B57559619DA3E9500799E6B /* Build configuration list for PBXProject "OverSight" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 8B57559219DA3E9500799E6B;
productRefGroup = 8B57559C19DA3E9500799E6B /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
8B57559A19DA3E9500799E6B /* OverSight */,
8B5755C119DA3F9300799E6B /* OverSight Helper */,
7DC9C8111D641A350017D143 /* OverSightXPC */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
7DC9C8101D641A350017D143 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
8B57559919DA3E9500799E6B /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8B5755A619DA3E9500799E6B /* Images.xcassets in Resources */,
7D17C53C1D659E580066232A /* icon.png in Resources */,
8B5755A919DA3E9500799E6B /* MainMenu.xib in Resources */,
7D17C53D1D659E580066232A /* overSight.png in Resources */,
7D17C4941D658A790066232A /* AboutWindow.xib in Resources */,
7D17C5391D659E580066232A /* AboutWindow.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
8B5755C019DA3F9300799E6B /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7D17C5271D6592FE0066232A /* statusIcon.png in Resources */,
7D17C5281D6592FE0066232A /* statusIcon@2x.png in Resources */,
8B5755CC19DA3F9300799E6B /* Images.xcassets in Resources */,
8B5755CF19DA3F9300799E6B /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
7DC9C80E1D641A350017D143 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7D6245811D8537CF00870565 /* Logging.m in Sources */,
7D17CFE41D811B4E0017B475 /* Utilities.m in Sources */,
7D62457C1D84FB8900870565 /* Enumerator.m in Sources */,
7DC9C8191D641A350017D143 /* main.m in Sources */,
7DC9C8171D641A350017D143 /* OverSightXPC.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
8B57559719DA3E9500799E6B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8B5755A419DA3E9500799E6B /* AppDelegate.m in Sources */,
8B5755A119DA3E9500799E6B /* main.m in Sources */,
7D17C4971D658A790066232A /* Logging.m in Sources */,
7D17C53B1D659E580066232A /* Exception.m in Sources */,
7D17C53F1D659E580066232A /* Utilities.m in Sources */,
7D17C4951D658A790066232A /* AboutWindowController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
8B5755BE19DA3F9300799E6B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
7D6245801D85348E00870565 /* Utilities.m in Sources */,
7D62457D1D8517F500870565 /* Logging.m in Sources */,
7D17CFE51D8133840017B475 /* AVMonitor.m in Sources */,
7DAF4B7F1D657192000DA31A /* StatusBarMenu.m in Sources */,
8B5755CA19DA3F9300799E6B /* AppDelegate.m in Sources */,
8B5755C719DA3F9300799E6B /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
7D17CFE71D81349D0017B475 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 7DC9C8111D641A350017D143 /* OverSightXPC */;
targetProxy = 7D17CFE61D81349D0017B475 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
8B5755A719DA3E9500799E6B /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
8B5755A819DA3E9500799E6B /* Base */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
8B5755CD19DA3F9300799E6B /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
8B5755CE19DA3F9300799E6B /* Base */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
7DC9C81F1D641A350017D143 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CODE_SIGN_IDENTITY = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
COMBINE_HIDPI_IMAGES = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_TESTABILITY = YES;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = OverSightXPC/Info.plist;
MACOSX_DEPLOYMENT_TARGET = 10.11;
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.OverSightXPC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Debug;
};
7DC9C8201D641A350017D143 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CODE_SIGN_IDENTITY = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = OverSightXPC/Info.plist;
MACOSX_DEPLOYMENT_TARGET = 10.11;
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.OverSightXPC";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Release;
};
8B5755B619DA3E9500799E6B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
8B5755B719DA3E9500799E6B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
};
name = Release;
};
8B5755B919DA3E9500799E6B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
INFOPLIST_FILE = MainApp/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.OverSight";
PRODUCT_NAME = OverSight;
PROVISIONING_PROFILE = "";
};
name = Debug;
};
8B5755BA19DA3E9500799E6B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
INFOPLIST_FILE = MainApp/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.OverSight";
PRODUCT_NAME = OverSight;
PROVISIONING_PROFILE = "";
};
name = Release;
};
8B5755DD19DA3F9300799E6B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
COMBINE_HIDPI_IMAGES = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = LoginItem/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.OverSightHelper";
PRODUCT_NAME = "OverSight Helper";
PROVISIONING_PROFILE = "";
SKIP_INSTALL = YES;
};
name = Debug;
};
8B5755DE19DA3F9300799E6B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application: Objective-See, LLC (VBG97UB4TA)";
COMBINE_HIDPI_IMAGES = YES;
INFOPLIST_FILE = LoginItem/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.OverSightHelper";
PRODUCT_NAME = "OverSight Helper";
PROVISIONING_PROFILE = "";
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
7DC9C81E1D641A350017D143 /* Build configuration list for PBXNativeTarget "OverSightXPC" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7DC9C81F1D641A350017D143 /* Debug */,
7DC9C8201D641A350017D143 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
8B57559619DA3E9500799E6B /* Build configuration list for PBXProject "OverSight" */ = {
isa = XCConfigurationList;
buildConfigurations = (
8B5755B619DA3E9500799E6B /* Debug */,
8B5755B719DA3E9500799E6B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
8B5755B819DA3E9500799E6B /* Build configuration list for PBXNativeTarget "OverSight" */ = {
isa = XCConfigurationList;
buildConfigurations = (
8B5755B919DA3E9500799E6B /* Debug */,
8B5755BA19DA3E9500799E6B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
8B5755DC19DA3F9300799E6B /* Build configuration list for PBXNativeTarget "OverSight Helper" */ = {
isa = XCConfigurationList;
buildConfigurations = (
8B5755DD19DA3F9300799E6B /* Debug */,
8B5755DE19DA3F9300799E6B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 8B57559319DA3E9500799E6B /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Test Application.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8B5755C119DA3F9300799E6B"
BuildableName = "OverSight Helper.app"
BlueprintName = "OverSight Helper"
ReferencedContainer = "container:OverSight.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8B5755D319DA3F9300799E6B"
BuildableName = "Test Application HelperTests.xctest"
BlueprintName = "Test Application HelperTests"
ReferencedContainer = "container:OverSight.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8B5755C119DA3F9300799E6B"
BuildableName = "OverSight Helper.app"
BlueprintName = "OverSight Helper"
ReferencedContainer = "container:OverSight.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8B5755C119DA3F9300799E6B"
BuildableName = "OverSight Helper.app"
BlueprintName = "OverSight Helper"
ReferencedContainer = "container:OverSight.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "8B5755C119DA3F9300799E6B"
BuildableName = "OverSight Helper.app"
BlueprintName = "OverSight Helper"
ReferencedContainer = "container:OverSight.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Installer.xcodeproj">
</FileRef>
<FileRef
location = "group:/Users/patrickw/objective-see/OverSight/OverSight.xcodeproj">
</FileRef>
</Workspace>

55
OverSightXPC/Enumerator.h Normal file
View File

@ -0,0 +1,55 @@
//
// Enumerator.h
// cameraUsers
//
// Created by Patrick Wardle on 9/9/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Enumerator : NSObject
/* DEFINES */
//camera assistant daemon on older macs
#define VDC_ASSISTANT @"/System/Library/Frameworks/CoreMediaIO.framework/Versions/A/Resources/VDC.plugin/Contents/Resources/VDCAssistant"
//camera assistant daemon
#define APPLE_CAMERA_ASSISTANT @"/Library/CoreMediaIO/Plug-Ins/DAL/AppleCamera.plugin/Contents/Resources/AppleCameraAssistant"
//lsmp binary
#define LSMP @"/usr/bin/lsmp"
//sample binary
#define SAMPLE @"/usr/bin/sample"
/* PROPERTIES */
//flag indicating video is action
@property BOOL videoActive;
//list of procs that have send Mach msg to *Assistant
@property(nonatomic, retain)NSMutableDictionary* machSenders;
/* METHODS */
//singleton interface
+(id)sharedManager;
//forever, baseline by getting all current procs that have sent a mach msg to *Assistant
// ->ensures its only invoke while camera is not in use, so these are all just baselined procs
-(void)start;
//find 'VDCAssistant' or 'AppleCameraAssistant'
-(pid_t)findCameraAssistant;
//enumerate all (recent) process that appear to be using video
-(NSMutableArray*)enumVideoProcs;
//set status of video
-(void)updateVideoStatus:(BOOL)isEnabled;
@end

423
OverSightXPC/Enumerator.m Normal file
View File

@ -0,0 +1,423 @@
//
// Enumerator.m
// cameraUsers
//
// Created by Patrick Wardle on 9/9/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import "Enumerator.h"
#import "../Shared/Logging.h"
#import "../Shared/Utilities.h"
#import <libproc.h>
#import <sys/sysctl.h>
//ignored mach sender procs
// TODO: maybe just ignore apple signed daemons/bg procs!?
static NSArray* ignoredProcs = nil;
@implementation Enumerator
@synthesize machSenders;
@synthesize videoActive;
//init
-(instancetype)init
{
//init
if(self = [super init])
{
//alloc dictionary
machSenders = [NSMutableDictionary dictionary];
//init ignored procs
ignoredProcs = @[
@"/sbin/launchd",
@"/usr/libexec/lsd",
@"/usr/sbin/notifyd",
@"/usr/sbin/syslogd",
@"/usr/sbin/cfprefsd",
@"/usr/libexec/opendirectoryd",
@"/usr/libexec/UserEventAgent",
@"/System/Library/CoreServices/launchservicesd",
@"/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/CVMServer",
@"/System/Library/Frameworks/CoreGraphics.framework/Versions/A/Resources/WindowServer",
@"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/Support/coreservicesd",
@"/System/Library/Frameworks/VideoToolbox.framework/Versions/A/XPCServices/VTDecoderXPCService.xpc/Contents/MacOS/VTDecoderXPCService"
];
}
return self;
}
//singleton interface
+(id)sharedManager
{
//instance
static Enumerator *sharedEnumerator = nil;
//once token
static dispatch_once_t onceToken;
//init
// ->only exec'd once though :)
dispatch_once(&onceToken, ^{
//init
sharedEnumerator = [[self alloc] init];
});
return sharedEnumerator;
}
//find 'VDCAssistant' or 'AppleCameraAssistant'
-(pid_t)findCameraAssistant
{
//pid of assistant
pid_t cameraAssistant = 0;
//status
int status = -1;
//# of procs
int numberOfProcesses = 0;
//array of pids
pid_t* pids = NULL;
//process path
NSString* processPath = nil;
//get # of procs
numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);
//alloc buffer for pids
pids = calloc(numberOfProcesses, sizeof(pid_t));
//get list of pids
status = proc_listpids(PROC_ALL_PIDS, 0, pids, numberOfProcesses * sizeof(pid_t));
if(status < 0)
{
//bail
goto bail;
}
//iterate over all pids
// ->get name for each via helper function
for(int i = 0; i < numberOfProcesses; ++i)
{
//skip blank pids
if(0 == pids[i])
{
//skip
continue;
}
//get name
processPath = getProcessPath(pids[i]);
if( (nil == processPath) ||
(0 == processPath.length) )
{
//skip
continue;
}
//is 'VDCAssistant'?
if(YES == [processPath isEqualToString:VDC_ASSISTANT])
{
//save
cameraAssistant = pids[i];
//pau
break;
}
//is 'AppleCameraAssistant'?
else if(YES == [processPath isEqualToString:APPLE_CAMERA_ASSISTANT])
{
//save
cameraAssistant = pids[i];
//pau
break;
}
}
//bail
bail:
//free buffer
if(NULL != pids)
{
//free
free(pids);
}
return cameraAssistant;
}
//forever, baseline by getting all current procs that have sent a mach msg to *Assistant
// ->ensures its only invoke while camera is not in use, so these are all just baselined procs
-(void)start
{
//baseline forever
// ->though logic will skip if video is active
while(YES)
{
//sync baselining
@synchronized(self)
{
//only baseline if video isn't active
if(YES != self.videoActive)
{
//dbg msg
logMsg(LOG_DEBUG, @"baselining mach senders...");
//enumerate procs that have send mach messages
self.machSenders = [self enumMachSenders:[self findCameraAssistant]];
//dbg msg
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"baselined mach senders: %@", self.machSenders]);
}
}
//nap for a minute
[NSThread sleepForTimeInterval:60];
}
return;
}
//enumerate all (recent) process that appear to be using video
-(NSMutableArray*)enumVideoProcs
{
//current procs
NSMutableArray* videoProcs = nil;
//mach senders
NSMutableDictionary* currentSenders = nil;
//candidate video procs
// ->those that have new mach message
NSMutableArray* candidateVideoProcs = nil;
//pid of camera assistant process
pid_t cameraAssistant = 0;
//alloc
candidateVideoProcs = [NSMutableArray array];
//sync this logic
// ->prevent baselining thread from doing anything
@synchronized(self)
{
//find 'VDCAssistant' or 'AppleCameraAssistant'
cameraAssistant = [self findCameraAssistant];
if(0 == cameraAssistant)
{
//err msg
logMsg(LOG_ERR, @"failed to find VDCAssistant/AppleCameraAssistant process");
//bail
goto bail;
}
//get procs that currrently have sent Mach msg to *Assistant
// ->returns dictionary of process id, and number of mach messages
currentSenders = [self enumMachSenders:cameraAssistant];
//dbg msg
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"current mach senders: %@", currentSenders]);
//remove any known/existing senders
for(NSNumber* processID in currentSenders.allKeys)
{
//add any candidate procs
// ->those that have new mach message
if( [currentSenders[processID] intValue] > [self.machSenders[processID] intValue])
{
//add
[candidateVideoProcs addObject:processID];
}
/*
//remove existing ones
if( (nil != self.machSenders[processID]) &&
([currentSenders[processID] intValue]) <= [self.machSenders[processID] intValue])
{
//remove
[currentSenders removeObjectForKey:processID];
}
*/
}
//dbg msg
logMsg(LOG_DEBUG, [NSString stringWithFormat:@"candidate video procs: %@", candidateVideoProcs]);
//save to baseline
// TODO: test that this works, esp if cnt goes down!! (facetime battery mode)
//[self.machSenders addEntriesFromDictionary:currentSenders];
//update
self.machSenders = currentSenders;
//invoke 'sample' to confirm that candidates are using CMIO/video inputs
videoProcs = [self sampleCandidates:candidateVideoProcs];
}//sync
//bail
bail:
return videoProcs;
}
//get procs that currrently have sent Mach msg to *Assistant
// ->returns dictionary of process id, and number of mach messages
-(NSMutableDictionary*)enumMachSenders:(pid_t)cameraAssistant
{
//senders
NSMutableDictionary* senders = nil;
//results from 'lsmp' cmd
NSString* results = nil;
//substrings
NSArray* subStrings = nil;
//process id
NSNumber* processID = nil;
//alloc
senders = [NSMutableDictionary dictionary];
//exec 'lsmp' w/ pid of camera asssistant to get mach ports
results = [[NSString alloc] initWithData:execTask(LSMP, @[@"-p", @(cameraAssistant).stringValue]) encoding:NSUTF8StringEncoding];
if( (nil == results) ||
(0 == results.length) )
{
//bail
goto bail;
}
//parse results
// ->looking for (<pid>) process name
for(NSString* line in [results componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"\n"]])
{
//skip blank lines
if(0 == line.length)
{
//skip
continue;
}
//parse on '()'
// TODO: improve this!
subStrings = [line componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"()"]];
if(subStrings.count < 3)
{
//skip
continue;
}
//skip 'unknown' processes
// output looks like "(-) Unknown Process"
if(YES == [[subStrings objectAtIndex:0x1] isEqualToString:@"-"])
{
//skip
continue;
}
//extract process id
// ->insides '()', so will be second substring
processID = @([[subStrings objectAtIndex:0x1] integerValue]);
if(nil == processID)
{
//skip
continue;
}
//ignore self
if(cameraAssistant == processID.intValue)
{
//skip
continue;
}
//ignore apple daemons (that send mach messages, etc)
if(YES == [ignoredProcs containsObject:getProcessPath(processID.intValue)])
{
//skip
continue;
}
//add/inc to dictionary
senders[processID] = @([senders[processID] unsignedIntegerValue] + 1);
}
//bail
bail:
return senders;
}
//invoke 'sample' to confirm candidates are using CMIO/video inputs
-(NSMutableArray*)sampleCandidates:(NSArray*)currentSenders
{
//current procs
NSMutableArray* videoProcs = nil;
//results from 'sample' cmd
NSString* results = nil;
//alloc
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
results = [[NSString alloc] initWithData:execTask(SAMPLE, @[processID.stringValue, @"1"]) encoding:NSUTF8StringEncoding];
if( (nil == results) ||
(0 == results.length) )
{
//skip
continue;
}
//for now, just check for 'CMIOGraph::DoWork'
// TODO: could look for dylibs, other calls, etc
if(YES != [results containsString:@"CMIOGraph::DoWork"])
{
//skip
continue;
}
//looks like a video proc!
[videoProcs addObject:processID];
}
return videoProcs;
}
//set status of video
-(void)updateVideoStatus:(BOOL)isEnabled
{
//sync
@synchronized(self)
{
//set
self.videoActive = isEnabled;
}
return;
}
@end

33
OverSightXPC/Info.plist Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>OverSightXPC</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright (c) 2016 Objective-See. All rights reserved.</string>
<key>XPCService</key>
<dict>
<key>ServiceType</key>
<string>Application</string>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,26 @@
//
// OverSightXPC.h
// OverSightXPC
//
// Created by Patrick Wardle on 8/16/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "../Shared/XPCProtocol.h"
/* DEFINES */
// 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.
@interface OverSightXPC : NSObject <XPCProtocol>
/* PROPERTIES */
//flag indicating video is action
@property BOOL videoActive;
//list of procs that have send Mach msg to *Assistant
@property(nonatomic, retain)NSMutableDictionary* machSenders;
@end

View File

@ -0,0 +1,93 @@
//
// OverSightXPC.m
// OverSightXPC
//
// Created by Patrick Wardle on 8/16/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import "Logging.h"
#import "Enumerator.h"
#import "OverSightXPC.h"
#import "../Shared/Utilities.h"
@implementation OverSightXPC
//TODO: method to set flag, that's sync'd~!?
@synthesize machSenders;
@synthesize videoActive;
//do any initializations
// ->for now, just kick off enumerator
-(void)initialize:(void (^)(void))reply
{
//start enumerating
// ->will forever baseline current mach msg procs
[NSThread detachNewThreadSelector:@selector(start) toTarget:[Enumerator sharedManager] withObject:nil];
//reply
reply();
return;
}
//call into emumerate to get (new) video proc
-(void)getVideoProcs:(void (^)(NSMutableArray *))reply
{
//reply w/ video procs
reply([[Enumerator sharedManager] enumVideoProcs]);
return;
}
//update status video
// ->allows enumerator to stop baselining (when active), etc
-(void)updateVideoStatus:(unsigned int)status reply:(void (^)(void))reply
{
//set status
[[Enumerator sharedManager] updateVideoStatus:status];
//reply
reply();
return;
}
//kill a process
-(void)killProcess:(NSNumber*)processID reply:(void (^)(BOOL))reply
{
//flag
BOOL wasKilled = NO;
//terminate
if(-1 == kill(processID.intValue, SIGKILL))
{
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"failed to kill %@, with %d", processID, errno]);
//bail
goto bail;
}
//happy
wasKilled = YES;
//bail
bail:
//reply
reply(wasKilled);
return;
}
@end

177
OverSightXPC/main.m Normal file
View File

@ -0,0 +1,177 @@
//
// main.m
// OverSightXPC
//
// Created by Patrick Wardle on 8/16/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "../Shared/Exception.h"
#import "../Shared/XPCProtocol.h"
#import "OverSightXPC.h"
//TODO: remove
#import <syslog.h>
//interface for 'extension' to NSXPCConnection
// ->allows us to access the 'private' auditToken iVar
@interface ExtendedNSXPCConnection : NSXPCConnection
{
//private iVar
audit_token_t auditToken;
}
//private iVar
@property audit_token_t auditToken;
@end
//implementation for 'extension' to NSXPCConnection
// ->allows us to access the 'private' auditToken iVar
@implementation ExtendedNSXPCConnection
//private iVar
@synthesize auditToken;
@end
//function def
OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement);
//signing auth
#define SIGNING_AUTH @"Developer ID Application: Objective-See, LLC (VBG97UB4TA)"
//skeleton interface
@interface ServiceDelegate : NSObject <NSXPCListenerDelegate>
@end
@implementation ServiceDelegate
//automatically invoked
//->allows NSXPCListener to configure/accept/resume a new incoming NSXPCConnection
// note: we only allow binaries signed by Objective-See to talk to this!
-(BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
{
//flag
BOOL shouldAccept = NO;
//task ref
SecTaskRef taskRef = 0;
//signing req string
NSString *requirementString = nil;
//init signing req string
requirementString = [NSString stringWithFormat:@"anchor trusted and certificate leaf [subject.CN] = \"%@\"", SIGNING_AUTH];
//step 1: create task ref
// ->uses NSXPCConnection's (private) 'auditToken' iVar
taskRef = SecTaskCreateWithAuditToken(NULL, ((ExtendedNSXPCConnection*)newConnection).auditToken);
if(NULL == taskRef)
{
//bail
goto bail;
}
//step 2: validate
// ->check that client is signed with Objective-See's dev cert
if(0 != SecTaskValidateForRequirement(taskRef, (__bridge CFStringRef)(requirementString)))
{
//bail
goto bail;
}
//set the interface that the exported object implements
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCProtocol)];
//set object exported by connection
newConnection.exportedObject = [[OverSightXPC alloc] init];
//resume
[newConnection resume];
//happy
shouldAccept = YES;
//bail
bail:
//release task ref object
if(NULL != taskRef)
{
//release
CFRelease(taskRef);
//unset
taskRef = NULL;
}
return shouldAccept;
}
@end
//main entrypoint
// ->install exception handlers & setup/kickoff listener
int main(int argc, const char *argv[])
{
//ret var
int status = -1;
//service delegate
ServiceDelegate* delegate = nil;
//listener
NSXPCListener* listener = nil;
//
//first thing...
// ->install exception handlers!
//TODO: re-enable
//installExceptionHandlers();
/*
//TODO: don't think we need this?
chown -R root:wheel & chmod -R +s seems to be all thats needed
//make really r00t
// ->needed for exec'ing vmmap, etc
if(0 != setuid(0))
{
//TODO: re-enable
//err msg
syslog(LOG_ERR, "OBJECTIVE-SEE TASKEXPLORER ERROR: setuid() failed with: %d\n", errno);
//bail
goto bail;
}
*/
//create the delegate for the service.
delegate = [ServiceDelegate new];
//set up the one NSXPCListener for this service
// ->handles incoming connections
listener = [NSXPCListener serviceListener];
//set delegate
listener.delegate = delegate;
//resuming the listener starts this service
// ->method does not return
[listener resume];
//baseline
//happy
status = 0;
//bail
bail:
return status;
}

61
Shared/AboutWindow.xib Normal file
View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15G1004" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="AboutWindowController">
<connections>
<outlet property="versionLabel" destination="OSm-xS-Dmd" id="luW-4O-fYe"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES" unifiedTitleAndToolbar="YES"/>
<rect key="contentRect" x="196" y="240" width="422" height="123"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1057"/>
<view key="contentView" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="422" height="123"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="lEv-Wj-6S5">
<rect key="frame" x="10" y="12" width="110" height="106"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="icon" id="xKf-GK-m0k"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Eaf-yA-bbe">
<rect key="frame" x="141" y="63" width="180" height="50"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="overSight" id="Ws8-bD-j2R"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OSm-xS-Dmd">
<rect key="frame" x="141" y="51" width="182" height="19"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="version:" id="bBK-v0-ypq">
<font key="font" size="13" name="Menlo-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="HZZ-Es-mpy">
<rect key="frame" x="280" y="11" width="128" height="32"/>
<buttonCell key="cell" type="push" title="more info" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="J9x-sM-h9S">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" size="13" name="Menlo-Regular"/>
</buttonCell>
<connections>
<action selector="moreInfo:" target="-2" id="YYW-1b-uJY"/>
</connections>
</button>
</subviews>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
<point key="canvasLocation" x="318" y="246.5"/>
</window>
</objects>
<resources>
<image name="icon" width="512" height="512"/>
<image name="overSight" width="1396" height="286"/>
</resources>
</document>

View File

@ -0,0 +1,27 @@
//
// AboutWindowController.h
// OverSight
//
// Created by Patrick Wardle on 7/15/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface AboutWindowController : NSWindowController <NSWindowDelegate>
{
}
/* PROPERTIES */
//version label/string
@property (weak) IBOutlet NSTextField *versionLabel;
/* METHODS */
//invoked when user clicks 'more info' button
// ->open KK's webpage
- (IBAction)moreInfo:(id)sender;
@end

View File

@ -0,0 +1,51 @@
//
// AboutWindowController.m
// OverSight
//
// Created by Patrick Wardle on 7/15/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import "Consts.h"
#import "Utilities.h"
#import "AboutWindowController.h"
@implementation AboutWindowController
@synthesize versionLabel;
//automatically called when nib is loaded
// ->center window
-(void)awakeFromNib
{
//center
[self.window center];
}
//automatically invoked when window is loaded
// ->set to white
-(void)windowDidLoad
{
//super
[super windowDidLoad];
//make white
[self.window setBackgroundColor: NSColor.whiteColor];
//set version sting
[self.versionLabel setStringValue:[NSString stringWithFormat:@"version: %@", getAppVersion()]];
return;
}
//automatically invoked when user clicks 'more info'
// ->load products webpage view their default browser
-(IBAction)moreInfo:(id)sender
{
//open URL
// ->invokes user's default browser
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:PRODUCT_URL]];
return;
}
@end

107
Shared/Consts.h Normal file
View File

@ -0,0 +1,107 @@
//
// Consts.h
// WhatsYourSign
//
// Created by Patrick Wardle on 7/7/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
//TODO: update!
#ifndef WYS_Consts_h
#define WYS_Consts_h
//success
#define STATUS_SUCCESS 0
//product url
#define PRODUCT_URL @"https://objective-see.com/products/whatsyoursign.html"
//installed extensions
#define INSTALLED_EXTENSIONS @"~/Library/Preferences/com.apple.preferences.extensions.FinderSync.plist"
//bundle ID of finder sync extension
#define EXTENSION_BUNDLE_ID @"com.objective-see.WhatsYourSignExt.FinderSync"
//extension folder
#define EXTENSION_FOLDER @"~/Library/WhatsYourSign"
//extension name
#define EXTENSION_NAME @"WhatsYourSign.appex"
//frame shift
// ->for status msg to avoid activity indicator
#define FRAME_SHIFT 45
//hotkey 'w'
#define KEYCODE_W 0xD
//hotkey 'q'
#define KEYCODE_Q 0xC
//signature status
#define KEY_SIGNATURE_STATUS @"signatureStatus"
//signing auths
#define KEY_SIGNING_AUTHORITIES @"signingAuthorities"
//file belongs to apple?
#define KEY_SIGNING_IS_APPLE @"signedByApple"
//file signed with apple dev id
#define KEY_SIGNING_IS_APPLE_DEV_ID @"signedWithDevID"
//from app store
#define KEY_SIGNING_IS_APP_STORE @"fromAppStore"
//OS version x
#define OS_MAJOR_VERSION_X 10
//OS minor version yosemite
#define OS_MINOR_VERSION_YOSEMITE 10
//OS minor version el capitan
#define OS_MINOR_VERSION_EL_CAPITAN 11
//path to file binary
#define FILE @"/usr/bin/file"
//path to pluginkit binary
#define PLUGIN_KIT @"/usr/bin/pluginkit"
//path to pkgutil
#define PKG_UTIL @"/usr/sbin/pkgutil"
//action to install
// ->also button title
#define ACTION_INSTALL @"Install"
//action to uninstall
// ->also button title
#define ACTION_UNINSTALL @"Uninstall"
//flag to uninstall
#define ACTION_UNINSTALL_FLAG 0
//flag to install
#define ACTION_INSTALL_FLAG 1
//error msg
#define KEY_ERROR_MSG @"errorMsg"
//sub msg
#define KEY_ERROR_SUB_MSG @"errorSubMsg"
//error URL
#define KEY_ERROR_URL @"errorURL"
//flag for error popup
#define KEY_ERROR_SHOULD_EXIT @"shouldExit"
//errors url
#define ERRORS_URL @"https://objective-see.com/errors.html"
//general error URL
#define FATAL_ERROR_URL @"https://objective-see.com/errors.html"
#endif

23
Shared/Exception.h Executable file
View File

@ -0,0 +1,23 @@
//
// Exception.h
// WhatsYourSign
//
// Created by Patrick Wardle on 7/7/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <signal.h>
//install exception/signal handlers
void installExceptionHandlers();
//exception handler for Obj-C exceptions
void exceptionHandler(NSException *exception);
//signal handler for *nix style exceptions
void signalHandler(int signal, siginfo_t *info, void *context);
//display error window
void displayErrorWindow(NSDictionary* errorInfo);

190
Shared/Exception.m Executable file
View File

@ -0,0 +1,190 @@
//
// Exception.m
// WhatsYourSign
//
// Created by Patrick Wardle on 7/7/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import "Consts.h"
#import "Logging.h"
#import "Exception.h"
#import "Utilities.h"
//TODO: renenable
/*
#ifdef IS_INSTALLER_APP
#import "AppDelegate.h"
#endif
*/
//global
// ->only report an fatal exception once
BOOL wasReported = NO;
//install exception/signal handlers
void installExceptionHandlers()
{
//sigaction struct
struct sigaction sa = {0};
//init signal struct
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = signalHandler;
//objective-C exception handler
NSSetUncaughtExceptionHandler(&exceptionHandler);
//install signal handlers
sigaction(SIGILL, &sa, NULL);
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGBUS, &sa, NULL);
sigaction(SIGABRT, &sa, NULL);
sigaction(SIGTRAP, &sa, NULL);
sigaction(SIGFPE, &sa, NULL);
return;
}
//exception handler
// will be invoked for Obj-C exceptions
void exceptionHandler(NSException *exception)
{
//error msg
NSString* errorMessage = nil;
//ignore if exception was already reported
if(YES == wasReported)
{
//bail
return;
}
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"OBJECTIVE-SEE ERROR: OS version: %@ /App version: %@", [[NSProcessInfo processInfo] operatingSystemVersionString], getAppVersion()]);
//create error msg
errorMessage = [NSString stringWithFormat:@"unhandled obj-c exception caught [name: %@ / reason: %@]", [exception name], [exception reason]];
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"OBJECTIVE-SEE ERROR: %@", errorMessage]);
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"OBJECTIVE-SEE ERROR: %@", [[NSThread callStackSymbols] description]]);
//set flag
wasReported = YES;
//start installer-specific code
#ifdef IS_INSTALLER_APP
//error info dictionary
NSMutableDictionary* errorInfo = nil;
//alloc
errorInfo = [NSMutableDictionary dictionary];
//add main error msg
errorInfo[KEY_ERROR_MSG] = @"ERROR: unrecoverable fault";
//add sub msg
errorInfo[KEY_ERROR_SUB_MSG] = [exception name];
//set error URL
errorInfo[KEY_ERROR_URL] = FATAL_ERROR_URL;
//fatal error
// ->agent should exit
errorInfo[KEY_ERROR_SHOULD_EXIT] = [NSNumber numberWithBool:YES];
//TODO: renable
//display error msg
//[((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)
if(YES != [NSThread isMainThread])
{
//nap
while(YES)
{
//nap
[NSThread sleepForTimeInterval:1.0f];
}
}
//end app-specific code
#endif
return;
}
//handler for signals
// will be invoked for BSD/*nix signals
void signalHandler(int signal, siginfo_t *info, void *context)
{
//error msg
NSString* errorMessage = nil;
//context
ucontext_t *uContext = NULL;
//ignore if exception was already reported
if(YES == wasReported)
{
//bail
return;
}
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"OBJECTIVE-SEE ERROR: OS version: %@ /App version: %@", [[NSProcessInfo processInfo] operatingSystemVersionString], getAppVersion()]);
//typecast context
uContext = (ucontext_t *)context;
//create error msg
errorMessage = [NSString stringWithFormat:@"unhandled exception caught, si_signo: %d /si_code: %s /si_addr: %p /rip: %p",
info->si_signo, (info->si_code == SEGV_MAPERR) ? "SEGV_MAPERR" : "SEGV_ACCERR", info->si_addr, (unsigned long*)uContext->uc_mcontext->__ss.__rip];
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"OBJECTIVE-SEE ERROR: %@", errorMessage]);
//err msg
logMsg(LOG_ERR, [NSString stringWithFormat:@"OBJECTIVE-SEE ERROR: %@", [[NSThread callStackSymbols] description]]);
//set flag
wasReported = YES;
//start installer-specific code
#ifdef IS_INSTALLER_APP
//error info dictionary
NSMutableDictionary* errorInfo = nil;
//alloc
errorInfo = [NSMutableDictionary dictionary];
//add main error msg
errorInfo[KEY_ERROR_MSG] = @"ERROR: unrecoverable fault";
//add sub msg
errorInfo[KEY_ERROR_SUB_MSG] = [NSString stringWithFormat:@"si_signo: %d / rip: %p", info->si_signo, (unsigned long*)uContext->uc_mcontext->__ss.__rip];
//set error URL
errorInfo[KEY_ERROR_URL] = FATAL_ERROR_URL;
//fatal error
// ->agent should exit
errorInfo[KEY_ERROR_SHOULD_EXIT] = [NSNumber numberWithBool:YES];
//TODO: renable
//display error msg
//[((AppDelegate*)[[NSApplication sharedApplication] delegate]) displayErrorWindow:errorInfo];
//end app-specific code
#endif
return;
}

BIN
Shared/Images/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
Shared/Images/overSight.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

14
Shared/Logging.h Normal file
View File

@ -0,0 +1,14 @@
//
// Logging.h
// WhatsYourSign
//
// Created by Patrick Wardle on 7/7/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <syslog.h>
#import <Foundation/Foundation.h>
//log a msg to syslog
// ->also disk, if error
void logMsg(int level, NSString* msg);

49
Shared/Logging.m Normal file
View File

@ -0,0 +1,49 @@
//
// Logging.m
// WhatsYourSign
//
// Created by Patrick Wardle on 7/7/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import "Consts.h"
#import "Logging.h"
#import "Utilities.h"
//log a msg
void logMsg(int level, NSString* msg)
{
//log prefix
NSMutableString* logPrefix = nil;
//alloc/init
logPrefix = [NSMutableString stringWithString:@"OBJECTIVE-SEE OVERSIGHT"];
//if its error, add error to prefix
if(LOG_ERR == level)
{
//add
[logPrefix appendString:@" ERROR"];
}
//debug mode logic
#ifdef DEBUG
//in debug mode promote debug msgs to LOG_NOTICE
// ->OS X only shows LOG_NOTICE and above
if(LOG_DEBUG == level)
{
//promote
level = LOG_NOTICE;
}
#endif
//log to syslog
syslog(level, "%s: %s\n", [logPrefix UTF8String], [msg UTF8String]);
//TODO: remove
NSLog(@"%s: %s", [logPrefix UTF8String], [msg UTF8String]);
return;
}

49
Shared/Utilities.h Normal file
View File

@ -0,0 +1,49 @@
//
// Utilities.h
// WhatsYourSign
//
// Created by Patrick Wardle on 7/7/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#ifndef WYS_Utilities_h
#define WYS_Utilities_h
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
/* FUNCTIONS */
//get app's version
// ->extracted from Info.plist
NSString* getAppVersion();
//exec a process and grab it's output
NSData* execTask(NSString* binaryPath, NSArray* arguments);
//get OS's major or minor version
SInt32 getVersion(OSType selector);
//given a path to binary
// 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();
//exec a process and grab it's output
NSData* execTask(NSString* binaryPath, NSArray* arguments);
//get process's path
NSString* getProcessPath(pid_t pid);
#endif

273
Shared/Utilities.m Normal file
View File

@ -0,0 +1,273 @@
//
// Utilities.m
// WhatsYourSign
//
// Created by Patrick Wardle on 7/7/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import "Consts.h"
#import "Utilities.h"
#import <signal.h>
#import <unistd.h>
#import <libproc.h>
#import <sys/sysctl.h>
#import <Security/Security.h>
#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonDigest.h>
#import <SystemConfiguration/SystemConfiguration.h>
//get app's version
// ->extracted from Info.plist
NSString* getAppVersion()
{
//read and return 'CFBundleVersion' from bundle
return [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
}
//given a path to binary
// parse it back up to find app's bundle
NSBundle* findAppBundle(NSString* binaryPath)
{
//app's bundle
NSBundle* appBundle = nil;
//app's path
NSString* appPath = nil;
//first just try full path
appPath = binaryPath;
//try to find the app's bundle/info dictionary
do
{
//try to load app's bundle
appBundle = [NSBundle bundleWithPath:appPath];
//check for match
// ->binary path's match
if( (nil != appBundle) &&
(YES == [appBundle.executablePath isEqualToString:binaryPath]))
{
//all done
break;
}
//always unset bundle var since it's being returned
// ->and at this point, its not a match
appBundle = nil;
//remove last part
// ->will try this next
appPath = [appPath stringByDeletingLastPathComponent];
//scan until we get to root
// ->of course, loop will exit if app info dictionary is found/loaded
} while( (nil != appPath) &&
(YES != [appPath isEqualToString:@"/"]) &&
(YES != [appPath isEqualToString:@""]) );
return appBundle;
}
//exec a process and grab it's output
NSData* execTask(NSString* binaryPath, NSArray* arguments)
{
//task
NSTask *task = nil;
//output pipe
NSPipe *outPipe = nil;
//read handle
NSFileHandle* readHandle = nil;
//output
NSMutableData *output = nil;
//init task
task = [NSTask new];
//init output pipe
outPipe = [NSPipe pipe];
//init read handle
readHandle = [outPipe fileHandleForReading];
//init output buffer
output = [NSMutableData data];
//set task's path
[task setLaunchPath:binaryPath];
//set task's args
[task setArguments:arguments];
//set task's output
[task setStandardOutput:outPipe];
//wrap task launch
@try
{
//launch
[task launch];
}
@catch(NSException *exception)
{
//bail
goto bail;
}
//read in output
while(YES == [task isRunning])
{
//accumulate output
[output appendData:[readHandle readDataToEndOfFile]];
}
//grab any left over data
[output appendData:[readHandle readDataToEndOfFile]];
//bail
bail:
return output;
}
//get OS's major or minor version
SInt32 getVersion(OSType selector)
{
//version
// ->major or minor
SInt32 version = -1;
//get version info
if(noErr != Gestalt(selector, &version))
{
//reset version
version = -1;
//err
goto bail;
}
//bail
bail:
return version;
}
//get process's path
NSString* getProcessPath(pid_t pid)
{
//task path
NSString* taskPath = nil;
//buffer for process path
char pathBuffer[PROC_PIDPATHINFO_MAXSIZE] = {0};
//status
int status = -1;
//'management info base' array
int mib[3] = {0};
//system's size for max args
int systemMaxArgs = 0;
//process's args
char* taskArgs = NULL;
//# of args
int numberOfArgs = 0;
//size of buffers, etc
size_t size = 0;
//reset buffer
bzero(pathBuffer, PROC_PIDPATHINFO_MAXSIZE);
//first attempt to get path via 'proc_pidpath()'
status = proc_pidpath(pid, pathBuffer, sizeof(pathBuffer));
if(0 != status)
{
//init task's name
taskPath = [NSString stringWithUTF8String:pathBuffer];
}
//otherwise
// ->try via task's args ('KERN_PROCARGS2')
else
{
//init mib
// ->want system's size for max args
mib[0] = CTL_KERN;
mib[1] = KERN_ARGMAX;
//set size
size = sizeof(systemMaxArgs);
//get system's size for max args
if(-1 == sysctl(mib, 2, &systemMaxArgs, &size, NULL, 0))
{
//bail
goto bail;
}
//alloc space for args
taskArgs = malloc(systemMaxArgs);
if(NULL == taskArgs)
{
//bail
goto bail;
}
//init mib
// ->want process args
mib[0] = CTL_KERN;
mib[1] = KERN_PROCARGS2;
mib[2] = pid;
//set size
size = (size_t)systemMaxArgs;
//get process's args
if(-1 == sysctl(mib, 3, taskArgs, &size, NULL, 0))
{
//bail
goto bail;
}
//sanity check
// ->ensure buffer is somewhat sane
if(size <= sizeof(int))
{
//bail
goto bail;
}
//extract number of args
// ->at start of buffer
memcpy(&numberOfArgs, taskArgs, sizeof(numberOfArgs));
//extract task's name
// ->follows # of args (int) and is NULL-terminated
taskPath = [NSString stringWithUTF8String:taskArgs + sizeof(int)];
}
//bail
bail:
//free process args
if(NULL != taskArgs)
{
//free
free(taskArgs);
//reset
taskArgs = NULL;
}
return taskPath;
}

27
Shared/XPCProtocol.h Normal file
View File

@ -0,0 +1,27 @@
//
// OverSightXPCProtocol.h
// OverSightXPC
//
// Created by Patrick Wardle on 8/16/16.
// Copyright (c) 2016 Objective-See. All rights reserved.
//
#import <Foundation/Foundation.h>
// The protocol that this service will vend as its API. This header file will also need to be visible to the process hosting the service.
@protocol XPCProtocol
//start enumerator
-(void)initialize:(void (^)(void))reply;
//get (new) video procs
-(void)getVideoProcs:(void (^)(NSMutableArray *))reply;
//update status video
// ->allows enumerator to stop baselining (when active), etc
-(void)updateVideoStatus:(unsigned int)status reply:(void (^)(void))reply;
//kill a process
-(void)killProcess:(NSNumber*)processID reply:(void (^)(BOOL))reply;
@end