dynamic code signing
for processes, use dynamic code signing APIs
This commit is contained in:
Binary file not shown.
+27
-31
@@ -19,7 +19,6 @@
|
||||
@class Binary;
|
||||
@class Process;
|
||||
|
||||
|
||||
/* DEFINES */
|
||||
|
||||
//from audit_kevents.h
|
||||
@@ -29,30 +28,29 @@
|
||||
#define EVENT_EXEC 27
|
||||
#define EVENT_SPAWN 43190
|
||||
|
||||
//signers
|
||||
enum Signer{None, Apple, AppStore, DevID, AdHoc};
|
||||
|
||||
//signature status
|
||||
#define KEY_SIGNATURE_STATUS @"signatureStatus"
|
||||
|
||||
//signer
|
||||
#define KEY_SIGNATURE_SIGNER @"signatureSigner"
|
||||
|
||||
//signing auths
|
||||
#define KEY_SIGNING_AUTHORITIES @"signingAuthorities"
|
||||
#define KEY_SIGNATURE_AUTHORITIES @"signatureAuthorities"
|
||||
|
||||
//code signing id
|
||||
#define KEY_SIGNATURE_IDENTIFIER @"signingIdentifier"
|
||||
#define KEY_SIGNATURE_IDENTIFIER @"signatureIdentifier"
|
||||
|
||||
//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"
|
||||
//entitlements
|
||||
#define KEY_SIGNATURE_ENTITLEMENTS @"signatureEntitlements"
|
||||
|
||||
/* TYPEDEFS */
|
||||
|
||||
//block for library
|
||||
typedef void (^ProcessCallbackBlock)(Process* _Nonnull);
|
||||
|
||||
|
||||
/* OBJECT: PROCESS INFO */
|
||||
|
||||
@interface ProcInfo : NSObject
|
||||
@@ -103,6 +101,9 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull);
|
||||
//ancestors
|
||||
@property(nonatomic, retain)NSMutableArray* _Nonnull ancestors;
|
||||
|
||||
//signing info
|
||||
@property(nonatomic, retain)NSMutableDictionary* _Nonnull signingInfo;
|
||||
|
||||
//Binary object
|
||||
// has path, hash, etc
|
||||
@property(nonatomic, retain)Binary* _Nonnull binary;
|
||||
@@ -116,13 +117,18 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull);
|
||||
// method will then (try) fill out rest of object
|
||||
-(id _Nullable)init:(pid_t)processID;
|
||||
|
||||
//generate signing info
|
||||
// also classifies if Apple/from App Store/etc.
|
||||
-(void)generateSigningInfo:(SecCSFlags)flags;
|
||||
|
||||
//set process's path
|
||||
-(void)pathFromPid;
|
||||
|
||||
//generate list of ancestors
|
||||
-(void)enumerateAncestors;
|
||||
|
||||
//class method to get parent of arbitrary process
|
||||
//class method
|
||||
// get's parent of arbitrary process
|
||||
+(pid_t)getParentID:(pid_t)child;
|
||||
|
||||
@end
|
||||
@@ -158,22 +164,13 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull);
|
||||
//signing info
|
||||
@property(nonatomic, retain)NSDictionary* _Nonnull signingInfo;
|
||||
|
||||
//entitlements
|
||||
@property(nonatomic, retain)NSDictionary* _Nonnull entitlements;
|
||||
|
||||
//hash
|
||||
@property(nonatomic, retain)NSString* _Nonnull sha256;
|
||||
@property(nonatomic, retain)NSMutableString* _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;
|
||||
|
||||
//flag indicating binary is from official App Store
|
||||
@property BOOL isAppStore;
|
||||
|
||||
/* METHODS */
|
||||
|
||||
//init w/ a path
|
||||
@@ -187,20 +184,19 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull);
|
||||
// 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:(SecCSFlags)flags entitlements:(BOOL)entitlements;
|
||||
//generate signing info (statically)
|
||||
-(void)generateSigningInfo:(SecCSFlags)flags;
|
||||
|
||||
//generate entitlements
|
||||
// note: can also call 'generateSigningInfo' w/ 'entitlements:YES'
|
||||
-(void)generateEntitlements;
|
||||
/* the following methods are not invoked automatically
|
||||
as such, if you code has to manually invoke them if you want this info
|
||||
*/
|
||||
|
||||
//generate hash
|
||||
// algo: sha256
|
||||
-(void)generateHash;
|
||||
|
||||
//generate id
|
||||
// eithersigning id, or sha256 hash
|
||||
// note: will generate signing info if needed
|
||||
// either signing id, or sha256 hash
|
||||
-(void)generateIdentifier;
|
||||
|
||||
@end
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
7DD0A9351F35A04B000EA15D /* Signing.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DD0A9271F35A04B000EA15D /* Signing.m */; };
|
||||
7DD0A9361F35A04B000EA15D /* Signing.h in Headers */ = {isa = PBXBuildFile; fileRef = 7DD0A9281F35A04B000EA15D /* Signing.h */; };
|
||||
7DD0A9371F35A04B000EA15D /* Consts.h in Headers */ = {isa = PBXBuildFile; fileRef = 7DD0A9291F35A04B000EA15D /* Consts.h */; };
|
||||
7DD0A9381F35A04B000EA15D /* AppReceipt.h in Headers */ = {isa = PBXBuildFile; fileRef = 7DD0A92A1F35A04B000EA15D /* AppReceipt.h */; };
|
||||
7DD0A9391F35A04B000EA15D /* AppReceipt.m in Sources */ = {isa = PBXBuildFile; fileRef = 7DD0A92B1F35A04B000EA15D /* AppReceipt.m */; };
|
||||
7DD0A9471F37F373000EA15D /* procInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 7DD0A9461F37F373000EA15D /* procInfo.h */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@@ -30,8 +28,6 @@
|
||||
7DD0A9271F35A04B000EA15D /* Signing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Signing.m; path = procInfo/Signing.m; sourceTree = SOURCE_ROOT; };
|
||||
7DD0A9281F35A04B000EA15D /* Signing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Signing.h; path = procInfo/Signing.h; sourceTree = SOURCE_ROOT; };
|
||||
7DD0A9291F35A04B000EA15D /* Consts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Consts.h; path = procInfo/Consts.h; sourceTree = SOURCE_ROOT; };
|
||||
7DD0A92A1F35A04B000EA15D /* AppReceipt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppReceipt.h; path = procInfo/AppReceipt.h; sourceTree = SOURCE_ROOT; };
|
||||
7DD0A92B1F35A04B000EA15D /* AppReceipt.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppReceipt.m; path = procInfo/AppReceipt.m; sourceTree = SOURCE_ROOT; };
|
||||
7DD0A9461F37F373000EA15D /* procInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = procInfo.h; path = procInfo/procInfo.h; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@@ -75,8 +71,6 @@
|
||||
7D83A9E61EB6465D001506F0 /* src */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7DD0A92A1F35A04B000EA15D /* AppReceipt.h */,
|
||||
7DD0A92B1F35A04B000EA15D /* AppReceipt.m */,
|
||||
7DD0A9211F35A04B000EA15D /* Binary.m */,
|
||||
7DD0A9291F35A04B000EA15D /* Consts.h */,
|
||||
7DD0A9221F35A04B000EA15D /* Process.m */,
|
||||
@@ -97,7 +91,6 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7DD0A9381F35A04B000EA15D /* AppReceipt.h in Headers */,
|
||||
7DD0A9331F35A04B000EA15D /* Utilities.h in Headers */,
|
||||
7DD0A9471F37F373000EA15D /* procInfo.h in Headers */,
|
||||
7DD0A9371F35A04B000EA15D /* Consts.h in Headers */,
|
||||
@@ -131,7 +124,7 @@
|
||||
7D83A9DC1EB6465D001506F0 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0900;
|
||||
LastUpgradeCheck = 0940;
|
||||
ORGANIZATIONNAME = "Objective-See";
|
||||
TargetAttributes = {
|
||||
7D83A9E31EB6465D001506F0 = {
|
||||
@@ -183,7 +176,6 @@
|
||||
7DD0A92F1F35A04B000EA15D /* Binary.m in Sources */,
|
||||
7DD0A92D1F35A04B000EA15D /* ProcessMonitor.m in Sources */,
|
||||
7DD0A9321F35A04B000EA15D /* Utilities.m in Sources */,
|
||||
7DD0A9391F35A04B000EA15D /* AppReceipt.m in Sources */,
|
||||
7DD0A9351F35A04B000EA15D /* Signing.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -204,6 +196,7 @@
|
||||
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;
|
||||
@@ -211,6 +204,7 @@
|
||||
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;
|
||||
@@ -257,6 +251,7 @@
|
||||
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;
|
||||
@@ -264,6 +259,7 @@
|
||||
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;
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
//
|
||||
// File: AppReceipt.h
|
||||
// Project: Proc Info
|
||||
//
|
||||
// Created by: Patrick Wardle
|
||||
// Copyright: 2017 Objective-See
|
||||
// License: Creative Commons Attribution-NonCommercial 4.0 International License
|
||||
//
|
||||
// note a: see https://objective-see.com/blog/blog_0x10.html for details
|
||||
// note b: this code inspired by https://gist.github.com/sazameki/3026845
|
||||
//
|
||||
|
||||
#ifndef AppReceipt_h
|
||||
#define AppReceipt_h
|
||||
|
||||
#import <Security/CMSDecoder.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <Security/SecAsn1Coder.h>
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
#import <Security/SecAsn1Templates.h>
|
||||
|
||||
|
||||
//from 'Receipt Fields' section (Apple's 'Receipt Validation Programming Guide')
|
||||
|
||||
//ANS.1 data struct
|
||||
typedef struct
|
||||
{
|
||||
size_t length;
|
||||
unsigned char *data;
|
||||
} ASN1_Data;
|
||||
|
||||
//receipt attribute struct
|
||||
typedef struct
|
||||
{
|
||||
ASN1_Data type; // INTEGER
|
||||
ASN1_Data version; // INTEGER
|
||||
ASN1_Data value; // OCTET STRING
|
||||
|
||||
} ReceiptAttribute;
|
||||
|
||||
//receipt payload struct
|
||||
typedef struct
|
||||
{
|
||||
ReceiptAttribute **attrs;
|
||||
|
||||
} ReceiptPayload;
|
||||
|
||||
//ASN.1 receipt attribute template (from [1])
|
||||
static const SecAsn1Template kReceiptAttributeTemplate[] =
|
||||
{
|
||||
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ReceiptAttribute) },
|
||||
{ SEC_ASN1_INTEGER, offsetof(ReceiptAttribute, type), NULL, 0 },
|
||||
{ SEC_ASN1_INTEGER, offsetof(ReceiptAttribute, version), NULL, 0 },
|
||||
{ SEC_ASN1_OCTET_STRING, offsetof(ReceiptAttribute, value), NULL, 0 },
|
||||
{ 0, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
//ASN.1 receipt template set (from [1])
|
||||
static const SecAsn1Template kSetOfReceiptAttributeTemplate[] =
|
||||
{
|
||||
{ SEC_ASN1_SET_OF, 0, kReceiptAttributeTemplate, sizeof(ReceiptPayload) },
|
||||
{ 0, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
//attribute type for bundle ID
|
||||
#define RECEIPT_ATTR_BUNDLE_ID 2
|
||||
|
||||
//attribute type for app version
|
||||
#define RECEIPT_ATTR_APP_VERSION 3
|
||||
|
||||
//attribute type for opaque value
|
||||
#define RECEIPT_ATTR_OPAQUE_VALUE 4
|
||||
|
||||
//attribute type for receipt's hash
|
||||
#define RECEIPT_ATTR_RECEIPT_HASH 5
|
||||
|
||||
//key for bundle id
|
||||
#define KEY_BUNDLE_ID @"bundleID"
|
||||
|
||||
//key for bundle id data
|
||||
#define KEY_BUNDLE_DATA @"bundleIDData"
|
||||
|
||||
//key for app version
|
||||
#define KEY_APP_VERSION @"applicationVersion"
|
||||
|
||||
//key for opaque value
|
||||
#define KEY_OPAQUE_VALUE @"opaqueValue"
|
||||
|
||||
//key for receipt's sha-1 hash
|
||||
#define KEY_RECEIPT_HASH @"receiptHash"
|
||||
|
||||
//class interface
|
||||
@interface AppReceipt : NSObject
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/* METHODS */
|
||||
|
||||
//init with app path
|
||||
// locate/load receipt, etc
|
||||
-(instancetype)init:(NSBundle *)bundle;
|
||||
|
||||
/* PROPERTIES */
|
||||
|
||||
//encoded receipt data
|
||||
@property (nonatomic, retain) NSData* encodedData;
|
||||
|
||||
//decoded receipt data
|
||||
@property (nonatomic, retain) NSData* decodedData;
|
||||
|
||||
//receipt components
|
||||
@property(nonatomic, retain)NSMutableDictionary* components;
|
||||
|
||||
//bundle id (from receipt)
|
||||
@property (nonatomic, strong, readonly) NSString *bundleIdentifier;
|
||||
|
||||
//bundle id data (from receipt)
|
||||
@property (nonatomic, strong, readonly) NSData *bundleIdentifierData;
|
||||
|
||||
//app version (from receipt)
|
||||
@property (nonatomic, strong, readonly) NSString *appVersion;
|
||||
|
||||
//opaque value (from receipt)
|
||||
@property (nonatomic, strong, readonly) NSData *opaqueValue;
|
||||
|
||||
//receipts hash (from receipt)
|
||||
@property (nonatomic, strong, readonly) NSData *receiptHash;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* AppReceipt_h */
|
||||
@@ -1,403 +0,0 @@
|
||||
//
|
||||
// File: AppReceipt.h
|
||||
// Project: Proc Info
|
||||
//
|
||||
// Created by: Patrick Wardle
|
||||
// Copyright: 2017 Objective-See
|
||||
// License: Creative Commons Attribution-NonCommercial 4.0 International License
|
||||
//
|
||||
// note a: see https://objective-see.com/blog/blog_0x10.html for details
|
||||
// note b: this code inspired by [1] https://gist.github.com/sazameki/3026845
|
||||
//
|
||||
|
||||
#import "AppReceipt.h"
|
||||
|
||||
//helper function from [1]
|
||||
// extract an int from ASN.1 data
|
||||
inline static int getIntValueFromASN1Data(const ASN1_Data *asn1Data)
|
||||
{
|
||||
int ret = 0;
|
||||
for (int i = 0; i < asn1Data->length; i++)
|
||||
{
|
||||
ret = (ret << 8) | asn1Data->data[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//helper function from [1]
|
||||
// decode string from ASN.1 data
|
||||
inline static NSString *decodeUTF8StringFromASN1Data(SecAsn1CoderRef decoder, ASN1_Data srcData)
|
||||
{
|
||||
//data struct
|
||||
ASN1_Data asn1Data = {0};
|
||||
|
||||
//decoded string
|
||||
NSString* decodedString = nil;
|
||||
|
||||
//status
|
||||
OSStatus status = -1;
|
||||
|
||||
//decode
|
||||
status = SecAsn1Decode(decoder, srcData.data, srcData.length, kSecAsn1UTF8StringTemplate, &asn1Data);
|
||||
if(noErr != status)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//convert to string
|
||||
decodedString = [[NSString alloc] initWithBytes:asn1Data.data length:asn1Data.length encoding:NSUTF8StringEncoding];
|
||||
|
||||
bail:
|
||||
|
||||
return decodedString;
|
||||
}
|
||||
|
||||
//class implementation
|
||||
@implementation AppReceipt
|
||||
|
||||
@synthesize components;
|
||||
@synthesize encodedData;
|
||||
@synthesize decodedData;
|
||||
|
||||
//init with app path
|
||||
// locate/load/decode receipt, etc
|
||||
-(instancetype)init:(NSBundle *)bundle
|
||||
{
|
||||
//init
|
||||
if(self = [super init])
|
||||
{
|
||||
//first check for receipt
|
||||
if( (nil == bundle.appStoreReceiptURL) ||
|
||||
(YES != [[NSFileManager defaultManager] fileExistsAtPath:bundle.appStoreReceiptURL.path]) )
|
||||
{
|
||||
//unset
|
||||
self = nil;
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//load encoded receipt data
|
||||
self.encodedData = [NSData dataWithContentsOfURL:bundle.appStoreReceiptURL];
|
||||
if(nil == self.encodedData)
|
||||
{
|
||||
//unset
|
||||
self = nil;
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//decode receipt data
|
||||
self.decodedData = [self decode];
|
||||
if(nil == self.decodedData)
|
||||
{
|
||||
//unset
|
||||
self = nil;
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//parse out values
|
||||
// bundle id, app version, etc
|
||||
self.components = [self parse];
|
||||
if( (nil == self.components) ||
|
||||
(0 == self.components.count) )
|
||||
{
|
||||
//unset
|
||||
self = nil;
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
bail:
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//decode receipt data
|
||||
// some validations performed here too
|
||||
-(NSData*)decode
|
||||
{
|
||||
//decoded data
|
||||
NSData* decoded = nil;
|
||||
|
||||
//decoder
|
||||
CMSDecoderRef decoder = NULL;
|
||||
|
||||
//policy
|
||||
SecPolicyRef policy = NULL;
|
||||
|
||||
//trust
|
||||
SecTrustRef trust = NULL;
|
||||
|
||||
//status
|
||||
OSStatus status = -1;
|
||||
|
||||
//data
|
||||
CFDataRef data = NULL;
|
||||
|
||||
//number of signers
|
||||
size_t signers = 0;
|
||||
|
||||
//signer status
|
||||
CMSSignerStatus signerStatus = -1;
|
||||
|
||||
//cert verify
|
||||
OSStatus certVerifyResult = 1;
|
||||
|
||||
//create decoder
|
||||
status = CMSDecoderCreate(&decoder);
|
||||
if(noErr != status)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//add encoded data to message
|
||||
status = CMSDecoderUpdateMessage(decoder, self.encodedData.bytes, self.encodedData.length);
|
||||
if(noErr != status)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//decode
|
||||
status = CMSDecoderFinalizeMessage(decoder);
|
||||
if(noErr != status)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//create policy
|
||||
policy = SecPolicyCreateBasicX509();
|
||||
if(NULL == policy)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//CHECK 1:
|
||||
// make sure there is a signer
|
||||
status = CMSDecoderGetNumSigners(decoder, &signers);
|
||||
if( (noErr != status) ||
|
||||
(0 == signers) )
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//CHECK 2:
|
||||
// make sure signer status is ok
|
||||
status = CMSDecoderCopySignerStatus(decoder, 0, policy, TRUE, &signerStatus, &trust, &certVerifyResult);
|
||||
if( (noErr != status) ||
|
||||
(kCMSSignerValid != signerStatus) )
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//grab decoded content
|
||||
status = CMSDecoderCopyContent(decoder, &data);
|
||||
if(noErr != status)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//convert to NSData
|
||||
decoded = [NSData dataWithData:(__bridge NSData * _Nonnull)(data)];
|
||||
|
||||
bail:
|
||||
|
||||
//release policy
|
||||
if(NULL != policy)
|
||||
{
|
||||
//release
|
||||
CFRelease(policy);
|
||||
|
||||
//unset
|
||||
policy = NULL;
|
||||
}
|
||||
|
||||
//release trust
|
||||
if(NULL != trust)
|
||||
{
|
||||
//release
|
||||
CFRelease(trust);
|
||||
|
||||
//unset
|
||||
trust = NULL;
|
||||
}
|
||||
|
||||
//release decoder
|
||||
if(NULL != decoder)
|
||||
{
|
||||
//release
|
||||
CFRelease(decoder);
|
||||
|
||||
//unset
|
||||
decoder = NULL;
|
||||
}
|
||||
|
||||
//release data
|
||||
if(NULL != data)
|
||||
{
|
||||
//release
|
||||
CFRelease(data);
|
||||
|
||||
//unset
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
//parse decoded receipt
|
||||
// extract out items such as bundle id, app version, etc.
|
||||
-(NSMutableDictionary*)parse
|
||||
{
|
||||
//decoder
|
||||
SecAsn1CoderRef decoder = NULL;
|
||||
|
||||
//status
|
||||
OSStatus status = -1;
|
||||
|
||||
//payload struct
|
||||
ReceiptPayload payload = {0};
|
||||
|
||||
//attribute
|
||||
ReceiptAttribute *attribute;
|
||||
|
||||
//dictionary for items
|
||||
NSMutableDictionary* items = nil;
|
||||
|
||||
//create decoder
|
||||
status = SecAsn1CoderCreate(&decoder);
|
||||
if(noErr != status)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//decode
|
||||
status = SecAsn1Decode(decoder, self.decodedData.bytes, self.decodedData.length, kSetOfReceiptAttributeTemplate, &payload);
|
||||
if(noErr != status)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//init dictionary for items
|
||||
items = [NSMutableDictionary dictionary];
|
||||
|
||||
//extact attributes
|
||||
// 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
|
||||
case RECEIPT_ATTR_BUNDLE_ID:
|
||||
{
|
||||
//save bundle id
|
||||
items[KEY_BUNDLE_ID] = decodeUTF8StringFromASN1Data(decoder, attribute->value);
|
||||
|
||||
//save bundle id data
|
||||
items[KEY_BUNDLE_DATA] = [NSData dataWithBytes:attribute->value.data length:attribute->value.length];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//app version
|
||||
case RECEIPT_ATTR_APP_VERSION:
|
||||
{
|
||||
//save
|
||||
items[KEY_APP_VERSION] = decodeUTF8StringFromASN1Data(decoder, attribute->value);
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
//opaque value
|
||||
case RECEIPT_ATTR_OPAQUE_VALUE:
|
||||
{
|
||||
//save
|
||||
items[KEY_OPAQUE_VALUE] = [NSData dataWithBytes:attribute->value.data length:attribute->value.length];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//receipt hash
|
||||
case RECEIPT_ATTR_RECEIPT_HASH:
|
||||
{
|
||||
//save
|
||||
items[KEY_RECEIPT_HASH] = [NSData dataWithBytes:attribute->value.data length:attribute->value.length];
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//default
|
||||
// ignore
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
}//switch
|
||||
|
||||
}//for all attributes
|
||||
|
||||
bail:
|
||||
|
||||
//release decoder
|
||||
if(NULL != decoder)
|
||||
{
|
||||
//release
|
||||
SecAsn1CoderRelease(decoder);
|
||||
|
||||
//unset
|
||||
decoder = NULL;
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
//return bundle id
|
||||
-(NSString*)bundleIdentifier
|
||||
{
|
||||
return self.components[KEY_BUNDLE_ID];
|
||||
}
|
||||
|
||||
//return bundle id data
|
||||
-(NSData*)bundleIdentifierData
|
||||
{
|
||||
return self.components[KEY_BUNDLE_DATA];
|
||||
}
|
||||
|
||||
//return app version
|
||||
-(NSString*)appVersion
|
||||
{
|
||||
return self.components[KEY_APP_VERSION];
|
||||
}
|
||||
|
||||
//return opaque data
|
||||
-(NSData*)opaqueValue
|
||||
{
|
||||
return self.components[KEY_OPAQUE_VALUE];
|
||||
}
|
||||
|
||||
//return receipt hash
|
||||
-(NSData*)receiptHash
|
||||
{
|
||||
return self.components[KEY_RECEIPT_HASH];
|
||||
}
|
||||
|
||||
@end
|
||||
+58
-70
@@ -12,19 +12,16 @@
|
||||
#import "procInfo.h"
|
||||
#import "Utilities.h"
|
||||
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
|
||||
@implementation Binary
|
||||
|
||||
@synthesize icon;
|
||||
@synthesize name;
|
||||
@synthesize path;
|
||||
@synthesize bundle;
|
||||
@synthesize isApple;
|
||||
@synthesize metadata;
|
||||
@synthesize attributes;
|
||||
@synthesize identifier;
|
||||
@synthesize isAppStore;
|
||||
@synthesize signingInfo;
|
||||
@synthesize entitlements;
|
||||
|
||||
//init binary object
|
||||
// note: CPU-intensive logic (code signing, etc) called manually
|
||||
@@ -151,17 +148,24 @@
|
||||
bail:
|
||||
|
||||
//release names
|
||||
if(nil != attributeNames)
|
||||
if(NULL != attributeNames)
|
||||
{
|
||||
//release
|
||||
CFRelease(attributeNames);
|
||||
|
||||
//unset
|
||||
attributeNames = NULL;
|
||||
|
||||
}
|
||||
|
||||
//release item
|
||||
if(nil != mdItem)
|
||||
//release md item
|
||||
if(NULL != mdItem)
|
||||
{
|
||||
//release
|
||||
CFRelease(mdItem);
|
||||
|
||||
//unset
|
||||
mdItem = NULL;
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -180,9 +184,6 @@ bail:
|
||||
//icon's path extension
|
||||
NSString* iconExtension = nil;
|
||||
|
||||
//system's document icon
|
||||
static NSData* documentIcon = nil;
|
||||
|
||||
//skip 'short' paths
|
||||
// otherwise system logs an error
|
||||
if( (YES != [self.path hasPrefix:@"/"]) &&
|
||||
@@ -224,24 +225,6 @@ bail:
|
||||
{
|
||||
//extract icon
|
||||
self.icon = [[NSWorkspace sharedWorkspace] iconForFile:self.path];
|
||||
|
||||
//load system document icon
|
||||
// static var, so only load once
|
||||
if(nil == documentIcon)
|
||||
{
|
||||
//load
|
||||
documentIcon = [[[NSWorkspace sharedWorkspace] iconForFileType:
|
||||
NSFileTypeForHFSTypeCode(kGenericDocumentIcon)] TIFFRepresentation];
|
||||
}
|
||||
|
||||
//if 'iconForFile' method doesn't find and icon, it returns the system 'document' icon
|
||||
// the system 'applicaiton' icon seems more applicable, so use that here...
|
||||
if(YES == [[self.icon TIFFRepresentation] isEqual:documentIcon])
|
||||
{
|
||||
//set icon to system 'applicaiton' icon
|
||||
self.icon = [[NSWorkspace sharedWorkspace]
|
||||
iconForFileType: NSFileTypeForHFSTypeCode(kGenericApplicationIcon)];
|
||||
}
|
||||
}
|
||||
|
||||
//make standard size...
|
||||
@@ -252,39 +235,47 @@ bail:
|
||||
return;
|
||||
}
|
||||
|
||||
//generate signing info
|
||||
// also classifies if Apple/from App Store/etc.
|
||||
-(void)generateSigningInfo:(SecCSFlags)flags entitlements:(BOOL)entitlements
|
||||
//statically, generate signing info
|
||||
-(void)generateSigningInfo:(SecCSFlags)flags
|
||||
{
|
||||
//extract signing info (do this first!)
|
||||
// from Apple, App Store, signing authorities, etc
|
||||
self.signingInfo = extractSigningInfo(self.path, flags, entitlements);
|
||||
|
||||
//perform more signing checks and lists
|
||||
// gotta be happily signed for checks though
|
||||
if(0 == [self.signingInfo[KEY_SIGNATURE_STATUS] intValue])
|
||||
{
|
||||
//set flag for signed by Apple proper
|
||||
self.isApple = [self.signingInfo[KEY_SIGNING_IS_APPLE] boolValue];
|
||||
|
||||
//when not Apple proper
|
||||
// check flag for from official App Store or is whitelisted
|
||||
if(YES != isApple)
|
||||
{
|
||||
//set flag
|
||||
self.isAppStore = [self.signingInfo[KEY_SIGNING_IS_APP_STORE] boolValue];
|
||||
}
|
||||
}
|
||||
|
||||
//extract signing info statically
|
||||
self.signingInfo = extractSigningInfo(0, self.path, flags);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//generate hash
|
||||
-(void)generateHash
|
||||
{
|
||||
//hash
|
||||
self.sha256 = PI_hashFile(self.path);
|
||||
//file's contents
|
||||
NSData* fileContents = nil;
|
||||
|
||||
//hash digest
|
||||
uint8_t digestSHA256[CC_SHA256_DIGEST_LENGTH] = {0};
|
||||
|
||||
//load file
|
||||
if(nil == (fileContents = [NSData dataWithContentsOfFile:self.path]))
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//sha1 it
|
||||
CC_SHA256(fileContents.bytes, (unsigned int)fileContents.length, digestSHA256);
|
||||
|
||||
//now init
|
||||
self.sha256 = [NSMutableString string];
|
||||
|
||||
//convert to NSString
|
||||
// iterate over each byte in computed digest and format
|
||||
for(NSUInteger index=0; index < CC_SHA256_DIGEST_LENGTH; index++)
|
||||
{
|
||||
//format/append
|
||||
[self.sha256 appendFormat:@"%02lX", (unsigned long)digestSHA256[index]];
|
||||
}
|
||||
|
||||
bail:
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -297,44 +288,41 @@ bail:
|
||||
if(nil == self.signingInfo)
|
||||
{
|
||||
//generate
|
||||
[self generateSigningInfo:kSecCSDefaultFlags entitlements:NO];
|
||||
[self generateSigningInfo:kSecCSDefaultFlags];
|
||||
}
|
||||
|
||||
//validly signed binary?
|
||||
// use its signing identifier
|
||||
if( (noErr == [self.signingInfo[KEY_SIGNATURE_STATUS] intValue]) &&
|
||||
(0 != [self.signingInfo[KEY_SIGNING_AUTHORITIES] count]) &&
|
||||
(0 != [self.signingInfo[KEY_SIGNATURE_AUTHORITIES] count]) &&
|
||||
(nil != self.signingInfo[KEY_SIGNATURE_IDENTIFIER]) )
|
||||
{
|
||||
//use signing id
|
||||
self.identifier = self.signingInfo[KEY_SIGNATURE_IDENTIFIER];
|
||||
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);
|
||||
//generate hash?
|
||||
if(0 != self.sha256.length)
|
||||
{
|
||||
//hash
|
||||
[self generateHash];
|
||||
}
|
||||
|
||||
//use hash
|
||||
self.identifier = self.sha256;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
//pretty print
|
||||
return [NSString stringWithFormat: @"name: %@\npath: %@\nattributes: %@\nsigning info: %@ (isApple: %d / isAppStore: %d)", self.name, self.path, self.attributes, self.signingInfo, self.isApple, self.isAppStore];
|
||||
return [NSString stringWithFormat: @"name: %@\npath: %@\nattributes: %@\nsigning info: %@", self.name, self.path, self.attributes, self.signingInfo];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+81
-55
@@ -7,6 +7,7 @@
|
||||
// License: Creative Commons Attribution-NonCommercial 4.0 International License
|
||||
//
|
||||
|
||||
#import "Signing.h"
|
||||
#import "procInfo.h"
|
||||
#import "Utilities.h"
|
||||
|
||||
@@ -19,6 +20,7 @@
|
||||
@synthesize ancestors;
|
||||
@synthesize arguments;
|
||||
@synthesize timestamp;
|
||||
@synthesize signingInfo;
|
||||
|
||||
//init
|
||||
-(id)init
|
||||
@@ -211,70 +213,73 @@ bail:
|
||||
//clear out buffer
|
||||
bzero(pathBuffer, PROC_PIDPATHINFO_MAXSIZE);
|
||||
|
||||
//first attempt to get path via 'proc_pidpath()'
|
||||
//1st:
|
||||
// attempt to get path via 'proc_pidpath()'
|
||||
status = proc_pidpath(self.pid, pathBuffer, sizeof(pathBuffer));
|
||||
if( (0 != status) &&
|
||||
(0 != strlen(pathBuffer)) )
|
||||
{
|
||||
//init path
|
||||
self.path = [NSString stringWithUTF8String:pathBuffer];
|
||||
|
||||
//all set
|
||||
goto bail;
|
||||
}
|
||||
//otherwise
|
||||
|
||||
//2nd:
|
||||
// try via process'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))
|
||||
{
|
||||
//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
|
||||
processArgs = calloc(systemMaxArgs, sizeof(char));
|
||||
if(NULL == processArgs)
|
||||
{
|
||||
//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, processArgs, &size, NULL, 0))
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//sanity check
|
||||
// ensure buffer is somewhat sane
|
||||
if(size <= sizeof(int))
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//extract process name
|
||||
// follows # of args (int) and is NULL-terminated
|
||||
self.path = [NSString stringWithUTF8String:processArgs + sizeof(int)];
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//alloc space for args
|
||||
processArgs = calloc(systemMaxArgs, sizeof(char));
|
||||
if(NULL == processArgs)
|
||||
{
|
||||
//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, processArgs, &size, NULL, 0))
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//sanity check
|
||||
// ensure buffer is somewhat sane
|
||||
if(size <= sizeof(int))
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//extract process name
|
||||
// follows # of args (int) and is NULL-terminated
|
||||
self.path = [NSString stringWithUTF8String:processArgs + sizeof(int)];
|
||||
|
||||
bail:
|
||||
|
||||
//free process args
|
||||
@@ -488,11 +493,33 @@ bail:
|
||||
return;
|
||||
}
|
||||
|
||||
//generate signing info
|
||||
// also classifies if Apple/from App Store/etc.
|
||||
-(void)generateSigningInfo:(SecCSFlags)flags
|
||||
{
|
||||
//extract signing info dynamically
|
||||
self.signingInfo = extractSigningInfo(self.pid, nil, flags);
|
||||
|
||||
//failed?
|
||||
// try extract signing info statically
|
||||
if(nil == self.signingInfo)
|
||||
{
|
||||
//add 'all archs' / 'nested'
|
||||
// cuz don't know which is/was running
|
||||
flags |= kSecCSCheckAllArchitectures | kSecCSCheckNestedCode | kSecCSDoNotValidateResources;
|
||||
|
||||
//extract signing info statically
|
||||
self.signingInfo = extractSigningInfo(0, self.binary.path, flags);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//for pretty printing
|
||||
-(NSString *)description
|
||||
{
|
||||
//pretty print
|
||||
return [NSString stringWithFormat: @"pid: %d\npath: %@\nuser: %d\nargs: %@\nancestors: %@\nbinary:\n%@", self.pid, self.path, self.uid, self.arguments, self.ancestors, self.binary];
|
||||
return [NSString stringWithFormat: @"pid: %d\npath: %@\nuser: %d\nargs: %@\nancestors: %@\n signing info: %@\n binary:\n%@", self.pid, self.path, self.uid, self.arguments, self.ancestors, self.signingInfo, self.binary];
|
||||
}
|
||||
|
||||
//class method to get parent of arbitrary process
|
||||
@@ -533,5 +560,4 @@ bail:
|
||||
return parentID;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
+15
-14
@@ -45,7 +45,7 @@
|
||||
|
||||
//init
|
||||
// just check OS version
|
||||
-(id _Nullable )init
|
||||
-(id _Nullable)init
|
||||
{
|
||||
//super
|
||||
self = [super init];
|
||||
@@ -73,7 +73,7 @@ bail:
|
||||
|
||||
//init w/ flag
|
||||
// flag dictates if CPU-intensive logic (code signing, etc) should be preformed
|
||||
-(id _Nullable )init:(BOOL)goEasy;
|
||||
-(id _Nullable)init:(BOOL)goEasy;
|
||||
{
|
||||
//init
|
||||
// calls 'super' too
|
||||
@@ -486,7 +486,7 @@ bail:
|
||||
//handle process starts
|
||||
else
|
||||
{
|
||||
//try get process path
|
||||
//also try get process path
|
||||
// this is the most 'trusted way' (since exec_args can change)
|
||||
[process pathFromPid];
|
||||
|
||||
@@ -661,11 +661,14 @@ bail:
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//handle new process
|
||||
// create Binary obj/enum/process ancestors, etc
|
||||
-(void)handleProcessStart:(Process*)process
|
||||
{
|
||||
//default cs flags
|
||||
// note: since this is dynamic check, we don't need to check all architectures, or skip resources, etc...
|
||||
SecCSFlags flags = kSecCSDefaultFlags;
|
||||
|
||||
//sanity check
|
||||
// should only occur for fork() events, which normally get superceeded by an exec(), etc
|
||||
if( (-1 == process.pid) ||
|
||||
@@ -705,7 +708,8 @@ bail:
|
||||
if(YES != self.goEasy)
|
||||
{
|
||||
//generate signing info
|
||||
[process.binary generateSigningInfo:kSecCSCheckAllArchitectures entitlements:NO];
|
||||
// first will try dynamic, falling back to static
|
||||
[process generateSigningInfo:flags];
|
||||
|
||||
//set icon
|
||||
[process.binary getIcon];
|
||||
@@ -732,6 +736,10 @@ bail:
|
||||
//return array of running processes
|
||||
-(NSMutableArray*)currentProcesses
|
||||
{
|
||||
//default cs flags
|
||||
// note: since this is dynamic check, we don't need to check all architectures, or skip resources, etc...
|
||||
SecCSFlags flags = kSecCSDefaultFlags;
|
||||
|
||||
//current process
|
||||
Process* currentProcess = nil;
|
||||
|
||||
@@ -745,15 +753,8 @@ bail:
|
||||
// init process object w/ pid/path, etc
|
||||
for(NSNumber* pid in PI_enumerateProcesses())
|
||||
{
|
||||
//skip 'blank' pids
|
||||
if(0 == pid.unsignedShortValue)
|
||||
{
|
||||
//skip
|
||||
continue;
|
||||
}
|
||||
|
||||
//create process obj
|
||||
currentProcess = [[Process alloc] init:pid.unsignedShortValue];
|
||||
currentProcess = [[Process alloc] init:pid.intValue];
|
||||
if(nil == currentProcess)
|
||||
{
|
||||
//skip
|
||||
@@ -761,7 +762,7 @@ bail:
|
||||
}
|
||||
|
||||
//generate signing info
|
||||
[currentProcess.binary generateSigningInfo:kSecCSCheckAllArchitectures entitlements:NO];
|
||||
[currentProcess generateSigningInfo:flags];
|
||||
|
||||
//add
|
||||
[processes addObject:currentProcess];
|
||||
|
||||
+10
-17
@@ -15,25 +15,18 @@
|
||||
|
||||
/* FUNCTIONS */
|
||||
|
||||
//get the signing info of a file
|
||||
NSMutableDictionary* extractSigningInfo(NSString* path, SecCSFlags flags, BOOL entitlements);
|
||||
//get the signing info of a item
|
||||
// pid specified: extract dynamic code signing info
|
||||
// path specified: generate static code signing info
|
||||
NSMutableDictionary* extractSigningInfo(pid_t pid, NSString* path, SecCSFlags flags);
|
||||
|
||||
//determine if a file is signed by Apple proper
|
||||
BOOL isApple(NSString* path, SecCSFlags flags);
|
||||
//determine who signed item
|
||||
NSNumber* extractSigner(SecStaticCodeRef code, SecCSFlags flags, BOOL isDynamic);
|
||||
|
||||
//determine if file is signed with Apple Dev ID/cert
|
||||
BOOL isSignedDevID(NSString* path, SecCSFlags flags);
|
||||
//validate a requirement
|
||||
OSStatus validateRequirement(SecStaticCodeRef code, SecRequirementRef requirement, SecCSFlags flags, BOOL isDynamic);
|
||||
|
||||
//determine if a file is from the app store
|
||||
// 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')
|
||||
NSData* getGUID(void);
|
||||
|
||||
//extact entitlements
|
||||
// note: execs apple's 'codesign' binary
|
||||
NSDictionary* extractEntitlements(NSString* path);
|
||||
//extract (names) of signing auths
|
||||
NSMutableArray* extractSigningAuths(NSDictionary* signingDetails);
|
||||
|
||||
#endif
|
||||
|
||||
+248
-682
@@ -11,265 +11,146 @@
|
||||
#import "Signing.h"
|
||||
#import "procInfo.h"
|
||||
#import "Utilities.h"
|
||||
#import "AppReceipt.h"
|
||||
|
||||
#import <mach-o/fat.h>
|
||||
#import <mach-o/arch.h>
|
||||
#import <mach-o/swap.h>
|
||||
|
||||
#import <sys/sysctl.h>
|
||||
#import <Security/Security.h>
|
||||
#import <CommonCrypto/CommonDigest.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
|
||||
//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
|
||||
NSMutableDictionary* extractSigningInfo(NSString* path, SecCSFlags flags, BOOL entitlements)
|
||||
// pid specified: extract dynamic code signing info
|
||||
// path specified: generate static code signing info
|
||||
NSMutableDictionary* extractSigningInfo(pid_t pid, NSString* path, SecCSFlags flags)
|
||||
{
|
||||
//info dictionary
|
||||
NSMutableDictionary* signingInfo = nil;
|
||||
|
||||
//offset of best architecture
|
||||
// for universal/fat binary, need to check correct arch
|
||||
uint32_t offset = 0;
|
||||
//status
|
||||
OSStatus status = !errSecSuccess;
|
||||
|
||||
//code
|
||||
//static code ref
|
||||
SecStaticCodeRef staticCode = NULL;
|
||||
|
||||
//status
|
||||
OSStatus status = -1;
|
||||
//dynamic code ref
|
||||
SecCodeRef dynamicCode = NULL;
|
||||
|
||||
//signing information
|
||||
//signing details
|
||||
CFDictionaryRef signingDetails = NULL;
|
||||
|
||||
//cert chain
|
||||
NSArray* certificateChain = nil;
|
||||
//signing authorities
|
||||
NSMutableArray* signingAuths = nil;
|
||||
|
||||
//index
|
||||
NSUInteger index = 0;
|
||||
|
||||
//cert
|
||||
SecCertificateRef certificate = NULL;
|
||||
|
||||
//common name on chert
|
||||
CFStringRef commonName = NULL;
|
||||
|
||||
//init signing status
|
||||
signingInfo = [NSMutableDictionary dictionary];
|
||||
|
||||
//sanity check
|
||||
//dynamic code checks
|
||||
// no path, dynamic check via pid
|
||||
if(nil == path)
|
||||
{
|
||||
//set err
|
||||
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 = SecStaticCodeCreateWithPathAndAttributes((__bridge CFURLRef)([NSURL fileURLWithPath:path]), kSecCSDefaultFlags, (__bridge CFDictionaryRef)@{(__bridge NSString *)kSecCodeAttributeUniversalFileOffset : [NSNumber numberWithUnsignedInt:offset]}, &staticCode);
|
||||
|
||||
//save signature status
|
||||
signingInfo[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status];
|
||||
if(noErr != status)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//check signature
|
||||
status = SecStaticCodeCheckValidity(staticCode, flags, NULL);
|
||||
|
||||
//(re)save signature status
|
||||
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])
|
||||
//generate dynamic code ref via pid
|
||||
if(errSecSuccess != SecCodeCopyGuestWithAttributes(NULL, (__bridge CFDictionaryRef _Nullable)(@{(__bridge NSString *)kSecGuestAttributePid : [NSNumber numberWithInt:pid]}), kSecCSDefaultFlags, &dynamicCode))
|
||||
{
|
||||
//from app store?
|
||||
signingInfo[KEY_SIGNING_IS_APP_STORE] = [NSNumber numberWithBool:fromAppStore(path)];
|
||||
}
|
||||
}
|
||||
|
||||
//init array for certificate names
|
||||
signingInfo[KEY_SIGNING_AUTHORITIES] = [NSMutableArray array];
|
||||
|
||||
//get cert chain
|
||||
certificateChain = [(__bridge NSDictionary*)signingDetails objectForKey:(__bridge NSString*)kSecCodeInfoCertificates];
|
||||
|
||||
//get name of all certs
|
||||
// add each to list
|
||||
for(index = 0; index < certificateChain.count; index++)
|
||||
{
|
||||
//reset
|
||||
commonName = NULL;
|
||||
|
||||
//extract cert
|
||||
certificate = (__bridge SecCertificateRef)([certificateChain objectAtIndex:index]);
|
||||
|
||||
//get common name
|
||||
status = SecCertificateCopyCommonName(certificate, &commonName);
|
||||
|
||||
//skip ones that error out
|
||||
if( (noErr != status) ||
|
||||
(NULL == commonName))
|
||||
{
|
||||
//release
|
||||
if(NULL != commonName)
|
||||
{
|
||||
//release
|
||||
CFRelease(commonName);
|
||||
}
|
||||
|
||||
//skip
|
||||
continue;
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//now, init signing status
|
||||
signingInfo = [NSMutableDictionary dictionary];
|
||||
|
||||
//validate code
|
||||
status = SecCodeCheckValidity(dynamicCode, flags, NULL);
|
||||
|
||||
//save result
|
||||
signingInfo[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status];
|
||||
|
||||
//sanity check
|
||||
// bail on error
|
||||
if(errSecSuccess != status)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//extract signing info
|
||||
status = SecCodeCopySigningInformation(dynamicCode, kSecCSSigningInformation, &signingDetails);
|
||||
|
||||
//save result
|
||||
signingInfo[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status];
|
||||
|
||||
//sanity check
|
||||
// bail on error
|
||||
if(errSecSuccess != status)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//determine signer
|
||||
// apple, app store, dev id, adhoc, etc...
|
||||
signingInfo[KEY_SIGNATURE_SIGNER] = extractSigner(dynamicCode, flags, YES);
|
||||
}
|
||||
|
||||
//static code checks
|
||||
else
|
||||
{
|
||||
//create static code ref via path
|
||||
if(errSecSuccess != SecStaticCodeCreateWithPath((__bridge CFURLRef)([NSURL fileURLWithPath:path]), kSecCSDefaultFlags, &staticCode))
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//now, init signing status
|
||||
signingInfo = [NSMutableDictionary dictionary];
|
||||
|
||||
//check signature
|
||||
status = SecStaticCodeCheckValidity(staticCode, flags, NULL);
|
||||
|
||||
//save result
|
||||
signingInfo[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status];
|
||||
|
||||
//sanity check
|
||||
// bail on error
|
||||
if(errSecSuccess != status)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//extract signing info
|
||||
status = SecCodeCopySigningInformation(staticCode, kSecCSSigningInformation, &signingDetails);
|
||||
|
||||
//save result
|
||||
signingInfo[KEY_SIGNATURE_STATUS] = [NSNumber numberWithInt:status];
|
||||
|
||||
//sanity check
|
||||
// bail on error
|
||||
if(errSecSuccess != status)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//determine signer
|
||||
// apple, app store, dev id, adhoc, etc...
|
||||
signingInfo[KEY_SIGNATURE_SIGNER] = extractSigner(staticCode, flags, NO);
|
||||
}
|
||||
|
||||
//extract code signing id
|
||||
if(nil != [(__bridge NSDictionary*)signingDetails objectForKey:(__bridge NSString*)kSecCodeInfoIdentifier])
|
||||
{
|
||||
//extract/save
|
||||
signingInfo[KEY_SIGNATURE_IDENTIFIER] = [(__bridge NSDictionary*)signingDetails objectForKey:(__bridge NSString*)kSecCodeInfoIdentifier];
|
||||
}
|
||||
|
||||
//extract entitlements
|
||||
if(nil != [(__bridge NSDictionary*)signingDetails objectForKey:(__bridge NSString*)kSecCodeInfoEntitlementsDict])
|
||||
{
|
||||
//extract/save
|
||||
signingInfo[KEY_SIGNATURE_ENTITLEMENTS] = [(__bridge NSDictionary*)signingDetails objectForKey:(__bridge NSString*)kSecCodeInfoEntitlementsDict];
|
||||
}
|
||||
|
||||
//extract signing authorities
|
||||
signingAuths = extractSigningAuths((__bridge NSDictionary *)(signingDetails));
|
||||
if(0 != signingAuths.count)
|
||||
{
|
||||
//save
|
||||
[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);
|
||||
signingInfo[KEY_SIGNATURE_AUTHORITIES] = signingAuths;
|
||||
}
|
||||
|
||||
bail:
|
||||
@@ -284,6 +165,16 @@ bail:
|
||||
signingDetails = NULL;
|
||||
}
|
||||
|
||||
//free dynamic code
|
||||
if(NULL != dynamicCode)
|
||||
{
|
||||
//free
|
||||
CFRelease(dynamicCode);
|
||||
|
||||
//unset
|
||||
dynamicCode = NULL;
|
||||
}
|
||||
|
||||
//free static code
|
||||
if(NULL != staticCode)
|
||||
{
|
||||
@@ -297,472 +188,147 @@ bail:
|
||||
return signingInfo;
|
||||
}
|
||||
|
||||
//determine if a file is signed by Apple proper
|
||||
BOOL isApple(NSString* path, SecCSFlags flags)
|
||||
//determine who signed item
|
||||
NSNumber* extractSigner(SecStaticCodeRef code, SecCSFlags flags, BOOL isDynamic)
|
||||
{
|
||||
//flag
|
||||
BOOL isApple = NO;
|
||||
//result
|
||||
NSNumber* signer = nil;
|
||||
|
||||
//code
|
||||
SecStaticCodeRef staticCode = NULL;
|
||||
//"anchor apple"
|
||||
static SecRequirementRef isApple = nil;
|
||||
|
||||
//signing reqs
|
||||
SecRequirementRef requirementRef = NULL;
|
||||
//"anchor apple generic"
|
||||
static SecRequirementRef isDevID = nil;
|
||||
|
||||
//status
|
||||
OSStatus status = -1;
|
||||
//"anchor apple generic and certificate leaf [subject.CN] = \"Apple Mac OS Application Signing\""
|
||||
static SecRequirementRef isAppStore = nil;
|
||||
|
||||
//create static code
|
||||
status = SecStaticCodeCreateWithPath((__bridge CFURLRef)([NSURL fileURLWithPath:path]), kSecCSDefaultFlags, &staticCode);
|
||||
if(noErr != status)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//create req string w/ 'anchor apple'
|
||||
// (3rd party: 'anchor apple generic')
|
||||
status = SecRequirementCreateWithString(CFSTR("anchor apple"), kSecCSDefaultFlags, &requirementRef);
|
||||
if( (noErr != status) ||
|
||||
(requirementRef == NULL) )
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//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, flags, requirementRef);
|
||||
if( (noErr != status) &&
|
||||
(errSecCSBadResource != status) )
|
||||
{
|
||||
//bail
|
||||
// just means isn't signed by apple
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//ok, happy (SecStaticCodeCheckValidity() didn't fail)
|
||||
// file is signed by Apple
|
||||
isApple = YES;
|
||||
|
||||
bail:
|
||||
|
||||
//free req reference
|
||||
if(NULL != requirementRef)
|
||||
{
|
||||
//free
|
||||
CFRelease(requirementRef);
|
||||
|
||||
//unset
|
||||
requirementRef = NULL;
|
||||
}
|
||||
|
||||
//free static code
|
||||
if(NULL != staticCode)
|
||||
{
|
||||
//free
|
||||
CFRelease(staticCode);
|
||||
|
||||
//unset
|
||||
staticCode = NULL;
|
||||
}
|
||||
|
||||
return isApple;
|
||||
}
|
||||
|
||||
//verify the receipt
|
||||
// check bundle ID, app version, and receipt's hash
|
||||
BOOL verifyReceipt(NSBundle* appBundle, AppReceipt* receipt)
|
||||
{
|
||||
//flag
|
||||
BOOL verified = NO;
|
||||
|
||||
//guid
|
||||
NSData* guid = nil;
|
||||
|
||||
//hash data
|
||||
NSMutableData *digestData = nil;
|
||||
|
||||
//hash buffer
|
||||
unsigned char digestBuffer[CC_SHA1_DIGEST_LENGTH] = {0};
|
||||
|
||||
//check guid
|
||||
guid = getGUID();
|
||||
if(nil == guid)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//create data obj
|
||||
digestData = [NSMutableData data];
|
||||
|
||||
//add guid to data obj
|
||||
[digestData appendData:guid];
|
||||
|
||||
//add receipt's 'opaque value' to data obj
|
||||
[digestData appendData:receipt.opaqueValue];
|
||||
|
||||
//add receipt's bundle id data to data obj
|
||||
[digestData appendData:receipt.bundleIdentifierData];
|
||||
|
||||
//CHECK 1:
|
||||
// app's bundle ID should match receipt's bundle ID
|
||||
if(YES != [receipt.bundleIdentifier isEqualToString:appBundle.bundleIdentifier])
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//CHECK 2:
|
||||
// app's version should match receipt's version
|
||||
if(YES != [receipt.appVersion isEqualToString:appBundle.infoDictionary[@"CFBundleShortVersionString"]])
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//CHECK 3:
|
||||
// verify receipt's hash (UUID)
|
||||
|
||||
//init SHA 1 hash
|
||||
CC_SHA1(digestData.bytes, (CC_LONG)digestData.length, digestBuffer);
|
||||
|
||||
//check for hash match
|
||||
if(0 != memcmp(digestBuffer, receipt.receiptHash.bytes, CC_SHA1_DIGEST_LENGTH))
|
||||
{
|
||||
//hash check failed
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//happy
|
||||
verified = YES;
|
||||
|
||||
bail:
|
||||
|
||||
return verified;
|
||||
}
|
||||
|
||||
//get GUID (really just computer's MAC address)
|
||||
// from Apple's 'Get the GUID in OS X' (see: 'Validating Receipts Locally')
|
||||
NSData* getGUID()
|
||||
{
|
||||
//status var
|
||||
__block kern_return_t kernResult = -1;
|
||||
|
||||
//master port
|
||||
__block mach_port_t masterPort = 0;
|
||||
|
||||
//matching dictionar
|
||||
__block CFMutableDictionaryRef matchingDict = NULL;
|
||||
|
||||
//iterator
|
||||
__block io_iterator_t iterator = 0;
|
||||
|
||||
//service
|
||||
__block io_object_t service = 0;
|
||||
|
||||
//registry property
|
||||
__block CFDataRef registryProperty = NULL;
|
||||
|
||||
//guid (MAC addr)
|
||||
static NSData* guid = nil;
|
||||
|
||||
//once token
|
||||
//token
|
||||
static dispatch_once_t onceToken = 0;
|
||||
|
||||
//only init guid once
|
||||
dispatch_once(&onceToken,
|
||||
^{
|
||||
|
||||
//get master port
|
||||
kernResult = IOMasterPort(MACH_PORT_NULL, &masterPort);
|
||||
if(KERN_SUCCESS != kernResult)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//get matching dictionary for 'en0'
|
||||
matchingDict = IOBSDNameMatching(masterPort, 0, "en0");
|
||||
if(NULL == matchingDict)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//get matching services
|
||||
kernResult = IOServiceGetMatchingServices(masterPort, matchingDict, &iterator);
|
||||
if(KERN_SUCCESS != kernResult)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//iterate over services, looking for 'IOMACAddress'
|
||||
while((service = IOIteratorNext(iterator)) != 0)
|
||||
{
|
||||
//parent
|
||||
io_object_t parentService = 0;
|
||||
|
||||
//get parent
|
||||
kernResult = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parentService);
|
||||
if(KERN_SUCCESS == kernResult)
|
||||
{
|
||||
//release prev
|
||||
if(NULL != registryProperty)
|
||||
{
|
||||
//release
|
||||
CFRelease(registryProperty);
|
||||
}
|
||||
|
||||
//get registry property for 'IOMACAddress'
|
||||
registryProperty = (CFDataRef) IORegistryEntryCreateCFProperty(parentService, CFSTR("IOMACAddress"), kCFAllocatorDefault, 0);
|
||||
|
||||
//release parent
|
||||
IOObjectRelease(parentService);
|
||||
}
|
||||
|
||||
//release service
|
||||
IOObjectRelease(service);
|
||||
}
|
||||
|
||||
//release iterator
|
||||
IOObjectRelease(iterator);
|
||||
|
||||
//convert guid to NSData*
|
||||
// also release registry property
|
||||
if(NULL != registryProperty)
|
||||
{
|
||||
//convert
|
||||
guid = [NSData dataWithData:(__bridge NSData *)registryProperty];
|
||||
|
||||
//release
|
||||
CFRelease(registryProperty);
|
||||
}
|
||||
bail:
|
||||
;
|
||||
|
||||
});//only once
|
||||
|
||||
return guid;
|
||||
}
|
||||
|
||||
//determine if file is signed with Apple Dev ID/cert
|
||||
BOOL isSignedDevID(NSString* path, SecCSFlags flags)
|
||||
{
|
||||
//flag
|
||||
BOOL signedOK = NO;
|
||||
|
||||
//code
|
||||
SecStaticCodeRef staticCode = NULL;
|
||||
|
||||
//signing reqs
|
||||
SecRequirementRef requirementRef = NULL;
|
||||
|
||||
//status
|
||||
OSStatus status = -1;
|
||||
|
||||
//create static code
|
||||
status = SecStaticCodeCreateWithPath((__bridge CFURLRef)([NSURL fileURLWithPath:path]), kSecCSDefaultFlags, &staticCode);
|
||||
if(noErr != status)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//create req string w/ 'anchor apple generic'
|
||||
status = SecRequirementCreateWithString(CFSTR("anchor apple generic"), kSecCSDefaultFlags, &requirementRef);
|
||||
if( (noErr != status) ||
|
||||
(requirementRef == NULL) )
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//check if file is signed w/ apple dev id by checking if it conforms to req string
|
||||
status = SecStaticCodeCheckValidity(staticCode, flags, requirementRef);
|
||||
if(noErr != status)
|
||||
{
|
||||
//bail
|
||||
// just means app isn't signed by apple dev id
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//ok, happy
|
||||
// file is signed by Apple Dev ID
|
||||
signedOK = YES;
|
||||
|
||||
bail:
|
||||
|
||||
//free req reference
|
||||
if(NULL != requirementRef)
|
||||
{
|
||||
//free
|
||||
CFRelease(requirementRef);
|
||||
//only once
|
||||
// init requirements
|
||||
dispatch_once(&onceToken, ^{
|
||||
|
||||
//unset
|
||||
requirementRef = NULL;
|
||||
}
|
||||
|
||||
//free static code
|
||||
if(NULL != staticCode)
|
||||
{
|
||||
//free
|
||||
CFRelease(staticCode);
|
||||
//init apple signing requirement
|
||||
SecRequirementCreateWithString(CFSTR("anchor apple"), kSecCSDefaultFlags, &isApple);
|
||||
|
||||
//unset
|
||||
staticCode = NULL;
|
||||
}
|
||||
//init dev id signing requirement
|
||||
SecRequirementCreateWithString(CFSTR("anchor apple generic"), kSecCSDefaultFlags, &isDevID);
|
||||
|
||||
//init app store signing requirement
|
||||
SecRequirementCreateWithString(CFSTR("anchor apple generic and certificate leaf [subject.CN] = \"Apple Mac OS Application Signing\""), kSecCSDefaultFlags, &isAppStore);
|
||||
});
|
||||
|
||||
return signedOK;
|
||||
}
|
||||
|
||||
//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!
|
||||
BOOL fromAppStore(NSString* path)
|
||||
{
|
||||
//flag
|
||||
BOOL appStoreApp = NO;
|
||||
|
||||
//app receipt
|
||||
AppReceipt* appReceipt = nil;
|
||||
|
||||
//path to app bundle
|
||||
// just have binary
|
||||
NSBundle* appBundle = nil;
|
||||
|
||||
//if it's an app
|
||||
// can directly load app bundle
|
||||
appBundle = [NSBundle bundleWithPath:path];
|
||||
if(nil == appBundle)
|
||||
//check 1: "is apple" (proper)
|
||||
if(errSecSuccess == validateRequirement(code, isApple, flags, isDynamic))
|
||||
{
|
||||
//find app bundle from binary
|
||||
// likely not an application if this fails
|
||||
appBundle = PI_findAppBundle(path);
|
||||
if(nil == appBundle)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
//set signer to apple
|
||||
signer = [NSNumber numberWithInt:Apple];
|
||||
}
|
||||
|
||||
//bail if it doesn't have an receipt
|
||||
// done here, since checking signature is expensive!
|
||||
if( (nil == appBundle.appStoreReceiptURL) ||
|
||||
(YES != [[NSFileManager defaultManager] fileExistsAtPath:appBundle.appStoreReceiptURL.path]) )
|
||||
//check 2: "is app store"
|
||||
// note: this is more specific than dev id, so do it first
|
||||
else if(errSecSuccess == validateRequirement(code, isAppStore, flags, isDynamic))
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
//set signer to app store
|
||||
signer = [NSNumber numberWithInt:AppStore];
|
||||
}
|
||||
|
||||
//init
|
||||
// will parse/decode, etc
|
||||
appReceipt = [[AppReceipt alloc] init:appBundle];
|
||||
if(nil == appReceipt)
|
||||
//check 3: "is dev id"
|
||||
else if(errSecSuccess == validateRequirement(code, isDevID, flags, isDynamic))
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
//set signer to dev id
|
||||
signer = [NSNumber numberWithInt:DevID];
|
||||
}
|
||||
|
||||
//verify
|
||||
if(YES != verifyReceipt(appBundle, appReceipt))
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//happy
|
||||
// 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
|
||||
//otherwise
|
||||
// has to be adhoc?
|
||||
else
|
||||
{
|
||||
//convert to string
|
||||
entitlementsXML = [[NSString alloc] initWithData:results[STDOUT] encoding:NSUTF8StringEncoding];
|
||||
//set signer to ad hoc
|
||||
signer = [NSNumber numberWithInt:AdHoc];
|
||||
}
|
||||
|
||||
//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;
|
||||
return signer;
|
||||
}
|
||||
|
||||
//validate a requirement
|
||||
OSStatus validateRequirement(SecStaticCodeRef code, SecRequirementRef requirement, SecCSFlags flags, BOOL isDynamic)
|
||||
{
|
||||
//result
|
||||
OSStatus result = -1;
|
||||
|
||||
//dynamic check?
|
||||
if(YES == isDynamic)
|
||||
{
|
||||
//validate dynamically
|
||||
result = SecCodeCheckValidity((SecCodeRef)code, flags, requirement);
|
||||
}
|
||||
//static check
|
||||
else
|
||||
{
|
||||
//validate statically
|
||||
result = SecStaticCodeCheckValidity(code, flags, requirement);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//extract (names) of signing auths
|
||||
NSMutableArray* extractSigningAuths(NSDictionary* signingDetails)
|
||||
{
|
||||
//signing auths
|
||||
NSMutableArray* authorities = nil;
|
||||
|
||||
//cert chain
|
||||
NSArray* certificateChain = nil;
|
||||
|
||||
//index
|
||||
NSUInteger index = 0;
|
||||
|
||||
//cert
|
||||
SecCertificateRef certificate = NULL;
|
||||
|
||||
//common name on chert
|
||||
CFStringRef commonName = NULL;
|
||||
|
||||
//init array for certificate names
|
||||
authorities = [NSMutableArray array];
|
||||
|
||||
//get cert chain
|
||||
certificateChain = [signingDetails objectForKey:(__bridge NSString*)kSecCodeInfoCertificates];
|
||||
|
||||
//get name of all certs
|
||||
// add each to list
|
||||
for(index = 0; index < certificateChain.count; index++)
|
||||
{
|
||||
//reset
|
||||
commonName = NULL;
|
||||
|
||||
//extract cert
|
||||
certificate = (__bridge SecCertificateRef)([certificateChain objectAtIndex:index]);
|
||||
|
||||
//get common name
|
||||
if(errSecSuccess != SecCertificateCopyCommonName(certificate, &commonName))
|
||||
{
|
||||
//release
|
||||
if(NULL != commonName)
|
||||
{
|
||||
//release
|
||||
CFRelease(commonName);
|
||||
}
|
||||
|
||||
//next
|
||||
continue;
|
||||
}
|
||||
|
||||
//save
|
||||
[authorities addObject:(__bridge NSString*)commonName];
|
||||
|
||||
//release name
|
||||
CFRelease(commonName);
|
||||
}
|
||||
|
||||
return authorities;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
NSBundle* PI_findAppBundle(NSString* binaryPath);
|
||||
|
||||
//check if current OS version is supported
|
||||
// for now, just...?
|
||||
BOOL PI_isSupportedOS(void);
|
||||
|
||||
//get OS version
|
||||
@@ -30,14 +29,8 @@ NSMutableArray* PI_enumerateProcesses(void);
|
||||
// find its executable
|
||||
NSString* PI_findAppBinary(NSString* appPath);
|
||||
|
||||
//sha256 a file
|
||||
NSString* PI_hashFile(NSString* filePath);
|
||||
|
||||
//given a 'short' path or process name
|
||||
// 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
|
||||
|
||||
+8
-199
@@ -9,7 +9,6 @@
|
||||
|
||||
#import "Consts.h"
|
||||
#import "Utilities.h"
|
||||
#import "AppReceipt.h"
|
||||
|
||||
#import <libproc.h>
|
||||
#import <sys/sysctl.h>
|
||||
@@ -123,9 +122,6 @@ bail:
|
||||
//enumerate all running processes
|
||||
NSMutableArray* PI_enumerateProcesses()
|
||||
{
|
||||
//status
|
||||
int status = -1;
|
||||
|
||||
//# of procs
|
||||
int numberOfProcesses = 0;
|
||||
|
||||
@@ -139,7 +135,12 @@ NSMutableArray* PI_enumerateProcesses()
|
||||
processes = [NSMutableArray array];
|
||||
|
||||
//get # of procs
|
||||
numberOfProcesses = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0);
|
||||
numberOfProcesses = proc_listallpids(NULL, 0);
|
||||
if(-1 == numberOfProcesses)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//alloc buffer for pids
|
||||
pids = calloc(numberOfProcesses, sizeof(pid_t));
|
||||
@@ -150,8 +151,8 @@ NSMutableArray* PI_enumerateProcesses()
|
||||
}
|
||||
|
||||
//get list of pids
|
||||
status = proc_listpids(PROC_ALL_PIDS, 0, pids, numberOfProcesses * sizeof(pid_t));
|
||||
if(status < 0)
|
||||
numberOfProcesses = proc_listallpids(pids, numberOfProcesses*sizeof(pid_t));
|
||||
if(-1 == numberOfProcesses)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
@@ -229,47 +230,6 @@ NSBundle* PI_findAppBundle(NSString* binaryPath)
|
||||
return appBundle;
|
||||
}
|
||||
|
||||
//hash a file
|
||||
// algorithm: sha256
|
||||
NSString* PI_hashFile(NSString* filePath)
|
||||
{
|
||||
//file's contents
|
||||
NSData* fileContents = nil;
|
||||
|
||||
//hash digest
|
||||
uint8_t digestSHA256[CC_SHA256_DIGEST_LENGTH] = {0};
|
||||
|
||||
//hash as string
|
||||
NSMutableString* sha256 = nil;
|
||||
|
||||
//index var
|
||||
NSUInteger index = 0;
|
||||
|
||||
//init
|
||||
sha256 = [NSMutableString string];
|
||||
|
||||
//load file
|
||||
if(nil == (fileContents = [NSData dataWithContentsOfFile:filePath]))
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//sha1 it
|
||||
CC_SHA256(fileContents.bytes, (unsigned int)fileContents.length, digestSHA256);
|
||||
|
||||
//convert to NSString
|
||||
// iterate over each bytes in computed digest and format
|
||||
for(index=0; index < CC_SHA256_DIGEST_LENGTH; index++)
|
||||
{
|
||||
//format/append
|
||||
[sha256 appendFormat:@"%02lX", (unsigned long)digestSHA256[index]];
|
||||
}
|
||||
|
||||
bail:
|
||||
|
||||
return sha256;
|
||||
}
|
||||
|
||||
//given a 'short' path or process name
|
||||
// try find the full path by scanning $PATH
|
||||
@@ -319,154 +279,3 @@ 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;
|
||||
}
|
||||
|
||||
+27
-31
@@ -19,7 +19,6 @@
|
||||
@class Binary;
|
||||
@class Process;
|
||||
|
||||
|
||||
/* DEFINES */
|
||||
|
||||
//from audit_kevents.h
|
||||
@@ -29,30 +28,29 @@
|
||||
#define EVENT_EXEC 27
|
||||
#define EVENT_SPAWN 43190
|
||||
|
||||
//signers
|
||||
enum Signer{None, Apple, AppStore, DevID, AdHoc};
|
||||
|
||||
//signature status
|
||||
#define KEY_SIGNATURE_STATUS @"signatureStatus"
|
||||
|
||||
//signer
|
||||
#define KEY_SIGNATURE_SIGNER @"signatureSigner"
|
||||
|
||||
//signing auths
|
||||
#define KEY_SIGNING_AUTHORITIES @"signingAuthorities"
|
||||
#define KEY_SIGNATURE_AUTHORITIES @"signatureAuthorities"
|
||||
|
||||
//code signing id
|
||||
#define KEY_SIGNATURE_IDENTIFIER @"signingIdentifier"
|
||||
#define KEY_SIGNATURE_IDENTIFIER @"signatureIdentifier"
|
||||
|
||||
//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"
|
||||
//entitlements
|
||||
#define KEY_SIGNATURE_ENTITLEMENTS @"signatureEntitlements"
|
||||
|
||||
/* TYPEDEFS */
|
||||
|
||||
//block for library
|
||||
typedef void (^ProcessCallbackBlock)(Process* _Nonnull);
|
||||
|
||||
|
||||
/* OBJECT: PROCESS INFO */
|
||||
|
||||
@interface ProcInfo : NSObject
|
||||
@@ -103,6 +101,9 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull);
|
||||
//ancestors
|
||||
@property(nonatomic, retain)NSMutableArray* _Nonnull ancestors;
|
||||
|
||||
//signing info
|
||||
@property(nonatomic, retain)NSMutableDictionary* _Nonnull signingInfo;
|
||||
|
||||
//Binary object
|
||||
// has path, hash, etc
|
||||
@property(nonatomic, retain)Binary* _Nonnull binary;
|
||||
@@ -116,13 +117,18 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull);
|
||||
// method will then (try) fill out rest of object
|
||||
-(id _Nullable)init:(pid_t)processID;
|
||||
|
||||
//generate signing info
|
||||
// also classifies if Apple/from App Store/etc.
|
||||
-(void)generateSigningInfo:(SecCSFlags)flags;
|
||||
|
||||
//set process's path
|
||||
-(void)pathFromPid;
|
||||
|
||||
//generate list of ancestors
|
||||
-(void)enumerateAncestors;
|
||||
|
||||
//class method to get parent of arbitrary process
|
||||
//class method
|
||||
// get's parent of arbitrary process
|
||||
+(pid_t)getParentID:(pid_t)child;
|
||||
|
||||
@end
|
||||
@@ -158,22 +164,13 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull);
|
||||
//signing info
|
||||
@property(nonatomic, retain)NSDictionary* _Nonnull signingInfo;
|
||||
|
||||
//entitlements
|
||||
@property(nonatomic, retain)NSDictionary* _Nonnull entitlements;
|
||||
|
||||
//hash
|
||||
@property(nonatomic, retain)NSString* _Nonnull sha256;
|
||||
@property(nonatomic, retain)NSMutableString* _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;
|
||||
|
||||
//flag indicating binary is from official App Store
|
||||
@property BOOL isAppStore;
|
||||
|
||||
/* METHODS */
|
||||
|
||||
//init w/ a path
|
||||
@@ -187,20 +184,19 @@ typedef void (^ProcessCallbackBlock)(Process* _Nonnull);
|
||||
// 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:(SecCSFlags)flags entitlements:(BOOL)entitlements;
|
||||
//generate signing info (statically)
|
||||
-(void)generateSigningInfo:(SecCSFlags)flags;
|
||||
|
||||
//generate entitlements
|
||||
// note: can also call 'generateSigningInfo' w/ 'entitlements:YES'
|
||||
-(void)generateEntitlements;
|
||||
/* the following methods are not invoked automatically
|
||||
as such, if you code has to manually invoke them if you want this info
|
||||
*/
|
||||
|
||||
//generate hash
|
||||
// algo: sha256
|
||||
-(void)generateHash;
|
||||
|
||||
//generate id
|
||||
// eithersigning id, or sha256 hash
|
||||
// note: will generate signing info if needed
|
||||
// either signing id, or sha256 hash
|
||||
-(void)generateIdentifier;
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user