#import <Foundation/Foundation.h>
#include <CommonCrypto/CommonDigest.h>
// Starts initialized to all 0xCC, total size of 88 bytes
typedef struct {
uint64_t random; // At offset 0 bytes
unsigned char token[32]; // At offset 8 bytes
unsigned char data1[6]; // At offset 40
unsigned char snStringBoardHash[16]; // At offset 46
unsigned char zeroes[16]; // At offset 62. Edit: THIS IS WRONG, see Post #91 by WFH for corrected version
unsigned char padding[10]; // At offset 78
} __attribute__((packed)) auth_info;
uint64_t generateRandomValue(void) {
// Generate two 32-bit random numbers
uint32_t high = arc4random();
arc4random_stir(); // Stir the random number generator
uint32_t low = arc4random();
// Combine into 64-bit value
uint64_t value = ((uint64_t)high << 32) | low;
// Byte swap the 64-bit value
return __builtin_bswap64(value);
}
NSData *getData1() {
// Setup initial bytes with XOR operation
// Initialize a buffer with specific values
unsigned char buffer[4] = {0x2d, 0x30, 0x32, 0};
// XOR each byte with 0x7F
for (int i = 0; i < 3; i++) {
buffer[i] ^= 0x7F;
}
// Create the registry path key
NSString *registryKey = [NSString stringWithFormat:@"%@:%s",
@"4D1EDE05-38C7-4a6a-9CC6-4BCCA8B38C14",
buffer];
// Get registry entry
io_registry_entry_t entry = IORegistryEntryFromPath(kIOMasterPortDefault,
"IODeviceTree:/options");
NSData *result = nil;
if (entry != 0) {
// Fetch property from registry
CFTypeRef property = IORegistryEntryCreateCFProperty(entry,
(__bridge CFStringRef) registryKey,
kCFAllocatorDefault,
0);
// Release the registry entry
IOObjectRelease(entry);
if (property != NULL) {
// Check if the property is of type CFData
if (CFGetTypeID(property) == CFDataGetTypeID()) {
result = (__bridge_transfer NSData *) property;
} else {
CFRelease(property);
}
}
}
return result;
}
NSString *getBoardID() {
// Get the root IOService entry
io_registry_entry_t registryEntry = IORegistryEntryFromPath(kIOMasterPortDefault,
"IOService:/");
NSString *boardID = nil;
if (registryEntry != 0) {
// Get the board-id property
CFTypeRef property = IORegistryEntryCreateCFProperty(registryEntry,
CFSTR("board-id"),
kCFAllocatorDefault,
0);
// Release the registry entry as we no longer need it
IOObjectRelease(registryEntry);
if (CFGetTypeID(property) == CFDataGetTypeID()) {
const char *bytes = (const char *)CFDataGetBytePtr((CFDataRef)property);
boardID = [NSString stringWithCString:bytes
encoding:NSUTF8StringEncoding];
} else {
CFRelease(property);
}
}
return boardID;
}
NSString *getData2() {
// Setup initial bytes and XOR them
char bytes[4] = {0x32, 0x33, 0x3d, 0}; // "23="
// XOR each byte with 0x7F
for (int i = 0; i < 3; i++) {
bytes[i] ^= 0x7F;
}
// Create the registry key
NSString *registryKey = [NSString stringWithFormat:@"%@:%s",
@"4D1EDE05-38C7-4a6a-9CC6-4BCCA8B38C14",
bytes];
// Get registry entry
io_registry_entry_t entry = IORegistryEntryFromPath(kIOMasterPortDefault,
"IODeviceTree:/options");
NSString *result = nil;
if (entry != 0) {
// Get property from registry
CFTypeRef property = IORegistryEntryCreateCFProperty(entry,
(__bridge CFStringRef)registryKey,
kCFAllocatorDefault,
0);
// Release registry entry
IOObjectRelease(entry);
if (property != NULL) {
if (CFGetTypeID(property) == CFDataGetTypeID()) {
// Convert CFData to NSData
NSData *propertyData = (__bridge_transfer NSData *)property;
// Try to deserialize as property list
NSError *error = nil;
id propertyList = [NSPropertyListSerialization
propertyListWithData:propertyData
options:0
format:NULL
error:&error];
// Check if result is a string
if ([propertyList isKindOfClass:[NSString class]]) {
result = propertyList;
}
} else {
CFRelease(property);
}
}
}
return result;
}
int main(int argc, char *argv[]) {
@autoreleasepool {
auth_info authInfo;
assert(sizeof(auth_info) == 0x58);
memset(&authInfo, 0xCC, sizeof(auth_info));
authInfo.random = generateRandomValue();
// Expect 64 hex characters (converts to 32 bytes)
unsigned char authToken[32];
// Take from session key of set-cookie header for initial request
const char *hexCString = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
for (int i = 0; i < 32; i++) {
unsigned int value;
sscanf(hexCString + i * 2, "%2x", &value);
authToken[i] = (unsigned char)value;
}
memcpy(authInfo.token, authToken, 32);
// Copy first 6 bytes of data1 into authInfo.data1
NSData *data1 = getData1();
NSLog(@"Got data1 %@", data1);
memcpy(authInfo.data1, [data1 bytes], MIN([data1 length], 6));
// Get C strings for data2 and boardID
const char *snString = [getData2() cStringUsingEncoding:NSASCIIStringEncoding];
printf("Got snString %s\n", snString);
const char *boardId = [getBoardID() cStringUsingEncoding:NSASCIIStringEncoding];
printf("Got boardId %s\n", boardId);
// Compute SHA256 hash of snString and boardId
CC_SHA256_CTX shaCtx;
CC_SHA256_Init(&shaCtx);
CC_SHA256_Update(&shaCtx, snString, (CC_LONG)strlen(snString));
CC_SHA256_Update(&shaCtx, boardId, (CC_LONG)strlen(boardId));
unsigned char hash[CC_SHA256_DIGEST_LENGTH];
CC_SHA256_Final(hash, &shaCtx);
memcpy(authInfo.snStringBoardHash, hash, 16);
memset(authInfo.zeroes, 0x0, 16);
NSString *cidString = [NSString stringWithFormat:@"%016llX", CFSwapInt64(authInfo.random)];
// Compute SHA256 hash of authInfo
unsigned char authinfo_sha256[CC_SHA256_DIGEST_LENGTH];
CC_SHA256(&authInfo, sizeof(auth_info), authinfo_sha256);
// Convert the hash to a hex string. This becomes our K value
NSMutableString *authinfoHex = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
for (int idx = 0; idx < CC_SHA256_DIGEST_LENGTH; idx++) {
[authinfoHex appendFormat:@"%02X", authinfo_sha256[idx]];
}
NSLog(@"%@", [NSString stringWithFormat:@"&cf_cid=%@sn=%sbid=%sk=%s",
cidString,
snString,
boardId,
[authinfoHex cStringUsingEncoding:NSASCIIStringEncoding]]);
}
}