]>
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 | ||
25 | #include <dlfcn.h> | |
26 | ||
27 | #include <sys/mman.h> | |
28 | #include <sys/types.h> | |
29 | #include <sys/stat.h> | |
30 | ||
31 | #include <mach-o/loader.h> | |
32 | ||
33 | #define INSTALLD "/usr/libexec/installd" | |
34 | #define LIBUICACHE "/usr/lib/libpatcyh.dylib" | |
35 | ||
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")); | |
37 | ||
38 | template <typename Header> | |
39 | static bool PatchInstall(bool uninstall, void *data) { | |
40 | Header *header(reinterpret_cast<Header *>(data)); | |
41 | ||
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"); | |
46 | return false; | |
47 | } | |
48 | ||
49 | if (command->cmd != LC_LOAD_DYLIB) | |
50 | continue; | |
51 | ||
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) | |
55 | continue; | |
56 | ||
57 | if (!uninstall) | |
58 | return true; | |
59 | ||
60 | if (i != header->ncmds - 1) { | |
61 | fprintf(stderr, "load command not in final position %zd %zd\n", i, header->ncmds); | |
62 | return false; | |
63 | } | |
64 | ||
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"); | |
67 | return false; | |
68 | } | |
69 | ||
70 | --header->ncmds; | |
71 | header->sizeofcmds -= command->cmdsize; | |
72 | memset(command, 0, command->cmdsize); | |
73 | ||
74 | return true; | |
75 | } | |
76 | ||
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"); | |
79 | return false; | |
80 | } | |
81 | ||
82 | if (uninstall) | |
83 | return true; | |
84 | ||
85 | dylib_command *load(reinterpret_cast<dylib_command *>(command)); | |
86 | memset(load, 0, sizeof(*load)); | |
87 | load->cmd = LC_LOAD_DYLIB; | |
88 | ||
89 | load->cmdsize = sizeof(*load) + sizeof(LIBUICACHE); | |
90 | load->cmdsize = (load->cmdsize + 15) / 16 * 16; | |
91 | memset(load + 1, 0, load->cmdsize - sizeof(*load)); | |
92 | ||
93 | dylib *dylib(&load->dylib); | |
94 | dylib->name.offset = sizeof(*load); | |
95 | memcpy(load + 1, LIBUICACHE, sizeof(LIBUICACHE)); | |
96 | ||
97 | ++header->ncmds; | |
98 | header->sizeofcmds += load->cmdsize; | |
99 | ||
100 | return true; | |
101 | } | |
102 | ||
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"); | |
106 | return false; | |
107 | } | |
108 | ||
109 | int fd(open(INSTALLD, O_RDWR)); | |
110 | if (fd == -1) | |
111 | return false; | |
112 | ||
113 | struct stat stat; | |
114 | if (fstat(fd, &stat) == -1) { | |
115 | close(fd); | |
116 | return false; | |
117 | } | |
118 | ||
119 | size_t size(stat.st_size); | |
120 | void *data(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); | |
121 | close(fd); | |
122 | if (data == MAP_FAILED) | |
123 | return false; | |
124 | ||
125 | bool changed(false); | |
126 | uint32_t magic(*reinterpret_cast<uint32_t *>(data)); | |
127 | switch (magic) { | |
128 | case MH_MAGIC: | |
129 | changed = PatchInstall<mach_header>(uninstall, data); | |
130 | break; | |
131 | case MH_MAGIC_64: | |
132 | changed = PatchInstall<mach_header_64>(uninstall, data); | |
133 | break; | |
134 | default: | |
135 | fprintf(stderr, "unknown header magic on installd: %08x\n", magic); | |
136 | return false; | |
137 | } | |
138 | ||
139 | munmap(data, size); | |
140 | ||
141 | if (changed) { | |
142 | system("ldid -s "INSTALLD""); | |
143 | system("cp -af "INSTALLD" "INSTALLD"_"); | |
144 | system("mv -f "INSTALLD"_ "INSTALLD""); | |
145 | } | |
146 | ||
147 | return true; | |
148 | } | |
149 | ||
150 | #endif//PATCH_HPP |