diff --git a/lib/libprocInfo.a b/lib/libprocInfo.a index bebc72d..0c38783 100644 Binary files a/lib/libprocInfo.a and b/lib/libprocInfo.a differ diff --git a/lib/procInfo.h b/lib/procInfo.h index b275679..12e69d7 100644 --- a/lib/procInfo.h +++ b/lib/procInfo.h @@ -29,6 +29,24 @@ #define EVENT_EXEC 27 #define EVENT_SPAWN 43190 +//signature status +#define KEY_SIGNATURE_STATUS @"signatureStatus" + +//signing auths +#define KEY_SIGNING_AUTHORITIES @"signingAuthorities" + +//code signing id +#define KEY_SIGNATURE_IDENTIFIER @"signingIdentifier" + +//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" + /* TYPEDEFS */ //block for library @@ -39,12 +57,12 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull); @interface ProcInfo : NSObject -//init -// flag dictates if CPU intensive signing checks should be performed --(id _Nullable )init:(BOOL)skipSigningInfo; +//init w/ flag +// flag dictates if CPU-intensive logic (code signing, etc) should be preformed +-(id _Nullable)init:(BOOL)goEasy; //start monitoring --(BOOL)start:(ProcessCallbackBlock _Nonnull )callback; +-(void)start:(ProcessCallbackBlock _Nonnull )callback; //stop monitoring -(void)stop; @@ -77,26 +95,26 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull); @property u_int32_t exit; //path -@property (nonatomic, retain) NSString* _Nullable path; +@property(nonatomic, retain)NSString* _Nullable path; //args -@property (nonatomic, retain) NSMutableArray* _Nonnull arguments; +@property(nonatomic, retain)NSMutableArray* _Nonnull arguments; //ancestors -@property (nonatomic, retain) NSMutableArray* _Nonnull ancestors; +@property(nonatomic, retain)NSMutableArray* _Nonnull ancestors; //Binary object // has path, hash, etc -@property (nonatomic, retain) Binary* _Nonnull binary; +@property(nonatomic, retain)Binary* _Nonnull binary; //timestamp -@property (nonatomic, retain) NSDate* _Nonnull timestamp; +@property(nonatomic, retain)NSDate* _Nonnull timestamp; /* METHODS */ //init with a pid // method will then (try) fill out rest of object --(id _Nullable )init:(pid_t)processID; +-(id _Nullable)init:(pid_t)processID; //set process's path -(void)pathFromPid; @@ -119,23 +137,36 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull); /* PROPERTIES */ //path -@property (nonatomic, retain)NSString* _Nonnull path; +@property(nonatomic, retain)NSString* _Nonnull path; //name -@property (nonatomic, retain)NSString* _Nonnull name; +@property(nonatomic, retain)NSString* _Nonnull name; //icon -@property (nonatomic, retain)NSImage* _Nonnull icon; +@property(nonatomic, retain)NSImage* _Nonnull icon; //file attributes -@property (nonatomic, retain)NSDictionary* _Nullable attributes; +@property(nonatomic, retain)NSDictionary* _Nullable attributes; + +//spotlight meta data +@property(nonatomic, retain)NSDictionary* _Nullable metadata; //bundle // nil for non-apps -@property (nonatomic, retain)NSBundle* _Nullable bundle; +@property(nonatomic, retain)NSBundle* _Nullable bundle; //signing info -@property (nonatomic, retain)NSDictionary* _Nonnull signingInfo; +@property(nonatomic, retain)NSDictionary* _Nonnull signingInfo; + +//entitlements +@property(nonatomic, retain)NSDictionary* _Nonnull entitlements; + +//hash +@property(nonatomic, retain)NSString* _Nonnull sha256; + +//identifier +// either signing id or sha256 hash +@property(nonatomic, retain)NSString* _Nonnull identifier; //flag indicating binary belongs to Apple OS @property BOOL isApple; @@ -145,13 +176,32 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull); /* METHODS */ -//init w/ an info dictionary --(id _Nonnull )init:(NSString* _Nonnull)path; +//init w/ a path +-(id _Nonnull)init:(NSString* _Nonnull)path; + +/* the following methods are rather CPU-intensive + as such, if the proc monitoring is run with the 'goEasy' option, they aren't automatically invoked +*/ + +//get an icon for a process +// for apps, this will be app's icon, otherwise just a standard system one +-(void)getIcon; //generate signing info // also classifies if Apple/from App Store/etc. --(void)generateSigningInfo; +-(void)generateSigningInfo:(SecCSFlags)flags entitlements:(BOOL)entitlements; +//generate entitlements +// note: can also call 'generateSigningInfo' w/ 'entitlements:YES' +-(void)generateEntitlements; + +//generate hash +-(void)generateHash; + +//generate id +// eithersigning id, or sha256 hash +// note: will generate signing info if needed +-(void)generateIdentifier; @end diff --git a/procInfo/AppReceipt.h b/procInfo/AppReceipt.h index 5f2dd15..d68f2c7 100644 --- a/procInfo/AppReceipt.h +++ b/procInfo/AppReceipt.h @@ -98,7 +98,7 @@ static const SecAsn1Template kSetOfReceiptAttributeTemplate[] = /* METHODS */ //init with app path -// ->locate/load receipt, etc +// locate/load receipt, etc -(instancetype)init:(NSBundle *)bundle; /* PROPERTIES */ diff --git a/procInfo/AppReceipt.m b/procInfo/AppReceipt.m index 23d84bb..743567a 100644 --- a/procInfo/AppReceipt.m +++ b/procInfo/AppReceipt.m @@ -13,7 +13,7 @@ #import "AppReceipt.h" //helper function from [1] -// ->extract an int from ASN.1 data +// extract an int from ASN.1 data inline static int getIntValueFromASN1Data(const ASN1_Data *asn1Data) { int ret = 0; @@ -25,7 +25,7 @@ inline static int getIntValueFromASN1Data(const ASN1_Data *asn1Data) } //helper function from [1] -// ->decode string from ASN.1 data +// decode string from ASN.1 data inline static NSString *decodeUTF8StringFromASN1Data(SecAsn1CoderRef decoder, ASN1_Data srcData) { //data struct @@ -61,7 +61,7 @@ bail: @synthesize decodedData; //init with app path -// ->locate/load/decode receipt, etc +// locate/load/decode receipt, etc -(instancetype)init:(NSBundle *)bundle { //init @@ -101,7 +101,7 @@ bail: } //parse out values - // ->bundle id, app version, etc + // bundle id, app version, etc self.components = [self parse]; if( (nil == self.components) || (0 == self.components.count) ) @@ -120,7 +120,7 @@ bail: } //decode receipt data -// ->some validations performed here too +// some validations performed here too -(NSData*)decode { //decoded data @@ -183,7 +183,7 @@ bail: } //CHECK 1: - // ->make sure there is a signer + // make sure there is a signer status = CMSDecoderGetNumSigners(decoder, &signers); if( (noErr != status) || (0 == signers) ) @@ -193,7 +193,7 @@ bail: } //CHECK 2: - // ->make sure signer status is ok + // make sure signer status is ok status = CMSDecoderCopySignerStatus(decoder, 0, policy, TRUE, &signerStatus, &trust, &certVerifyResult); if( (noErr != status) || (kCMSSignerValid != signerStatus) ) @@ -259,7 +259,7 @@ bail: } //parse decoded receipt -// ->extract out items such as bundle id, app version, etc. +// extract out items such as bundle id, app version, etc. -(NSMutableDictionary*)parse { //decoder @@ -297,14 +297,14 @@ bail: items = [NSMutableDictionary dictionary]; //extact attributes - // ->save those of interest + // save those of interest for(int i = 0; (attribute = payload.attrs[i]); i++) { //process each type switch(getIntValueFromASN1Data(&attribute->type)) { //bundle id - // ->save bundle id and data + // save bundle id and data case RECEIPT_ATTR_BUNDLE_ID: { //save bundle id @@ -345,7 +345,7 @@ bail: } //default - // ->ignore + // ignore default: { break; diff --git a/procInfo/Binary.m b/procInfo/Binary.m index c7d357b..69812a1 100644 --- a/procInfo/Binary.m +++ b/procInfo/Binary.m @@ -19,12 +19,15 @@ @synthesize path; @synthesize bundle; @synthesize isApple; +@synthesize metadata; @synthesize attributes; +@synthesize identifier; @synthesize isAppStore; @synthesize signingInfo; +@synthesize entitlements; //init binary object -// generates signing info, classifies binary, etc +// note: CPU-intensive logic (code signing, etc) called manually -(id)init:(NSString*)binaryPath { //init super @@ -57,11 +60,11 @@ //get name [self getName]; - //get icon - [self getIcon]; - - //get attributes + //get file attributes [self getAttributes]; + + //get meta data (spotlight) + [self getMetadata]; } return self; @@ -89,17 +92,17 @@ // either via app bundle, or from path -(void)getName { - //found app bundle? - // grab name from 'CFBundleName' + //first try get name from app bundle + // specifically, via grab name from 'CFBundleName' if(nil != self.bundle) { //extract name self.name = [self.bundle infoDictionary][@"CFBundleName"]; } - //no app bundle + //no app bundle || no 'CFBundleName' // just use last component from path - else + if(nil == self.name) { //set name self.name = [self.path lastPathComponent]; @@ -108,7 +111,7 @@ return; } -//get attributes +//get file attributes -(void)getAttributes { //grab (file) attributes @@ -117,6 +120,53 @@ return; } +//get (spotlight) meta data +-(void)getMetadata +{ + //md item ref + MDItemRef mdItem = nil; + + //attributes names + CFArrayRef attributeNames = nil; + + //create + mdItem = MDItemCreate(kCFAllocatorDefault, (CFStringRef)self.path); + if(nil == mdItem) + { + //bail + goto bail; + } + + //copy names + attributeNames = MDItemCopyAttributeNames(mdItem); + if(nil == attributeNames) + { + //bail + goto bail; + } + + //get metadata + self.metadata = CFBridgingRelease(MDItemCopyAttributes(mdItem, attributeNames)); + +bail: + + //release names + if(nil != attributeNames) + { + //release + CFRelease(attributeNames); + } + + //release item + if(nil != mdItem) + { + //release + CFRelease(mdItem); + } + + return; +} + //get an icon for a process // for apps, this will be app's icon, otherwise just a standard system one -(void)getIcon @@ -153,7 +203,7 @@ iconExtension = [iconFile pathExtension]; //if its blank (i.e. not specified) - // ->go with 'icns' + // go with 'icns' if(YES == [iconExtension isEqualTo:@""]) { //set type @@ -176,7 +226,7 @@ self.icon = [[NSWorkspace sharedWorkspace] iconForFile:self.path]; //load system document icon - // ->static var, so only load once + // static var, so only load once if(nil == documentIcon) { //load @@ -204,11 +254,11 @@ bail: //generate signing info // also classifies if Apple/from App Store/etc. --(void)generateSigningInfo +-(void)generateSigningInfo:(SecCSFlags)flags entitlements:(BOOL)entitlements { //extract signing info (do this first!) // from Apple, App Store, signing authorities, etc - self.signingInfo = extractSigningInfo(self.path); + self.signingInfo = extractSigningInfo(self.path, flags, entitlements); //perform more signing checks and lists // gotta be happily signed for checks though @@ -229,6 +279,57 @@ bail: return; } +//generate hash +-(void)generateHash +{ + //hash + self.sha256 = PI_hashFile(self.path); + + return; +} + +//generate id +// either signing id, or sha256 hash +// note: will generate signing info if needed +-(void)generateIdentifier +{ + //generate signing info? + if(nil == self.signingInfo) + { + //generate + [self generateSigningInfo:kSecCSDefaultFlags entitlements:NO]; + } + + //validly signed binary? + // use its signing identifier + if( (noErr == [self.signingInfo[KEY_SIGNATURE_STATUS] intValue]) && + (0 != [self.signingInfo[KEY_SIGNING_AUTHORITIES] count]) && + (nil != self.signingInfo[KEY_SIGNATURE_IDENTIFIER]) ) + { + //use signing id + self.identifier = self.signingInfo[KEY_SIGNATURE_IDENTIFIER]; + } + //not validly signed or unsigned + // generate sha256 hash for identifier + else + { + //hash + self.identifier = PI_hashFile(self.path); + } + + return; +} + +//generate entitlements +// note: can also call 'generateSigningInfo' w/ 'entitlements:YES' +-(void)generateEntitlements +{ + //call into helper function + self.entitlements = extractEntitlements(self.path); + + return; +} + //for pretty printing -(NSString *)description { diff --git a/procInfo/Consts.h b/procInfo/Consts.h index 6da73c1..3c71027 100644 --- a/procInfo/Consts.h +++ b/procInfo/Consts.h @@ -28,22 +28,19 @@ //audit class for exec events #define AUDIT_CLASS_EXEC 0x40000000 -//signature status -#define KEY_SIGNATURE_STATUS @"signatureStatus" +//key for stdout output +#define STDOUT @"stdOutput" -//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" +//key for stderr output +#define STDERR @"stdError" //key for exit code #define EXIT_CODE @"exitCode" +//path to codesign +#define CODE_SIGN @"/usr/bin/codesign" + +//entitlements +#define KEY_SIGNING_ENTITLEMENTS @"entitlements" + #endif /* Consts_h */ diff --git a/procInfo/Process.m b/procInfo/Process.m index d707608..6c28330 100644 --- a/procInfo/Process.m +++ b/procInfo/Process.m @@ -102,7 +102,7 @@ bail: } //get uid -// ->sets 'user' instance var +// sets 'user' instance var -(void)getUser { //kinfo_proc struct @@ -156,7 +156,7 @@ bail: currentPID = self.ppid; } //don't know parent - // ->just start with self + // just start with self else { //start w/ self @@ -164,7 +164,7 @@ bail: } //add until we get to to end (pid 0) - // ->or error out during the traversal + // or error out during the traversal while(YES) { //get parent pid @@ -300,7 +300,7 @@ bail: int signalStatus = -1; //send kill with 0 to determine if alive - // -> see: http://stackoverflow.com/questions/9152979/check-if-process-exists-given-its-pid + // see: http://stackoverflow.com/questions/9152979/check-if-process-exists-given-its-pid signalStatus = kill(self.pid, 0); //is alive? @@ -315,7 +315,7 @@ bail: } //extract commandline args -// ->saves into 'arguments' ivar +// saves into 'arguments' ivar -(void)getArgs { //'management info base' array @@ -340,7 +340,7 @@ bail: char* parser = NULL; //init mib - // ->want system's size for max args + // want system's size for max args mib[0] = CTL_KERN; mib[1] = KERN_ARGMAX; @@ -363,7 +363,7 @@ bail: } //init mib - // ->want process args + // want process args mib[0] = CTL_KERN; mib[1] = KERN_PROCARGS2; mib[2] = pid; @@ -379,7 +379,7 @@ bail: } //sanity check - // ->ensure buffer is somewhat sane + // ensure buffer is somewhat sane if(size <= sizeof(int)) { //bail @@ -387,12 +387,11 @@ bail: } //extract number of args - // ->at start of buffer + // at start of buffer memcpy(&numberOfArgs, processArgs, sizeof(numberOfArgs)); - //init pointer to start of args - // ->they start right after # of args + // they start right after # of args parser = processArgs + sizeof(numberOfArgs); //scan until end of process's NULL-terminated path @@ -410,7 +409,7 @@ bail: } //sanity check - // ->make sure end-of-buffer wasn't reached + // make sure end-of-buffer wasn't reached if(parser == &processArgs[size]) { //bail @@ -418,7 +417,7 @@ bail: } //skip all trailing NULLs - // ->scan will end when non-NULL is found + // scan will end when non-NULL is found while(parser < &processArgs[size]) { //scan till NULL-terminator @@ -433,7 +432,7 @@ bail: } //sanity check - // ->(again), make sure end-of-buffer wasn't reached + // (again), make sure end-of-buffer wasn't reached if(parser == &processArgs[size]) { //bail @@ -441,15 +440,15 @@ bail: } //parser should now point to argv[0], process name - // ->init arg start + // init arg start argStart = parser; //keep scanning until all args are found - // ->each is NULL-terminated + // each is NULL-terminated while(parser < &processArgs[size]) { //each arg is NULL-terminated - // ->so scan till NULL, then save into array + // so scan till NULL, then save into array if(*parser == '\0') { //save arg diff --git a/procInfo/ProcessMonitor.m b/procInfo/ProcessMonitor.m index 89a9800..fcbd162 100644 --- a/procInfo/ProcessMonitor.m +++ b/procInfo/ProcessMonitor.m @@ -8,7 +8,7 @@ // //disable incomplete/umbrella warnings -// ->otherwise complains about 'audit_kevents.h' +// otherwise complains about 'audit_kevents.h' #pragma clang diagnostic ignored "-Wincomplete-umbrella" #import "Consts.h" @@ -30,8 +30,8 @@ /* INSTANCE VARIABLES */ -//do signing checks? -@property BOOL skipSigningInfo; +//skip CPU-intensive logic +@property BOOL goEasy; //callback block @property(nonatomic, copy)ProcessCallbackBlock processCallback; @@ -72,8 +72,8 @@ bail: } //init w/ flag -// flag dictates if CPU intensive signing checks should be performed --(id _Nullable )init:(BOOL)skipSigningInfo; +// flag dictates if CPU-intensive logic (code signing, etc) should be preformed +-(id _Nullable )init:(BOOL)goEasy; { //init // calls 'super' too @@ -81,7 +81,7 @@ bail: if(self) { //save mode - self.skipSigningInfo = skipSigningInfo; + self.goEasy = goEasy; } bail: @@ -91,7 +91,7 @@ bail: //start monitoring // note: requires root/macOS 10.12.4+ for full monitoring --(BOOL)start:(ProcessCallbackBlock)callback +-(void)start:(ProcessCallbackBlock)callback { //OS version info NSDictionary* osVersionInfo = nil; @@ -103,7 +103,7 @@ bail: osVersionInfo = PI_getOSVersion(); //do basic (app) monitoring - // ->if not root, or OS version is < 10.12.4 (due to kernel bug) + // if not root, or OS version is < 10.12.4 (due to kernel bug) if( (0 != getuid()) || ([osVersionInfo[@"minorVersion"] intValue] < OS_MINOR_VERSION_SIERRA) || (([osVersionInfo[@"minorVersion"] intValue] == OS_MINOR_VERSION_SIERRA) && ([osVersionInfo[@"bugfixVersion"] intValue] < 4)) ) @@ -125,7 +125,7 @@ bail: }); } - return NO; + return; } //stop monitoring @@ -136,7 +136,7 @@ bail: [[NSNotificationCenter defaultCenter] removeObserver: self]; //set 'stop' monitor bool - // ->is checked in 'monitor' method as termination condition + // is checked in 'monitor' method as termination condition self.shouldStop = YES; return; @@ -231,7 +231,7 @@ bail: } //set preselect flags - // ->event classes we're interested in + // event classes we're interested in status = ioctl(auditFileDescriptor, AUDITPIPE_SET_PRESELECT_FLAGS, &eventClasses); if(-1 == status) { @@ -240,7 +240,7 @@ bail: } //set non-attributable flags - // ->event classes we're interested in + // event classes we're interested in status = ioctl(auditFileDescriptor, AUDITPIPE_SET_PRESELECT_NAFLAGS, &eventClasses); if(-1 == status) { @@ -249,7 +249,7 @@ bail: } //forever - // ->read/parse/process audit records + // read/parse/process audit records while(YES) { @autoreleasepool @@ -293,7 +293,7 @@ bail: processedLength = 0; //parse record - // ->read all tokens/process + // read all tokens/process while(0 != recordBalance) { //extract token @@ -301,7 +301,7 @@ bail: if(-1 == au_fetch_tok(&tokenStruct, recordBuffer + processedLength, recordBalance)) { //error - // ->skip record + // skip record break; } @@ -311,7 +311,7 @@ bail: (YES != [self shouldProcessRecord:process.type]) ) { //bail - // ->skips rest of record + // skips rest of record break; } @@ -700,11 +700,15 @@ bail: goto bail; } - //automatically generate signing info? - if(YES != self.skipSigningInfo) + //automatically generate signing info/icon? + // these can be skipped for performance reasons + if(YES != self.goEasy) { //generate signing info - [process.binary generateSigningInfo]; + [process.binary generateSigningInfo:kSecCSDefaultFlags entitlements:NO]; + + //set icon + [process.binary getIcon]; } //invoke user callback @@ -756,6 +760,9 @@ bail: continue; } + //generate signing info + [currentProcess.binary generateSigningInfo:kSecCSDefaultFlags entitlements:NO]; + //add [processes addObject:currentProcess]; } diff --git a/procInfo/Signing.h b/procInfo/Signing.h index 03ee300..0f510c5 100644 --- a/procInfo/Signing.h +++ b/procInfo/Signing.h @@ -16,20 +16,24 @@ /* FUNCTIONS */ //get the signing info of a file -NSDictionary* extractSigningInfo(NSString* path); +NSMutableDictionary* extractSigningInfo(NSString* path, SecCSFlags flags, BOOL entitlements); + +//determine if a file is signed by Apple proper +BOOL isApple(NSString* path, SecCSFlags flags); //determine if file is signed with Apple Dev ID/cert -BOOL isSignedDevID(NSString* binary); +BOOL isSignedDevID(NSString* path, SecCSFlags flags); //determine if a file is from the app store -// ->gotta be signed w/ Apple Dev ID & have valid app receipt +// gotta be signed w/ Apple Dev ID & have valid app receipt BOOL fromAppStore(NSString* path); //get GUID (really just computer's MAC address) -// ->from Apple's 'Get the GUID in OS X' (see: 'Validating Receipts Locally') +// from Apple's 'Get the GUID in OS X' (see: 'Validating Receipts Locally') NSData* getGUID(void); -//determine if a file is signed by Apple proper -BOOL isApple(NSString* path); +//extact entitlements +// note: execs apple's 'codesign' binary +NSDictionary* extractEntitlements(NSString* path); #endif diff --git a/procInfo/Signing.m b/procInfo/Signing.m index 7063cd8..db628b7 100644 --- a/procInfo/Signing.m +++ b/procInfo/Signing.m @@ -9,19 +9,130 @@ #import "Consts.h" #import "Signing.h" +#import "procInfo.h" #import "Utilities.h" #import "AppReceipt.h" +#import +#import +#import + #import #import #import #import +//determine the offset (if any) +// of the 'best' architecture in a (fat) binary +uint32_t bestArchOffset(NSString* path) +{ + //offset of best architecture + uint32_t offset = 0; + + //pool + @autoreleasepool + { + + //binary + NSMutableData* binary = nil; + + //le bytez + const void* binaryBytes = NULL; + + //fat header + struct fat_header* fatHeader = NULL; + + //number of fat architectures + uint32_t fatArchitectureCount = 0; + + //fat architectures + void *fatArchitectures = NULL; + + //local architecture + const NXArchInfo *localArchitecture = NULL; + + //best matching architecture + struct fat_arch *bestArchitecture = NULL; + + //load binary into memory + binary = [NSMutableData dataWithContentsOfFile:path]; + if(binary.length < sizeof(struct fat_header)) + { + //bail + goto bail; + } + + //grab bytes + binaryBytes = binary.bytes; + + //not universal (fat) + if( (FAT_MAGIC != *(const uint32_t *)binaryBytes) && + (FAT_CIGAM != *(const uint32_t *)binaryBytes) ) + { + //bail + goto bail; + } + + //binary is fat + // init pointer to fat header + fatHeader = (struct fat_header*)binaryBytes; + + //swap size? + if(fatHeader->magic == OSSwapHostToBigInt32(FAT_MAGIC)) + { + //swap + fatArchitectureCount = OSSwapBigToHostInt32(fatHeader->nfat_arch); + } + + //sanity check + if(binary.length <= sizeof(struct fat_header) + fatArchitectureCount * sizeof(struct fat_arch)) + { + //bail + goto bail; + } + + //init pointer to fat architectures + fatArchitectures = (char*)binaryBytes + sizeof(struct fat_header); + + //get local architecture + localArchitecture = NXGetLocalArchInfo(); + + //swap fat architectures? + if(fatHeader->magic == OSSwapHostToBigInt32(FAT_MAGIC)) + { + //swap + swap_fat_arch(fatArchitectures, fatArchitectureCount, localArchitecture->byteorder); + } + + //find best architecture + bestArchitecture = NXFindBestFatArch(localArchitecture->cputype, localArchitecture->cpusubtype, fatArchitectures, fatArchitectureCount); + if(NULL == bestArchitecture) + { + //bail + goto bail; + } + + //init offset + offset = bestArchitecture->offset; + +bail: + + ; + + }//autorelease + + return offset; +} + //get the signing info of a item -NSDictionary* extractSigningInfo(NSString* path) +NSMutableDictionary* extractSigningInfo(NSString* path, SecCSFlags flags, BOOL entitlements) { //info dictionary - NSMutableDictionary* signingStatus = nil; + NSMutableDictionary* signingInfo = nil; + + //offset of best architecture + // for universal/fat binary, need to check correct arch + uint32_t offset = 0; //code SecStaticCodeRef staticCode = NULL; @@ -30,7 +141,7 @@ NSDictionary* extractSigningInfo(NSString* path) OSStatus status = -1; //signing information - CFDictionaryRef signingInformation = NULL; + CFDictionaryRef signingDetails = NULL; //cert chain NSArray* certificateChain = nil; @@ -45,23 +156,27 @@ NSDictionary* extractSigningInfo(NSString* path) CFStringRef commonName = NULL; //init signing status - signingStatus = [NSMutableDictionary dictionary]; + signingInfo = [NSMutableDictionary dictionary]; //sanity check if(nil == path) { //set err - signingStatus[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:errSecCSObjectRequired]; + signingInfo[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:errSecCSObjectRequired]; //bail goto bail; } + //get offset of 'best' architecute + // this is what loader will run, and thus, what we should validate! + offset = bestArchOffset(path); + //create static code - status = SecStaticCodeCreateWithPath((__bridge CFURLRef)([NSURL fileURLWithPath:path]), kSecCSDefaultFlags, &staticCode); + status = SecStaticCodeCreateWithPathAndAttributes((__bridge CFURLRef)([NSURL fileURLWithPath:path]), kSecCSDefaultFlags, (__bridge CFDictionaryRef)@{(__bridge NSString *)kSecCodeAttributeUniversalFileOffset : [NSNumber numberWithUnsignedInt:offset]}, &staticCode); //save signature status - signingStatus[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status]; + signingInfo[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status]; if(noErr != status) { //bail @@ -69,60 +184,59 @@ NSDictionary* extractSigningInfo(NSString* path) } //check signature - status = SecStaticCodeCheckValidityWithErrors(staticCode, kSecCSDoNotValidateResources, NULL, NULL); + status = SecStaticCodeCheckValidity(staticCode, flags, NULL); //(re)save signature status - signingStatus[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status]; - - //if file is signed - // ->grab signing authorities - if(noErr == status) - { - //grab signing authorities - status = SecCodeCopySigningInformation(staticCode, kSecCSSigningInformation, &signingInformation); - if(noErr != status) - { - //bail - goto bail; - } - - //determine if binary is signed by Apple - signingStatus[KEY_SIGNING_IS_APPLE] = [NSNumber numberWithBool:isApple(path)]; - - //not apple proper - // ->is signed with Apple Dev ID? - if(YES != [signingStatus[KEY_SIGNING_IS_APPLE] boolValue]) - { - //determine if binary is Apple Dev ID - signingStatus[KEY_SIGNING_IS_APPLE_DEV_ID] = [NSNumber numberWithBool:isSignedDevID(path)]; - - //if dev id - // ->from app store? - if(YES == [signingStatus[KEY_SIGNING_IS_APPLE_DEV_ID] boolValue]) - { - //from app store? - signingStatus[KEY_SIGNING_IS_APP_STORE] = [NSNumber numberWithBool:fromAppStore(path)]; - } - } - } - //error - // ->not signed, or something else, so no need to check cert's names - else + signingInfo[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status]; + if(noErr != status) { //bail goto bail; } + //grab signing info + status = SecCodeCopySigningInformation(staticCode, kSecCSSigningInformation, &signingDetails); + if(noErr != status) + { + //bail + goto bail; + } + + //grab signing ID + signingInfo[KEY_SIGNATURE_IDENTIFIER] = [(__bridge NSDictionary*)signingDetails objectForKey:(__bridge NSString*)kSecCodeInfoIdentifier]; + + //determine if binary is signed by Apple + signingInfo[KEY_SIGNING_IS_APPLE] = [NSNumber numberWithBool:isApple(path, flags)]; + + //not apple proper + // is signed with Apple Dev ID? + if(YES != [signingInfo[KEY_SIGNING_IS_APPLE] boolValue]) + { + //determine if binary is Apple Dev ID + signingInfo[KEY_SIGNING_IS_APPLE_DEV_ID] = [NSNumber numberWithBool:isSignedDevID(path, flags)]; + + //if dev id + // from app store? + if(YES == [signingInfo[KEY_SIGNING_IS_APPLE_DEV_ID] boolValue]) + { + //from app store? + signingInfo[KEY_SIGNING_IS_APP_STORE] = [NSNumber numberWithBool:fromAppStore(path)]; + } + } + //init array for certificate names - signingStatus[KEY_SIGNING_AUTHORITIES] = [NSMutableArray array]; + signingInfo[KEY_SIGNING_AUTHORITIES] = [NSMutableArray array]; //get cert chain - certificateChain = [(__bridge NSDictionary*)signingInformation objectForKey:(__bridge NSString*)kSecCodeInfoCertificates]; + certificateChain = [(__bridge NSDictionary*)signingDetails objectForKey:(__bridge NSString*)kSecCodeInfoCertificates]; //get name of all certs - // ->add each to list + // add each to list for(index = 0; index < certificateChain.count; index++) { + //reset + commonName = NULL; + //extract cert certificate = (__bridge SecCertificateRef)([certificateChain objectAtIndex:index]); @@ -133,28 +247,41 @@ NSDictionary* extractSigningInfo(NSString* path) if( (noErr != status) || (NULL == commonName)) { + //release + if(NULL != commonName) + { + //release + CFRelease(commonName); + } + //skip continue; } //save - [signingStatus[KEY_SIGNING_AUTHORITIES] addObject:(__bridge NSString*)commonName]; + [signingInfo[KEY_SIGNING_AUTHORITIES] addObject:(__bridge NSString*)commonName]; //release name CFRelease(commonName); } - + //add entitlements? + if(YES == entitlements) + { + //extract entitlements via Apple's 'codesign' + signingInfo[KEY_SIGNING_ENTITLEMENTS] = extractEntitlements(path); + } + bail: //free signing info - if(NULL != signingInformation) + if(NULL != signingDetails) { //free - CFRelease(signingInformation); + CFRelease(signingDetails); //unset - signingInformation = NULL; + signingDetails = NULL; } //free static code @@ -167,11 +294,11 @@ bail: staticCode = NULL; } - return signingStatus; + return signingInfo; } //determine if a file is signed by Apple proper -BOOL isApple(NSString* path) +BOOL isApple(NSString* path, SecCSFlags flags) { //flag BOOL isApple = NO; @@ -205,17 +332,17 @@ BOOL isApple(NSString* path) //check if file is signed by apple by checking if it conforms to req string // note: ignore 'errSecCSBadResource' as lots of signed apple files return this issue :/ - status = SecStaticCodeCheckValidity(staticCode, kSecCSDefaultFlags, requirementRef); + status = SecStaticCodeCheckValidity(staticCode, flags, requirementRef); if( (noErr != status) && (errSecCSBadResource != status) ) { //bail - // ->just means app isn't signed by apple + // just means isn't signed by apple goto bail; } //ok, happy (SecStaticCodeCheckValidity() didn't fail) - // ->file is signed by Apple + // file is signed by Apple isApple = YES; bail: @@ -244,7 +371,7 @@ bail: } //verify the receipt -// ->check bundle ID, app version, and receipt's hash +// check bundle ID, app version, and receipt's hash BOOL verifyReceipt(NSBundle* appBundle, AppReceipt* receipt) { //flag @@ -280,7 +407,7 @@ BOOL verifyReceipt(NSBundle* appBundle, AppReceipt* receipt) [digestData appendData:receipt.bundleIdentifierData]; //CHECK 1: - // ->app's bundle ID should match receipt's bundle ID + // app's bundle ID should match receipt's bundle ID if(YES != [receipt.bundleIdentifier isEqualToString:appBundle.bundleIdentifier]) { //bail @@ -288,7 +415,7 @@ BOOL verifyReceipt(NSBundle* appBundle, AppReceipt* receipt) } //CHECK 2: - // ->app's version should match receipt's version + // app's version should match receipt's version if(YES != [receipt.appVersion isEqualToString:appBundle.infoDictionary[@"CFBundleShortVersionString"]]) { //bail @@ -296,7 +423,7 @@ BOOL verifyReceipt(NSBundle* appBundle, AppReceipt* receipt) } //CHECK 3: - // ->verify receipt's hash (UUID) + // verify receipt's hash (UUID) //init SHA 1 hash CC_SHA1(digestData.bytes, (CC_LONG)digestData.length, digestBuffer); @@ -317,7 +444,7 @@ bail: } //get GUID (really just computer's MAC address) -// ->from Apple's 'Get the GUID in OS X' (see: 'Validating Receipts Locally') +// from Apple's 'Get the GUID in OS X' (see: 'Validating Receipts Locally') NSData* getGUID() { //status var @@ -346,7 +473,7 @@ NSData* getGUID() //only init guid once dispatch_once(&onceToken, - ^{ + ^{ //get master port kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort); @@ -404,7 +531,7 @@ NSData* getGUID() IOObjectRelease(iterator); //convert guid to NSData* - // ->also release registry property + // also release registry property if(NULL != registryProperty) { //convert @@ -415,7 +542,7 @@ NSData* getGUID() } bail: - ; + ; });//only once @@ -423,7 +550,7 @@ bail: } //determine if file is signed with Apple Dev ID/cert -BOOL isSignedDevID(NSString* binary) +BOOL isSignedDevID(NSString* path, SecCSFlags flags) { //flag BOOL signedOK = NO; @@ -438,7 +565,7 @@ BOOL isSignedDevID(NSString* binary) OSStatus status = -1; //create static code - status = SecStaticCodeCreateWithPath((__bridge CFURLRef)([NSURL fileURLWithPath:binary]), kSecCSDefaultFlags, &staticCode); + status = SecStaticCodeCreateWithPath((__bridge CFURLRef)([NSURL fileURLWithPath:path]), kSecCSDefaultFlags, &staticCode); if(noErr != status) { //bail @@ -455,16 +582,16 @@ BOOL isSignedDevID(NSString* binary) } //check if file is signed w/ apple dev id by checking if it conforms to req string - status = SecStaticCodeCheckValidity(staticCode, kSecCSDefaultFlags, requirementRef); + status = SecStaticCodeCheckValidity(staticCode, flags, requirementRef); if(noErr != status) { //bail - // ->just means app isn't signed by apple dev id + // just means app isn't signed by apple dev id goto bail; } //ok, happy - // ->file is signed by Apple Dev ID + // file is signed by Apple Dev ID signedOK = YES; bail: @@ -493,8 +620,8 @@ bail: } //determine if a file is from the app store -// ->gotta be signed w/ Apple Dev ID & have valid app receipt -// note: here, assume this function is only called on Apps signed with Apple Dev ID! +// gotta be signed w/ Apple Dev ID & have valid app receipt +// note: here, assume this function is only called on Apps signed with Apple Dev ID! BOOL fromAppStore(NSString* path) { //flag @@ -504,16 +631,16 @@ BOOL fromAppStore(NSString* path) AppReceipt* appReceipt = nil; //path to app bundle - // ->just have binary + // just have binary NSBundle* appBundle = nil; //if it's an app - // ->can directly load app bundle + // can directly load app bundle appBundle = [NSBundle bundleWithPath:path]; if(nil == appBundle) { //find app bundle from binary - // ->likely not an application if this fails + // likely not an application if this fails appBundle = PI_findAppBundle(path); if(nil == appBundle) { @@ -523,7 +650,7 @@ BOOL fromAppStore(NSString* path) } //bail if it doesn't have an receipt - // ->done here, since checking signature is expensive! + // done here, since checking signature is expensive! if( (nil == appBundle.appStoreReceiptURL) || (YES != [[NSFileManager defaultManager] fileExistsAtPath:appBundle.appStoreReceiptURL.path]) ) { @@ -532,7 +659,7 @@ BOOL fromAppStore(NSString* path) } //init - // ->will parse/decode, etc + // will parse/decode, etc appReceipt = [[AppReceipt alloc] init:appBundle]; if(nil == appReceipt) { @@ -548,10 +675,95 @@ BOOL fromAppStore(NSString* path) } //happy - // ->app is signed w/ dev ID & its receipt is solid + // app is signed w/ dev ID & its receipt is solid appStoreApp = YES; bail: return appStoreApp; } + +//extact entitlements +// note: execs apple's 'codesign' binary +NSDictionary* extractEntitlements(NSString* path) +{ + //entitlements + NSDictionary* entitlements = nil; + + //results + NSMutableDictionary* results = nil; + + //entitlements at bytes + unsigned char* entitlementBytes = nil; + + //entitlements as data + NSData* entitlementsData = nil; + + //entitlements as xml + NSString* entitlementsXML = nil; + + //if path is '/usr/bin/codesign' + // this isn't entitled, and to avoid recursive calling, just bail + if(YES == [path isEqualToString:CODE_SIGN]) + { + //bail + goto bail; + } + + //exec 'codesign' + results = PI_execTask(CODE_SIGN, @[@"-d", @"--entitlements", @"-", path], YES, YES); + if(noErr != [results[EXIT_CODE] intValue]) + { + //bail + goto bail; + } + + //not entitled? + // could just check for nil, but use offset below + if([results[STDOUT] length] < 0x10) + { + //bail + goto bail; + } + + //grab bytes + entitlementBytes = (unsigned char*)[results[STDOUT] bytes]; + + //codesign has a bug where it returns some (encoding?) bytes first + // check for that here, and if found, start string conversion at offset 0x8 + if(0xFA == entitlementBytes[0]) + { + //convert to string + entitlementsXML = [[NSString alloc] initWithData:[results[STDOUT] subdataWithRange:NSMakeRange(0x8, [results[STDOUT] length] - 0x8)] encoding:NSUTF8StringEncoding]; + } + + //other just convert as is + else + { + //convert to string + entitlementsXML = [[NSString alloc] initWithData:results[STDOUT] encoding:NSUTF8StringEncoding]; + } + + //sanity check + // make sure conversion to string ok + if(0 == [entitlementsXML length]) + { + //bail + goto bail; + } + + //convert to data + entitlementsData = [entitlementsXML dataUsingEncoding:NSUTF8StringEncoding]; + if(nil == entitlementsData) + { + //bail + goto bail; + } + + //convert to dictionary + entitlements = [NSPropertyListSerialization propertyListWithData:entitlementsData options:NSPropertyListImmutable format:nil error:nil]; + +bail: + + return entitlements; +} diff --git a/procInfo/Utilities.h b/procInfo/Utilities.h index e3a128c..f176ff4 100644 --- a/procInfo/Utilities.h +++ b/procInfo/Utilities.h @@ -17,7 +17,7 @@ NSBundle* PI_findAppBundle(NSString* binaryPath); //check if current OS version is supported -// ->for now, just...? +// for now, just...? BOOL PI_isSupportedOS(void); //get OS version @@ -27,7 +27,7 @@ NSDictionary* PI_getOSVersion(void); NSMutableArray* PI_enumerateProcesses(void); //given a bundle -// ->find its executable +// find its executable NSString* PI_findAppBinary(NSString* appPath); //sha256 a file @@ -37,4 +37,7 @@ NSString* PI_hashFile(NSString* filePath); // find the full path by scanning $PATH NSString* PI_which(NSString* processName); +//exec a process with args +NSMutableDictionary* PI_execTask(NSString* binaryPath, NSArray* arguments, BOOL shouldWait, BOOL grabOutput); + #endif diff --git a/procInfo/Utilities.m b/procInfo/Utilities.m index e6b55a8..f609c14 100644 --- a/procInfo/Utilities.m +++ b/procInfo/Utilities.m @@ -15,7 +15,7 @@ #import //disable deprecated warnings -// ->use 'Gestalt' as this code may run on old OS X vers. +// use 'Gestalt' as this code may run on old OS X vers. #pragma clang diagnostic ignored "-Wdeprecated-declarations" //get OS version @@ -81,7 +81,7 @@ bail: } //is current OS version supported? -// ->for now, just OS X 10.8+ +// for now, just OS X 10.8+ BOOL PI_isSupportedOS() { //support flag @@ -158,7 +158,7 @@ NSMutableArray* PI_enumerateProcesses() } //iterate over all pids - // ->save pid into return array + // save pid into return array for(int i = 0; i < numberOfProcesses; ++i) { //save each pid @@ -185,7 +185,7 @@ bail: } //given a path to binary -// ->parse it back up to find app's bundle +// parse it back up to find app's bundle NSBundle* PI_findAppBundle(NSString* binaryPath) { //app's bundle @@ -204,24 +204,24 @@ NSBundle* PI_findAppBundle(NSString* binaryPath) appBundle = [NSBundle bundleWithPath:appPath]; //check for match - // ->binary path's match + // binary path's match if( (nil != appBundle) && - (YES == [appBundle.executablePath isEqualToString:binaryPath])) + (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 + // and at this point, its not a match appBundle = nil; //remove last part - // ->will try this next + // will try this next appPath = [appPath stringByDeletingLastPathComponent]; //scan until we get to root - // ->of course, loop will be exited if app info dictionary is found/loaded + // of course, loop will be exited if app info dictionary is found/loaded } while( (nil != appPath) && (YES != [appPath isEqualToString:@"/"]) && (YES != [appPath isEqualToString:@""]) ); @@ -229,7 +229,8 @@ NSBundle* PI_findAppBundle(NSString* binaryPath) return appBundle; } -//sha256 a file +//hash a file +// algorithm: sha256 NSString* PI_hashFile(NSString* filePath) { //file's contents @@ -258,7 +259,7 @@ NSString* PI_hashFile(NSString* filePath) CC_SHA256(fileContents.bytes, (unsigned int)fileContents.length, digestSHA256); //convert to NSString - // ->iterate over each bytes in computed digest and format + // iterate over each bytes in computed digest and format for(index=0; index < CC_SHA256_DIGEST_LENGTH; index++) { //format/append @@ -293,11 +294,11 @@ NSString* PI_which(NSString* processName) pathComponents = [path componentsSeparatedByString:@":"]; //iterate over all path components - // ->build candidate path and check if it exists + // build candidate path and check if it exists for(NSString* pathComponent in pathComponents) { //build candidate path - // ->current path component + process name + // current path component + process name candidateBinary = [pathComponent stringByAppendingPathComponent:processName]; //check if it exists @@ -318,3 +319,154 @@ NSString* PI_which(NSString* processName) return fullPath; } + +//exec a process with args +NSMutableDictionary* PI_execTask(NSString* binaryPath, NSArray* arguments, BOOL shouldWait, BOOL grabOutput) +{ + //task + NSTask* task = nil; + + //output pipe for stdout + NSPipe* stdOutPipe = nil; + + //output pipe for stderr + NSPipe* stdErrPipe = nil; + + //read handle for stdout + NSFileHandle* stdOutReadHandle = nil; + + //read handle for stderr + NSFileHandle* stdErrReadHandle = nil; + + //results dictionary + NSMutableDictionary* results = nil; + + //output for stdout + NSMutableData *stdOutData = nil; + + //output for stderr + NSMutableData *stdErrData = nil; + + //init dictionary for results + results = [NSMutableDictionary dictionary]; + + //init task + task = [[NSTask alloc] init]; + + //only setup pipes if wait flag is set + if(YES == grabOutput) + { + //init stdout pipe + stdOutPipe = [NSPipe pipe]; + + //init stderr pipe + stdErrPipe = [NSPipe pipe]; + + //init stdout read handle + stdOutReadHandle = [stdOutPipe fileHandleForReading]; + + //init stderr read handle + stdErrReadHandle = [stdErrPipe fileHandleForReading]; + + //init stdout output buffer + stdOutData = [NSMutableData data]; + + //init stderr output buffer + stdErrData = [NSMutableData data]; + + //set task's stdout + task.standardOutput = stdOutPipe; + + //set task's stderr + task.standardError = stdErrPipe; + } + + //set task's path + task.launchPath = binaryPath; + + //set task's args + if(nil != arguments) + { + //set + task.arguments = arguments; + } + + //wrap task launch + @try + { + //launch + [task launch]; + } + @catch(NSException *exception) + { + //bail + goto bail; + } + + //no need to wait + // can just bail w/ no output + if( (YES != shouldWait) && + (YES != grabOutput) ) + { + //bail + goto bail; + } + + + //wait + // ...but no output + else if( (YES == shouldWait) && + (YES != grabOutput) ) + { + //wait + [task waitUntilExit]; + + //add exit code + results[EXIT_CODE] = [NSNumber numberWithInteger:task.terminationStatus]; + + //bail + goto bail; + } + + //grab output? + // even if wait not set, still will wait! + else + { + //read in stdout/stderr + while(YES == [task isRunning]) + { + //accumulate stdout + [stdOutData appendData:[stdOutReadHandle readDataToEndOfFile]]; + + //accumulate stderr + [stdErrData appendData:[stdErrReadHandle readDataToEndOfFile]]; + } + + //grab any leftover stdout + [stdOutData appendData:[stdOutReadHandle readDataToEndOfFile]]; + + //grab any leftover stderr + [stdErrData appendData:[stdErrReadHandle readDataToEndOfFile]]; + + //add stdout + if(0 != stdOutData.length) + { + //add + results[STDOUT] = stdOutData; + } + + //add stderr + if(0 != stdErrData.length) + { + //add + results[STDERR] = stdErrData; + } + + //add exit code + results[EXIT_CODE] = [NSNumber numberWithInteger:task.terminationStatus]; + } + +bail: + + return results; +} diff --git a/procInfo/procInfo.h b/procInfo/procInfo.h index b275679..12e69d7 100644 --- a/procInfo/procInfo.h +++ b/procInfo/procInfo.h @@ -29,6 +29,24 @@ #define EVENT_EXEC 27 #define EVENT_SPAWN 43190 +//signature status +#define KEY_SIGNATURE_STATUS @"signatureStatus" + +//signing auths +#define KEY_SIGNING_AUTHORITIES @"signingAuthorities" + +//code signing id +#define KEY_SIGNATURE_IDENTIFIER @"signingIdentifier" + +//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" + /* TYPEDEFS */ //block for library @@ -39,12 +57,12 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull); @interface ProcInfo : NSObject -//init -// flag dictates if CPU intensive signing checks should be performed --(id _Nullable )init:(BOOL)skipSigningInfo; +//init w/ flag +// flag dictates if CPU-intensive logic (code signing, etc) should be preformed +-(id _Nullable)init:(BOOL)goEasy; //start monitoring --(BOOL)start:(ProcessCallbackBlock _Nonnull )callback; +-(void)start:(ProcessCallbackBlock _Nonnull )callback; //stop monitoring -(void)stop; @@ -77,26 +95,26 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull); @property u_int32_t exit; //path -@property (nonatomic, retain) NSString* _Nullable path; +@property(nonatomic, retain)NSString* _Nullable path; //args -@property (nonatomic, retain) NSMutableArray* _Nonnull arguments; +@property(nonatomic, retain)NSMutableArray* _Nonnull arguments; //ancestors -@property (nonatomic, retain) NSMutableArray* _Nonnull ancestors; +@property(nonatomic, retain)NSMutableArray* _Nonnull ancestors; //Binary object // has path, hash, etc -@property (nonatomic, retain) Binary* _Nonnull binary; +@property(nonatomic, retain)Binary* _Nonnull binary; //timestamp -@property (nonatomic, retain) NSDate* _Nonnull timestamp; +@property(nonatomic, retain)NSDate* _Nonnull timestamp; /* METHODS */ //init with a pid // method will then (try) fill out rest of object --(id _Nullable )init:(pid_t)processID; +-(id _Nullable)init:(pid_t)processID; //set process's path -(void)pathFromPid; @@ -119,23 +137,36 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull); /* PROPERTIES */ //path -@property (nonatomic, retain)NSString* _Nonnull path; +@property(nonatomic, retain)NSString* _Nonnull path; //name -@property (nonatomic, retain)NSString* _Nonnull name; +@property(nonatomic, retain)NSString* _Nonnull name; //icon -@property (nonatomic, retain)NSImage* _Nonnull icon; +@property(nonatomic, retain)NSImage* _Nonnull icon; //file attributes -@property (nonatomic, retain)NSDictionary* _Nullable attributes; +@property(nonatomic, retain)NSDictionary* _Nullable attributes; + +//spotlight meta data +@property(nonatomic, retain)NSDictionary* _Nullable metadata; //bundle // nil for non-apps -@property (nonatomic, retain)NSBundle* _Nullable bundle; +@property(nonatomic, retain)NSBundle* _Nullable bundle; //signing info -@property (nonatomic, retain)NSDictionary* _Nonnull signingInfo; +@property(nonatomic, retain)NSDictionary* _Nonnull signingInfo; + +//entitlements +@property(nonatomic, retain)NSDictionary* _Nonnull entitlements; + +//hash +@property(nonatomic, retain)NSString* _Nonnull sha256; + +//identifier +// either signing id or sha256 hash +@property(nonatomic, retain)NSString* _Nonnull identifier; //flag indicating binary belongs to Apple OS @property BOOL isApple; @@ -145,13 +176,32 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull); /* METHODS */ -//init w/ an info dictionary --(id _Nonnull )init:(NSString* _Nonnull)path; +//init w/ a path +-(id _Nonnull)init:(NSString* _Nonnull)path; + +/* the following methods are rather CPU-intensive + as such, if the proc monitoring is run with the 'goEasy' option, they aren't automatically invoked +*/ + +//get an icon for a process +// for apps, this will be app's icon, otherwise just a standard system one +-(void)getIcon; //generate signing info // also classifies if Apple/from App Store/etc. --(void)generateSigningInfo; +-(void)generateSigningInfo:(SecCSFlags)flags entitlements:(BOOL)entitlements; +//generate entitlements +// note: can also call 'generateSigningInfo' w/ 'entitlements:YES' +-(void)generateEntitlements; + +//generate hash +-(void)generateHash; + +//generate id +// eithersigning id, or sha256 hash +// note: will generate signing info if needed +-(void)generateIdentifier; @end