#include <stdio.h> #include <CoreFoundation/CoreFoundation.h> #include <sys/stat.h> typedef void *MISProfileRef; typedef void (^block_handler_t)(MISProfileRef); // From libmis.h - the missing (still partial) header // libmis exports some 65 functions in 9.3.1. This shows a dozen. // some are accessors. But others (notably blacklist, UPP and // profile creation) will be included in a future version. #define MIS_ERROR_BASE 0xe8008000 // The #1 function in the library ... extern int MISValidateSignature (void *File, CFDictionaryRef Opts); // which is really just a pass through to : extern int MISValidateSignatureAndCopyInfo (CFStringRef File, CFDictionaryRef Opts, void **Info); extern CFStringRef MISCopyErrorStringForErrorCode(int Error); extern int MISEnumerateInstalledProvisioningProfiles (int flags, block_handler_t); extern CFStringRef MISProfileGetValue (MISProfileRef, CFStringRef Key); extern CFStringRef MISProvisioningProfileGetUUID (MISProfileRef); extern CFStringRef MISProvisioningProfileGetName (MISProfileRef); extern int MISProvisioningProfileGetVersion (MISProfileRef); extern CFDictionaryRef MISProvisioningProfileGetEntitlements (MISProfileRef); extern CFStringRef MISProvisioningProfileGetTeamIdentifier (MISProfileRef); extern CFArrayRef MISProvisioningProfileGetProvisionedDevices(MISProfileRef); extern int MISProfileIsMutable(MISProfileRef); extern int MISProvisioningProfileIsAppleInternalProfile(MISProfileRef); extern int MISProvisioningProfileIsForLocalProvisioning(MISProfileRef); extern int MISProvisioningProfileProvisionsAllDevices(MISProfileRef); extern int MISProvisioningProfileGrantsEntitlement(MISProfileRef, CFStringRef ,void *); // Validation options - actually CFStringRefs. but whatever extern void *kMISValidationOptionRespectUppTrustAndAuthorization; extern void *kMISValidationOptionValidateSignatureOnly; extern void *kMISValidationOptionUniversalFileOffset; extern void *kMISValidationOptionAllowAdHocSigning; extern void *kMISValidationOptionOnlineAuthorization; // triggers online validation of cert // end header part /** * MIStool - A CLI client for libmis.dylib and friends * * 04/25/2016 * * By Jonathan Levin, http://www.NewOSXBook.com/ * * Part of the free sources accompanying "Mac OS X and *OS Internals" * * Free to use and abuse, but give credit where credit is due, * instead of just copying into GitHub and claiming ownership of * code you didn't really took the effort to either reverse or rebuild. * * To compile: gcc-arm64 mistool.c -o mistool -lmis -framework CoreFoundation -Wall; * jtool --sign --inplace mistool; * * Then scp to your device. Supports "list", "validate" and "errors" command line actions. * * At this point, allows you to enumerate/dump profiles. I'd add Insert/Remove, but * insertion requires a valid dev profile to work with - and I got none. * * Expect the reversed source of libmis.dylib (and its nefarious hench-daemons, * misagent and online-auth-agent) around the same time as MOX*I II - and that's soon - * I promise. This should give you an idea of just how thorough I'm going to get. * */ // Utility function char * dumpCFArray(CFArrayRef Arr) { CFIndex count = CFArrayGetCount(Arr); int v = 0; const void *val; static char entVal [8192]; entVal [0] = '\0'; sprintf (entVal , "(%ld values) ", count); for (v = 0; v < count; v++) { val = CFArrayGetValueAtIndex(Arr, v); // Assuming string here.. const char *printed = CFStringGetCStringPtr (val, // CFStringRef theString, kCFStringEncodingUTF8); // CFStringEncoding encoding ); // yeah, possible buffer overflow here on malicious profile :-) strcat (entVal, printed); if (v < count -1) strcat(entVal,","); } return (entVal); } // Utility function void printDictKey (const void* key, const void* value, void* context) { // You could also use CFShow() here, with the dup2() trick // to get CFShow to print to stdout.. int indent = (int) context; const char *entName = CFStringGetCStringPtr (key , // CFStringRef theString, kCFStringEncodingUTF8); // CFStringEncoding encoding ); const char *entValue = "?"; // Why AAPL doesn't have constants for the typeID (so you could switch) still // eludes me. if (CFGetTypeID(value) == CFStringGetTypeID()) { entValue = CFStringGetCStringPtr (value , // CFStringRef theString, kCFStringEncodingUTF8); // CFStringEncoding encoding ); } else if (CFGetTypeID(value) == CFBooleanGetTypeID()) { entValue = CFBooleanGetValue(value) ? "true" : "false"; } else if (CFGetTypeID(value) == CFArrayGetTypeID()) { entValue = dumpCFArray(value); } printf("\t%s: %s\n", entName,entValue); } void dumpProfile(MISProfileRef Prof) { CFStringRef cfsProfName= MISProvisioningProfileGetName(Prof); const char *profName = CFStringGetCStringPtr (cfsProfName , // CFStringRef theString, kCFStringEncodingUTF8); // CFStringEncoding encoding ); CFStringRef cfsUuid = MISProvisioningProfileGetUUID(Prof); const char *uuid = CFStringGetCStringPtr (cfsUuid , // CFStringRef theString, kCFStringEncodingUTF8); // CFStringEncoding encoding ); CFDictionaryRef dictEnts = MISProvisioningProfileGetEntitlements(Prof); CFStringRef cfsTeamID = MISProvisioningProfileGetTeamIdentifier(Prof); const char *teamID = CFStringGetCStringPtr (cfsTeamID , // CFStringRef theString, kCFStringEncodingUTF8); // CFStringEncoding encoding ); CFArrayRef arrayDevs = MISProvisioningProfileGetProvisionedDevices(Prof); int profVersion = MISProvisioningProfileGetVersion(Prof); int mutable = MISProfileIsMutable(Prof); // TeamName is apparently not accessible from MISProvisioningProfile* APIs, // so use MISProfileGetValue(...) instead. For a list of values, q.v. MOX*I // Vol. III, Chapter 6 (AMFI), Table 6-5. CFStringRef cfsTeamName = MISProfileGetValue(Prof, CFSTR("TeamName")); const char *teamName = CFStringGetCStringPtr (cfsTeamName , // CFStringRef theString, kCFStringEncodingUTF8); // CFStringEncoding encoding ); int provisionsAll = MISProvisioningProfileProvisionsAllDevices(Prof); int localProvision = MISProvisioningProfileIsForLocalProvisioning(Prof); int appleInternal = MISProvisioningProfileIsAppleInternalProfile(Prof); // There is also a MISProvisioningProfileGrantsEntitlement, // which takes the profile and an entitlement (CFStringRef), and tells // you if it's in the entitlement array. // Dump out printf("Profile Name: %s\n", profName); printf("Version: %d\n", profVersion); printf("AppleInternal: %s\n", (appleInternal ? "yes" : "no")); // I wish, but no :-( printf("Local Provisioning: %s\n", (localProvision ? "yes" : "no")); printf("Mutable: %s\n", (mutable ? "yes" : "no")); printf("UUID: %s\n", uuid); printf("Team ID: %s\n", teamID); printf("Team Name: %s\n", teamName); printf("Entitlements:\n"); CFDictionaryApplyFunction(dictEnts, printDictKey, NULL); if (provisionsAll) { printf("Devices: all (Enterprise)\n"); } else { printf("Device list:\n"); printf("\t%s\n",dumpCFArray( arrayDevs)); } }; // dumpProf int main (int argc, char **argv) { if (argc < 2) { fprintf(stderr,"%s list - to list all installed profiles\n", getprogname()); fprintf(stderr,"%s errors - to list all error codes\n", getprogname()); fprintf(stderr,"%s validate _path_ - Validate a file signature (\"what would AMFI do?\")\n", getprogname()); exit(1); } if (strcmp(argv[1], "validate") == 0) { if (argc != 3) { fprintf(stderr, "Work with me here.. I need a path to validate!\n"); exit(1);} // MVS is stupid and returns e8008001 ("An unknown error has occurred") // when passed a null file name. Spare it the shame and check existence first. struct stat stbuf; if (stat (argv[2], &stbuf)) { fprintf(stderr,"File %s not found! Why are you wasting my time, little man?!\n", argv[2]); exit(5); } void * copiedInfo = NULL; CFMutableDictionaryRef optionsDict = CFDictionaryCreateMutable(kCFAllocatorDefault, // CFAllocatorRef allocator, 0, // CFIndex capacity &kCFTypeDictionaryKeyCallBacks, // const CFDictionaryKeyCallBacks *keyCallBacks, &kCFTypeDictionaryValueCallBacks); // const CFDictionaryValueCallBacks *valueCallBacks ); // Now, here's what AMFI really would do: #ifdef IOS_9 // In 9.x: CFDictionarySetValue(optionsDict,kMISValidationOptionRespectUppTrustAndAuthorization, kCFBooleanTrue); #endif // CFDictionarySetValue(optionsDict,kMISValidationOptionValidateSignatureOnly,kCFBooleanTrue); // CFDictionarySetValue(optionsDict,kMISValidationOptionExpectedCDHash,CFData of CDhash here..); // CFDictionarySetValue(optionsDict,kMISValidationOptionUniversalFileOffset, CFNumber...); // Me, I just try the ad-hoc validation, or defaults, which validates App store too. CFDictionarySetValue(optionsDict,kMISValidationOptionAllowAdHocSigning, kCFBooleanTrue); // $%#$%$#%# CFStrings CFStringRef FileName = CFStringCreateWithCStringNoCopy (kCFAllocatorDefault, // CFAllocatorRef alloc, argv[2], // const char *cStr, kCFStringEncodingUTF8, // CFStringEncoding encoding, kCFAllocatorDefault); // CFAllocatorRef contentsDeallocator ); // Using MISValidateSignatureAndCopyInfo because on JB devices MISValidateSignature // is re-exported to return 0 in any case... // change this to MVS if you want to check if your code signature bypass works: int rc = MISValidateSignatureAndCopyInfo(FileName, optionsDict,NULL); if (rc) { fprintf(stderr,"Error %d (0x%x) - ",rc, rc); CFShow(MISCopyErrorStringForErrorCode(rc)); } else {printf("Valid!\n"); } return (rc); } if (strcmp(argv[1], "list") == 0) { block_handler_t miscbb = ^(MISProfileRef prof) { // Stupid Blocks. dumpProfile (prof); }; int rc = MISEnumerateInstalledProvisioningProfiles (0, miscbb); if (rc !=0) { fprintf(stderr,"Err.. Something's not right?\n"); // CFShow prints to stderr anyway, so why not? CFShow(MISCopyErrorStringForErrorCode(rc)); } exit(rc); } // for list if (strcmp(argv[1], "errors") == 0) { int i =0; for (i = MIS_ERROR_BASE; i < MIS_ERROR_BASE + 30; // CopyErrorString handles 29, just to be safe, I +1 i++) { fprintf(stderr,"Error 0x%x: ", i); CFStringRef err =MISCopyErrorStringForErrorCode(i); if (err) { CFShow(err);} } exit(0); } // for error fprintf(stderr,"*Sigh*\n"); exit(1); }