Rizin
unix-like reverse engineering framework and cli tools
axml.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2021 keegan
2 // SPDX-License-Identifier: LGPL-3.0-only
3 
4 #include <string.h>
5 #include <rz_types.h>
6 #include <rz_core.h>
7 
8 #include "axml_resources.h"
9 
10 enum {
11  TYPE_STRING_POOL = 0x0001,
12  TYPE_XML = 0x0003,
18 };
19 
20 enum {
21  RESOURCE_NULL = 0x00,
27  RESOURCE_BOOL = 0x12,
28 };
29 
30 enum {
31  FLAG_UTF8 = 1 << 8,
32 };
33 
34 // Beginning of every header
36  typedef struct {
37  ut16 type;
38  ut16 header_size;
39  ut32 size;
40  })
41 chunk_header_t;
42 
43 // String pool referenced throughout the Binary XML, there must only be ONE
44 RZ_PACKED(
45  typedef struct {
46  ut32 string_count;
47  ut32 style_count;
48  ut32 flags;
49  ut32 strings_offset;
50  ut32 styles_offset;
51  ut32 offsets[];
52  })
54 
56  typedef struct {
57  ut16 size;
58  ut8 unused;
59  ut8 type;
60  union {
61  ut32 d;
62  float f;
63  } data;
64  })
65 resource_value_t;
66 
67 RZ_PACKED(
68  typedef struct {
69  ut32 namespace;
70  ut32 name;
71  ut32 unused;
72  resource_value_t value;
73  })
75 
77  typedef struct {
78  ut32 line;
79  ut32 comment;
80  ut32 namespace;
81  ut32 name;
82  ut32 flags;
83  ut16 attribute_count;
84  ut16 unused0;
85  ut16 unused1;
86  ut16 unused2;
87  attribute_t attributes[];
88  })
89 start_element_t;
90 
91 RZ_PACKED(
92  typedef struct {
93  ut32 line;
94  ut32 comment;
95  ut32 namespace;
96  ut32 name;
97  })
99 
101  typedef struct {
102  ut32 line;
103  ut32 comment;
104  ut32 prefix;
105  ut32 uri;
106  })
107 namespace_t;
108 
109 static char *string_lookup(string_pool_t *pool, const ut8 *data, ut64 data_size, ut32 i, size_t *length) {
110  if (i > rz_read_le32(&pool->string_count)) {
111  return NULL;
112  }
113 
114  ut32 offset = rz_read_le32(&pool->offsets[i]);
115  ut8 *start = (ut8 *)((uintptr_t)data + rz_read_le32(&pool->strings_offset) + 8 + offset);
116 
117  char *name = NULL;
118  if (pool->flags & FLAG_UTF8) {
119  if ((ut64)start > (ut64)data + data_size - sizeof(ut16)) {
120  return NULL;
121  }
122 
123  // Ignore UTF-16LE encoded length
124  ut32 n = *start++;
125  if (n & 0x80) {
126  n = ((n & 0x7f) << 8) | *start++;
127  }
128 
129  if ((ut64)start > (ut64)data + data_size - sizeof(ut16)) {
130  return NULL;
131  }
132 
133  // UTF-8 encoded length
134  n = *start++;
135  if (n & 0x80) {
136  n = ((n & 0x7f) << 8) | *start++;
137  }
138 
139  name = calloc(n + 1, 1);
140 
141  if (n == 0) {
142  if (length) {
143  *length = 0;
144  }
145 
146  return name;
147  }
148 
149  if ((ut64)start > (ut64)data + data_size - sizeof(ut32) - n - 1) {
150  free(name);
151  return NULL;
152  }
153 
154  memcpy(name, start, n);
155 
156  if (length) {
157  *length = n;
158  }
159  } else {
160  if ((ut64)start > (ut64)data + data_size - sizeof(ut32)) {
161  return NULL;
162  }
163 
164  ut16 *start16 = (ut16 *)start;
165 
166  // If >0x7fff, stored as a big-endian ut32
167  ut32 n = rz_read_le16(start16++);
168  if (n & 0x8000) {
169  n |= ((n & 0x7fff) << 16) | rz_read_le16(start16++);
170  }
171 
172  // Size of UTF-16LE without NULL
173  n *= 2;
174 
175  name = calloc(n * 2 + 1, 1);
176 
177  if ((ut64)start16 > (ut64)data + data_size - sizeof(ut32) - n - 1) {
178  free(name);
179  return NULL;
180  }
181 
182  // If UTF-16LE, decode to UTF-8 so we can print it to the screen
183  if (rz_str_utf16_to_utf8((ut8 *)name, n * 2, (const ut8 *)start16, n, true) < 0) {
184  free(name);
185  RZ_LOG_ERROR("Failed to decode UTF16-LE\n");
186  return NULL;
187  }
188 
189  if (length) {
190  *length = n;
191  }
192  }
193 
194  return name;
195 }
196 
197 static char *resource_value(string_pool_t *pool, const ut8 *data, ut64 data_size,
198  resource_value_t *value) {
199  switch (value->type) {
200  case RESOURCE_NULL:
201  return rz_str_new("");
202  case RESOURCE_REFERENCE:
203  return rz_str_newf("@0x%x", value->data.d);
204  case RESOURCE_STRING:
205  return string_lookup(pool, data, data_size, rz_read_le32(&value->data.d), NULL);
206  case RESOURCE_FLOAT:
207  return rz_str_newf("%f", value->data.f);
208  case RESOURCE_INT_DEC:
209  return rz_str_newf("%d", value->data.d);
210  case RESOURCE_INT_HEX:
211  return rz_str_newf("0x%x", value->data.d);
212  case RESOURCE_BOOL:
213  return rz_str_newf(value->data.d ? "true" : "false");
214  default:
215  RZ_LOG_WARN("Resource type is not recognized: %#x\n", value->type);
216  break;
217  }
218  return rz_str_new("null");
219 }
220 
221 static bool dump_element(RzStrBuf *sb, string_pool_t *pool, namespace_t *namespace,
222  const ut8 *data, ut64 data_size, start_element_t *element,
223  const ut32 *resource_map, ut32 resource_map_length, st32 *depth, bool start) {
224  ut32 i;
225 
226  char *name = string_lookup(pool, data, data_size, rz_read_le32(&element->name), NULL);
227  for (i = 0; i < *depth; i++) {
228  rz_strbuf_appendf(sb, "\t");
229  }
230 
231  if (start) {
232  rz_strbuf_appendf(sb, "<%s", name);
233  ut16 count = rz_read_le16(&element->attribute_count);
234  if (*depth == 0 && namespace) {
235  char *key = string_lookup(pool, data, data_size, namespace->prefix, NULL);
236  char *value = string_lookup(pool, data, data_size, namespace->uri, NULL);
237  rz_strbuf_appendf(sb, " xmlns:%s=\"%s\"", key, value);
238  free(key);
239  free(value);
240  }
241 
242  if (count != 0) {
243  rz_strbuf_appendf(sb, " ");
244  }
245 
246  for (i = 0; i < count; i++) {
247  attribute_t a = element->attributes[i];
248  ut32 key_index = rz_read_le32(&a.name);
249  const char *key = (const char *)string_lookup(pool, data, data_size, key_index, NULL);
250  bool resource_key = false;
251  // If the key is empty, it is a cached resource name
252  if (key && *key == '\0') {
253  free((char *)key);
254  resource_key = true;
255  if (resource_map && key_index < resource_map_length) {
256  ut32 resource = rz_read_le32(&resource_map[key_index]);
257  if (resource >= 0x1010000) {
258  resource -= 0x1010000;
259  if (resource < ANDROID_ATTRIBUTE_NAMES_SIZE) {
260  key = ANDROID_ATTRIBUTE_NAMES[resource];
261  }
262  } else {
263  key = "null";
264  }
265  } else {
266  key = "null";
267  }
268  }
269  char *value = resource_value(pool, data, data_size, &a.value);
270  // If there is a namespace on the value, and there is an active
271  // namespace, assume it is the same
272  if (rz_read_le32(&a.namespace) != -1 && namespace && namespace->prefix != -1) {
273  char *ns = string_lookup(pool, data, data_size, namespace->prefix, NULL);
274  rz_strbuf_appendf(sb, "%s:%s=\"%s\"", ns, key, value);
275  free(ns);
276  } else {
277  rz_strbuf_appendf(sb, "%s=\"%s\"", key, value);
278  }
279  if (i != count - 1) {
280  rz_strbuf_appendf(sb, " ");
281  }
282  if (!resource_key) {
283  free((char *)key);
284  }
285  free(value);
286  }
287  } else {
288  rz_strbuf_appendf(sb, "</%s", name);
289  }
290 
291  rz_strbuf_appendf(sb, ">\n");
292  free(name);
293  return true;
294 }
295 
303 RZ_API RZ_OWN char *rz_axml_decode(RZ_NONNULL const ut8 *data, const ut64 data_size) {
304  string_pool_t *pool = NULL;
305  namespace_t *namespace = NULL;
306  const ut32 *resource_map = NULL;
307  ut32 resource_map_length = 0;
308  RzStrBuf *sb = NULL;
309  st32 depth = 0;
310 
312  if (data_size == 0) {
313  return strdup("");
314  }
315 
316  RzBuffer *buffer = rz_buf_new_with_pointers(data, data_size, false);
317  if (!buffer) {
318  RZ_LOG_ERROR("Error allocating RzBuffer\n");
319  goto error;
320  }
321 
322  ut64 offset = 0;
323 
324  chunk_header_t header = { 0 };
325  if (rz_buf_fread_at(buffer, offset, (ut8 *)&header, "ssi", 1) != sizeof(header)) {
326  goto bad;
327  }
328 
329  if (header.type != TYPE_XML) {
330  goto bad;
331  }
332 
333  ut64 binary_size = header.size;
334  if (binary_size > data_size) {
335  goto bad;
336  }
337 
338  offset += sizeof(header);
339 
340  sb = rz_strbuf_new("");
341 
342  while (offset < binary_size) {
343  if (rz_buf_fread_at(buffer, offset, (ut8 *)&header, "ssi", 1) != sizeof(header)) {
344  goto bad;
345  }
346 
347  ut16 type = header.type;
348 
349  // After reading the original chunk header, read the type-specific
350  // header
351  offset += sizeof(header);
352 
353  switch (type) {
354  case TYPE_STRING_POOL: {
355  ut16 header_size = header.size;
356  if (header_size == 0 || header_size > data_size) {
357  goto bad;
358  }
359 
360  pool = malloc(header_size);
361  if (!pool) {
362  goto bad;
363  }
364 
365  if (rz_buf_read_at(buffer, offset, (void *)pool, header_size) != header_size) {
366  goto bad;
367  }
368  break;
369  }
370  case TYPE_START_ELEMENT: {
371  // The string pool must be the first header
372  if (!pool) {
373  goto bad;
374  }
375 
376  ut16 header_size = header.size;
377  if (header_size == 0 || header_size > data_size) {
378  goto bad;
379  }
380 
381  start_element_t *element = malloc(header_size);
382  if (!element) {
383  goto bad;
384  }
385 
386  if (rz_buf_read_at(buffer, offset, (void *)element, header_size) != header_size) {
387  free(element);
388  goto bad;
389  }
390 
391  if (!dump_element(sb, pool, namespace, data, data_size, element,
392  resource_map, resource_map_length, &depth, true)) {
393  free(element);
394  goto bad;
395  }
396 
397  depth++;
398 
399  free(element);
400  break;
401  }
402  case TYPE_END_ELEMENT: {
403  depth--;
404  if (depth < 0) {
405  goto bad;
406  }
407 
409  if (rz_buf_read_at(buffer, offset, (void *)&end, sizeof(end)) != sizeof(end)) {
410  goto bad;
411  }
412 
413  // The beginning of the start and end element structs
414  // are the same, so we can use this interchangably
415  if (!dump_element(sb, pool, namespace, data, data_size, (start_element_t *)&end,
416  resource_map, resource_map_length, &depth, false)) {
417  goto bad;
418  }
419  break;
420  }
422  // If there is already a start namespace, override it
423  free(namespace);
424  namespace = malloc(sizeof(*namespace));
425  if (rz_buf_fread_at(buffer, offset, (ut8 *)namespace, "iiii", 1) != sizeof(*namespace)) {
426  goto bad;
427  }
428  break;
429  case TYPE_END_NAMESPACE:
430  break;
431  case TYPE_RESOURCE_MAP:
432  resource_map = (ut32 *)(data + offset);
433  resource_map_length = header.size;
434  if (resource_map_length > data_size - offset) {
435  goto bad;
436  }
437  resource_map_length /= sizeof(ut32);
438  break;
439  default:
440  RZ_LOG_WARN("Type is not recognized: %#x\n", type);
441  }
442 
443  offset += header.size - sizeof(header);
444  }
445 
447  free(pool);
448  free(namespace);
449  return rz_strbuf_drain(sb);
450 bad:
451  RZ_LOG_ERROR("Invalid Android Binary XML\n");
452 error:
453  if (buffer)
455  free(pool);
457  return NULL;
458 }
lzma_index ** i
Definition: index.h:629
attribute_t
Definition: axml.c:74
@ RESOURCE_NULL
Definition: axml.c:21
@ RESOURCE_REFERENCE
Definition: axml.c:22
@ RESOURCE_INT_DEC
Definition: axml.c:25
@ RESOURCE_FLOAT
Definition: axml.c:24
@ RESOURCE_BOOL
Definition: axml.c:27
@ RESOURCE_INT_HEX
Definition: axml.c:26
@ RESOURCE_STRING
Definition: axml.c:23
string_pool_t
Definition: axml.c:53
@ FLAG_UTF8
Definition: axml.c:31
static char * resource_value(string_pool_t *pool, const ut8 *data, ut64 data_size, resource_value_t *value)
Definition: axml.c:197
end_element_t
Definition: axml.c:98
static bool dump_element(RzStrBuf *sb, string_pool_t *pool, namespace_t *namespace, const ut8 *data, ut64 data_size, start_element_t *element, const ut32 *resource_map, ut32 resource_map_length, st32 *depth, bool start)
Definition: axml.c:221
RZ_PACKED(typedef struct { ut16 type;ut16 header_size;ut32 size;})
Definition: axml.c:35
RZ_API RZ_OWN char * rz_axml_decode(RZ_NONNULL const ut8 *data, const ut64 data_size)
Decode a buffer with Android XML to regular XML string representation.
Definition: axml.c:303
@ TYPE_END_ELEMENT
Definition: axml.c:16
@ TYPE_STRING_POOL
Definition: axml.c:11
@ TYPE_START_ELEMENT
Definition: axml.c:15
@ TYPE_XML
Definition: axml.c:12
@ TYPE_RESOURCE_MAP
Definition: axml.c:17
@ TYPE_START_NAMESPACE
Definition: axml.c:13
@ TYPE_END_NAMESPACE
Definition: axml.c:14
size_t ANDROID_ATTRIBUTE_NAMES_SIZE
const char * ANDROID_ATTRIBUTE_NAMES[]
static SblHeader sb
Definition: bin_mbn.c:26
static int value
Definition: cmd_api.c:93
#define RZ_API
#define NULL
Definition: cris-opc.c:27
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
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 static offset struct stat static buf void long static basep static whence static length const void static len static semflg const void static shmflg const struct timespec struct timespec static rem const char static group const void start
Definition: sflib.h:133
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 static offset struct stat static buf void long static basep static whence static length const void static len key
Definition: sflib.h:118
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 static offset struct stat static buf void long static basep static whence static length const void static len static semflg const void static shmflg const struct timespec struct timespec static rem const char static group const void length
Definition: sflib.h:133
uint16_t ut16
uint32_t ut32
unsigned short prefix[65536]
Definition: gun.c:163
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
uint8_t ut8
Definition: lh5801.h:11
memcpy(mem, inblock.get(), min(CONTAINING_RECORD(inblock.get(), MEMBLOCK, data) ->size, size))
void * malloc(size_t size)
Definition: malloc.c:123
void * calloc(size_t number, size_t size)
Definition: malloc.c:102
return strdup("=SP r13\n" "=LR r14\n" "=PC r15\n" "=A0 r0\n" "=A1 r1\n" "=A2 r2\n" "=A3 r3\n" "=ZF zf\n" "=SF nf\n" "=OF vf\n" "=CF cf\n" "=SN or0\n" "gpr lr .32 56 0\n" "gpr pc .32 60 0\n" "gpr cpsr .32 64 0 ____tfiae_________________qvczn\n" "gpr or0 .32 68 0\n" "gpr tf .1 64.5 0 thumb\n" "gpr ef .1 64.9 0 endian\n" "gpr jf .1 64.24 0 java\n" "gpr qf .1 64.27 0 sticky_overflow\n" "gpr vf .1 64.28 0 overflow\n" "gpr cf .1 64.29 0 carry\n" "gpr zf .1 64.30 0 zero\n" "gpr nf .1 64.31 0 negative\n" "gpr itc .4 64.10 0 if_then_count\n" "gpr gef .4 64.16 0 great_or_equal\n" "gpr r0 .32 0 0\n" "gpr r1 .32 4 0\n" "gpr r2 .32 8 0\n" "gpr r3 .32 12 0\n" "gpr r4 .32 16 0\n" "gpr r5 .32 20 0\n" "gpr r6 .32 24 0\n" "gpr r7 .32 28 0\n" "gpr r8 .32 32 0\n" "gpr r9 .32 36 0\n" "gpr r10 .32 40 0\n" "gpr r11 .32 44 0\n" "gpr r12 .32 48 0\n" "gpr r13 .32 52 0\n" "gpr r14 .32 56 0\n" "gpr r15 .32 60 0\n" "gpr r16 .32 64 0\n" "gpr r17 .32 68 0\n")
#define header(is_bt, len_min, ret_op)
int n
Definition: mipsasm.c:19
int type
Definition: mipsasm.c:17
line
Definition: setup.py:34
const char * name
Definition: op.c:541
#define rz_return_val_if_fail(expr, val)
Definition: rz_assert.h:108
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 RZ_OWN RzBuffer * rz_buf_new_with_pointers(const ut8 *bytes, ut64 len, bool steal)
Creates a new buffer with a bytes array.
Definition: buf.c:552
RZ_API st64 rz_buf_fread_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL ut8 *buf, RZ_NONNULL const char *fmt, int n)
...
Definition: buf.c:1001
RZ_API void rz_buf_free(RzBuffer *b)
Free all internal data hold by the buffer and the buffer.
Definition: buf.c:1253
static ut16 rz_read_le16(const void *src)
Definition: rz_endian.h:206
static ut32 rz_read_le32(const void *src)
Definition: rz_endian.h:239
#define RZ_LOG_WARN(fmtstr,...)
Definition: rz_log.h:56
#define RZ_LOG_ERROR(fmtstr,...)
Definition: rz_log.h:58
RZ_API int rz_str_utf16_to_utf8(ut8 *dst, int len_dst, const ut8 *src, int len_src, bool little_endian)
Definition: str.c:2884
RZ_API char * rz_str_newf(const char *fmt,...) RZ_PRINTF_CHECK(1
RZ_API char * rz_str_new(const char *str)
Definition: str.c:865
RZ_API RZ_OWN char * rz_strbuf_drain(RzStrBuf *sb)
Definition: strbuf.c:342
RZ_API RzStrBuf * rz_strbuf_new(const char *s)
Definition: strbuf.c:8
RZ_API void rz_strbuf_free(RzStrBuf *sb)
Definition: strbuf.c:358
RZ_API bool rz_strbuf_appendf(RzStrBuf *sb, const char *fmt,...) RZ_PRINTF_CHECK(2
#define RZ_OWN
Definition: rz_types.h:62
#define RZ_NONNULL
Definition: rz_types.h:64
#define st32
Definition: rz_types_base.h:12
static struct sockaddr static addrlen static backlog const void static flags void flags
Definition: sfsocketcall.h:123
#define d(i)
Definition: sha256.c:44
#define f(i)
Definition: sha256.c:46
#define a(i)
Definition: sha256.c:41
_W64 unsigned int uintptr_t
Definition: buffer.h:15
Definition: z80asm.h:102
void error(const char *msg)
Definition: untgz.c:593
ut64(WINAPI *w32_GetEnabledXStateFeatures)()