Rizin
unix-like reverse engineering framework and cli tools
ar.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2017 xarkes <antide.petit@gmail.com>
2 // SPDX-License-Identifier: LGPL-3.0-only
3 #include <stdio.h>
4 #include "ar.h"
5 
6 #define AR_MAGIC_HEADER "!<arch>\n"
7 #define AR_FILE_HEADER_END "`\n"
8 
9 typedef struct Filetable {
10  char *data;
14 
15 static RzArFp *arfp_new(RzBuffer *b, bool shared_buf) {
17  RzArFp *f = RZ_NEW(RzArFp);
18  if (f) {
19  f->name = NULL;
20  f->shared_buf = shared_buf;
21  f->buf = b;
22  f->start = 0;
23  f->end = 0;
24  }
25  return f;
26 }
27 
29  char buf[sizeof(AR_MAGIC_HEADER) - 1];
30  if (rz_buf_read(b, (ut8 *)buf, sizeof(buf)) != sizeof(buf)) {
31  return false;
32  }
33  if (strncmp(buf, AR_MAGIC_HEADER, 8)) {
34  RZ_LOG_ERROR("ar: Wrong file type.\n");
35  return false;
36  }
37  return true;
38 }
39 
40 static inline void arf_clean_name(RzArFp *arf) {
41  free(arf->name);
42  arf->name = NULL;
43 }
44 
45 static char *name_from_table(ut64 off, filetable *tbl) {
46  if (off > tbl->size) {
47  RZ_LOG_ERROR("ar: Malformed ar: name lookup out of bounds for header at offset 0x%" PFMT64x "\n", off);
48  return NULL;
49  }
50  // files are suppose to be line feed seperated but we also stop on invalid
51  // chars, such as '/' or '\0'
52 
53  char *buf = tbl->data;
54  ut64 i;
55  for (i = off; i < tbl->size; i++) {
56  char c = buf[i];
57  if (c == '\n' || c == '\0') {
58  break;
59  }
60  }
61  if (i == off) {
62  return NULL;
63  }
64  return rz_str_newlen(buf + off, i - off - 1);
65 }
66 
67 #define VERIFY_AR_NUM_FIELD(x, s) \
68  x[sizeof(x) - 1] = '\0'; \
69  rz_str_trim_tail(x); \
70  if (x[0] != '\0' && (x[0] == '-' || !rz_str_isnumber(x))) { \
71  RZ_LOG_ERROR("ar: Malformed AR: bad %s in header at offset 0x%" PFMT64x "\n", s, h_off); \
72  return -1; \
73  }
74 
75 /* -1 error, 0 end, 1 contnue */
76 static int ar_parse_header(RzArFp *arf, filetable *tbl, ut64 arsize) {
77  rz_return_val_if_fail(arf && arf->buf && tbl, -1);
78  RzBuffer *b = arf->buf;
79 
80  ut64 h_off = rz_buf_tell(b);
81  if (h_off % 2 == 1) {
82  // headers start at even offset
83  ut8 tmp[1];
84  if (rz_buf_read(b, tmp, 1) != 1 || tmp[0] != '\n') {
85  return -1;
86  }
87  h_off++;
88  }
89 
90  struct header {
91  char name[16];
92  char timestamp[12];
93  char oid[6];
94  char gid[6];
95  char mode[8];
96  char size[10];
97  char end[2];
98  } h;
99 
100  int r = rz_buf_read(b, (ut8 *)&h, sizeof(h));
101  if (r != sizeof(h)) {
102  if (r == 0) {
103  return 0; // no more file
104  }
105  if (r < 0) {
106  RZ_LOG_ERROR("ar: io error\n");
107  } else {
108  RZ_LOG_ERROR("ar: Invalid file length\n");
109  }
110  return -1;
111  }
112 
113  if (strncmp(h.end, AR_FILE_HEADER_END, sizeof(h.end))) {
114  RZ_LOG_ERROR("ar: Invalid header at offset 0x%" PFMT64x ": bad end field\n", h_off);
115  return -1;
116  }
117 
118  // remove trailing spaces from fields and verify they are valid
119  VERIFY_AR_NUM_FIELD(h.timestamp, "timestamp")
120  VERIFY_AR_NUM_FIELD(h.oid, "oid")
121  VERIFY_AR_NUM_FIELD(h.gid, "gid")
122  VERIFY_AR_NUM_FIELD(h.mode, "mode")
123  VERIFY_AR_NUM_FIELD(h.size, "size")
124 
125  if (h.size[0] == '\0') {
126  RZ_LOG_ERROR("ar: Malformed AR: bad size in header at offset 0x%" PFMT64x "\n", h_off);
127  return -1;
128  }
129  ut64 size = atol(h.size);
130 
131  h.timestamp[0] = '\0'; // null terminate h.name
132  rz_str_trim_tail(h.name);
133 
134  /*
135  * handle fake files
136  */
137  if (!strcmp(h.name, "/")) {
138  // skip over symbol table
139  if (rz_buf_seek(b, size, RZ_BUF_CUR) <= 0 || rz_buf_tell(b) > arsize) {
140  RZ_LOG_ERROR("ar: Malformed ar: too short\n");
141  return -1;
142  }
143  // return next entry
144  return ar_parse_header(arf, tbl, arsize);
145  } else if (!strcmp(h.name, "//")) {
146  // table of file names
147  if (tbl->data || tbl->size != 0) {
148  RZ_LOG_ERROR("ar: invalid ar file: two filename lookup tables (at 0x%" PFMT64x ", and 0x%" PFMT64x ")\n", tbl->offset, h_off);
149  return -1;
150  }
151  tbl->data = (char *)malloc(size + 1);
152  if (!tbl->data || rz_buf_read(b, (ut8 *)tbl->data, size) != size) {
153  return -1;
154  }
155  tbl->data[size] = '\0';
156  tbl->size = size;
157  tbl->offset = h_off;
158 
159  // return next entry
160  return ar_parse_header(arf, tbl, arsize);
161  }
162 
163  /*
164  * handle real files
165  */
166  RzList *list = rz_str_split_duplist(h.name, "/", false); // don't strip spaces
167  if (rz_list_length(list) != 2) {
169  RZ_LOG_ERROR("ar: invalid ar file: invalid file name in header at: 0x%" PFMT64x "\n", h_off);
170  return -1;
171  }
172 
173  char *tmp = rz_list_pop_head(list);
174  if (tmp[0] == '\0') {
175  free(tmp);
176  tmp = rz_list_pop(list);
177  if (rz_str_isnumber(tmp)) {
178  arf->name = name_from_table(atol(tmp), tbl);
179  } else {
180  RZ_LOG_ERROR("ar: invalid ar file: invalid file name in header at: 0x%" PFMT64x "\n", h_off);
181  }
182  free(tmp);
183  } else {
184  arf->name = tmp;
185  tmp = rz_list_pop(list);
186  if (tmp[0]) {
187  arf_clean_name(arf);
188  RZ_LOG_ERROR("ar: invalid ar file: invalid file name in header at: 0x%" PFMT64x "\n", h_off);
189  }
190  free(tmp);
191  }
193 
194  if (!arf->name) {
195  return -1;
196  }
197  arf->start = rz_buf_tell(b);
198  arf->end = arf->start + size;
199 
200  // skip over file content and make sure it is all there
201  if (rz_buf_seek(b, size, RZ_BUF_CUR) <= 0 || rz_buf_tell(b) > arsize) {
202  RZ_LOG_ERROR("ar: Malformed ar: missing the end of %s (header offset: 0x%" PFMT64x ")\n", arf->name, h_off);
203  arf_clean_name(arf);
204  return -1;
205  }
206 
207  return 1;
208 }
209 #undef VERIFY_AR_NUM_FIELD
210 
218 RZ_API RzList *ar_open_all(const char *arname, int perm) {
219  if (!arname) {
220  rz_sys_perror(__FUNCTION__);
221  return NULL;
222  }
223 
225  if (!files) {
226  rz_sys_perror(__FUNCTION__);
227  return NULL;
228  }
229 
230  RzBuffer *b = rz_buf_new_file(arname, perm, 0);
231  if (!b) {
233  rz_sys_perror(__FUNCTION__);
234  return NULL;
235  }
236 
237  ut64 arsize = rz_buf_size(b);
238 
239  if (!ar_check_magic(b)) {
241  rz_buf_free(b);
242  return NULL;
243  }
244 
245  filetable tbl = { NULL, 0, 0 };
246  int res = -1;
247  bool shared = false;
248 
249  do {
250  shared = !rz_list_empty(files);
251  RzArFp *arf = arfp_new(b, shared);
252  if (!arf) {
254  if (!shared) {
255  rz_buf_free(b);
256  }
257  return NULL;
258  }
259  if ((res = ar_parse_header(arf, &tbl, arsize)) <= 0) {
260  // on error or when it has reached the EOF
261  free(tbl.data);
262  ar_close(arf);
263  return files;
264  }
265  if (!rz_list_append(files, arf)) {
266  free(tbl.data);
267  ar_close(arf);
269  return NULL;
270  }
271  } while (res > 0);
272 
273  // this portion should never be reached
274  free(tbl.data);
275  return files;
276 }
277 
286 RZ_API RzArFp *ar_open_file(const char *arname, int perm, const char *filename) {
287  if (!filename || !arname) {
288  rz_sys_perror(__FUNCTION__);
289  return NULL;
290  }
291 
292  RzBuffer *b = rz_buf_new_file(arname, perm, 0);
293  if (!b) {
294  rz_sys_perror(__FUNCTION__);
295  return NULL;
296  }
297 
298  ut64 arsize = rz_buf_size(b);
299 
300  if (!ar_check_magic(b)) {
301  rz_buf_free(b);
302  return NULL;
303  }
304 
305  RzArFp *arf = arfp_new(b, NULL);
306  if (!arf) {
307  rz_buf_free(b);
308  return NULL;
309  }
310 
311  filetable tbl = { NULL, 0, 0 };
312  int r;
313  while ((r = ar_parse_header(arf, &tbl, arsize)) > 0) {
314  if (filename) {
315  if (!strcmp(filename, arf->name)) {
316  // found the right file
317  break;
318  }
319  }
320 
321  // clean RzArFp for next loop
322  arf_clean_name(arf);
323  }
324 
325  free(tbl.data);
326 
327  if (r <= 0) {
328  if (r == 0 && filename) {
329  RZ_LOG_ERROR("ar: Cound not find file '%s' in archive '%s'\n", filename, arname);
330  }
331  ar_close(arf); // results in buf being free'd
332  return NULL;
333  }
334 
335  return arf;
336 }
337 
339  if (!f) {
340  return;
341  }
342  free(f->name);
343  if (!f->shared_buf) {
344  rz_buf_free(f->buf);
345  }
346  free(f);
347 }
348 
349 RZ_API int ar_read_at(RzArFp *f, ut64 off, void *buf, int count) {
350  off += f->start;
351  if (off > f->end) {
352  return -1;
353  }
354  if (count + off > f->end) {
355  count = f->end - off;
356  }
357  return rz_buf_read_at(f->buf, off, buf, count);
358 }
359 
360 RZ_API int ar_write_at(RzArFp *f, ut64 off, void *buf, int count) {
361  off += f->start;
362  if (off > f->end) {
363  return -1;
364  }
365  if (count + off > f->end) {
366  count = f->end - off;
367  }
368  return rz_buf_write_at(f->buf, off + f->start, buf, count);
369 }
lzma_index ** i
Definition: index.h:629
RZ_API RzList * ar_open_all(const char *arname, int perm)
Open specific file withen a ar/lib file.
Definition: ar.c:218
struct Filetable filetable
#define AR_MAGIC_HEADER
Definition: ar.c:6
bool ar_check_magic(RzBuffer *b)
Definition: ar.c:28
RZ_API int ar_write_at(RzArFp *f, ut64 off, void *buf, int count)
Definition: ar.c:360
static char * name_from_table(ut64 off, filetable *tbl)
Definition: ar.c:45
RZ_API int ar_read_at(RzArFp *f, ut64 off, void *buf, int count)
Definition: ar.c:349
RZ_API RzArFp * ar_open_file(const char *arname, int perm, const char *filename)
Open specific file withen a ar/lib file.
Definition: ar.c:286
#define AR_FILE_HEADER_END
Definition: ar.c:7
static void arf_clean_name(RzArFp *arf)
Definition: ar.c:40
#define VERIFY_AR_NUM_FIELD(x, s)
Definition: ar.c:67
static int ar_parse_header(RzArFp *arf, filetable *tbl, ut64 arsize)
Definition: ar.c:76
static RzArFp * arfp_new(RzBuffer *b, bool shared_buf)
Definition: ar.c:15
RZ_API void ar_close(RzArFp *f)
Definition: ar.c:338
#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
checking print the parsed form of the magic use in n conjunction with m to debug a new magic file n before installing it n output MIME type special files
Definition: file_opts.h:46
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
voidpf void uLong size
Definition: ioapi.h:138
const char * filename
Definition: ioapi.h:137
const char int mode
Definition: ioapi.h:137
voidpf void * buf
Definition: ioapi.h:138
uint8_t ut8
Definition: lh5801.h:11
static void list(RzEgg *egg)
Definition: rz-gg.c:52
RZ_API RZ_OWN RzList * rz_list_newf(RzListFree f)
Returns a new initialized RzList pointer and sets the free method.
Definition: list.c:248
RZ_API RZ_OWN void * rz_list_pop(RZ_NONNULL RzList *list)
Removes and returns the last element of the list.
Definition: list.c:376
RZ_API ut32 rz_list_length(RZ_NONNULL const RzList *list)
Returns the length of the list.
Definition: list.c:109
RZ_API RZ_OWN void * rz_list_pop_head(RZ_NONNULL RzList *list)
Removes and returns the first element of the list.
Definition: list.c:401
RZ_API RZ_BORROW RzListIter * rz_list_append(RZ_NONNULL RzList *list, void *data)
Appends at the end of the list a new element.
Definition: list.c:288
RZ_API void rz_list_free(RZ_NONNULL RzList *list)
Empties the list and frees the list pointer.
Definition: list.c:137
void * malloc(size_t size)
Definition: malloc.c:123
#define header(is_bt, len_min, ret_op)
int off
Definition: pal.c:13
#define rz_return_val_if_fail(expr, val)
Definition: rz_assert.h:108
RZ_API ut64 rz_buf_tell(RZ_NONNULL RzBuffer *b)
Return the current cursor position.
Definition: buf.c:1238
RZ_API st64 rz_buf_seek(RZ_NONNULL RzBuffer *b, st64 addr, int whence)
Modify the current cursor position in the buffer.
Definition: buf.c:1166
#define RZ_BUF_CUR
Definition: rz_buf.h:15
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_API void rz_buf_free(RzBuffer *b)
Free all internal data hold by the buffer and the buffer.
Definition: buf.c:1253
RZ_API st64 rz_buf_read(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_OUT ut8 *buf, ut64 len)
RZ_API ut64 rz_buf_size(RZ_NONNULL RzBuffer *b)
Return the size of the buffer.
Definition: buf.c:1225
RZ_API RZ_OWN RzBuffer * rz_buf_new_file(const char *file, int perm, int mode)
Creates a new buffer from a file.
Definition: buf.c:317
void(* RzListFree)(void *ptr)
Definition: rz_list.h:11
#define RZ_LOG_ERROR(fmtstr,...)
Definition: rz_log.h:58
RZ_API char RZ_API char * rz_str_newlen(const char *str, int len)
Definition: str.c:871
RZ_API RZ_BORROW char * rz_str_trim_tail(RZ_NONNULL char *str)
Removes whitespace characters (space, tab, newline etc.) from the end of a string and replaces them w...
Definition: str_trim.c:125
RZ_API RzList * rz_str_split_duplist(const char *str, const char *c, bool trim)
Split the string str according to the substring c and returns a RzList with the result.
Definition: str.c:3464
RZ_API bool rz_str_isnumber(const char *str)
Definition: str.c:3550
#define rz_sys_perror(x)
Definition: rz_types.h:336
#define RZ_NEW(x)
Definition: rz_types.h:285
#define PFMT64x
Definition: rz_types.h:393
#define b(i)
Definition: sha256.c:42
#define f(i)
Definition: sha256.c:46
#define c(i)
Definition: sha256.c:43
#define h(i)
Definition: sha256.c:48
Definition: ar.c:9
ut64 offset
Definition: ar.c:12
char * data
Definition: ar.c:10
ut64 size
Definition: ar.c:11
Definition: ar.h:7
ut64 start
Definition: ar.h:9
char * name
Definition: ar.h:8
ut64 end
Definition: ar.h:10
RzBuffer * buf
Definition: ar.h:11
Definition: z80asm.h:102
ut64(WINAPI *w32_GetEnabledXStateFeatures)()