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/>.
30 #include <sys/types.h>
33 #include <mach-o/loader.h>
35 #define LIBUICACHE "/usr/lib/libpatcyh.dylib"
37 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"));
39 template <typename Header>
40 static bool PatchInstall(bool uninstall, void *data) {
41 Header *header(reinterpret_cast<Header *>(data));
43 load_command *command(reinterpret_cast<load_command *>(header + 1));
44 for (size_t i(0); i != header->ncmds; ++i, command = reinterpret_cast<load_command *>(reinterpret_cast<uint8_t *>(command) + command->cmdsize)) {
45 if (command->cmdsize > sizeof(Header) + header->sizeofcmds - (reinterpret_cast<uint8_t *>(command) - reinterpret_cast<uint8_t *>(header))) {
46 fprintf(stderr, "load command is to long to fit in header\n");
50 if (command->cmd != LC_LOAD_DYLIB)
53 dylib_command *load(reinterpret_cast<dylib_command *>(command));
54 const char *name(reinterpret_cast<char *>(command) + load->dylib.name.offset);
55 if (strcmp(name, LIBUICACHE) != 0)
61 if (i != header->ncmds - 1) {
62 fprintf(stderr, "load command not in final position %zd %zd\n", i, header->ncmds);
66 if (reinterpret_cast<uint8_t *>(command) + command->cmdsize != reinterpret_cast<uint8_t *>(header + 1) + header->sizeofcmds) {
67 fprintf(stderr, "load command header size integrity fail\n");
72 header->sizeofcmds -= command->cmdsize;
73 memset(command, 0, command->cmdsize);
78 if (reinterpret_cast<uint8_t *>(command) != reinterpret_cast<uint8_t *>(header + 1) + header->sizeofcmds) {
79 fprintf(stderr, "load command header size integrity fail\n");
86 dylib_command *load(reinterpret_cast<dylib_command *>(command));
87 memset(load, 0, sizeof(*load));
88 load->cmd = LC_LOAD_DYLIB;
90 load->cmdsize = sizeof(*load) + sizeof(LIBUICACHE);
91 load->cmdsize = (load->cmdsize + 15) / 16 * 16;
92 memset(load + 1, 0, load->cmdsize - sizeof(*load));
94 dylib *dylib(&load->dylib);
95 dylib->name.offset = sizeof(*load);
96 memcpy(load + 1, LIBUICACHE, sizeof(LIBUICACHE));
99 header->sizeofcmds += load->cmdsize;
104 static bool Patch(const std::string &path, const std::string &service, bool uninstall, bool abort) {
105 if (!abort && system("ldid --") != 0) {
106 fprintf(stderr, "this package requires ldid to be installed\n");
110 int fd(open(path.c_str(), O_RDWR));
115 if (fstat(fd, &stat) == -1) {
120 size_t size(stat.st_size);
121 void *data(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
123 if (data == MAP_FAILED)
127 uint32_t magic(*reinterpret_cast<uint32_t *>(data));
130 changed = PatchInstall<mach_header>(uninstall, data);
133 changed = PatchInstall<mach_header_64>(uninstall, data);
136 fprintf(stderr, "unknown header magic on installd: %08x\n", magic);
143 system(("ldid -s " + path + "").c_str());
144 system(("cp -af " + path + " " + path + "_").c_str());
145 system(("mv -f " + path + "_ " + path + "").c_str());
146 system(("launchctl stop " + service + "").c_str());
153 static bool PatchInstall(bool uninstall, bool abort) {
154 return Patch("/usr/libexec/installd", "com.apple.mobile.installd", uninstall, abort);
157 static bool PatchLaunch(bool uninstall, bool abort) {
158 return Patch("/usr/libexec/lsd", "com.apple.lsd", uninstall, abort);