mirror of https://github.com/opa334/TrollStore.git
Unfinished broken attempt at registering apps as User, I forgot about some checks in FrontBoard so this can't work and won't be continued
This commit is contained in:
parent
d922a8211c
commit
9d541fcfdc
2
Makefile
2
Makefile
|
@ -29,6 +29,8 @@ make_trollhelper_embedded:
|
||||||
|
|
||||||
assemble_trollstore:
|
assemble_trollstore:
|
||||||
@cp cert.p12 ./TrollStore/.theos/obj/TrollStore.app/cert.p12
|
@cp cert.p12 ./TrollStore/.theos/obj/TrollStore.app/cert.p12
|
||||||
|
@cp ./Victim/Victim.p12 ./TrollStore/.theos/obj/TrollStore.app/user_cert.p12
|
||||||
|
@cp ./Victim/victim ./TrollStore/.theos/obj/TrollStore.app/user_victim
|
||||||
@cp ./RootHelper/.theos/obj/trollstorehelper ./TrollStore/.theos/obj/TrollStore.app/trollstorehelper
|
@cp ./RootHelper/.theos/obj/trollstorehelper ./TrollStore/.theos/obj/TrollStore.app/trollstorehelper
|
||||||
@cp ./TrollHelper/.theos/obj/TrollStorePersistenceHelper.app/TrollStorePersistenceHelper ./TrollStore/.theos/obj/TrollStore.app/PersistenceHelper
|
@cp ./TrollHelper/.theos/obj/TrollStorePersistenceHelper.app/TrollStorePersistenceHelper ./TrollStore/.theos/obj/TrollStore.app/PersistenceHelper
|
||||||
@export COPYFILE_DISABLE=1
|
@export COPYFILE_DISABLE=1
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
pwnify:
|
pwnify:
|
||||||
@clang main.m -fobjc-arc -fmodules -mmacosx-version-min=11.0 -o pwnify
|
@clang main.m ../Shared/libPwnify.m -I../Shared -fobjc-arc -fmodules -mmacosx-version-min=11.0 -o pwnify
|
||||||
|
|
||||||
install: pwnify
|
install: pwnify
|
||||||
-@sudo rm /usr/local/bin/pwnify 2>/dev/null || true
|
-@sudo rm /usr/local/bin/pwnify 2>/dev/null || true
|
||||||
|
|
346
Pwnify/main.m
346
Pwnify/main.m
|
@ -6,91 +6,16 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <libPwnify.h>
|
||||||
|
|
||||||
#import <mach-o/loader.h>
|
#import <mach-o/loader.h>
|
||||||
#import <mach-o/fat.h>
|
#import <mach-o/fat.h>
|
||||||
#import <sys/stat.h>
|
#import <sys/stat.h>
|
||||||
|
|
||||||
#define ALIGN_DEFAULT 0xE
|
|
||||||
|
|
||||||
uint32_t roundUp(int numToRound, int multiple)
|
|
||||||
{
|
|
||||||
if (multiple == 0)
|
|
||||||
return numToRound;
|
|
||||||
|
|
||||||
int remainder = numToRound % multiple;
|
|
||||||
if (remainder == 0)
|
|
||||||
return numToRound;
|
|
||||||
|
|
||||||
return numToRound + multiple - remainder;
|
|
||||||
}
|
|
||||||
|
|
||||||
void expandFile(FILE* file, uint32_t size)
|
|
||||||
{
|
|
||||||
fseek(file, 0, SEEK_END);
|
|
||||||
if(ftell(file) >= size) return;
|
|
||||||
|
|
||||||
while(ftell(file) != size)
|
|
||||||
{
|
|
||||||
char c = 0;
|
|
||||||
fwrite(&c, 1, 1, file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void copyData(FILE* sourceFile, FILE* targetFile, size_t size)
|
|
||||||
{
|
|
||||||
for(size_t i = 0; i < size; i++)
|
|
||||||
{
|
|
||||||
char b;
|
|
||||||
fread(&b, 1, 1, sourceFile);
|
|
||||||
fwrite(&b, 1, 1, targetFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void enumerateArchs(NSString* binaryPath, void (^archEnumBlock)(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop))
|
|
||||||
{
|
|
||||||
FILE* machoFile = fopen(binaryPath.UTF8String, "rb");
|
|
||||||
if(!machoFile) return;
|
|
||||||
|
|
||||||
struct mach_header header;
|
|
||||||
fread(&header,sizeof(header),1,machoFile);
|
|
||||||
|
|
||||||
if(header.magic == FAT_MAGIC || header.magic == FAT_CIGAM)
|
|
||||||
{
|
|
||||||
fseek(machoFile,0,SEEK_SET);
|
|
||||||
struct fat_header fatHeader;
|
|
||||||
fread(&fatHeader,sizeof(fatHeader),1,machoFile);
|
|
||||||
|
|
||||||
for(int i = 0; i < OSSwapBigToHostInt32(fatHeader.nfat_arch); i++)
|
|
||||||
{
|
|
||||||
uint32_t archFileOffset = sizeof(fatHeader) + sizeof(struct fat_arch) * i;
|
|
||||||
struct fat_arch fatArch;
|
|
||||||
fseek(machoFile, archFileOffset,SEEK_SET);
|
|
||||||
fread(&fatArch,sizeof(fatArch),1,machoFile);
|
|
||||||
|
|
||||||
uint32_t sliceFileOffset = OSSwapBigToHostInt32(fatArch.offset);
|
|
||||||
struct mach_header archHeader;
|
|
||||||
fseek(machoFile, sliceFileOffset, SEEK_SET);
|
|
||||||
fread(&archHeader,sizeof(archHeader),1,machoFile);
|
|
||||||
|
|
||||||
BOOL stop = NO;
|
|
||||||
archEnumBlock(&fatArch, archFileOffset, &archHeader, sliceFileOffset, machoFile, &stop);
|
|
||||||
if(stop) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(header.magic == MH_MAGIC_64 || header.magic == MH_CIGAM_64)
|
|
||||||
{
|
|
||||||
BOOL stop;
|
|
||||||
archEnumBlock(NULL, 0, &header, 0, machoFile, &stop);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(machoFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
void printArchs(NSString* binaryPath)
|
void printArchs(NSString* binaryPath)
|
||||||
{
|
{
|
||||||
__block int i = 0;
|
__block int i = 0;
|
||||||
enumerateArchs(binaryPath, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {
|
pwnify_enumerateArchs(binaryPath, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {
|
||||||
if(arch)
|
if(arch)
|
||||||
{
|
{
|
||||||
printf("%d. fatArch type: 0x%X, subtype: 0x%X, align:0x%X, size:0x%X, offset:0x%X\n| ", i, OSSwapBigToHostInt32(arch->cputype), OSSwapBigToHostInt32(arch->cpusubtype), OSSwapBigToHostInt32(arch->align), OSSwapBigToHostInt32(arch->size), OSSwapBigToHostInt32(arch->offset));
|
printf("%d. fatArch type: 0x%X, subtype: 0x%X, align:0x%X, size:0x%X, offset:0x%X\n| ", i, OSSwapBigToHostInt32(arch->cputype), OSSwapBigToHostInt32(arch->cpusubtype), OSSwapBigToHostInt32(arch->align), OSSwapBigToHostInt32(arch->size), OSSwapBigToHostInt32(arch->offset));
|
||||||
|
@ -101,267 +26,6 @@ void printArchs(NSString* binaryPath)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void pwnify(NSString* appStoreBinary, NSString* binaryToInject, BOOL preferArm64e)
|
|
||||||
{
|
|
||||||
NSString* tmpFilePath = [NSTemporaryDirectory() stringByAppendingString:[[NSUUID UUID] UUIDString]];
|
|
||||||
|
|
||||||
// Determine amount of slices in output
|
|
||||||
__block int slicesCount = 1;
|
|
||||||
enumerateArchs(appStoreBinary, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {
|
|
||||||
slicesCount++;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Allocate FAT data
|
|
||||||
uint32_t fatDataSize = sizeof(struct fat_header) + slicesCount * sizeof(struct fat_arch);
|
|
||||||
char* fatData = malloc(fatDataSize);
|
|
||||||
|
|
||||||
// Construct new fat header
|
|
||||||
struct fat_header fatHeader;
|
|
||||||
fatHeader.magic = OSSwapHostToBigInt32(0xCAFEBABE);
|
|
||||||
fatHeader.nfat_arch = OSSwapHostToBigInt32(slicesCount);
|
|
||||||
memcpy(&fatData[0], &fatHeader, sizeof(fatHeader));
|
|
||||||
|
|
||||||
uint32_t align = pow(2, ALIGN_DEFAULT);
|
|
||||||
__block uint32_t curOffset = align;
|
|
||||||
__block uint32_t curArchIndex = 0;
|
|
||||||
|
|
||||||
// Construct new fat arch data
|
|
||||||
enumerateArchs(appStoreBinary, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {
|
|
||||||
struct fat_arch newArch;
|
|
||||||
if(arch)
|
|
||||||
{
|
|
||||||
newArch.cputype = arch->cputype;
|
|
||||||
|
|
||||||
if(OSSwapBigToHostInt32(arch->cputype) == 0x100000C)
|
|
||||||
{
|
|
||||||
newArch.cpusubtype = OSSwapHostToBigInt32(2); // SET app store binary in FAT header to 2, fixes arm64e
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newArch.cpusubtype = arch->cpusubtype;
|
|
||||||
}
|
|
||||||
|
|
||||||
newArch.size = arch->size;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newArch.cputype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cputype));
|
|
||||||
|
|
||||||
if(OSSwapLittleToHostInt32(machHeader->cputype) == 0x100000C)
|
|
||||||
{
|
|
||||||
newArch.cpusubtype = OSSwapHostToBigInt32(2); // SET app store binary in FAT header to 2, fixes arm64e
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
newArch.cpusubtype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cpusubtype));
|
|
||||||
}
|
|
||||||
|
|
||||||
newArch.size = OSSwapHostToBigInt32((uint32_t)[[[NSFileManager defaultManager] attributesOfItemAtPath:appStoreBinary error:nil] fileSize]);
|
|
||||||
}
|
|
||||||
|
|
||||||
newArch.align = OSSwapHostToBigInt32(ALIGN_DEFAULT);
|
|
||||||
newArch.offset = OSSwapHostToBigInt32(curOffset);
|
|
||||||
curOffset += roundUp(OSSwapBigToHostInt32(newArch.size), align);
|
|
||||||
|
|
||||||
memcpy(&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex], &newArch, sizeof(newArch));
|
|
||||||
curArchIndex++;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Determine what slices our injection binary contains
|
|
||||||
__block BOOL toInjectHasArm64e = NO;
|
|
||||||
__block BOOL toInjectHasArm64 = NO;
|
|
||||||
enumerateArchs(binaryToInject, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {
|
|
||||||
if(arch)
|
|
||||||
{
|
|
||||||
if(OSSwapBigToHostInt32(arch->cputype) == 0x100000C)
|
|
||||||
{
|
|
||||||
if (!((OSSwapBigToHostInt32(arch->cpusubtype) ^ 0x2) & 0xFFFFFF))
|
|
||||||
{
|
|
||||||
toInjectHasArm64e = YES;
|
|
||||||
}
|
|
||||||
else if(!((OSSwapBigToHostInt32(arch->cpusubtype) ^ 0x1) & 0xFFFFFF))
|
|
||||||
{
|
|
||||||
toInjectHasArm64 = YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(OSSwapLittleToHostInt32(machHeader->cputype) == 0x100000C)
|
|
||||||
{
|
|
||||||
if (!((OSSwapLittleToHostInt32(machHeader->cpusubtype) ^ 0x2) & 0xFFFFFF))
|
|
||||||
{
|
|
||||||
toInjectHasArm64e = YES;
|
|
||||||
}
|
|
||||||
else if(!((OSSwapLittleToHostInt32(machHeader->cpusubtype) ^ 0x1) & 0xFFFFFF))
|
|
||||||
{
|
|
||||||
toInjectHasArm64 = YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if(!toInjectHasArm64 && !preferArm64e)
|
|
||||||
{
|
|
||||||
printf("ERROR: can't proceed injection because binary to inject has no arm64 slice\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t subtypeToUse = 0x1;
|
|
||||||
if(preferArm64e && toInjectHasArm64e)
|
|
||||||
{
|
|
||||||
subtypeToUse = 0x2;
|
|
||||||
}
|
|
||||||
|
|
||||||
enumerateArchs(binaryToInject, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {
|
|
||||||
struct fat_arch currentArch;
|
|
||||||
if(arch)
|
|
||||||
{
|
|
||||||
currentArch.cputype = arch->cputype;
|
|
||||||
currentArch.cpusubtype = arch->cpusubtype;
|
|
||||||
currentArch.size = arch->size;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentArch.cputype = OSSwapHostToBigInt(OSSwapLittleToHostInt32(machHeader->cputype));
|
|
||||||
currentArch.cpusubtype = OSSwapHostToBigInt(OSSwapLittleToHostInt32(machHeader->cpusubtype));
|
|
||||||
currentArch.size = OSSwapHostToBigInt((uint32_t)[[[NSFileManager defaultManager] attributesOfItemAtPath:binaryToInject error:nil] fileSize]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(OSSwapBigToHostInt32(currentArch.cputype) == 0x100000C)
|
|
||||||
{
|
|
||||||
if (!((OSSwapBigToHostInt32(currentArch.cpusubtype) ^ subtypeToUse) & 0xFFFFFF))
|
|
||||||
{
|
|
||||||
currentArch.align = OSSwapHostToBigInt32(ALIGN_DEFAULT);
|
|
||||||
currentArch.offset = OSSwapHostToBigInt32(curOffset);
|
|
||||||
curOffset += roundUp(OSSwapBigToHostInt32(currentArch.size), align);
|
|
||||||
memcpy(&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex], ¤tArch, sizeof(currentArch));
|
|
||||||
curArchIndex++;
|
|
||||||
*stop = YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// FAT Header constructed, now write to file and then write the slices themselves
|
|
||||||
|
|
||||||
FILE* tmpFile = fopen(tmpFilePath.UTF8String, "wb");
|
|
||||||
fwrite(&fatData[0], fatDataSize, 1, tmpFile);
|
|
||||||
|
|
||||||
curArchIndex = 0;
|
|
||||||
enumerateArchs(appStoreBinary, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {
|
|
||||||
struct fat_arch* toWriteArch = (struct fat_arch*)&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex];
|
|
||||||
|
|
||||||
expandFile(tmpFile, OSSwapBigToHostInt32(toWriteArch->offset));
|
|
||||||
|
|
||||||
uint32_t offset = 0;
|
|
||||||
uint32_t size = 0;
|
|
||||||
|
|
||||||
if(arch)
|
|
||||||
{
|
|
||||||
offset = OSSwapBigToHostInt32(arch->offset);
|
|
||||||
size = OSSwapBigToHostInt32(arch->size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size = OSSwapBigToHostInt32(toWriteArch->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE* appStoreBinaryFile = fopen(appStoreBinary.UTF8String, "rb");
|
|
||||||
fseek(appStoreBinaryFile, offset, SEEK_SET);
|
|
||||||
copyData(appStoreBinaryFile, tmpFile, size);
|
|
||||||
fclose(appStoreBinaryFile);
|
|
||||||
curArchIndex++;
|
|
||||||
});
|
|
||||||
|
|
||||||
struct fat_arch* toWriteArch = (struct fat_arch*)&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex];
|
|
||||||
enumerateArchs(binaryToInject, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {
|
|
||||||
struct fat_arch currentArch;
|
|
||||||
if(arch)
|
|
||||||
{
|
|
||||||
currentArch.cputype = arch->cputype;
|
|
||||||
currentArch.cpusubtype = arch->cpusubtype;
|
|
||||||
currentArch.size = arch->size;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentArch.cputype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cputype));
|
|
||||||
currentArch.cpusubtype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cpusubtype));
|
|
||||||
currentArch.size = OSSwapHostToBigInt32((uint32_t)[[[NSFileManager defaultManager] attributesOfItemAtPath:binaryToInject error:nil] fileSize]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(OSSwapBigToHostInt32(currentArch.cputype) == 0x100000C)
|
|
||||||
{
|
|
||||||
if (!((OSSwapBigToHostInt32(currentArch.cpusubtype) ^ subtypeToUse) & 0xFFFFFF))
|
|
||||||
{
|
|
||||||
expandFile(tmpFile, OSSwapBigToHostInt32(toWriteArch->offset));
|
|
||||||
|
|
||||||
uint32_t offset = 0;
|
|
||||||
uint32_t size = 0;
|
|
||||||
|
|
||||||
if(arch)
|
|
||||||
{
|
|
||||||
offset = OSSwapBigToHostInt32(arch->offset);
|
|
||||||
size = OSSwapBigToHostInt32(arch->size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size = OSSwapBigToHostInt32(toWriteArch->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE* binaryToInjectFile = fopen(binaryToInject.UTF8String, "rb");
|
|
||||||
fseek(binaryToInjectFile, offset, SEEK_SET);
|
|
||||||
copyData(binaryToInjectFile, tmpFile, size);
|
|
||||||
fclose(binaryToInjectFile);
|
|
||||||
*stop = YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
fclose(tmpFile);
|
|
||||||
chmod(tmpFilePath.UTF8String, 0755);
|
|
||||||
|
|
||||||
[[NSFileManager defaultManager] removeItemAtPath:appStoreBinary error:nil];
|
|
||||||
[[NSFileManager defaultManager] moveItemAtPath:tmpFilePath toPath:appStoreBinary error:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCPUSubtype(NSString* binaryPath, uint32_t subtype)
|
|
||||||
{
|
|
||||||
FILE* binaryFile = fopen(binaryPath.UTF8String, "rb+");
|
|
||||||
if(!binaryFile)
|
|
||||||
{
|
|
||||||
printf("ERROR: File not found\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
enumerateArchs(binaryPath, ^(struct fat_arch *arch, uint32_t archFileOffset, struct mach_header *machHeader, uint32_t sliceFileOffset, FILE *file, BOOL *stop) {
|
|
||||||
|
|
||||||
if(arch)
|
|
||||||
{
|
|
||||||
if(OSSwapBigToHostInt(arch->cputype) == 0x100000C)
|
|
||||||
{
|
|
||||||
if(OSSwapBigToHostInt(arch->cpusubtype) == 0x0)
|
|
||||||
{
|
|
||||||
arch->cpusubtype = OSSwapHostToBigInt32(subtype);
|
|
||||||
fseek(binaryFile, archFileOffset, SEEK_SET);
|
|
||||||
fwrite(arch, sizeof(struct fat_arch), 1, binaryFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(OSSwapLittleToHostInt32(machHeader->cputype) == 0x100000C)
|
|
||||||
{
|
|
||||||
if(OSSwapLittleToHostInt32(machHeader->cpusubtype) == 0x0)
|
|
||||||
{
|
|
||||||
machHeader->cpusubtype = OSSwapHostToLittleInt32(subtype);
|
|
||||||
fseek(binaryFile, sliceFileOffset, SEEK_SET);
|
|
||||||
fwrite(machHeader, sizeof(struct mach_header), 1, binaryFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
fclose(binaryFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
void printUsageAndExit(void)
|
void printUsageAndExit(void)
|
||||||
{
|
{
|
||||||
printf("Usage:\n\nPrint architectures of a binary:\npwnify print <path/to/binary>\n\nInject target slice into victim binary:\npwnify pwn(64e) <path/to/victim/binary> <path/to/target/binary>\n\nModify cpusubtype of a non FAT binary:\npwnify set-cpusubtype <path/to/binary> <cpusubtype>\n");
|
printf("Usage:\n\nPrint architectures of a binary:\npwnify print <path/to/binary>\n\nInject target slice into victim binary:\npwnify pwn(64e) <path/to/victim/binary> <path/to/target/binary>\n\nModify cpusubtype of a non FAT binary:\npwnify set-cpusubtype <path/to/binary> <cpusubtype>\n");
|
||||||
|
@ -387,14 +51,14 @@ int main(int argc, const char * argv[]) {
|
||||||
if(argc < 4) printUsageAndExit();
|
if(argc < 4) printUsageAndExit();
|
||||||
NSString* victimBinary = [NSString stringWithUTF8String:argv[2]];
|
NSString* victimBinary = [NSString stringWithUTF8String:argv[2]];
|
||||||
NSString* targetBinary = [NSString stringWithUTF8String:argv[3]];
|
NSString* targetBinary = [NSString stringWithUTF8String:argv[3]];
|
||||||
pwnify(victimBinary, targetBinary, NO);
|
pwnify(victimBinary, targetBinary, NO, NO);
|
||||||
}
|
}
|
||||||
else if([operation isEqualToString:@"pwn64e"])
|
else if([operation isEqualToString:@"pwn64e"])
|
||||||
{
|
{
|
||||||
if(argc < 4) printUsageAndExit();
|
if(argc < 4) printUsageAndExit();
|
||||||
NSString* victimBinary = [NSString stringWithUTF8String:argv[2]];
|
NSString* victimBinary = [NSString stringWithUTF8String:argv[2]];
|
||||||
NSString* targetBinary = [NSString stringWithUTF8String:argv[3]];
|
NSString* targetBinary = [NSString stringWithUTF8String:argv[3]];
|
||||||
pwnify(victimBinary, targetBinary, YES);
|
pwnify(victimBinary, targetBinary, YES, NO);
|
||||||
}
|
}
|
||||||
else if([operation isEqualToString:@"set-cpusubtype"])
|
else if([operation isEqualToString:@"set-cpusubtype"])
|
||||||
{
|
{
|
||||||
|
@ -406,7 +70,7 @@ int main(int argc, const char * argv[]) {
|
||||||
f.numberStyle = NSNumberFormatterDecimalStyle;
|
f.numberStyle = NSNumberFormatterDecimalStyle;
|
||||||
NSNumber* subtypeToSetNum = [f numberFromString:subtypeToSet];
|
NSNumber* subtypeToSetNum = [f numberFromString:subtypeToSet];
|
||||||
|
|
||||||
setCPUSubtype(binaryToModify, [subtypeToSetNum unsignedIntValue]);
|
pwnify_setCPUSubtype(binaryToModify, [subtypeToSetNum unsignedIntValue]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#import <objc/runtime.h>
|
#import <objc/runtime.h>
|
||||||
#import <TSUtil.h>
|
#import <TSUtil.h>
|
||||||
#import <sys/utsname.h>
|
#import <sys/utsname.h>
|
||||||
|
#import <libPwnify.h>
|
||||||
|
|
||||||
#import <SpringBoardServices/SpringBoardServices.h>
|
#import <SpringBoardServices/SpringBoardServices.h>
|
||||||
#import <Security/Security.h>
|
#import <Security/Security.h>
|
||||||
|
@ -414,11 +415,13 @@ BOOL codeCertChainContainsFakeAppStoreExtensions(SecStaticCodeRef codeRef)
|
||||||
return evaluatesToCustomAnchor;
|
return evaluatesToCustomAnchor;
|
||||||
}
|
}
|
||||||
|
|
||||||
int signApp(NSString* appPath)
|
int signApp(NSString* appPath, BOOL useUserVictimCert)
|
||||||
{
|
{
|
||||||
NSDictionary* appInfoDict = infoDictionaryForAppPath(appPath);
|
NSDictionary* appInfoDict = infoDictionaryForAppPath(appPath);
|
||||||
if(!appInfoDict) return 172;
|
if(!appInfoDict) return 172;
|
||||||
|
|
||||||
|
NSString* appId = appIdForAppPath(appPath);
|
||||||
|
|
||||||
NSString* executablePath = appMainExecutablePathForAppPath(appPath);
|
NSString* executablePath = appMainExecutablePathForAppPath(appPath);
|
||||||
if(!executablePath) return 176;
|
if(!executablePath) return 176;
|
||||||
|
|
||||||
|
@ -454,7 +457,9 @@ int signApp(NSString* appPath)
|
||||||
|
|
||||||
if(!isLdidInstalled()) return 173;
|
if(!isLdidInstalled()) return 173;
|
||||||
|
|
||||||
NSString* certPath = [trollStoreAppPath() stringByAppendingPathComponent:@"cert.p12"];
|
NSString* tsPath = [appId isEqualToString:@"com.opa334.TrollStore"] ? appPath : trollStoreAppPath();
|
||||||
|
NSString* certName = useUserVictimCert ? @"user_cert.p12" : @"cert.p12";
|
||||||
|
NSString* certPath = [tsPath stringByAppendingPathComponent:certName];
|
||||||
NSString* certArg = [@"-K" stringByAppendingPathComponent:certPath];
|
NSString* certArg = [@"-K" stringByAppendingPathComponent:certPath];
|
||||||
NSString* errorOutput;
|
NSString* errorOutput;
|
||||||
int ldidRet;
|
int ldidRet;
|
||||||
|
@ -462,9 +467,9 @@ int signApp(NSString* appPath)
|
||||||
NSDictionary* entitlements = dumpEntitlements(codeRef);
|
NSDictionary* entitlements = dumpEntitlements(codeRef);
|
||||||
CFRelease(codeRef);
|
CFRelease(codeRef);
|
||||||
|
|
||||||
if(!entitlements)
|
if(!entitlements || entitlements.count == 0)
|
||||||
{
|
{
|
||||||
NSLog(@"app main binary has no entitlements, signing app with fallback entitlements...");
|
NSLog(@"[signApp] app main binary has no entitlements, signing app with fallback entitlements...");
|
||||||
// app has no entitlements, sign with fallback entitlements
|
// app has no entitlements, sign with fallback entitlements
|
||||||
NSString* entitlementPath = [trollStoreAppPath() stringByAppendingPathComponent:@"fallback.entitlements"];
|
NSString* entitlementPath = [trollStoreAppPath() stringByAppendingPathComponent:@"fallback.entitlements"];
|
||||||
NSString* entitlementArg = [@"-S" stringByAppendingString:entitlementPath];
|
NSString* entitlementArg = [@"-S" stringByAppendingString:entitlementPath];
|
||||||
|
@ -501,6 +506,7 @@ int signApp(NSString* appPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSLog(@"[signApp] running ldid with cert arg: %@", certArg);
|
||||||
// app has entitlements, keep them
|
// app has entitlements, keep them
|
||||||
ldidRet = runLdid(@[@"-s", certArg, appPath], nil, &errorOutput);
|
ldidRet = runLdid(@[@"-s", certArg, appPath], nil, &errorOutput);
|
||||||
|
|
||||||
|
@ -514,7 +520,7 @@ int signApp(NSString* appPath)
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
NSLog(@"ldid exited with status %d", ldidRet);
|
NSLog(@"[signApp] ldid exited with status %d", ldidRet);
|
||||||
|
|
||||||
NSLog(@"- ldid error output start -");
|
NSLog(@"- ldid error output start -");
|
||||||
|
|
||||||
|
@ -586,12 +592,46 @@ int installApp(NSString* appPath, BOOL sign, BOOL force)
|
||||||
|
|
||||||
applyPatchesToInfoDictionary(appPath);
|
applyPatchesToInfoDictionary(appPath);
|
||||||
|
|
||||||
if(sign)
|
NSString* tsPath = [appId isEqualToString:@"com.opa334.TrollStore"] ? appPath : trollStoreAppPath();
|
||||||
|
NSString* userVictim = [tsPath stringByAppendingPathComponent:@"user_victim"];
|
||||||
|
BOOL pwn = pwnifyArm64Works() && [[NSFileManager defaultManager] fileExistsAtPath:userVictim];
|
||||||
|
|
||||||
|
if(pwn)
|
||||||
{
|
{
|
||||||
int signRet = signApp(appPath);
|
NSDictionary* infoDictionary = infoDictionaryForAppPath(appPath);
|
||||||
|
NSNumber* forceLegacySystemInstall = infoDictionary[@"TSForceLegacySystemInstall"];
|
||||||
|
BOOL forceLegacySystemInstallB = NO;
|
||||||
|
if(forceLegacySystemInstall && [forceLegacySystemInstall isKindOfClass:NSNumber.class])
|
||||||
|
{
|
||||||
|
forceLegacySystemInstallB = forceLegacySystemInstall.boolValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: If target app is old TrollStore version (<2.0), don't pwn to support downgrades
|
||||||
|
|
||||||
|
if(forceLegacySystemInstallB)
|
||||||
|
{
|
||||||
|
pwn = NO;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NSString* mainExecutablePath = appMainExecutablePathForAppPath(appPath);
|
||||||
|
pwnify_setCPUSubtype(mainExecutablePath, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sign || pwn)
|
||||||
|
{
|
||||||
|
int signRet = signApp(appPath, pwn);
|
||||||
if(signRet != 0) return signRet;
|
if(signRet != 0) return signRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(pwn)
|
||||||
|
{
|
||||||
|
NSString* mainExecutablePath = appMainExecutablePathForAppPath(appPath);
|
||||||
|
pwnify(userVictim, mainExecutablePath, NO, YES);
|
||||||
|
NSLog(@"[installApp] pwnifying %@", appId);
|
||||||
|
}
|
||||||
|
|
||||||
BOOL existed;
|
BOOL existed;
|
||||||
NSError* mcmError;
|
NSError* mcmError;
|
||||||
MCMAppContainer* appContainer = [objc_getClass("MCMAppContainer") containerWithIdentifier:appId createIfNecessary:YES existed:&existed error:&mcmError];
|
MCMAppContainer* appContainer = [objc_getClass("MCMAppContainer") containerWithIdentifier:appId createIfNecessary:YES existed:&existed error:&mcmError];
|
||||||
|
@ -961,10 +1001,12 @@ void refreshAppRegistrations()
|
||||||
|
|
||||||
for(NSString* appPath in trollStoreInstalledAppBundlePaths())
|
for(NSString* appPath in trollStoreInstalledAppBundlePaths())
|
||||||
{
|
{
|
||||||
//registerPath((char*)appPath.UTF8String, 1);
|
if(!isAppPathPwnifySigned(appPath))
|
||||||
|
{
|
||||||
registerPath((char*)appPath.UTF8String, 0);
|
registerPath((char*)appPath.UTF8String, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BOOL _installPersistenceHelper(LSApplicationProxy* appProxy, NSString* sourcePersistenceHelper, NSString* sourceRootHelper)
|
BOOL _installPersistenceHelper(LSApplicationProxy* appProxy, NSString* sourcePersistenceHelper, NSString* sourceRootHelper)
|
||||||
{
|
{
|
||||||
|
@ -1128,12 +1170,7 @@ int MAIN_NAME(int argc, char *argv[], char *envp[])
|
||||||
if(argc <= 2) return -3;
|
if(argc <= 2) return -3;
|
||||||
NSString* appId = [NSString stringWithUTF8String:argv[2]];
|
NSString* appId = [NSString stringWithUTF8String:argv[2]];
|
||||||
ret = uninstallAppById(appId);
|
ret = uninstallAppById(appId);
|
||||||
} /*else if([cmd isEqualToString:@"detach"])
|
} else if([cmd isEqualToString:@"uninstall-path"])
|
||||||
{
|
|
||||||
if(argc <= 2) return -3;
|
|
||||||
NSString* appId = [NSString stringWithUTF8String:argv[2]];
|
|
||||||
ret = detachApp(appId);
|
|
||||||
} */else if([cmd isEqualToString:@"uninstall-path"])
|
|
||||||
{
|
{
|
||||||
if(argc <= 2) return -3;
|
if(argc <= 2) return -3;
|
||||||
NSString* appPath = [NSString stringWithUTF8String:argv[2]];
|
NSString* appPath = [NSString stringWithUTF8String:argv[2]];
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#import "CoreServices.h"
|
#import "CoreServices.h"
|
||||||
#import <objc/runtime.h>
|
#import <objc/runtime.h>
|
||||||
#import "dlfcn.h"
|
#import "dlfcn.h"
|
||||||
|
#import <TSUtil.h>
|
||||||
|
|
||||||
// uicache on steroids
|
// uicache on steroids
|
||||||
|
|
||||||
|
@ -126,7 +127,17 @@ void registerPath(char* cPath, int unregister)
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
|
|
||||||
|
if(isBinaryPwnifySigned(appExecutablePath))
|
||||||
|
{
|
||||||
|
NSLog(@"[uicache] doing User registration because app %@ appears to be pwnified", appBundleID);
|
||||||
|
dictToRegister[@"ApplicationType"] = @"User";
|
||||||
|
dictToRegister[@"IsDeletable"] = @1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
dictToRegister[@"ApplicationType"] = @"System";
|
dictToRegister[@"ApplicationType"] = @"System";
|
||||||
|
dictToRegister[@"IsDeletable"] = @0;
|
||||||
|
}
|
||||||
dictToRegister[@"CFBundleIdentifier"] = appBundleID;
|
dictToRegister[@"CFBundleIdentifier"] = appBundleID;
|
||||||
dictToRegister[@"CodeInfoIdentifier"] = appBundleID;
|
dictToRegister[@"CodeInfoIdentifier"] = appBundleID;
|
||||||
dictToRegister[@"CompatibilityState"] = @0;
|
dictToRegister[@"CompatibilityState"] = @0;
|
||||||
|
@ -135,7 +146,6 @@ void registerPath(char* cPath, int unregister)
|
||||||
dictToRegister[@"Container"] = containerPath;
|
dictToRegister[@"Container"] = containerPath;
|
||||||
dictToRegister[@"EnvironmentVariables"] = constructEnvironmentVariablesForContainerPath(containerPath);
|
dictToRegister[@"EnvironmentVariables"] = constructEnvironmentVariablesForContainerPath(containerPath);
|
||||||
}
|
}
|
||||||
dictToRegister[@"IsDeletable"] = @0;
|
|
||||||
dictToRegister[@"Path"] = path;
|
dictToRegister[@"Path"] = path;
|
||||||
dictToRegister[@"IsContainerized"] = @(constructContainerizationForEntitlements(entitlements));
|
dictToRegister[@"IsContainerized"] = @(constructContainerizationForEntitlements(entitlements));
|
||||||
dictToRegister[@"SignerOrganization"] = @"Apple Inc.";
|
dictToRegister[@"SignerOrganization"] = @"Apple Inc.";
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
@interface LSApplicationProxy : LSBundleProxy
|
@interface LSApplicationProxy : LSBundleProxy
|
||||||
+ (instancetype)applicationProxyForIdentifier:(NSString*)identifier;
|
+ (instancetype)applicationProxyForIdentifier:(NSString*)identifier;
|
||||||
|
+ (instancetype)applicationProxyForBundleURL:(NSURL*)bundleURL;
|
||||||
@property NSURL* bundleURL;
|
@property NSURL* bundleURL;
|
||||||
@property NSString* bundleType;
|
@property NSString* bundleType;
|
||||||
@property NSString* canonicalExecutablePath;
|
@property NSString* canonicalExecutablePath;
|
||||||
|
|
|
@ -24,3 +24,9 @@ typedef enum
|
||||||
} PERSISTENCE_HELPER_TYPE;
|
} PERSISTENCE_HELPER_TYPE;
|
||||||
|
|
||||||
extern LSApplicationProxy* findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE allowedTypes);
|
extern LSApplicationProxy* findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE allowedTypes);
|
||||||
|
extern BOOL pwnifyArm64Works(void);
|
||||||
|
extern BOOL pwnifyArm64eWorks(void);
|
||||||
|
extern BOOL isBinaryPwnifySigned(NSString* executablePath);
|
||||||
|
BOOL isAppPwnifySigned(LSApplicationProxy* appProxy);
|
||||||
|
extern BOOL isAppPathPwnifySigned(NSString* appPath);
|
||||||
|
extern BOOL isAppIdPwnifySigned(NSString* appId);
|
|
@ -3,6 +3,8 @@
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <spawn.h>
|
#import <spawn.h>
|
||||||
#import <sys/sysctl.h>
|
#import <sys/sysctl.h>
|
||||||
|
#import "libPwnify.h"
|
||||||
|
#import "CoreServices.h"
|
||||||
|
|
||||||
@interface PSAppDataUsagePolicyCache : NSObject
|
@interface PSAppDataUsagePolicyCache : NSObject
|
||||||
+ (instancetype)sharedInstance;
|
+ (instancetype)sharedInstance;
|
||||||
|
@ -344,3 +346,57 @@ LSApplicationProxy* findPersistenceHelperApp(PERSISTENCE_HELPER_TYPE allowedType
|
||||||
|
|
||||||
return outProxy;
|
return outProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: actually do checks (try to run both binaries)
|
||||||
|
|
||||||
|
BOOL pwnifyArm64Works(void)
|
||||||
|
{
|
||||||
|
static BOOL _pwnifyArm64Works = NO;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once (&onceToken, ^{
|
||||||
|
_pwnifyArm64Works = YES;
|
||||||
|
});
|
||||||
|
return _pwnifyArm64Works;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL pwnifyArm64eWorks(void)
|
||||||
|
{
|
||||||
|
static BOOL _pwnifyArm64eWorks = NO;
|
||||||
|
static dispatch_once_t onceToken;
|
||||||
|
dispatch_once (&onceToken, ^{
|
||||||
|
_pwnifyArm64eWorks = YES;
|
||||||
|
});
|
||||||
|
return _pwnifyArm64eWorks;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL isBinaryPwnifySigned(NSString* executablePath)
|
||||||
|
{
|
||||||
|
pwnify_state executablePwnifyState = pwnifyGetBinaryState(executablePath);
|
||||||
|
return ((executablePwnifyState == PWNIFY_STATE_PWNIFIED_ARM64 && pwnifyArm64Works()) || (executablePwnifyState == PWNIFY_STATE_PWNIFIED_ARM64E && pwnifyArm64eWorks()));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL isAppPwnifySigned(LSApplicationProxy* appProxy)
|
||||||
|
{
|
||||||
|
NSString* executablePath = appProxy.canonicalExecutablePath;
|
||||||
|
NSString* bundlePath = appProxy.bundleURL.path;
|
||||||
|
if(!executablePath)
|
||||||
|
{
|
||||||
|
NSBundle* appBundle = [NSBundle bundleWithPath:bundlePath];
|
||||||
|
executablePath = [bundlePath stringByAppendingPathComponent:[appBundle objectForInfoDictionaryKey:@"CFBundleExecutable"]];
|
||||||
|
}
|
||||||
|
return isBinaryPwnifySigned(executablePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL isAppPathPwnifySigned(NSString* appPath)
|
||||||
|
{
|
||||||
|
if(!appPath) return NO;
|
||||||
|
LSApplicationProxy* appProxy = [LSApplicationProxy applicationProxyForBundleURL:[NSURL fileURLWithPath:appPath]];
|
||||||
|
return isAppPwnifySigned(appProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOL isAppIdPwnifySigned(NSString* appId)
|
||||||
|
{
|
||||||
|
if(!appId) return NO;
|
||||||
|
LSApplicationProxy* appProxy = [LSApplicationProxy applicationProxyForIdentifier:appId];
|
||||||
|
return isAppPwnifySigned(appProxy);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#import <mach-o/loader.h>
|
||||||
|
#import <mach-o/fat.h>
|
||||||
|
#import <sys/stat.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
PWNIFY_STATE_NOT_PWNIFIED,
|
||||||
|
PWNIFY_STATE_PWNIFIED_ARM64,
|
||||||
|
PWNIFY_STATE_PWNIFIED_ARM64E
|
||||||
|
} pwnify_state;
|
||||||
|
|
||||||
|
|
||||||
|
extern void pwnify_enumerateArchs(NSString* binaryPath, void (^archEnumBlock)(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop));
|
||||||
|
extern void pwnify_setCPUSubtype(NSString* binaryPath, uint32_t subtype);
|
||||||
|
extern void pwnify(NSString* appStoreBinary, NSString* binaryToInject, BOOL preferArm64e, BOOL replaceBinaryToInject);
|
||||||
|
extern pwnify_state pwnifyGetBinaryState(NSString* binaryToCheck);
|
|
@ -0,0 +1,391 @@
|
||||||
|
#import "libPwnify.h"
|
||||||
|
|
||||||
|
#define ALIGN_DEFAULT 0xE
|
||||||
|
|
||||||
|
uint32_t roundUp(int numToRound, int multiple)
|
||||||
|
{
|
||||||
|
if (multiple == 0)
|
||||||
|
return numToRound;
|
||||||
|
|
||||||
|
int remainder = numToRound % multiple;
|
||||||
|
if (remainder == 0)
|
||||||
|
return numToRound;
|
||||||
|
|
||||||
|
return numToRound + multiple - remainder;
|
||||||
|
}
|
||||||
|
|
||||||
|
void expandFile(FILE* file, uint32_t size)
|
||||||
|
{
|
||||||
|
fseek(file, 0, SEEK_END);
|
||||||
|
if(ftell(file) >= size) return;
|
||||||
|
|
||||||
|
while(ftell(file) != size)
|
||||||
|
{
|
||||||
|
char c = 0;
|
||||||
|
fwrite(&c, 1, 1, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void copyData(FILE* sourceFile, FILE* targetFile, size_t size)
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
char b;
|
||||||
|
fread(&b, 1, 1, sourceFile);
|
||||||
|
fwrite(&b, 1, 1, targetFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pwnify_enumerateArchs(NSString* binaryPath, void (^archEnumBlock)(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop))
|
||||||
|
{
|
||||||
|
FILE* machoFile = fopen(binaryPath.UTF8String, "rb");
|
||||||
|
if(!machoFile) return;
|
||||||
|
|
||||||
|
struct mach_header header;
|
||||||
|
fread(&header,sizeof(header),1,machoFile);
|
||||||
|
|
||||||
|
if(header.magic == FAT_MAGIC || header.magic == FAT_CIGAM)
|
||||||
|
{
|
||||||
|
fseek(machoFile,0,SEEK_SET);
|
||||||
|
struct fat_header fatHeader;
|
||||||
|
fread(&fatHeader,sizeof(fatHeader),1,machoFile);
|
||||||
|
|
||||||
|
for(int i = 0; i < OSSwapBigToHostInt32(fatHeader.nfat_arch); i++)
|
||||||
|
{
|
||||||
|
uint32_t archFileOffset = sizeof(fatHeader) + sizeof(struct fat_arch) * i;
|
||||||
|
struct fat_arch fatArch;
|
||||||
|
fseek(machoFile, archFileOffset,SEEK_SET);
|
||||||
|
fread(&fatArch,sizeof(fatArch),1,machoFile);
|
||||||
|
|
||||||
|
uint32_t sliceFileOffset = OSSwapBigToHostInt32(fatArch.offset);
|
||||||
|
struct mach_header archHeader;
|
||||||
|
fseek(machoFile, sliceFileOffset, SEEK_SET);
|
||||||
|
fread(&archHeader,sizeof(archHeader),1,machoFile);
|
||||||
|
|
||||||
|
BOOL stop = NO;
|
||||||
|
archEnumBlock(&fatArch, archFileOffset, &archHeader, sliceFileOffset, machoFile, &stop);
|
||||||
|
if(stop) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(header.magic == MH_MAGIC_64 || header.magic == MH_CIGAM_64)
|
||||||
|
{
|
||||||
|
BOOL stop;
|
||||||
|
archEnumBlock(NULL, 0, &header, 0, machoFile, &stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(machoFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pwnify_setCPUSubtype(NSString* binaryPath, uint32_t subtype)
|
||||||
|
{
|
||||||
|
FILE* binaryFile = fopen(binaryPath.UTF8String, "rb+");
|
||||||
|
if(!binaryFile)
|
||||||
|
{
|
||||||
|
printf("ERROR: File not found\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pwnify_enumerateArchs(binaryPath, ^(struct fat_arch *arch, uint32_t archFileOffset, struct mach_header *machHeader, uint32_t sliceFileOffset, FILE *file, BOOL *stop) {
|
||||||
|
|
||||||
|
if(arch)
|
||||||
|
{
|
||||||
|
if(OSSwapBigToHostInt(arch->cputype) == 0x100000C)
|
||||||
|
{
|
||||||
|
if(OSSwapBigToHostInt(arch->cpusubtype) == 0x0)
|
||||||
|
{
|
||||||
|
arch->cpusubtype = OSSwapHostToBigInt32(subtype);
|
||||||
|
fseek(binaryFile, archFileOffset, SEEK_SET);
|
||||||
|
fwrite(arch, sizeof(struct fat_arch), 1, binaryFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(OSSwapLittleToHostInt32(machHeader->cputype) == 0x100000C)
|
||||||
|
{
|
||||||
|
if(OSSwapLittleToHostInt32(machHeader->cpusubtype) == 0x0)
|
||||||
|
{
|
||||||
|
machHeader->cpusubtype = OSSwapHostToLittleInt32(subtype);
|
||||||
|
fseek(binaryFile, sliceFileOffset, SEEK_SET);
|
||||||
|
fwrite(machHeader, sizeof(struct mach_header), 1, binaryFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fclose(binaryFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pwnify(NSString* appStoreBinary, NSString* binaryToInject, BOOL preferArm64e, BOOL replaceBinaryToInject)
|
||||||
|
{
|
||||||
|
NSString* tmpFilePath = [NSTemporaryDirectory() stringByAppendingString:[[NSUUID UUID] UUIDString]];
|
||||||
|
|
||||||
|
// Determine amount of slices in output
|
||||||
|
__block int slicesCount = 1;
|
||||||
|
pwnify_enumerateArchs(appStoreBinary, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {
|
||||||
|
slicesCount++;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Allocate FAT data
|
||||||
|
uint32_t fatDataSize = sizeof(struct fat_header) + slicesCount * sizeof(struct fat_arch);
|
||||||
|
char* fatData = malloc(fatDataSize);
|
||||||
|
|
||||||
|
// Construct new fat header
|
||||||
|
struct fat_header fatHeader;
|
||||||
|
fatHeader.magic = OSSwapHostToBigInt32(0xCAFEBABE);
|
||||||
|
fatHeader.nfat_arch = OSSwapHostToBigInt32(slicesCount);
|
||||||
|
memcpy(&fatData[0], &fatHeader, sizeof(fatHeader));
|
||||||
|
|
||||||
|
uint32_t align = pow(2, ALIGN_DEFAULT);
|
||||||
|
__block uint32_t curOffset = align;
|
||||||
|
__block uint32_t curArchIndex = 0;
|
||||||
|
|
||||||
|
// Construct new fat arch data
|
||||||
|
pwnify_enumerateArchs(appStoreBinary, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {
|
||||||
|
struct fat_arch newArch;
|
||||||
|
if(arch)
|
||||||
|
{
|
||||||
|
newArch.cputype = arch->cputype;
|
||||||
|
|
||||||
|
if(OSSwapBigToHostInt32(arch->cputype) == 0x100000C)
|
||||||
|
{
|
||||||
|
newArch.cpusubtype = OSSwapHostToBigInt32(2); // SET app store binary in FAT header to 2, fixes arm64e
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newArch.cpusubtype = arch->cpusubtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
newArch.size = arch->size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newArch.cputype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cputype));
|
||||||
|
|
||||||
|
if(OSSwapLittleToHostInt32(machHeader->cputype) == 0x100000C)
|
||||||
|
{
|
||||||
|
newArch.cpusubtype = OSSwapHostToBigInt32(2); // SET app store binary in FAT header to 2, fixes arm64e
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newArch.cpusubtype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cpusubtype));
|
||||||
|
}
|
||||||
|
|
||||||
|
newArch.size = OSSwapHostToBigInt32((uint32_t)[[[NSFileManager defaultManager] attributesOfItemAtPath:appStoreBinary error:nil] fileSize]);
|
||||||
|
}
|
||||||
|
|
||||||
|
newArch.align = OSSwapHostToBigInt32(ALIGN_DEFAULT);
|
||||||
|
newArch.offset = OSSwapHostToBigInt32(curOffset);
|
||||||
|
curOffset += roundUp(OSSwapBigToHostInt32(newArch.size), align);
|
||||||
|
|
||||||
|
memcpy(&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex], &newArch, sizeof(newArch));
|
||||||
|
curArchIndex++;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Determine what slices our injection binary contains
|
||||||
|
__block BOOL toInjectHasArm64e = NO;
|
||||||
|
__block BOOL toInjectHasArm64 = NO;
|
||||||
|
pwnify_enumerateArchs(binaryToInject, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {
|
||||||
|
if(arch)
|
||||||
|
{
|
||||||
|
if(OSSwapBigToHostInt32(arch->cputype) == 0x100000C)
|
||||||
|
{
|
||||||
|
if (!((OSSwapBigToHostInt32(arch->cpusubtype) ^ 0x2) & 0xFFFFFF))
|
||||||
|
{
|
||||||
|
toInjectHasArm64e = YES;
|
||||||
|
}
|
||||||
|
else if(!((OSSwapBigToHostInt32(arch->cpusubtype) ^ 0x1) & 0xFFFFFF))
|
||||||
|
{
|
||||||
|
toInjectHasArm64 = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(OSSwapLittleToHostInt32(machHeader->cputype) == 0x100000C)
|
||||||
|
{
|
||||||
|
if (!((OSSwapLittleToHostInt32(machHeader->cpusubtype) ^ 0x2) & 0xFFFFFF))
|
||||||
|
{
|
||||||
|
toInjectHasArm64e = YES;
|
||||||
|
}
|
||||||
|
else if(!((OSSwapLittleToHostInt32(machHeader->cpusubtype) ^ 0x1) & 0xFFFFFF))
|
||||||
|
{
|
||||||
|
toInjectHasArm64 = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!toInjectHasArm64 && !preferArm64e)
|
||||||
|
{
|
||||||
|
printf("ERROR: can't proceed injection because binary to inject has no arm64 slice\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t subtypeToUse = 0x1;
|
||||||
|
if(preferArm64e && toInjectHasArm64e)
|
||||||
|
{
|
||||||
|
subtypeToUse = 0x2;
|
||||||
|
}
|
||||||
|
|
||||||
|
pwnify_enumerateArchs(binaryToInject, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {
|
||||||
|
struct fat_arch currentArch;
|
||||||
|
if(arch)
|
||||||
|
{
|
||||||
|
currentArch.cputype = arch->cputype;
|
||||||
|
currentArch.cpusubtype = arch->cpusubtype;
|
||||||
|
currentArch.size = arch->size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentArch.cputype = OSSwapHostToBigInt(OSSwapLittleToHostInt32(machHeader->cputype));
|
||||||
|
currentArch.cpusubtype = OSSwapHostToBigInt(OSSwapLittleToHostInt32(machHeader->cpusubtype));
|
||||||
|
currentArch.size = OSSwapHostToBigInt((uint32_t)[[[NSFileManager defaultManager] attributesOfItemAtPath:binaryToInject error:nil] fileSize]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(OSSwapBigToHostInt32(currentArch.cputype) == 0x100000C)
|
||||||
|
{
|
||||||
|
if (!((OSSwapBigToHostInt32(currentArch.cpusubtype) ^ subtypeToUse) & 0xFFFFFF))
|
||||||
|
{
|
||||||
|
currentArch.align = OSSwapHostToBigInt32(ALIGN_DEFAULT);
|
||||||
|
currentArch.offset = OSSwapHostToBigInt32(curOffset);
|
||||||
|
curOffset += roundUp(OSSwapBigToHostInt32(currentArch.size), align);
|
||||||
|
memcpy(&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex], ¤tArch, sizeof(currentArch));
|
||||||
|
curArchIndex++;
|
||||||
|
*stop = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// FAT Header constructed, now write to file and then write the slices themselves
|
||||||
|
|
||||||
|
FILE* tmpFile = fopen(tmpFilePath.UTF8String, "wb");
|
||||||
|
fwrite(&fatData[0], fatDataSize, 1, tmpFile);
|
||||||
|
|
||||||
|
curArchIndex = 0;
|
||||||
|
pwnify_enumerateArchs(appStoreBinary, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {
|
||||||
|
struct fat_arch* toWriteArch = (struct fat_arch*)&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex];
|
||||||
|
|
||||||
|
expandFile(tmpFile, OSSwapBigToHostInt32(toWriteArch->offset));
|
||||||
|
|
||||||
|
uint32_t offset = 0;
|
||||||
|
uint32_t size = 0;
|
||||||
|
|
||||||
|
if(arch)
|
||||||
|
{
|
||||||
|
offset = OSSwapBigToHostInt32(arch->offset);
|
||||||
|
size = OSSwapBigToHostInt32(arch->size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size = OSSwapBigToHostInt32(toWriteArch->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* appStoreBinaryFile = fopen(appStoreBinary.UTF8String, "rb");
|
||||||
|
fseek(appStoreBinaryFile, offset, SEEK_SET);
|
||||||
|
copyData(appStoreBinaryFile, tmpFile, size);
|
||||||
|
fclose(appStoreBinaryFile);
|
||||||
|
curArchIndex++;
|
||||||
|
});
|
||||||
|
|
||||||
|
struct fat_arch* toWriteArch = (struct fat_arch*)&fatData[sizeof(fatHeader) + sizeof(struct fat_arch)*curArchIndex];
|
||||||
|
pwnify_enumerateArchs(binaryToInject, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop) {
|
||||||
|
struct fat_arch currentArch;
|
||||||
|
if(arch)
|
||||||
|
{
|
||||||
|
currentArch.cputype = arch->cputype;
|
||||||
|
currentArch.cpusubtype = arch->cpusubtype;
|
||||||
|
currentArch.size = arch->size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentArch.cputype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cputype));
|
||||||
|
currentArch.cpusubtype = OSSwapHostToBigInt32(OSSwapLittleToHostInt32(machHeader->cpusubtype));
|
||||||
|
currentArch.size = OSSwapHostToBigInt32((uint32_t)[[[NSFileManager defaultManager] attributesOfItemAtPath:binaryToInject error:nil] fileSize]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(OSSwapBigToHostInt32(currentArch.cputype) == 0x100000C)
|
||||||
|
{
|
||||||
|
if (!((OSSwapBigToHostInt32(currentArch.cpusubtype) ^ subtypeToUse) & 0xFFFFFF))
|
||||||
|
{
|
||||||
|
expandFile(tmpFile, OSSwapBigToHostInt32(toWriteArch->offset));
|
||||||
|
|
||||||
|
uint32_t offset = 0;
|
||||||
|
uint32_t size = 0;
|
||||||
|
|
||||||
|
if(arch)
|
||||||
|
{
|
||||||
|
offset = OSSwapBigToHostInt32(arch->offset);
|
||||||
|
size = OSSwapBigToHostInt32(arch->size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size = OSSwapBigToHostInt32(toWriteArch->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* binaryToInjectFile = fopen(binaryToInject.UTF8String, "rb");
|
||||||
|
fseek(binaryToInjectFile, offset, SEEK_SET);
|
||||||
|
copyData(binaryToInjectFile, tmpFile, size);
|
||||||
|
fclose(binaryToInjectFile);
|
||||||
|
*stop = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fclose(tmpFile);
|
||||||
|
chmod(tmpFilePath.UTF8String, 0755);
|
||||||
|
|
||||||
|
if(replaceBinaryToInject)
|
||||||
|
{
|
||||||
|
[[NSFileManager defaultManager] removeItemAtPath:binaryToInject error:nil];
|
||||||
|
[[NSFileManager defaultManager] moveItemAtPath:tmpFilePath toPath:binaryToInject error:nil];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[[NSFileManager defaultManager] removeItemAtPath:appStoreBinary error:nil];
|
||||||
|
[[NSFileManager defaultManager] moveItemAtPath:tmpFilePath toPath:appStoreBinary error:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pwnify_state pwnifyGetBinaryState(NSString* binaryToCheck)
|
||||||
|
{
|
||||||
|
__block BOOL has2To0Slice = NO;
|
||||||
|
__block BOOL hasArmv8Slice = NO;
|
||||||
|
__block BOOL hasArm64eNewAbiSlice = NO;
|
||||||
|
|
||||||
|
pwnify_enumerateArchs(binaryToCheck, ^(struct fat_arch* arch, uint32_t archFileOffset, struct mach_header* machHeader, uint32_t sliceFileOffset, FILE* file, BOOL* stop)
|
||||||
|
{
|
||||||
|
if(arch && machHeader)
|
||||||
|
{
|
||||||
|
uint32_t mach_cputype = OSSwapLittleToHostInt32(machHeader->cputype);
|
||||||
|
uint32_t fat_cputype = OSSwapBigToHostInt32(arch->cputype);
|
||||||
|
uint32_t mach_cpusubtype = OSSwapLittleToHostInt32(machHeader->cpusubtype);
|
||||||
|
uint32_t fat_cpusubtype = OSSwapBigToHostInt32(arch->cpusubtype);
|
||||||
|
if(mach_cputype == 0x100000C && fat_cputype == 0x100000C)
|
||||||
|
{
|
||||||
|
if(fat_cpusubtype == 0x2 && mach_cpusubtype == 0x0)
|
||||||
|
{
|
||||||
|
has2To0Slice = YES;
|
||||||
|
}
|
||||||
|
else if(fat_cpusubtype == 0x1 && mach_cpusubtype == 0x1)
|
||||||
|
{
|
||||||
|
hasArmv8Slice = YES;
|
||||||
|
}
|
||||||
|
else if(fat_cpusubtype == 0x80000002 && mach_cpusubtype == 0x80000002)
|
||||||
|
{
|
||||||
|
hasArm64eNewAbiSlice = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pwnify_state outState = PWNIFY_STATE_NOT_PWNIFIED;
|
||||||
|
if(has2To0Slice && hasArmv8Slice)
|
||||||
|
{
|
||||||
|
outState = PWNIFY_STATE_PWNIFIED_ARM64;
|
||||||
|
}
|
||||||
|
else if(has2To0Slice && hasArm64eNewAbiSlice)
|
||||||
|
{
|
||||||
|
outState = PWNIFY_STATE_PWNIFIED_ARM64E;
|
||||||
|
}
|
||||||
|
|
||||||
|
return outState;
|
||||||
|
}
|
Loading…
Reference in New Issue