mirror of https://github.com/opa334/TrollStore.git
Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
e157415304
|
@ -10,7 +10,6 @@
|
||||||
#include <choma/MemoryStream.h>
|
#include <choma/MemoryStream.h>
|
||||||
#include <choma/FileStream.h>
|
#include <choma/FileStream.h>
|
||||||
#include <choma/BufferedStream.h>
|
#include <choma/BufferedStream.h>
|
||||||
#include <choma/Signing.h>
|
|
||||||
#include <choma/SignOSSL.h>
|
#include <choma/SignOSSL.h>
|
||||||
#include <choma/CodeDirectory.h>
|
#include <choma/CodeDirectory.h>
|
||||||
#include <choma/Base64.h>
|
#include <choma/Base64.h>
|
||||||
|
@ -123,6 +122,12 @@ int apply_coretrust_bypass(const char *machoPath)
|
||||||
MachO *macho = macho_init_for_writing(machoPath);
|
MachO *macho = macho_init_for_writing(machoPath);
|
||||||
if (!macho) return -1;
|
if (!macho) return -1;
|
||||||
|
|
||||||
|
if (macho_is_encrypted(macho)) {
|
||||||
|
printf("Error: MachO is encrypted, please use a decrypted app!\n");
|
||||||
|
macho_free(macho);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
CS_SuperBlob *superblob = macho_read_code_signature(macho);
|
CS_SuperBlob *superblob = macho_read_code_signature(macho);
|
||||||
if (!superblob) {
|
if (!superblob) {
|
||||||
printf("Error: no code signature found, please fake-sign the binary at minimum before running the bypass.\n");
|
printf("Error: no code signature found, please fake-sign the binary at minimum before running the bypass.\n");
|
||||||
|
@ -137,8 +142,19 @@ int apply_coretrust_bypass(const char *machoPath)
|
||||||
CS_DecodedBlob *mainCodeDirBlob = csd_superblob_find_blob(decodedSuperblob, CSSLOT_CODEDIRECTORY, NULL);
|
CS_DecodedBlob *mainCodeDirBlob = csd_superblob_find_blob(decodedSuperblob, CSSLOT_CODEDIRECTORY, NULL);
|
||||||
CS_DecodedBlob *alternateCodeDirBlob = csd_superblob_find_blob(decodedSuperblob, CSSLOT_ALTERNATE_CODEDIRECTORIES, NULL);
|
CS_DecodedBlob *alternateCodeDirBlob = csd_superblob_find_blob(decodedSuperblob, CSSLOT_ALTERNATE_CODEDIRECTORIES, NULL);
|
||||||
|
|
||||||
|
CS_DecodedBlob *entitlementsBlob = csd_superblob_find_blob(decodedSuperblob, CSSLOT_ENTITLEMENTS, NULL);
|
||||||
|
CS_DecodedBlob *derEntitlementsBlob = csd_superblob_find_blob(decodedSuperblob, CSSLOT_DER_ENTITLEMENTS, NULL);
|
||||||
|
|
||||||
|
if (!entitlementsBlob && !derEntitlementsBlob && macho->machHeader.filetype == MH_EXECUTE) {
|
||||||
|
printf("Error: Unable to find existing entitlements blobs in executable MachO, please make sure to ad-hoc sign with entitlements before running the bypass.\n");
|
||||||
|
csd_blob_free(mainCodeDirBlob);
|
||||||
|
if (alternateCodeDirBlob) csd_blob_free(alternateCodeDirBlob);
|
||||||
|
macho_free(macho);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!mainCodeDirBlob) {
|
if (!mainCodeDirBlob) {
|
||||||
printf("Error: Unable to find code directory, make sure the input binary is ad-hoc signed?\n");
|
printf("Error: Unable to find code directory, make sure the input binary is ad-hoc signed.\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,63 +12,74 @@
|
||||||
|
|
||||||
// Blob index
|
// Blob index
|
||||||
typedef struct __BlobIndex {
|
typedef struct __BlobIndex {
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
} CS_BlobIndex;
|
} CS_BlobIndex;
|
||||||
|
|
||||||
// CMS superblob
|
// CMS superblob
|
||||||
typedef struct __SuperBlob {
|
typedef struct __SuperBlob {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
uint32_t length;
|
uint32_t length;
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
CS_BlobIndex index[];
|
CS_BlobIndex index[];
|
||||||
} CS_SuperBlob;
|
} CS_SuperBlob;
|
||||||
|
|
||||||
typedef struct __GenericBlob {
|
typedef struct __GenericBlob {
|
||||||
uint32_t magic; /* magic number */
|
uint32_t magic; /* magic number */
|
||||||
uint32_t length; /* total length of blob */
|
uint32_t length; /* total length of blob */
|
||||||
char data[];
|
char data[];
|
||||||
} CS_GenericBlob;
|
} CS_GenericBlob;
|
||||||
|
|
||||||
// CMS blob magic types
|
// CMS blob magic types
|
||||||
enum {
|
enum {
|
||||||
CSBLOB_REQUIREMENT = 0xfade0c00,
|
CSMAGIC_REQUIREMENT = 0xfade0c00,
|
||||||
CSBLOB_REQUIREMENTS = 0xfade0c01,
|
CSMAGIC_REQUIREMENTS = 0xfade0c01,
|
||||||
CSBLOB_CODEDIRECTORY = 0xfade0c02,
|
CSMAGIC_CODEDIRECTORY = 0xfade0c02,
|
||||||
CSBLOB_EMBEDDED_SIGNATURE = 0xfade0cc0,
|
CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0,
|
||||||
CSBLOB_DETACHED_SIGNATURE = 0xfade0cc1,
|
CSMAGIC_EMBEDDED_SIGNATURE_OLD = 0xfade0b02,
|
||||||
CSBLOB_ENTITLEMENTS = 0xfade7171,
|
CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171,
|
||||||
CSBLOB_DER_ENTITLEMENTS = 0xfade7172,
|
CSMAGIC_EMBEDDED_DER_ENTITLEMENTS = 0xfade7172,
|
||||||
CSBLOB_SIGNATURE_BLOB = 0xfade0b01
|
CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1,
|
||||||
} CS_BlobType;
|
CSMAGIC_BLOBWRAPPER = 0xfade0b01,
|
||||||
|
CSMAGIC_EMBEDDED_LAUNCH_CONSTRAINT = 0xfade8181,
|
||||||
|
} CS_BlobMagic;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
CSSLOT_CODEDIRECTORY = 0,
|
CSSLOT_CODEDIRECTORY = 0,
|
||||||
CSSLOT_INFOSLOT = 1,
|
CSSLOT_INFOSLOT = 1,
|
||||||
CSSLOT_REQUIREMENTS = 2,
|
CSSLOT_REQUIREMENTS = 2,
|
||||||
CSSLOT_RESOURCEDIR = 3,
|
CSSLOT_RESOURCEDIR = 3,
|
||||||
CSSLOT_APPLICATION = 4,
|
CSSLOT_APPLICATION = 4,
|
||||||
CSSLOT_ENTITLEMENTS = 5,
|
CSSLOT_ENTITLEMENTS = 5,
|
||||||
CSSLOT_DER_ENTITLEMENTS = 7,
|
CSSLOT_DER_ENTITLEMENTS = 7,
|
||||||
CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000,
|
CSSLOT_LAUNCH_CONSTRAINT_SELF = 8,
|
||||||
CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5,
|
CSSLOT_LAUNCH_CONSTRAINT_PARENT = 9,
|
||||||
CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT = CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX,
|
CSSLOT_LAUNCH_CONSTRAINT_RESPONSIBLE = 10,
|
||||||
CSSLOT_SIGNATURESLOT = 0x10000
|
CSSLOT_LIBRARY_CONSTRAINT = 11,
|
||||||
|
|
||||||
|
CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000, /* first alternate CodeDirectory, if any */
|
||||||
|
CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5, /* max number of alternate CD slots */
|
||||||
|
CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT = CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX, /* one past the last */
|
||||||
|
|
||||||
|
CSSLOT_SIGNATURESLOT = 0x10000,
|
||||||
|
CSSLOT_IDENTIFICATIONSLOT = 0x10001,
|
||||||
|
CSSLOT_TICKETSLOT = 0x10002,
|
||||||
} CS_SlotType;
|
} CS_SlotType;
|
||||||
|
|
||||||
typedef struct s_CS_DecodedBlob {
|
typedef struct s_CS_DecodedBlob {
|
||||||
struct s_CS_DecodedBlob *next;
|
struct s_CS_DecodedBlob *next;
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
MemoryStream *stream;
|
MemoryStream *stream;
|
||||||
} CS_DecodedBlob;
|
} CS_DecodedBlob;
|
||||||
|
|
||||||
typedef struct s_CS_DecodedSuperBlob {
|
typedef struct s_CS_DecodedSuperBlob {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
struct s_CS_DecodedBlob *firstBlob;
|
struct s_CS_DecodedBlob *firstBlob;
|
||||||
} CS_DecodedSuperBlob;
|
} CS_DecodedSuperBlob;
|
||||||
|
|
||||||
// Convert blob magic to readable blob type string
|
// Convert blob magic to readable blob type string
|
||||||
char *cs_blob_magic_to_string(int magic);
|
const char *cs_blob_magic_to_string(uint32_t magic);
|
||||||
|
const char *cs_slot_type_to_string(uint32_t slotType);
|
||||||
|
|
||||||
// Extract Code Signature to file
|
// Extract Code Signature to file
|
||||||
int macho_extract_cs_to_file(MachO *macho, CS_SuperBlob *superblob);
|
int macho_extract_cs_to_file(MachO *macho, CS_SuperBlob *superblob);
|
||||||
|
|
|
@ -30,11 +30,16 @@ FAT *fat_init_from_memory_stream(MemoryStream *stream);
|
||||||
|
|
||||||
// Initialise a FAT structure using the path to the file
|
// Initialise a FAT structure using the path to the file
|
||||||
FAT *fat_init_from_path(const char *filePath);
|
FAT *fat_init_from_path(const char *filePath);
|
||||||
//FAT *fat_init_from_path_for_writing(const char *filePath);
|
|
||||||
|
|
||||||
// Find macho with cputype and cpusubtype in FAT, returns NULL if not found
|
// Find macho with cputype and cpusubtype in FAT, returns NULL if not found
|
||||||
MachO *fat_find_slice(FAT *fat, cpu_type_t cputype, cpu_subtype_t cpusubtype);
|
MachO *fat_find_slice(FAT *fat, cpu_type_t cputype, cpu_subtype_t cpusubtype);
|
||||||
|
|
||||||
|
// Create a FAT structure from an array of MachO structures
|
||||||
|
FAT *fat_create_for_macho_array(char *firstInputPath, MachO **machoArray, int machoArrayCount);
|
||||||
|
|
||||||
|
// Add a MachO to the FAT structure
|
||||||
|
int fat_add_macho(FAT *fat, MachO *macho);
|
||||||
|
|
||||||
// Free all elements of the FAT structure
|
// Free all elements of the FAT structure
|
||||||
void fat_free(FAT *fat);
|
void fat_free(FAT *fat);
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,12 @@ MachO *macho_init(MemoryStream *stream, struct fat_arch_64 archDescriptor);
|
||||||
// Initialize a single slice macho for writing to it
|
// Initialize a single slice macho for writing to it
|
||||||
MachO *macho_init_for_writing(const char *filePath);
|
MachO *macho_init_for_writing(const char *filePath);
|
||||||
|
|
||||||
|
// Create an array of MachO objects from an array of paths
|
||||||
|
MachO **macho_array_create_for_paths(char **inputPaths, int inputPathsCount);
|
||||||
|
|
||||||
|
// Check if a MachO is encrypted
|
||||||
|
bool macho_is_encrypted(MachO *macho);
|
||||||
|
|
||||||
void macho_free(MachO *macho);
|
void macho_free(MachO *macho);
|
||||||
|
|
||||||
#endif // MACHO_SLICE_H
|
#endif // MACHO_SLICE_H
|
|
@ -99,6 +99,13 @@
|
||||||
applier(lc, dataoff); \
|
applier(lc, dataoff); \
|
||||||
applier(lc, datasize);
|
applier(lc, datasize);
|
||||||
|
|
||||||
|
#define ENCRYPTION_INFO_COMMAND_APPLY_BYTE_ORDER(eic, applier) \
|
||||||
|
applier(eic, cmd); \
|
||||||
|
applier(eic, cmdsize); \
|
||||||
|
applier(eic, cryptoff); \
|
||||||
|
applier(eic, cryptsize); \
|
||||||
|
applier(eic, cryptid);
|
||||||
|
|
||||||
#define BLOB_INDEX_APPLY_BYTE_ORDER(bi, applier) \
|
#define BLOB_INDEX_APPLY_BYTE_ORDER(bi, applier) \
|
||||||
applier(bi, type); \
|
applier(bi, type); \
|
||||||
applier(bi, offset);
|
applier(bi, offset);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#define METRIC_TYPE_FUNCTION_XREF 3
|
#define METRIC_TYPE_FUNCTION_XREF 3
|
||||||
|
|
||||||
typedef struct PFSection {
|
typedef struct PFSection {
|
||||||
|
MachO *macho;
|
||||||
uint64_t fileoff;
|
uint64_t fileoff;
|
||||||
uint64_t vmaddr;
|
uint64_t vmaddr;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
|
@ -13,13 +14,16 @@ typedef struct PFSection {
|
||||||
bool ownsCache;
|
bool ownsCache;
|
||||||
} PFSection;
|
} PFSection;
|
||||||
|
|
||||||
PFSection *macho_patchfinder_create_section(MachO *macho, const char *filesetEntryId, const char *segName, const char *sectName);
|
PFSection *pf_section_init_from_macho(MachO *macho, const char *filesetEntryId, const char *segName, const char *sectName);
|
||||||
int macho_patchfinder_cache_section(PFSection *section, MachO *fromMacho);
|
int pf_section_read_at_relative_offset(PFSection *section, uint64_t rel, size_t size, void *outBuf);
|
||||||
void macho_patchfinder_section_free(PFSection *section);
|
int pf_section_read_at_address(PFSection *section, uint64_t vmaddr, void *outBuf, size_t size);
|
||||||
|
uint32_t pf_section_read32(PFSection *section, uint64_t vmaddr);
|
||||||
|
int pf_section_set_cached(PFSection *section, bool cached);
|
||||||
|
void pf_section_free(PFSection *section);
|
||||||
|
|
||||||
|
|
||||||
typedef struct MetricShared {
|
typedef struct MetricShared {
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
PFSection *section;
|
|
||||||
} MetricShared;
|
} MetricShared;
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,6 +43,5 @@ typedef struct BytePatternMetric {
|
||||||
BytePatternAlignment alignment;
|
BytePatternAlignment alignment;
|
||||||
} BytePatternMetric;
|
} BytePatternMetric;
|
||||||
|
|
||||||
BytePatternMetric *macho_patchfinder_create_byte_pattern_metric(PFSection *section, void *bytes, void *mask, size_t nbytes, BytePatternAlignment alignment);
|
BytePatternMetric *pf_create_byte_pattern_metric(void *bytes, void *mask, size_t nbytes, BytePatternAlignment alignment);
|
||||||
|
void pf_section_run_metric(PFSection *section, void *metric, void (^matchBlock)(uint64_t vmaddr, bool *stop));
|
||||||
void macho_patchfinder_run_metric(MachO *macho, void *metric, void (^matchBlock)(uint64_t vmaddr, bool *stop));
|
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
#ifndef SIGNING_H
|
|
||||||
#define SIGNING_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <CommonCrypto/CommonCrypto.h>
|
|
||||||
#include <Security/SecKey.h>
|
|
||||||
#include <Security/Security.h>
|
|
||||||
|
|
||||||
// int signWithRSA(const char *certificateFile, const char *inputFile, const char *outputFile);
|
|
||||||
|
|
||||||
#endif // SIGNING_H
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int memcmp_masked(const void *str1, const void *str2, unsigned char* mask, size_t n);
|
||||||
uint64_t align_to_size(int size, int alignment);
|
uint64_t align_to_size(int size, int alignment);
|
||||||
int count_digits(int64_t num);
|
int count_digits(int64_t num);
|
||||||
void print_hash(uint8_t *hash, size_t size);
|
void print_hash(uint8_t *hash, size_t size);
|
Binary file not shown.
|
@ -11,7 +11,30 @@ char *extract_preferred_slice(const char *fatPath)
|
||||||
FAT *fat = fat_init_from_path(fatPath);
|
FAT *fat = fat_init_from_path(fatPath);
|
||||||
if (!fat) return NULL;
|
if (!fat) return NULL;
|
||||||
MachO *macho = fat_find_preferred_slice(fat);
|
MachO *macho = fat_find_preferred_slice(fat);
|
||||||
if (!macho) return NULL;
|
|
||||||
|
#if TARGET_OS_MAC && !TARGET_OS_IPHONE
|
||||||
|
if (!macho) {
|
||||||
|
// Check for arm64v8 first
|
||||||
|
macho = fat_find_slice(fat, CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_V8);
|
||||||
|
if (!macho) {
|
||||||
|
// If that fails, check for regular arm64
|
||||||
|
macho = fat_find_slice(fat, CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL);
|
||||||
|
if (!macho) {
|
||||||
|
// If that fails, check for arm64e
|
||||||
|
macho = fat_find_slice(fat, CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E);
|
||||||
|
if (!macho) {
|
||||||
|
fat_free(fat);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (!macho) {
|
||||||
|
fat_free(fat);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif // TARGET_OS_MAC && !TARGET_OS_IPHONE
|
||||||
|
|
||||||
char *temp = strdup("/tmp/XXXXXX");
|
char *temp = strdup("/tmp/XXXXXX");
|
||||||
int fd = mkstemp(temp);
|
int fd = mkstemp(temp);
|
||||||
|
@ -26,30 +49,6 @@ char *extract_preferred_slice(const char *fatPath)
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
int apply_coretrust_bypass_wrapper(const char *inputPath, const char *outputPath)
|
|
||||||
{
|
|
||||||
char *machoPath = extract_preferred_slice(inputPath);
|
|
||||||
printf("extracted best slice to %s\n", machoPath);
|
|
||||||
|
|
||||||
int r = apply_coretrust_bypass(machoPath);
|
|
||||||
if (r != 0) {
|
|
||||||
free(machoPath);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = copyfile(machoPath, outputPath, 0, COPYFILE_ALL | COPYFILE_MOVE | COPYFILE_UNLINK);
|
|
||||||
if (r == 0) {
|
|
||||||
chmod(outputPath, 0755);
|
|
||||||
printf("Signed file! CoreTrust bypass eta now!!\n");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
perror("copyfile");
|
|
||||||
}
|
|
||||||
|
|
||||||
free(machoPath);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
if (argc < 2) return -1;
|
if (argc < 2) return -1;
|
||||||
|
@ -77,9 +76,11 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
printf("Applying CoreTrust bypass...\n");
|
printf("Applying CoreTrust bypass...\n");
|
||||||
|
|
||||||
if (apply_coretrust_bypass(machoPath) != 0) {
|
r = apply_coretrust_bypass(machoPath);
|
||||||
|
|
||||||
|
if (r != 0) {
|
||||||
printf("Failed applying CoreTrust bypass\n");
|
printf("Failed applying CoreTrust bypass\n");
|
||||||
return -1;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (copyfile(machoPath, input, 0, COPYFILE_ALL | COPYFILE_MOVE | COPYFILE_UNLINK) == 0) {
|
if (copyfile(machoPath, input, 0, COPYFILE_ALL | COPYFILE_MOVE | COPYFILE_UNLINK) == 0) {
|
||||||
|
|
12
README.md
12
README.md
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
TrollStore is a permasigned jailed app that can permanently install any IPA you open in it.
|
TrollStore is a permasigned jailed app that can permanently install any IPA you open in it.
|
||||||
|
|
||||||
It works because of an AMFI/CoreTrust bug where iOS does not verify whether or not a root certificate used to sign a binary is legit.
|
It works because of an AMFI/CoreTrust bug where iOS does not correctly verify code signatures of binaries in which there are multiple signers.
|
||||||
|
|
||||||
Supported versions: 14.0 - 16.6.1, 17.0
|
Supported versions: 14.0 - 16.6.1, 17.0
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ When your app is not sandboxed, you can spawn other binaries using posix_spawn,
|
||||||
|
|
||||||
You can also add your own binaries into your app bundle.
|
You can also add your own binaries into your app bundle.
|
||||||
|
|
||||||
Afterwards you can use the [spawnRoot function in TSUtil.m](./Shared/TSUtil.m#L77) to spawn the binary as root.
|
Afterwards you can use the [spawnRoot function in TSUtil.m](./Shared/TSUtil.m#L79) to spawn the binary as root.
|
||||||
|
|
||||||
### Things that are not possible using TrollStore
|
### Things that are not possible using TrollStore
|
||||||
|
|
||||||
|
@ -110,8 +110,12 @@ Afterwards you can use the [spawnRoot function in TSUtil.m](./Shared/TSUtil.m#L7
|
||||||
|
|
||||||
## Credits and Further Reading
|
## Credits and Further Reading
|
||||||
|
|
||||||
[@LinusHenze](https://twitter.com/LinusHenze/) - Found the CoreTrust bug that allows TrollStore to work.
|
[@alfiecg_dev](https://twitter.com/alfiecg_dev/) - Found the CoreTrust bug that allows TrollStore to work through patchdiffing and worked on automating the bypass.
|
||||||
|
|
||||||
|
Google Threat Analysis Group - Found the CoreTrust bug as part of an in-the-wild spyware chain and reported it to Apple.
|
||||||
|
|
||||||
|
[@LinusHenze](https://twitter.com/LinusHenze) - Found the installd bypass used to install TrollStore on iOS 14-15.6.1 via TrollHelperOTA, as well as the original CoreTrust bug used in TrollStore 1.0.
|
||||||
|
|
||||||
[Fugu15 Presentation](https://youtu.be/rPTifU1lG7Q)
|
[Fugu15 Presentation](https://youtu.be/rPTifU1lG7Q)
|
||||||
|
|
||||||
[Write-Up on the CoreTrust bug with more information](https://worthdoingbadly.com/coretrust/).
|
[Write-Up on the first CoreTrust bug with more information](https://worthdoingbadly.com/coretrust/).
|
||||||
|
|
|
@ -12,63 +12,74 @@
|
||||||
|
|
||||||
// Blob index
|
// Blob index
|
||||||
typedef struct __BlobIndex {
|
typedef struct __BlobIndex {
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
} CS_BlobIndex;
|
} CS_BlobIndex;
|
||||||
|
|
||||||
// CMS superblob
|
// CMS superblob
|
||||||
typedef struct __SuperBlob {
|
typedef struct __SuperBlob {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
uint32_t length;
|
uint32_t length;
|
||||||
uint32_t count;
|
uint32_t count;
|
||||||
CS_BlobIndex index[];
|
CS_BlobIndex index[];
|
||||||
} CS_SuperBlob;
|
} CS_SuperBlob;
|
||||||
|
|
||||||
typedef struct __GenericBlob {
|
typedef struct __GenericBlob {
|
||||||
uint32_t magic; /* magic number */
|
uint32_t magic; /* magic number */
|
||||||
uint32_t length; /* total length of blob */
|
uint32_t length; /* total length of blob */
|
||||||
char data[];
|
char data[];
|
||||||
} CS_GenericBlob;
|
} CS_GenericBlob;
|
||||||
|
|
||||||
// CMS blob magic types
|
// CMS blob magic types
|
||||||
enum {
|
enum {
|
||||||
CSBLOB_REQUIREMENT = 0xfade0c00,
|
CSMAGIC_REQUIREMENT = 0xfade0c00,
|
||||||
CSBLOB_REQUIREMENTS = 0xfade0c01,
|
CSMAGIC_REQUIREMENTS = 0xfade0c01,
|
||||||
CSBLOB_CODEDIRECTORY = 0xfade0c02,
|
CSMAGIC_CODEDIRECTORY = 0xfade0c02,
|
||||||
CSBLOB_EMBEDDED_SIGNATURE = 0xfade0cc0,
|
CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0,
|
||||||
CSBLOB_DETACHED_SIGNATURE = 0xfade0cc1,
|
CSMAGIC_EMBEDDED_SIGNATURE_OLD = 0xfade0b02,
|
||||||
CSBLOB_ENTITLEMENTS = 0xfade7171,
|
CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171,
|
||||||
CSBLOB_DER_ENTITLEMENTS = 0xfade7172,
|
CSMAGIC_EMBEDDED_DER_ENTITLEMENTS = 0xfade7172,
|
||||||
CSBLOB_SIGNATURE_BLOB = 0xfade0b01
|
CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1,
|
||||||
} CS_BlobType;
|
CSMAGIC_BLOBWRAPPER = 0xfade0b01,
|
||||||
|
CSMAGIC_EMBEDDED_LAUNCH_CONSTRAINT = 0xfade8181,
|
||||||
|
} CS_BlobMagic;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
CSSLOT_CODEDIRECTORY = 0,
|
CSSLOT_CODEDIRECTORY = 0,
|
||||||
CSSLOT_INFOSLOT = 1,
|
CSSLOT_INFOSLOT = 1,
|
||||||
CSSLOT_REQUIREMENTS = 2,
|
CSSLOT_REQUIREMENTS = 2,
|
||||||
CSSLOT_RESOURCEDIR = 3,
|
CSSLOT_RESOURCEDIR = 3,
|
||||||
CSSLOT_APPLICATION = 4,
|
CSSLOT_APPLICATION = 4,
|
||||||
CSSLOT_ENTITLEMENTS = 5,
|
CSSLOT_ENTITLEMENTS = 5,
|
||||||
CSSLOT_DER_ENTITLEMENTS = 7,
|
CSSLOT_DER_ENTITLEMENTS = 7,
|
||||||
CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000,
|
CSSLOT_LAUNCH_CONSTRAINT_SELF = 8,
|
||||||
CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5,
|
CSSLOT_LAUNCH_CONSTRAINT_PARENT = 9,
|
||||||
CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT = CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX,
|
CSSLOT_LAUNCH_CONSTRAINT_RESPONSIBLE = 10,
|
||||||
CSSLOT_SIGNATURESLOT = 0x10000
|
CSSLOT_LIBRARY_CONSTRAINT = 11,
|
||||||
|
|
||||||
|
CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000, /* first alternate CodeDirectory, if any */
|
||||||
|
CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5, /* max number of alternate CD slots */
|
||||||
|
CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT = CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX, /* one past the last */
|
||||||
|
|
||||||
|
CSSLOT_SIGNATURESLOT = 0x10000,
|
||||||
|
CSSLOT_IDENTIFICATIONSLOT = 0x10001,
|
||||||
|
CSSLOT_TICKETSLOT = 0x10002,
|
||||||
} CS_SlotType;
|
} CS_SlotType;
|
||||||
|
|
||||||
typedef struct s_CS_DecodedBlob {
|
typedef struct s_CS_DecodedBlob {
|
||||||
struct s_CS_DecodedBlob *next;
|
struct s_CS_DecodedBlob *next;
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
MemoryStream *stream;
|
MemoryStream *stream;
|
||||||
} CS_DecodedBlob;
|
} CS_DecodedBlob;
|
||||||
|
|
||||||
typedef struct s_CS_DecodedSuperBlob {
|
typedef struct s_CS_DecodedSuperBlob {
|
||||||
uint32_t magic;
|
uint32_t magic;
|
||||||
struct s_CS_DecodedBlob *firstBlob;
|
struct s_CS_DecodedBlob *firstBlob;
|
||||||
} CS_DecodedSuperBlob;
|
} CS_DecodedSuperBlob;
|
||||||
|
|
||||||
// Convert blob magic to readable blob type string
|
// Convert blob magic to readable blob type string
|
||||||
char *cs_blob_magic_to_string(int magic);
|
const char *cs_blob_magic_to_string(uint32_t magic);
|
||||||
|
const char *cs_slot_type_to_string(uint32_t slotType);
|
||||||
|
|
||||||
// Extract Code Signature to file
|
// Extract Code Signature to file
|
||||||
int macho_extract_cs_to_file(MachO *macho, CS_SuperBlob *superblob);
|
int macho_extract_cs_to_file(MachO *macho, CS_SuperBlob *superblob);
|
||||||
|
|
|
@ -30,11 +30,16 @@ FAT *fat_init_from_memory_stream(MemoryStream *stream);
|
||||||
|
|
||||||
// Initialise a FAT structure using the path to the file
|
// Initialise a FAT structure using the path to the file
|
||||||
FAT *fat_init_from_path(const char *filePath);
|
FAT *fat_init_from_path(const char *filePath);
|
||||||
//FAT *fat_init_from_path_for_writing(const char *filePath);
|
|
||||||
|
|
||||||
// Find macho with cputype and cpusubtype in FAT, returns NULL if not found
|
// Find macho with cputype and cpusubtype in FAT, returns NULL if not found
|
||||||
MachO *fat_find_slice(FAT *fat, cpu_type_t cputype, cpu_subtype_t cpusubtype);
|
MachO *fat_find_slice(FAT *fat, cpu_type_t cputype, cpu_subtype_t cpusubtype);
|
||||||
|
|
||||||
|
// Create a FAT structure from an array of MachO structures
|
||||||
|
FAT *fat_create_for_macho_array(char *firstInputPath, MachO **machoArray, int machoArrayCount);
|
||||||
|
|
||||||
|
// Add a MachO to the FAT structure
|
||||||
|
int fat_add_macho(FAT *fat, MachO *macho);
|
||||||
|
|
||||||
// Free all elements of the FAT structure
|
// Free all elements of the FAT structure
|
||||||
void fat_free(FAT *fat);
|
void fat_free(FAT *fat);
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,12 @@ MachO *macho_init(MemoryStream *stream, struct fat_arch_64 archDescriptor);
|
||||||
// Initialize a single slice macho for writing to it
|
// Initialize a single slice macho for writing to it
|
||||||
MachO *macho_init_for_writing(const char *filePath);
|
MachO *macho_init_for_writing(const char *filePath);
|
||||||
|
|
||||||
|
// Create an array of MachO objects from an array of paths
|
||||||
|
MachO **macho_array_create_for_paths(char **inputPaths, int inputPathsCount);
|
||||||
|
|
||||||
|
// Check if a MachO is encrypted
|
||||||
|
bool macho_is_encrypted(MachO *macho);
|
||||||
|
|
||||||
void macho_free(MachO *macho);
|
void macho_free(MachO *macho);
|
||||||
|
|
||||||
#endif // MACHO_SLICE_H
|
#endif // MACHO_SLICE_H
|
|
@ -99,6 +99,13 @@
|
||||||
applier(lc, dataoff); \
|
applier(lc, dataoff); \
|
||||||
applier(lc, datasize);
|
applier(lc, datasize);
|
||||||
|
|
||||||
|
#define ENCRYPTION_INFO_COMMAND_APPLY_BYTE_ORDER(eic, applier) \
|
||||||
|
applier(eic, cmd); \
|
||||||
|
applier(eic, cmdsize); \
|
||||||
|
applier(eic, cryptoff); \
|
||||||
|
applier(eic, cryptsize); \
|
||||||
|
applier(eic, cryptid);
|
||||||
|
|
||||||
#define BLOB_INDEX_APPLY_BYTE_ORDER(bi, applier) \
|
#define BLOB_INDEX_APPLY_BYTE_ORDER(bi, applier) \
|
||||||
applier(bi, type); \
|
applier(bi, type); \
|
||||||
applier(bi, offset);
|
applier(bi, offset);
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#define METRIC_TYPE_FUNCTION_XREF 3
|
#define METRIC_TYPE_FUNCTION_XREF 3
|
||||||
|
|
||||||
typedef struct PFSection {
|
typedef struct PFSection {
|
||||||
|
MachO *macho;
|
||||||
uint64_t fileoff;
|
uint64_t fileoff;
|
||||||
uint64_t vmaddr;
|
uint64_t vmaddr;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
|
@ -13,13 +14,16 @@ typedef struct PFSection {
|
||||||
bool ownsCache;
|
bool ownsCache;
|
||||||
} PFSection;
|
} PFSection;
|
||||||
|
|
||||||
PFSection *macho_patchfinder_create_section(MachO *macho, const char *filesetEntryId, const char *segName, const char *sectName);
|
PFSection *pf_section_init_from_macho(MachO *macho, const char *filesetEntryId, const char *segName, const char *sectName);
|
||||||
int macho_patchfinder_cache_section(PFSection *section, MachO *fromMacho);
|
int pf_section_read_at_relative_offset(PFSection *section, uint64_t rel, size_t size, void *outBuf);
|
||||||
void macho_patchfinder_section_free(PFSection *section);
|
int pf_section_read_at_address(PFSection *section, uint64_t vmaddr, void *outBuf, size_t size);
|
||||||
|
uint32_t pf_section_read32(PFSection *section, uint64_t vmaddr);
|
||||||
|
int pf_section_set_cached(PFSection *section, bool cached);
|
||||||
|
void pf_section_free(PFSection *section);
|
||||||
|
|
||||||
|
|
||||||
typedef struct MetricShared {
|
typedef struct MetricShared {
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
PFSection *section;
|
|
||||||
} MetricShared;
|
} MetricShared;
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,6 +43,5 @@ typedef struct BytePatternMetric {
|
||||||
BytePatternAlignment alignment;
|
BytePatternAlignment alignment;
|
||||||
} BytePatternMetric;
|
} BytePatternMetric;
|
||||||
|
|
||||||
BytePatternMetric *macho_patchfinder_create_byte_pattern_metric(PFSection *section, void *bytes, void *mask, size_t nbytes, BytePatternAlignment alignment);
|
BytePatternMetric *pf_create_byte_pattern_metric(void *bytes, void *mask, size_t nbytes, BytePatternAlignment alignment);
|
||||||
|
void pf_section_run_metric(PFSection *section, void *metric, void (^matchBlock)(uint64_t vmaddr, bool *stop));
|
||||||
void macho_patchfinder_run_metric(MachO *macho, void *metric, void (^matchBlock)(uint64_t vmaddr, bool *stop));
|
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
#ifndef SIGNING_H
|
|
||||||
#define SIGNING_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <CommonCrypto/CommonCrypto.h>
|
|
||||||
#include <Security/SecKey.h>
|
|
||||||
#include <Security/Security.h>
|
|
||||||
|
|
||||||
// int signWithRSA(const char *certificateFile, const char *inputFile, const char *outputFile);
|
|
||||||
|
|
||||||
#endif // SIGNING_H
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int memcmp_masked(const void *str1, const void *str2, unsigned char* mask, size_t n);
|
||||||
uint64_t align_to_size(int size, int alignment);
|
uint64_t align_to_size(int size, int alignment);
|
||||||
int count_digits(int64_t num);
|
int count_digits(int64_t num);
|
||||||
void print_hash(uint8_t *hash, size_t size);
|
void print_hash(uint8_t *hash, size_t size);
|
Binary file not shown.
|
@ -691,6 +691,11 @@ int signApp(NSString* appPath)
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
NSLog(@"[%@] Applied CoreTrust bypass!", filePath);
|
NSLog(@"[%@] Applied CoreTrust bypass!", filePath);
|
||||||
}
|
}
|
||||||
|
else if (r == 2) {
|
||||||
|
NSLog(@"[%@] Cannot apply CoreTrust bypass on an encrypted binary!", filePath);
|
||||||
|
fat_free(fat);
|
||||||
|
return 180;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
NSLog(@"[%@] CoreTrust bypass failed!!! :(", filePath);
|
NSLog(@"[%@] CoreTrust bypass failed!!! :(", filePath);
|
||||||
fat_free(fat);
|
fat_free(fat);
|
||||||
|
@ -708,7 +713,7 @@ int signApp(NSString* appPath)
|
||||||
|
|
||||||
if (requiresDevMode) {
|
if (requiresDevMode) {
|
||||||
// Postpone trying to enable dev mode until after the app is (successfully) installed
|
// Postpone trying to enable dev mode until after the app is (successfully) installed
|
||||||
return 180;
|
return 182;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -770,6 +775,7 @@ void applyPatchesToInfoDictionary(NSString* appPath)
|
||||||
// 172: no info.plist found in app
|
// 172: no info.plist found in app
|
||||||
// 173: app is not signed and cannot be signed because ldid not installed or didn't work
|
// 173: app is not signed and cannot be signed because ldid not installed or didn't work
|
||||||
// 174:
|
// 174:
|
||||||
|
// 180: tried to sign encrypted binary
|
||||||
int installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate, BOOL useInstalldMethod)
|
int installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate, BOOL useInstalldMethod)
|
||||||
{
|
{
|
||||||
NSLog(@"[installApp force = %d]", force);
|
NSLog(@"[installApp force = %d]", force);
|
||||||
|
@ -799,9 +805,9 @@ int installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate,
|
||||||
if(sign)
|
if(sign)
|
||||||
{
|
{
|
||||||
int signRet = signApp(appBundleToInstallPath);
|
int signRet = signApp(appBundleToInstallPath);
|
||||||
// 180: app requires developer mode; non-fatal
|
// 182: app requires developer mode; non-fatal
|
||||||
if(signRet != 0) {
|
if(signRet != 0) {
|
||||||
if (signRet == 180) {
|
if (signRet == 182) {
|
||||||
requiresDevMode = YES;
|
requiresDevMode = YES;
|
||||||
} else {
|
} else {
|
||||||
return signRet;
|
return signRet;
|
||||||
|
@ -948,7 +954,10 @@ int installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate,
|
||||||
// Also permissions need to be fixed
|
// Also permissions need to be fixed
|
||||||
NSURL* updatedAppURL = findAppURLInBundleURL(appContainer.url);
|
NSURL* updatedAppURL = findAppURLInBundleURL(appContainer.url);
|
||||||
fixPermissionsOfAppBundle(updatedAppURL.path);
|
fixPermissionsOfAppBundle(updatedAppURL.path);
|
||||||
registerPath(updatedAppURL.path, 0, YES);
|
if (!registerPath(updatedAppURL.path, 0, YES)) {
|
||||||
|
[[NSFileManager defaultManager] removeItemAtURL:appContainer.url error:nil];
|
||||||
|
return 181;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle developer mode after installing and registering the app, to ensure that we
|
// Handle developer mode after installing and registering the app, to ensure that we
|
||||||
// don't arm developer mode but then fail to install the app
|
// don't arm developer mode but then fail to install the app
|
||||||
|
@ -958,12 +967,12 @@ int installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate,
|
||||||
if (!alreadyEnabled) {
|
if (!alreadyEnabled) {
|
||||||
NSLog(@"[installApp] app requires developer mode and we have successfully armed it");
|
NSLog(@"[installApp] app requires developer mode and we have successfully armed it");
|
||||||
// non-fatal
|
// non-fatal
|
||||||
return 180;
|
return 182;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
NSLog(@"[installApp] failed to arm developer mode");
|
NSLog(@"[installApp] failed to arm developer mode");
|
||||||
// fatal
|
// fatal
|
||||||
return 181;
|
return 183;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1070,6 +1079,7 @@ int uninstallAppById(NSString* appId, BOOL useCustomMethod)
|
||||||
|
|
||||||
// 166: IPA does not exist or is not accessible
|
// 166: IPA does not exist or is not accessible
|
||||||
// 167: IPA does not appear to contain an app
|
// 167: IPA does not appear to contain an app
|
||||||
|
// 180: IPA contains an encrypted binary
|
||||||
int installIpa(NSString* ipaPath, BOOL force, BOOL useInstalldMethod)
|
int installIpa(NSString* ipaPath, BOOL force, BOOL useInstalldMethod)
|
||||||
{
|
{
|
||||||
cleanRestrictions();
|
cleanRestrictions();
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
extern void registerPath(NSString* path, BOOL unregister, BOOL system);
|
extern bool registerPath(NSString *path, BOOL unregister, BOOL forceSystem);
|
|
@ -81,8 +81,8 @@ NSDictionary *constructEnvironmentVariablesForContainerPath(NSString *containerP
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerPath(NSString *path, BOOL unregister, BOOL forceSystem) {
|
bool registerPath(NSString *path, BOOL unregister, BOOL forceSystem) {
|
||||||
if (!path) return;
|
if (!path) return false;
|
||||||
|
|
||||||
LSApplicationWorkspace *workspace = [LSApplicationWorkspace defaultWorkspace];
|
LSApplicationWorkspace *workspace = [LSApplicationWorkspace defaultWorkspace];
|
||||||
if (unregister && ![[NSFileManager defaultManager] fileExistsAtPath:path]) {
|
if (unregister && ![[NSFileManager defaultManager] fileExistsAtPath:path]) {
|
||||||
|
@ -97,7 +97,7 @@ void registerPath(NSString *path, BOOL unregister, BOOL forceSystem) {
|
||||||
NSDictionary *appInfoPlist = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingPathComponent:@"Info.plist"]];
|
NSDictionary *appInfoPlist = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingPathComponent:@"Info.plist"]];
|
||||||
NSString *appBundleID = [appInfoPlist objectForKey:@"CFBundleIdentifier"];
|
NSString *appBundleID = [appInfoPlist objectForKey:@"CFBundleIdentifier"];
|
||||||
|
|
||||||
if([immutableAppBundleIdentifiers() containsObject:appBundleID.lowercaseString]) return;
|
if([immutableAppBundleIdentifiers() containsObject:appBundleID.lowercaseString]) return false;
|
||||||
|
|
||||||
if (appBundleID && !unregister) {
|
if (appBundleID && !unregister) {
|
||||||
NSString *appExecutablePath = [path stringByAppendingPathComponent:appInfoPlist[@"CFBundleExecutable"]];
|
NSString *appExecutablePath = [path stringByAppendingPathComponent:appInfoPlist[@"CFBundleExecutable"]];
|
||||||
|
@ -236,11 +236,19 @@ void registerPath(NSString *path, BOOL unregister, BOOL forceSystem) {
|
||||||
|
|
||||||
if (![workspace registerApplicationDictionary:dictToRegister]) {
|
if (![workspace registerApplicationDictionary:dictToRegister]) {
|
||||||
NSLog(@"Error: Unable to register %@", path);
|
NSLog(@"Error: Unable to register %@", path);
|
||||||
|
NSLog(@"Used dictionary: {");
|
||||||
|
[dictToRegister enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSObject *obj, BOOL *stop) {
|
||||||
|
NSLog(@"%@ = %@", key, obj);
|
||||||
|
}];
|
||||||
|
NSLog(@"}");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
NSURL *url = [NSURL fileURLWithPath:path];
|
NSURL *url = [NSURL fileURLWithPath:path];
|
||||||
if (![workspace unregisterApplication:url]) {
|
if (![workspace unregisterApplication:url]) {
|
||||||
NSLog(@"Error: Unable to register %@", path);
|
NSLog(@"Error: Unable to register %@", path);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,9 +75,15 @@ extern NSUserDefaults* trollStoreUserDefaults();
|
||||||
errorDescription = @"The app you tried to install has the same identifier as a system app already installed on the device. The installation has been prevented to protect you from possible bootloops or other issues.";
|
errorDescription = @"The app you tried to install has the same identifier as a system app already installed on the device. The installation has been prevented to protect you from possible bootloops or other issues.";
|
||||||
break;
|
break;
|
||||||
case 180:
|
case 180:
|
||||||
errorDescription = @"The app was installed successfully, but requires developer mode to be enabled to run. After rebooting, select \"Turn On\" to enable developer mode.";
|
errorDescription = @"The app you tried to install contains encrypted binaries, which cannot have the CoreTrust bypass applied to them. Please ensure you install decrypted apps.";
|
||||||
break;
|
break;
|
||||||
case 181:
|
case 181:
|
||||||
|
errorDescription = @"Failed to add app to icon cache.";
|
||||||
|
break;
|
||||||
|
case 182:
|
||||||
|
errorDescription = @"The app was installed successfully, but requires developer mode to be enabled to run. After rebooting, select \"Turn On\" to enable developer mode.";
|
||||||
|
break;
|
||||||
|
case 183:
|
||||||
errorDescription = @"Failed to enable developer mode.";
|
errorDescription = @"Failed to enable developer mode.";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue