]> git.saurik.com Git - patcyh.git/blame - patch.hpp
Initial version of patcyh, carved from uikittools.
[patcyh.git] / patch.hpp
CommitLineData
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
36static 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
38template <typename Header>
39static 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
103static 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