rizin/librz/bin/format/java/class_bin.c

2037 lines
58 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 "class_bin.h"
#include "class_private.h"
#define startswith(a, b) (!strncmp(a, b, strlen(b)))
#define ACCESS_FLAG_MASK_SRC (ACCESS_FLAG_PUBLIC | ACCESS_FLAG_PRIVATE | ACCESS_FLAG_PROTECTED | ACCESS_FLAG_STATIC | ACCESS_FLAG_FINAL)
#define CLASS_ACCESS_FLAGS_SIZE 16
static const AccessFlagsReadable 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_SUPER, /* */ "super" },
{ 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" },
};
static ut64 java_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_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;
}
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;
}
return flags;
}
static const ConstPool *java_class_constant_pool_at(RzBinJavaClass *bin, ut32 index) {
if (bin->constant_pool && index < bin->constant_pool_count) {
return bin->constant_pool[index];
}
return NULL;
}
static char *java_class_constant_pool_stringify_at(RzBinJavaClass *bin, ut32 index) {
const ConstPool *cpool = java_class_constant_pool_at(bin, index);
if (!cpool) {
return NULL;
}
return java_constant_pool_stringify(cpool);
}
static ut32 sanitize_size(st64 buffer_size, ut64 count, ut32 min_struct_size) {
ut64 memory_size = count * min_struct_size;
return memory_size <= buffer_size ? count : 0;
}
static bool java_class_is_oak(RzBinJavaClass *bin) {
return bin->major_version < (45) || (bin->major_version == 45 && bin->minor_version < 3);
}
static bool is_eob(RzBuffer *buf) {
st64 size = rz_buf_size(buf);
st64 position = rz_buf_tell(buf);
return position >= size;
}
static bool java_class_parse(RzBinJavaClass *bin, ut64 base, Sdb *kv, RzBuffer *buf, ut64 *size) {
// https://docs.oracle.com/javase/specs/jvms/se15/html/jvms-4.html#jvms-4.1
ut64 offset = 0;
st64 buffer_size = rz_buf_size(buf);
if (buffer_size < 1) {
RZ_LOG_ERROR("java bin: invalid buffer size (size < 1)\n");
goto java_class_parse_bad;
}
if (!rz_buf_read_be32(buf, &bin->magic) ||
!rz_buf_read_be16(buf, &bin->minor_version) ||
!rz_buf_read_be16(buf, &bin->major_version) ||
!rz_buf_read_be16(buf, &bin->constant_pool_count)) {
goto java_class_parse_bad;
}
// Before version 1.0.2 it was called oak
// which uses a different file structure.
bool is_oak = java_class_is_oak(bin);
bin->constant_pool_count = sanitize_size(buffer_size - rz_buf_tell(buf), bin->constant_pool_count, 3);
bin->constant_pool_offset = base + rz_buf_tell(buf);
if (bin->constant_pool_count > 0) {
bin->constant_pool = RZ_NEWS0(ConstPool *, bin->constant_pool_count);
if (!bin->constant_pool) {
goto java_class_parse_bad;
}
for (ut32 i = 1; i < bin->constant_pool_count; ++i) {
offset = rz_buf_tell(buf) + base;
ConstPool *cpool = java_constant_pool_new(buf, offset);
if (!cpool) {
RZ_LOG_ERROR("java bin: could not parse the constant pool value at offset %" PFMT64x "\n", offset);
break;
}
bin->constant_pool[i] = cpool;
if (java_constant_pool_requires_null(cpool)) {
if (i >= (bin->constant_pool_count - 1)) {
break;
}
i++;
bin->constant_pool[i] = java_constant_null_new(offset);
}
}
if (is_eob(buf)) {
rz_warn_if_reached();
goto java_class_parse_bad;
}
}
if (!rz_buf_read_be16(buf, &bin->access_flags) ||
!rz_buf_read_be16(buf, &bin->this_class) ||
!rz_buf_read_be16(buf, &bin->super_class) ||
!rz_buf_read_be16(buf, &bin->interfaces_count)) {
goto java_class_parse_bad;
}
bin->interfaces_count = sanitize_size(buffer_size - rz_buf_tell(buf), bin->interfaces_count, 2);
bin->interfaces_offset = base + rz_buf_tell(buf);
if (bin->interfaces_count > 0) {
bin->interfaces = RZ_NEWS0(Interface *, bin->interfaces_count);
if (!bin->interfaces) {
goto java_class_parse_bad;
}
for (ut32 i = 0; i < bin->interfaces_count; ++i) {
offset = rz_buf_tell(buf) + base;
bin->interfaces[i] = java_interface_new(buf, offset);
}
if (is_eob(buf)) {
rz_warn_if_reached();
goto java_class_parse_bad;
}
}
if (!rz_buf_read_be16(buf, &bin->fields_count)) {
goto java_class_parse_bad;
}
bin->fields_count = sanitize_size(buffer_size - rz_buf_tell(buf), bin->fields_count, 8);
bin->fields_offset = base + rz_buf_tell(buf);
if (bin->fields_count > 0) {
bin->fields = RZ_NEWS0(Field *, bin->fields_count);
if (!bin->fields) {
goto java_class_parse_bad;
}
for (ut32 i = 0; i < bin->fields_count; ++i) {
offset = rz_buf_tell(buf) + base;
bin->fields[i] = java_field_new(bin->constant_pool,
bin->constant_pool_count, buf, offset);
}
if (is_eob(buf)) {
rz_warn_if_reached();
goto java_class_parse_bad;
}
}
if (!rz_buf_read_be16(buf, &bin->methods_count)) {
goto java_class_parse_bad;
}
bin->methods_count = sanitize_size(buffer_size - rz_buf_tell(buf), bin->methods_count, 8);
bin->methods_offset = base + rz_buf_tell(buf);
if (bin->methods_count > 0) {
bin->methods = RZ_NEWS0(Method *, bin->methods_count);
if (!bin->methods) {
goto java_class_parse_bad;
}
for (ut32 i = 0; i < bin->methods_count; ++i) {
offset = rz_buf_tell(buf) + base;
bin->methods[i] = java_method_new(bin->constant_pool,
bin->constant_pool_count, buf, offset, is_oak);
}
if (is_eob(buf)) {
rz_warn_if_reached();
goto java_class_parse_bad;
}
}
if (!rz_buf_read_be16(buf, &bin->attributes_count)) {
goto java_class_parse_bad;
}
bin->attributes_count = sanitize_size(buffer_size - rz_buf_tell(buf), bin->attributes_count, 6);
bin->attributes_offset = base + rz_buf_tell(buf);
if (bin->attributes_count > 0) {
bin->attributes = RZ_NEWS0(Attribute *, bin->attributes_count);
if (!bin->attributes) {
goto java_class_parse_bad;
}
for (ut32 i = 0; i < bin->attributes_count; ++i) {
offset = rz_buf_tell(buf) + base;
Attribute *attr = java_attribute_new(buf, offset);
if (attr && java_attribute_resolve(bin->constant_pool, bin->constant_pool_count, attr, buf, false)) {
bin->attributes[i] = attr;
} else {
java_attribute_free(attr);
break;
}
}
}
bin->class_end_offset = base + rz_buf_tell(buf);
if (size) {
*size = rz_buf_tell(buf);
}
return true;
java_class_parse_bad:
rz_bin_java_class_free(bin);
return false;
}
static void java_set_sdb(Sdb *kv, RzBinJavaClass *bin, ut64 offset, ut64 size) {
char *tmp_val;
char tmp_key[256];
sdb_num_set(kv, "java_class.offset", offset, 0);
sdb_num_set(kv, "java_class.size", size, 0);
sdb_num_set(kv, "java_class.magic", size, 0);
sdb_num_set(kv, "java_class.minor_version", size, 0);
sdb_num_set(kv, "java_class.major_version", size, 0);
tmp_val = rz_bin_java_class_version(bin);
if (tmp_val) {
sdb_set(kv, "java_class.version", tmp_val, 0);
free(tmp_val);
}
sdb_num_set(kv, "java_class.constant_pool_count", bin->constant_pool_count, 0);
for (ut32 i = 0; i < bin->constant_pool_count; ++i) {
ConstPool *cpool = bin->constant_pool[i];
if (!cpool) {
continue;
}
tmp_val = java_constant_pool_stringify(cpool);
if (tmp_val) {
snprintf(tmp_key, sizeof(tmp_key), "java_class.constant_pool_%d", i);
sdb_set(kv, tmp_key, tmp_val, 0);
free(tmp_val);
}
}
sdb_num_set(kv, "java_class.fields_count", bin->fields_count, 0);
sdb_num_set(kv, "java_class.methods_count", bin->methods_count, 0);
sdb_num_set(kv, "java_class.attributes_count", bin->attributes_count, 0);
}
/**
* \brief Parses the java class file and returns a RzBinJavaClass struct
*/
RZ_API RZ_OWN RzBinJavaClass *rz_bin_java_class_new(RZ_NONNULL RzBuffer *buf, ut64 offset, RZ_NONNULL Sdb *kv) {
RzBinJavaClass *bin = (RzBinJavaClass *)RZ_NEW0(RzBinJavaClass);
rz_return_val_if_fail(bin, NULL);
ut64 size;
if (!java_class_parse(bin, offset, kv, buf, &size)) {
return NULL;
}
java_set_sdb(kv, bin, offset, size);
return bin;
}
/**
* \brief Parses the java class file and returns a RzBinJavaClass struct
*/
RZ_API RZ_OWN char *rz_bin_java_class_version(RZ_NONNULL RzBinJavaClass *bin) {
if (!bin) {
return NULL;
}
#define is_version(bin, major, minor) ((bin)->major_version == (major) && (bin)->minor_version >= (minor))
if (bin->major_version < 45 ||
(bin->major_version == 45 && bin->minor_version < 3)) {
return strdup("Java SE 1.0.2"); // old format
} else if (is_version(bin, 45, 3)) {
return strdup("Java SE 1.1");
} else if (is_version(bin, 46, 0)) {
return strdup("Java SE 1.2");
} else if (is_version(bin, 47, 0)) {
return strdup("Java SE 1.3");
} else if (is_version(bin, 48, 0)) {
return strdup("Java SE 1.4");
} else if (is_version(bin, 49, 0)) {
return strdup("Java SE 1.5"); // enum, generics, annotations
} else if (is_version(bin, 50, 0)) {
return strdup("Java SE 1.6"); // stackmaps
} else if (is_version(bin, 51, 0)) {
return strdup("Java SE 1.7");
} else if (is_version(bin, 52, 0)) {
return strdup("Java SE 1.8"); // lambda, type annos, param names
} else if (is_version(bin, 53, 0)) {
return strdup("Java SE 1.9"); // modules, indy string concat
} else if (is_version(bin, 54, 0)) {
return strdup("Java SE 10");
} else if (is_version(bin, 55, 0)) {
return strdup("Java SE 11"); // constant dynamic, nest mates
} else if (is_version(bin, 56, 0)) {
return strdup("Java SE 12");
} else if (is_version(bin, 57, 0)) {
return strdup("Java SE 13");
} else if (is_version(bin, 58, 0)) {
return strdup("Java SE 14");
} else if (is_version(bin, 59, 0)) {
return strdup("Java SE 15");
} else if (is_version(bin, 60, 0)) {
return strdup("Java SE 16");
}
#undef is_version
return strdup("unknown");
}
RZ_API ut64 rz_bin_java_class_debug_info(RZ_NONNULL RzBinJavaClass *bin) {
if (!bin) {
return 0;
}
if (bin->methods) {
for (ut32 i = 0; i < bin->methods_count; ++i) {
Method *method = bin->methods[i];
if (!method || method->attributes_count < 1) {
continue;
}
for (ut32 k = 0; k < method->attributes_count; ++k) {
Attribute *attr = method->attributes[k];
if (attr && attr->type == ATTRIBUTE_TYPE_CODE) {
AttributeCode *ac = (AttributeCode *)attr->info;
for (ut32 k = 0; k < ac->attributes_count; k++) {
Attribute *cattr = ac->attributes[k];
if (cattr && cattr->type == ATTRIBUTE_TYPE_LINENUMBERTABLE) {
return RZ_BIN_DBG_LINENUMS | RZ_BIN_DBG_SYMS;
}
}
}
}
}
}
return RZ_BIN_DBG_SYMS;
}
RZ_API RZ_BORROW const char *rz_bin_java_class_language(RZ_NONNULL RzBinJavaClass *bin) {
rz_return_val_if_fail(bin, NULL);
const char *language = "java";
char *string = NULL;
if (bin->constant_pool) {
for (ut32 i = 0; i < bin->constant_pool_count; ++i) {
const ConstPool *cpool = bin->constant_pool[i];
if (!cpool || !java_constant_pool_is_string(cpool)) {
continue;
}
string = java_constant_pool_stringify(cpool);
if (string && startswith(string, "kotlin/jvm")) {
language = "kotlin";
break;
} else if (string && startswith(string, "org/codehaus/groovy/runtime")) {
language = "groovy";
break;
}
free(string);
string = NULL;
}
}
free(string);
return language;
}
/**
* \brief Frees a RzBinJavaClass pointer
*/
RZ_API void rz_bin_java_class_free(RZ_NULLABLE RzBinJavaClass *bin) {
if (!bin) {
return;
}
if (bin->constant_pool) {
for (ut32 i = 0; i < bin->constant_pool_count; ++i) {
java_constant_pool_free(bin->constant_pool[i]);
}
free(bin->constant_pool);
}
if (bin->interfaces) {
for (ut32 i = 0; i < bin->interfaces_count; ++i) {
java_interface_free(bin->interfaces[i]);
}
free(bin->interfaces);
}
if (bin->fields) {
for (ut32 i = 0; i < bin->fields_count; ++i) {
java_field_free(bin->fields[i]);
}
free(bin->fields);
}
if (bin->methods) {
for (ut32 i = 0; i < bin->methods_count; ++i) {
java_method_free(bin->methods[i]);
}
free(bin->methods);
}
if (bin->attributes) {
for (ut32 i = 0; i < bin->attributes_count; ++i) {
java_attribute_free(bin->attributes[i]);
}
free(bin->attributes);
}
free(bin);
}
/**
* \brief Returns the class name
*/
RZ_API RZ_OWN char *rz_bin_java_class_name(RZ_NONNULL RzBinJavaClass *bin) {
ut16 index;
rz_return_val_if_fail(bin, NULL);
const ConstPool *cpool = java_class_constant_pool_at(bin, bin->this_class);
if (!cpool || java_constant_pool_resolve(cpool, &index, NULL) != 1) {
RZ_LOG_ERROR("java bin: unknown class name at constant pool index %u\n", bin->this_class);
return strdup("unknown_class");
}
char *tmp = java_class_constant_pool_stringify_at(bin, index);
char *class_name = rz_str_newf("L%s;", tmp);
free(tmp);
return class_name;
}
/**
* \brief Returns the class super name
*/
RZ_API RZ_OWN char *rz_bin_java_class_super(RZ_NONNULL RzBinJavaClass *bin) {
ut16 index;
rz_return_val_if_fail(bin, NULL);
const ConstPool *cpool = java_class_constant_pool_at(bin, bin->super_class);
if (!cpool || java_constant_pool_resolve(cpool, &index, NULL) != 1) {
RZ_LOG_ERROR("java bin: unknown super name at constant pool index %u\n", bin->this_class);
return strdup("unknown_super");
}
char *tmp = java_class_constant_pool_stringify_at(bin, index);
if (!tmp) {
return NULL;
}
char *class_name = rz_str_newf("L%s;", tmp);
free(tmp);
return class_name;
}
RZ_API ut32 rz_bin_java_class_access_flags(RZ_NONNULL RzBinJavaClass *bin) {
rz_return_val_if_fail(bin, 0xffffffff);
return bin->access_flags;
}
/**
* \brief Returns the readable class access flags
*/
RZ_API RZ_OWN char *rz_bin_java_class_access_flags_readable(RZ_NONNULL RzBinJavaClass *bin, ut16 mask) {
rz_return_val_if_fail(bin, NULL);
RzStrBuf *sb = NULL;
ut16 access_flags = bin->access_flags & mask;
for (ut32 i = 0; i < CLASS_ACCESS_FLAGS_SIZE; ++i) {
const AccessFlagsReadable *afr = &access_flags_list[i];
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;
}
static int calculate_padding_ut16(ut16 count) {
if (count > 9999) {
return 5;
} else if (count > 999) {
return 4;
} else if (count > 99) {
return 3;
}
return 2;
}
/**
* \brief Returns the class info as json
*/
RZ_API void rz_bin_java_class_as_json(RZ_NONNULL RzBinJavaClass *bin, RZ_NONNULL PJ *j) {
rz_return_if_fail(bin && j);
char *tmp = NULL;
pj_o(j);
pj_ko(j, "version");
{
pj_kn(j, "minor", bin->minor_version);
pj_kn(j, "major", bin->major_version);
tmp = rz_bin_java_class_version(bin);
pj_ks(j, "version", tmp ? tmp : "");
free(tmp);
}
pj_end(j);
pj_kn(j, "constant_pool_count", bin->constant_pool_count);
pj_k(j, "constant_pool");
rz_bin_java_class_const_pool_as_json(bin, j);
pj_kn(j, "access_flags_n", bin->access_flags);
tmp = rz_bin_java_class_access_flags_readable(bin, ACCESS_FLAG_MASK_ALL);
pj_ks(j, "access_flags_s", tmp ? tmp : "");
free(tmp);
pj_kn(j, "class_n", bin->this_class);
tmp = rz_bin_java_class_name(bin);
pj_ks(j, "class_s", tmp ? tmp : "");
free(tmp);
pj_kn(j, "super_n", bin->super_class);
tmp = rz_bin_java_class_super(bin);
pj_ks(j, "super_s", tmp ? tmp : "");
free(tmp);
pj_kn(j, "interfaces_count", bin->interfaces_count);
pj_k(j, "interfaces");
rz_bin_java_class_interfaces_as_json(bin, j);
pj_kn(j, "methods_count", bin->methods_count);
pj_k(j, "methods");
rz_bin_java_class_methods_as_json(bin, j);
pj_kn(j, "fields_count", bin->fields_count);
pj_k(j, "fields");
rz_bin_java_class_fields_as_json(bin, j);
pj_kn(j, "attributes_count", bin->attributes_count);
pj_ka(j, "attributes");
for (ut32 i = 0; i < bin->attributes_count; ++i) {
Attribute *attr = bin->attributes[i];
if (!attr) {
continue;
}
pj_o(j);
pj_kn(j, "offset", attr->offset);
pj_kn(j, "size", attr->attribute_length);
pj_kn(j, "name_n", attr->attribute_name_index);
tmp = java_class_constant_pool_stringify_at(bin, attr->attribute_name_index);
pj_ks(j, "name_s", tmp ? tmp : "");
free(tmp);
pj_end(j);
}
pj_end(j);
pj_end(j);
}
/**
* \brief Returns the class info as text
*/
RZ_API void rz_bin_java_class_as_text(RZ_NONNULL RzBinJavaClass *bin, RZ_NONNULL RzStrBuf *sb) {
rz_return_if_fail(bin && sb);
char number[16];
char *tmp = NULL;
int padding;
tmp = rz_bin_java_class_version(bin);
rz_strbuf_appendf(sb, "Version: (%u.%u) %s\n", bin->major_version, bin->minor_version, tmp);
free(tmp);
tmp = rz_bin_java_class_access_flags_readable(bin, ACCESS_FLAG_MASK_ALL);
rz_strbuf_appendf(sb, "Flags: (0x%04x) %s\n", bin->access_flags, tmp);
free(tmp);
tmp = rz_bin_java_class_name(bin);
rz_strbuf_appendf(sb, "Class: (#%u) %s\n", bin->this_class, tmp);
free(tmp);
tmp = rz_bin_java_class_super(bin);
rz_strbuf_appendf(sb, "Super: (#%u) %s\n", bin->super_class, tmp);
free(tmp);
rz_bin_java_class_const_pool_as_text(bin, sb);
rz_bin_java_class_interfaces_as_text(bin, sb);
rz_bin_java_class_methods_as_text(bin, sb);
rz_bin_java_class_fields_as_text(bin, sb);
rz_strbuf_appendf(sb, "Attributes: %u\n", bin->attributes_count);
padding = calculate_padding_ut16(bin->attributes_count) + 1;
for (ut32 i = 0; i < bin->attributes_count; ++i) {
Attribute *attr = bin->attributes[i];
if (!attr) {
continue;
}
snprintf(number, sizeof(number), "#%u", i);
tmp = java_class_constant_pool_stringify_at(bin, attr->attribute_name_index);
rz_strbuf_appendf(sb, " %-*s = #%-5u size: %-5u %s\n", padding, number, attr->attribute_name_index, attr->attribute_length, tmp);
free(tmp);
}
}
static inline bool is_dual_index(const ConstPool *cpool) {
return cpool->tag == CONSTANT_POOL_FIELDREF ||
cpool->tag == CONSTANT_POOL_METHODREF ||
cpool->tag == CONSTANT_POOL_INTERFACEMETHODREF ||
cpool->tag == CONSTANT_POOL_NAMEANDTYPE ||
cpool->tag == CONSTANT_POOL_DYNAMIC ||
cpool->tag == CONSTANT_POOL_INVOKEDYNAMIC;
}
/**
* \brief Returns the string linked to the class const pool index
*/
RZ_API RZ_OWN char *rz_bin_java_class_const_pool_resolve_index(RZ_NONNULL RzBinJavaClass *bin, st32 index) {
rz_return_val_if_fail(bin && index >= 0, NULL);
ut16 arg0, arg1;
char *tmp;
const ConstPool *cpool = java_class_constant_pool_at(bin, index);
if (!cpool || !index) {
return NULL;
}
if (java_constant_pool_is_string(cpool) ||
java_constant_pool_is_number(cpool)) {
return java_constant_pool_stringify(cpool);
} else if (cpool->tag == CONSTANT_POOL_CLASS) {
if (java_constant_pool_resolve(cpool, &arg0, NULL) != 1) {
RZ_LOG_ERROR("java bin: can't resolve constant pool index %u\n", index);
return NULL;
}
tmp = rz_bin_java_class_const_pool_resolve_index(bin, arg0);
if (tmp[0] == '[' && tmp[1] == 'L') {
return tmp;
}
char *res = rz_str_newf("L%s;", tmp);
free(tmp);
return res;
} else if (cpool->tag == CONSTANT_POOL_STRING) {
if (java_constant_pool_resolve(cpool, &arg0, NULL) != 1) {
RZ_LOG_ERROR("java bin: can't resolve constant pool index %u\n", index);
return NULL;
}
char *s0 = rz_bin_java_class_const_pool_resolve_index(bin, arg0);
tmp = rz_str_newf("\"%s\"", s0);
free(s0);
return tmp;
} else if (is_dual_index(cpool)) {
if (java_constant_pool_resolve(cpool, &arg0, &arg1) != 2) {
RZ_LOG_ERROR("java bin: can't resolve constant pool index %u\n", index);
return NULL;
}
char *s0 = arg0 ? rz_bin_java_class_const_pool_resolve_index(bin, arg0) : NULL;
char *s1 = rz_bin_java_class_const_pool_resolve_index(bin, arg1);
if ((arg0 && !s0) || !s1) {
RZ_LOG_ERROR("java bin: can't resolve constant pool index %u\n", index);
free(s0);
free(s1);
return NULL;
}
if (!arg0) {
return s1;
}
if (s1[0] == '(') {
tmp = rz_str_newf("%s%s", s0, s1);
} else {
tmp = rz_str_newf("%s.%s", s0, s1);
}
free(s0);
free(s1);
return tmp;
}
return NULL;
}
/**
* \brief Returns the class info as text source code
*/
RZ_API void rz_bin_java_class_as_source_code(RZ_NONNULL RzBinJavaClass *bin, RZ_NONNULL RzStrBuf *sb) {
rz_return_if_fail(bin && sb);
char *dem = NULL;
char *tmp = NULL;
ut16 index;
void **iter;
RzPVector *vec = rz_bin_java_class_as_libraries(bin);
rz_pvector_foreach (vec, iter) {
tmp = *iter;
rz_str_replace_char(tmp, '/', '.');
rz_strbuf_appendf(sb, "import %s;\n", tmp);
}
if (rz_pvector_len(vec) > 0) {
rz_strbuf_appendf(sb, "\n");
}
rz_pvector_free(vec);
rz_strbuf_append(sb, "class");
tmp = rz_bin_java_class_access_flags_readable(bin, ACCESS_FLAG_MASK_SRC);
if (tmp) {
rz_strbuf_appendf(sb, " %s", tmp);
free(tmp);
}
tmp = rz_bin_java_class_name(bin);
dem = rz_demangler_java(tmp, RZ_DEMANGLER_FLAG_ENABLE_ALL);
if (dem) {
rz_strbuf_appendf(sb, " %s", dem);
RZ_FREE(dem);
} else {
rz_strbuf_appendf(sb, " %s", tmp);
}
free(tmp);
if (bin->access_flags & ACCESS_FLAG_SUPER) {
tmp = rz_bin_java_class_super(bin);
if (strcmp(tmp, "Ljava/lang/Object;") != 0) {
rz_str_replace_char(tmp, '/', '.');
rz_strbuf_appendf(sb, " extends %s", tmp);
}
free(tmp);
}
if (bin->interfaces_count > 0) {
rz_strbuf_append(sb, " implements ");
ut32 k = 0;
for (ut32 i = 0; i < bin->interfaces_count; ++i) {
if (!bin->interfaces[i]) {
continue;
}
const ConstPool *cpool = java_class_constant_pool_at(bin, bin->interfaces[i]->index);
if (!cpool || java_constant_pool_resolve(cpool, &index, NULL) != 1) {
RZ_LOG_ERROR("java bin: can't resolve constant pool index %u\n", bin->interfaces[i]->index);
break;
}
tmp = java_class_constant_pool_stringify_at(bin, index);
if ((dem = rz_demangler_java(tmp, RZ_DEMANGLER_FLAG_ENABLE_ALL))) {
free(tmp);
tmp = dem;
}
if (k > 0) {
rz_strbuf_appendf(sb, ", %s", tmp);
} else {
rz_strbuf_append(sb, tmp);
}
free(tmp);
k++;
}
if (k < 1) {
rz_strbuf_append(sb, "?");
}
}
rz_strbuf_append(sb, " {\n");
if (bin->methods) {
for (ut32 i = 0; i < bin->methods_count; ++i) {
const Method *method = bin->methods[i];
if (!method) {
continue;
}
rz_strbuf_append(sb, " ");
tmp = java_method_access_flags_readable(method);
if (tmp) {
rz_strbuf_appendf(sb, "%s ", tmp);
free(tmp);
}
char *name = java_class_constant_pool_stringify_at(bin, method->name_index);
if (!name) {
name = strdup("?");
}
char *desc = java_class_constant_pool_stringify_at(bin, method->descriptor_index);
if (!desc) {
desc = strdup("(?)V");
}
if (desc[0] == '(') {
tmp = rz_str_newf("%s%s", name, desc);
} else {
tmp = strdup(name);
}
free(desc);
free(name);
dem = rz_demangler_java(tmp, RZ_DEMANGLER_FLAG_ENABLE_ALL);
if (!dem) {
rz_strbuf_append(sb, tmp);
} else {
rz_strbuf_append(sb, dem);
RZ_FREE(dem);
}
free(tmp);
rz_strbuf_append(sb, ";\n");
}
}
if (bin->methods_count > 0 && bin->fields_count) {
rz_strbuf_append(sb, "\n");
}
if (bin->fields) {
for (ut32 i = 0; i < bin->fields_count; ++i) {
const Field *field = bin->fields[i];
if (!field) {
continue;
}
rz_strbuf_append(sb, " ");
tmp = java_field_access_flags_readable(field);
if (tmp) {
rz_strbuf_appendf(sb, "%s ", tmp);
free(tmp);
}
tmp = java_class_constant_pool_stringify_at(bin, field->name_index);
if (tmp) {
rz_str_replace_char(tmp, '/', '.');
rz_strbuf_appendf(sb, "%s ", tmp);
free(tmp);
}
tmp = java_class_constant_pool_stringify_at(bin, field->descriptor_index);
if (tmp) {
rz_str_replace_char(tmp, '/', '.');
rz_strbuf_append(sb, tmp);
free(tmp);
}
rz_strbuf_append(sb, "\n");
}
}
rz_strbuf_append(sb, "}\n");
}
/**
* \brief Resolves and returns the RzBinAddr struct linked to the input RzBinSpecialSymbol
*/
RZ_API RZ_OWN RzBinAddr *rz_bin_java_class_resolve_symbol(RZ_NONNULL RzBinJavaClass *bin, RzBinSpecialSymbol resolve) {
rz_return_val_if_fail(bin, NULL);
RzBinAddr *ret = RZ_NEW0(RzBinAddr);
if (!ret) {
return NULL;
}
ret->paddr = UT64_MAX;
char *name = NULL;
if (bin->methods) {
for (ut32 i = 0; i < bin->methods_count; ++i) {
const Method *method = bin->methods[i];
if (!method) {
continue;
}
name = java_class_constant_pool_stringify_at(bin, method->name_index);
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);
ut64 addr = UT64_MAX;
for (ut32 i = 0; i < method->attributes_count; ++i) {
Attribute *attr = method->attributes[i];
if (attr && attr->type == ATTRIBUTE_TYPE_CODE) {
AttributeCode *ac = (AttributeCode *)attr->info;
addr = ac->code_offset;
break;
}
}
if (addr == UT64_MAX) {
RZ_LOG_ERROR("java bin: can't resolve symbol address\n");
continue;
}
ret->paddr = addr;
break;
}
}
return ret;
}
/**
* \brief Returns a RzPVector<RzBinAddr*> containing the entrypoints
*/
RZ_API RZ_OWN RzPVector /*<RzBinAddr *>*/ *rz_bin_java_class_entrypoints(RZ_NONNULL RzBinJavaClass *bin) {
rz_return_val_if_fail(bin, NULL);
RzPVector *vec = rz_pvector_new(free);
if (!vec) {
return NULL;
}
char *name = NULL;
bool is_static;
if (bin->methods) {
for (ut32 i = 0; i < bin->methods_count; ++i) {
const Method *method = bin->methods[i];
if (!method) {
rz_warn_if_reached();
continue;
}
is_static = method->access_flags & METHOD_ACCESS_FLAG_STATIC;
if (!is_static) {
name = java_class_constant_pool_stringify_at(bin, method->name_index);
if (!name) {
continue;
}
if (strcmp(name, "main") != 0 && strcmp(name, "<init>") != 0 && strcmp(name, "<clinit>") != 0) {
free(name);
continue;
}
free(name);
}
ut64 addr = UT64_MAX;
for (ut32 i = 0; i < method->attributes_count; ++i) {
Attribute *attr = method->attributes[i];
if (attr && attr->type == ATTRIBUTE_TYPE_CODE) {
AttributeCode *ac = (AttributeCode *)attr->info;
addr = ac->code_offset;
break;
}
}
if (addr == UT64_MAX) {
RZ_LOG_ERROR("java bin: can't resolve entrypoint address\n");
continue;
}
RzBinAddr *entrypoint = RZ_NEW0(RzBinAddr);
if (!entrypoint) {
rz_warn_if_reached();
continue;
}
entrypoint->vaddr = entrypoint->paddr = addr;
rz_pvector_push(vec, entrypoint);
}
}
return vec;
}
/**
* \brief Returns a RzList<RzBinString*> containing the strings
*/
RZ_API RZ_OWN RzPVector /*<RzBinString *>*/ *rz_bin_java_class_strings(RZ_NONNULL RzBinJavaClass *bin) {
rz_return_val_if_fail(bin, NULL);
RzPVector *vec = rz_pvector_new((RzPVectorFree)rz_bin_string_free);
if (!vec) {
return NULL;
}
char *string;
if (bin->constant_pool_count > 0) {
for (ut32 i = 1; i < bin->constant_pool_count; ++i) {
const ConstPool *cpool = bin->constant_pool[i];
if (!cpool || !java_constant_pool_is_string(cpool) || cpool->size < 1) {
continue;
}
string = java_constant_pool_stringify(cpool);
if (!string) {
RZ_LOG_ERROR("java bin: expecting a string, got NULL\n");
continue;
}
RzBinString *bstr = RZ_NEW0(RzBinString);
if (!bstr) {
free(string);
rz_warn_if_reached();
continue;
}
bstr->paddr = cpool->offset;
bstr->ordinal = i;
bstr->length = cpool->size;
bstr->size = cpool->size;
bstr->string = string;
bstr->type = RZ_STRING_ENC_MUTF8;
rz_pvector_push(vec, bstr);
}
}
for (ut32 i = 0; i < bin->attributes_count; ++i) {
Attribute *attr = bin->attributes[i];
if (attr && attr->type == ATTRIBUTE_TYPE_SOURCEDEBUGEXTENSION) {
RzBinString *bstr = RZ_NEW0(RzBinString);
if (!bstr) {
rz_warn_if_reached();
continue;
}
bstr->paddr = attr->offset;
bstr->ordinal = i;
bstr->length = attr->attribute_length;
bstr->size = attr->attribute_length;
bstr->string = strdup(attr->info);
bstr->type = RZ_STRING_ENC_UTF8;
rz_pvector_push(vec, bstr);
}
}
return vec;
}
static char *add_class_name_to_name(char *name, char *classname) {
if (classname) {
return rz_str_newf("%s.%s", classname, name);
}
return strdup(name);
}
static char *demangle_java_and_free(char *mangled) {
if (!mangled) {
return NULL;
}
if (startswith(mangled, "unknown_")) {
return mangled;
}
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 = rz_str_newf("java.lang.%s", object);
} else {
*out_lib = strdup(object);
}
}
/**
* \brief Returns a RzList<RzBinSymbol*> containing the class methods
*/
RZ_API RZ_OWN RzList /*<RzBinSymbol *>*/ *rz_bin_java_class_methods_as_symbols(RZ_NONNULL RzBinJavaClass *bin) {
rz_return_val_if_fail(bin, NULL);
RzList *list = rz_list_newf((RzListFree)rz_bin_symbol_free);
if (!list) {
return NULL;
}
char *method_name = NULL;
if (bin->methods) {
for (ut32 i = 0; i < bin->methods_count; ++i) {
const Method *method = bin->methods[i];
if (!method) {
rz_warn_if_reached();
continue;
}
const ConstPool *cpool = java_class_constant_pool_at(bin, method->name_index);
if (!cpool || !java_constant_pool_is_string(cpool)) {
RZ_LOG_ERROR("java bin: can't resolve method with constant pool index %u\n", method->name_index);
continue;
}
method_name = java_constant_pool_stringify(cpool);
if (!method_name) {
continue;
}
ut64 size = 0;
ut64 addr = UT64_MAX;
for (ut32 i = 0; i < method->attributes_count; ++i) {
Attribute *attr = method->attributes[i];
if (attr && attr->type == ATTRIBUTE_TYPE_CODE) {
AttributeCode *ac = (AttributeCode *)attr->info;
addr = ac->code_offset;
size = attr->attribute_length;
break;
}
}
RzBinSymbol *symbol = rz_bin_symbol_new(NULL, addr, addr);
if (!symbol) {
rz_warn_if_reached();
free(method_name);
continue;
}
char *desc = java_class_constant_pool_stringify_at(bin, method->descriptor_index);
if (!desc) {
desc = strdup("(?)V");
}
set_lib_and_class_name(rz_bin_java_class_name(bin), &symbol->classname, &symbol->libname);
symbol->dname = demangle_java_and_free(rz_str_newf("%s%s", method_name, desc));
symbol->name = add_class_name_to_name(method_name, symbol->classname);
symbol->size = size;
symbol->bind = java_method_is_global(method) ? RZ_BIN_BIND_GLOBAL_STR : RZ_BIN_BIND_LOCAL_STR;
symbol->type = RZ_BIN_TYPE_FUNC_STR;
symbol->ordinal = rz_list_length(list);
symbol->visibility = method->access_flags;
symbol->visibility_str = java_method_access_flags_readable(method);
symbol->method_flags = java_access_flags_to_bin_flags(method->access_flags);
free(desc);
free(method_name);
rz_list_append(list, symbol);
}
}
return list;
}
/**
* \brief Returns the methods in text format via RzStrBuf arg
*/
RZ_API void rz_bin_java_class_methods_as_text(RZ_NONNULL RzBinJavaClass *bin, RZ_NONNULL RzStrBuf *sb) {
rz_return_if_fail(bin && sb);
rz_strbuf_appendf(sb, "Methods: %u\n", bin->methods_count);
char number[16];
char *flags, *name, *descr;
if (bin->methods) {
for (ut32 i = 0; i < bin->methods_count; ++i) {
const Method *method = bin->methods[i];
if (!method) {
rz_warn_if_reached();
continue;
}
flags = java_method_access_flags_readable(method);
name = java_class_constant_pool_stringify_at(bin, method->name_index);
descr = java_class_constant_pool_stringify_at(bin, method->descriptor_index);
if (flags) {
rz_strbuf_appendf(sb, " %s %s%s;\n", flags, name, descr);
} else {
rz_strbuf_appendf(sb, " %s%s;\n", name, descr);
}
rz_strbuf_appendf(sb, " name: %s\n", name);
rz_strbuf_appendf(sb, " descriptor: %s\n", descr);
rz_strbuf_appendf(sb, " flags: (0x%04x) %s\n", method->access_flags, flags ? flags : "");
free(flags);
free(name);
free(descr);
rz_strbuf_appendf(sb, " attributes: %u\n", method->attributes_count);
int padding = calculate_padding_ut16(method->attributes_count) + 1;
for (ut32 i = 0; i < method->attributes_count; ++i) {
Attribute *attr = method->attributes[i];
if (!attr) {
continue;
}
snprintf(number, sizeof(number), "#%u", i);
name = java_class_constant_pool_stringify_at(bin, attr->attribute_name_index);
rz_strbuf_appendf(sb, " %-*s = #%-5u size: %-5u %s\n", padding, number, attr->attribute_name_index, attr->attribute_length, name);
free(name);
}
}
}
}
/**
* \brief Returns the methods in json format via PJ arg
*/
RZ_API void rz_bin_java_class_methods_as_json(RZ_NONNULL RzBinJavaClass *bin, RZ_NONNULL PJ *j) {
rz_return_if_fail(bin && j);
pj_a(j);
char *tmp;
if (bin->methods) {
for (ut32 i = 0; i < bin->methods_count; ++i) {
const Method *method = bin->methods[i];
if (!method) {
rz_warn_if_reached();
continue;
}
pj_o(j); // {
pj_kn(j, "offset", method->offset);
pj_kn(j, "access_flags_n", method->access_flags);
tmp = java_method_access_flags_readable(method);
pj_ks(j, "access_flags_s", tmp ? tmp : "");
free(tmp);
pj_kn(j, "name_n", method->name_index);
tmp = java_class_constant_pool_stringify_at(bin, method->name_index);
pj_ks(j, "name_s", tmp ? tmp : "");
free(tmp);
pj_kn(j, "descriptor_n", method->descriptor_index);
tmp = java_class_constant_pool_stringify_at(bin, method->descriptor_index);
pj_ks(j, "descriptor_s", tmp ? tmp : "");
free(tmp);
pj_kn(j, "attributes_count", method->attributes_count);
pj_ka(j, "attributes"); // [
for (ut32 i = 0; i < method->attributes_count; ++i) {
Attribute *attr = method->attributes[i];
if (!attr) {
rz_warn_if_reached();
continue;
}
pj_o(j);
pj_kn(j, "offset", attr->offset);
pj_kn(j, "size", attr->attribute_length);
pj_kn(j, "name_n", attr->attribute_name_index);
tmp = java_class_constant_pool_stringify_at(bin, attr->attribute_name_index);
pj_ks(j, "name_s", tmp ? tmp : "");
free(tmp);
pj_end(j);
}
pj_end(j); // ]
pj_end(j); // }
}
}
pj_end(j);
}
/**
* \brief Returns a RzList<RzBinSymbol*> containing the class fields
*/
RZ_API RZ_OWN RzList /*<RzBinSymbol *>*/ *rz_bin_java_class_fields_as_symbols(RZ_NONNULL RzBinJavaClass *bin) {
rz_return_val_if_fail(bin, NULL);
RzList *list = rz_list_newf((RzListFree)rz_bin_symbol_free);
if (!list) {
return NULL;
}
char *field_name = NULL;
if (bin->fields) {
for (ut32 i = 0; i < bin->fields_count; ++i) {
const Field *field = bin->fields[i];
if (!field) {
rz_warn_if_reached();
continue;
}
const ConstPool *cpool = java_class_constant_pool_at(bin, field->name_index);
if (!cpool || !java_constant_pool_is_string(cpool)) {
RZ_LOG_ERROR("java bin: can't resolve field with constant pool index %u\n", field->name_index);
continue;
}
field_name = java_constant_pool_stringify(cpool);
if (!field_name) {
continue;
}
RzBinSymbol *symbol = rz_bin_symbol_new(NULL, field->offset, field->offset);
if (!symbol) {
rz_warn_if_reached();
free(field_name);
continue;
}
set_lib_and_class_name(rz_bin_java_class_name(bin), &symbol->classname, &symbol->libname);
symbol->name = add_class_name_to_name(field_name, symbol->classname);
symbol->dname = rz_demangler_java(symbol->name, RZ_DEMANGLER_FLAG_ENABLE_ALL);
symbol->size = 0;
symbol->bind = java_field_is_global(field) ? RZ_BIN_BIND_GLOBAL_STR : RZ_BIN_BIND_LOCAL_STR;
symbol->type = RZ_BIN_TYPE_OBJECT_STR;
symbol->ordinal = i;
symbol->visibility = field->access_flags;
symbol->visibility_str = java_field_access_flags_readable(field);
symbol->method_flags = java_access_flags_to_bin_flags(field->access_flags);
free(field_name);
rz_list_append(list, symbol);
}
}
return list;
}
/**
* \brief Returns a RzList<RzBinClassField*> containing the class fields
*/
RZ_API RZ_OWN RzList /*<RzBinClassField *>*/ *rz_bin_java_class_fields_as_binfields(RZ_NONNULL RzBinJavaClass *bin) {
rz_return_val_if_fail(bin, NULL);
RzList *list = rz_list_newf((RzListFree)rz_bin_class_field_free);
if (!list) {
return NULL;
}
char *name = NULL;
if (bin->fields) {
for (ut32 i = 0; i < bin->fields_count; ++i) {
const Field *field = bin->fields[i];
if (!field) {
rz_warn_if_reached();
continue;
}
const ConstPool *cpool = java_class_constant_pool_at(bin, field->name_index);
if (!cpool || !java_constant_pool_is_string(cpool)) {
RZ_LOG_ERROR("java bin: can't resolve field with constant pool index %u\n", field->name_index);
continue;
}
name = java_constant_pool_stringify(cpool);
if (!name) {
continue;
}
char *ftype = demangle_java_and_free(java_class_constant_pool_stringify_at(bin, field->descriptor_index));
RzBinClassField *bf = rz_bin_class_field_new(field->offset, field->offset, name, NULL, NULL, ftype);
free(ftype);
if (bf) {
set_lib_and_class_name(rz_bin_java_class_name(bin), &bf->classname, &bf->libname);
bf->visibility = field->access_flags;
bf->flags = java_access_flags_to_bin_flags(field->access_flags);
rz_list_append(list, bf);
}
free(name);
}
}
return list;
}
/**
* \brief Returns the fields in text format via RzStrBuf arg
*/
RZ_API void rz_bin_java_class_fields_as_text(RZ_NONNULL RzBinJavaClass *bin, RZ_NONNULL RzStrBuf *sb) {
rz_return_if_fail(bin && sb);
rz_strbuf_appendf(sb, "Fields: %u\n", bin->fields_count);
char number[16];
char *flags, *name, *descr;
if (bin->fields) {
for (ut32 i = 0; i < bin->fields_count; ++i) {
const Field *field = bin->fields[i];
if (!field) {
rz_warn_if_reached();
continue;
}
flags = java_field_access_flags_readable(field);
name = java_class_constant_pool_stringify_at(bin, field->name_index);
descr = java_class_constant_pool_stringify_at(bin, field->descriptor_index);
if (flags) {
rz_strbuf_appendf(sb, " %s %s%s;\n", flags, name, descr);
} else {
rz_strbuf_appendf(sb, " %s%s;\n", name, descr);
}
rz_strbuf_appendf(sb, " name: %s\n", name);
rz_strbuf_appendf(sb, " descriptor: %s\n", descr);
rz_strbuf_appendf(sb, " flags: (0x%04x) %s\n", field->access_flags, flags);
free(flags);
free(name);
free(descr);
rz_strbuf_appendf(sb, " attributes: %u\n", field->attributes_count);
int padding = calculate_padding_ut16(field->attributes_count) + 1;
for (ut32 i = 0; i < field->attributes_count; ++i) {
Attribute *attr = field->attributes[i];
if (!attr) {
continue;
}
snprintf(number, sizeof(number), "#%u", i);
name = java_class_constant_pool_stringify_at(bin, attr->attribute_name_index);
rz_strbuf_appendf(sb, " %*s = #%-5u size: %-5u %s\n", padding, number, attr->attribute_name_index, attr->attribute_length, name);
free(name);
}
}
}
}
/**
* \brief Returns the fields in json format via PJ arg
*/
RZ_API void rz_bin_java_class_fields_as_json(RZ_NONNULL RzBinJavaClass *bin, RZ_NONNULL PJ *j) {
rz_return_if_fail(bin && j);
pj_a(j);
char *tmp;
if (bin->fields) {
for (ut32 i = 0; i < bin->fields_count; ++i) {
const Field *field = bin->fields[i];
if (!field) {
rz_warn_if_reached();
continue;
}
pj_o(j); // {
pj_kn(j, "offset", field->offset);
pj_kn(j, "access_flags_n", field->access_flags);
tmp = java_field_access_flags_readable(field);
pj_ks(j, "access_flags_s", tmp ? tmp : "");
free(tmp);
pj_kn(j, "name_n", field->name_index);
tmp = java_class_constant_pool_stringify_at(bin, field->name_index);
pj_ks(j, "name_s", tmp ? tmp : "");
free(tmp);
pj_kn(j, "descriptor_n", field->descriptor_index);
tmp = java_class_constant_pool_stringify_at(bin, field->descriptor_index);
pj_ks(j, "descriptor_s", tmp ? tmp : "");
free(tmp);
pj_kn(j, "attributes_count", field->attributes_count);
pj_ka(j, "attributes"); // [
for (ut32 i = 0; i < field->attributes_count; ++i) {
Attribute *attr = field->attributes[i];
if (!attr) {
continue;
}
pj_o(j);
pj_kn(j, "offset", attr->offset);
pj_kn(j, "size", attr->attribute_length);
pj_kn(j, "name_n", attr->attribute_name_index);
tmp = java_class_constant_pool_stringify_at(bin, attr->attribute_name_index);
pj_ks(j, "name_s", tmp ? tmp : "");
free(tmp);
pj_end(j);
}
pj_end(j); // ]
pj_end(j); // }
}
}
pj_end(j);
}
static char *import_type(const ConstPool *cpool) {
if (cpool->tag == CONSTANT_POOL_METHODREF) {
return RZ_BIN_TYPE_METH_STR;
} else if (cpool->tag == CONSTANT_POOL_FIELDREF) {
return "FIELD";
} else if (cpool->tag == CONSTANT_POOL_INTERFACEMETHODREF) {
return "IMETH";
}
return RZ_BIN_TYPE_UNKNOWN_STR;
}
/**
* \brief Returns a RzList<RzBinSymbol*> containing the class const pool
*/
RZ_API RZ_OWN RzList /*<RzBinSymbol *>*/ *rz_bin_java_class_const_pool_as_symbols(RZ_NONNULL RzBinJavaClass *bin) {
rz_return_val_if_fail(bin, NULL);
RzList *list = rz_list_newf((RzListFree)rz_bin_symbol_free);
if (!list) {
return NULL;
}
char *method_name, *classname;
ut16 class_index, name_and_type_index, name_index, descriptor_index, class_name_index;
if (bin->constant_pool) {
for (ut32 i = 0; i < bin->constant_pool_count; ++i) {
const ConstPool *cpool = bin->constant_pool[i];
if (!cpool || !java_constant_pool_is_import(cpool)) {
continue;
}
if (java_constant_pool_resolve(cpool, &class_index, &name_and_type_index) != 2) {
RZ_LOG_ERROR("java bin: can't resolve symbol with constant pool index %u\n", i);
break;
}
const ConstPool *nat = java_class_constant_pool_at(bin, name_and_type_index);
if (!nat ||
java_constant_pool_resolve(nat, &name_index, &descriptor_index) != 2) {
RZ_LOG_ERROR("java bin: can't resolve symbol with constant pool index %u\n", i);
break;
}
const ConstPool *pclass = java_class_constant_pool_at(bin, class_index);
if (!pclass ||
java_constant_pool_resolve(pclass, &class_name_index, NULL) != 1) {
RZ_LOG_ERROR("java bin: can't resolve symbol with constant pool index %u\n", i);
break;
}
RzBinSymbol *symbol = rz_bin_symbol_new(NULL, cpool->offset, cpool->offset);
if (!symbol) {
rz_warn_if_reached();
break;
}
char *desc = java_class_constant_pool_stringify_at(bin, descriptor_index);
if (!desc) {
desc = strdup("(?)V");
}
method_name = java_class_constant_pool_stringify_at(bin, name_index);
if (!method_name) {
method_name = strdup("unknown_method");
}
classname = java_class_constant_pool_stringify_at(bin, class_name_index);
if (!classname) {
classname = strdup("unknown_class");
}
set_lib_and_class_name(rz_str_newf("L%s;", classname), &symbol->classname, &symbol->libname);
symbol->name = add_class_name_to_name(method_name, symbol->classname);
if (desc[0] == '(') {
symbol->dname = rz_str_newf("%s%s", method_name, desc);
} else {
symbol->dname = strdup(method_name);
}
symbol->dname = demangle_java_and_free(symbol->dname);
symbol->bind = RZ_BIN_BIND_IMPORT_STR;
symbol->type = !strcmp(method_name, "main") ? RZ_BIN_TYPE_FUNC_STR : import_type(cpool);
symbol->ordinal = i;
symbol->is_imported = true;
free(desc);
free(classname);
free(method_name);
rz_list_append(list, symbol);
}
}
return list;
}
/**
* \brief Returns a RzPVector<RzBinImport*> containing the class const pool
*/
RZ_API RZ_OWN RzPVector /*<RzBinImport *>*/ *rz_bin_java_class_const_pool_as_imports(RZ_NONNULL RzBinJavaClass *bin) {
rz_return_val_if_fail(bin, NULL);
RzPVector *imports = rz_pvector_new((RzListFree)rz_bin_import_free);
if (!imports) {
return NULL;
}
bool is_main;
ut16 class_index, name_and_type_index, name_index, descriptor_index, class_name_index;
if (bin->constant_pool) {
for (ut32 i = 0; i < bin->constant_pool_count; ++i) {
const ConstPool *cpool = bin->constant_pool[i];
if (!cpool || !java_constant_pool_is_import(cpool)) {
continue;
}
if (java_constant_pool_resolve(cpool, &class_index, &name_and_type_index) != 2) {
RZ_LOG_ERROR("java bin: can't resolve import with constant pool index %u\n", i);
continue;
}
const ConstPool *nat = java_class_constant_pool_at(bin, name_and_type_index);
if (!nat ||
java_constant_pool_resolve(nat, &name_index, &descriptor_index) != 2) {
RZ_LOG_ERROR("java bin: can't resolve import with constant pool index %u\n", i);
continue;
}
const ConstPool *pclass = java_class_constant_pool_at(bin, class_index);
if (!pclass ||
java_constant_pool_resolve(pclass, &class_name_index, NULL) != 1) {
RZ_LOG_ERROR("java bin: can't resolve import with constant pool index %u\n", i);
continue;
}
char *object = java_class_constant_pool_stringify_at(bin, class_name_index);
if (!object) {
continue;
}
RzBinImport *import = RZ_NEW0(RzBinImport);
if (!import) {
rz_warn_if_reached();
free(object);
continue;
}
set_lib_and_class_name(rz_str_newf("L%s;", object), &import->classname, &import->libname);
import->name = java_class_constant_pool_stringify_at(bin, name_index);
is_main = import->name && !strcmp(import->name, "main");
import->bind = is_main ? RZ_BIN_BIND_GLOBAL_STR : NULL;
import->type = is_main ? RZ_BIN_TYPE_FUNC_STR : import_type(cpool);
import->descriptor = java_class_constant_pool_stringify_at(bin, descriptor_index);
import->ordinal = i;
rz_pvector_push(imports, import);
free(object);
}
}
if (bin->interfaces) {
for (ut32 i = 0; i < bin->interfaces_count; ++i) {
if (!bin->interfaces[i]) {
continue;
}
const ConstPool *cpool = java_class_constant_pool_at(bin, bin->interfaces[i]->index);
if (!cpool || java_constant_pool_resolve(cpool, &class_index, NULL) != 1) {
RZ_LOG_ERROR("java bin: can't resolve interface with constant pool index %u\n", i);
continue;
}
char *object = java_class_constant_pool_stringify_at(bin, class_index);
if (!object) {
continue;
}
RzBinImport *import = RZ_NEW0(RzBinImport);
if (!import) {
rz_warn_if_reached();
free(object);
continue;
}
set_lib_and_class_name(rz_str_newf("L%s;", object), &import->classname, &import->libname);
import->name = strdup("*");
import->bind = RZ_BIN_BIND_WEAK_STR;
import->type = RZ_BIN_TYPE_IFACE_STR;
import->ordinal = i;
rz_pvector_push(imports, import);
free(object);
}
}
return imports;
}
/**
* \brief Returns the class const pool in text format via RzStrBuf arg
*/
RZ_API void rz_bin_java_class_const_pool_as_text(RZ_NONNULL RzBinJavaClass *bin, RZ_NONNULL RzStrBuf *sb) {
rz_return_if_fail(bin && sb);
char number[16];
const char *tag;
char *text, *rtext;
rz_strbuf_appendf(sb, "Constant pool: %u\n", bin->constant_pool_count);
if (bin->constant_pool) {
int padding = calculate_padding_ut16(bin->constant_pool_count) + 1;
for (ut32 i = 0; i < bin->constant_pool_count; ++i) {
rtext = NULL;
const ConstPool *cpool = bin->constant_pool[i];
if (!cpool) {
continue;
}
tag = java_constant_pool_tag_name(cpool);
if (!tag) {
RZ_LOG_ERROR("java bin: invalid tag name for constant pool at index %u\n", i);
continue;
}
snprintf(number, sizeof(number), "#%u", i);
text = java_constant_pool_stringify(cpool);
if (i > 0 && !java_constant_pool_is_string(cpool) &&
!java_constant_pool_is_number(cpool)) {
rtext = rz_bin_java_class_const_pool_resolve_index(bin, i);
}
if (rtext) {
char *dem = rz_demangler_java(rtext, RZ_DEMANGLER_FLAG_ENABLE_ALL);
if (dem) {
free(rtext);
rtext = dem;
}
rz_strbuf_appendf(sb, " %*s = %-19s %-14s // %s\n", padding, number, tag, text, rtext);
} else {
rz_strbuf_appendf(sb, " %*s = %-19s %s\n", padding, number, tag, text);
}
free(text);
free(rtext);
}
}
}
/**
* \brief Returns the class const pool in json format via PJ arg
*/
RZ_API void rz_bin_java_class_const_pool_as_json(RZ_NONNULL RzBinJavaClass *bin, RZ_NONNULL PJ *j) {
rz_return_if_fail(bin && j);
const char *tag;
char *text, *rtext;
pj_a(j);
if (bin->constant_pool) {
for (ut32 i = 0; i < bin->constant_pool_count; ++i) {
rtext = NULL;
const ConstPool *cpool = bin->constant_pool[i];
if (!cpool) {
continue;
}
tag = java_constant_pool_tag_name(cpool);
if (!tag) {
RZ_LOG_ERROR("java bin: invalid tag name for constant pool at index %u\n", i);
continue;
}
text = java_constant_pool_stringify(cpool);
pj_o(j);
pj_kn(j, "index", i);
pj_kn(j, "tag_n", cpool->tag);
pj_ks(j, "tag_s", tag);
pj_ks(j, "value", text ? text : "");
if (i > 0 && !java_constant_pool_is_string(cpool) &&
!java_constant_pool_is_number(cpool)) {
rtext = rz_bin_java_class_const_pool_resolve_index(bin, i);
pj_ks(j, "resolved", rtext ? rtext : "");
}
pj_end(j);
free(text);
free(rtext);
}
}
pj_end(j);
}
static RzBinSection *new_section(const char *name, ut64 start, ut64 end, ut32 perm) {
RzBinSection *section = RZ_NEW0(RzBinSection);
if (!section) {
rz_warn_if_reached();
return NULL;
}
section->name = strdup(name);
if (!section->name) {
rz_warn_if_reached();
free(section);
return NULL;
}
section->paddr = start;
section->vaddr = start;
section->size = end - start;
section->vsize = section->size;
section->perm = perm;
return section;
}
static void section_free(void *u) {
rz_bin_section_free((RzBinSection *)u);
}
static int compare_section_names(const void *a, const void *b) {
RzBinSection *sec = (RzBinSection *)b;
return strcmp((const char *)a, sec->name);
}
/**
* \brief Returns a RzPVector<RzBinSection*> containing the class sections
*/
RZ_API RZ_OWN RzPVector /*<RzBinSection *>*/ *rz_bin_java_class_as_sections(RZ_NONNULL RzBinJavaClass *bin) {
rz_return_val_if_fail(bin, NULL);
RzPVector *sections = rz_pvector_new(section_free);
if (!sections) {
return NULL;
}
ut32 iname;
char *tmp;
char secname[512];
ut64 end_offset;
if (bin->constant_pool) {
rz_pvector_push(sections,
new_section("class.constant_pool",
bin->constant_pool_offset,
bin->interfaces_offset,
RZ_PERM_R));
}
if (bin->interfaces) {
rz_pvector_push(sections,
new_section("class.interfaces",
bin->interfaces_offset,
bin->fields_offset,
RZ_PERM_R));
}
if (bin->fields) {
for (ut32 i = 0; i < bin->fields_count; ++i) {
Field *field = bin->fields[i];
if (!field) {
continue;
}
tmp = java_class_constant_pool_stringify_at(bin, field->name_index);
if (!tmp) {
rz_warn_if_reached();
continue;
}
snprintf(secname, sizeof(secname), "class.fields.%s.attr", tmp);
if ((i + 1) < bin->fields_count && bin->fields[i + 1]) {
end_offset = bin->fields[i + 1]->offset;
} else {
end_offset = bin->methods_offset;
}
for (iname = 0; rz_pvector_find(sections, secname, (RzPVectorComparator)compare_section_names, NULL); iname++) {
snprintf(secname, sizeof(secname), "class.fields.%s_%d.attr", tmp, iname);
}
free(tmp);
rz_pvector_push(sections, new_section(secname, field->offset, end_offset, RZ_PERM_R));
}
rz_pvector_push(sections,
new_section("class.fields",
bin->fields_offset,
bin->methods_offset,
RZ_PERM_R));
}
if (bin->methods) {
rz_pvector_push(sections,
new_section("class.methods",
bin->methods_offset,
bin->attributes_offset,
RZ_PERM_R));
for (ut32 i = 0; i < bin->methods_count; ++i) {
Method *method = bin->methods[i];
if (!method || method->attributes_count < 1) {
continue;
}
tmp = java_class_constant_pool_stringify_at(bin, method->name_index);
if (!tmp) {
rz_warn_if_reached();
continue;
}
snprintf(secname, sizeof(secname), "class.methods.%s.attr", tmp);
for (iname = 0; rz_pvector_find(sections, secname, (RzPVectorComparator)compare_section_names, NULL); iname++) {
snprintf(secname, sizeof(secname), "class.methods.%s_%d.attr", tmp, iname);
}
if ((i + 1) < bin->methods_count && bin->methods[i + 1]) {
end_offset = bin->methods[i + 1]->offset;
} else {
end_offset = bin->attributes_offset;
}
if (iname > 0) {
snprintf(secname, sizeof(secname), "class.methods.%s_%d.attr", tmp, iname);
} else {
snprintf(secname, sizeof(secname), "class.methods.%s.attr", tmp);
}
rz_pvector_push(sections, new_section(secname, method->offset, end_offset, RZ_PERM_R));
if (!method->attributes) {
free(tmp);
continue;
}
for (ut32 k = 0; k < method->attributes_count; ++k) {
Attribute *attr = method->attributes[k];
if (attr && attr->type == ATTRIBUTE_TYPE_CODE) {
AttributeCode *ac = (AttributeCode *)attr->info;
if (iname > 0) {
snprintf(secname, sizeof(secname), "class.methods.%s_%d.attr.%d.code", tmp, iname, k);
} else {
snprintf(secname, sizeof(secname), "class.methods.%s.attr.%d.code", tmp, k);
}
ut64 size = ac->code_offset + attr->attribute_length;
rz_pvector_push(sections, new_section(secname, ac->code_offset, size, RZ_PERM_R | RZ_PERM_X));
break;
}
}
free(tmp);
}
}
if (bin->attributes) {
rz_pvector_push(sections,
new_section("class.attr",
bin->attributes_offset,
bin->class_end_offset,
RZ_PERM_R));
}
return sections;
}
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 class libraries
*/
RZ_API RZ_OWN RzPVector /*<char *>*/ *rz_bin_java_class_as_libraries(RZ_NONNULL RzBinJavaClass *bin) {
rz_return_val_if_fail(bin, NULL);
RzPVector *vec = rz_pvector_new(free);
if (!vec) {
return NULL;
}
ut16 arg0, arg1;
char *tmp;
if (bin->constant_pool) {
for (ut32 i = 0; i < bin->constant_pool_count; ++i) {
tmp = NULL;
const ConstPool *cpool = bin->constant_pool[i];
if (!cpool) {
continue;
}
if (cpool->tag == CONSTANT_POOL_CLASS) {
if (java_constant_pool_resolve(cpool, &arg0, &arg1) != 1) {
RZ_LOG_ERROR("java bin: can't resolve library with constant pool index %u\n", i);
break;
}
// arg0 is name_index
tmp = java_class_constant_pool_stringify_at(bin, arg0);
} else if (java_constant_pool_is_import(cpool)) {
if (java_constant_pool_resolve(cpool, &arg0, &arg1) != 2) {
RZ_LOG_ERROR("java bin: can't resolve library with constant pool index %u\n", i);
break;
}
// arg0 is name_and_type_index
const ConstPool *nat = java_class_constant_pool_at(bin, arg0);
if (!nat ||
java_constant_pool_resolve(nat, &arg0, &arg1) != 1) {
RZ_LOG_ERROR("java bin: can't resolve library with constant pool index %u\n", i);
break;
}
// arg0 is name_index
tmp = java_class_constant_pool_stringify_at(bin, arg0);
}
if (tmp && !rz_pvector_find(vec, tmp, compare_strings, NULL)) {
rz_pvector_push(vec, tmp);
} else {
free(tmp);
}
}
}
return vec;
}
/**
* \brief Returns the class interfaces as text via RzStrBuf arg
*/
RZ_API void rz_bin_java_class_interfaces_as_text(RZ_NONNULL RzBinJavaClass *bin, RZ_NONNULL RzStrBuf *sb) {
rz_return_if_fail(bin && sb);
ut16 index;
char number[16];
char *tmp = NULL;
rz_strbuf_appendf(sb, "Interfaces: %u\n", bin->interfaces_count);
if (bin->interfaces) {
int padding = calculate_padding_ut16(bin->constant_pool_count) + 1;
for (ut32 i = 0; i < bin->interfaces_count; ++i) {
if (!bin->interfaces[i]) {
continue;
}
const ConstPool *cpool = java_class_constant_pool_at(bin, bin->interfaces[i]->index);
if (!cpool || java_constant_pool_resolve(cpool, &index, NULL) != 1) {
RZ_LOG_ERROR("java bin: can't resolve interface with constant pool index %u\n", i);
break;
}
snprintf(number, sizeof(number), "#%u", i);
tmp = java_class_constant_pool_stringify_at(bin, index);
rz_str_replace_char(tmp, '/', '.');
rz_strbuf_appendf(sb, " %*s = #%-5u %s\n", padding, number, index, tmp);
free(tmp);
}
}
}
/**
* \brief Returns the class interfaces as json via PJ arg
*/
RZ_API void rz_bin_java_class_interfaces_as_json(RZ_NONNULL RzBinJavaClass *bin, RZ_NONNULL PJ *j) {
rz_return_if_fail(bin && j);
pj_a(j);
char *tmp = NULL;
ut16 index;
if (bin->interfaces) {
for (ut32 i = 0; i < bin->interfaces_count; ++i) {
if (!bin->interfaces[i]) {
continue;
}
const ConstPool *cpool = java_class_constant_pool_at(bin, bin->interfaces[i]->index);
if (!cpool || java_constant_pool_resolve(cpool, &index, NULL) != 1) {
RZ_LOG_ERROR("java bin: can't resolve interface with constant pool index %u\n", i);
continue;
}
pj_o(j);
pj_kn(j, "offset", bin->interfaces[i]->offset);
pj_kn(j, "name_n", bin->interfaces[i]->index);
tmp = java_class_constant_pool_stringify_at(bin, index);
rz_str_replace_char(tmp, '/', '.');
pj_ks(j, "name_s", tmp ? tmp : "");
free(tmp);
pj_end(j);
}
}
pj_end(j);
}
/**
* \brief Returns a RzPVector<RzBinClass*> containing only the class of the bin
*/
RZ_API RZ_OWN RzPVector /*<RzBinClass *>*/ *rz_bin_java_class_as_classes(RZ_NONNULL RzBinJavaClass *bin) {
rz_return_val_if_fail(bin, NULL);
RzBinClass *bclass = NULL;
RzPVector *vec = rz_pvector_new((RzPVectorFree)rz_bin_class_free);
if (!vec) {
return NULL;
}
bclass = RZ_NEW0(RzBinClass);
if (!bclass) {
rz_pvector_free(vec);
return NULL;
}
rz_pvector_push(vec, bclass);
bclass->name = demangle_java_and_free(rz_bin_java_class_name(bin));
bclass->super = demangle_java_and_free(rz_bin_java_class_super(bin));
bclass->visibility = rz_bin_java_class_access_flags(bin);
bclass->visibility_str = rz_bin_java_class_access_flags_readable(bin, ACCESS_FLAG_MASK_ALL_NO_SUPER);
bclass->methods = rz_bin_java_class_methods_as_symbols(bin);
bclass->fields = rz_bin_java_class_fields_as_binfields(bin);
if (!bclass->methods || !bclass->fields) {
rz_pvector_free(vec);
return NULL;
}
return vec;
}