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 *)&sect, 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