Rizin
unix-like reverse engineering framework and cli tools
dyldcache_rebase.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2021 Florian Märkl <info@florianmaerkl.de>
2 // SPDX-FileCopyrightText: 2020 Francesco Tamagni <mrmacete@protonmail.ch>
3 // SPDX-License-Identifier: LGPL-3.0-only
4 
5 #include "dyldcache.h"
6 
7 static void rebase_bytes_v1(RzDyldRebaseInfo1 *rebase_info, ut8 *buf, ut64 offset, int count, ut64 start_of_write) {
8  int in_buf = 0;
9  while (in_buf < count) {
10  ut64 offset_in_data = offset - rebase_info->start_of_data;
11  ut64 page_index = offset_in_data / rebase_info->page_size;
12  ut64 page_offset = offset_in_data % rebase_info->page_size;
13  ut64 to_next_page = rebase_info->page_size - page_offset;
14  ut64 entry_index = page_offset / 32;
15  ut64 offset_in_entry = (page_offset % 32) / 4;
16 
17  if (entry_index >= rebase_info->entries_size) {
18  in_buf += to_next_page;
19  offset += to_next_page;
20  continue;
21  }
22 
23  if (page_index >= rebase_info->toc_count) {
24  break;
25  }
26 
27  ut8 *entry = &rebase_info->entries[rebase_info->toc[page_index] * rebase_info->entries_size];
28  ut8 b = entry[entry_index];
29 
30  if (b & (1 << offset_in_entry)) {
32  value += rebase_info->slide;
34  in_buf += 8;
35  offset += 8;
36  } else {
37  in_buf += 4;
38  offset += 4;
39  }
40  }
41 }
42 
43 static void rebase_bytes_v2(RzDyldRebaseInfo2 *rebase_info, ut8 *buf, ut64 offset, int count, ut64 start_of_write) {
44  int in_buf = 0;
45  while (in_buf < count) {
46  ut64 offset_in_data = offset - rebase_info->start_of_data;
47  ut64 page_index = offset_in_data / rebase_info->page_size;
48  ut64 page_offset = offset_in_data % rebase_info->page_size;
49  ut64 to_next_page = rebase_info->page_size - page_offset;
50 
51  if (page_index >= rebase_info->page_starts_count) {
52  goto next_page;
53  }
54  ut16 page_flag = rebase_info->page_starts[page_index];
55 
56  if (page_flag == DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE) {
57  goto next_page;
58  }
59 
60  if (!(page_flag & DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA)) {
61  ut64 first_rebase_off = rebase_info->page_starts[page_index] * 4;
62  if (first_rebase_off >= page_offset && first_rebase_off < page_offset + count) {
63  ut32 delta = 1;
64  while (delta) {
65  ut64 position = in_buf + first_rebase_off - page_offset;
66  if (position >= count) {
67  break;
68  }
69  ut64 raw_value = rz_read_le64(buf + position);
70  delta = ((raw_value & rebase_info->delta_mask) >> rebase_info->delta_shift);
71  if (position >= start_of_write) {
72  ut64 new_value = raw_value & rebase_info->value_mask;
73  if (new_value != 0) {
74  new_value += rebase_info->value_add;
75  new_value += rebase_info->slide;
76  }
77  rz_write_le64(buf + position, new_value);
78  }
79  first_rebase_off += delta;
80  }
81  }
82  }
83  next_page:
84  in_buf += to_next_page;
85  offset += to_next_page;
86  }
87 }
88 
89 #define RZ_IS_PTR_AUTHENTICATED(x) B_IS_SET(x, 63)
90 
91 static void rebase_bytes_v3(RzDyldRebaseInfo3 *rebase_info, ut8 *buf, ut64 offset, int count, ut64 start_of_write) {
92  int in_buf = 0;
93  while (in_buf < count) {
94  ut64 offset_in_data = offset - rebase_info->start_of_data;
95  ut64 page_index = offset_in_data / rebase_info->page_size;
96  ut64 page_offset = offset_in_data % rebase_info->page_size;
97  ut64 to_next_page = rebase_info->page_size - page_offset;
98 
99  if (page_index >= rebase_info->page_starts_count) {
100  goto next_page;
101  }
102  ut64 delta = rebase_info->page_starts[page_index];
103 
105  goto next_page;
106  }
107 
108  ut64 first_rebase_off = delta;
109  if (first_rebase_off >= page_offset && first_rebase_off < page_offset + count) {
110  do {
111  ut64 position = in_buf + first_rebase_off - page_offset;
112  if (position >= count) {
113  break;
114  }
115  ut64 raw_value = rz_read_le64(buf + position);
116  delta = ((raw_value & rebase_info->delta_mask) >> rebase_info->delta_shift) * 8;
117  if (position >= start_of_write) {
118  ut64 new_value = 0;
119  if (RZ_IS_PTR_AUTHENTICATED(raw_value)) {
120  new_value = (raw_value & 0xFFFFFFFFULL) + rebase_info->auth_value_add;
121  // TODO: don't throw auth info away
122  } else {
123  new_value = ((raw_value << 13) & 0xFF00000000000000ULL) | (raw_value & 0x7ffffffffffULL);
124  new_value &= 0x00FFFFFFFFFFFFFFULL;
125  }
126  if (new_value != 0) {
127  new_value += rebase_info->slide;
128  }
129  rz_write_le64(buf + position, new_value);
130  }
131  first_rebase_off += delta;
132  } while (delta);
133  }
134  next_page:
135  in_buf += to_next_page;
136  offset += to_next_page;
137  }
138 }
139 
141  int imid;
142  int imin = 0;
143  int imax = infos->length - 1;
144 
145  while (imin < imax) {
146  imid = (imin + imax) / 2;
147  RzDyldRebaseInfosEntry *entry = &infos->entries[imid];
148  if ((entry->end) <= offset) {
149  imin = imid + 1;
150  } else {
151  imax = imid;
152  }
153  }
154 
155  RzDyldRebaseInfosEntry *minEntry = &infos->entries[imin];
156  if ((imax == imin) && (minEntry->start <= offset + count) && (minEntry->end >= offset)) {
157  return minEntry->info;
158  }
159  return NULL;
160 }
161 
162 static void rebase_bytes(RzDyldRebaseInfo *rebase_info, ut8 *buf, ut64 offset, int count, ut64 start_of_write) {
163  if (!rebase_info || !buf) {
164  return;
165  }
166 
167  if (rebase_info->version == 3) {
168  rebase_bytes_v3((RzDyldRebaseInfo3 *)rebase_info, buf, offset, count, start_of_write);
169  } else if (rebase_info->version == 2 || rebase_info->version == 4) {
170  rebase_bytes_v2((RzDyldRebaseInfo2 *)rebase_info, buf, offset, count, start_of_write);
171  } else if (rebase_info->version == 1) {
172  rebase_bytes_v1((RzDyldRebaseInfo1 *)rebase_info, buf, offset, count, start_of_write);
173  }
174 }
175 
176 typedef struct {
179 } BufCtx;
180 
181 static bool buf_init(RzBuffer *b, const void *user) {
182  BufCtx *ctx = RZ_NEW0(BufCtx);
183  if (!ctx) {
184  return false;
185  }
186  ctx->cache = (void *)user;
187  b->priv = ctx;
188  return true;
189 }
190 
191 static bool buf_fini(RzBuffer *b) {
192  BufCtx *ctx = b->priv;
193  free(ctx);
194  return true;
195 }
196 
197 static bool buf_resize(RzBuffer *b, ut64 newsize) {
198  BufCtx *ctx = b->priv;
199  return rz_buf_resize(ctx->cache->buf, newsize);
200 }
201 
203  BufCtx *ctx = b->priv;
204  st64 r = rz_buf_read_at(ctx->cache->buf, ctx->off, buf, len);
205  if (r <= 0 || !len) {
206  return r;
207  }
208 
209  RzDyldCache *cache = ctx->cache;
210  RzDyldRebaseInfo *rebase_info = rebase_info_by_range(cache->rebase_infos, ctx->off, len);
211 
212  if (!rebase_info) {
213  return rz_buf_read_at(cache->buf, ctx->off, buf, len);
214  }
215  st64 result = 0;
216  ut64 offset_in_data = ctx->off - rebase_info->start_of_data;
217  ut64 page_offset = offset_in_data % rebase_info->page_size;
218 
219  ut64 internal_offset = ctx->off & ~(rebase_info->page_size - 1);
220  ut64 internal_end = ctx->off + len;
221  int rounded_count = internal_end - internal_offset;
222 
223  ut8 *internal_buf = rebase_info->one_page_buf;
224  if (rounded_count > rebase_info->page_size) {
225  internal_buf = malloc(rounded_count);
226  if (!internal_buf) {
227  RZ_LOG_ERROR("Cannot allocate memory for 'internal_buf'\n");
228  return -1;
229  }
230  }
231 
232  st64 internal_result = rz_buf_read_at(cache->buf, internal_offset, internal_buf, rounded_count);
233  if (internal_result >= page_offset + len) {
234  rebase_bytes(rebase_info, internal_buf, internal_offset, internal_result, page_offset);
235  result = RZ_MIN(len, internal_result);
236  memcpy(buf, internal_buf + page_offset, result);
237  } else {
238  RZ_LOG_ERROR("Cannot rebase\n");
239  result = rz_buf_read_at(cache->buf, ctx->off, buf, len);
240  }
241 
242  if (internal_buf != rebase_info->one_page_buf) {
243  RZ_FREE(internal_buf);
244  }
245  return result;
246 }
247 
248 static st64 buf_write(RzBuffer *b, const ut8 *buf, ut64 len) {
249  BufCtx *ctx = b->priv;
250  return rz_buf_write_at(ctx->cache->buf, ctx->off, buf, len);
251 }
252 
254  BufCtx *ctx = b->priv;
255  return rz_buf_size(ctx->cache->buf);
256 }
257 
258 static st64 buf_seek(RzBuffer *b, st64 addr, int whence) {
259  BufCtx *ctx = b->priv;
260  return ctx->off = rz_seek_offset(ctx->off, rz_buf_size(b), addr, whence);
261 }
262 
264  BufCtx *ctx = b->priv;
265  return (ut8 *)rz_buf_data(ctx->cache->buf, sz);
266 }
267 
268 static const RzBufferMethods buf_methods = {
269  .init = buf_init,
270  .fini = buf_fini,
271  .read = buf_read,
272  .write = buf_write,
273  .get_size = buf_get_size,
274  .resize = buf_resize,
275  .seek = buf_seek,
276  .get_whole_buf = buf_get_whole_buf
277 };
278 
280  rz_return_val_if_fail(cache, NULL);
281  return rz_buf_new_with_methods(&buf_methods, cache);
282 }
283 
285  rz_return_val_if_fail(cache, false);
286  if (cache->rebase_infos) {
287  if (!rz_dyldcache_get_slide(cache)) {
288  return true;
289  }
290  }
291  return false;
292 }
293 
295  rz_return_val_if_fail(cache, false);
296  if (!rz_dyldcache_needs_rebasing(cache)) {
297  return false;
298  }
299  return !!rebase_info_by_range(cache->rebase_infos, paddr, size);
300 }
size_t len
Definition: 6502dis.c:15
static int value
Definition: cmd_api.c:93
static io_buf in_buf
Input and output buffers.
Definition: coder.c:39
#define RZ_API
#define NULL
Definition: cris-opc.c:27
#define r
Definition: crypto_rc6.c:12
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags static arg static fd static protocol static who struct sockaddr static addrlen static backlog struct timeval struct timezone static tz const struct iovec static count static mode const void const struct sockaddr static tolen const char static pathname void count
Definition: sflib.h:98
uint16_t ut16
uint32_t ut32
RZ_API ut64 rz_dyldcache_get_slide(RzDyldCache *cache)
Definition: dyldcache.c:1097
static ut8 * buf_get_whole_buf(RzBuffer *b, ut64 *sz)
static void rebase_bytes_v1(RzDyldRebaseInfo1 *rebase_info, ut8 *buf, ut64 offset, int count, ut64 start_of_write)
static ut64 buf_get_size(RzBuffer *b)
static RzDyldRebaseInfo * rebase_info_by_range(RzDyldRebaseInfos *infos, ut64 offset, ut64 count)
RZ_API RzBuffer * rz_dyldcache_new_rebasing_buf(RzDyldCache *cache)
static st64 buf_seek(RzBuffer *b, st64 addr, int whence)
static void rebase_bytes(RzDyldRebaseInfo *rebase_info, ut8 *buf, ut64 offset, int count, ut64 start_of_write)
RZ_API bool rz_dyldcache_needs_rebasing(RzDyldCache *cache)
static st64 buf_write(RzBuffer *b, const ut8 *buf, ut64 len)
static st64 buf_read(RzBuffer *b, ut8 *buf, ut64 len)
static bool buf_fini(RzBuffer *b)
static void rebase_bytes_v2(RzDyldRebaseInfo2 *rebase_info, ut8 *buf, ut64 offset, int count, ut64 start_of_write)
#define RZ_IS_PTR_AUTHENTICATED(x)
static const RzBufferMethods buf_methods
RZ_API bool rz_dyldcache_range_needs_rebasing(RzDyldCache *cache, ut64 paddr, ut64 size)
static bool buf_resize(RzBuffer *b, ut64 newsize)
static bool buf_init(RzBuffer *b, const void *user)
static void rebase_bytes_v3(RzDyldRebaseInfo3 *rebase_info, ut8 *buf, ut64 offset, int count, ut64 start_of_write)
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
voidpf void uLong size
Definition: ioapi.h:138
voidpf uLong offset
Definition: ioapi.h:144
voidpf void * buf
Definition: ioapi.h:138
uint8_t ut8
Definition: lh5801.h:11
memcpy(mem, inblock.get(), min(CONTAINING_RECORD(inblock.get(), MEMBLOCK, data) ->size, size))
static const char * page_index
Definition: rz-agent.c:8
void * malloc(size_t size)
Definition: malloc.c:123
#define DYLD_CACHE_SLIDE_V3_PAGE_ATTR_NO_REBASE
Definition: mach0_specs.h:353
#define DYLD_CACHE_SLIDE_PAGE_ATTR_NO_REBASE
Definition: mach0_specs.h:351
#define DYLD_CACHE_SLIDE_PAGE_ATTR_EXTRA
Definition: mach0_specs.h:350
#define rz_return_val_if_fail(expr, val)
Definition: rz_assert.h:108
RZ_API RZ_OWN RzBuffer * rz_buf_new_with_methods(RZ_NONNULL const RzBufferMethods *methods, void *init_user)
Creates a new buffer with a specific back end.
Definition: buf.c:525
RZ_API bool rz_buf_resize(RZ_NONNULL RzBuffer *b, ut64 newsize)
Resize the buffer size.
Definition: buf.c:890
static ut64 rz_seek_offset(ut64 cur, ut64 length, st64 addr, int whence)
change cur according to addr and whence (RZ_BUF_SET/RZ_BUF_CUR/RZ_BUF_END)
Definition: rz_buf.h:67
RZ_API st64 rz_buf_write_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL const ut8 *buf, ut64 len)
Write len bytes of the buffer at the specified address.
Definition: buf.c:1197
RZ_API st64 rz_buf_read_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL RZ_OUT ut8 *buf, ut64 len)
Read len bytes of the buffer at the specified address.
Definition: buf.c:1136
RZ_DEPRECATE RZ_API RZ_BORROW ut8 * rz_buf_data(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_OUT ut64 *size)
Return a borrowed array of bytes representing the buffer data.
Definition: buf.c:1287
RZ_API ut64 rz_buf_size(RZ_NONNULL RzBuffer *b)
Return the size of the buffer.
Definition: buf.c:1225
static ut64 rz_read_le64(const void *src)
Definition: rz_endian.h:266
static void rz_write_le64(void *dest, ut64 val)
Definition: rz_endian.h:277
#define RZ_LOG_ERROR(fmtstr,...)
Definition: rz_log.h:58
#define RZ_NEW0(x)
Definition: rz_types.h:284
#define RZ_FREE(x)
Definition: rz_types.h:369
#define RZ_MIN(x, y)
#define st64
Definition: rz_types_base.h:10
#define b(i)
Definition: sha256.c:42
RzDyldCache * cache
Definition: zipcmp.c:77
RzBufferInit init
Definition: rz_buf.h:32
Definition: dyldcache.h:23
ut64 start
Definition: dyldcache.h:24
RzDyldRebaseInfo * info
Definition: dyldcache.h:26
ut64 end
Definition: dyldcache.h:25
RzDyldRebaseInfosEntry * entries
Definition: dyldcache.h:30
RzBuffer * buf
Definition: dyldcache.h:105
RzDyldRebaseInfos * rebase_infos
Definition: dyldcache.h:106
static st64 delta
Definition: vmenus.c:2425
ut64(WINAPI *w32_GetEnabledXStateFeatures)()
static int addr
Definition: z80asm.c:58