Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 19ba380052 | |||
| efe7496191 | |||
| 7c2adefa80 | |||
| 553e530104 | |||
| e35933845a | |||
| b66337a74d | |||
| 25f3522b92 | |||
| 5294417c67 | |||
| 0b4d083b48 | |||
| 42b22a7a67 | |||
| b910b30783 | |||
| d608c1d5f8 |
+3
-1
@@ -1,3 +1,5 @@
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
*.xcworkspace
|
||||
|
||||
.DS_Store
|
||||
libDumpBTM.a
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "group:Library/library.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:tool/tool.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
+4
-1
@@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -2,7 +2,11 @@
|
||||
tl;dr: an open-source version of `% sfltool dumpbtm`
|
||||
|
||||
```
|
||||
% ./dumpBTM
|
||||
% ./dumpBTM
|
||||
Dumps (unserializes) BackgroundItems-v*.btm
|
||||
|
||||
Opened /private/var/db/com.apple.backgroundtaskmanagement/BackgroundItems-v7.btm
|
||||
...
|
||||
|
||||
========================
|
||||
Records for UID 501 : 1CAA5D2B-A526-49E2-9A6F-58CACBDF0AFB
|
||||
@@ -36,21 +40,26 @@ tl;dr: an open-source version of `% sfltool dumpbtm`
|
||||
Parent Identifier: Adobe Creative Cloud
|
||||
```
|
||||
|
||||
In macOS Ventura (13), Apple consolidated persistent items (login items, launch agents/daemons) in a new file: `BackgroundItems-v4.btm` (found in `/private/var/db/com.apple.backgroundtaskmanagement/`).
|
||||
Note: If you're running the pre-built binary, though signed, it's not notarized (Apple doesn't support notarized commandline tools). So after making it executable, remove the quarantine attributue to make it runnable (via Terminal).
|
||||
|
||||
```
|
||||
% chmod +x dumpBTM
|
||||
% xattr -rc dumpBTM
|
||||
```
|
||||
|
||||
Also, make sure you give Terminal "Full Disk Access" (a requirment to read the `BackgroundItems-v4.btm` file).
|
||||
|
||||
In macOS Ventura (13), Apple consolidated persistent items (login items, launch agents/daemons) in a new file: `BackgroundItems-v*.btm`, found in `/private/var/db/com.apple.backgroundtaskmanagement/`. On macOS 13.0 this file is named `BackgroundItems-v*.btm` whereas on macOS 13.1 it's `BackgroundItems-v7.btm`.
|
||||
|
||||
This file is a serialized binary propertly list. You can dump it via Apple's `sfltool`, specifying the `dumpbtm` command line flag.
|
||||
|
||||
`DumpBTM` is an open-source version of this, which has the following benefits:
|
||||
|
||||
* open-source
|
||||
* programmatic access to enumerate items in the file
|
||||
* Open-source
|
||||
* Programmatic access to enumerate (persistent) items in the file
|
||||
|
||||
The latter point is most notable as this allow you to now add such logic into security/EDR tools. Spefically you can now easily and programmatically enumerate all (ok most) persistent items on a macOS Ventura system (which will include any persistently installed malware).
|
||||
The latter point is most notable as this allow you to now add such logic into security/EDR tools. Specifically you can now easily and programmatically enumerate all (ok most) persistent items on a macOS Ventura system (which will include any persistently installed malware).
|
||||
|
||||
You can also then monitor this file for changes to detect new persistence events (as now you can parse/unserialize its contents via this project's code).
|
||||
|
||||
Note: Such monitoring was supposed to be accomplished via the Endpoint Security `ES_EVENT_TYPE_NOTIFY_BTM_LAUNCH_ITEM_ADD` event ...but this event is broken (See: "[Endpoint Security Event: ES_EVENT_TYPE_NOTIFY_BTM_LAUNCH_ITEM_ADD is ...broken?](https://developer.apple.com/forums/thread/720468)" 😓
|
||||
|
||||
|
||||
|
||||
|
||||
Note: Such monitoring was supposed to be accomplished via the Endpoint Security `ES_EVENT_TYPE_NOTIFY_BTM_LAUNCH_ITEM_ADD` event ...but this event is broken (See: "[Endpoint Security Event: ES_EVENT_TYPE_NOTIFY_BTM_LAUNCH_ITEM_ADD is ...broken?](https://developer.apple.com/forums/thread/720468)" 😓).
|
||||
|
||||
-460
@@ -1,460 +0,0 @@
|
||||
//
|
||||
// main.m
|
||||
// dumpBTM
|
||||
//
|
||||
// Created by Patrick Wardle on 1/1/23.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <OpenDirectory/OpenDirectory.h>
|
||||
#import <DirectoryService/DirectoryService.h>
|
||||
|
||||
#define DB_PATH @"/private/var/db/com.apple.backgroundtaskmanagement/BackgroundItems-v4.btm"
|
||||
|
||||
//helper function(s)
|
||||
uid_t uidFromUUID(NSString* uuid);
|
||||
|
||||
//Storage obj
|
||||
@interface Storage : NSObject <NSSecureCoding>
|
||||
|
||||
@property(nonatomic, retain)NSDictionary* items;
|
||||
|
||||
@end
|
||||
|
||||
//item record obj
|
||||
@interface ItemRecord : NSObject <NSSecureCoding>
|
||||
|
||||
@property(nonatomic, retain)NSUUID* uuid;
|
||||
@property(nonatomic, retain)NSString* identifier;
|
||||
@property NSInteger generation;
|
||||
@property(nonatomic, retain)NSURL* url;
|
||||
@property(nonatomic, retain)NSString* executablePath;
|
||||
@property(nonatomic, retain)NSString* name;
|
||||
@property(nonatomic, retain)NSString* developerName;
|
||||
@property(nonatomic, retain)NSString* teamIdentifier;
|
||||
@property(nonatomic, retain)NSData* lightweightRequirement;
|
||||
@property NSInteger type;
|
||||
@property NSInteger disposition;
|
||||
@property(nonatomic, retain)NSData* bookmark;
|
||||
@property(nonatomic, retain)NSString* bundleIdentifier;
|
||||
@property(nonatomic, retain)NSArray* associatedBundleIdentifiers;
|
||||
@property(nonatomic, retain)NSString* container;
|
||||
@property(nonatomic, retain)NSMutableSet* items;
|
||||
|
||||
-(NSString*)dumpVerboseDescription;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
//main
|
||||
// just dump DB to stdout
|
||||
// TODO: add option to print as JSON
|
||||
int main(int argc, const char * argv[]) {
|
||||
|
||||
//status
|
||||
int status = -1;
|
||||
|
||||
@autoreleasepool {
|
||||
|
||||
//error
|
||||
NSError* error = nil;
|
||||
|
||||
//unarchiver
|
||||
NSKeyedUnarchiver* keyedUnarchiver = nil;
|
||||
|
||||
//database (raw/unserialized) data
|
||||
NSData* dbData = nil;
|
||||
|
||||
//database dictionary
|
||||
NSMutableDictionary* dbDictionary = nil;
|
||||
|
||||
//database storage obj
|
||||
Storage* storage = nil;
|
||||
|
||||
//init
|
||||
dbDictionary = [NSMutableDictionary dictionary];
|
||||
|
||||
//dbg msg
|
||||
printf("%s v0.95\nDumps (unserializes) BackgroundItems-v4.btm\n", [[NSProcessInfo.processInfo.arguments.firstObject lastPathComponent] UTF8String]);
|
||||
|
||||
//load database
|
||||
// note: this will fail if you don't have full disk access
|
||||
dbData = [NSData dataWithContentsOfURL:[[NSURL alloc] initFileURLWithPath:DB_PATH] options:0 error:&error];
|
||||
if(nil == dbData)
|
||||
{
|
||||
//error msg
|
||||
printf("ERROR: failed to load %s\n\nDo you have Full Disk Access?\n\n", DB_PATH.UTF8String);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//dbg msg
|
||||
printf("Opened %s\n\n", DB_PATH.UTF8String);
|
||||
|
||||
//init unachiver
|
||||
keyedUnarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:dbData error:&error];
|
||||
|
||||
//set this (for debugging)
|
||||
keyedUnarchiver.decodingFailurePolicy = NSDecodingFailurePolicyRaiseException;
|
||||
|
||||
//get version
|
||||
dbDictionary[@"version"] = [NSNumber numberWithInteger:[keyedUnarchiver decodeIntegerForKey:@"version"]];
|
||||
|
||||
//get store
|
||||
dbDictionary[@"store"] = [keyedUnarchiver decodeObjectOfClass:[Storage class] forKey:@"store"];
|
||||
|
||||
//grab storage obj
|
||||
storage = dbDictionary[@"store"];
|
||||
|
||||
//process each/all items
|
||||
for(NSString* key in storage.items)
|
||||
{
|
||||
//uid
|
||||
uid_t uid = 0;
|
||||
|
||||
//items
|
||||
NSArray* items = nil;
|
||||
|
||||
//item number
|
||||
int itemNumber = 0;
|
||||
|
||||
//get uid from uuid
|
||||
uid = uidFromUUID(key);
|
||||
|
||||
//header (w/ uid/uuid)
|
||||
printf("========================\n Records for UID %d : %s\n========================\n\n", uid, key.description.UTF8String);
|
||||
|
||||
printf(" Items:\n\n");
|
||||
|
||||
//process each item
|
||||
items = storage.items[key];
|
||||
for(ItemRecord* item in items)
|
||||
{
|
||||
//display number and item details.
|
||||
printf(" #%d\n", ++itemNumber);
|
||||
printf(" %s\n", [[item dumpVerboseDescription] UTF8String]);
|
||||
}
|
||||
}
|
||||
|
||||
//happy
|
||||
status = 0;
|
||||
}
|
||||
|
||||
bail:
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@implementation Storage
|
||||
|
||||
@synthesize items;
|
||||
|
||||
+(BOOL)supportsSecureCoding
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
//decode object
|
||||
-(id)initWithCoder:(NSCoder *)decoder
|
||||
{
|
||||
if(self = [super init])
|
||||
{
|
||||
//supported classes
|
||||
NSArray* itemClasses = @[[NSMutableDictionary class], [NSString class], [NSArray class], [ItemRecord class]];
|
||||
|
||||
//decode items
|
||||
// 'itemsByUserIdentifier'
|
||||
self.items = [decoder decodeObjectOfClasses:[NSSet setWithArray:itemClasses] forKey:@"itemsByUserIdentifier"];
|
||||
|
||||
}
|
||||
|
||||
//TODO:
|
||||
// "userSettingsByUserIdentifier" & "mdmPaloadsByIdentifier"
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)encodeWithCoder:(nonnull NSCoder *)coder {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ItemRecord
|
||||
|
||||
+(BOOL)supportsSecureCoding
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
//decode object
|
||||
-(id)initWithCoder:(NSCoder *)decoder
|
||||
{
|
||||
self = [super init];
|
||||
if(nil != self)
|
||||
{
|
||||
self.uuid = [decoder decodeObjectOfClass:[NSUUID class] forKey:@"uuid"];
|
||||
self.identifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"identifier"];
|
||||
self.generation = [decoder decodeIntegerForKey:@"generation"];
|
||||
self.url = [decoder decodeObjectOfClass:[NSURL class] forKey:@"url"];
|
||||
self.executablePath = [decoder decodeObjectOfClass:[NSString class] forKey:@"executablePath"];
|
||||
self.name = [decoder decodeObjectOfClass:[NSString class] forKey:@"name"];
|
||||
self.developerName = [decoder decodeObjectOfClass:[NSString class] forKey:@"developerName"];
|
||||
self.teamIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"teamIdentifier"];
|
||||
self.lightweightRequirement = [decoder decodeObjectOfClass:[NSData class] forKey:@"lightweightRequirement"];
|
||||
self.type = [decoder decodeIntegerForKey:@"type"];
|
||||
self.disposition = [decoder decodeIntegerForKey:@"disposition"];
|
||||
self.bookmark = [decoder decodeObjectOfClass:[NSData class] forKey:@"bookmark"];
|
||||
self.bundleIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"bundleIdentifier"];
|
||||
self.associatedBundleIdentifiers = [decoder decodeObjectOfClass:[NSArray class] forKey:@"associatedBundleIdentifiers"];
|
||||
self.container = [decoder decodeObjectOfClass:[NSString class] forKey:@"container"];
|
||||
|
||||
NSArray* itemsArray = [decoder decodeArrayOfObjectsOfClass:[NSString class] forKey:@"items"];
|
||||
if(nil != itemsArray)
|
||||
{
|
||||
self.items = [NSMutableSet setWithArray:itemsArray];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.items = [NSMutableSet set];
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)encodeWithCoder:(nonnull NSCoder *)coder {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//convert item record's type to string
|
||||
// TODO: handle all types (e.g. managaged?)
|
||||
-(NSString *)typeDetails
|
||||
{
|
||||
//details
|
||||
NSMutableString* details = nil;
|
||||
|
||||
//init
|
||||
details = [NSMutableString string];
|
||||
|
||||
//curated
|
||||
if(self.type & 0x80000)
|
||||
{
|
||||
[details appendString:@"curated "];
|
||||
}
|
||||
|
||||
//legacy
|
||||
if(self.type & 0x10000)
|
||||
{
|
||||
[details appendString:@"legacy "];
|
||||
}
|
||||
|
||||
//developer
|
||||
if(self.type & 0x20)
|
||||
{
|
||||
[details appendString:@"developer "];
|
||||
}
|
||||
|
||||
//daaemon
|
||||
if(self.type & 0x10)
|
||||
{
|
||||
[details appendString:@"daemon "];
|
||||
}
|
||||
|
||||
//agent
|
||||
if(self.type & 0x8)
|
||||
{
|
||||
[details appendString:@"agent "];
|
||||
}
|
||||
|
||||
//login item
|
||||
if(self.type & 0x4)
|
||||
{
|
||||
[details appendString:@"login item "];
|
||||
}
|
||||
|
||||
//app
|
||||
if(self.type & 0x2)
|
||||
{
|
||||
[details appendString:@"app "];
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
//convert item record's disposition to string
|
||||
-(NSString *)dispositionDetails
|
||||
{
|
||||
//details
|
||||
NSMutableString* details = nil;
|
||||
details = [NSMutableString string];
|
||||
|
||||
//enabled
|
||||
if(self.disposition & 0x1)
|
||||
{
|
||||
[details appendString:@"enabled "];
|
||||
}
|
||||
//disabled
|
||||
else
|
||||
{
|
||||
[details appendString:@"disabled "];
|
||||
}
|
||||
//allowed
|
||||
if(self.disposition & 0x2)
|
||||
{
|
||||
[details appendString:@"allowed "];
|
||||
}
|
||||
//disallowed
|
||||
else
|
||||
{
|
||||
[details appendString:@"disallowed "];
|
||||
}
|
||||
|
||||
//hidden
|
||||
if(self.disposition & 0x4)
|
||||
{
|
||||
[details appendString:@"hidden "];
|
||||
}
|
||||
//visibile
|
||||
else
|
||||
{
|
||||
[details appendString:@"visible "];
|
||||
}
|
||||
|
||||
//notified
|
||||
if(self.disposition & 0x8)
|
||||
{
|
||||
[details appendString:@"notified"];
|
||||
}
|
||||
//not notified
|
||||
else
|
||||
{
|
||||
[details appendString:@"not notified"];
|
||||
}
|
||||
return details;
|
||||
}
|
||||
|
||||
//dump
|
||||
-(NSString *)dumpVerboseDescription {
|
||||
|
||||
//desc
|
||||
NSMutableString* description = nil;
|
||||
|
||||
//init
|
||||
description = [NSMutableString string];
|
||||
|
||||
//build description
|
||||
[description appendFormat:@" UUID: %@\r\n", self.uuid];
|
||||
[description appendFormat:@" Name: %@\r\n", self.name];
|
||||
[description appendFormat:@" Developer Name: %@\r\n", self.developerName];
|
||||
|
||||
//team id
|
||||
if(nil != self.teamIdentifier)
|
||||
{
|
||||
[description appendFormat:@" Team Identifier: %@\r\n", self.teamIdentifier];
|
||||
}
|
||||
|
||||
//type
|
||||
[description appendFormat:@" Type: %@ (%#lx)\n", [self typeDetails], (long)self.type];
|
||||
|
||||
//disposition
|
||||
[description appendFormat:@" Disposition: [%@] (%ld)\n", [self dispositionDetails], (long)self.disposition];
|
||||
|
||||
//id
|
||||
[description appendFormat:@" Indentifier: %@\r\n", self.identifier];
|
||||
|
||||
//url
|
||||
[description appendFormat:@" URL: %@\n", self.url];
|
||||
|
||||
//path
|
||||
[description appendFormat:@" Executable Path: %@\n", self.executablePath];
|
||||
|
||||
//generation
|
||||
[description appendFormat:@" Generation: %ld\n", self.generation];
|
||||
|
||||
//associated bundle ids
|
||||
if(0 != self.associatedBundleIdentifiers.count)
|
||||
{
|
||||
[description appendFormat:@" Assoc. Bundle IDs: "];
|
||||
[description appendString:@"["];
|
||||
|
||||
for(NSString* associatedBundleIdentifier in self.associatedBundleIdentifiers)
|
||||
{
|
||||
[description appendFormat:@"%@ ", associatedBundleIdentifier];
|
||||
}
|
||||
|
||||
//remove last " "
|
||||
if(YES == [[description substringFromIndex:[description length] - 1] isEqualToString:@" "])
|
||||
{
|
||||
description = [[description substringToIndex:[description length]-1] mutableCopy];
|
||||
}
|
||||
|
||||
[description appendString:@"]\n"];
|
||||
}
|
||||
|
||||
//parent id
|
||||
[description appendFormat:@" Parent Identifier: %@\n", self.container];
|
||||
|
||||
//items
|
||||
if(0 != self.items.count)
|
||||
{
|
||||
int count = 0;
|
||||
[description appendFormat:@" Embedded Item Identifiers:"];
|
||||
for(NSString* item in self.items.allObjects)
|
||||
{
|
||||
[description appendFormat:@"\n #%d: %@", ++count, item];
|
||||
}
|
||||
[description appendString:@"\n"];
|
||||
}
|
||||
|
||||
return description;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
//helper function
|
||||
//grab a UID from a UUID
|
||||
uid_t uidFromUUID(NSString* uuid)
|
||||
{
|
||||
//uid
|
||||
uid_t uid = 0;
|
||||
|
||||
//node
|
||||
ODNode* node = nil;
|
||||
|
||||
//query
|
||||
ODQuery* query = nil;
|
||||
|
||||
//return
|
||||
NSArray* result = nil;
|
||||
|
||||
//record
|
||||
ODRecord* record = nil;
|
||||
|
||||
//attribute (@kDS1AttrUniqueID)
|
||||
NSArray* attribute = nil;
|
||||
|
||||
//init node
|
||||
node = [ODNode nodeWithSession:ODSession.defaultSession type:0x2200 error:nil];
|
||||
|
||||
//perform query
|
||||
query = [ODQuery queryWithNode:node forRecordTypes:kODRecordTypeUsers attribute:kODAttributeTypeGUID matchType:0x2001 queryValues:uuid returnAttributes:nil maximumResults:1 error:nil];
|
||||
|
||||
//result
|
||||
result = [query resultsAllowingPartial:0x0 error:nil];
|
||||
|
||||
//grab first result
|
||||
record = result.firstObject;
|
||||
|
||||
//grab '@kDS1AttrUniqueID'
|
||||
attribute = [record valuesForAttribute:@kDS1AttrUniqueID error:nil];
|
||||
|
||||
//uid is first obj, as int
|
||||
uid = [attribute.firstObject intValue];
|
||||
|
||||
return uid;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// dumpBTM.h
|
||||
// library
|
||||
//
|
||||
// Created by Patrick Wardle on 1/20/23.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
|
||||
//keys for dictionary
|
||||
#define KEY_BTM_PATH @"path"
|
||||
#define KEY_BTM_ERROR @"error"
|
||||
#define KEY_BTM_VERSION @"version"
|
||||
#define KEY_BTM_MDM_PAYLOAD @"mdmPayloadsByIdentifier"
|
||||
#define KEY_BTM_ITEMS_BY_USER_ID @"itemsByUserIdentifier"
|
||||
|
||||
//keys for item(s)
|
||||
#define KEY_BTM_ITEM_ID @"id"
|
||||
#define KEY_BTM_ITEM_URL @"url"
|
||||
#define KEY_BTM_ITEM_UUID @"uuid"
|
||||
#define KEY_BTM_ITEM_NAME @"name"
|
||||
#define KEY_BTM_ITEM_TYPE @"type"
|
||||
#define KEY_BTM_ITEM_TEAM_ID @"teamID"
|
||||
#define KEY_BTM_ITEM_DEV_NAME @"devName"
|
||||
#define KEY_BTM_ITEM_BUNDLE_ID @"bundleID"
|
||||
#define KEY_BTM_ITEM_PARENT_ID @"parentID"
|
||||
#define KEY_BTM_ITEM_PLIST_PATH @"plistPath"
|
||||
#define KEY_BTM_ITEM_GENERATION @"generation"
|
||||
#define KEY_BTM_ITEM_EXE_PATH @"executablePath"
|
||||
#define KEY_BTM_ITEM_DISPOSITION @"disposition"
|
||||
#define KEY_BTM_ITEM_TYPE_DETAILS @"typeDetails"
|
||||
#define KEY_BTM_ITEM_EMBEDDED_IDS @"embeddedIDs"
|
||||
#define KEY_BTM_ITEM_ASSOCIATED_IDS @"associatedBundleIDs"
|
||||
#define KEY_BTM_ITEM_DISPOSITION_DETAILS @"dispositionDetails"
|
||||
|
||||
//APIs
|
||||
// note: path is optional
|
||||
NSInteger dumpBTM(NSURL* path);
|
||||
NSDictionary* parseBTM(NSURL* path);
|
||||
@@ -0,0 +1,727 @@
|
||||
//
|
||||
// dumpBTM.m
|
||||
// dumpBTM (library)
|
||||
//
|
||||
// Created by Patrick Wardle on 1/20/23.
|
||||
//
|
||||
|
||||
// Note: Once compiled, header file (dumpBTM.h) and library (libDumpBTM.a)
|
||||
// are added to: $SRCROOT/lib/
|
||||
//
|
||||
// Copy this header file / link this library in your own project(s)!
|
||||
|
||||
#import <dlfcn.h>
|
||||
#import "dumpBTM.h"
|
||||
#import "dumpBT_Internal.h"
|
||||
|
||||
/* GLOBALS */
|
||||
|
||||
//handle to btm daemon
|
||||
void* btmd = NULL;
|
||||
|
||||
/* FUNCTION DEF */
|
||||
|
||||
//helper
|
||||
// convert ItemRecord to dictionary
|
||||
NSDictionary* toDictionary(ItemRecord* record, NSArray* items);
|
||||
|
||||
//constructor
|
||||
// load btm daemon (into our memory)
|
||||
__attribute__((constructor)) static void initDumpBTM(void)
|
||||
{
|
||||
//load btm daemon
|
||||
// its contains obj methods we need
|
||||
btmd = dlopen(BTM_DAEMON, RTLD_LAZY);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//load a btm file
|
||||
// and init a NSKeyedUnarchiver for it
|
||||
NSKeyedUnarchiver* load(NSURL** path)
|
||||
{
|
||||
//error
|
||||
NSError* error = nil;
|
||||
|
||||
//database data
|
||||
NSData* data = nil;
|
||||
|
||||
//unarchiver
|
||||
NSKeyedUnarchiver* keyedUnarchiver = nil;
|
||||
|
||||
//no (user-specified) path?
|
||||
// find/use system's btm path
|
||||
if(nil == *path)
|
||||
{
|
||||
//get btm path
|
||||
*path = getPath();
|
||||
if(nil == *path)
|
||||
{
|
||||
//err msg
|
||||
os_log_error(OS_LOG_DEFAULT, "ERROR: failed to find a .btm file in %{public}@", BTM_DIRECTORY);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
//load database *path
|
||||
// note: this will fail if client doesn't have full disk access
|
||||
data = [NSData dataWithContentsOfURL:*path options:0 error:&error];
|
||||
if(nil == data)
|
||||
{
|
||||
//err msg
|
||||
os_log_error(OS_LOG_DEFAULT, "ERROR: failed to open/load %{public}@", *path);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//init unachiver
|
||||
keyedUnarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:&error];
|
||||
|
||||
//set decoding failure policy
|
||||
// will throw an exception if unserialization fails
|
||||
keyedUnarchiver.decodingFailurePolicy = NSDecodingFailurePolicyRaiseException;
|
||||
|
||||
bail:
|
||||
|
||||
return keyedUnarchiver;
|
||||
}
|
||||
|
||||
//deserialize
|
||||
Storage* deserialize(NSURL* path)
|
||||
{
|
||||
//'Storage' class
|
||||
Class StorageClass = nil;
|
||||
|
||||
//'Storage' object
|
||||
Storage* storage = nil;
|
||||
|
||||
//unarchiver
|
||||
NSKeyedUnarchiver* keyedUnarchiver = nil;
|
||||
|
||||
//lookup 'Storage' class
|
||||
StorageClass = NSClassFromString(@"Storage");
|
||||
if(nil == StorageClass)
|
||||
{
|
||||
//err msg
|
||||
printf("ERROR: failed to find 'Storage' class\n\n");
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//load btm file
|
||||
keyedUnarchiver = load(&path);
|
||||
if(nil == keyedUnarchiver)
|
||||
{
|
||||
//err msg
|
||||
printf("ERROR: failed to open/load %s\n\n", path.path.UTF8String);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//dbg msg
|
||||
printf("Opened %s\n\n", path.path.UTF8String);
|
||||
|
||||
//wrap unserialization
|
||||
@try {
|
||||
|
||||
//decode storage object
|
||||
// this in turn will decode all items
|
||||
storage = [keyedUnarchiver decodeObjectOfClass:StorageClass forKey:@"store"];
|
||||
}
|
||||
//exception
|
||||
@catch(NSException *exception) {
|
||||
|
||||
//err msg
|
||||
printf("ERROR: unserializing failed with %s\n\n", exception.description.UTF8String);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//sanity check
|
||||
// iVar: 'itemsByUserIdentifier' and 'mdmPayloadsByIdentifier'
|
||||
if( (YES != [storage respondsToSelector:@selector(itemsByUserIdentifier)]) ||
|
||||
(YES != [storage respondsToSelector:@selector(mdmPayloadsByIdentifier)]) )
|
||||
{
|
||||
//err msg
|
||||
printf("ERROR: failed to find 'Storage' class's 'itemsByUserIdentifier' or 'mdmPayloadsByIdentifier' instance variables\n\n");
|
||||
|
||||
//unset
|
||||
storage = nil;
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
bail:
|
||||
|
||||
return storage;
|
||||
}
|
||||
|
||||
//API INTERFACE
|
||||
// dump a btm file to stdout
|
||||
// path is optional, and if nil, will be found dynamically
|
||||
NSInteger dumpBTM(NSURL* path)
|
||||
{
|
||||
//result
|
||||
NSInteger result = -1;
|
||||
|
||||
//'Storage' object
|
||||
Storage* storage = nil;
|
||||
|
||||
//item #
|
||||
int itemNumber = 0;
|
||||
|
||||
//deserialze
|
||||
storage = deserialize(path);
|
||||
if(nil == storage)
|
||||
{
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//process each/all items
|
||||
for(NSString* key in storage.itemsByUserIdentifier)
|
||||
{
|
||||
//uid
|
||||
uid_t uid = 0;
|
||||
|
||||
//items
|
||||
NSArray* items = nil;
|
||||
|
||||
//get uid from uuid
|
||||
uid = uidFromUUID(key);
|
||||
|
||||
//header (w/ uid/uuid)
|
||||
printf("========================\n Records for UID %d : %s\n========================\n\n", uid, key.description.UTF8String);
|
||||
|
||||
printf(" Items:\n\n");
|
||||
|
||||
//process each item
|
||||
items = storage.itemsByUserIdentifier[key];
|
||||
for(ItemRecord* item in items)
|
||||
{
|
||||
//sanity check
|
||||
if(YES != [item respondsToSelector:NSSelectorFromString(@"dumpVerboseDescription")])
|
||||
{
|
||||
//err msg
|
||||
printf("ERROR: failed to find current 'ItemRecord' object's 'dumpVerboseDescription' method\n\n");
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//display number and item details.
|
||||
printf(" #%d\n", ++itemNumber);
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
|
||||
printf(" %s\n", [[item performSelector:NSSelectorFromString(@"dumpVerboseDescription")] UTF8String]);
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//any mdm items?
|
||||
if(0 != storage.mdmPayloadsByIdentifier.count)
|
||||
{
|
||||
//reset
|
||||
itemNumber = 0;
|
||||
|
||||
//dbg output
|
||||
printf("========================\n");
|
||||
printf("\n MDM Payloads:\n\n");
|
||||
|
||||
//print out all mdm items
|
||||
for(NSString* key in storage.mdmPayloadsByIdentifier)
|
||||
{
|
||||
//item
|
||||
NSDictionary* mdmItem = storage.mdmPayloadsByIdentifier[key];
|
||||
|
||||
//print
|
||||
printf("#%d: %s:\n%s\n", ++itemNumber, key.UTF8String, mdmItem.description.UTF8String);
|
||||
}
|
||||
}
|
||||
|
||||
//happy
|
||||
result = noErr;
|
||||
|
||||
bail:
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//API INTERFACE
|
||||
// parse a btm file into a dictionary
|
||||
// path is optional, and if nil, will be found dynamically
|
||||
NSDictionary* parseBTM(NSURL* path)
|
||||
{
|
||||
//contents
|
||||
NSMutableDictionary* contents = nil;
|
||||
|
||||
//'Storage' object
|
||||
Storage* storage = nil;
|
||||
|
||||
//items
|
||||
NSMutableDictionary* itemsByUserID = nil;
|
||||
|
||||
//init contents
|
||||
contents = [NSMutableDictionary dictionary];
|
||||
|
||||
//init
|
||||
itemsByUserID = [NSMutableDictionary dictionary];
|
||||
|
||||
//deserialze
|
||||
storage = deserialize(path);
|
||||
if(nil == storage)
|
||||
{
|
||||
//set error
|
||||
contents[KEY_BTM_ERROR] = [NSNumber numberWithLong:EFTYPE];
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//set path
|
||||
contents[KEY_BTM_PATH] = path;
|
||||
|
||||
//process each/all items
|
||||
// key is the UUID (mapping to a uid)
|
||||
for(NSString* key in storage.itemsByUserIdentifier)
|
||||
{
|
||||
//uid
|
||||
uid_t uid = 0;
|
||||
|
||||
//items
|
||||
NSMutableArray* items = nil;
|
||||
|
||||
//init
|
||||
items = [NSMutableArray array];
|
||||
|
||||
//get uid from uuid
|
||||
uid = uidFromUUID(key);
|
||||
|
||||
//process each item
|
||||
for(ItemRecord* item in storage.itemsByUserIdentifier[key])
|
||||
{
|
||||
//add item
|
||||
[items addObject:toDictionary(item, storage.itemsByUserIdentifier[key])];
|
||||
}
|
||||
|
||||
//add items
|
||||
itemsByUserID[key] = items;
|
||||
}
|
||||
|
||||
//save all items by user id
|
||||
contents[KEY_BTM_ITEMS_BY_USER_ID] = itemsByUserID;
|
||||
|
||||
//save MDM payloads
|
||||
if(0 != storage.mdmPayloadsByIdentifier.count)
|
||||
{
|
||||
//save
|
||||
contents[KEY_BTM_MDM_PAYLOAD] = storage.itemsByUserIdentifier;
|
||||
}
|
||||
|
||||
bail:
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
//get path of the latest BTM file
|
||||
// looks in "/private/var/db/com.apple.backgroundtaskmanagement/"
|
||||
NSURL* getPath(void)
|
||||
{
|
||||
//path
|
||||
NSURL* path = nil;
|
||||
|
||||
//directory files
|
||||
NSArray* files = nil;
|
||||
|
||||
//btm files
|
||||
NSArray* btmFiles = nil;
|
||||
|
||||
//error
|
||||
NSError* error = nil;
|
||||
|
||||
//load all files in directory
|
||||
files = [NSFileManager.defaultManager contentsOfDirectoryAtURL:[NSURL fileURLWithPath:BTM_DIRECTORY] includingPropertiesForKeys:nil options:0 error:&error];
|
||||
if(nil == files)
|
||||
{
|
||||
//err msg
|
||||
os_log_error(OS_LOG_DEFAULT, "ERROR: failed to enumerate files in %{public}@\nDetails: %{public}@\n\n", BTM_DIRECTORY, error);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
||||
//get files that match ".btm"
|
||||
btmFiles = [files filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"self.absoluteString ENDSWITH '.btm'"]];
|
||||
|
||||
//grab last file
|
||||
path = btmFiles.lastObject;
|
||||
|
||||
bail:
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/* (helper) EXTENSION FUNCTIONS for ItemRecord object */
|
||||
/* ...can't extented ItemRecord class, as its defined in a daemon (vs. framework) */
|
||||
|
||||
//helper function
|
||||
//convert Item Record's type to string
|
||||
NSString* typeDetails(ItemRecord* record)
|
||||
{
|
||||
//details
|
||||
NSMutableString* details = nil;
|
||||
|
||||
//init
|
||||
details = [NSMutableString string];
|
||||
|
||||
//curated
|
||||
if(record.type & 0x80000)
|
||||
{
|
||||
[details appendString:@"curated "];
|
||||
}
|
||||
|
||||
//legacy
|
||||
if(record.type & 0x10000)
|
||||
{
|
||||
[details appendString:@"legacy "];
|
||||
}
|
||||
|
||||
//developer
|
||||
if(record.type & 0x20)
|
||||
{
|
||||
[details appendString:@"developer "];
|
||||
}
|
||||
|
||||
//daemon
|
||||
if(record.type & 0x10)
|
||||
{
|
||||
[details appendString:@"daemon "];
|
||||
}
|
||||
|
||||
//agent
|
||||
if(record.type & 0x8)
|
||||
{
|
||||
[details appendString:@"agent "];
|
||||
}
|
||||
|
||||
//login item
|
||||
if(record.type & 0x4)
|
||||
{
|
||||
[details appendString:@"login item "];
|
||||
}
|
||||
|
||||
//app
|
||||
if(record.type & 0x2)
|
||||
{
|
||||
[details appendString:@"app "];
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
//helper function
|
||||
// find parent of ItemRecord, via ID
|
||||
ItemRecord* findParent(ItemRecord* record, NSArray* items)
|
||||
{
|
||||
//parent
|
||||
ItemRecord* parent = nil;
|
||||
|
||||
//find parent
|
||||
for(ItemRecord* item in items)
|
||||
{
|
||||
//not a parent?
|
||||
if(nil == item.identifier)
|
||||
{
|
||||
//skip
|
||||
continue;
|
||||
}
|
||||
|
||||
//no match
|
||||
if(YES != [item.identifier isEqualToString:record.container])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//match
|
||||
// save parent
|
||||
parent = item;
|
||||
|
||||
//done
|
||||
break;
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
//helper function
|
||||
//convert ItemRecord disposition to string
|
||||
NSString* dispositionDetails(ItemRecord* record)
|
||||
{
|
||||
//details
|
||||
NSMutableString* details = nil;
|
||||
|
||||
//init
|
||||
details = [NSMutableString string];
|
||||
|
||||
//enabled
|
||||
if(record.disposition & 0x1)
|
||||
{
|
||||
[details appendString:@"enabled "];
|
||||
}
|
||||
//disabled
|
||||
else
|
||||
{
|
||||
[details appendString:@"disabled "];
|
||||
}
|
||||
|
||||
//allowed
|
||||
if(record.disposition & 0x2)
|
||||
{
|
||||
[details appendString:@"allowed "];
|
||||
}
|
||||
//disallowed
|
||||
else
|
||||
{
|
||||
[details appendString:@"disallowed "];
|
||||
}
|
||||
|
||||
//hidden
|
||||
if(record.disposition & 0x4)
|
||||
{
|
||||
[details appendString:@"hidden "];
|
||||
}
|
||||
//visibile
|
||||
else
|
||||
{
|
||||
[details appendString:@"visible "];
|
||||
}
|
||||
|
||||
//notified
|
||||
if(record.disposition & 0x8)
|
||||
{
|
||||
[details appendString:@"notified"];
|
||||
}
|
||||
//not notified
|
||||
else
|
||||
{
|
||||
[details appendString:@"not notified"];
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
//helper function
|
||||
// convert ItemRecord to a dictionary
|
||||
// w/ additional logic to extract plist/exe paths
|
||||
NSDictionary* toDictionary(ItemRecord* record, NSArray* items)
|
||||
{
|
||||
//item
|
||||
NSMutableDictionary* item = nil;
|
||||
|
||||
//bundle
|
||||
NSBundle* bundle = nil;
|
||||
|
||||
//bundle ids
|
||||
NSMutableArray* bundleIDs = nil;
|
||||
|
||||
//parent
|
||||
ItemRecord* parent = nil;
|
||||
|
||||
//embedded items
|
||||
NSMutableArray* embeddedItems = nil;
|
||||
|
||||
//init
|
||||
item = [NSMutableDictionary dictionary];
|
||||
|
||||
//uuid
|
||||
item[KEY_BTM_ITEM_UUID] = record.uuid;
|
||||
|
||||
//name
|
||||
item[KEY_BTM_ITEM_NAME] = record.name;
|
||||
|
||||
//dev
|
||||
item[KEY_BTM_ITEM_DEV_NAME] = record.developerName;
|
||||
|
||||
//team id
|
||||
if(nil != record.teamIdentifier)
|
||||
{
|
||||
item[KEY_BTM_ITEM_TEAM_ID] = record.teamIdentifier;
|
||||
}
|
||||
|
||||
//type
|
||||
item[KEY_BTM_ITEM_TYPE] = [NSNumber numberWithLong:(long)record.type];
|
||||
|
||||
//type description
|
||||
item[KEY_BTM_ITEM_TYPE_DETAILS] = typeDetails(record);
|
||||
|
||||
//disposition
|
||||
item[KEY_BTM_ITEM_DISPOSITION] = [NSNumber numberWithLong:(long)record.disposition];
|
||||
|
||||
//disposition details
|
||||
item[KEY_BTM_ITEM_DISPOSITION_DETAILS] = dispositionDetails(record);
|
||||
|
||||
//identifier
|
||||
item[KEY_BTM_ITEM_ID] = record.identifier;
|
||||
|
||||
//url
|
||||
item[KEY_BTM_ITEM_URL] = record.url;
|
||||
|
||||
//path
|
||||
item[KEY_BTM_ITEM_EXE_PATH] = record.executablePath;
|
||||
|
||||
//generation
|
||||
item[KEY_BTM_ITEM_GENERATION] = [NSNumber numberWithLong:(long)record.generation];
|
||||
|
||||
//bundle id
|
||||
if(nil != record.bundleIdentifier)
|
||||
{
|
||||
//add
|
||||
item[KEY_BTM_ITEM_BUNDLE_ID] = record.bundleIdentifier;
|
||||
}
|
||||
|
||||
//associated bundle ids
|
||||
if(0 != record.associatedBundleIdentifiers.count)
|
||||
{
|
||||
//init
|
||||
bundleIDs = [NSMutableArray array];
|
||||
|
||||
//add each
|
||||
for(NSString* associatedBundleIdentifier in record.associatedBundleIdentifiers)
|
||||
{
|
||||
//add
|
||||
[bundleIDs addObject:associatedBundleIdentifier];
|
||||
}
|
||||
|
||||
item[KEY_BTM_ITEM_ASSOCIATED_IDS] = bundleIDs;
|
||||
}
|
||||
|
||||
//parent
|
||||
if(0 != record.container.length)
|
||||
{
|
||||
//add
|
||||
item[KEY_BTM_ITEM_PARENT_ID] = record.container;
|
||||
}
|
||||
|
||||
//items
|
||||
if(0 != record.embeddedItems.count)
|
||||
{
|
||||
//init
|
||||
embeddedItems = [NSMutableArray array];
|
||||
|
||||
//add each
|
||||
for(NSString* item in record.embeddedItems.allObjects)
|
||||
{
|
||||
//add
|
||||
[embeddedItems addObject:item];
|
||||
}
|
||||
|
||||
//save
|
||||
item[KEY_BTM_ITEM_EMBEDDED_IDS] = embeddedItems;
|
||||
}
|
||||
|
||||
//agents/daemons
|
||||
// path: already set
|
||||
// plist: is in 'url'
|
||||
if( (record.type & 0x8) ||
|
||||
(record.type & 0x10) )
|
||||
{
|
||||
//set plist
|
||||
if(nil != record.url)
|
||||
{
|
||||
//plist
|
||||
item[KEY_BTM_ITEM_PLIST_PATH] = record.url.path;
|
||||
}
|
||||
}
|
||||
|
||||
//login item
|
||||
// path: construct via parent
|
||||
else if(record.type & 0x4)
|
||||
{
|
||||
//find parent
|
||||
parent = findParent(record, items);
|
||||
if(nil != parent)
|
||||
{
|
||||
bundle = [NSBundle bundleWithPath:[NSString stringWithFormat:@"%@%@", parent.url.path, record.url.path]];
|
||||
}
|
||||
|
||||
//add exe path
|
||||
if(nil != bundle.executablePath)
|
||||
{
|
||||
//exe path
|
||||
item[KEY_BTM_ITEM_EXE_PATH] = bundle.executablePath;
|
||||
}
|
||||
}
|
||||
|
||||
//app
|
||||
// path to app bundle is in url
|
||||
// ...load bundle to get exe path
|
||||
else if(record.type & 0x2)
|
||||
{
|
||||
//load bundle from app path
|
||||
bundle = [NSBundle bundleWithPath:record.url.path];
|
||||
|
||||
//add exe path
|
||||
if(nil != bundle.executablePath)
|
||||
{
|
||||
//exe path
|
||||
item[KEY_BTM_ITEM_EXE_PATH] = bundle.executablePath;
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
//helper function
|
||||
//get a UID from a UUID
|
||||
uid_t uidFromUUID(NSString* uuid)
|
||||
{
|
||||
//uid
|
||||
uid_t uid = 0;
|
||||
|
||||
//node
|
||||
ODNode* node = nil;
|
||||
|
||||
//query
|
||||
ODQuery* query = nil;
|
||||
|
||||
//return
|
||||
NSArray* result = nil;
|
||||
|
||||
//record
|
||||
ODRecord* record = nil;
|
||||
|
||||
//attribute (@kDS1AttrUniqueID)
|
||||
NSArray* attribute = nil;
|
||||
|
||||
//init node
|
||||
node = [ODNode nodeWithSession:ODSession.defaultSession type:0x2200 error:nil];
|
||||
|
||||
//perform query
|
||||
query = [ODQuery queryWithNode:node forRecordTypes:kODRecordTypeUsers attribute:kODAttributeTypeGUID matchType:0x2001 queryValues:uuid returnAttributes:nil maximumResults:1 error:nil];
|
||||
|
||||
//result
|
||||
result = [query resultsAllowingPartial:0x0 error:nil];
|
||||
|
||||
//grab first result
|
||||
record = result.firstObject;
|
||||
|
||||
//grab '@kDS1AttrUniqueID'
|
||||
attribute = [record valuesForAttribute:@kDS1AttrUniqueID error:nil];
|
||||
|
||||
//uid is first obj, as int
|
||||
uid = [attribute.firstObject intValue];
|
||||
|
||||
return uid;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// dumpBTM_Internal.h
|
||||
// dumpBTM (library)
|
||||
//
|
||||
// Created by Patrick Wardle on 1/20/23.
|
||||
//
|
||||
|
||||
@import OSLog;
|
||||
@import Foundation;
|
||||
@import OpenDirectory;
|
||||
@import DirectoryService;
|
||||
|
||||
#define BTM_DAEMON "/System/Library/PrivateFrameworks/BackgroundTaskManagement.framework/Resources/backgroundtaskmanagementd"
|
||||
|
||||
#define BTM_DIRECTORY @"/private/var/db/com.apple.backgroundtaskmanagement/"
|
||||
|
||||
//helper function(s)
|
||||
NSURL* getPath(void);
|
||||
uid_t uidFromUUID(NSString* uuid);
|
||||
|
||||
/* the following objects
|
||||
...dumped from backgroundtaskmanagementd */
|
||||
|
||||
//Storage object
|
||||
@interface Storage : NSObject
|
||||
|
||||
//items
|
||||
@property(nonatomic, retain)NSDictionary* itemsByUserIdentifier;
|
||||
|
||||
//mdm items
|
||||
@property(nonatomic, retain)NSDictionary* mdmPayloadsByIdentifier;
|
||||
|
||||
@end
|
||||
|
||||
//Item Record object
|
||||
@interface ItemRecord : NSObject
|
||||
|
||||
@property NSInteger type;
|
||||
@property NSInteger generation;
|
||||
@property NSInteger disposition;
|
||||
|
||||
@property(nonatomic, retain)NSURL* url;
|
||||
@property(nonatomic, retain)NSUUID* uuid;
|
||||
@property(nonatomic, retain)NSString* name;
|
||||
@property(nonatomic, retain)NSData* bookmark;
|
||||
@property(nonatomic, retain)NSString* container;
|
||||
@property(nonatomic, retain)NSMutableSet* embeddedItems;
|
||||
@property(nonatomic, retain)NSString* identifier;
|
||||
@property(nonatomic, retain)NSString* developerName;
|
||||
@property(nonatomic, retain)NSString* executablePath;
|
||||
@property(nonatomic, retain)NSString* teamIdentifier;
|
||||
@property(nonatomic, retain)NSString* bundleIdentifier;
|
||||
@property(nonatomic, retain)NSData* lightweightRequirement;
|
||||
@property(nonatomic, retain)NSArray* associatedBundleIdentifiers;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// dumpBTM.h
|
||||
// library
|
||||
//
|
||||
// Created by Patrick Wardle on 1/20/23.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
|
||||
//keys for dictionary
|
||||
#define KEY_BTM_PATH @"path"
|
||||
#define KEY_BTM_ERROR @"error"
|
||||
#define KEY_BTM_VERSION @"version"
|
||||
#define KEY_BTM_MDM_PAYLOAD @"mdmPayloadsByIdentifier"
|
||||
#define KEY_BTM_ITEMS_BY_USER_ID @"itemsByUserIdentifier"
|
||||
|
||||
//keys for item(s)
|
||||
#define KEY_BTM_ITEM_ID @"id"
|
||||
#define KEY_BTM_ITEM_URL @"url"
|
||||
#define KEY_BTM_ITEM_UUID @"uuid"
|
||||
#define KEY_BTM_ITEM_NAME @"name"
|
||||
#define KEY_BTM_ITEM_TYPE @"type"
|
||||
#define KEY_BTM_ITEM_TEAM_ID @"teamID"
|
||||
#define KEY_BTM_ITEM_DEV_NAME @"devName"
|
||||
#define KEY_BTM_ITEM_BUNDLE_ID @"bundleID"
|
||||
#define KEY_BTM_ITEM_PARENT_ID @"parentID"
|
||||
#define KEY_BTM_ITEM_PLIST_PATH @"plistPath"
|
||||
#define KEY_BTM_ITEM_GENERATION @"generation"
|
||||
#define KEY_BTM_ITEM_EXE_PATH @"executablePath"
|
||||
#define KEY_BTM_ITEM_DISPOSITION @"disposition"
|
||||
#define KEY_BTM_ITEM_TYPE_DETAILS @"typeDetails"
|
||||
#define KEY_BTM_ITEM_EMBEDDED_IDS @"embeddedIDs"
|
||||
#define KEY_BTM_ITEM_ASSOCIATED_IDS @"associatedBundleIDs"
|
||||
#define KEY_BTM_ITEM_DISPOSITION_DETAILS @"dispositionDetails"
|
||||
|
||||
//APIs
|
||||
// note: path is optional
|
||||
NSInteger dumpBTM(NSURL* path);
|
||||
NSDictionary* parseBTM(NSURL* path);
|
||||
@@ -0,0 +1,331 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 56;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
CD7B6A7D297B0CC100E59E7F /* dumpBT_Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = CD7B6A7C297B0CC100E59E7F /* dumpBT_Internal.h */; };
|
||||
CD7B6A7F297B0CC100E59E7F /* dumpBTM.m in Sources */ = {isa = PBXBuildFile; fileRef = CD7B6A7E297B0CC100E59E7F /* dumpBTM.m */; };
|
||||
CDBFE39F2A49CF85005A9819 /* dumpBTM.h in Headers */ = {isa = PBXBuildFile; fileRef = CDBFE39E2A49CF85005A9819 /* dumpBTM.h */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
CD4D4EAD2ADFCBF000DAFFB5 /* BackgroundTaskManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BackgroundTaskManagement.framework; path = ../../../../../System/Library/PrivateFrameworks/BackgroundTaskManagement.framework; sourceTree = "<group>"; };
|
||||
CD7B6A79297B0CC100E59E7F /* libdumpBTM.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdumpBTM.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
CD7B6A7C297B0CC100E59E7F /* dumpBT_Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = dumpBT_Internal.h; sourceTree = "<group>"; };
|
||||
CD7B6A7E297B0CC100E59E7F /* dumpBTM.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = dumpBTM.m; sourceTree = "<group>"; };
|
||||
CDBFE39E2A49CF85005A9819 /* dumpBTM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dumpBTM.h; path = ../../library/code/dumpBTM.h; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
CD7B6A77297B0CC100E59E7F /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
CD4D4EAC2ADFCBF000DAFFB5 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CD4D4EAD2ADFCBF000DAFFB5 /* BackgroundTaskManagement.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CD7B6A70297B0CC100E59E7F = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CD7B6A7B297B0CC100E59E7F /* code */,
|
||||
CD7B6A7A297B0CC100E59E7F /* Products */,
|
||||
CD4D4EAC2ADFCBF000DAFFB5 /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CD7B6A7A297B0CC100E59E7F /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CD7B6A79297B0CC100E59E7F /* libdumpBTM.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CD7B6A7B297B0CC100E59E7F /* code */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CDBFE39E2A49CF85005A9819 /* dumpBTM.h */,
|
||||
CD7B6A7C297B0CC100E59E7F /* dumpBT_Internal.h */,
|
||||
CD7B6A7E297B0CC100E59E7F /* dumpBTM.m */,
|
||||
);
|
||||
path = code;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
CD7B6A75297B0CC100E59E7F /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CDBFE39F2A49CF85005A9819 /* dumpBTM.h in Headers */,
|
||||
CD7B6A7D297B0CC100E59E7F /* dumpBT_Internal.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
CD7B6A78297B0CC100E59E7F /* library */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = CD7B6A82297B0CC100E59E7F /* Build configuration list for PBXNativeTarget "library" */;
|
||||
buildPhases = (
|
||||
CD7B6A75297B0CC100E59E7F /* Headers */,
|
||||
CD7B6A76297B0CC100E59E7F /* Sources */,
|
||||
CD7B6A77297B0CC100E59E7F /* Frameworks */,
|
||||
CD7B6A9A297B56C600E59E7F /* ShellScript */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = library;
|
||||
productName = library;
|
||||
productReference = CD7B6A79297B0CC100E59E7F /* libdumpBTM.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
CD7B6A71297B0CC100E59E7F /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastUpgradeCheck = 1420;
|
||||
TargetAttributes = {
|
||||
CD7B6A78297B0CC100E59E7F = {
|
||||
CreatedOnToolsVersion = 14.2;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = CD7B6A74297B0CC100E59E7F /* Build configuration list for PBXProject "library" */;
|
||||
compatibilityVersion = "Xcode 14.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = CD7B6A70297B0CC100E59E7F;
|
||||
productRefGroup = CD7B6A7A297B0CC100E59E7F /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
CD7B6A78297B0CC100E59E7F /* library */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
CD7B6A9A297B56C600E59E7F /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "#move library/h into top-level \"lib/\" folder\ncp $CODESIGNING_FOLDER_PATH $SRCROOT/lib/libDumpBTM.a\ncp $SRCROOT/code/dumpBTM.h $SRCROOT/lib/dumpBTM.h\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
CD7B6A76297B0CC100E59E7F /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CD7B6A7F297B0CC100E59E7F /* dumpBTM.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
CD7B6A80297B0CC100E59E7F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.1;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
CD7B6A81297B0CC100E59E7F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.1;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
CD7B6A83297B0CC100E59E7F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = VBG97UB4TA;
|
||||
EXECUTABLE_PREFIX = lib;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
PRODUCT_NAME = dumpBTM;
|
||||
SKIP_INSTALL = YES;
|
||||
SYSTEM_FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
|
||||
);
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
CD7B6A84297B0CC100E59E7F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = VBG97UB4TA;
|
||||
EXECUTABLE_PREFIX = lib;
|
||||
PRODUCT_NAME = dumpBTM;
|
||||
SKIP_INSTALL = YES;
|
||||
SYSTEM_FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks",
|
||||
);
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
CD7B6A74297B0CC100E59E7F /* Build configuration list for PBXProject "library" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
CD7B6A80297B0CC100E59E7F /* Debug */,
|
||||
CD7B6A81297B0CC100E59E7F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
CD7B6A82297B0CC100E59E7F /* Build configuration list for PBXNativeTarget "library" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
CD7B6A83297B0CC100E59E7F /* Debug */,
|
||||
CD7B6A84297B0CC100E59E7F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = CD7B6A71297B0CC100E59E7F /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1420"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CD7B6A78297B0CC100E59E7F"
|
||||
BuildableName = "libdumpBTM.a"
|
||||
BlueprintName = "library"
|
||||
ReferencedContainer = "container:library.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CD7B6A78297B0CC100E59E7F"
|
||||
BuildableName = "libdumpBTM.a"
|
||||
BlueprintName = "library"
|
||||
ReferencedContainer = "container:library.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// main.m
|
||||
// tool
|
||||
//
|
||||
// Created by Patrick Wardle on 1/20/23.
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#include "dumpBTM.h"
|
||||
|
||||
//example program to link/invoke 'dumpBTM' library
|
||||
int main(int argc, const char * argv[]) {
|
||||
|
||||
//for user-specified path
|
||||
NSURL* path = nil;
|
||||
|
||||
//user-specified path?
|
||||
if(argc == 2)
|
||||
{
|
||||
//set
|
||||
path = [[NSURL alloc] initFileURLWithPath:[NSString stringWithUTF8String:argv[1]]];
|
||||
}
|
||||
|
||||
//just dump to stdout
|
||||
// similar to: sfltool dumpbtm
|
||||
dumpBTM(path);
|
||||
|
||||
//parse into a dictionary
|
||||
// containing items (in dictionaries)
|
||||
NSDictionary* contents = parseBTM(path);
|
||||
|
||||
//now do something with extracted/parsed out items
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -7,11 +7,12 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
CDCA2856296A748A00944E6D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CDCA2855296A748A00944E6D /* main.m */; };
|
||||
CD7B6A94297B564300E59E7F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CD7B6A93297B564300E59E7F /* main.m */; };
|
||||
CD7B6AA3297B5B8C00E59E7F /* libDumpBTM.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CD7B6AA2297B5B8C00E59E7F /* libDumpBTM.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
CDCA2850296A748A00944E6D /* CopyFiles */ = {
|
||||
CD7B6A8E297B564300E59E7F /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = /usr/share/man/man1/;
|
||||
@@ -23,84 +24,93 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
CD5A02232978734E00363786 /* dumpBTM.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = dumpBTM.entitlements; sourceTree = "<group>"; };
|
||||
CD5A02242978758B00363786 /* Products */ = {isa = PBXFileReference; lastKnownFileType = text; name = Products; path = DerivedData/dumpBTM/Build/Products; sourceTree = SOURCE_ROOT; };
|
||||
CDCA2852296A748A00944E6D /* dumpBTM */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dumpBTM; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
CDCA2855296A748A00944E6D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
CD7B6A90297B564300E59E7F /* dumpBTM */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dumpBTM; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
CD7B6A93297B564300E59E7F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = main.m; path = code/main.m; sourceTree = "<group>"; };
|
||||
CD7B6A9F297B5B2B00E59E7F /* dumpBTM.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = dumpBTM.h; path = ../library/lib/dumpBTM.h; sourceTree = "<group>"; };
|
||||
CD7B6AA2297B5B8C00E59E7F /* libDumpBTM.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libDumpBTM.a; path = ../library/lib/libDumpBTM.a; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
CDCA284F296A748A00944E6D /* Frameworks */ = {
|
||||
CD7B6A8D297B564300E59E7F /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CD7B6AA3297B5B8C00E59E7F /* libDumpBTM.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
CDCA2849296A748A00944E6D = {
|
||||
CD7B6A87297B564300E59E7F = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CDCA2854296A748A00944E6D /* dumpBTM */,
|
||||
CDCA2853296A748A00944E6D /* Products */,
|
||||
CD7B6A92297B564300E59E7F /* tool */,
|
||||
CD7B6A91297B564300E59E7F /* Products */,
|
||||
CD7B6A9C297B5A9D00E59E7F /* Frameworks */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CDCA2853296A748A00944E6D /* Products */ = {
|
||||
CD7B6A91297B564300E59E7F /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CDCA2852296A748A00944E6D /* dumpBTM */,
|
||||
CD7B6A90297B564300E59E7F /* dumpBTM */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CDCA2854296A748A00944E6D /* dumpBTM */ = {
|
||||
CD7B6A92297B564300E59E7F /* tool */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CD5A02232978734E00363786 /* dumpBTM.entitlements */,
|
||||
CDCA2855296A748A00944E6D /* main.m */,
|
||||
CD5A02242978758B00363786 /* Products */,
|
||||
CD7B6A9F297B5B2B00E59E7F /* dumpBTM.h */,
|
||||
CD7B6A93297B564300E59E7F /* main.m */,
|
||||
);
|
||||
path = dumpBTM;
|
||||
name = tool;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CD7B6A9C297B5A9D00E59E7F /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CD7B6AA2297B5B8C00E59E7F /* libDumpBTM.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
CDCA2851296A748A00944E6D /* dumpBTM */ = {
|
||||
CD7B6A8F297B564300E59E7F /* tool */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = CDCA2859296A748A00944E6D /* Build configuration list for PBXNativeTarget "dumpBTM" */;
|
||||
buildConfigurationList = CD7B6A97297B564300E59E7F /* Build configuration list for PBXNativeTarget "tool" */;
|
||||
buildPhases = (
|
||||
CDCA284E296A748A00944E6D /* Sources */,
|
||||
CDCA284F296A748A00944E6D /* Frameworks */,
|
||||
CDCA2850296A748A00944E6D /* CopyFiles */,
|
||||
CD7B6A8C297B564300E59E7F /* Sources */,
|
||||
CD7B6A8D297B564300E59E7F /* Frameworks */,
|
||||
CD7B6A8E297B564300E59E7F /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = dumpBTM;
|
||||
productName = parseBTM;
|
||||
productReference = CDCA2852296A748A00944E6D /* dumpBTM */;
|
||||
name = tool;
|
||||
productName = tool;
|
||||
productReference = CD7B6A90297B564300E59E7F /* dumpBTM */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
CDCA284A296A748A00944E6D /* Project object */ = {
|
||||
CD7B6A88297B564300E59E7F /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastUpgradeCheck = 1420;
|
||||
TargetAttributes = {
|
||||
CDCA2851296A748A00944E6D = {
|
||||
CreatedOnToolsVersion = 14.1;
|
||||
CD7B6A8F297B564300E59E7F = {
|
||||
CreatedOnToolsVersion = 14.2;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = CDCA284D296A748A00944E6D /* Build configuration list for PBXProject "dumpBTM" */;
|
||||
buildConfigurationList = CD7B6A8B297B564300E59E7F /* Build configuration list for PBXProject "tool" */;
|
||||
compatibilityVersion = "Xcode 14.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
@@ -108,29 +118,29 @@
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = CDCA2849296A748A00944E6D;
|
||||
productRefGroup = CDCA2853296A748A00944E6D /* Products */;
|
||||
mainGroup = CD7B6A87297B564300E59E7F;
|
||||
productRefGroup = CD7B6A91297B564300E59E7F /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
CDCA2851296A748A00944E6D /* dumpBTM */,
|
||||
CD7B6A8F297B564300E59E7F /* tool */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
CDCA284E296A748A00944E6D /* Sources */ = {
|
||||
CD7B6A8C297B564300E59E7F /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CDCA2856296A748A00944E6D /* main.m in Sources */,
|
||||
CD7B6A94297B564300E59E7F /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
CDCA2857296A748A00944E6D /* Debug */ = {
|
||||
CD7B6A95297B564300E59E7F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
@@ -140,7 +150,6 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_OPTIMIZATION_PROFILE_FILE = "";
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
@@ -164,7 +173,6 @@
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
@@ -182,7 +190,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.1;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
@@ -190,7 +198,7 @@
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
CDCA2858296A748A00944E6D /* Release */ = {
|
||||
CD7B6A96297B564300E59E7F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
@@ -200,7 +208,6 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_OPTIMIZATION_PROFILE_FILE = "";
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
@@ -224,7 +231,6 @@
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
@@ -236,43 +242,39 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.1;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
CDCA285A296A748A00944E6D /* Debug */ = {
|
||||
CD7B6A98297B564300E59E7F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = dumpBTM/dumpBTM.entitlements;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = VBG97UB4TA;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.dumpbtm";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/",
|
||||
);
|
||||
PRODUCT_NAME = dumpBTM;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
CDCA285B296A748A00944E6D /* Release */ = {
|
||||
CD7B6A99297B564300E59E7F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = dumpBTM/dumpBTM.entitlements;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = VBG97UB4TA;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.dumpbtm";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/",
|
||||
);
|
||||
PRODUCT_NAME = dumpBTM;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
};
|
||||
name = Release;
|
||||
@@ -280,25 +282,25 @@
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
CDCA284D296A748A00944E6D /* Build configuration list for PBXProject "dumpBTM" */ = {
|
||||
CD7B6A8B297B564300E59E7F /* Build configuration list for PBXProject "tool" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
CDCA2857296A748A00944E6D /* Debug */,
|
||||
CDCA2858296A748A00944E6D /* Release */,
|
||||
CD7B6A95297B564300E59E7F /* Debug */,
|
||||
CD7B6A96297B564300E59E7F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
CDCA2859296A748A00944E6D /* Build configuration list for PBXNativeTarget "dumpBTM" */ = {
|
||||
CD7B6A97297B564300E59E7F /* Build configuration list for PBXNativeTarget "tool" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
CDCA285A296A748A00944E6D /* Debug */,
|
||||
CDCA285B296A748A00944E6D /* Release */,
|
||||
CD7B6A98297B564300E59E7F /* Debug */,
|
||||
CD7B6A99297B564300E59E7F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = CDCA284A296A748A00944E6D /* Project object */;
|
||||
rootObject = CD7B6A88297B564300E59E7F /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
+25
-11
@@ -3,8 +3,8 @@
|
||||
LastUpgradeVersion = "1420"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
parallelizeBuildables = "NO"
|
||||
buildImplicitDependencies = "NO">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
@@ -14,10 +14,24 @@
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CDCA2851296A748A00944E6D"
|
||||
BlueprintIdentifier = "CD7B6A78297B0CC100E59E7F"
|
||||
BuildableName = "libdumpBTM.a"
|
||||
BlueprintName = "library"
|
||||
ReferencedContainer = "container:../Library/library.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CD7B6A8F297B564300E59E7F"
|
||||
BuildableName = "dumpBTM"
|
||||
BlueprintName = "dumpBTM"
|
||||
ReferencedContainer = "container:dumpBTM.xcodeproj">
|
||||
BlueprintName = "tool"
|
||||
ReferencedContainer = "container:tool.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
@@ -45,10 +59,10 @@
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CDCA2851296A748A00944E6D"
|
||||
BlueprintIdentifier = "CD7B6A8F297B564300E59E7F"
|
||||
BuildableName = "dumpBTM"
|
||||
BlueprintName = "dumpBTM"
|
||||
ReferencedContainer = "container:dumpBTM.xcodeproj">
|
||||
BlueprintName = "tool"
|
||||
ReferencedContainer = "container:tool.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
@@ -62,10 +76,10 @@
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "CDCA2851296A748A00944E6D"
|
||||
BlueprintIdentifier = "CD7B6A8F297B564300E59E7F"
|
||||
BuildableName = "dumpBTM"
|
||||
BlueprintName = "dumpBTM"
|
||||
ReferencedContainer = "container:dumpBTM.xcodeproj">
|
||||
BlueprintName = "tool"
|
||||
ReferencedContainer = "container:tool.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
Reference in New Issue
Block a user