From b4d9f952fa71ee29df515a6635306ce5cf4fa501 Mon Sep 17 00:00:00 2001 From: Patrick Wardle Date: Thu, 5 Sep 2019 20:32:45 -1000 Subject: [PATCH] initial checkin --- .gitignore | 1 + FileMonitor.xcodeproj/project.pbxproj | 292 ++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + Source/File.m | 145 ++++++++ Source/FileMonitor.h | 109 ++++++ Source/FileMonitor.m | 230 +++++++++++++ Source/Process.m | 315 ++++++++++++++++++ Source/utilities.h | 18 + Source/utilities.m | 35 ++ 9 files changed, 1153 insertions(+) create mode 100644 FileMonitor.xcodeproj/project.pbxproj create mode 100644 FileMonitor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Source/File.m create mode 100644 Source/FileMonitor.h create mode 100644 Source/FileMonitor.m create mode 100644 Source/Process.m create mode 100644 Source/utilities.h create mode 100644 Source/utilities.m diff --git a/.gitignore b/.gitignore index 86de6aa..b1ca717 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,4 @@ fastlane/test_output # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ +FileMonitor.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/FileMonitor.xcodeproj/project.pbxproj b/FileMonitor.xcodeproj/project.pbxproj new file mode 100644 index 0000000..9c26586 --- /dev/null +++ b/FileMonitor.xcodeproj/project.pbxproj @@ -0,0 +1,292 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + CE56C7ED231DAAD10046EDA3 /* File.m in Sources */ = {isa = PBXBuildFile; fileRef = CE56C7EC231DAAD10046EDA3 /* File.m */; }; + CE91B743231A0E6D003273AF /* FileMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = CE91B742231A0E6D003273AF /* FileMonitor.h */; }; + CE91B745231A0E6D003273AF /* FileMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = CE91B744231A0E6D003273AF /* FileMonitor.m */; }; + CE91B74E231A129D003273AF /* Process.m in Sources */ = {isa = PBXBuildFile; fileRef = CE91B74C231A129D003273AF /* Process.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + CE56C7EC231DAAD10046EDA3 /* File.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = File.m; sourceTree = ""; }; + CE91B73F231A0E6D003273AF /* libFileMonitor.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libFileMonitor.a; sourceTree = BUILT_PRODUCTS_DIR; }; + CE91B742231A0E6D003273AF /* FileMonitor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FileMonitor.h; sourceTree = ""; }; + CE91B744231A0E6D003273AF /* FileMonitor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FileMonitor.m; sourceTree = ""; }; + CE91B74C231A129D003273AF /* Process.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Process.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + CE91B73D231A0E6D003273AF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + CE91B736231A0E6D003273AF = { + isa = PBXGroup; + children = ( + CE91B741231A0E6D003273AF /* Source */, + CE91B740231A0E6D003273AF /* Products */, + ); + sourceTree = ""; + }; + CE91B740231A0E6D003273AF /* Products */ = { + isa = PBXGroup; + children = ( + CE91B73F231A0E6D003273AF /* libFileMonitor.a */, + ); + name = Products; + sourceTree = ""; + }; + CE91B741231A0E6D003273AF /* Source */ = { + isa = PBXGroup; + children = ( + CE91B742231A0E6D003273AF /* FileMonitor.h */, + CE91B744231A0E6D003273AF /* FileMonitor.m */, + CE91B74C231A129D003273AF /* Process.m */, + CE56C7EC231DAAD10046EDA3 /* File.m */, + ); + path = Source; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + CE91B73B231A0E6D003273AF /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + CE91B743231A0E6D003273AF /* FileMonitor.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + CE91B73E231A0E6D003273AF /* FileMonitor */ = { + isa = PBXNativeTarget; + buildConfigurationList = CE91B748231A0E6D003273AF /* Build configuration list for PBXNativeTarget "FileMonitor" */; + buildPhases = ( + CE91B73B231A0E6D003273AF /* Headers */, + CE91B73C231A0E6D003273AF /* Sources */, + CE91B73D231A0E6D003273AF /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FileMonitor; + productName = processMonitor; + productReference = CE91B73F231A0E6D003273AF /* libFileMonitor.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CE91B737231A0E6D003273AF /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1100; + ORGANIZATIONNAME = "Objective-See"; + TargetAttributes = { + CE91B73E231A0E6D003273AF = { + CreatedOnToolsVersion = 11.0; + }; + }; + }; + buildConfigurationList = CE91B73A231A0E6D003273AF /* Build configuration list for PBXProject "FileMonitor" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = CE91B736231A0E6D003273AF; + productRefGroup = CE91B740231A0E6D003273AF /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + CE91B73E231A0E6D003273AF /* FileMonitor */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + CE91B73C231A0E6D003273AF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CE56C7ED231DAAD10046EDA3 /* File.m in Sources */, + CE91B745231A0E6D003273AF /* FileMonitor.m in Sources */, + CE91B74E231A129D003273AF /* Process.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + CE91B746231A0E6D003273AF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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.15; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + CE91B747231A0E6D003273AF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + 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.15; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + }; + name = Release; + }; + CE91B749231A0E6D003273AF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + CE91B74A231A0E6D003273AF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + EXECUTABLE_PREFIX = lib; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + CE91B73A231A0E6D003273AF /* Build configuration list for PBXProject "FileMonitor" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE91B746231A0E6D003273AF /* Debug */, + CE91B747231A0E6D003273AF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + CE91B748231A0E6D003273AF /* Build configuration list for PBXNativeTarget "FileMonitor" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE91B749231A0E6D003273AF /* Debug */, + CE91B74A231A0E6D003273AF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = CE91B737231A0E6D003273AF /* Project object */; +} diff --git a/FileMonitor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/FileMonitor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/FileMonitor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Source/File.m b/Source/File.m new file mode 100644 index 0000000..e3b1bd2 --- /dev/null +++ b/Source/File.m @@ -0,0 +1,145 @@ +// +// Process.m +// ProcessMonitor +// +// Created by Patrick Wardle on 9/1/19. +// Copyright © 2019 Objective-See. All rights reserved. +// + +#import +#import +#import + +#import "utilities.h" +#import "FileMonitor.h" + +//events +extern NSDictionary* eventsOfInterest; + +/* FUNCTIONS */ + +@implementation File + +@synthesize process; +@synthesize sourcePath; +@synthesize destinationPath; + +//init +-(id)init:(es_message_t*)message +{ + //init super + self = [super init]; + if(nil != self) + { + //set type + self.event = message->event_type; + + //set process + self.process = [[Process alloc] init:message]; + + //extract path(s) + // logic is specific to event + [self extractPaths:message]; + + } + + return self; +} + +//extract source & destination path +// this requires event specific logic +-(void)extractPaths:(es_message_t*)message +{ + //event specific logic + switch (message->event_type) { + + //create + case ES_EVENT_TYPE_NOTIFY_CREATE: + + //set path + self.destinationPath = convertStringToken(&message->event.create.target->path); + + break; + + //open + case ES_EVENT_TYPE_NOTIFY_OPEN: + + //set path + self.destinationPath = convertStringToken(&message->event.open.file->path); + + break; + + //write + case ES_EVENT_TYPE_NOTIFY_WRITE: + + //set path + self.destinationPath = convertStringToken(&message->event.write.target->path); + + break; + + //close + case ES_EVENT_TYPE_NOTIFY_CLOSE: + + //set path + self.destinationPath = convertStringToken(&message->event.close.target->path); + + break; + + //link + case ES_EVENT_TYPE_NOTIFY_LINK: + + //set (src) path + self.sourcePath = convertStringToken(&message->event.link.source->path); + + //set (dest) path + // combine dest dir + dest file + self.destinationPath = [convertStringToken(&message->event.link.target_dir->path) stringByAppendingPathComponent:convertStringToken(&message->event.link.target_filename)]; + + break; + + //rename + case ES_EVENT_TYPE_NOTIFY_RENAME: + + //set (src) path + self.sourcePath = convertStringToken(&message->event.rename.source->path); + + //existing file ('ES_DESTINATION_TYPE_EXISTING_FILE') + if(ES_DESTINATION_TYPE_EXISTING_FILE == message->event.rename.destination_type) + { + //set (dest) file + self.destinationPath = convertStringToken(&message->event.rename.destination.existing_file->path); + } + //new path ('ES_DESTINATION_TYPE_NEW_PATH') + else + { + //set (dest) path + // combine dest dir + dest file + self.destinationPath = [convertStringToken(&message->event.rename.destination.new_path.dir->path) stringByAppendingPathComponent:convertStringToken(&message->event.rename.destination.new_path.filename)]; + } + + break; + + //unlink + case ES_EVENT_TYPE_NOTIFY_UNLINK: + + //set path + self.destinationPath = convertStringToken(&message->event.unlink.target->path); + + break; + + + default: + break; + } + + return; +} + +//for pretty printing +-(NSString *)description +{ + //pretty print + return [NSString stringWithFormat: @"%@: \nsource path: %@\ndestination path: %@\nprocess: %@", eventsOfInterest[[NSNumber numberWithInt:self.event]], self.sourcePath, self.destinationPath, process]; +} + +@end diff --git a/Source/FileMonitor.h b/Source/FileMonitor.h new file mode 100644 index 0000000..d5df8b1 --- /dev/null +++ b/Source/FileMonitor.h @@ -0,0 +1,109 @@ +// +// FileMonitor.h +// FileMonitor +// +// Created by Patrick Wardle on 9/1/19. +// Copyright © 2019 Objective-See. All rights reserved. +// + +#import +#import + +/* CONSTS */ + +//code signing keys +#define KEY_SIGNATURE_CDHASH @"cdHash" +#define KEY_SIGNATURE_FLAGS @"csFlags" +#define KEY_SIGNATURE_IDENTIFIER @"signatureIdentifier" +#define KEY_SIGNATURE_TEAM_IDENTIFIER @"teamIdentifier" +#define KEY_SIGNATURE_PLATFORM_BINARY @"isPlatforBinary" + +/* CLASSES */ +@class File; +@class Process; + +/* TYPEDEFS */ + +//block for library +typedef void (^FileCallbackBlock)(File* _Nonnull); + +@interface FileMonitor : NSObject + +//start monitoring +-(BOOL)start:(FileCallbackBlock _Nonnull )callback; + +//stop monitoring +-(BOOL)stop; + +@end + +/* OBJECT: FILE */ + +@interface File : NSObject + +/* PROPERTIES */ + +//event +// create, write, etc... +@property u_int32_t event; + +//src path +@property(nonatomic, retain)NSString* _Nullable sourcePath; + +//dest path +@property(nonatomic, retain)NSString* _Nullable destinationPath; + +//process +@property(nonatomic, retain)Process* _Nullable process; + +/* METHODS */ + +//init +-(id _Nullable)init:(es_message_t* _Nonnull)message; + +@end + + +/* OBJECT: PROCESS */ + +@interface Process : NSObject + +/* PROPERTIES */ + +//pid +@property pid_t pid; + +//ppid +@property pid_t ppid; + +//user id +@property uid_t uid; + +//event +// exec, fork, exit +@property u_int32_t event; + +//exit code +@property u_int32_t exit; + +//path +@property(nonatomic, retain)NSString* _Nullable path; + +//args +@property(nonatomic, retain)NSMutableArray* _Nonnull arguments; + +//ancestors +@property(nonatomic, retain)NSMutableArray* _Nonnull ancestors; + +//signing info +@property(nonatomic, retain)NSMutableDictionary* _Nonnull signingInfo; + +//timestamp +@property(nonatomic, retain)NSDate* _Nonnull timestamp; + +/* METHODS */ + +//init +-(id _Nullable)init:(es_message_t* _Nonnull)message; + +@end diff --git a/Source/FileMonitor.m b/Source/FileMonitor.m new file mode 100644 index 0000000..8fd69df --- /dev/null +++ b/Source/FileMonitor.m @@ -0,0 +1,230 @@ +// +// FileMonitor.m +// FileMonitor +// +// Created by Patrick Wardle on 9/1/19. +// Copyright © 2019 Objective-See. All rights reserved. +// + +// Inspired by https://gist.github.com/Omar-Ikram/8e6721d8e83a3da69b31d4c2612a68ba +// NOTE: requires a) root b) the 'com.apple.developer.endpoint-security.client' entitlement + +#import "utilities.h" +#import "FileMonitor.h" + +#import +#import + +//endpoint +es_client_t *endpointClient = nil; + +//file events of interest +NSDictionary* eventsOfInterest = nil; + +@implementation FileMonitor + +//init +-(id)init +{ + //init super + self = [super init]; + if(nil != self) + { + //init events of interest + eventsOfInterest = @{[NSNumber numberWithInt:ES_EVENT_TYPE_NOTIFY_CREATE]:@"ES_EVENT_TYPE_NOTIFY_CREATE", + [NSNumber numberWithInt:ES_EVENT_TYPE_NOTIFY_OPEN]:@"ES_EVENT_TYPE_NOTIFY_OPEN", + [NSNumber numberWithInt:ES_EVENT_TYPE_NOTIFY_WRITE]:@"ES_EVENT_TYPE_NOTIFY_WRITE", + [NSNumber numberWithInt:ES_EVENT_TYPE_NOTIFY_CLOSE]:@"ES_EVENT_TYPE_NOTIFY_CLOSE", + [NSNumber numberWithInt:ES_EVENT_TYPE_NOTIFY_RENAME]:@"ES_EVENT_TYPE_NOTIFY_RENAME", + [NSNumber numberWithInt:ES_EVENT_TYPE_NOTIFY_LINK]:@"ES_EVENT_TYPE_NOTIFY_LINK", + [NSNumber numberWithInt:ES_EVENT_TYPE_NOTIFY_UNLINK]:@"ES_EVENT_TYPE_NOTIFY_UNLINK"}; + } + + return self; +} + +//start monitoring +-(BOOL)start:(FileCallbackBlock)callback +{ + //flag + BOOL started = NO; + + //events (as array) + es_event_type_t* events = NULL; + + //result + es_new_client_result_t result = 0; + + //alloc events + events = malloc(sizeof(es_event_type_t) * eventsOfInterest.count); + + //init events + // es_* APIs expect a C-array... + for(int i = 0; i < eventsOfInterest.count; i++) + { + //add event + events[i] = [eventsOfInterest.allKeys[i] intValue]; + } + + //sync + @synchronized (self) + { + + //create client + // callback invokes (user) callback for new processes + result = es_new_client(&endpointClient, ^(es_client_t *cleint, const es_message_t *message) + { + //new file event + File* file = nil; + + //ignore non-notify messages + if(ES_ACTION_TYPE_NOTIFY != message->action_type) + { + //ignore + return; + } + + //ignore non-msg's of interest + if(nil == [eventsOfInterest objectForKey:[NSNumber numberWithInt:message->event_type]]) + { + //ignore + return; + } + + //init process obj + file = [[File alloc] init:(es_message_t* _Nonnull)message]; + if(nil != file) + { + //invoke user callback + callback(file); + } + }); + + //error? + if(ES_NEW_CLIENT_RESULT_SUCCESS != result) + { + //err msg + NSLog(@"ERROR: es_new_client() failed with %d", result); + + //bail + goto bail; + } + + //clear cache + if(ES_CLEAR_CACHE_RESULT_SUCCESS != es_clear_cache(endpointClient)) + { + //err msg + NSLog(@"ERROR: es_clear_cache() failed"); + + //bail + goto bail; + } + + //mute self + // note: you might not want this, but for a cmdline-based filemonitor + // this ensures we don't constantly report writes to /dev/tty + es_mute_path_literal(endpointClient, [NSProcessInfo.processInfo.arguments[0] UTF8String]); + + //subscribe + if(ES_RETURN_SUCCESS != es_subscribe(endpointClient, events, (u_int32_t)eventsOfInterest.count)) + { + //err msg + NSLog(@"ERROR: es_subscribe() failed"); + + //bail + goto bail; + } + + } //sync + + //happy + started = YES; + +bail: + + //free events + if(NULL != events) + { + //free + free(events); + events = NULL; + } + + return started; +} + +//stop +-(BOOL)stop +{ + //flag + BOOL stopped = NO; + + //sync + @synchronized (self) + { + + //unsubscribe & delete + if(NULL != endpointClient) + { + //unsubscribe + if(ES_RETURN_SUCCESS != es_unsubscribe_all(endpointClient)) + { + //err msg + NSLog(@"ERROR: es_unsubscribe_all() failed"); + + //bail + goto bail; + } + + //delete + if(ES_RETURN_SUCCESS != es_delete_client(endpointClient)) + { + //err msg + NSLog(@"ERROR: es_delete_client() failed"); + + //bail + goto bail; + } + + //unset + endpointClient = NULL; + + //happy + stopped = YES; + } + + } //sync + +bail: + + return stopped; +} + +@end + +//helper function +// convert es_string_token_t to string +NSString* convertStringToken(es_string_token_t* stringToken) +{ + //string + NSString* string = nil; + + //init to empty string + string = [NSString string]; + + //sanity check(s) + if( (NULL == stringToken) || + (0 == stringToken->length) || + (NULL == stringToken->data) ) + { + //bail + goto bail; + } + + //convert to data, then to string + string = [NSString stringWithUTF8String:[[NSData dataWithBytes:stringToken->data length:stringToken->length] bytes]]; + +bail: + + return string; +} diff --git a/Source/Process.m b/Source/Process.m new file mode 100644 index 0000000..d183141 --- /dev/null +++ b/Source/Process.m @@ -0,0 +1,315 @@ +// +// Process.m +// ProcessMonitor +// +// Created by Patrick Wardle on 9/1/19. +// Copyright © 2019 Objective-See. All rights reserved. +// + + +#import +#import +#import + +#import "utilities.h" +#import "FileMonitor.h" + +/* FUNCTIONS */ + +//helper function +// get parent of arbitrary process +pid_t getParentID(pid_t child); + +@implementation Process + +@synthesize pid; +@synthesize exit; +@synthesize path; +@synthesize ppid; +@synthesize event; +@synthesize ancestors; +@synthesize arguments; +@synthesize timestamp; +@synthesize signingInfo; + +//init +-(id)init:(es_message_t*)message +{ + //init super + self = [super init]; + if(nil != self) + { + //process from msg + es_process_t* process = NULL; + + //alloc array for args + self.arguments = [NSMutableArray array]; + + //alloc array for parents + self.ancestors = [NSMutableArray array]; + + //alloc dictionary for signing info + self.signingInfo = [NSMutableDictionary dictionary]; + + //init exit + self.exit = -1; + + //init user id + self.uid = -1; + + //init event + self.event = -1; + + //set start time + self.timestamp = [NSDate date]; + + //set type + self.event = message->event_type; + + //event specific logic + // set type + // extract (relevant) process object, etc + switch (message->event_type) { + + //exec + case ES_EVENT_TYPE_NOTIFY_EXEC: + + //set process (target) + process = message->event.exec.target; + + //extract/format args + [self extractArgs:&message->event]; + + break; + + //fork + case ES_EVENT_TYPE_NOTIFY_FORK: + + //set process (child) + process = message->event.fork.child; + + break; + + //exit + case ES_EVENT_TYPE_NOTIFY_EXIT: + + //set process + process = message->process; + + //set exit code + self.exit = message->event.exit.stat; + + break; + + //default + default: + + //set process + process = message->process; + + break; + } + + //init pid + self.pid = audit_token_to_pid(process->audit_token); + + //init ppid + self.ppid = process->ppid; + + //init uuid + self.uid = audit_token_to_euid(process->audit_token); + + //init path + self.path = convertStringToken(&process->executable->path); + + //extract/format code signing info + [self extractSigningInfo:process]; + + //enum ancestors + [self enumerateAncestors]; + + } + + return self; +} + +//extract/format args +-(void)extractArgs:(es_events_t *)event +{ + //number of args + uint32_t count = 0; + + //get # of args + count = es_exec_arg_count(&event->exec); + if(0 == count) + { + //bail + goto bail; + } + + //extact all args + for(uint32_t i = 0; i < count; i++) + { + //current arg + es_string_token_t currentArg = {0}; + + //extract current arg + currentArg = es_exec_arg(&event->exec, i); + + //append + [self.arguments addObject:convertStringToken(¤tArg)]; + } + +bail: + + return; +} + +//extract/format signing info +-(void)extractSigningInfo:(es_process_t *)process +{ + //cd hash + NSMutableString* cdHash = nil; + + //alloc string for hash + cdHash = [NSMutableString string]; + + //add flags + self.signingInfo[KEY_SIGNATURE_FLAGS] = [NSNumber numberWithUnsignedInt:process->codesigning_flags]; + + //add signing id + self.signingInfo[KEY_SIGNATURE_IDENTIFIER] = convertStringToken(&process->signing_id); + + //add team id + self.signingInfo[KEY_SIGNATURE_TEAM_IDENTIFIER] = convertStringToken(&process->team_id); + + //add platform binary + self.signingInfo[KEY_SIGNATURE_PLATFORM_BINARY] = [NSNumber numberWithBool:process->is_platform_binary]; + + //format cdhash + for(uint32_t i = 0; icdhash[i]]; + } + + //add cdhash + self.signingInfo[KEY_SIGNATURE_CDHASH] = cdHash; + + return; +} + +//generate list of ancestors +-(void)enumerateAncestors +{ + //current process id + pid_t currentPID = -1; + + //parent pid + pid_t parentPID = -1; + + //add parent + if(-1 != self.ppid) + { + //add + [self.ancestors addObject:[NSNumber numberWithInt:self.ppid]]; + + //set current to parent + currentPID = self.ppid; + } + //don't know parent + // just start with self + else + { + //start w/ self + currentPID = self.pid; + } + + //complete ancestry + while(YES) + { + //get parent pid + parentPID = getParentID(currentPID); + if( (0 == parentPID) || + (-1 == parentPID) || + (currentPID == parentPID) ) + { + //bail + break; + } + + //update + currentPID = parentPID; + + //add + [self.ancestors addObject:[NSNumber numberWithInt:parentPID]]; + } + + return; +} + +//for pretty printing +-(NSString *)description +{ + //description + NSString* description = nil; + + //exec/fork events + // don't add exit code... + if(ES_EVENT_TYPE_NOTIFY_EXIT != self.event) + { + //pretty print + description = [NSString stringWithFormat: @"pid: %d\npath: %@\nuid: %d\nargs: %@\nancestors: %@\nsigning info: %@", self.pid, self.path, self.uid, self.arguments, self.ancestors, self.signingInfo]; + } + //exit event + // add exit code + else + { + description = [NSString stringWithFormat: @"pid: %d\npath: %@\nuid: %d\nargs: %@\nancestors: %@\nsigning info: %@\nexit code: %d", self.pid, self.path, self.uid, self.arguments, self.ancestors, self.signingInfo, self.exit]; + } + + return description; + +} + +@end + +//helper function +// get parent of arbitrary process +pid_t getParentID(pid_t child) +{ + //parent id + pid_t parentID = -1; + + //kinfo_proc struct + struct kinfo_proc processStruct = {0}; + + //size + size_t procBufferSize = 0; + + //mib + const u_int mibLength = 4; + + //syscall result + int sysctlResult = -1; + + //init buffer length + procBufferSize = sizeof(processStruct); + + //init mib + int mib[mibLength] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, child}; + + //make syscall + sysctlResult = sysctl(mib, mibLength, &processStruct, &procBufferSize, NULL, 0); + + //check if got ppid + if( (noErr == sysctlResult) && + (0 != procBufferSize) ) + { + //save ppid + parentID = processStruct.kp_eproc.e_ppid; + } + + return parentID; +} + diff --git a/Source/utilities.h b/Source/utilities.h new file mode 100644 index 0000000..f571c7c --- /dev/null +++ b/Source/utilities.h @@ -0,0 +1,18 @@ +// +// utilities.h +// ProcessMonitor +// +// Created by Patrick Wardle on 9/1/19. +// Copyright © 2019 Objective-See. All rights reserved. +// + +#ifndef utilities_h +#define utilities_h + +#import +#import + +//convert es_string_token_t to string +NSString* convertStringToken(es_string_token_t* stringToken); + +#endif /* utilities_h */ diff --git a/Source/utilities.m b/Source/utilities.m new file mode 100644 index 0000000..6d1b0d6 --- /dev/null +++ b/Source/utilities.m @@ -0,0 +1,35 @@ +// +// utilities.m +// ProcessMonitor +// +// Created by Patrick Wardle on 9/1/19. +// Copyright © 2019 Objective-See. All rights reserved. +// + +#import "utilities.h" + +//convert es_string_token_t to string +NSString* convertStringToken(es_string_token_t* stringToken) +{ + //string + NSString* string = nil; + + //init to empty string + string = [NSString string]; + + //sanity check(s) + if( (NULL == stringToken) || + (0 == stringToken->length) || + (NULL == stringToken->data) ) + { + //bail + goto bail; + } + + //convert to data, then to string + string = [NSString stringWithUTF8String:[[NSData dataWithBytes:stringToken->data length:stringToken->length] bytes]]; + +bail: + + return string; +}