]>
Commit | Line | Data |
---|---|---|
ec4c25d4 JF |
1 | /* patcyh - you should pronounce it like patch |
2 | * Copyright (C) 2015 Jay Freeman (saurik) | |
3 | */ | |
4 | ||
5 | /* GNU General Public License, Version 3 {{{ */ | |
6 | /* | |
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. | |
11 | * | |
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. | |
16 | * | |
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/>. | |
19 | **/ | |
20 | /* }}} */ | |
21 | ||
22 | #ifndef PATCH_HPP | |
23 | #define PATCH_HPP | |
24 | ||
c3315f13 JF |
25 | #include <string> |
26 | ||
ec4c25d4 JF |
27 | #include <dlfcn.h> |
28 | ||
29 | #include <sys/mman.h> | |
30 | #include <sys/types.h> | |
31 | #include <sys/stat.h> | |
32 | ||
33 | #include <mach-o/loader.h> | |
34 | ||
ec4c25d4 JF |
35 | #define LIBUICACHE "/usr/lib/libpatcyh.dylib" |
36 | ||
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")); | |
38 | ||
39 | template <typename Header> | |
40 | static bool PatchInstall(bool uninstall, void *data) { | |
41 | Header *header(reinterpret_cast<Header *>(data)); | |
42 | ||
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"); | |
47 | return false; | |
48 | } | |
49 | ||
50 | if (command->cmd != LC_LOAD_DYLIB) | |
51 | continue; | |
52 | ||
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) | |
56 | continue; | |
57 | ||
58 | if (!uninstall) | |
59 | return true; | |
60 | ||
61 | if (i != header->ncmds - 1) { | |
62 | fprintf(stderr, "load command not in final position %zd %zd\n", i, header->ncmds); | |
63 | return false; | |
64 | } | |
65 | ||
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"); | |
68 | return false; | |
69 | } | |
70 | ||
71 | --header->ncmds; | |
72 | header->sizeofcmds -= command->cmdsize; | |
73 | memset(command, 0, command->cmdsize); | |
74 | ||
75 | return true; | |
76 | } | |
77 | ||
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"); | |
80 | return false; | |
81 | } | |
82 | ||
83 | if (uninstall) | |
84 | return true; | |
85 | ||
86 | dylib_command *load(reinterpret_cast<dylib_command *>(command)); | |
87 | memset(load, 0, sizeof(*load)); | |
88 | load->cmd = LC_LOAD_DYLIB; | |
89 | ||
90 | load->cmdsize = sizeof(*load) + sizeof(LIBUICACHE); | |
91 | load->cmdsize = (load->cmdsize + 15) / 16 * 16; | |
92 | memset(load + 1, 0, load->cmdsize - sizeof(*load)); | |
93 | ||
94 | dylib *dylib(&load->dylib); | |
95 | dylib->name.offset = sizeof(*load); | |
96 | memcpy(load + 1, LIBUICACHE, sizeof(LIBUICACHE)); | |
97 | ||
98 | ++header->ncmds; | |
99 | header->sizeofcmds += load->cmdsize; | |
100 | ||
101 | return true; | |
102 | } | |
103 | ||
c3315f13 | 104 | static bool Patch(const std::string &path, const std::string &service, bool uninstall, bool abort) { |
ec4c25d4 JF |
105 | if (!abort && system("ldid --") != 0) { |
106 | fprintf(stderr, "this package requires ldid to be installed\n"); | |
107 | return false; | |
108 | } | |
109 | ||
c3315f13 | 110 | int fd(open(path.c_str(), O_RDWR)); |
ec4c25d4 JF |
111 | if (fd == -1) |
112 | return false; | |
113 | ||
114 | struct stat stat; | |
115 | if (fstat(fd, &stat) == -1) { | |
116 | close(fd); | |
117 | return false; | |
118 | } | |
119 | ||
120 | size_t size(stat.st_size); | |
121 | void *data(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); | |
122 | close(fd); | |
123 | if (data == MAP_FAILED) | |
124 | return false; | |
125 | ||
126 | bool changed(false); | |
127 | uint32_t magic(*reinterpret_cast<uint32_t *>(data)); | |
128 | switch (magic) { | |
129 | case MH_MAGIC: | |
130 | changed = PatchInstall<mach_header>(uninstall, data); | |
131 | break; | |
132 | case MH_MAGIC_64: | |
133 | changed = PatchInstall<mach_header_64>(uninstall, data); | |
134 | break; | |
135 | default: | |
136 | fprintf(stderr, "unknown header magic on installd: %08x\n", magic); | |
137 | return false; | |
138 | } | |
139 | ||
140 | munmap(data, size); | |
141 | ||
142 | if (changed) { | |
c3315f13 JF |
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()); | |
ec4c25d4 JF |
147 | } |
148 | ||
149 | return true; | |
150 | } | |
151 | ||
c3315f13 JF |
152 | |
153 | static bool PatchInstall(bool uninstall, bool abort) { | |
154 | return Patch("/usr/libexec/installd", "com.apple.mobile.installd", uninstall, abort); | |
155 | } | |
156 | ||
157 | static bool PatchLaunch(bool uninstall, bool abort) { | |
158 | return Patch("/usr/libexec/lsd", "com.apple.lsd", uninstall, abort); | |
159 | } | |
160 | ||
ec4c25d4 | 161 | #endif//PATCH_HPP |