Compare commits

..

No commits in common. "f5a2dfae0184b26cd90dcb8f45530395d294541a" and "2587c320d02fa3753d700eb65a679fc07aa637cd" have entirely different histories.

51 changed files with 1430 additions and 139 deletions

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "ChOma"]
path = ChOma
url = https://github.com/opa334/ChOma

1
ChOma

@ -1 +0,0 @@
Subproject commit b819acbad86eae7a187062d344a1cb62c60daa9b

View File

@ -2,10 +2,10 @@ TARGET = fastPathSign
CC = clang CC = clang
CFLAGS = -framework Foundation -framework CoreServices -framework Security -fobjc-arc $(shell pkg-config --cflags libcrypto) -I../../ChOma/src CFLAGS = -framework Foundation -framework CoreServices -framework Security -fobjc-arc $(shell pkg-config --cflags libcrypto) -Isrc/external/include
LDFLAGS = $(shell pkg-config --libs libcrypto) LDFLAGS = $(shell pkg-config --libs libcrypto) -Lsrc/external/lib -lchoma
$(TARGET): $(wildcard src/*.m src/*.c ../../ChOma/src/*.c) $(TARGET): $(wildcard src/*.m src/*.c)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
clean: clean:

View File

@ -3,16 +3,16 @@
#include <stdio.h> #include <stdio.h>
#include <dirent.h> #include <dirent.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "CSBlob.h" #include <choma/CSBlob.h>
#include "MachOByteOrder.h" #include <choma/MachOByteOrder.h>
#include "MachO.h" #include <choma/MachO.h>
#include "Host.h" #include <choma/Host.h>
#include "MemoryStream.h" #include <choma/MemoryStream.h>
#include "FileStream.h" #include <choma/FileStream.h>
#include "BufferedStream.h" #include <choma/BufferedStream.h>
#include "SignOSSL.h" #include <choma/SignOSSL.h>
#include "CodeDirectory.h" #include <choma/CodeDirectory.h>
#include "Base64.h" #include <choma/Base64.h>
#include "Templates/AppStoreCodeDirectory.h" #include "Templates/AppStoreCodeDirectory.h"
#include "Templates/SignatureBlob.h" #include "Templates/SignatureBlob.h"
#include "Templates/DecryptedSignature.h" #include "Templates/DecryptedSignature.h"
@ -142,6 +142,17 @@ 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;

View File

View File

@ -0,0 +1,11 @@
#ifndef BASE64_H
#define BASE64_H
#include <stdint.h>
#include <stdlib.h>
char *base64_encode(const unsigned char *data,
size_t input_length,
size_t *output_length);
#endif // BASE64_H

View File

@ -0,0 +1,19 @@
#ifndef BUFFERED_STREAM_H
#define BUFFERED_STREAM_H
#include "MemoryStream.h"
#include <stdbool.h>
#define BUFFERED_STREAM_FLAG_AUTO_EXPAND (1 << 0)
typedef struct BufferedStreamContext {
uint8_t *buffer;
size_t bufferSize;
uint32_t subBufferStart;
size_t subBufferSize;
} BufferedStreamContext;
MemoryStream *buffered_stream_init_from_buffer_nocopy(void *buffer, size_t bufferSize, uint32_t flags);
MemoryStream *buffered_stream_init_from_buffer(void *buffer, size_t bufferSize, uint32_t flags);
#endif // BUFFERED_STREAM_H

View File

@ -0,0 +1,119 @@
#ifndef CS_BLOB_H
#define CS_BLOB_H
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include "FAT.h"
#include "MachO.h"
#include "MemoryStream.h"
// Blob index
typedef struct __BlobIndex {
uint32_t type;
uint32_t offset;
} CS_BlobIndex;
// CMS superblob
typedef struct __SuperBlob {
uint32_t magic;
uint32_t length;
uint32_t count;
CS_BlobIndex index[];
} CS_SuperBlob;
typedef struct __GenericBlob {
uint32_t magic; /* magic number */
uint32_t length; /* total length of blob */
char data[];
} CS_GenericBlob;
// CMS blob magic types
enum {
CSMAGIC_REQUIREMENT = 0xfade0c00,
CSMAGIC_REQUIREMENTS = 0xfade0c01,
CSMAGIC_CODEDIRECTORY = 0xfade0c02,
CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0,
CSMAGIC_EMBEDDED_SIGNATURE_OLD = 0xfade0b02,
CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171,
CSMAGIC_EMBEDDED_DER_ENTITLEMENTS = 0xfade7172,
CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1,
CSMAGIC_BLOBWRAPPER = 0xfade0b01,
CSMAGIC_EMBEDDED_LAUNCH_CONSTRAINT = 0xfade8181,
} CS_BlobMagic;
enum {
CSSLOT_CODEDIRECTORY = 0,
CSSLOT_INFOSLOT = 1,
CSSLOT_REQUIREMENTS = 2,
CSSLOT_RESOURCEDIR = 3,
CSSLOT_APPLICATION = 4,
CSSLOT_ENTITLEMENTS = 5,
CSSLOT_DER_ENTITLEMENTS = 7,
CSSLOT_LAUNCH_CONSTRAINT_SELF = 8,
CSSLOT_LAUNCH_CONSTRAINT_PARENT = 9,
CSSLOT_LAUNCH_CONSTRAINT_RESPONSIBLE = 10,
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;
typedef struct s_CS_DecodedBlob {
struct s_CS_DecodedBlob *next;
uint32_t type;
MemoryStream *stream;
} CS_DecodedBlob;
typedef struct s_CS_DecodedSuperBlob {
uint32_t magic;
struct s_CS_DecodedBlob *firstBlob;
} CS_DecodedSuperBlob;
// Convert blob magic to readable blob type string
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
int macho_extract_cs_to_file(MachO *macho, CS_SuperBlob *superblob);
int macho_find_code_signature_bounds(MachO *macho, uint32_t *offsetOut, uint32_t *sizeOut);
CS_SuperBlob *macho_read_code_signature(MachO *macho);
int macho_replace_code_signature(MachO *macho, CS_SuperBlob *superblob);
int update_load_commands(MachO *macho, CS_SuperBlob *superblob, uint64_t originalSize);
CS_DecodedBlob *csd_blob_init(uint32_t type, CS_GenericBlob *blobData);
int csd_blob_read(CS_DecodedBlob *blob, uint64_t offset, size_t size, void *outBuf);
int csd_blob_write(CS_DecodedBlob *blob, uint64_t offset, size_t size, const void *inBuf);
int csd_blob_insert(CS_DecodedBlob *blob, uint64_t offset, size_t size, const void *inBuf);
int csd_blob_delete(CS_DecodedBlob *blob, uint64_t offset, size_t size);
int csd_blob_read_string(CS_DecodedBlob *blob, uint64_t offset, char **outString);
int csd_blob_write_string(CS_DecodedBlob *blob, uint64_t offset, const char *string);
int csd_blob_get_size(CS_DecodedBlob *blob);
uint32_t csd_blob_get_type(CS_DecodedBlob *blob);
void csd_blob_set_type(CS_DecodedBlob *blob, uint32_t type);
void csd_blob_free(CS_DecodedBlob *blob);
CS_DecodedSuperBlob *csd_superblob_decode(CS_SuperBlob *superblob);
CS_SuperBlob *csd_superblob_encode(CS_DecodedSuperBlob *decodedSuperblob);
CS_DecodedBlob *csd_superblob_find_blob(CS_DecodedSuperBlob *superblob, uint32_t type, uint32_t *indexOut);
int csd_superblob_insert_blob_after_blob(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToInsert, CS_DecodedBlob *afterBlob);
int csd_superblob_insert_blob_at_index(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToInsert, uint32_t atIndex);
int csd_superblob_append_blob(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToAppend);
int csd_superblob_remove_blob(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToRemove); // <- Important: When calling this, caller is responsible for freeing blobToRemove
int csd_superblob_remove_blob_at_index(CS_DecodedSuperBlob *superblob, uint32_t atIndex);
int csd_superblob_print_content(CS_DecodedSuperBlob *decodedSuperblob, MachO *macho, bool printAllSlots, bool verifySlots);
void csd_superblob_free(CS_DecodedSuperBlob *decodedSuperblob);
#endif // CS_BLOB_H

View File

@ -0,0 +1,53 @@
#ifndef CODE_DIRECTORY_H
#define CODE_DIRECTORY_H
#include <stdint.h>
#include <math.h>
#include <CommonCrypto/CommonDigest.h>
#include "MachO.h"
#include "CSBlob.h"
#include "FAT.h"
#include "MachOByteOrder.h"
#include "MachOLoadCommand.h"
#include "MemoryStream.h"
// Code directory blob header
typedef struct __CodeDirectory {
uint32_t magic;
uint32_t length;
uint32_t version;
uint32_t flags;
uint32_t hashOffset;
uint32_t identOffset;
uint32_t nSpecialSlots;
uint32_t nCodeSlots;
uint32_t codeLimit;
uint8_t hashSize;
uint8_t hashType;
uint8_t spare1;
uint8_t pageSize;
uint32_t spare2;
uint32_t scatterOffset;
uint32_t teamOffset;
} CS_CodeDirectory;
enum CS_HashType {
CS_HASHTYPE_SHA160_160 = 1,
CS_HASHTYPE_SHA256_256 = 2,
CS_HASHTYPE_SHA256_160 = 3,
CS_HASHTYPE_SHA384_384 = 4,
};
char *csd_code_directory_copy_identity(CS_DecodedBlob *codeDirBlob, uint32_t *offsetOut);
char *csd_code_directory_copy_team_id(CS_DecodedBlob *codeDirBlob, uint32_t *offsetOut);
int csd_code_directory_set_team_id(CS_DecodedBlob *codeDirBlob, char *newTeamID);
uint32_t csd_code_directory_get_flags(CS_DecodedBlob *codeDirBlob);
void csd_code_directory_set_flags(CS_DecodedBlob *codeDirBlob, uint32_t flags);
uint8_t csd_code_directory_get_hash_type(CS_DecodedBlob *codeDirBlob);
void csd_code_directory_set_hash_type(CS_DecodedBlob *codeDirBlob, uint8_t hashType);
int csd_code_directory_print_content(CS_DecodedBlob *codeDirBlob, MachO *macho, bool printSlots, bool verifySlots);
void csd_code_directory_update(CS_DecodedBlob *codeDirBlob, MachO *macho);
#endif // CODE_DIRECTORY_H

View File

@ -0,0 +1,46 @@
#ifndef MACHO_H
#define MACHO_H
#include <stdio.h>
#include <libkern/OSByteOrder.h>
#include <mach/mach.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <sys/stat.h>
#include "MemoryStream.h"
typedef struct MachO MachO;
// A FAT structure can either represent a FAT file with multiple slices, in which the slices will be loaded into the slices attribute
// Or a single slice MachO, in which case it serves as a compatibility layer and the single slice will also be loaded into the slices attribute
typedef struct FAT
{
MemoryStream *stream;
MachO **slices;
uint32_t slicesCount;
int fileDescriptor;
} FAT;
int fat_read_at_offset(FAT *fat, uint64_t offset, size_t size, void *outBuf);
MemoryStream *fat_get_stream(FAT *fat);
// Initialise a FAT structure from a memory stream
FAT *fat_init_from_memory_stream(MemoryStream *stream);
// Initialise a FAT structure using the path to the file
FAT *fat_init_from_path(const char *filePath);
// 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);
// 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
void fat_free(FAT *fat);
#endif // MACHO_H

View File

@ -0,0 +1,21 @@
#ifndef FILE_STREAM_H
#define FILE_STREAM_H
#include "MemoryStream.h"
#define FILE_STREAM_SIZE_AUTO 0
#define FILE_STREAM_FLAG_WRITABLE (1 << 0)
#define FILE_STREAM_FLAG_AUTO_EXPAND (1 << 1)
typedef struct FileStreamContext {
int fd;
size_t fileSize;
uint32_t bufferStart;
size_t bufferSize;
} FileStreamContext;
MemoryStream *file_stream_init_from_file_descriptor_nodup(int fd, uint32_t bufferStart, size_t bufferSize, uint32_t flags);
MemoryStream *file_stream_init_from_file_descriptor(int fd, uint32_t bufferStart, size_t bufferSize, uint32_t flags);
MemoryStream *file_stream_init_from_path(const char *path, uint32_t bufferStart, size_t bufferSize, uint32_t flags);
#endif // FILE_STREAM_H

View File

@ -0,0 +1,10 @@
#ifndef HOST_H
#define HOST_H
#include "FAT.h"
// Retrieve the preferred MachO slice from a FAT
// Preferred slice as in the slice that the kernel would use when loading the file
MachO *fat_find_preferred_slice(FAT *fat);
#endif // HOST_H

View File

@ -0,0 +1,68 @@
#ifndef MACHO_SLICE_H
#define MACHO_SLICE_H
#include <stdbool.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include "MemoryStream.h"
#include "FAT.h"
typedef struct MachOSegment
{
struct segment_command_64 command;
struct section_64 sections[];
} __attribute__((__packed__)) MachOSegment;
typedef struct FilesetMachO {
char *entry_id;
uint64_t vmaddr;
uint64_t fileoff;
FAT *underlyingMachO;
} FilesetMachO;
typedef struct MachO {
MemoryStream *stream;
bool isSupported;
struct mach_header_64 machHeader;
struct fat_arch_64 archDescriptor;
uint32_t filesetCount;
FilesetMachO *filesetMachos;
uint32_t segmentCount;
MachOSegment **segments;
} MachO;
// Read data from a MachO at a specified offset
int macho_read_at_offset(MachO *macho, uint64_t offset, size_t size, void *outBuf);
// Write data from a MachO at a specified offset, auto expands, only works if opened via macho_init_for_writing
int macho_write_at_offset(MachO *macho, uint64_t offset, size_t size, void *inBuf);
MemoryStream *macho_get_stream(MachO *macho);
uint32_t macho_get_filetype(MachO *macho);
// Perform translation between file offsets and virtual addresses
int macho_translate_fileoff_to_vmaddr(MachO *macho, uint64_t fileoff, uint64_t *vmaddrOut, MachOSegment **segmentOut);
int macho_translate_vmaddr_to_fileoff(MachO *macho, uint64_t vmaddr, uint64_t *fileoffOut, MachOSegment **segmentOut);
// Read data from a MachO at a specified virtual address
int macho_read_at_vmaddr(MachO *macho, uint64_t vmaddr, size_t size, void *outBuf);
int macho_enumerate_load_commands(MachO *macho, void (^enumeratorBlock)(struct load_command loadCommand, uint64_t offset, void *cmd, bool *stop));
// Initialise a MachO object from a MemoryStream and it's corresponding FAT arch descriptor
MachO *macho_init(MemoryStream *stream, struct fat_arch_64 archDescriptor);
// Initialize a single slice macho for writing to it
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);
#endif // MACHO_SLICE_H

View File

@ -0,0 +1,171 @@
#ifndef MACHO_BYTE_ORDER_H
#define MACHO_BYTE_ORDER_H
#include <stdio.h>
#include <stdlib.h>
// 8-bit integers needed for CodeDirectory
#define BIG_TO_HOST(n) _Generic((n), \
int8_t: n, \
uint8_t: n, \
int16_t: OSSwapBigToHostInt16(n), \
uint16_t: OSSwapBigToHostInt16(n), \
int32_t: OSSwapBigToHostInt32(n), \
uint32_t: OSSwapBigToHostInt32(n), \
int64_t: OSSwapBigToHostInt64(n), \
uint64_t: OSSwapBigToHostInt64(n) \
)
#define HOST_TO_BIG(n) _Generic((n), \
int8_t: n, \
uint8_t: n, \
uint16_t: OSSwapHostToBigInt16(n), \
int16_t: OSSwapHostToBigInt16(n), \
int32_t: OSSwapHostToBigInt32(n), \
uint32_t: OSSwapHostToBigInt32(n), \
int64_t: OSSwapHostToBigInt64(n), \
uint64_t: OSSwapHostToBigInt64(n) \
)
#define LITTLE_TO_HOST(n) _Generic((n), \
int8_t: n, \
uint8_t: n, \
int16_t: OSSwapLittleToHostInt16(n), \
uint16_t: OSSwapLittleToHostInt16(n), \
int32_t: OSSwapLittleToHostInt32(n), \
uint32_t: OSSwapLittleToHostInt32(n), \
int64_t: OSSwapLittleToHostInt64(n), \
uint64_t: OSSwapLittleToHostInt64(n) \
)
#define HOST_TO_LITTLE(n) _Generic((n), \
int8_t: n, \
uint8_t: n, \
int16_t: OSSwapHostToLittleInt16(n), \
uint16_t: OSSwapHostToLittleInt16(n), \
int32_t: OSSwapHostToLittleInt32(n), \
uint32_t: OSSwapHostToLittleInt32(n), \
int64_t: OSSwapHostToLittleInt64(n), \
uint64_t: OSSwapHostToLittleInt64(n) \
)
#define HOST_TO_LITTLE_APPLIER(instance, member) \
(instance)->member = HOST_TO_LITTLE((instance)->member)
#define HOST_TO_BIG_APPLIER(instance, member) \
(instance)->member = HOST_TO_BIG((instance)->member)
#define LITTLE_TO_HOST_APPLIER(instance, member) \
(instance)->member = LITTLE_TO_HOST((instance)->member)
#define BIG_TO_HOST_APPLIER(instance, member) \
(instance)->member = BIG_TO_HOST((instance)->member)
#define FAT_HEADER_APPLY_BYTE_ORDER(fh, applier) \
applier(fh, magic); \
applier(fh, nfat_arch);
#define FAT_ARCH_APPLY_BYTE_ORDER(arch, applier) \
applier(arch, cputype); \
applier(arch, cpusubtype); \
applier(arch, offset); \
applier(arch, size); \
applier(arch, align); \
#define FAT_ARCH_64_APPLY_BYTE_ORDER(arch, applier) \
applier(arch, cputype); \
applier(arch, cpusubtype); \
applier(arch, offset); \
applier(arch, size); \
applier(arch, align); \
applier(arch, reserved); \
#define MACH_HEADER_APPLY_BYTE_ORDER(mh, applier) \
applier(mh, magic); \
applier(mh, cputype); \
applier(mh, cpusubtype); \
applier(mh, filetype); \
applier(mh, ncmds); \
applier(mh, sizeofcmds); \
applier(mh, reserved);
#define LOAD_COMMAND_APPLY_BYTE_ORDER(lc, applier) \
applier(lc, cmd); \
applier(lc, cmdsize);
#define LINKEDIT_DATA_COMMAND_APPLY_BYTE_ORDER(lc, applier) \
applier(lc, cmd); \
applier(lc, cmdsize); \
applier(lc, dataoff); \
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) \
applier(bi, type); \
applier(bi, offset);
#define SUPERBLOB_APPLY_BYTE_ORDER(sb, applier) \
applier(sb, magic); \
applier(sb, length); \
applier(sb, count);
#define GENERIC_BLOB_APPLY_BYTE_ORDER(gb, applier) \
applier(gb, magic); \
applier(gb, length);
#define CODE_DIRECTORY_APPLY_BYTE_ORDER(cd, applier) \
applier(cd, magic); \
applier(cd, length); \
applier(cd, version); \
applier(cd, flags); \
applier(cd, hashOffset); \
applier(cd, identOffset); \
applier(cd, nSpecialSlots); \
applier(cd, nCodeSlots); \
applier(cd, codeLimit); \
applier(cd, hashSize); \
applier(cd, hashType); \
applier(cd, spare1); \
applier(cd, pageSize); \
applier(cd, spare2); \
applier(cd, scatterOffset); \
applier(cd, teamOffset);
#define SEGMENT_COMMAND_64_APPLY_BYTE_ORDER(sc64, applier) \
applier(sc64, cmd); \
applier(sc64, cmdsize); \
applier(sc64, fileoff); \
applier(sc64, filesize); \
applier(sc64, vmaddr); \
applier(sc64, vmsize); \
applier(sc64, flags); \
applier(sc64, initprot); \
applier(sc64, maxprot); \
applier(sc64, nsects);
#define SECTION_64_APPLY_BYTE_ORDER(sc64, applier) \
applier(sc64, addr); \
applier(sc64, align); \
applier(sc64, flags); \
applier(sc64, nreloc); \
applier(sc64, offset); \
applier(sc64, reserved1); \
applier(sc64, reserved2); \
applier(sc64, reserved3); \
applier(sc64, size);
#define FILESET_ENTRY_COMMAND_APPLY_BYTE_ORDER(fse, applier) \
applier(fse, cmd); \
applier(fse, cmdsize); \
applier(fse, vmaddr); \
applier(fse, fileoff); \
applier(fse, entry_id.offset); \
applier(fse, reserved); \
#endif // MACHO_BYTE_ORDER_H

View File

@ -0,0 +1,16 @@
#ifndef MACHO_LOAD_COMMAND_H
#define MACHO_LOAD_COMMAND_H
#include <mach-o/loader.h>
#include "MachO.h"
#include "CSBlob.h"
#include "FileStream.h"
#include "MachOByteOrder.h"
// Convert load command to load command name
char *load_command_to_string(int loadCommand);
void update_segment_command_64(MachO *macho, const char *segmentName, uint64_t vmaddr, uint64_t vmsize, uint64_t fileoff, uint64_t filesize);
void update_lc_code_signature(MachO *macho, uint64_t size);
int update_load_commands_for_coretrust_bypass(MachO *macho, CS_SuperBlob *superblob, uint64_t originalCodeSignatureSize, uint64_t originalMachOSize);
#endif // MACHO_LOAD_COMMAND_H

View File

@ -0,0 +1,60 @@
#ifndef MEMORY_STREAM_H
#define MEMORY_STREAM_H
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#define MEMORY_STREAM_FLAG_OWNS_DATA (1 << 0)
#define MEMORY_STREAM_FLAG_MUTABLE (1 << 1)
#define MEMORY_STREAM_FLAG_AUTO_EXPAND (1 << 2)
#define MEMORY_STREAM_SIZE_INVALID (size_t)-1
// A generic memory IO interface that is used throughout this project
// Can be backed by anything, just the functions have to be implemented
typedef struct s_MemoryStream {
void *context;
uint32_t flags;
int (*read)(struct s_MemoryStream *stream, uint64_t offset, size_t size, void *outBuf);
int (*write)(struct s_MemoryStream *stream, uint64_t offset, size_t size, const void *inBuf);
int (*getSize)(struct s_MemoryStream *stream, size_t *sizeOut);
uint8_t *(*getRawPtr)(struct s_MemoryStream *stream);
int (*trim)(struct s_MemoryStream *stream, size_t trimAtStart, size_t trimAtEnd);
int (*expand)(struct s_MemoryStream *stream, size_t expandAtStart, size_t expandAtEnd);
struct s_MemoryStream *(*hardclone)(struct s_MemoryStream *stream);
struct s_MemoryStream *(*softclone)(struct s_MemoryStream *stream);
void (*free)(struct s_MemoryStream *stream);
} MemoryStream;
int memory_stream_read(MemoryStream *stream, uint64_t offset, size_t size, void *outBuf);
int memory_stream_write(MemoryStream *stream, uint64_t offset, size_t size, const void *inBuf);
int memory_stream_insert(MemoryStream *stream, uint64_t offset, size_t size, const void *inBuf);
int memory_stream_delete(MemoryStream *stream, uint64_t offset, size_t size);
int memory_stream_read_string(MemoryStream *stream, uint64_t offset, char **outString);
int memory_stream_write_string(MemoryStream *stream, uint64_t offset, const char *string);
size_t memory_stream_get_size(MemoryStream *stream);
uint8_t *memory_stream_get_raw_pointer(MemoryStream *stream);
uint32_t memory_stream_get_flags(MemoryStream *stream);
MemoryStream *memory_stream_softclone(MemoryStream *stream);
MemoryStream *memory_stream_hardclone(MemoryStream *stream);
int memory_stream_trim(MemoryStream *stream, size_t trimAtStart, size_t trimAtEnd);
int memory_stream_expand(MemoryStream *stream, size_t expandAtStart, size_t expandAtEnd);
void memory_stream_free(MemoryStream *stream);
int memory_stream_copy_data(MemoryStream *originStream, uint64_t originOffset, MemoryStream *targetStream, uint64_t targetOffset, size_t size);
int memory_stream_find_memory(MemoryStream *stream, uint64_t searchOffset, size_t searchSize, void *bytes, void *mask, size_t nbytes, uint16_t alignment, uint64_t *foundOffsetOut);
#endif // MEMORY_STREAM_H

View File

@ -0,0 +1,47 @@
#include <stdint.h>
#include "MachO.h"
#define METRIC_TYPE_PATTERN 1
#define METRIC_TYPE_STRING_XREF 2
#define METRIC_TYPE_FUNCTION_XREF 3
typedef struct PFSection {
MachO *macho;
uint64_t fileoff;
uint64_t vmaddr;
uint64_t size;
uint8_t *cache;
bool ownsCache;
} PFSection;
PFSection *pf_section_init_from_macho(MachO *macho, const char *filesetEntryId, const char *segName, const char *sectName);
int pf_section_read_at_relative_offset(PFSection *section, uint64_t rel, size_t size, void *outBuf);
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 {
uint32_t type;
} MetricShared;
typedef enum {
BYTE_PATTERN_ALIGN_8_BIT,
BYTE_PATTERN_ALIGN_16_BIT,
BYTE_PATTERN_ALIGN_32_BIT,
BYTE_PATTERN_ALIGN_64_BIT,
} BytePatternAlignment;
typedef struct BytePatternMetric {
MetricShared shared;
void *bytes;
void *mask;
size_t nbytes;
BytePatternAlignment alignment;
} BytePatternMetric;
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));

View File

@ -0,0 +1,16 @@
#ifndef SIGN_OSSL_H
#define SIGN_OSSL_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/err.h>
unsigned char *signWithRSA(unsigned char *inputData, size_t inputDataLength, unsigned char *key, size_t key_len, size_t *outputDataLength);
#endif // SIGN_OSSL_H
// 0xA422

View File

@ -0,0 +1,7 @@
#include <stdint.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);
int count_digits(int64_t num);
void print_hash(uint8_t *hash, size_t size);

Binary file not shown.

View File

@ -1,9 +1,9 @@
#include "codesign.h" #include "codesign.h"
#include "coretrust_bug.h" #include "coretrust_bug.h"
#include "FAT.h" #include <choma/FAT.h>
#include "MachO.h" #include <choma/MachO.h>
#include "FileStream.h" #include <choma/FileStream.h>
#include "Host.h" #include <choma/Host.h>
#include <copyfile.h> #include <copyfile.h>
char *extract_preferred_slice(const char *fatPath) char *extract_preferred_slice(const char *fatPath)

View File

@ -4,13 +4,13 @@ Upstream-Contact: opa334 <opa334@protonmail.com>
Source: https://github.com/opa334/TrollStore Source: https://github.com/opa334/TrollStore
Files: * Files: *
Copyright: 2022-2024 Lars Fröder Copyright: 2022-2023 Lars Fröder
License: MIT License: MIT
Files: RootHelper/uicache.m Files: RootHelper/uicache.m
Copyright: Copyright (c) 2019 CoolStar, Copyright: Copyright (c) 2019 CoolStar,
Modified work Copyright (c) 2020-2022 Procursus Team <team@procurs.us> Modified work Copyright (c) 2020-2022 Procursus Team <team@procurs.us>
Modified work Copyright (c) 2022-2024 Lars Fröder <opa334@protonmail.com> Modified work Copyright (c) 2022-2023 Lars Fröder <opa334@protonmail.com>
License: BSD-4-Clause License: BSD-4-Clause
License: BSD-4-Clause License: BSD-4-Clause

View File

@ -1,4 +1,4 @@
TOPTARGETS := all clean update TOPTARGETS := all clean
$(TOPTARGETS): pre_build make_fastPathSign make_roothelper make_trollstore make_trollhelper_embedded make_trollhelper_package assemble_trollstore build_installer15 build_installer64e $(TOPTARGETS): pre_build make_fastPathSign make_roothelper make_trollstore make_trollhelper_embedded make_trollhelper_package assemble_trollstore build_installer15 build_installer64e
@ -72,7 +72,6 @@ build_installer64e:
zip -vrD ../../_build/TrollHelper_arm64e.ipa * ; \ zip -vrD ../../_build/TrollHelper_arm64e.ipa * ; \
popd popd
@rm -rf ./_build/tmp64e @rm -rf ./_build/tmp64e
endif endif
.PHONY: $(TOPTARGETS) pre_build assemble_trollstore make_trollhelper_package make_trollhelper_embedded build_installer15 build_installer64e .PHONY: $(TOPTARGETS) pre_build assemble_trollstore make_trollhelper_package make_trollhelper_embedded build_installer15 build_installer64e

0
RootHelper/.gitignore vendored Normal file
View File

View File

@ -7,9 +7,9 @@ include $(THEOS)/makefiles/common.mk
TOOL_NAME = trollstorehelper TOOL_NAME = trollstorehelper
trollstorehelper_FILES = $(wildcard *.m) $(wildcard ../Shared/*.m) $(wildcard ../ChOma/src/*.c) ../Exploits/fastPathSign/src/coretrust_bug.c ../Exploits/fastPathSign/src/codesign.m trollstorehelper_FILES = $(wildcard *.m) $(wildcard ../Shared/*.m) ../Exploits/fastPathSign/src/coretrust_bug.c ../Exploits/fastPathSign/src/codesign.m
trollstorehelper_CFLAGS = -fobjc-arc -I../Shared $(shell pkg-config --cflags libcrypto) -I../ChOma/src -I../Exploits/fastPathSign/src trollstorehelper_CFLAGS = -fobjc-arc -I../Shared $(shell pkg-config --cflags libcrypto) -Iexternal/include -I../Exploits/fastPathSign/src
trollstorehelper_LDFLAGS = -L../ChOma/external/ios -lcrypto trollstorehelper_LDFLAGS = -Lexternal/lib -lcrypto -lchoma
trollstorehelper_CODESIGN_FLAGS = --entitlements entitlements.plist trollstorehelper_CODESIGN_FLAGS = --entitlements entitlements.plist
trollstorehelper_INSTALL_PATH = /usr/local/bin trollstorehelper_INSTALL_PATH = /usr/local/bin
trollstorehelper_LIBRARIES = archive trollstorehelper_LIBRARIES = archive

View File

@ -1,6 +1,6 @@
Package: com.opa334.trollstoreroothelper Package: com.opa334.trollstoreroothelper
Name: trollstoreroothelper Name: trollstoreroothelper
Version: 2.0.10 Version: 2.0.9
Architecture: iphoneos-arm Architecture: iphoneos-arm
Description: An awesome tool of some sort!! Description: An awesome tool of some sort!!
Maintainer: opa334 Maintainer: opa334

View File

@ -0,0 +1,11 @@
#ifndef BASE64_H
#define BASE64_H
#include <stdint.h>
#include <stdlib.h>
char *base64_encode(const unsigned char *data,
size_t input_length,
size_t *output_length);
#endif // BASE64_H

View File

@ -0,0 +1,19 @@
#ifndef BUFFERED_STREAM_H
#define BUFFERED_STREAM_H
#include "MemoryStream.h"
#include <stdbool.h>
#define BUFFERED_STREAM_FLAG_AUTO_EXPAND (1 << 0)
typedef struct BufferedStreamContext {
uint8_t *buffer;
size_t bufferSize;
uint32_t subBufferStart;
size_t subBufferSize;
} BufferedStreamContext;
MemoryStream *buffered_stream_init_from_buffer_nocopy(void *buffer, size_t bufferSize, uint32_t flags);
MemoryStream *buffered_stream_init_from_buffer(void *buffer, size_t bufferSize, uint32_t flags);
#endif // BUFFERED_STREAM_H

View File

@ -0,0 +1,119 @@
#ifndef CS_BLOB_H
#define CS_BLOB_H
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include "FAT.h"
#include "MachO.h"
#include "MemoryStream.h"
// Blob index
typedef struct __BlobIndex {
uint32_t type;
uint32_t offset;
} CS_BlobIndex;
// CMS superblob
typedef struct __SuperBlob {
uint32_t magic;
uint32_t length;
uint32_t count;
CS_BlobIndex index[];
} CS_SuperBlob;
typedef struct __GenericBlob {
uint32_t magic; /* magic number */
uint32_t length; /* total length of blob */
char data[];
} CS_GenericBlob;
// CMS blob magic types
enum {
CSMAGIC_REQUIREMENT = 0xfade0c00,
CSMAGIC_REQUIREMENTS = 0xfade0c01,
CSMAGIC_CODEDIRECTORY = 0xfade0c02,
CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0,
CSMAGIC_EMBEDDED_SIGNATURE_OLD = 0xfade0b02,
CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171,
CSMAGIC_EMBEDDED_DER_ENTITLEMENTS = 0xfade7172,
CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1,
CSMAGIC_BLOBWRAPPER = 0xfade0b01,
CSMAGIC_EMBEDDED_LAUNCH_CONSTRAINT = 0xfade8181,
} CS_BlobMagic;
enum {
CSSLOT_CODEDIRECTORY = 0,
CSSLOT_INFOSLOT = 1,
CSSLOT_REQUIREMENTS = 2,
CSSLOT_RESOURCEDIR = 3,
CSSLOT_APPLICATION = 4,
CSSLOT_ENTITLEMENTS = 5,
CSSLOT_DER_ENTITLEMENTS = 7,
CSSLOT_LAUNCH_CONSTRAINT_SELF = 8,
CSSLOT_LAUNCH_CONSTRAINT_PARENT = 9,
CSSLOT_LAUNCH_CONSTRAINT_RESPONSIBLE = 10,
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;
typedef struct s_CS_DecodedBlob {
struct s_CS_DecodedBlob *next;
uint32_t type;
MemoryStream *stream;
} CS_DecodedBlob;
typedef struct s_CS_DecodedSuperBlob {
uint32_t magic;
struct s_CS_DecodedBlob *firstBlob;
} CS_DecodedSuperBlob;
// Convert blob magic to readable blob type string
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
int macho_extract_cs_to_file(MachO *macho, CS_SuperBlob *superblob);
int macho_find_code_signature_bounds(MachO *macho, uint32_t *offsetOut, uint32_t *sizeOut);
CS_SuperBlob *macho_read_code_signature(MachO *macho);
int macho_replace_code_signature(MachO *macho, CS_SuperBlob *superblob);
int update_load_commands(MachO *macho, CS_SuperBlob *superblob, uint64_t originalSize);
CS_DecodedBlob *csd_blob_init(uint32_t type, CS_GenericBlob *blobData);
int csd_blob_read(CS_DecodedBlob *blob, uint64_t offset, size_t size, void *outBuf);
int csd_blob_write(CS_DecodedBlob *blob, uint64_t offset, size_t size, const void *inBuf);
int csd_blob_insert(CS_DecodedBlob *blob, uint64_t offset, size_t size, const void *inBuf);
int csd_blob_delete(CS_DecodedBlob *blob, uint64_t offset, size_t size);
int csd_blob_read_string(CS_DecodedBlob *blob, uint64_t offset, char **outString);
int csd_blob_write_string(CS_DecodedBlob *blob, uint64_t offset, const char *string);
int csd_blob_get_size(CS_DecodedBlob *blob);
uint32_t csd_blob_get_type(CS_DecodedBlob *blob);
void csd_blob_set_type(CS_DecodedBlob *blob, uint32_t type);
void csd_blob_free(CS_DecodedBlob *blob);
CS_DecodedSuperBlob *csd_superblob_decode(CS_SuperBlob *superblob);
CS_SuperBlob *csd_superblob_encode(CS_DecodedSuperBlob *decodedSuperblob);
CS_DecodedBlob *csd_superblob_find_blob(CS_DecodedSuperBlob *superblob, uint32_t type, uint32_t *indexOut);
int csd_superblob_insert_blob_after_blob(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToInsert, CS_DecodedBlob *afterBlob);
int csd_superblob_insert_blob_at_index(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToInsert, uint32_t atIndex);
int csd_superblob_append_blob(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToAppend);
int csd_superblob_remove_blob(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToRemove); // <- Important: When calling this, caller is responsible for freeing blobToRemove
int csd_superblob_remove_blob_at_index(CS_DecodedSuperBlob *superblob, uint32_t atIndex);
int csd_superblob_print_content(CS_DecodedSuperBlob *decodedSuperblob, MachO *macho, bool printAllSlots, bool verifySlots);
void csd_superblob_free(CS_DecodedSuperBlob *decodedSuperblob);
#endif // CS_BLOB_H

View File

@ -0,0 +1,53 @@
#ifndef CODE_DIRECTORY_H
#define CODE_DIRECTORY_H
#include <stdint.h>
#include <math.h>
#include <CommonCrypto/CommonDigest.h>
#include "MachO.h"
#include "CSBlob.h"
#include "FAT.h"
#include "MachOByteOrder.h"
#include "MachOLoadCommand.h"
#include "MemoryStream.h"
// Code directory blob header
typedef struct __CodeDirectory {
uint32_t magic;
uint32_t length;
uint32_t version;
uint32_t flags;
uint32_t hashOffset;
uint32_t identOffset;
uint32_t nSpecialSlots;
uint32_t nCodeSlots;
uint32_t codeLimit;
uint8_t hashSize;
uint8_t hashType;
uint8_t spare1;
uint8_t pageSize;
uint32_t spare2;
uint32_t scatterOffset;
uint32_t teamOffset;
} CS_CodeDirectory;
enum CS_HashType {
CS_HASHTYPE_SHA160_160 = 1,
CS_HASHTYPE_SHA256_256 = 2,
CS_HASHTYPE_SHA256_160 = 3,
CS_HASHTYPE_SHA384_384 = 4,
};
char *csd_code_directory_copy_identity(CS_DecodedBlob *codeDirBlob, uint32_t *offsetOut);
char *csd_code_directory_copy_team_id(CS_DecodedBlob *codeDirBlob, uint32_t *offsetOut);
int csd_code_directory_set_team_id(CS_DecodedBlob *codeDirBlob, char *newTeamID);
uint32_t csd_code_directory_get_flags(CS_DecodedBlob *codeDirBlob);
void csd_code_directory_set_flags(CS_DecodedBlob *codeDirBlob, uint32_t flags);
uint8_t csd_code_directory_get_hash_type(CS_DecodedBlob *codeDirBlob);
void csd_code_directory_set_hash_type(CS_DecodedBlob *codeDirBlob, uint8_t hashType);
int csd_code_directory_print_content(CS_DecodedBlob *codeDirBlob, MachO *macho, bool printSlots, bool verifySlots);
void csd_code_directory_update(CS_DecodedBlob *codeDirBlob, MachO *macho);
#endif // CODE_DIRECTORY_H

46
RootHelper/external/include/choma/FAT.h vendored Normal file
View File

@ -0,0 +1,46 @@
#ifndef MACHO_H
#define MACHO_H
#include <stdio.h>
#include <libkern/OSByteOrder.h>
#include <mach/mach.h>
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <sys/stat.h>
#include "MemoryStream.h"
typedef struct MachO MachO;
// A FAT structure can either represent a FAT file with multiple slices, in which the slices will be loaded into the slices attribute
// Or a single slice MachO, in which case it serves as a compatibility layer and the single slice will also be loaded into the slices attribute
typedef struct FAT
{
MemoryStream *stream;
MachO **slices;
uint32_t slicesCount;
int fileDescriptor;
} FAT;
int fat_read_at_offset(FAT *fat, uint64_t offset, size_t size, void *outBuf);
MemoryStream *fat_get_stream(FAT *fat);
// Initialise a FAT structure from a memory stream
FAT *fat_init_from_memory_stream(MemoryStream *stream);
// Initialise a FAT structure using the path to the file
FAT *fat_init_from_path(const char *filePath);
// 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);
// 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
void fat_free(FAT *fat);
#endif // MACHO_H

View File

@ -0,0 +1,21 @@
#ifndef FILE_STREAM_H
#define FILE_STREAM_H
#include "MemoryStream.h"
#define FILE_STREAM_SIZE_AUTO 0
#define FILE_STREAM_FLAG_WRITABLE (1 << 0)
#define FILE_STREAM_FLAG_AUTO_EXPAND (1 << 1)
typedef struct FileStreamContext {
int fd;
size_t fileSize;
uint32_t bufferStart;
size_t bufferSize;
} FileStreamContext;
MemoryStream *file_stream_init_from_file_descriptor_nodup(int fd, uint32_t bufferStart, size_t bufferSize, uint32_t flags);
MemoryStream *file_stream_init_from_file_descriptor(int fd, uint32_t bufferStart, size_t bufferSize, uint32_t flags);
MemoryStream *file_stream_init_from_path(const char *path, uint32_t bufferStart, size_t bufferSize, uint32_t flags);
#endif // FILE_STREAM_H

View File

@ -0,0 +1,10 @@
#ifndef HOST_H
#define HOST_H
#include "FAT.h"
// Retrieve the preferred MachO slice from a FAT
// Preferred slice as in the slice that the kernel would use when loading the file
MachO *fat_find_preferred_slice(FAT *fat);
#endif // HOST_H

View File

@ -0,0 +1,68 @@
#ifndef MACHO_SLICE_H
#define MACHO_SLICE_H
#include <stdbool.h>
#include <mach-o/fat.h>
#include <mach-o/loader.h>
#include "MemoryStream.h"
#include "FAT.h"
typedef struct MachOSegment
{
struct segment_command_64 command;
struct section_64 sections[];
} __attribute__((__packed__)) MachOSegment;
typedef struct FilesetMachO {
char *entry_id;
uint64_t vmaddr;
uint64_t fileoff;
FAT *underlyingMachO;
} FilesetMachO;
typedef struct MachO {
MemoryStream *stream;
bool isSupported;
struct mach_header_64 machHeader;
struct fat_arch_64 archDescriptor;
uint32_t filesetCount;
FilesetMachO *filesetMachos;
uint32_t segmentCount;
MachOSegment **segments;
} MachO;
// Read data from a MachO at a specified offset
int macho_read_at_offset(MachO *macho, uint64_t offset, size_t size, void *outBuf);
// Write data from a MachO at a specified offset, auto expands, only works if opened via macho_init_for_writing
int macho_write_at_offset(MachO *macho, uint64_t offset, size_t size, void *inBuf);
MemoryStream *macho_get_stream(MachO *macho);
uint32_t macho_get_filetype(MachO *macho);
// Perform translation between file offsets and virtual addresses
int macho_translate_fileoff_to_vmaddr(MachO *macho, uint64_t fileoff, uint64_t *vmaddrOut, MachOSegment **segmentOut);
int macho_translate_vmaddr_to_fileoff(MachO *macho, uint64_t vmaddr, uint64_t *fileoffOut, MachOSegment **segmentOut);
// Read data from a MachO at a specified virtual address
int macho_read_at_vmaddr(MachO *macho, uint64_t vmaddr, size_t size, void *outBuf);
int macho_enumerate_load_commands(MachO *macho, void (^enumeratorBlock)(struct load_command loadCommand, uint64_t offset, void *cmd, bool *stop));
// Initialise a MachO object from a MemoryStream and it's corresponding FAT arch descriptor
MachO *macho_init(MemoryStream *stream, struct fat_arch_64 archDescriptor);
// Initialize a single slice macho for writing to it
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);
#endif // MACHO_SLICE_H

View File

@ -0,0 +1,171 @@
#ifndef MACHO_BYTE_ORDER_H
#define MACHO_BYTE_ORDER_H
#include <stdio.h>
#include <stdlib.h>
// 8-bit integers needed for CodeDirectory
#define BIG_TO_HOST(n) _Generic((n), \
int8_t: n, \
uint8_t: n, \
int16_t: OSSwapBigToHostInt16(n), \
uint16_t: OSSwapBigToHostInt16(n), \
int32_t: OSSwapBigToHostInt32(n), \
uint32_t: OSSwapBigToHostInt32(n), \
int64_t: OSSwapBigToHostInt64(n), \
uint64_t: OSSwapBigToHostInt64(n) \
)
#define HOST_TO_BIG(n) _Generic((n), \
int8_t: n, \
uint8_t: n, \
uint16_t: OSSwapHostToBigInt16(n), \
int16_t: OSSwapHostToBigInt16(n), \
int32_t: OSSwapHostToBigInt32(n), \
uint32_t: OSSwapHostToBigInt32(n), \
int64_t: OSSwapHostToBigInt64(n), \
uint64_t: OSSwapHostToBigInt64(n) \
)
#define LITTLE_TO_HOST(n) _Generic((n), \
int8_t: n, \
uint8_t: n, \
int16_t: OSSwapLittleToHostInt16(n), \
uint16_t: OSSwapLittleToHostInt16(n), \
int32_t: OSSwapLittleToHostInt32(n), \
uint32_t: OSSwapLittleToHostInt32(n), \
int64_t: OSSwapLittleToHostInt64(n), \
uint64_t: OSSwapLittleToHostInt64(n) \
)
#define HOST_TO_LITTLE(n) _Generic((n), \
int8_t: n, \
uint8_t: n, \
int16_t: OSSwapHostToLittleInt16(n), \
uint16_t: OSSwapHostToLittleInt16(n), \
int32_t: OSSwapHostToLittleInt32(n), \
uint32_t: OSSwapHostToLittleInt32(n), \
int64_t: OSSwapHostToLittleInt64(n), \
uint64_t: OSSwapHostToLittleInt64(n) \
)
#define HOST_TO_LITTLE_APPLIER(instance, member) \
(instance)->member = HOST_TO_LITTLE((instance)->member)
#define HOST_TO_BIG_APPLIER(instance, member) \
(instance)->member = HOST_TO_BIG((instance)->member)
#define LITTLE_TO_HOST_APPLIER(instance, member) \
(instance)->member = LITTLE_TO_HOST((instance)->member)
#define BIG_TO_HOST_APPLIER(instance, member) \
(instance)->member = BIG_TO_HOST((instance)->member)
#define FAT_HEADER_APPLY_BYTE_ORDER(fh, applier) \
applier(fh, magic); \
applier(fh, nfat_arch);
#define FAT_ARCH_APPLY_BYTE_ORDER(arch, applier) \
applier(arch, cputype); \
applier(arch, cpusubtype); \
applier(arch, offset); \
applier(arch, size); \
applier(arch, align); \
#define FAT_ARCH_64_APPLY_BYTE_ORDER(arch, applier) \
applier(arch, cputype); \
applier(arch, cpusubtype); \
applier(arch, offset); \
applier(arch, size); \
applier(arch, align); \
applier(arch, reserved); \
#define MACH_HEADER_APPLY_BYTE_ORDER(mh, applier) \
applier(mh, magic); \
applier(mh, cputype); \
applier(mh, cpusubtype); \
applier(mh, filetype); \
applier(mh, ncmds); \
applier(mh, sizeofcmds); \
applier(mh, reserved);
#define LOAD_COMMAND_APPLY_BYTE_ORDER(lc, applier) \
applier(lc, cmd); \
applier(lc, cmdsize);
#define LINKEDIT_DATA_COMMAND_APPLY_BYTE_ORDER(lc, applier) \
applier(lc, cmd); \
applier(lc, cmdsize); \
applier(lc, dataoff); \
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) \
applier(bi, type); \
applier(bi, offset);
#define SUPERBLOB_APPLY_BYTE_ORDER(sb, applier) \
applier(sb, magic); \
applier(sb, length); \
applier(sb, count);
#define GENERIC_BLOB_APPLY_BYTE_ORDER(gb, applier) \
applier(gb, magic); \
applier(gb, length);
#define CODE_DIRECTORY_APPLY_BYTE_ORDER(cd, applier) \
applier(cd, magic); \
applier(cd, length); \
applier(cd, version); \
applier(cd, flags); \
applier(cd, hashOffset); \
applier(cd, identOffset); \
applier(cd, nSpecialSlots); \
applier(cd, nCodeSlots); \
applier(cd, codeLimit); \
applier(cd, hashSize); \
applier(cd, hashType); \
applier(cd, spare1); \
applier(cd, pageSize); \
applier(cd, spare2); \
applier(cd, scatterOffset); \
applier(cd, teamOffset);
#define SEGMENT_COMMAND_64_APPLY_BYTE_ORDER(sc64, applier) \
applier(sc64, cmd); \
applier(sc64, cmdsize); \
applier(sc64, fileoff); \
applier(sc64, filesize); \
applier(sc64, vmaddr); \
applier(sc64, vmsize); \
applier(sc64, flags); \
applier(sc64, initprot); \
applier(sc64, maxprot); \
applier(sc64, nsects);
#define SECTION_64_APPLY_BYTE_ORDER(sc64, applier) \
applier(sc64, addr); \
applier(sc64, align); \
applier(sc64, flags); \
applier(sc64, nreloc); \
applier(sc64, offset); \
applier(sc64, reserved1); \
applier(sc64, reserved2); \
applier(sc64, reserved3); \
applier(sc64, size);
#define FILESET_ENTRY_COMMAND_APPLY_BYTE_ORDER(fse, applier) \
applier(fse, cmd); \
applier(fse, cmdsize); \
applier(fse, vmaddr); \
applier(fse, fileoff); \
applier(fse, entry_id.offset); \
applier(fse, reserved); \
#endif // MACHO_BYTE_ORDER_H

View File

@ -0,0 +1,16 @@
#ifndef MACHO_LOAD_COMMAND_H
#define MACHO_LOAD_COMMAND_H
#include <mach-o/loader.h>
#include "MachO.h"
#include "CSBlob.h"
#include "FileStream.h"
#include "MachOByteOrder.h"
// Convert load command to load command name
char *load_command_to_string(int loadCommand);
void update_segment_command_64(MachO *macho, const char *segmentName, uint64_t vmaddr, uint64_t vmsize, uint64_t fileoff, uint64_t filesize);
void update_lc_code_signature(MachO *macho, uint64_t size);
int update_load_commands_for_coretrust_bypass(MachO *macho, CS_SuperBlob *superblob, uint64_t originalCodeSignatureSize, uint64_t originalMachOSize);
#endif // MACHO_LOAD_COMMAND_H

View File

@ -0,0 +1,60 @@
#ifndef MEMORY_STREAM_H
#define MEMORY_STREAM_H
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#define MEMORY_STREAM_FLAG_OWNS_DATA (1 << 0)
#define MEMORY_STREAM_FLAG_MUTABLE (1 << 1)
#define MEMORY_STREAM_FLAG_AUTO_EXPAND (1 << 2)
#define MEMORY_STREAM_SIZE_INVALID (size_t)-1
// A generic memory IO interface that is used throughout this project
// Can be backed by anything, just the functions have to be implemented
typedef struct s_MemoryStream {
void *context;
uint32_t flags;
int (*read)(struct s_MemoryStream *stream, uint64_t offset, size_t size, void *outBuf);
int (*write)(struct s_MemoryStream *stream, uint64_t offset, size_t size, const void *inBuf);
int (*getSize)(struct s_MemoryStream *stream, size_t *sizeOut);
uint8_t *(*getRawPtr)(struct s_MemoryStream *stream);
int (*trim)(struct s_MemoryStream *stream, size_t trimAtStart, size_t trimAtEnd);
int (*expand)(struct s_MemoryStream *stream, size_t expandAtStart, size_t expandAtEnd);
struct s_MemoryStream *(*hardclone)(struct s_MemoryStream *stream);
struct s_MemoryStream *(*softclone)(struct s_MemoryStream *stream);
void (*free)(struct s_MemoryStream *stream);
} MemoryStream;
int memory_stream_read(MemoryStream *stream, uint64_t offset, size_t size, void *outBuf);
int memory_stream_write(MemoryStream *stream, uint64_t offset, size_t size, const void *inBuf);
int memory_stream_insert(MemoryStream *stream, uint64_t offset, size_t size, const void *inBuf);
int memory_stream_delete(MemoryStream *stream, uint64_t offset, size_t size);
int memory_stream_read_string(MemoryStream *stream, uint64_t offset, char **outString);
int memory_stream_write_string(MemoryStream *stream, uint64_t offset, const char *string);
size_t memory_stream_get_size(MemoryStream *stream);
uint8_t *memory_stream_get_raw_pointer(MemoryStream *stream);
uint32_t memory_stream_get_flags(MemoryStream *stream);
MemoryStream *memory_stream_softclone(MemoryStream *stream);
MemoryStream *memory_stream_hardclone(MemoryStream *stream);
int memory_stream_trim(MemoryStream *stream, size_t trimAtStart, size_t trimAtEnd);
int memory_stream_expand(MemoryStream *stream, size_t expandAtStart, size_t expandAtEnd);
void memory_stream_free(MemoryStream *stream);
int memory_stream_copy_data(MemoryStream *originStream, uint64_t originOffset, MemoryStream *targetStream, uint64_t targetOffset, size_t size);
int memory_stream_find_memory(MemoryStream *stream, uint64_t searchOffset, size_t searchSize, void *bytes, void *mask, size_t nbytes, uint16_t alignment, uint64_t *foundOffsetOut);
#endif // MEMORY_STREAM_H

View File

@ -0,0 +1,47 @@
#include <stdint.h>
#include "MachO.h"
#define METRIC_TYPE_PATTERN 1
#define METRIC_TYPE_STRING_XREF 2
#define METRIC_TYPE_FUNCTION_XREF 3
typedef struct PFSection {
MachO *macho;
uint64_t fileoff;
uint64_t vmaddr;
uint64_t size;
uint8_t *cache;
bool ownsCache;
} PFSection;
PFSection *pf_section_init_from_macho(MachO *macho, const char *filesetEntryId, const char *segName, const char *sectName);
int pf_section_read_at_relative_offset(PFSection *section, uint64_t rel, size_t size, void *outBuf);
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 {
uint32_t type;
} MetricShared;
typedef enum {
BYTE_PATTERN_ALIGN_8_BIT,
BYTE_PATTERN_ALIGN_16_BIT,
BYTE_PATTERN_ALIGN_32_BIT,
BYTE_PATTERN_ALIGN_64_BIT,
} BytePatternAlignment;
typedef struct BytePatternMetric {
MetricShared shared;
void *bytes;
void *mask;
size_t nbytes;
BytePatternAlignment alignment;
} BytePatternMetric;
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));

View File

@ -0,0 +1,16 @@
#ifndef SIGN_OSSL_H
#define SIGN_OSSL_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/err.h>
unsigned char *signWithRSA(unsigned char *inputData, size_t inputDataLength, unsigned char *key, size_t key_len, size_t *outputDataLength);
#endif // SIGN_OSSL_H
// 0xA422

View File

@ -0,0 +1,7 @@
#include <stdint.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);
int count_digits(int64_t num);
void print_hash(uint8_t *hash, size_t size);

BIN
RootHelper/external/lib/libchoma.a vendored Normal file

Binary file not shown.

BIN
RootHelper/external/lib/libcrypto.a vendored Normal file

Binary file not shown.

View File

@ -14,10 +14,10 @@
#ifndef EMBEDDED_ROOT_HELPER #ifndef EMBEDDED_ROOT_HELPER
#import "codesign.h" #import "codesign.h"
#import "coretrust_bug.h" #import "coretrust_bug.h"
#import "FAT.h" #import <choma/FAT.h>
#import "MachO.h" #import <choma/MachO.h>
#import "FileStream.h" #import <choma/FileStream.h>
#import "Host.h" #import <choma/Host.h>
#endif #endif
#import <SpringBoardServices/SpringBoardServices.h> #import <SpringBoardServices/SpringBoardServices.h>
@ -570,11 +570,6 @@ int signApp(NSString* appPath)
// while we're fixing entitlements // while we're fixing entitlements
BOOL requiresDevMode = NO; BOOL requiresDevMode = NO;
// The majority of IPA decryption utilities only decrypt the main executable of the app bundle
// As a result, we cannot bail on the entire app if an additional binary is encrypted (e.g. app extensions)
// Instead, we will display a warning to the user, and warn them that the app may not work properly
BOOL hasAdditionalEncryptedBinaries = NO;
NSURL* fileURL; NSURL* fileURL;
NSDirectoryEnumerator *enumerator; NSDirectoryEnumerator *enumerator;
@ -698,17 +693,9 @@ int signApp(NSString* appPath)
} }
else if (r == 2) { else if (r == 2) {
NSLog(@"[%@] Cannot apply CoreTrust bypass on an encrypted binary!", filePath); NSLog(@"[%@] Cannot apply CoreTrust bypass on an encrypted binary!", filePath);
if (isSameFile(filePath, mainExecutablePath)) {
// If this is the main binary, this error is fatal
NSLog(@"[%@] Main binary is encrypted, cannot continue!", filePath);
fat_free(fat); fat_free(fat);
return 180; return 180;
} }
else {
// If not, we can continue but want to show a warning after the app is installed
hasAdditionalEncryptedBinaries = YES;
}
}
else { else {
NSLog(@"[%@] CoreTrust bypass failed!!! :(", filePath); NSLog(@"[%@] CoreTrust bypass failed!!! :(", filePath);
fat_free(fat); fat_free(fat);
@ -729,10 +716,6 @@ int signApp(NSString* appPath)
return 182; return 182;
} }
if (hasAdditionalEncryptedBinaries) {
return 184;
}
return 0; return 0;
} }
#endif #endif
@ -792,9 +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 app where the main binary is encrypted // 180: tried to sign encrypted binary
// 184: tried to sign app where an additional binary is encrypted
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);
@ -820,18 +801,14 @@ int installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate,
} }
BOOL requiresDevMode = NO; BOOL requiresDevMode = NO;
BOOL hasAdditionalEncryptedBinaries = NO;
if(sign) if(sign)
{ {
int signRet = signApp(appBundleToInstallPath); int signRet = signApp(appBundleToInstallPath);
// 182: app requires developer mode; non-fatal // 182: app requires developer mode; non-fatal
// 184: app has additional encrypted binaries; non-fatal
if(signRet != 0) { if(signRet != 0) {
if (signRet == 182) { if (signRet == 182) {
requiresDevMode = YES; requiresDevMode = YES;
} else if (signRet == 184) {
hasAdditionalEncryptedBinaries = YES;
} else { } else {
return signRet; return signRet;
} }
@ -998,13 +975,6 @@ int installApp(NSString* appPackagePath, BOOL sign, BOOL force, BOOL isTSUpdate,
return 183; return 183;
} }
} }
if (hasAdditionalEncryptedBinaries) {
NSLog(@"[installApp] app has additional encrypted binaries");
// non-fatal
return 184;
}
return 0; return 0;
} }
@ -1109,8 +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's main binary is encrypted // 180: IPA contains an encrypted binary
// 184: IPA contains additional encrypted binaries
int installIpa(NSString* ipaPath, BOOL force, BOOL useInstalldMethod) int installIpa(NSString* ipaPath, BOOL force, BOOL useInstalldMethod)
{ {
cleanRestrictions(); cleanRestrictions();

View File

@ -52,7 +52,7 @@
<string>iPhoneOS</string> <string>iPhoneOS</string>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>2.0.10</string> <string>2.0.9</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>

View File

@ -38,9 +38,9 @@
_specifiers = [NSMutableArray new]; _specifiers = [NSMutableArray new];
#ifdef LEGACY_CT_BUG #ifdef LEGACY_CT_BUG
NSString* credits = @"Powered by Fugu15 CoreTrust & installd bugs, thanks to @LinusHenze\n\n© 2022-2024 Lars Fröder (opa334)"; NSString* credits = @"Powered by Fugu15 CoreTrust & installd bugs, thanks to @LinusHenze\n\n© 2022-2023 Lars Fröder (opa334)";
#else #else
NSString* credits = @"Powered by CVE-2023-41991, originally discovered by Google TAG, rediscovered via patchdiffing by @alfiecg_dev\n\n© 2022-2024 Lars Fröder (opa334)"; NSString* credits = @"Powered by CVE-2023-41991, originally discovered by Google TAG, rediscovered via patchdiffing by @alfiecg_dev\n\n© 2022-2023 Lars Fröder (opa334)";
#endif #endif
PSSpecifier* infoGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; PSSpecifier* infoGroupSpecifier = [PSSpecifier emptyGroupSpecifier];

View File

@ -1,6 +1,6 @@
Package: com.opa334.trollstorehelper Package: com.opa334.trollstorehelper
Name: TrollStore Helper Name: TrollStore Helper
Version: 2.0.10 Version: 2.0.9
Architecture: iphoneos-arm Architecture: iphoneos-arm
Description: Helper utility to install and manage TrollStore! Description: Helper utility to install and manage TrollStore!
Maintainer: opa334 Maintainer: opa334

View File

@ -50,7 +50,7 @@
<string>iPhoneOS</string> <string>iPhoneOS</string>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>2.0.10</string> <string>2.0.9</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>

View File

@ -75,7 +75,7 @@ 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 you tried to install has an encrypted main binary, which cannot have the CoreTrust bypass applied to it. Please ensure you install decrypted apps."; 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."; errorDescription = @"Failed to add app to icon cache.";
@ -86,8 +86,6 @@ extern NSUserDefaults* trollStoreUserDefaults();
case 183: case 183:
errorDescription = @"Failed to enable developer mode."; errorDescription = @"Failed to enable developer mode.";
break; break;
case 184:
errorDescription = @"The app was installed successfully, but has additional binaries that are encrypted (e.g. extensions, plugins). The app itself should work, but you may experience broken functionality as a result.";
} }
NSError* error = [NSError errorWithDomain:TrollStoreErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : errorDescription}]; NSError* error = [NSError errorWithDomain:TrollStoreErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : errorDescription}];

View File

@ -68,16 +68,6 @@ extern NSUserDefaults* trollStoreUserDefaults(void);
[rebootNotification addAction:rebootAction]; [rebootNotification addAction:rebootAction];
[TSPresentationDelegate presentViewController:rebootNotification animated:YES completion:nil]; [TSPresentationDelegate presentViewController:rebootNotification animated:YES completion:nil];
} else if (ret == 184) {
// warning
UIAlertController* warningAlert = [UIAlertController alertControllerWithTitle:@"Warning" message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* closeAction = [UIAlertAction actionWithTitle:@"Close" style:UIAlertActionStyleCancel handler:^(UIAlertAction* action)
{
if(completionBlock) completionBlock(YES, nil);
}];
[warningAlert addAction:closeAction];
[TSPresentationDelegate presentViewController:warningAlert animated:YES completion:nil];
} else { } else {
// unrecoverable error // unrecoverable error
UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Install Error %d", ret] message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert]; UIAlertController* errorAlert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Install Error %d", ret] message:[error localizedDescription] preferredStyle:UIAlertControllerStyleAlert];

View File

@ -323,7 +323,7 @@ extern NSUserDefaults* trollStoreUserDefaults(void);
[_specifiers addObject:installAlertConfigurationSpecifier]; [_specifiers addObject:installAlertConfigurationSpecifier];
PSSpecifier* otherGroupSpecifier = [PSSpecifier emptyGroupSpecifier]; PSSpecifier* otherGroupSpecifier = [PSSpecifier emptyGroupSpecifier];
[otherGroupSpecifier setProperty:[NSString stringWithFormat:@"TrollStore %@\n\n© 2022-2024 Lars Fröder (opa334)\n\nTrollStore is NOT for piracy!\n\nCredits:\nGoogle TAG, @alfiecg_dev: CoreTrust bug\n@lunotech11, @SerenaKit, @tylinux, @TheRealClarity: Various contributions\n@ProcursusTeam: uicache, ldid\n@cstar_ow: uicache\n@saurik: ldid", [self getTrollStoreVersion]] forKey:@"footerText"]; [otherGroupSpecifier setProperty:[NSString stringWithFormat:@"TrollStore %@\n\n© 2022-2023 Lars Fröder (opa334)\n\nTrollStore is NOT for piracy!\n\nCredits:\nGoogle TAG, @alfiecg_dev: CoreTrust bug\n@lunotech11, @SerenaKit, @tylinux, @TheRealClarity: Various contributions\n@ProcursusTeam: uicache, ldid\n@cstar_ow: uicache\n@saurik: ldid", [self getTrollStoreVersion]] forKey:@"footerText"];
[_specifiers addObject:otherGroupSpecifier]; [_specifiers addObject:otherGroupSpecifier];
PSSpecifier* advancedLinkSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Advanced" PSSpecifier* advancedLinkSpecifier = [PSSpecifier preferenceSpecifierNamed:@"Advanced"

View File

@ -1,6 +1,6 @@
Package: com.opa334.trollstore Package: com.opa334.trollstore
Name: TrollStore Name: TrollStore
Version: 2.0.10 Version: 2.0.9
Architecture: iphoneos-arm Architecture: iphoneos-arm
Description: An awesome application! Description: An awesome application!
Maintainer: opa334 Maintainer: opa334