2043 lines
59 KiB
C
2043 lines
59 KiB
C
// SPDX-FileCopyrightText: 2021-2023 RizinOrg <info@rizin.re>
|
|
// SPDX-FileCopyrightText: 2021-2023 deroad <wargio@libero.it>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include "dex.h"
|
|
#include <rz_util.h>
|
|
|
|
#define DEX_INVALID_CLASS "Lunknown_class;"
|
|
#define DEX_INVALID_METHOD "unknown_method"
|
|
|
|
#define startswith(a, b) (!strncmp(a, b, strlen(b)))
|
|
|
|
typedef struct dex_access_flags_readable_t {
|
|
ut32 flag;
|
|
const char *readable;
|
|
} DexAccessFlagsReadable;
|
|
|
|
#define read_le_bits_or_fail(bits, buf, val, fail) \
|
|
do { \
|
|
if (!rz_buf_read_le##bits(buf, &val)) { \
|
|
goto fail; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define read_le_at_bits_or_fail(bits, buf, val, offset, fail) \
|
|
do { \
|
|
if (!rz_buf_read_le##bits##_at(buf, offset, &val)) { \
|
|
goto fail; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define read_le32_or_fail(buf, val, fail) read_le_bits_or_fail(32, buf, val, fail)
|
|
#define read_le16_or_fail(buf, val, fail) read_le_bits_or_fail(16, buf, val, fail)
|
|
#define read_le32_at_or_fail(buf, val, offset, fail) read_le_at_bits_or_fail(32, buf, val, offset, fail)
|
|
#define read_le16_at_or_fail(buf, val, offset, fail) read_le_at_bits_or_fail(16, buf, val, offset, fail)
|
|
|
|
#define dex_is_static(a) (a & ACCESS_FLAG_STATIC)
|
|
#define dex_is_varargs(a) (a & ACCESS_FLAG_VARARGS)
|
|
|
|
#define dex_fail_if_bad_ids(name, z, s, g) \
|
|
do { \
|
|
ut64 end = name##_offset; \
|
|
end += name##_size * (z); \
|
|
if (end >= (s)) { \
|
|
goto g; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define CLASS_ACCESS_FLAGS_SIZE 18
|
|
static const DexAccessFlagsReadable access_flags_list[CLASS_ACCESS_FLAGS_SIZE] = {
|
|
{ ACCESS_FLAG_PUBLIC /* */, "public" },
|
|
{ ACCESS_FLAG_PRIVATE /* */, "private" },
|
|
{ ACCESS_FLAG_PROTECTED /* */, "protected" },
|
|
{ ACCESS_FLAG_STATIC /* */, "static" },
|
|
{ ACCESS_FLAG_FINAL /* */, "final" },
|
|
{ ACCESS_FLAG_SYNCHRONIZED /* */, "synchronized" },
|
|
{ ACCESS_FLAG_BRIDGE /* */, "bridge" },
|
|
{ ACCESS_FLAG_VARARGS /* */, "varargs" },
|
|
{ ACCESS_FLAG_NATIVE /* */, "native" },
|
|
{ ACCESS_FLAG_INTERFACE /* */, "interface" },
|
|
{ ACCESS_FLAG_ABSTRACT /* */, "abstract" },
|
|
{ ACCESS_FLAG_STRICT /* */, "strict" },
|
|
{ ACCESS_FLAG_SYNTHETIC /* */, "synthetic" },
|
|
{ ACCESS_FLAG_ANNOTATION /* */, "annotation" },
|
|
{ ACCESS_FLAG_ENUM /* */, "enum" },
|
|
{ ACCESS_FLAG_MODULE /* */, "module" },
|
|
{ ACCESS_FLAG_CONSTRUCTOR /* */, "constructor" },
|
|
{ ACCESS_FLAG_DECLARED_SYNCHRONIZED /**/, "synchronized" },
|
|
};
|
|
|
|
static void dex_string_free(DexString *string) {
|
|
if (!string) {
|
|
return;
|
|
}
|
|
free(string->data);
|
|
free(string);
|
|
}
|
|
|
|
static DexString *dex_string_new(RzBuffer *buf, ut64 offset, st64 *pread) {
|
|
ut64 size = 0;
|
|
char *data = NULL;
|
|
st64 read;
|
|
DexString *string = NULL;
|
|
|
|
read = rz_buf_uleb128(buf, &size);
|
|
if (UT64_ADD_OVFCHK(size, 1)) {
|
|
return NULL;
|
|
}
|
|
|
|
data = malloc(size + 1);
|
|
if (!data || rz_buf_read(buf, (ut8 *)data, size) != size) {
|
|
free(data);
|
|
return NULL;
|
|
}
|
|
data[size] = 0;
|
|
|
|
string = RZ_NEW0(DexString);
|
|
if (!string) {
|
|
free(data);
|
|
return NULL;
|
|
}
|
|
|
|
*pread = read;
|
|
string->size = size;
|
|
string->offset = offset;
|
|
string->data = data;
|
|
return string;
|
|
}
|
|
|
|
static void dex_proto_id_free(DexProtoId *string) {
|
|
if (!string) {
|
|
return;
|
|
}
|
|
free(string->type_list);
|
|
free(string);
|
|
}
|
|
|
|
static DexProtoId *dex_proto_id_new(RzBuffer *buf, ut64 offset) {
|
|
DexProtoId *proto_id = RZ_NEW0(DexProtoId);
|
|
if (!proto_id) {
|
|
return NULL;
|
|
}
|
|
|
|
ut32 parameters_offset = 0;
|
|
proto_id->offset = offset;
|
|
read_le32_or_fail(buf, proto_id->shorty_idx, dex_proto_id_new_fail);
|
|
read_le32_or_fail(buf, proto_id->return_type_idx, dex_proto_id_new_fail);
|
|
|
|
read_le32_or_fail(buf, parameters_offset, dex_proto_id_new_fail);
|
|
if (parameters_offset > 0) {
|
|
ut32 count = 0;
|
|
read_le32_at_or_fail(buf, count, parameters_offset, dex_proto_id_new_fail);
|
|
|
|
proto_id->type_list_size = count;
|
|
proto_id->type_list = RZ_NEWS(ut16, count);
|
|
if (!proto_id->type_list) {
|
|
goto dex_proto_id_new_fail;
|
|
}
|
|
|
|
parameters_offset += sizeof(ut32);
|
|
for (ut32 i = 0; i < count; ++i, parameters_offset += sizeof(ut16)) {
|
|
read_le16_at_or_fail(buf, proto_id->type_list[i], parameters_offset, dex_proto_id_new_fail);
|
|
}
|
|
}
|
|
|
|
return proto_id;
|
|
|
|
dex_proto_id_new_fail:
|
|
free(proto_id);
|
|
return NULL;
|
|
}
|
|
|
|
#define dex_field_id_free free
|
|
static DexFieldId *dex_field_id_new(RzBuffer *buf, ut64 offset) {
|
|
DexFieldId *field_id = RZ_NEW0(DexFieldId);
|
|
if (!field_id) {
|
|
return NULL;
|
|
}
|
|
|
|
read_le16_or_fail(buf, field_id->class_idx, dex_field_id_new_fail);
|
|
read_le16_or_fail(buf, field_id->type_idx, dex_field_id_new_fail);
|
|
read_le32_or_fail(buf, field_id->name_idx, dex_field_id_new_fail);
|
|
field_id->offset = offset;
|
|
return field_id;
|
|
|
|
dex_field_id_new_fail:
|
|
free(field_id);
|
|
return NULL;
|
|
}
|
|
|
|
#define dex_method_id_free free
|
|
static DexMethodId *dex_method_id_new(RzBuffer *buf, ut64 offset) {
|
|
DexMethodId *method_id = RZ_NEW0(DexMethodId);
|
|
if (!method_id) {
|
|
return NULL;
|
|
}
|
|
|
|
read_le16_or_fail(buf, method_id->class_idx, dex_method_id_new_fail);
|
|
read_le16_or_fail(buf, method_id->proto_idx, dex_method_id_new_fail);
|
|
read_le32_or_fail(buf, method_id->name_idx, dex_method_id_new_fail);
|
|
method_id->offset = offset;
|
|
return method_id;
|
|
|
|
dex_method_id_new_fail:
|
|
free(method_id);
|
|
return NULL;
|
|
}
|
|
|
|
static void dex_class_def_free(DexClassDef *class_def) {
|
|
if (!class_def) {
|
|
return;
|
|
}
|
|
rz_list_free(class_def->static_fields);
|
|
rz_list_free(class_def->instance_fields);
|
|
rz_list_free(class_def->direct_methods);
|
|
rz_list_free(class_def->virtual_methods);
|
|
free(class_def->interfaces);
|
|
free(class_def);
|
|
}
|
|
|
|
static DexEncodedField *dex_new_encoded_field(RzBuffer *buf, ut64 base, ut64 *diff_value_prev, bool first) {
|
|
DexEncodedField *encoded_field = RZ_NEW0(DexEncodedField);
|
|
if (!encoded_field) {
|
|
return NULL;
|
|
}
|
|
ut64 diff_value = 0;
|
|
|
|
encoded_field->offset = rz_buf_tell(buf) + base;
|
|
rz_buf_uleb128(buf, &diff_value);
|
|
rz_buf_uleb128(buf, &encoded_field->access_flags);
|
|
|
|
if (first) {
|
|
encoded_field->field_idx = diff_value;
|
|
*diff_value_prev = diff_value;
|
|
} else {
|
|
encoded_field->field_idx = *diff_value_prev + diff_value;
|
|
*diff_value_prev = encoded_field->field_idx;
|
|
}
|
|
return encoded_field;
|
|
}
|
|
|
|
static DexEncodedMethod *dex_new_encoded_method(RzBuffer *buf, ut64 base, ut64 *diff_value_prev, bool first, RzPVector /*<DexMethodId *>*/ *method_ids) {
|
|
DexEncodedMethod *encoded_method = RZ_NEW0(DexEncodedMethod);
|
|
if (!encoded_method) {
|
|
return NULL;
|
|
}
|
|
ut64 diff_value = 0;
|
|
ut64 code_offset = 0;
|
|
|
|
encoded_method->offset = rz_buf_tell(buf) + base;
|
|
rz_buf_uleb128(buf, &diff_value);
|
|
rz_buf_uleb128(buf, &encoded_method->access_flags);
|
|
rz_buf_uleb128(buf, &code_offset);
|
|
|
|
if (first) {
|
|
encoded_method->method_idx = diff_value;
|
|
*diff_value_prev = diff_value;
|
|
} else {
|
|
encoded_method->method_idx = *diff_value_prev + diff_value;
|
|
*diff_value_prev = encoded_method->method_idx;
|
|
}
|
|
|
|
if (code_offset > 0) {
|
|
read_le16_at_or_fail(buf, encoded_method->registers_size, code_offset, dex_new_encoded_method_fail);
|
|
read_le16_at_or_fail(buf, encoded_method->ins_size, code_offset + 2, dex_new_encoded_method_fail);
|
|
read_le16_at_or_fail(buf, encoded_method->outs_size, code_offset + 4, dex_new_encoded_method_fail);
|
|
read_le16_at_or_fail(buf, encoded_method->tries_size, code_offset + 6, dex_new_encoded_method_fail);
|
|
read_le32_at_or_fail(buf, encoded_method->debug_info_offset, code_offset + 8, dex_new_encoded_method_fail);
|
|
read_le32_at_or_fail(buf, encoded_method->code_size, code_offset + 12, dex_new_encoded_method_fail);
|
|
encoded_method->code_size *= sizeof(ut16); // code ushort[insns_size]
|
|
encoded_method->code_offset = code_offset + 16 + base;
|
|
|
|
if (encoded_method->method_idx < rz_pvector_len(method_ids)) {
|
|
DexMethodId *method_id = (DexMethodId *)rz_pvector_at(method_ids, encoded_method->method_idx);
|
|
if (method_id) {
|
|
method_id->code_offset = encoded_method->code_offset;
|
|
method_id->code_size = encoded_method->code_size;
|
|
}
|
|
}
|
|
}
|
|
return encoded_method;
|
|
|
|
dex_new_encoded_method_fail:
|
|
free(encoded_method);
|
|
return NULL;
|
|
}
|
|
|
|
static DexClassDef *dex_class_def_new(RzBuffer *buf, ut64 offset, ut64 base, RzPVector /*<DexMethodId *>*/ *method_ids) {
|
|
DexClassDef *class_def = RZ_NEW0(DexClassDef);
|
|
if (!class_def) {
|
|
return NULL;
|
|
}
|
|
|
|
ut64 static_fields_size = 0;
|
|
ut64 instance_fields_size = 0;
|
|
ut64 direct_methods_size = 0;
|
|
ut64 virtual_methods_size = 0;
|
|
ut64 diff_value_prev;
|
|
|
|
class_def->static_fields = /* */ rz_list_newf((RzListFree)free);
|
|
class_def->instance_fields = /**/ rz_list_newf((RzListFree)free);
|
|
class_def->direct_methods = /* */ rz_list_newf((RzListFree)free);
|
|
class_def->virtual_methods = /**/ rz_list_newf((RzListFree)free);
|
|
|
|
read_le16_or_fail(buf, class_def->class_idx, dex_class_def_new_fail);
|
|
read_le16_or_fail(buf, class_def->_padding1, dex_class_def_new_fail);
|
|
read_le32_or_fail(buf, class_def->access_flags, dex_class_def_new_fail);
|
|
read_le16_or_fail(buf, class_def->superclass_idx, dex_class_def_new_fail);
|
|
read_le16_or_fail(buf, class_def->_padding2, dex_class_def_new_fail);
|
|
read_le32_or_fail(buf, class_def->interfaces_offset, dex_class_def_new_fail);
|
|
read_le32_or_fail(buf, class_def->source_file_idx, dex_class_def_new_fail);
|
|
read_le32_or_fail(buf, class_def->annotations_offset, dex_class_def_new_fail);
|
|
read_le32_or_fail(buf, class_def->class_data_offset, dex_class_def_new_fail);
|
|
read_le32_or_fail(buf, class_def->static_values_offset, dex_class_def_new_fail);
|
|
class_def->offset = offset;
|
|
|
|
if (class_def->interfaces_offset > 0) {
|
|
if (rz_buf_seek(buf, class_def->interfaces_offset, RZ_BUF_SET) < 0) {
|
|
goto dex_class_def_new_fail;
|
|
}
|
|
read_le32_or_fail(buf, class_def->n_interfaces, dex_class_def_new_fail);
|
|
if (class_def->n_interfaces > 0) {
|
|
class_def->interfaces = RZ_NEWS0(ut16, class_def->n_interfaces);
|
|
if (!class_def->interfaces) {
|
|
goto dex_class_def_new_fail;
|
|
}
|
|
for (ut32 i = 0; i < class_def->n_interfaces; ++i) {
|
|
read_le16_or_fail(buf, class_def->interfaces[i], dex_class_def_new_fail);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!class_def->class_data_offset) {
|
|
return class_def;
|
|
}
|
|
|
|
if (rz_buf_seek(buf, class_def->class_data_offset, RZ_BUF_SET) < 0) {
|
|
goto dex_class_def_new_fail;
|
|
}
|
|
|
|
rz_buf_uleb128(buf, &static_fields_size);
|
|
rz_buf_uleb128(buf, &instance_fields_size);
|
|
rz_buf_uleb128(buf, &direct_methods_size);
|
|
rz_buf_uleb128(buf, &virtual_methods_size);
|
|
|
|
for (ut64 i = 0; i < static_fields_size; ++i) {
|
|
DexEncodedField *encoded_field = dex_new_encoded_field(buf, base, &diff_value_prev, i < 1);
|
|
if (!encoded_field || !rz_list_append(class_def->static_fields, encoded_field)) {
|
|
free(encoded_field);
|
|
goto dex_class_def_new_fail;
|
|
}
|
|
}
|
|
|
|
for (ut64 i = 0; i < instance_fields_size; ++i) {
|
|
DexEncodedField *encoded_field = dex_new_encoded_field(buf, base, &diff_value_prev, i < 1);
|
|
if (!encoded_field || !rz_list_append(class_def->instance_fields, encoded_field)) {
|
|
free(encoded_field);
|
|
goto dex_class_def_new_fail;
|
|
}
|
|
}
|
|
|
|
for (ut64 i = 0; i < direct_methods_size; ++i) {
|
|
DexEncodedMethod *encoded_method = dex_new_encoded_method(buf, base, &diff_value_prev, i < 1, method_ids);
|
|
if (!encoded_method || !rz_list_append(class_def->direct_methods, encoded_method)) {
|
|
free(encoded_method);
|
|
goto dex_class_def_new_fail;
|
|
}
|
|
}
|
|
|
|
for (ut64 i = 0; i < virtual_methods_size; ++i) {
|
|
DexEncodedMethod *encoded_method = dex_new_encoded_method(buf, base, &diff_value_prev, i < 1, method_ids);
|
|
if (!encoded_method || !rz_list_append(class_def->virtual_methods, encoded_method)) {
|
|
free(encoded_method);
|
|
goto dex_class_def_new_fail;
|
|
}
|
|
}
|
|
|
|
return class_def;
|
|
|
|
dex_class_def_new_fail:
|
|
dex_class_def_free(class_def);
|
|
return NULL;
|
|
}
|
|
|
|
static void dex_resolve_virtual_method_code(RzBinDex *dex, DexMethodId *to_resolve, ut32 superclass_idx) {
|
|
DexMethodId *method_id = NULL;
|
|
void **it;
|
|
|
|
rz_pvector_foreach (dex->method_ids, it) {
|
|
method_id = (DexMethodId *)*it;
|
|
if (method_id == to_resolve ||
|
|
method_id->class_idx != superclass_idx ||
|
|
method_id->proto_idx != to_resolve->proto_idx ||
|
|
method_id->name_idx != to_resolve->name_idx) {
|
|
continue;
|
|
}
|
|
to_resolve->code_offset = method_id->code_offset;
|
|
to_resolve->code_size = method_id->code_size;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void dex_resolve_all_virtual_methods(RzBinDex *dex) {
|
|
DexClassDef *class_def;
|
|
DexMethodId *method_id = NULL;
|
|
void **it;
|
|
dex->relocs_size = 0;
|
|
rz_pvector_foreach (dex->method_ids, it) {
|
|
method_id = (DexMethodId *)*it;
|
|
if (method_id->code_offset ||
|
|
method_id->class_idx >= rz_pvector_len(dex->class_defs)) {
|
|
continue;
|
|
}
|
|
class_def = rz_pvector_at(dex->class_defs, method_id->class_idx);
|
|
dex_resolve_virtual_method_code(dex, method_id, class_def->superclass_idx);
|
|
}
|
|
}
|
|
|
|
static bool dex_create_relocations(RzBinDex *dex) {
|
|
void **iter_p0;
|
|
RzListIter *iter_l;
|
|
DexClassDef *class_def;
|
|
DexEncodedMethod *encoded_method = NULL;
|
|
DexMethodId *method_id = NULL;
|
|
|
|
dex->relocs_size = 0;
|
|
rz_pvector_foreach (dex->method_ids, iter_p0) {
|
|
method_id = (DexMethodId *)*iter_p0;
|
|
if (method_id->code_offset) {
|
|
continue;
|
|
}
|
|
// patch the imported method.
|
|
method_id->code_offset = dex->relocs_offset + dex->relocs_size;
|
|
method_id->code_size = 2;
|
|
dex->relocs_size += 2;
|
|
}
|
|
|
|
// sync classes with method_ids
|
|
rz_pvector_foreach (dex->class_defs, iter_p0) {
|
|
class_def = (DexClassDef *)*iter_p0;
|
|
rz_list_foreach (class_def->direct_methods, iter_l, encoded_method) {
|
|
if (encoded_method->code_offset) {
|
|
continue;
|
|
}
|
|
method_id = NULL;
|
|
if (encoded_method->method_idx < rz_pvector_len(dex->method_ids)) {
|
|
method_id = (DexMethodId *)rz_pvector_at(dex->method_ids, encoded_method->method_idx);
|
|
if (method_id->code_offset) {
|
|
encoded_method->code_offset = method_id->code_offset;
|
|
encoded_method->code_size = method_id->code_size;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// patch the imported method.
|
|
encoded_method->code_offset = dex->relocs_offset + dex->relocs_size;
|
|
encoded_method->code_size = 2;
|
|
dex->relocs_size += 2;
|
|
if (method_id) {
|
|
method_id->code_offset = encoded_method->code_offset;
|
|
method_id->code_size = encoded_method->code_size;
|
|
}
|
|
}
|
|
|
|
rz_list_foreach (class_def->virtual_methods, iter_l, encoded_method) {
|
|
if (encoded_method->code_offset) {
|
|
continue;
|
|
}
|
|
method_id = NULL;
|
|
if (encoded_method->method_idx < rz_pvector_len(dex->method_ids)) {
|
|
method_id = (DexMethodId *)rz_pvector_at(dex->method_ids, encoded_method->method_idx);
|
|
if (method_id->code_offset) {
|
|
encoded_method->code_offset = method_id->code_offset;
|
|
encoded_method->code_size = method_id->code_size;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// patch the imported method.
|
|
encoded_method->code_offset = dex->relocs_offset + dex->relocs_size;
|
|
encoded_method->code_size = 2;
|
|
dex->relocs_size += 2;
|
|
if (method_id) {
|
|
method_id->code_offset = encoded_method->code_offset;
|
|
method_id->code_size = encoded_method->code_size;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dex->relocs_size < 1) {
|
|
return true;
|
|
}
|
|
dex->relocs_code = malloc(dex->relocs_size);
|
|
if (!dex->relocs_code) {
|
|
return false;
|
|
}
|
|
for (ut32 i = 0; i < dex->relocs_size; i += 2) {
|
|
dex->relocs_code[i] = 0x0e;
|
|
dex->relocs_code[i + 1] = 0x00;
|
|
}
|
|
dex->relocs_buffer = rz_buf_new_with_bytes(dex->relocs_code, dex->relocs_size);
|
|
return dex->relocs_buffer;
|
|
}
|
|
|
|
static bool dex_parse(RzBinDex *dex, ut64 base, RzBuffer *buf) {
|
|
ut64 offset = 0;
|
|
st64 read = 0;
|
|
st64 buffer_size = rz_buf_size(buf);
|
|
if (buffer_size < 116) {
|
|
// 116 bytes is the smalled dex that can be built.
|
|
RZ_LOG_ERROR("dex bin: invalid buffer size (size < 116)\n");
|
|
goto dex_parse_bad;
|
|
}
|
|
|
|
dex->header_offset = base;
|
|
dex->relocs_offset = base + RZ_DEX_RELOC_ADDRESS;
|
|
rz_buf_read(buf, dex->magic, sizeof(dex->magic));
|
|
rz_buf_read(buf, dex->version, sizeof(dex->version));
|
|
dex->checksum_offset = rz_buf_tell(buf) + base;
|
|
read_le32_or_fail(buf, dex->checksum, dex_parse_bad);
|
|
dex->signature_offset = rz_buf_tell(buf) + base;
|
|
rz_buf_read(buf, dex->signature, sizeof(dex->signature));
|
|
read_le32_or_fail(buf, dex->file_size, dex_parse_bad);
|
|
read_le32_or_fail(buf, dex->header_size, dex_parse_bad);
|
|
read_le32_or_fail(buf, dex->endian_tag, dex_parse_bad);
|
|
|
|
read_le32_or_fail(buf, dex->link_size, dex_parse_bad);
|
|
read_le32_or_fail(buf, dex->link_offset, dex_parse_bad);
|
|
|
|
read_le32_or_fail(buf, dex->map_offset, dex_parse_bad);
|
|
|
|
read_le32_or_fail(buf, dex->string_ids_size, dex_parse_bad);
|
|
read_le32_or_fail(buf, dex->string_ids_offset, dex_parse_bad);
|
|
// string_ids points to an array of offsets.
|
|
dex_fail_if_bad_ids(dex->string_ids, sizeof(ut32), buffer_size, dex_parse_bad);
|
|
|
|
read_le32_or_fail(buf, dex->type_ids_size, dex_parse_bad);
|
|
read_le32_or_fail(buf, dex->type_ids_offset, dex_parse_bad);
|
|
dex_fail_if_bad_ids(dex->type_ids, DEX_TYPE_ID_SIZE, buffer_size, dex_parse_bad);
|
|
|
|
read_le32_or_fail(buf, dex->proto_ids_size, dex_parse_bad);
|
|
read_le32_or_fail(buf, dex->proto_ids_offset, dex_parse_bad);
|
|
dex_fail_if_bad_ids(dex->proto_ids, DEX_PROTO_ID_SIZE, buffer_size, dex_parse_bad);
|
|
|
|
read_le32_or_fail(buf, dex->field_ids_size, dex_parse_bad);
|
|
read_le32_or_fail(buf, dex->field_ids_offset, dex_parse_bad);
|
|
dex_fail_if_bad_ids(dex->field_ids, DEX_FIELD_ID_SIZE, buffer_size, dex_parse_bad);
|
|
|
|
read_le32_or_fail(buf, dex->method_ids_size, dex_parse_bad);
|
|
read_le32_or_fail(buf, dex->method_ids_offset, dex_parse_bad);
|
|
dex_fail_if_bad_ids(dex->method_ids, DEX_METHOD_ID_SIZE, buffer_size, dex_parse_bad);
|
|
|
|
read_le32_or_fail(buf, dex->class_defs_size, dex_parse_bad);
|
|
read_le32_or_fail(buf, dex->class_defs_offset, dex_parse_bad);
|
|
|
|
read_le32_or_fail(buf, dex->data_size, dex_parse_bad);
|
|
read_le32_or_fail(buf, dex->data_offset, dex_parse_bad);
|
|
|
|
/* Strings */
|
|
offset = dex->string_ids_offset;
|
|
if (!rz_pvector_reserve(dex->strings, dex->string_ids_size)) {
|
|
goto dex_parse_bad;
|
|
}
|
|
for (ut32 i = 0; i < dex->string_ids_size; ++i, offset += sizeof(ut32)) {
|
|
ut32 string_offset = 0;
|
|
read_le32_at_or_fail(buf, string_offset, offset, dex_parse_bad);
|
|
|
|
if (rz_buf_seek(buf, string_offset, RZ_BUF_SET) < 0) {
|
|
goto dex_parse_bad;
|
|
}
|
|
DexString *string = dex_string_new(buf, base + string_offset, &read);
|
|
if (!string) {
|
|
goto dex_parse_bad;
|
|
}
|
|
rz_pvector_push(dex->strings, string);
|
|
}
|
|
|
|
/* Type Ids */
|
|
dex->types = RZ_NEWS0(DexTypeId, dex->type_ids_size);
|
|
if (!dex->types) {
|
|
goto dex_parse_bad;
|
|
}
|
|
if (rz_buf_seek(buf, dex->type_ids_offset, RZ_BUF_SET) < 0) {
|
|
goto dex_parse_bad;
|
|
}
|
|
for (ut32 i = 0; i < dex->type_ids_size; ++i) {
|
|
read_le32_or_fail(buf, dex->types[i], dex_parse_bad);
|
|
}
|
|
|
|
/* Proto Ids */
|
|
offset = dex->proto_ids_offset;
|
|
if (!rz_pvector_reserve(dex->proto_ids, dex->proto_ids_size)) {
|
|
goto dex_parse_bad;
|
|
}
|
|
for (ut32 i = 0; i < dex->proto_ids_size; ++i, offset += DEX_PROTO_ID_SIZE) {
|
|
if (rz_buf_seek(buf, offset, RZ_BUF_SET) < 0) {
|
|
goto dex_parse_bad;
|
|
}
|
|
DexProtoId *proto_id = dex_proto_id_new(buf, base + offset);
|
|
if (!proto_id) {
|
|
goto dex_parse_bad;
|
|
}
|
|
rz_pvector_push(dex->proto_ids, proto_id);
|
|
}
|
|
|
|
/* Field Ids */
|
|
offset = dex->field_ids_offset;
|
|
if (!rz_pvector_reserve(dex->field_ids, dex->field_ids_size)) {
|
|
goto dex_parse_bad;
|
|
}
|
|
for (ut32 i = 0; i < dex->field_ids_size; ++i, offset += DEX_FIELD_ID_SIZE) {
|
|
if (rz_buf_seek(buf, offset, RZ_BUF_SET) < 0) {
|
|
goto dex_parse_bad;
|
|
}
|
|
DexFieldId *field_id = dex_field_id_new(buf, base + offset);
|
|
if (!field_id) {
|
|
goto dex_parse_bad;
|
|
}
|
|
rz_pvector_push(dex->field_ids, field_id);
|
|
}
|
|
|
|
/* Method Ids */
|
|
offset = dex->method_ids_offset;
|
|
if (!rz_pvector_reserve(dex->method_ids, dex->method_ids_size)) {
|
|
goto dex_parse_bad;
|
|
}
|
|
for (ut32 i = 0; i < dex->method_ids_size; ++i, offset += DEX_METHOD_ID_SIZE) {
|
|
if (rz_buf_seek(buf, offset, RZ_BUF_SET) < 0) {
|
|
goto dex_parse_bad;
|
|
}
|
|
DexMethodId *method_id = dex_method_id_new(buf, base + offset);
|
|
if (!method_id) {
|
|
goto dex_parse_bad;
|
|
}
|
|
rz_pvector_push(dex->method_ids, method_id);
|
|
}
|
|
|
|
/* Class Defs */
|
|
offset = dex->class_defs_offset;
|
|
if (!rz_pvector_reserve(dex->class_defs, dex->class_defs_size)) {
|
|
goto dex_parse_bad;
|
|
}
|
|
for (ut32 i = 0; i < dex->class_defs_size; ++i, offset += DEX_CLASS_DEF_SIZE) {
|
|
if (rz_buf_seek(buf, offset, RZ_BUF_SET) < 0) {
|
|
goto dex_parse_bad;
|
|
}
|
|
DexClassDef *class_def = dex_class_def_new(buf, base + offset, base, dex->method_ids);
|
|
if (!class_def) {
|
|
goto dex_parse_bad;
|
|
}
|
|
rz_pvector_push(dex->class_defs, class_def);
|
|
}
|
|
|
|
/* Resolve all virtual methods */
|
|
dex_resolve_all_virtual_methods(dex);
|
|
|
|
/* generate relocation code buffer this buffer will contain a
|
|
* sequence of 0e00, i.e 'return-void', which will be used to
|
|
* resolve imports for the xrefs.
|
|
*/
|
|
if (!dex_create_relocations(dex)) {
|
|
goto dex_parse_bad;
|
|
}
|
|
|
|
return true;
|
|
|
|
dex_parse_bad:
|
|
rz_bin_dex_free(dex);
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* \brief Frees a RzBinDex struct
|
|
*/
|
|
RZ_API void rz_bin_dex_free(RZ_NULLABLE RzBinDex *dex) {
|
|
if (!dex) {
|
|
return;
|
|
}
|
|
|
|
rz_pvector_free(dex->strings);
|
|
rz_pvector_free(dex->proto_ids);
|
|
rz_pvector_free(dex->field_ids);
|
|
rz_pvector_free(dex->method_ids);
|
|
rz_pvector_free(dex->class_defs);
|
|
rz_buf_free(dex->relocs_buffer);
|
|
|
|
free(dex->types);
|
|
free(dex->relocs_code);
|
|
free(dex);
|
|
}
|
|
|
|
/**
|
|
* \brief Parses the dex file and returns a RzBinDex struct
|
|
*/
|
|
RZ_API RZ_OWN RzBinDex *rz_bin_dex_new(RZ_NONNULL RzBuffer *buf, ut64 base, RZ_NONNULL Sdb *kv) {
|
|
rz_return_val_if_fail(buf, NULL);
|
|
|
|
RzBinDex *dex = (RzBinDex *)RZ_NEW0(RzBinDex);
|
|
if (!dex) {
|
|
return NULL;
|
|
}
|
|
|
|
dex->strings = rz_pvector_new((RzPVectorFree)dex_string_free);
|
|
if (!dex->strings) {
|
|
rz_bin_dex_free(dex);
|
|
return NULL;
|
|
}
|
|
dex->proto_ids = rz_pvector_new((RzPVectorFree)dex_proto_id_free);
|
|
if (!dex->proto_ids) {
|
|
rz_bin_dex_free(dex);
|
|
return NULL;
|
|
}
|
|
dex->field_ids = rz_pvector_new((RzPVectorFree)dex_field_id_free);
|
|
if (!dex->field_ids) {
|
|
rz_bin_dex_free(dex);
|
|
return NULL;
|
|
}
|
|
dex->method_ids = rz_pvector_new((RzPVectorFree)dex_method_id_free);
|
|
if (!dex->method_ids) {
|
|
rz_bin_dex_free(dex);
|
|
return NULL;
|
|
}
|
|
dex->class_defs = rz_pvector_new((RzPVectorFree)dex_class_def_free);
|
|
if (!dex->class_defs) {
|
|
rz_bin_dex_free(dex);
|
|
return NULL;
|
|
}
|
|
|
|
if (!dex_parse(dex, base, buf)) {
|
|
return NULL;
|
|
}
|
|
|
|
return dex;
|
|
}
|
|
|
|
RZ_API RZ_OWN char *rz_bin_dex_access_flags_readable(ut32 access_flags) {
|
|
RzStrBuf *sb = NULL;
|
|
for (ut32 i = 0; i < CLASS_ACCESS_FLAGS_SIZE; ++i) {
|
|
const DexAccessFlagsReadable *afr = &access_flags_list[i];
|
|
if (afr->flag == ACCESS_FLAG_VARARGS) {
|
|
continue;
|
|
}
|
|
if (access_flags & afr->flag) {
|
|
if (!sb) {
|
|
sb = rz_strbuf_new(afr->readable);
|
|
if (!sb) {
|
|
return NULL;
|
|
}
|
|
} else {
|
|
rz_strbuf_appendf(sb, " %s", afr->readable);
|
|
}
|
|
}
|
|
}
|
|
|
|
return sb ? rz_strbuf_drain(sb) : NULL;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns a RzPVector<RzBinString*> containing the dex strings
|
|
*/
|
|
RZ_API RZ_OWN RzPVector /*<RzBinString *>*/ *rz_bin_dex_strings(RZ_NONNULL RzBinDex *dex) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
|
|
DexString *string;
|
|
void **it;
|
|
RzPVector *strings = rz_pvector_new((RzPVectorFree)rz_bin_string_free);
|
|
if (!strings) {
|
|
return NULL;
|
|
}
|
|
|
|
ut32 ordinal = 0;
|
|
rz_pvector_foreach (dex->strings, it) {
|
|
string = (DexString *)*it;
|
|
RzBinString *bstr = RZ_NEW0(RzBinString);
|
|
if (!bstr) {
|
|
continue;
|
|
}
|
|
bstr->paddr = string->offset;
|
|
bstr->vaddr = RZ_DEX_VIRT_ADDRESS + string->offset;
|
|
bstr->ordinal = ordinal;
|
|
bstr->length = string->size;
|
|
bstr->size = string->size;
|
|
bstr->string = rz_str_ndup(string->data, string->size);
|
|
bstr->type = RZ_STRING_ENC_UTF8;
|
|
if (!rz_pvector_push(strings, bstr)) {
|
|
free(bstr);
|
|
}
|
|
ordinal++;
|
|
}
|
|
return strings;
|
|
}
|
|
|
|
static inline DexString *dex_resolve_string_id_native(RzBinDex *dex, ut32 string_idx) {
|
|
if (string_idx >= rz_pvector_len(dex->strings)) {
|
|
return NULL;
|
|
}
|
|
return (DexString *)rz_pvector_at(dex->strings, string_idx);
|
|
}
|
|
|
|
static char *dex_resolve_string_id(RzBinDex *dex, ut32 string_idx) {
|
|
DexString *string = dex_resolve_string_id_native(dex, string_idx);
|
|
if (!string) {
|
|
RZ_LOG_INFO("cannot find string with index %u\n", string_idx);
|
|
return NULL;
|
|
}
|
|
return rz_str_ndup(string->data, string->size);
|
|
}
|
|
|
|
static char *dex_resolve_type_id(RzBinDex *dex, ut32 type_idx) {
|
|
if (type_idx >= dex->type_ids_size) {
|
|
RZ_LOG_INFO("cannot find type_id with index %u\n", type_idx);
|
|
return NULL;
|
|
}
|
|
DexTypeId type_id = dex->types[type_idx];
|
|
return dex_resolve_string_id(dex, type_id);
|
|
}
|
|
|
|
static char *dex_resolve_proto_id(RzBinDex *dex, const char *name, ut32 proto_idx, bool varargs) {
|
|
if (proto_idx >= rz_pvector_len(dex->proto_ids)) {
|
|
RZ_LOG_INFO("cannot find proto_id with index %u\n", proto_idx);
|
|
return NULL;
|
|
}
|
|
|
|
DexProtoId *proto_id = (DexProtoId *)rz_pvector_at(dex->proto_ids, proto_idx);
|
|
if (proto_id->return_type_idx >= dex->type_ids_size) {
|
|
RZ_LOG_INFO("cannot find return type id with index %u\n", proto_id->return_type_idx);
|
|
return NULL;
|
|
}
|
|
|
|
RzStrBuf *sb = rz_strbuf_new(name);
|
|
if (!sb) {
|
|
return NULL;
|
|
}
|
|
|
|
const DexString *return_type = dex_resolve_string_id_native(dex, dex->types[proto_id->return_type_idx]);
|
|
if (!return_type) {
|
|
RZ_LOG_INFO("cannot find return type string with index %u\n", proto_id->return_type_idx);
|
|
rz_strbuf_free(sb);
|
|
return NULL;
|
|
}
|
|
|
|
rz_strbuf_append(sb, "(");
|
|
for (ut32 i = 0; i < proto_id->type_list_size; ++i) {
|
|
ut32 type_idx = proto_id->type_list[i];
|
|
if (type_idx >= dex->type_ids_size) {
|
|
continue;
|
|
}
|
|
|
|
const DexString *param = dex_resolve_string_id_native(dex, dex->types[type_idx]);
|
|
if (!param) {
|
|
RZ_LOG_INFO("cannot find param string with index %d\n", dex->types[type_idx]);
|
|
rz_strbuf_free(sb);
|
|
return NULL;
|
|
}
|
|
if (varargs && (i + 1) >= proto_id->type_list_size) {
|
|
rz_strbuf_append(sb, "...");
|
|
}
|
|
rz_strbuf_append_n(sb, param->data, param->size);
|
|
}
|
|
rz_strbuf_append(sb, ")");
|
|
rz_strbuf_append_n(sb, return_type->data, return_type->size);
|
|
return rz_strbuf_drain(sb);
|
|
}
|
|
|
|
static ut64 dex_access_flags_to_bin_flags(ut64 access_flags) {
|
|
ut64 flags = 0;
|
|
if (access_flags & ACCESS_FLAG_PUBLIC) {
|
|
flags |= RZ_BIN_METH_PUBLIC;
|
|
}
|
|
if (access_flags & ACCESS_FLAG_PRIVATE) {
|
|
flags |= RZ_BIN_METH_PRIVATE;
|
|
}
|
|
if (access_flags & ACCESS_FLAG_PROTECTED) {
|
|
flags |= RZ_BIN_METH_PROTECTED;
|
|
}
|
|
if (access_flags & ACCESS_FLAG_STATIC) {
|
|
flags |= RZ_BIN_METH_STATIC;
|
|
}
|
|
if (access_flags & ACCESS_FLAG_FINAL) {
|
|
flags |= RZ_BIN_METH_FINAL;
|
|
}
|
|
if (access_flags & ACCESS_FLAG_SYNCHRONIZED) {
|
|
flags |= RZ_BIN_METH_SYNCHRONIZED;
|
|
}
|
|
if (access_flags & ACCESS_FLAG_BRIDGE) {
|
|
flags |= RZ_BIN_METH_BRIDGE;
|
|
}
|
|
if (access_flags & ACCESS_FLAG_VARARGS) {
|
|
flags |= RZ_BIN_METH_VARARGS;
|
|
}
|
|
if (access_flags & ACCESS_FLAG_NATIVE) {
|
|
flags |= RZ_BIN_METH_NATIVE;
|
|
}
|
|
// RZ_BIN_METH_INTERFACE does not exists
|
|
// if (access_flags & ACCESS_FLAG_INTERFACE) {
|
|
// flags |= RZ_BIN_METH_INTERFACE;
|
|
//}
|
|
if (access_flags & ACCESS_FLAG_ABSTRACT) {
|
|
flags |= RZ_BIN_METH_ABSTRACT;
|
|
}
|
|
if (access_flags & ACCESS_FLAG_STRICT) {
|
|
flags |= RZ_BIN_METH_STRICT;
|
|
}
|
|
if (access_flags & ACCESS_FLAG_SYNTHETIC) {
|
|
flags |= RZ_BIN_METH_SYNTHETIC;
|
|
}
|
|
// RZ_BIN_METH_ANNOTATION does not exists
|
|
// if (access_flags & ACCESS_FLAG_ANNOTATION) {
|
|
// flags |= RZ_BIN_METH_ANNOTATION;
|
|
//}
|
|
// RZ_BIN_METH_ENUM does not exists
|
|
// if (access_flags & ACCESS_FLAG_ENUM) {
|
|
// flags |= RZ_BIN_METH_ENUM;
|
|
//}
|
|
// RZ_BIN_METH_MODULE does not exists
|
|
// if (access_flags & ACCESS_FLAG_MODULE) {
|
|
// flags |= RZ_BIN_METH_MODULE;
|
|
//}
|
|
if (access_flags & ACCESS_FLAG_CONSTRUCTOR) {
|
|
flags |= RZ_BIN_METH_CONSTRUCTOR;
|
|
}
|
|
if (access_flags & ACCESS_FLAG_DECLARED_SYNCHRONIZED) {
|
|
flags |= RZ_BIN_METH_DECLARED_SYNCHRONIZED;
|
|
}
|
|
return flags;
|
|
}
|
|
|
|
static char *demangle_java_and_free(char *mangled) {
|
|
if (!mangled) {
|
|
return NULL;
|
|
}
|
|
char *demangled = rz_demangler_java(mangled, RZ_DEMANGLER_FLAG_ENABLE_ALL);
|
|
free(mangled);
|
|
return demangled;
|
|
}
|
|
|
|
static void set_lib_and_class_name(char *mangled, char **out_class, char **out_lib) {
|
|
if (!mangled) {
|
|
return;
|
|
}
|
|
bool is_java_lang = startswith(mangled, "Ljava/lang");
|
|
|
|
char *object = demangle_java_and_free(mangled);
|
|
if (!object) {
|
|
return;
|
|
}
|
|
|
|
*out_class = object;
|
|
if (!is_java_lang || startswith(object, "java.lang")) {
|
|
*out_lib = strdup(object);
|
|
} else {
|
|
*out_lib = rz_str_newf("java.lang.%s", object);
|
|
}
|
|
}
|
|
|
|
static RzBinSymbol *dex_method_to_symbol(RzBinDex *dex, DexEncodedMethod *encoded_method, DexMethodId *method_id, bool is_imported) {
|
|
RzBinSymbol *symbol = RZ_NEW0(RzBinSymbol);
|
|
if (!symbol) {
|
|
return NULL;
|
|
}
|
|
|
|
bool varargs = dex_is_varargs(encoded_method->access_flags);
|
|
symbol->name = dex_resolve_string_id(dex, method_id->name_idx);
|
|
char *mangled = dex_resolve_type_id(dex, method_id->class_idx);
|
|
set_lib_and_class_name(mangled, &symbol->classname, &symbol->libname);
|
|
symbol->dname = demangle_java_and_free(dex_resolve_proto_id(dex, symbol->name, method_id->proto_idx, varargs));
|
|
symbol->bind = dex_is_static(encoded_method->access_flags) ? RZ_BIN_BIND_GLOBAL_STR : RZ_BIN_BIND_LOCAL_STR;
|
|
symbol->is_imported = is_imported;
|
|
symbol->visibility = encoded_method->access_flags & UT32_MAX;
|
|
symbol->visibility_str = rz_bin_dex_access_flags_readable(symbol->visibility);
|
|
symbol->size = encoded_method->code_size;
|
|
if (encoded_method->code_offset < RZ_DEX_RELOC_ADDRESS) {
|
|
symbol->vaddr = RZ_DEX_VIRT_ADDRESS + encoded_method->code_offset;
|
|
symbol->paddr = encoded_method->code_offset;
|
|
} else {
|
|
symbol->vaddr = encoded_method->code_offset;
|
|
symbol->paddr = 0;
|
|
}
|
|
symbol->ordinal = encoded_method->method_idx;
|
|
symbol->method_flags = dex_access_flags_to_bin_flags(encoded_method->access_flags);
|
|
symbol->type = RZ_BIN_TYPE_METH_STR;
|
|
|
|
return symbol;
|
|
}
|
|
|
|
static RzList /*<RzBinSymbol *>*/ *dex_resolve_methods_in_class(RzBinDex *dex, DexClassDef *class_def, ut8 *inserted) {
|
|
RzList *methods = rz_list_newf((RzListFree)rz_bin_symbol_free);
|
|
if (!methods) {
|
|
return NULL;
|
|
}
|
|
|
|
DexMethodId *method_id = NULL;
|
|
DexEncodedMethod *encoded_method = NULL;
|
|
RzListIter *it = NULL;
|
|
|
|
rz_list_foreach (class_def->direct_methods, it, encoded_method) {
|
|
if (encoded_method->method_idx >= rz_pvector_len(dex->method_ids)) {
|
|
RZ_LOG_INFO("cannot find direct method with index %" PFMT64u "\n", encoded_method->method_idx);
|
|
continue;
|
|
} else if (inserted[encoded_method->method_idx]) {
|
|
continue;
|
|
}
|
|
method_id = (DexMethodId *)rz_pvector_at(dex->method_ids, encoded_method->method_idx);
|
|
inserted[encoded_method->method_idx] = true;
|
|
|
|
RzBinSymbol *symbol = dex_method_to_symbol(dex, encoded_method, method_id, false);
|
|
if (!symbol || !rz_list_append(methods, symbol)) {
|
|
rz_bin_symbol_free(symbol);
|
|
break;
|
|
}
|
|
}
|
|
|
|
rz_list_foreach (class_def->virtual_methods, it, encoded_method) {
|
|
if (encoded_method->method_idx >= rz_pvector_len(dex->method_ids)) {
|
|
RZ_LOG_INFO("cannot find virtual method with index %" PFMT64u "\n", encoded_method->method_idx);
|
|
continue;
|
|
} else if (inserted[encoded_method->method_idx]) {
|
|
continue;
|
|
}
|
|
method_id = (DexMethodId *)rz_pvector_at(dex->method_ids, encoded_method->method_idx);
|
|
inserted[encoded_method->method_idx] = true;
|
|
|
|
RzBinSymbol *symbol = dex_method_to_symbol(dex, encoded_method, method_id, false);
|
|
if (!symbol || !rz_list_append(methods, symbol)) {
|
|
rz_bin_symbol_free(symbol);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return methods;
|
|
}
|
|
|
|
static RzBinClassField *dex_field_to_bin_field(RzBinDex *dex, DexEncodedField *encoded_field, DexFieldId *field_id, bool is_static) {
|
|
RzBinClassField *field = RZ_NEW0(RzBinClassField);
|
|
if (!field) {
|
|
return NULL;
|
|
}
|
|
|
|
ut64 access_flags = encoded_field->access_flags;
|
|
if (is_static) {
|
|
access_flags |= ACCESS_FLAG_STATIC;
|
|
}
|
|
|
|
field->vaddr = RZ_DEX_VIRT_ADDRESS + encoded_field->offset;
|
|
field->paddr = encoded_field->offset;
|
|
field->visibility = encoded_field->access_flags & UT32_MAX;
|
|
field->visibility_str = rz_bin_dex_access_flags_readable(access_flags);
|
|
char *mangled = dex_resolve_type_id(dex, field_id->class_idx);
|
|
set_lib_and_class_name(mangled, &field->classname, &field->libname);
|
|
field->name = dex_resolve_string_id(dex, field_id->name_idx);
|
|
field->type = demangle_java_and_free(dex_resolve_type_id(dex, field_id->type_idx));
|
|
field->flags = dex_access_flags_to_bin_flags(access_flags);
|
|
|
|
return field;
|
|
}
|
|
|
|
static RzList /*<RzBinClassField *>*/ *dex_resolve_fields_in_class(RzBinDex *dex, DexClassDef *class_def, ut8 *inserted) {
|
|
RzList *fields = rz_list_newf((RzListFree)rz_bin_class_field_free);
|
|
if (!fields) {
|
|
return NULL;
|
|
}
|
|
DexFieldId *field_id = NULL;
|
|
DexEncodedField *encoded_field = NULL;
|
|
RzListIter *it = NULL;
|
|
|
|
rz_list_foreach (class_def->static_fields, it, encoded_field) {
|
|
if (encoded_field->field_idx >= rz_pvector_len(dex->field_ids)) {
|
|
RZ_LOG_INFO("cannot find static field with index %" PFMT64u "\n", encoded_field->field_idx);
|
|
continue;
|
|
} else if (inserted[encoded_field->field_idx]) {
|
|
continue;
|
|
}
|
|
inserted[encoded_field->field_idx] = true;
|
|
field_id = (DexFieldId *)rz_pvector_at(dex->field_ids, encoded_field->field_idx);
|
|
|
|
RzBinClassField *field = dex_field_to_bin_field(dex, encoded_field, field_id, true);
|
|
if (!field || !rz_list_append(fields, field)) {
|
|
rz_bin_class_field_free(field);
|
|
break;
|
|
}
|
|
}
|
|
|
|
rz_list_foreach (class_def->instance_fields, it, encoded_field) {
|
|
if (encoded_field->field_idx >= rz_pvector_len(dex->field_ids)) {
|
|
RZ_LOG_INFO("cannot find instance field with index %" PFMT64u "\n", encoded_field->field_idx);
|
|
continue;
|
|
} else if (inserted[encoded_field->field_idx]) {
|
|
continue;
|
|
}
|
|
inserted[encoded_field->field_idx] = true;
|
|
field_id = (DexFieldId *)rz_pvector_at(dex->field_ids, encoded_field->field_idx);
|
|
|
|
RzBinClassField *field = dex_field_to_bin_field(dex, encoded_field, field_id, false);
|
|
if (!field || !rz_list_append(fields, field)) {
|
|
rz_bin_class_field_free(field);
|
|
break;
|
|
}
|
|
}
|
|
return fields;
|
|
}
|
|
|
|
static RzBinSymbol *dex_field_to_symbol(RzBinDex *dex, DexEncodedField *encoded_field, DexFieldId *field_id, bool is_imported) {
|
|
RzBinSymbol *field = RZ_NEW0(RzBinSymbol);
|
|
if (!field) {
|
|
return NULL;
|
|
}
|
|
|
|
field->name = dex_resolve_string_id(dex, field_id->name_idx);
|
|
char *mangled = dex_resolve_type_id(dex, field_id->class_idx);
|
|
set_lib_and_class_name(mangled, &field->classname, &field->libname);
|
|
field->bind = dex_is_static(encoded_field->access_flags) ? RZ_BIN_BIND_GLOBAL_STR : RZ_BIN_BIND_LOCAL_STR;
|
|
field->is_imported = false;
|
|
field->visibility = encoded_field->access_flags & UT32_MAX;
|
|
field->visibility_str = rz_bin_dex_access_flags_readable(encoded_field->access_flags);
|
|
field->vaddr = RZ_DEX_VIRT_ADDRESS + encoded_field->offset;
|
|
field->paddr = encoded_field->offset;
|
|
field->ordinal = encoded_field->field_idx;
|
|
field->method_flags = dex_access_flags_to_bin_flags(encoded_field->access_flags);
|
|
field->type = RZ_BIN_TYPE_FIELD_STR;
|
|
|
|
return field;
|
|
}
|
|
|
|
static RzList /*<RzBinSymbol *>*/ *dex_resolve_fields_in_class_as_symbols(RzBinDex *dex, DexClassDef *class_def, ut8 *inserted) {
|
|
RzList *fields = rz_list_newf((RzListFree)rz_bin_symbol_free);
|
|
if (!fields) {
|
|
return NULL;
|
|
}
|
|
DexFieldId *field_id = NULL;
|
|
DexEncodedField *encoded_field = NULL;
|
|
RzListIter *it = NULL;
|
|
|
|
rz_list_foreach (class_def->static_fields, it, encoded_field) {
|
|
if (encoded_field->field_idx >= rz_pvector_len(dex->field_ids)) {
|
|
RZ_LOG_INFO("cannot find static field with index %" PFMT64u "\n", encoded_field->field_idx);
|
|
continue;
|
|
}
|
|
field_id = (DexFieldId *)rz_pvector_at(dex->field_ids, encoded_field->field_idx);
|
|
inserted[encoded_field->field_idx] = true;
|
|
|
|
RzBinSymbol *field = dex_field_to_symbol(dex, encoded_field, field_id, false);
|
|
if (!field || !rz_list_append(fields, field)) {
|
|
rz_bin_symbol_free(field);
|
|
break;
|
|
}
|
|
}
|
|
|
|
rz_list_foreach (class_def->instance_fields, it, encoded_field) {
|
|
if (encoded_field->field_idx >= rz_pvector_len(dex->field_ids)) {
|
|
RZ_LOG_INFO("cannot find instance field with index %" PFMT64u "\n", encoded_field->field_idx);
|
|
continue;
|
|
}
|
|
field_id = (DexFieldId *)rz_pvector_at(dex->field_ids, encoded_field->field_idx);
|
|
inserted[encoded_field->field_idx] = true;
|
|
|
|
RzBinSymbol *field = dex_field_to_symbol(dex, encoded_field, field_id, false);
|
|
if (!field || !rz_list_append(fields, field)) {
|
|
rz_bin_symbol_free(field);
|
|
break;
|
|
}
|
|
}
|
|
return fields;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns a RzPVector<RzBinClass*> containing the dex classes
|
|
*/
|
|
RZ_API RZ_OWN RzPVector /*<RzBinClass *>*/ *rz_bin_dex_classes(RZ_NONNULL RzBinDex *dex) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
|
|
DexClassDef *class_def;
|
|
RzBinClass *bclass = NULL;
|
|
RzPVector *classes = NULL;
|
|
void **it;
|
|
|
|
ut32 n_methods = rz_pvector_len(dex->method_ids);
|
|
ut32 n_fields = rz_pvector_len(dex->field_ids);
|
|
|
|
ut8 *inserted_methods = RZ_NEWS0(ut8, n_methods);
|
|
ut8 *inserted_fields = RZ_NEWS0(ut8, n_fields);
|
|
if ((n_methods > 0 && !inserted_methods) || (n_fields > 0 && !inserted_fields)) {
|
|
free(inserted_fields);
|
|
free(inserted_methods);
|
|
return NULL;
|
|
}
|
|
|
|
classes = rz_pvector_new((RzPVectorFree)rz_bin_class_free);
|
|
if (!classes) {
|
|
free(inserted_fields);
|
|
free(inserted_methods);
|
|
return NULL;
|
|
}
|
|
|
|
rz_pvector_foreach (dex->class_defs, it) {
|
|
class_def = (DexClassDef *)*it;
|
|
bclass = RZ_NEW0(RzBinClass);
|
|
if (!bclass) {
|
|
break;
|
|
}
|
|
|
|
bclass->name = demangle_java_and_free(dex_resolve_type_id(dex, class_def->class_idx));
|
|
bclass->super = demangle_java_and_free(dex_resolve_type_id(dex, class_def->superclass_idx));
|
|
bclass->visibility = class_def->access_flags;
|
|
bclass->visibility_str = rz_bin_dex_access_flags_readable(class_def->access_flags);
|
|
bclass->addr = class_def->offset;
|
|
bclass->methods = dex_resolve_methods_in_class(dex, class_def, inserted_methods);
|
|
bclass->fields = dex_resolve_fields_in_class(dex, class_def, inserted_fields);
|
|
|
|
if (!rz_pvector_push(classes, bclass)) {
|
|
rz_bin_class_free(bclass);
|
|
break;
|
|
}
|
|
}
|
|
free(inserted_fields);
|
|
free(inserted_methods);
|
|
|
|
return classes;
|
|
}
|
|
|
|
static RzBinSection *section_new(const char *name, ut32 perm, ut32 size, ut64 paddr, ut64 vaddr) {
|
|
RzBinSection *section = RZ_NEW0(RzBinSection);
|
|
if (!section) {
|
|
return NULL;
|
|
}
|
|
section->name = strdup(name);
|
|
section->paddr = paddr;
|
|
section->vaddr = vaddr;
|
|
section->size = section->vsize = size;
|
|
section->perm = perm;
|
|
return section;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns a RzPVector<RzBinSection*> containing the dex sections
|
|
*/
|
|
RZ_API RZ_OWN RzPVector /*<RzBinSection *>*/ *rz_bin_dex_sections(RZ_NONNULL RzBinDex *dex) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
|
|
RzBinSection *section;
|
|
RzPVector *sections = NULL;
|
|
|
|
sections = rz_pvector_new((RzPVectorFree)rz_bin_section_free);
|
|
if (!sections) {
|
|
return NULL;
|
|
}
|
|
section = section_new("data", RZ_PERM_RWX, dex->data_size, dex->data_offset, RZ_DEX_VIRT_ADDRESS + dex->data_offset);
|
|
if (section && !rz_pvector_push(sections, section)) {
|
|
rz_bin_section_free(section);
|
|
}
|
|
section = section_new("file", RZ_PERM_R, dex->file_size, dex->header_offset, 0);
|
|
if (section && !rz_pvector_push(sections, section)) {
|
|
rz_bin_section_free(section);
|
|
}
|
|
|
|
if (dex->relocs_code) {
|
|
section = section_new(RZ_DEX_RELOC_TARGETS, RZ_PERM_RWX, dex->relocs_size, 0, dex->relocs_offset);
|
|
if (section && !rz_pvector_push(sections, section)) {
|
|
rz_bin_section_free(section);
|
|
}
|
|
}
|
|
|
|
return sections;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns a RzList<RzBinClassField*> containing the dex fields
|
|
*/
|
|
RZ_API RZ_OWN RzList /*<RzBinClassField *>*/ *rz_bin_dex_fields(RZ_NONNULL RzBinDex *dex) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
|
|
DexClassDef *class_def;
|
|
RzList *fields = NULL;
|
|
void **it;
|
|
ut32 n_fields = rz_pvector_len(dex->field_ids);
|
|
ut8 *inserted = RZ_NEWS0(ut8, n_fields);
|
|
if (n_fields > 0 && !inserted) {
|
|
free(inserted);
|
|
return NULL;
|
|
}
|
|
|
|
fields = rz_list_newf((RzListFree)rz_bin_class_field_free);
|
|
if (!fields) {
|
|
free(inserted);
|
|
return NULL;
|
|
}
|
|
|
|
rz_pvector_foreach (dex->class_defs, it) {
|
|
class_def = (DexClassDef *)*it;
|
|
RzList *class_fields = dex_resolve_fields_in_class(dex, class_def, inserted);
|
|
if (class_fields) {
|
|
rz_list_join(fields, class_fields);
|
|
rz_list_free(class_fields);
|
|
}
|
|
}
|
|
free(inserted);
|
|
|
|
return fields;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns a RzPVector<RzBinSymbol*> containing the dex symbols
|
|
*/
|
|
RZ_API RZ_OWN RzPVector /*<RzBinSymbol *>*/ *rz_bin_dex_symbols(RZ_NONNULL RzBinDex *dex) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
|
|
DexClassDef *class_def;
|
|
DexFieldId *field_id;
|
|
DexMethodId *method_id;
|
|
RzList *class_symbols = NULL;
|
|
RzPVector *symbols = NULL;
|
|
void **vit;
|
|
ut8 *inserted_methods = NULL;
|
|
ut8 *inserted_fields = NULL;
|
|
ut32 n_methods = rz_pvector_len(dex->method_ids);
|
|
ut32 n_fields = rz_pvector_len(dex->field_ids);
|
|
|
|
inserted_methods = RZ_NEWS0(ut8, n_methods);
|
|
inserted_fields = RZ_NEWS0(ut8, n_fields);
|
|
if ((n_methods > 0 && !inserted_methods) || (n_fields > 0 && !inserted_fields)) {
|
|
free(inserted_fields);
|
|
free(inserted_methods);
|
|
return NULL;
|
|
}
|
|
|
|
symbols = rz_pvector_new((RzPVectorFree)rz_bin_symbol_free);
|
|
if (!symbols) {
|
|
free(inserted_fields);
|
|
free(inserted_methods);
|
|
return NULL;
|
|
}
|
|
|
|
rz_pvector_foreach (dex->class_defs, vit) {
|
|
class_def = (DexClassDef *)*vit;
|
|
RzListIter *iter;
|
|
RzBinSymbol *sym;
|
|
|
|
class_symbols = dex_resolve_fields_in_class_as_symbols(dex, class_def, inserted_fields);
|
|
if (class_symbols) {
|
|
rz_list_foreach (class_symbols, iter, sym) {
|
|
rz_pvector_push(symbols, sym);
|
|
}
|
|
class_symbols->length = 0;
|
|
class_symbols->head = class_symbols->tail = NULL;
|
|
rz_list_free(class_symbols);
|
|
}
|
|
|
|
class_symbols = dex_resolve_methods_in_class(dex, class_def, inserted_methods);
|
|
if (class_symbols) {
|
|
rz_list_foreach (class_symbols, iter, sym) {
|
|
rz_pvector_push(symbols, sym);
|
|
}
|
|
class_symbols->length = 0;
|
|
class_symbols->head = class_symbols->tail = NULL;
|
|
rz_list_free(class_symbols);
|
|
}
|
|
}
|
|
|
|
ut32 j = 0;
|
|
rz_pvector_foreach (dex->field_ids, vit) {
|
|
field_id = (DexFieldId *)*vit;
|
|
if (inserted_fields[j++]) {
|
|
continue;
|
|
}
|
|
|
|
RzBinSymbol *field = RZ_NEW0(RzBinSymbol);
|
|
if (!field) {
|
|
break;
|
|
}
|
|
|
|
field->name = dex_resolve_string_id(dex, field_id->name_idx);
|
|
char *mangled = dex_resolve_type_id(dex, field_id->class_idx);
|
|
set_lib_and_class_name(mangled, &field->classname, &field->libname);
|
|
field->bind = RZ_BIN_BIND_WEAK_STR;
|
|
field->type = RZ_BIN_TYPE_FIELD_STR;
|
|
field->is_imported = true;
|
|
field->vaddr = UT64_MAX;
|
|
field->paddr = UT64_MAX;
|
|
|
|
if (!rz_pvector_push(symbols, field)) {
|
|
rz_bin_symbol_free(field);
|
|
break;
|
|
}
|
|
}
|
|
|
|
j = 0;
|
|
rz_pvector_foreach (dex->method_ids, vit) {
|
|
method_id = (DexMethodId *)*vit;
|
|
if (inserted_methods[j++]) {
|
|
continue;
|
|
}
|
|
|
|
RzBinSymbol *method = RZ_NEW0(RzBinSymbol);
|
|
if (!method) {
|
|
break;
|
|
}
|
|
|
|
method->name = dex_resolve_string_id(dex, method_id->name_idx);
|
|
char *mangled = dex_resolve_type_id(dex, method_id->class_idx);
|
|
set_lib_and_class_name(mangled, &method->classname, &method->libname);
|
|
method->dname = demangle_java_and_free(dex_resolve_proto_id(dex, method->name, method_id->proto_idx, false));
|
|
method->bind = RZ_BIN_BIND_WEAK_STR;
|
|
method->is_imported = true;
|
|
method->type = RZ_BIN_TYPE_METH_STR;
|
|
if (method_id->code_offset < RZ_DEX_RELOC_ADDRESS) {
|
|
method->vaddr = RZ_DEX_VIRT_ADDRESS + method_id->code_offset;
|
|
method->paddr = method_id->code_offset;
|
|
} else {
|
|
method->vaddr = method_id->code_offset;
|
|
method->paddr = 0;
|
|
}
|
|
method->size = method_id->code_size;
|
|
|
|
if (!rz_pvector_push(symbols, method)) {
|
|
rz_bin_symbol_free(method);
|
|
break;
|
|
}
|
|
}
|
|
|
|
free(inserted_fields);
|
|
free(inserted_methods);
|
|
return symbols;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns a RzPVector<RzBinImport*> containing the dex imports
|
|
*/
|
|
RZ_API RZ_OWN RzPVector /*<RzBinImport *>*/ *rz_bin_dex_imports(RZ_NONNULL RzBinDex *dex) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
|
|
DexFieldId *field_id;
|
|
DexMethodId *method_id;
|
|
DexClassDef *class_def;
|
|
RzPVector *imports = NULL;
|
|
ut32 *class_ids = NULL;
|
|
void **vit;
|
|
|
|
ut32 n_classes = rz_pvector_len(dex->class_defs);
|
|
if (n_classes < 1) {
|
|
return rz_pvector_new((RzPVectorFree)rz_bin_import_free);
|
|
}
|
|
|
|
class_ids = RZ_NEWS0(ut32, n_classes);
|
|
if (!class_ids) {
|
|
return NULL;
|
|
}
|
|
|
|
ut32 j = 0;
|
|
rz_pvector_foreach (dex->class_defs, vit) {
|
|
class_def = (DexClassDef *)*vit;
|
|
class_ids[j] = class_def->class_idx;
|
|
j++;
|
|
}
|
|
|
|
imports = rz_pvector_new((RzPVectorFree)rz_bin_import_free);
|
|
if (!imports) {
|
|
free(class_ids);
|
|
return NULL;
|
|
}
|
|
|
|
ut32 ordinal = 0;
|
|
rz_pvector_foreach (dex->field_ids, vit) {
|
|
field_id = (DexFieldId *)*vit;
|
|
bool class_found = false;
|
|
for (ut32 i = 0; i < n_classes; ++i) {
|
|
if (field_id->class_idx == class_ids[i]) {
|
|
class_found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (class_found) {
|
|
continue;
|
|
}
|
|
|
|
RzBinImport *import = RZ_NEW0(RzBinImport);
|
|
if (!import) {
|
|
break;
|
|
}
|
|
|
|
import->name = dex_resolve_string_id(dex, field_id->name_idx);
|
|
char *mangled = dex_resolve_type_id(dex, field_id->class_idx);
|
|
set_lib_and_class_name(mangled, &import->classname, &import->libname);
|
|
import->bind = RZ_BIN_BIND_WEAK_STR;
|
|
import->type = RZ_BIN_TYPE_FIELD_STR;
|
|
import->ordinal = ordinal;
|
|
|
|
if (!rz_pvector_push(imports, import)) {
|
|
rz_bin_import_free(import);
|
|
break;
|
|
}
|
|
ordinal++;
|
|
}
|
|
|
|
rz_pvector_foreach (dex->method_ids, vit) {
|
|
method_id = (DexMethodId *)*vit;
|
|
bool class_found = false;
|
|
for (ut32 i = 0; i < n_classes; ++i) {
|
|
if (method_id->class_idx == class_ids[i]) {
|
|
class_found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (class_found) {
|
|
continue;
|
|
}
|
|
|
|
RzBinImport *import = RZ_NEW0(RzBinImport);
|
|
if (!import) {
|
|
break;
|
|
}
|
|
|
|
char *name = dex_resolve_string_id(dex, method_id->name_idx);
|
|
import->name = dex_resolve_proto_id(dex, name, method_id->proto_idx, false);
|
|
char *mangled = dex_resolve_type_id(dex, method_id->class_idx);
|
|
set_lib_and_class_name(mangled, &import->classname, &import->libname);
|
|
import->bind = RZ_BIN_BIND_WEAK_STR;
|
|
import->type = RZ_BIN_TYPE_FUNC_STR;
|
|
import->ordinal = ordinal;
|
|
free(name);
|
|
|
|
if (!rz_pvector_push(imports, import)) {
|
|
rz_bin_import_free(import);
|
|
break;
|
|
}
|
|
ordinal++;
|
|
}
|
|
|
|
free(class_ids);
|
|
return imports;
|
|
}
|
|
|
|
static int compare_strings(const void *a, const void *b, void *user) {
|
|
return strcmp((const char *)a, (const char *)b);
|
|
}
|
|
|
|
/**
|
|
* \brief Returns a RzPVector<char*> containing the dex libraries
|
|
*/
|
|
RZ_API RZ_OWN RzPVector /*<char *>*/ *rz_bin_dex_libraries(RZ_NONNULL RzBinDex *dex) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
|
|
DexMethodId *method_id;
|
|
DexClassDef *class_def;
|
|
RzPVector *libraries = NULL;
|
|
ut32 *class_ids = NULL;
|
|
void **vit;
|
|
|
|
ut32 n_classes = rz_pvector_len(dex->class_defs);
|
|
if (n_classes < 1) {
|
|
return rz_pvector_new((RzPVectorFree)free);
|
|
}
|
|
|
|
class_ids = RZ_NEWS0(ut32, n_classes);
|
|
if (!class_ids) {
|
|
return NULL;
|
|
}
|
|
|
|
ut32 j = 0;
|
|
rz_pvector_foreach (dex->class_defs, vit) {
|
|
class_def = (DexClassDef *)*vit;
|
|
class_ids[j] = class_def->class_idx;
|
|
j++;
|
|
}
|
|
|
|
libraries = rz_pvector_new((RzPVectorFree)free);
|
|
if (!libraries) {
|
|
free(class_ids);
|
|
return NULL;
|
|
}
|
|
|
|
rz_pvector_foreach (dex->method_ids, vit) {
|
|
method_id = (DexMethodId *)*vit;
|
|
bool class_found = false;
|
|
for (ut32 i = 0; i < n_classes; ++i) {
|
|
if (method_id->class_idx == class_ids[i]) {
|
|
class_found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (class_found) {
|
|
continue;
|
|
}
|
|
|
|
char *object = dex_resolve_type_id(dex, method_id->class_idx);
|
|
if (RZ_STR_ISEMPTY(object) || *object != 'L' || startswith(object, "Ljava/")) {
|
|
free(object);
|
|
continue;
|
|
}
|
|
|
|
char *p = object;
|
|
if ((p = strchr(p, '/')) && (p = strchr(p + 1, '/'))) {
|
|
*p = 0;
|
|
p = rz_str_newf("%s/*;", object);
|
|
free(object);
|
|
object = p;
|
|
}
|
|
|
|
if (rz_pvector_find(libraries, object, compare_strings, NULL)) {
|
|
free(object);
|
|
continue;
|
|
}
|
|
|
|
if (!rz_pvector_push(libraries, object)) {
|
|
free(object);
|
|
break;
|
|
}
|
|
}
|
|
|
|
free(class_ids);
|
|
return libraries;
|
|
}
|
|
|
|
static bool dex_resolve_symbol_in_class_methods(RzBinDex *dex, DexClassDef *class_def, RzBinSpecialSymbol resolve, ut64 *paddr, ut64 *vaddr) {
|
|
RzListIter *it;
|
|
DexEncodedMethod *encoded_method = NULL;
|
|
|
|
rz_list_foreach (class_def->direct_methods, it, encoded_method) {
|
|
if (encoded_method->method_idx >= rz_pvector_len(dex->method_ids)) {
|
|
RZ_LOG_INFO("cannot find virtual method with index %" PFMT64u "\n", encoded_method->method_idx);
|
|
continue;
|
|
}
|
|
DexMethodId *method_id = (DexMethodId *)rz_pvector_at(dex->method_ids, encoded_method->method_idx);
|
|
|
|
char *name = dex_resolve_string_id(dex, method_id->name_idx);
|
|
if (!name) {
|
|
continue;
|
|
}
|
|
if (resolve == RZ_BIN_SPECIAL_SYMBOL_ENTRY || resolve == RZ_BIN_SPECIAL_SYMBOL_INIT) {
|
|
if (strcmp(name, "<init>") != 0 && strcmp(name, "<clinit>") != 0) {
|
|
free(name);
|
|
continue;
|
|
}
|
|
} else if (resolve == RZ_BIN_SPECIAL_SYMBOL_MAIN) {
|
|
if (strcmp(name, "main") != 0) {
|
|
free(name);
|
|
continue;
|
|
}
|
|
}
|
|
free(name);
|
|
if (method_id->code_offset < RZ_DEX_RELOC_ADDRESS) {
|
|
*vaddr = RZ_DEX_VIRT_ADDRESS + encoded_method->code_offset;
|
|
*paddr = method_id->code_offset;
|
|
} else {
|
|
*vaddr = encoded_method->code_offset;
|
|
*paddr = 0;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
rz_list_foreach (class_def->virtual_methods, it, encoded_method) {
|
|
if (encoded_method->method_idx >= rz_pvector_len(dex->method_ids)) {
|
|
RZ_LOG_INFO("cannot find direct method with index %" PFMT64u "\n", encoded_method->method_idx);
|
|
continue;
|
|
}
|
|
DexMethodId *method_id = (DexMethodId *)rz_pvector_at(dex->method_ids, encoded_method->method_idx);
|
|
|
|
char *name = dex_resolve_string_id(dex, method_id->name_idx);
|
|
if (!name) {
|
|
continue;
|
|
}
|
|
if (resolve == RZ_BIN_SPECIAL_SYMBOL_ENTRY || resolve == RZ_BIN_SPECIAL_SYMBOL_INIT) {
|
|
if (strcmp(name, "<init>") != 0 && strcmp(name, "<clinit>") != 0) {
|
|
free(name);
|
|
continue;
|
|
}
|
|
} else if (resolve == RZ_BIN_SPECIAL_SYMBOL_MAIN) {
|
|
if (strcmp(name, "main") != 0) {
|
|
free(name);
|
|
continue;
|
|
}
|
|
}
|
|
free(name);
|
|
|
|
if (method_id->code_offset < RZ_DEX_RELOC_ADDRESS) {
|
|
*vaddr = RZ_DEX_VIRT_ADDRESS + encoded_method->code_offset;
|
|
*paddr = method_id->code_offset;
|
|
} else {
|
|
*vaddr = encoded_method->code_offset;
|
|
*paddr = 0;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns a RzBinAddr pointer containing the resolved RzBinSpecialSymbol
|
|
*/
|
|
RZ_API RZ_OWN RzBinAddr *rz_bin_dex_resolve_symbol(RZ_NONNULL RzBinDex *dex, RzBinSpecialSymbol resolve) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
|
|
DexClassDef *class_def;
|
|
void **it;
|
|
|
|
RzBinAddr *ret = RZ_NEW0(RzBinAddr);
|
|
if (!ret) {
|
|
return NULL;
|
|
}
|
|
ret->paddr = UT64_MAX;
|
|
ret->vaddr = UT64_MAX;
|
|
|
|
rz_pvector_foreach (dex->class_defs, it) {
|
|
class_def = (DexClassDef *)*it;
|
|
if (dex_resolve_symbol_in_class_methods(dex, class_def, resolve, &ret->paddr, &ret->vaddr)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static RzList /*<RzBinAddr *>*/ *dex_resolve_entrypoints_in_class(RzBinDex *dex, DexClassDef *class_def) {
|
|
RzListIter *it;
|
|
DexEncodedMethod *encoded_method = NULL;
|
|
RzList *entrypoints = NULL;
|
|
|
|
entrypoints = rz_list_newf((RzListFree)free);
|
|
if (!entrypoints) {
|
|
return NULL;
|
|
}
|
|
|
|
rz_list_foreach (class_def->direct_methods, it, encoded_method) {
|
|
if (!dex_is_static(encoded_method->access_flags)) {
|
|
// entrypoints are static
|
|
continue;
|
|
}
|
|
|
|
if (encoded_method->method_idx >= rz_pvector_len(dex->method_ids)) {
|
|
RZ_LOG_INFO("cannot find direct method with index %" PFMT64u "\n", encoded_method->method_idx);
|
|
continue;
|
|
}
|
|
DexMethodId *method_id = (DexMethodId *)rz_pvector_at(dex->method_ids, encoded_method->method_idx);
|
|
|
|
char *name = dex_resolve_string_id(dex, method_id->name_idx);
|
|
if (!name) {
|
|
continue;
|
|
}
|
|
|
|
if (strcmp(name, "main") != 0 && strcmp(name, "<init>") != 0 && strcmp(name, "<clinit>") != 0) {
|
|
free(name);
|
|
continue;
|
|
}
|
|
free(name);
|
|
|
|
RzBinAddr *entrypoint = RZ_NEW0(RzBinAddr);
|
|
if (!entrypoint) {
|
|
break;
|
|
}
|
|
if (encoded_method->code_offset < RZ_DEX_RELOC_ADDRESS) {
|
|
entrypoint->vaddr = RZ_DEX_VIRT_ADDRESS + encoded_method->code_offset;
|
|
entrypoint->paddr = encoded_method->code_offset;
|
|
} else {
|
|
entrypoint->vaddr = encoded_method->code_offset;
|
|
entrypoint->paddr = 0;
|
|
}
|
|
if (entrypoint && !rz_list_append(entrypoints, entrypoint)) {
|
|
free(entrypoint);
|
|
}
|
|
}
|
|
|
|
rz_list_foreach (class_def->virtual_methods, it, encoded_method) {
|
|
if (!dex_is_static(encoded_method->access_flags)) {
|
|
// entrypoints are static
|
|
continue;
|
|
}
|
|
|
|
if (encoded_method->method_idx >= rz_pvector_len(dex->method_ids)) {
|
|
RZ_LOG_INFO("cannot find virtual method with index %" PFMT64u "\n", encoded_method->method_idx);
|
|
continue;
|
|
}
|
|
|
|
DexMethodId *method_id = (DexMethodId *)rz_pvector_at(dex->method_ids, encoded_method->method_idx);
|
|
if (method_id->code_offset < 1) {
|
|
// if there is no code, skip
|
|
continue;
|
|
}
|
|
|
|
char *name = dex_resolve_string_id(dex, method_id->name_idx);
|
|
if (!name) {
|
|
continue;
|
|
}
|
|
|
|
if (strcmp(name, "main") != 0 && strcmp(name, "<init>") != 0 && strcmp(name, "<clinit>") != 0) {
|
|
free(name);
|
|
continue;
|
|
}
|
|
free(name);
|
|
|
|
RzBinAddr *entrypoint = RZ_NEW0(RzBinAddr);
|
|
if (!entrypoint) {
|
|
break;
|
|
}
|
|
if (encoded_method->code_offset < RZ_DEX_RELOC_ADDRESS) {
|
|
entrypoint->vaddr = RZ_DEX_VIRT_ADDRESS + encoded_method->code_offset;
|
|
entrypoint->paddr = encoded_method->code_offset;
|
|
} else {
|
|
entrypoint->vaddr = encoded_method->code_offset;
|
|
entrypoint->paddr = 0;
|
|
}
|
|
if (entrypoint && !rz_list_append(entrypoints, entrypoint)) {
|
|
free(entrypoint);
|
|
}
|
|
}
|
|
|
|
return entrypoints;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns a RzList<RzBinAddr*> containing the dex entripoints
|
|
*/
|
|
RZ_API RZ_OWN RzList /*<RzBinAddr *>*/ *rz_bin_dex_entrypoints(RZ_NONNULL RzBinDex *dex) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
|
|
DexClassDef *class_def;
|
|
RzList *list = NULL;
|
|
RzList *entrypoints = NULL;
|
|
void **it;
|
|
|
|
entrypoints = rz_list_newf((RzListFree)free);
|
|
if (!entrypoints) {
|
|
return NULL;
|
|
}
|
|
|
|
rz_pvector_foreach (dex->class_defs, it) {
|
|
class_def = (DexClassDef *)*it;
|
|
list = dex_resolve_entrypoints_in_class(dex, class_def);
|
|
if (list) {
|
|
rz_list_join(entrypoints, list);
|
|
rz_list_free(list);
|
|
}
|
|
}
|
|
|
|
return entrypoints;
|
|
}
|
|
|
|
/**
|
|
* This method will create a buffer filled with return-void values to mapped
|
|
* to the imports and allow the code to generate xrefs pointing to the imports
|
|
*/
|
|
RZ_API RZ_BORROW RzBuffer *rz_bin_dex_relocations(RZ_NONNULL RzBinDex *dex) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
return dex->relocs_buffer;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the resolved string linked to the given method id
|
|
*/
|
|
RZ_API RZ_OWN char *rz_bin_dex_resolve_method_by_idx(RZ_NONNULL RzBinDex *dex, ut32 method_idx) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
|
|
if (method_idx >= rz_pvector_len(dex->method_ids)) {
|
|
return NULL;
|
|
}
|
|
|
|
DexMethodId *method_id = (DexMethodId *)rz_pvector_at(dex->method_ids, method_idx);
|
|
char *name = dex_resolve_string_id(dex, method_id->name_idx);
|
|
if (!name) {
|
|
return NULL;
|
|
}
|
|
|
|
char *proto = dex_resolve_proto_id(dex, name, method_id->proto_idx, false);
|
|
RZ_FREE(name);
|
|
if (!proto) {
|
|
return NULL;
|
|
}
|
|
|
|
name = dex_resolve_type_id(dex, method_id->class_idx);
|
|
if (!name) {
|
|
free(proto);
|
|
return NULL;
|
|
}
|
|
|
|
char *method = rz_str_newf("%s->%s", name, proto);
|
|
free(name);
|
|
free(proto);
|
|
return method;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the resolved string linked to the given field id
|
|
*/
|
|
RZ_API RZ_OWN char *rz_bin_dex_resolve_field_by_idx(RZ_NONNULL RzBinDex *dex, ut32 field_idx) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
|
|
if (field_idx >= rz_pvector_len(dex->field_ids)) {
|
|
return NULL;
|
|
}
|
|
|
|
DexFieldId *field_id = (DexFieldId *)rz_pvector_at(dex->field_ids, field_idx);
|
|
char *class_name = dex_resolve_type_id(dex, field_id->class_idx);
|
|
if (!class_name) {
|
|
return NULL;
|
|
}
|
|
|
|
char *name = dex_resolve_string_id(dex, field_id->name_idx);
|
|
if (!name) {
|
|
free(class_name);
|
|
return NULL;
|
|
}
|
|
|
|
char *type = dex_resolve_type_id(dex, field_id->type_idx);
|
|
if (!type) {
|
|
free(class_name);
|
|
free(name);
|
|
return NULL;
|
|
}
|
|
|
|
char *method = rz_str_newf("%s->%s %s", class_name, name, type);
|
|
|
|
free(type);
|
|
free(class_name);
|
|
free(name);
|
|
return method;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the resolved offset linked to the given string id
|
|
*/
|
|
RZ_API ut64 rz_bin_dex_resolve_string_offset_by_idx(RZ_NONNULL RzBinDex *dex, ut32 string_idx) {
|
|
rz_return_val_if_fail(dex, UT64_MAX);
|
|
|
|
DexString *string = dex_resolve_string_id_native(dex, string_idx);
|
|
if (!string) {
|
|
RZ_LOG_INFO("cannot find string with index %u\n", string_idx);
|
|
return UT64_MAX;
|
|
}
|
|
return RZ_DEX_VIRT_ADDRESS + string->offset;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the resolved offset linked to the given type id
|
|
*/
|
|
RZ_API ut64 rz_bin_dex_resolve_type_id_offset_by_idx(RZ_NONNULL RzBinDex *dex, ut32 type_idx) {
|
|
rz_return_val_if_fail(dex, UT64_MAX);
|
|
|
|
if (type_idx >= dex->type_ids_size) {
|
|
RZ_LOG_INFO("cannot find type_id with index %u\n", type_idx);
|
|
return UT64_MAX;
|
|
}
|
|
DexTypeId type_id = dex->types[type_idx];
|
|
return rz_bin_dex_resolve_string_offset_by_idx(dex, type_id);
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the resolved offset linked to the given method id
|
|
*/
|
|
RZ_API ut64 rz_bin_dex_resolve_method_offset_by_idx(RZ_NONNULL RzBinDex *dex, ut32 method_idx) {
|
|
rz_return_val_if_fail(dex, UT64_MAX);
|
|
|
|
if (method_idx >= rz_pvector_len(dex->method_ids)) {
|
|
RZ_LOG_INFO("cannot find method with index %u\n", method_idx);
|
|
return UT64_MAX;
|
|
}
|
|
|
|
DexMethodId *method = (DexMethodId *)rz_pvector_at(dex->method_ids, method_idx);
|
|
if (method->code_offset) {
|
|
if (method->code_offset < RZ_DEX_RELOC_ADDRESS) {
|
|
return RZ_DEX_VIRT_ADDRESS + method->code_offset;
|
|
}
|
|
return method->code_offset;
|
|
}
|
|
return UT64_MAX;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the resolved string linked to the given type id
|
|
*/
|
|
RZ_API RZ_OWN char *rz_bin_dex_resolve_type_id_by_idx(RZ_NONNULL RzBinDex *dex, ut32 type_idx) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
|
|
if (type_idx >= dex->type_ids_size) {
|
|
RZ_LOG_INFO("cannot find type_id with index %u\n", type_idx);
|
|
return NULL;
|
|
}
|
|
DexTypeId type_id = dex->types[type_idx];
|
|
return rz_bin_dex_resolve_string_by_idx(dex, type_id);
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the resolved string linked to the given string id
|
|
*/
|
|
RZ_API RZ_OWN char *rz_bin_dex_resolve_string_by_idx(RZ_NONNULL RzBinDex *dex, ut32 string_idx) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
|
|
return dex_resolve_string_id(dex, string_idx);
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the resolved string linked to the given class id
|
|
*/
|
|
RZ_API RZ_OWN char *rz_bin_dex_resolve_class_by_idx(RZ_NONNULL RzBinDex *dex, ut32 class_idx) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
|
|
return dex_resolve_type_id(dex, class_idx);
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the resolved string linked to the given prototype id
|
|
*/
|
|
RZ_API RZ_OWN char *rz_bin_dex_resolve_proto_by_idx(RZ_NONNULL RzBinDex *dex, ut32 proto_idx) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
|
|
return dex_resolve_proto_id(dex, "", proto_idx, false);
|
|
}
|
|
|
|
/**
|
|
* \brief Sets the RzBinHash dex checksum (adler32)
|
|
*/
|
|
RZ_API void rz_bin_dex_checksum(RZ_NONNULL RzBinDex *dex, RZ_NONNULL RzBinHash *hash) {
|
|
rz_return_if_fail(dex && hash);
|
|
hash->type = "adler32";
|
|
hash->len = sizeof(dex->checksum);
|
|
hash->addr = dex->checksum_offset;
|
|
hash->from = dex->checksum_offset + sizeof(dex->checksum);
|
|
hash->to = dex->file_size - hash->from;
|
|
rz_write_le32(hash->buf, dex->checksum);
|
|
}
|
|
|
|
/**
|
|
* \brief Sets the RzBinHash dex digest (sha1)
|
|
*/
|
|
RZ_API void rz_bin_dex_sha1(RZ_NONNULL RzBinDex *dex, RZ_NONNULL RzBinHash *hash) {
|
|
rz_return_if_fail(dex && hash);
|
|
hash->type = "sha1";
|
|
hash->len = 20;
|
|
hash->addr = dex->signature_offset;
|
|
hash->from = dex->signature_offset + sizeof(dex->signature);
|
|
hash->to = dex->file_size - hash->from;
|
|
memcpy(hash->buf, dex->signature, sizeof(dex->signature));
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the dex version (string format)
|
|
*/
|
|
RZ_API RZ_OWN char *rz_bin_dex_version(RZ_NONNULL RzBinDex *dex) {
|
|
rz_return_val_if_fail(dex, NULL);
|
|
// https://cs.android.com/android/platform/superproject/+/master:dalvik/dx/src/com/android/dex/DexFormat.java;l=55;bpv=1;bpt=0
|
|
// https://developer.android.com/studio/releases/platforms
|
|
if (startswith((char *)dex->version, "009")) {
|
|
return strdup("Android M3 release (Nov-Dec 2007)");
|
|
} else if (startswith((char *)dex->version, "013")) {
|
|
return strdup("Android M5 release (Feb-Mar 2008)");
|
|
} else if (startswith((char *)dex->version, "035")) {
|
|
return strdup("Android 3.2 (API level 13 and earlier)");
|
|
} else if (startswith((char *)dex->version, "037")) {
|
|
return strdup("Android 7 (API level 24 and earlier)");
|
|
} else if (startswith((char *)dex->version, "038")) {
|
|
return strdup("Android 8 (API level 26 and earlier)");
|
|
} else if (startswith((char *)dex->version, "039")) {
|
|
return strdup("Android 9 (API level 28 and earlier)");
|
|
} else if (startswith((char *)dex->version, "040")) {
|
|
return strdup("Android 10+ (Aug 2019)");
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* \brief Returns the dex debug info RzBin values
|
|
*/
|
|
RZ_API ut64 rz_bin_dex_debug_info(RZ_NONNULL RzBinDex *dex) {
|
|
rz_return_val_if_fail(dex, 0);
|
|
// TODO
|
|
return 0;
|
|
}
|