1 /* patcyh - you should pronounce it like patch
2 * Copyright (C) 2015 Jay Freeman (saurik)
5 /* GNU General Public License, Version 3 {{{ */
7 * Cydia is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published
9 * by the Free Software Foundation, either version 3 of the License,
10 * or (at your option) any later version.
12 * Cydia is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Cydia. If not, see <http://www.gnu.org/licenses/>.
28 #include <sys/types.h>
31 #include <mach-o/loader.h>
33 #define INSTALLD "/usr/libexec/installd"
34 #define LIBUICACHE "/usr/lib/libpatcyh.dylib"
36 static void *(*$memmem)(const void *, size_t, const void *, size_t) = reinterpret_cast<void *(*)(const void *, size_t, const void *, size_t)>(dlsym(RTLD_DEFAULT, "memmem"));
38 template <typename Header>
39 static bool PatchInstall(bool uninstall, void *data) {
40 Header *header(reinterpret_cast<Header *>(data));
42 load_command *command(reinterpret_cast<load_command *>(header + 1));
43 for (size_t i(0); i != header->ncmds; ++i, command = reinterpret_cast<load_command *>(reinterpret_cast<uint8_t *>(command) + command->cmdsize)) {
44 if (command->cmdsize > sizeof(Header) + header->sizeofcmds - (reinterpret_cast<uint8_t *>(command) - reinterpret_cast<uint8_t *>(header))) {
45 fprintf(stderr, "load command is to long to fit in header\n");
49 if (command->cmd != LC_LOAD_DYLIB)
52 dylib_command *load(reinterpret_cast<dylib_command *>(command));
53 const char *name(reinterpret_cast<char *>(command) + load->dylib.name.offset);
54 if (strcmp(name, LIBUICACHE) != 0)
60 if (i != header->ncmds - 1) {
61 fprintf(stderr, "load command not in final position %zd %zd\n", i, header->ncmds);
65 if (reinterpret_cast<uint8_t *>(command) + command->cmdsize != reinterpret_cast<uint8_t *>(header + 1) + header->sizeofcmds) {
66 fprintf(stderr, "load command header size integrity fail\n");
71 header->sizeofcmds -= command->cmdsize;
72 memset(command, 0, command->cmdsize);
77 if (reinterpret_cast<uint8_t *>(command) != reinterpret_cast<uint8_t *>(header + 1) + header->sizeofcmds) {
78 fprintf(stderr, "load command header size integrity fail\n");
85 dylib_command *load(reinterpret_cast<dylib_command *>(command));
86 memset(load, 0, sizeof(*load));
87 load->cmd = LC_LOAD_DYLIB;
89 load->cmdsize = sizeof(*load) + sizeof(LIBUICACHE);
90 load->cmdsize = (load->cmdsize + 15) / 16 * 16;
91 memset(load + 1, 0, load->cmdsize - sizeof(*load));
93 dylib *dylib(&load->dylib);
94 dylib->name.offset = sizeof(*load);
95 memcpy(load + 1, LIBUICACHE, sizeof(LIBUICACHE));
98 header->sizeofcmds += load->cmdsize;
103 static bool PatchInstall(bool uninstall, bool abort) {
104 if (!abort && system("ldid --") != 0) {
105 fprintf(stderr, "this package requires ldid to be installed\n");
109 int fd(open(INSTALLD, O_RDWR));
114 if (fstat(fd, &stat) == -1) {
119 size_t size(stat.st_size);
120 void *data(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
122 if (data == MAP_FAILED)
126 uint32_t magic(*reinterpret_cast<uint32_t *>(data));
129 changed = PatchInstall<mach_header>(uninstall, data);
132 changed = PatchInstall<mach_header_64>(uninstall, data);
135 fprintf(stderr, "unknown header magic on installd: %08x\n", magic);
142 system("ldid -s "INSTALLD"");
143 system("cp -af "INSTALLD" "INSTALLD"_");
144 system("mv -f "INSTALLD"_ "INSTALLD"");