1 /* radare - LGPL - Copyright 2010-2018 - nibble, pancake */
2
3 #include <stdio.h>
4 #include <r_types.h>
5 #include <r_util.h>
6 #include "dyldcache.h"
7
r_bin_dyldcache_init(struct r_bin_dyldcache_obj_t * bin)8 static int r_bin_dyldcache_init(struct r_bin_dyldcache_obj_t* bin) {
9 int len = r_buf_fread_at (bin->b, 0, (ut8*)&bin->hdr, "16c4i7l", 1);
10 if (len == -1) {
11 perror ("read (cache_header)");
12 return false;
13 }
14 bin->nlibs = bin->hdr.numlibs;
15 return true;
16 }
17
r_bin_dyldcache_apply_patch(RBuffer * buf,ut32 data,ut64 offset)18 static int r_bin_dyldcache_apply_patch(RBuffer* buf, ut32 data, ut64 offset) {
19 return r_buf_write_at (buf, offset, (ut8 *)&data, sizeof (data));
20 }
21
22 #define NZ_OFFSET(x, y, z) if((x) > 0) r_bin_dyldcache_apply_patch (dbuf, (x) - linkedit_offset, addend + r_offsetof (y, z))
23
24 // make it public in util/buf.c ?
r_buf_read64le(RBuffer * buf,ut64 off)25 static ut64 r_buf_read64le(RBuffer *buf, ut64 off) {
26 ut8 data[8] = {0};
27 r_buf_read_at (buf, off, data, 8);
28 return r_read_le64 (data);
29 }
30
r_buf_read_string(RBuffer * buf,ut64 addr,int len)31 static char *r_buf_read_string(RBuffer *buf, ut64 addr, int len) {
32 ut8 *data = malloc (len);
33 if (data) {
34 r_buf_read_at (buf, addr, data, len);
35 data[len - 1] = 0;
36 return (char *)data;
37 }
38 return NULL;
39 }
40
41 /* TODO: Needs more testing and ERROR HANDLING */
r_bin_dyldcache_extract(struct r_bin_dyldcache_obj_t * bin,int idx,int * nlib)42 struct r_bin_dyldcache_lib_t *r_bin_dyldcache_extract(struct r_bin_dyldcache_obj_t* bin, int idx, int *nlib) {
43 ut64 liboff, linkedit_offset;
44 ut64 dyld_vmbase;
45 ut32 addend = 0;
46 struct r_bin_dyldcache_lib_t *ret = NULL;
47 struct dyld_cache_image_info* image_infos = NULL;
48 struct mach_header mh;
49 ut64 cmdptr;
50 int cmd, libsz = 0;
51 RBuffer* dbuf = NULL;
52 char *libname;
53
54 if (!bin) {
55 return NULL;
56 }
57 if (bin->size < 1) {
58 eprintf ("Empty file? (%s)\n", r_str_getf (bin->file));
59 return NULL;
60 }
61 if (bin->nlibs < 0 || idx < 0 || idx >= bin->nlibs) {
62 return NULL;
63 }
64 *nlib = bin->nlibs;
65 ret = R_NEW0 (struct r_bin_dyldcache_lib_t);
66 if (!ret) {
67 return NULL;
68 }
69 if (bin->hdr.startaddr > bin->size) {
70 eprintf ("corrupted dyldcache");
71 goto ret_err;
72 }
73
74 if (bin->hdr.startaddr > bin->size || bin->hdr.baseaddroff > bin->size) {
75 eprintf ("corrupted dyldcache");
76 goto ret_err;
77 }
78 int sz = bin->nlibs * sizeof (struct dyld_cache_image_info);
79 image_infos = malloc (sz);
80 if (!image_infos) {
81 goto ret_err;
82 }
83 r_buf_read_at (bin->b, bin->hdr.startaddr, (ut8*)image_infos, sz);
84 dyld_vmbase = r_buf_read64le (bin->b, bin->hdr.baseaddroff);
85 liboff = image_infos[idx].address - dyld_vmbase;
86 if (liboff > bin->size) {
87 eprintf ("Corrupted file\n");
88 goto ret_err;
89 }
90 ret->offset = liboff;
91 int pfo = image_infos[idx].pathFileOffset;
92 if (pfo < 0 || pfo > bin->size) {
93 eprintf ("corrupted file: pathFileOffset > bin->size (%d)\n", pfo);
94 goto ret_err;
95 }
96 libname = r_buf_read_string (bin->b, pfo, 64);
97 /* Locate lib hdr in cache */
98 int r = r_buf_read_at (bin->b, liboff, (ut8 *)&mh, sizeof (mh));
99 if (r != sizeof (mh)) {
100 goto ret_err;
101 }
102 /* Check it is mach-o */
103 if (mh.magic != MH_MAGIC && mh.magic != MH_MAGIC_64) {
104 if (mh.magic == 0xbebafeca) { //FAT binary
105 eprintf ("FAT Binary\n");
106 }
107 eprintf ("Not mach-o\n");
108 goto ret_err;
109 }
110 addend = mh.magic == MH_MAGIC? sizeof (struct mach_header) : sizeof (struct mach_header_64);
111 /* Write mach-o hdr */
112 if (!(dbuf = r_buf_new ())) {
113 eprintf ("new (dbuf)\n");
114 goto ret_err;
115 }
116 if (!r_buf_append_buf_slice (dbuf, bin->b, liboff, addend)) {
117 goto dbuf_err;
118 }
119 cmdptr = liboff + addend;
120 /* Write load commands */
121 for (cmd = 0; cmd < mh.ncmds; cmd++) {
122 struct load_command lc;
123 int r = r_buf_read_at (bin->b, cmdptr, (ut8 *)&lc, sizeof (lc));
124 if (r != sizeof (lc)) {
125 goto dbuf_err;
126 }
127 r_buf_append_bytes (dbuf, (ut8 *)&lc, lc.cmdsize);
128 cmdptr += lc.cmdsize;
129 }
130 cmdptr = liboff + addend;
131 /* Write segments */
132 for (cmd = linkedit_offset = 0; cmd < mh.ncmds; cmd++) {
133 struct load_command lc;
134 int r = r_buf_read_at (bin->b, cmdptr, (ut8 *)&lc, sizeof (lc));
135 if (r != sizeof (lc)) {
136 goto dbuf_err;
137 }
138 switch (lc.cmd) {
139 case LC_SEGMENT:
140 {
141 /* Write segment and patch offset */
142 struct segment_command seg;
143 r = r_buf_read_at (bin->b, cmdptr, (ut8 *)&seg, sizeof (seg));
144 if (r != sizeof (seg)) {
145 goto dbuf_err;
146 }
147 int t = seg.filesize;
148 if (seg.fileoff + seg.filesize > bin->size || seg.fileoff > bin->size) {
149 eprintf ("malformed dyldcache\n");
150 goto dbuf_err;
151 }
152 r_buf_append_buf_slice (dbuf, bin->b, seg.fileoff, t);
153 r_bin_dyldcache_apply_patch (dbuf, r_buf_size (dbuf),
154 addend + r_offsetof (struct segment_command, fileoff));
155 /* Patch section offsets */
156 int sect_offset = seg.fileoff - libsz;
157 libsz = r_buf_size (dbuf);
158 if (!strcmp (seg.segname, "__LINKEDIT")) {
159 linkedit_offset = sect_offset;
160 }
161 if (seg.nsects > 0) {
162 int nsect;
163 for (nsect = 0; nsect < seg.nsects; nsect++) {
164 struct section sect;
165 r = r_buf_read_at (bin->b, cmdptr + nsect * sizeof (struct segment_command), (ut8 *)§, sizeof (sect));
166 if (r != sizeof (sect)) {
167 break;
168 }
169 if (sect.offset > libsz) {
170 r_bin_dyldcache_apply_patch (dbuf, sect.offset - sect_offset,
171 addend + r_offsetof (struct section, offset));
172 }
173 }
174 }
175 }
176 break;
177 case LC_SYMTAB:
178 {
179 struct symtab_command st;
180 r = r_buf_read_at (bin->b, cmdptr, (ut8 *)&st, sizeof (st));
181 if (r != sizeof (st)) {
182 goto dbuf_err;
183 }
184 NZ_OFFSET (st.symoff, struct symtab_command, symoff);
185 NZ_OFFSET (st.stroff, struct symtab_command, stroff);
186 }
187 break;
188 case LC_DYSYMTAB:
189 {
190 struct dysymtab_command st;
191 r = r_buf_read_at (bin->b, cmdptr, (ut8 *)&st, sizeof (st));
192 if (r != sizeof (st)) {
193 goto dbuf_err;
194 }
195 NZ_OFFSET (st.tocoff, struct dysymtab_command, tocoff);
196 NZ_OFFSET (st.modtaboff, struct dysymtab_command, modtaboff);
197 NZ_OFFSET (st.extrefsymoff, struct dysymtab_command, extrefsymoff);
198 NZ_OFFSET (st.indirectsymoff, struct dysymtab_command, indirectsymoff);
199 NZ_OFFSET (st.extreloff, struct dysymtab_command, extreloff);
200 NZ_OFFSET (st.locreloff, struct dysymtab_command, locreloff);
201 }
202 break;
203 case LC_DYLD_INFO:
204 case LC_DYLD_INFO_ONLY:
205 {
206 struct dyld_info_command st;
207 r = r_buf_read_at (bin->b, cmdptr, (ut8 *)&st, sizeof (st));
208 if (r != sizeof (st)) {
209 goto dbuf_err;
210 }
211 NZ_OFFSET (st.rebase_off, struct dyld_info_command, rebase_off);
212 NZ_OFFSET (st.bind_off, struct dyld_info_command, bind_off);
213 NZ_OFFSET (st.weak_bind_off, struct dyld_info_command, weak_bind_off);
214 NZ_OFFSET (st.lazy_bind_off, struct dyld_info_command, lazy_bind_off);
215 NZ_OFFSET (st.export_off, struct dyld_info_command, export_off);
216 }
217 break;
218 }
219 cmdptr += lc.cmdsize;
220 }
221 /* Fill r_bin_dyldcache_lib_t ret */
222 ret->b = dbuf;
223 strncpy (ret->path, libname, sizeof (ret->path) - 1);
224 ret->size = libsz;
225 return ret;
226
227 dbuf_err:
228 r_buf_free (dbuf);
229 ret_err:
230 free (ret);
231 return NULL;
232 }
233
r_bin_dyldcache_free(struct r_bin_dyldcache_obj_t * bin)234 void* r_bin_dyldcache_free(struct r_bin_dyldcache_obj_t* bin) {
235 if (!bin) {
236 return NULL;
237 }
238 r_buf_free (bin->b);
239 free (bin);
240 return NULL;
241 }
242
r_bin_dydlcache_get_libname(struct r_bin_dyldcache_lib_t * lib,char ** libname)243 void r_bin_dydlcache_get_libname(struct r_bin_dyldcache_lib_t *lib, char **libname) {
244 char *cur = lib->path;
245 char *res = lib->path;
246 int path_length = strlen (lib->path);
247 while (cur < cur + path_length - 1) {
248 cur = strchr (cur, '/');
249 if (!cur) {
250 break;
251 }
252 cur++;
253 res = cur;
254 }
255 *libname = res;
256 }
257
r_bin_dyldcache_new(const char * file)258 struct r_bin_dyldcache_obj_t* r_bin_dyldcache_new(const char* file) {
259 struct r_bin_dyldcache_obj_t *bin;
260 if (!(bin = R_NEW0 (struct r_bin_dyldcache_obj_t))) {
261 return NULL;
262 }
263 bin->file = file;
264 size_t binsz;
265 ut8 *buf = (ut8 *)r_file_slurp (file, &binsz);
266 bin->size = binsz;
267 if (!buf) {
268 return r_bin_dyldcache_free (bin);
269 }
270 bin->b = r_buf_new ();
271 if (!r_buf_set_bytes (bin->b, buf, bin->size)) {
272 free (buf);
273 return r_bin_dyldcache_free (bin);
274 }
275 free (buf);
276 if (!r_bin_dyldcache_init (bin)) {
277 return r_bin_dyldcache_free (bin);
278 }
279 return bin;
280 }
281
r_bin_dyldcache_from_bytes_new(const ut8 * buf,ut64 size)282 struct r_bin_dyldcache_obj_t* r_bin_dyldcache_from_bytes_new(const ut8* buf, ut64 size) {
283 struct r_bin_dyldcache_obj_t *bin = R_NEW0 (struct r_bin_dyldcache_obj_t);
284 if (!bin) {
285 return NULL;
286 }
287 if (!buf) {
288 return r_bin_dyldcache_free (bin);
289 }
290 bin->b = r_buf_new ();
291 if (!bin->b || !r_buf_set_bytes (bin->b, buf, size)) {
292 return r_bin_dyldcache_free (bin);
293 }
294 if (!r_bin_dyldcache_init (bin)) {
295 return r_bin_dyldcache_free (bin);
296 }
297 bin->size = size;
298 return bin;
299 }
300