X-Git-Url: https://git.saurik.com/ldid.git/blobdiff_plain/a362a82f75061b7c77c2af8f0dd0efc4e63dd589..e067acdcc9870305978666f6c1f277688893dc01:/ldid.cpp diff --git a/ldid.cpp b/ldid.cpp index b1c114a..19bfcc7 100644 --- a/ldid.cpp +++ b/ldid.cpp @@ -36,16 +36,23 @@ */ #include "minimal/stdlib.h" +#include "minimal/string.h" #include "minimal/mapping.h" +#include "sha1.h" + #include #include #include +#include +#include +#include + struct fat_header { uint32_t magic; uint32_t nfat_arch; -}; +} _packed; #define FAT_MAGIC 0xcafebabe #define FAT_CIGAM 0xbebafeca @@ -56,7 +63,7 @@ struct fat_arch { uint32_t offset; uint32_t size; uint32_t align; -}; +} _packed; struct mach_header { uint32_t magic; @@ -66,25 +73,27 @@ struct mach_header { uint32_t ncmds; uint32_t sizeofcmds; uint32_t flags; -}; +} _packed; -#define MH_MAGIC 0xfeedface +#define MH_MAGIC 0xfeedface #define MH_CIGAM 0xcefaedfe -#define MH_EXECUTE 0x2 -#define MH_DYLIB 0x6 -#define MH_BUNDLE 0x8 +#define MH_EXECUTE 0x2 +#define MH_DYLIB 0x6 +#define MH_BUNDLE 0x8 +#define MH_DYLIB_STUB 0x9 struct load_command { uint32_t cmd; uint32_t cmdsize; -}; +} _packed; #define LC_REQ_DYLD 0x80000000 -#define LC_LOAD_DYLIB 0x0c -#define LC_ID_DYLIB 0x0d -#define LC_UUID 0x1b +#define LC_LOAD_DYLIB 0x0c +#define LC_ID_DYLIB 0x0d +#define LC_UUID 0x1b +#define LC_CODE_SIGNATURE 0x1d #define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD) struct dylib { @@ -92,19 +101,64 @@ struct dylib { uint32_t timestamp; uint32_t current_version; uint32_t compatibility_version; -}; +} _packed; struct dylib_command { uint32_t cmd; uint32_t cmdsize; struct dylib dylib; -}; +} _packed; struct uuid_command { uint32_t cmd; uint32_t cmdsize; uint8_t uuid[16]; -}; +} _packed; + +struct linkedit_data_command { + uint32_t cmd; + uint32_t cmdsize; + uint32_t dataoff; + uint32_t datasize; +} _packed; + +uint16_t Swap_(uint16_t value) { + return + ((value >> 8) & 0x00ff) | + ((value << 8) & 0xff00); +} + +uint32_t Swap_(uint32_t value) { + value = ((value >> 8) & 0x00ff00ff) | + ((value << 8) & 0xff00ff00); + value = ((value >> 16) & 0x0000ffff) | + ((value << 16) & 0xffff0000); + return value; +} + +int16_t Swap_(int16_t value) { + return Swap_(static_cast(value)); +} + +int32_t Swap_(int32_t value) { + return Swap_(static_cast(value)); +} + +uint16_t Swap(uint16_t value) { + return true ? Swap_(value) : value; +} + +uint32_t Swap(uint32_t value) { + return true ? Swap_(value) : value; +} + +int16_t Swap(int16_t value) { + return Swap(static_cast(value)); +} + +int32_t Swap(int32_t value) { + return Swap(static_cast(value)); +} class Framework { private: @@ -114,30 +168,20 @@ class Framework { bool swapped_; public: - int16_t Swap(int16_t value) const { - return Swap(static_cast(value)); + uint16_t Swap(uint16_t value) const { + return swapped_ ? Swap_(value) : value; } - int32_t Swap(int32_t value) const { - return Swap(static_cast(value)); + uint32_t Swap(uint32_t value) const { + return swapped_ ? Swap_(value) : value; } - uint16_t Swap(uint16_t value) const { - return !swapped_ ? value : - ((value >> 8) & 0x00ff) | - ((value << 8) & 0xff00); + int16_t Swap(int16_t value) const { + return Swap(static_cast(value)); } - uint32_t Swap(uint32_t value) const { - if (!swapped_) - return value; - else { - value = ((value >> 8) & 0x00ff00ff) | - ((value << 8) & 0xff00ff00); - value = ((value >> 16) & 0x0000ffff) | - ((value << 16) & 0xffff0000); - return value; - } + int32_t Swap(int32_t value) const { + return Swap(static_cast(value)); } Framework(const char *framework_path) : @@ -177,6 +221,10 @@ class Framework { ); } + struct mach_header *operator ->() const { + return mach_header_; + } + void *GetBase() { return base_; } @@ -198,22 +246,85 @@ class Framework { } }; +#define CSMAGIC_CODEDIRECTORY 0xfade0c02 +#define CSMAGIC_EMBEDDED_SIGNATURE 0xfade0cc0 +#define CSMAGIC_ENTITLEMENTS 0xfade7171 + +#define CSSLOT_CODEDIRECTORY 0 +#define CSSLOT_REQUIREMENTS 2 +#define CSSLOT_ENTITLEMENTS 5 + +struct BlobIndex { + uint32_t type; + uint32_t offset; +} _packed; + +struct Blob { + uint32_t magic; + uint32_t length; +} _packed; + +struct SuperBlob { + struct Blob blob; + uint32_t count; + struct BlobIndex index[]; +} _packed; + +struct CodeDirectory { + struct Blob blob; + 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; +} _packed; + extern "C" uint32_t hash(uint8_t *k, uint32_t length, uint32_t initval); +#define CODESIGN_ALLOCATE "arm-apple-darwin9-codesign_allocate" + +void sha1(uint8_t *hash, uint8_t *data, size_t size) { + SHA1Context context; + SHA1Reset(&context); + SHA1Input(&context, data, size); + SHA1Result(&context, hash); +} + int main(int argc, const char *argv[]) { bool flag_R(false); bool flag_t(false); bool flag_p(false); bool flag_u(false); + bool flag_e(false); bool flag_T(false); + bool flag_S(false); + bool flag_s(false); + bool timeh(false); uint32_t timev(0); + const void *xmld(NULL); + size_t xmls(0); + std::vector files; - _assert(argc != 0); + if (argc == 1) { + fprintf(stderr, "usage: %s -S[entitlements.xml] \n", argv[0]); + fprintf(stderr, " %s -e MobileSafari\n", argv[0]); + fprintf(stderr, " %s -S cat\n", argv[0]); + fprintf(stderr, " %s -Stfp.xml gdb\n", argv[0]); + exit(0); + } + for (int argi(1); argi != argc; ++argi) if (argv[argi][0] != '-') files.push_back(argv[argi]); @@ -222,6 +333,21 @@ int main(int argc, const char *argv[]) { case 't': flag_t = true; break; case 'u': flag_u = true; break; case 'p': flag_p = true; break; + case 'e': flag_e = true; break; + + case 's': + _assert(!flag_S); + flag_s = true; + break; + + case 'S': + _assert(!flag_s); + flag_S = true; + if (argv[argi][2] != '\0') { + const char *xml = argv[argi] + 2; + xmld = map(xml, 0, _not(size_t), &xmls, true); + } + break; case 'T': { flag_T = true; @@ -245,7 +371,70 @@ int main(int argc, const char *argv[]) { size_t filei(0), filee(0); _foreach (file, files) try { - Framework framework(file->c_str()); + const char *path(file->c_str()); + const char *base = strrchr(path, '/'); + char *temp(NULL), *dir; + + if (base != NULL) + dir = strndup_(path, base++ - path + 1); + else { + dir = strdup(""); + base = path; + } + + if (flag_S) { + asprintf(&temp, "%s.%s.cs", dir, base); + const char *allocate = getenv("CODESIGN_ALLOCATE"); + if (allocate == NULL) + allocate = "codesign_allocate"; + + size_t size = _not(size_t); + const char *arch; { + Framework framework(path); + _foreach (load_command, framework.GetLoadCommands()) { + uint32_t cmd(framework.Swap((*load_command)->cmd)); + if (cmd == LC_CODE_SIGNATURE) { + struct linkedit_data_command *signature = reinterpret_cast(*load_command); + size = framework.Swap(signature->dataoff); + _assert(size < framework.GetSize()); + break; + } + } + + if (size == _not(size_t)) + size = framework.GetSize(); + + switch (framework->cputype) { + case 12: switch (framework->cpusubtype) { + case 0: arch = "arm"; break; + case 6: arch = "armv6"; break; + default: arch = NULL; break; + } break; + + default: arch = NULL; break; + } + } + + _assert(arch != NULL); + + pid_t pid = fork(); + _syscall(pid); + if (pid == 0) { + char *ssize; + asprintf(&ssize, "%u", (sizeof(struct SuperBlob) + 2 * sizeof(struct BlobIndex) + sizeof(struct CodeDirectory) + strlen(base) + 1 + ((xmld == NULL ? CSSLOT_REQUIREMENTS : CSSLOT_ENTITLEMENTS) + (size + 0x1000 - 1) / 0x1000) * 0x14 + 0xc + (xmld == NULL ? 0 : 0x10 + xmls) + 15) / 16 * 16); + //printf("%s -i %s -a %s %s -o %s\n", allocate, path, arch, ssize, temp); + execlp(allocate, allocate, "-i", path, "-a", arch, ssize, "-o", temp, NULL); + _assert(false); + } + + int status; + _syscall(waitpid(pid, &status, 0)); + _assert(WIFEXITED(status)); + _assert(WEXITSTATUS(status) == 0); + } + + Framework framework(temp == NULL ? path : temp); + struct linkedit_data_command *signature(NULL); if (flag_p) printf("path%zu='%s'\n", filei, file->c_str()); @@ -255,6 +444,8 @@ int main(int argc, const char *argv[]) { if (flag_R && cmd == LC_REEXPORT_DYLIB) (*load_command)->cmd = framework.Swap(LC_LOAD_DYLIB); + else if (cmd == LC_CODE_SIGNATURE) + signature = reinterpret_cast(*load_command); else if (cmd == LC_UUID) { volatile struct uuid_command *uuid_command(reinterpret_cast(*load_command)); @@ -287,6 +478,159 @@ int main(int argc, const char *argv[]) { } } + if (flag_e) { + _assert(signature != NULL); + + uint32_t data = framework.Swap(signature->dataoff); + uint32_t size = framework.Swap(signature->datasize); + + uint8_t *top = reinterpret_cast(framework.GetBase()); + uint8_t *blob = top + data; + struct SuperBlob *super = reinterpret_cast(blob); + + for (size_t index(0); index != Swap(super->count); ++index) + if (Swap(super->index[index].type) == CSSLOT_ENTITLEMENTS) { + uint32_t begin = Swap(super->index[index].offset); + struct Blob *entitlements = reinterpret_cast(blob + begin); + fwrite(entitlements + 1, 1, Swap(entitlements->length) - sizeof(struct Blob), stdout); + } + } + + if (flag_s) { + _assert(signature != NULL); + + uint32_t data = framework.Swap(signature->dataoff); + uint32_t size = framework.Swap(signature->datasize); + + uint8_t *top = reinterpret_cast(framework.GetBase()); + uint8_t *blob = top + data; + struct SuperBlob *super = reinterpret_cast(blob); + + for (size_t index(0); index != Swap(super->count); ++index) + if (Swap(super->index[index].type) == CSSLOT_CODEDIRECTORY) { + uint32_t begin = Swap(super->index[index].offset); + struct CodeDirectory *directory = reinterpret_cast(blob + begin); + + uint8_t (*hashes)[20] = reinterpret_cast(blob + begin + Swap(directory->hashOffset)); + uint32_t pages = Swap(directory->nCodeSlots); + + if (pages != 1) + for (size_t i = 0; i != pages - 1; ++i) + sha1(hashes[i], top + 0x1000 * i, 0x1000); + if (pages != 0) + sha1(hashes[pages - 1], top + 0x1000 * (pages - 1), ((data - 1) % 0x1000) + 1); + } + } + + if (flag_S) { + _assert(signature != NULL); + + uint32_t data = framework.Swap(signature->dataoff); + uint32_t size = framework.Swap(signature->datasize); + + uint8_t *top = reinterpret_cast(framework.GetBase()); + uint8_t *blob = top + data; + struct SuperBlob *super = reinterpret_cast(blob); + super->blob.magic = Swap(CSMAGIC_EMBEDDED_SIGNATURE); + + uint32_t count = xmld == NULL ? 2 : 3; + uint32_t offset = sizeof(struct SuperBlob) + count * sizeof(struct BlobIndex); + + super->index[0].type = Swap(CSSLOT_CODEDIRECTORY); + super->index[0].offset = Swap(offset); + + uint32_t begin = offset; + struct CodeDirectory *directory = reinterpret_cast(blob + begin); + offset += sizeof(struct CodeDirectory); + + directory->blob.magic = Swap(CSMAGIC_CODEDIRECTORY); + directory->version = Swap(0x00020001); + directory->flags = Swap(0); + directory->codeLimit = Swap(data); + directory->hashSize = 0x14; + directory->hashType = 0x01; + directory->spare1 = 0x00; + directory->pageSize = 0x0c; + directory->spare2 = Swap(0); + + directory->identOffset = Swap(offset - begin); + strcpy(reinterpret_cast(blob + offset), base); + offset += strlen(base) + 1; + + uint32_t special = xmld == NULL ? CSSLOT_REQUIREMENTS : CSSLOT_ENTITLEMENTS; + directory->nSpecialSlots = Swap(special); + + uint8_t (*hashes)[20] = reinterpret_cast(blob + offset); + memset(hashes, 0, sizeof(*hashes) * special); + + offset += sizeof(*hashes) * special; + hashes += special; + + uint32_t pages = (data + 0x1000 - 1) / 0x1000; + directory->nCodeSlots = Swap(pages); + + if (pages != 1) + for (size_t i = 0; i != pages - 1; ++i) + sha1(hashes[i], top + 0x1000 * i, 0x1000); + if (pages != 0) + sha1(hashes[pages - 1], top + 0x1000 * (pages - 1), ((data - 1) % 0x1000) + 1); + + directory->hashOffset = Swap(offset - begin); + offset += sizeof(*hashes) * pages; + directory->blob.length = Swap(offset - begin); + + super->index[1].type = Swap(CSSLOT_REQUIREMENTS); + super->index[1].offset = Swap(offset); + + memcpy(blob + offset, "\xfa\xde\x0c\x01\x00\x00\x00\x0c\x00\x00\x00\x00", 0xc); + offset += 0xc; + + if (xmld != NULL) { + super->index[2].type = Swap(CSSLOT_ENTITLEMENTS); + super->index[2].offset = Swap(offset); + + uint32_t begin = offset; + struct Blob *entitlements = reinterpret_cast(blob + begin); + offset += sizeof(struct Blob); + + memcpy(blob + offset, xmld, xmls); + offset += xmls; + + entitlements->magic = Swap(CSMAGIC_ENTITLEMENTS); + entitlements->length = Swap(offset - begin); + } + + for (size_t index(0); index != count; ++index) { + uint32_t type = Swap(super->index[index].type); + if (type != 0 && type <= special) { + uint32_t offset = Swap(super->index[index].offset); + struct Blob *local = (struct Blob *) (blob + offset); + sha1((uint8_t *) (hashes - type), (uint8_t *) local, Swap(local->length)); + } + } + + super->count = Swap(count); + super->blob.length = Swap(offset); + + if (offset > size) { + fprintf(stderr, "offset (%u) > size (%u)\n", offset, size); + _assert(false); + } //else fprintf(stderr, "offset (%zu) <= size (%zu)\n", offset, size); + + memset(blob + offset, 0, size - offset); + } + + if (temp) { + struct stat info; + _syscall(stat(path, &info)); + _syscall(chown(temp, info.st_uid, info.st_gid)); + _syscall(chmod(temp, info.st_mode)); + _syscall(unlink(path)); + _syscall(rename(temp, path)); + free(temp); + } + + free(dir); ++filei; } catch (const char *) { ++filee;