With -p on all platforms, we can't use asprintf().
[cycript.git] / Inject.cpp
1 /* Cycript - The Truly Universal Scripting Language
2 * Copyright (C) 2009-2016 Jay Freeman (saurik)
3 */
4
5 /* GNU Affero General Public License, Version 3 {{{ */
6 /*
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/
20 /* }}} */
21
22 #include <sstream>
23 #include <string>
24
25 #include <dlfcn.h>
26
27 #include "Exception.hpp"
28 #include "Pooling.hpp"
29
30 #if defined(__APPLE__) && (defined(__i386__) || defined(__x86_64__))
31 #include <unistd.h>
32
33 #include <sys/fcntl.h>
34 #include <sys/mman.h>
35
36 #include <mach-o/loader.h>
37
38 #define CS_OPS_PIDOFFSET 6
39
40 extern "C" int csops(pid_t pid, unsigned int ops, void *useraddr, size_t usersize);
41 extern "C" int proc_pidpath(int pid, void *buffer, uint32_t buffersize);
42 #endif
43
44 int main(int argc, char * const argv[], char const * const envp[]);
45 extern "C" char *MSmain0(int argc, char *argv[]);
46
47 static std::string LibraryFor(void *address) {
48 Dl_info info;
49 _assert(dladdr(address, &info) != 0);
50 return info.dli_fname;
51 }
52
53 template <typename Type_>
54 Type_ *shift(Type_ *data, size_t size) {
55 return reinterpret_cast<Type_ *>(reinterpret_cast<uint8_t *>(data) + size);
56 }
57
58 void InjectLibrary(int pid, std::ostream &stream, int argc, const char *const argv[]) {
59 auto cynject(LibraryFor(reinterpret_cast<void *>(&main)));
60 auto slash(cynject.rfind('/'));
61 _assert(slash != std::string::npos);
62 cynject = cynject.substr(0, slash) + "/cynject";
63
64 auto library(LibraryFor(reinterpret_cast<void *>(&MSmain0)));
65
66 #if defined(__APPLE__) && (defined(__i386__) || defined(__x86_64__))
67 off_t offset;
68 _assert(csops(pid, CS_OPS_PIDOFFSET, &offset, sizeof(offset)) != -1);
69
70 // XXX: implement a safe version of this
71 char path[4096];
72 int writ(proc_pidpath(pid, path, sizeof(path)));
73 _assert(writ != 0);
74
75 auto fd(_syscall(open(path, O_RDONLY)));
76
77 auto page(getpagesize());
78 auto size(page * 4);
79 auto map(_syscall(mmap(NULL, size, PROT_READ, MAP_SHARED, fd, offset)));
80
81 _syscall(close(fd)); // XXX: _scope
82
83 auto header(reinterpret_cast<mach_header *>(map));
84 auto command(reinterpret_cast<load_command *>(header + 1));
85
86 switch (header->magic) {
87 case MH_MAGIC_64:
88 command = shift(command, sizeof(uint32_t));
89 case MH_MAGIC:
90 break;
91 default:
92 _assert(false);
93 }
94
95 bool ios(false);
96 for (decltype(header->ncmds) i(0); i != header->ncmds; ++i) {
97 if (command->cmd == LC_VERSION_MIN_IPHONEOS)
98 ios = true;
99 command = shift(command, command->cmdsize);
100 }
101
102 _syscall(munmap(map, size)); // XXX: _scope
103
104 auto length(library.size());
105 _assert(length >= 6);
106 length -= 6;
107
108 _assert(library.substr(length) == ".dylib");
109 library = library.substr(0, length);
110 library += ios ? "-sim" : "-sys";
111 library += ".dylib";
112 #endif
113
114 std::ostringstream inject;
115 inject << cynject << " " << std::dec << pid << " " << library;
116 for (decltype(argc) i(0); i != argc; ++i) {
117 inject << " '";
118 for (const char *arg(argv[i]); *arg != '\0'; ++arg)
119 if (*arg != '\'')
120 inject.put(*arg);
121 else
122 inject << "'\\''";
123 inject << "'";
124 }
125
126 FILE *process(popen(inject.str().c_str(), "r"));
127 _assert(process != NULL);
128
129 for (;;) {
130 char data[1024];
131 auto writ(fread(data, 1, sizeof(data), process));
132 stream.write(data, writ);
133
134 if (writ == sizeof(data))
135 continue;
136 _assert(!ferror(process));
137 if (feof(process))
138 break;
139 }
140
141 auto status(pclose(process)); // XXX: _scope (sort of?)
142 _assert(status != -1);
143 _assert(status == 0);
144 }