1739 lines
50 KiB
C
1739 lines
50 KiB
C
// SPDX-FileCopyrightText: 2021 deroad <wargio@libero.it>
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
|
|
#include "class_bin.h"
|
|
#include "class_private.h"
|
|
|
|
#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 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;
|
|
}
|
|
|
|
bin->magic = rz_buf_read_be32(buf);
|
|
bin->minor_version = rz_buf_read_be16(buf);
|
|
bin->major_version = rz_buf_read_be16(buf);
|
|
|
|
// 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 = rz_buf_read_be16(buf);
|
|
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)) {
|
|
i++;
|
|
bin->constant_pool[i] = java_constant_null_new(offset);
|
|
}
|
|
}
|
|
if (is_eob(buf)) {
|
|
rz_warn_if_reached();
|
|
goto java_class_parse_bad;
|
|
}
|
|
}
|
|
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 = rz_buf_read_be16(buf);
|
|
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;
|
|
}
|
|
}
|
|
|
|
bin->fields_count = rz_buf_read_be16(buf);
|
|
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;
|
|
}
|
|
}
|
|
|
|
bin->methods_count = rz_buf_read_be16(buf);
|
|
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;
|
|
}
|
|
}
|
|
|
|
bin->attributes_count = rz_buf_read_be16(buf);
|
|
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);
|
|
}
|
|
|
|
RZ_API RzBinJavaClass *rz_bin_java_class_new(RzBuffer *buf, ut64 offset, 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;
|
|
}
|
|
|
|
RZ_API char *rz_bin_java_class_version(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(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 const char *rz_bin_java_class_language(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;
|
|
}
|
|
char *string = java_constant_pool_stringify(cpool);
|
|
if (string && !strncmp(string, "kotlin/jvm", 10)) {
|
|
language = "kotlin";
|
|
break;
|
|
} else if (string && !strncmp(string, "org/codehaus/groovy/runtime", 27)) {
|
|
language = "groovy";
|
|
break;
|
|
}
|
|
free(string);
|
|
string = NULL;
|
|
}
|
|
}
|
|
free(string);
|
|
return language;
|
|
}
|
|
|
|
RZ_API void rz_bin_java_class_free(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);
|
|
}
|
|
|
|
RZ_API char *rz_bin_java_class_name(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");
|
|
}
|
|
|
|
return java_class_constant_pool_stringify_at(bin, index);
|
|
}
|
|
|
|
RZ_API char *rz_bin_java_class_super(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");
|
|
}
|
|
return java_class_constant_pool_stringify_at(bin, index);
|
|
}
|
|
|
|
RZ_API ut32 rz_bin_java_class_access_flags(RzBinJavaClass *bin) {
|
|
rz_return_val_if_fail(bin, 0xffffffff);
|
|
return bin->access_flags;
|
|
}
|
|
|
|
RZ_API char *rz_bin_java_class_access_flags_readable(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;
|
|
}
|
|
|
|
RZ_API void rz_bin_java_class_as_json(RzBinJavaClass *bin, 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);
|
|
}
|
|
|
|
RZ_API void rz_bin_java_class_as_text(RzBinJavaClass *bin, 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;
|
|
}
|
|
|
|
RZ_API char *rz_bin_java_class_const_pool_resolve_index(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);
|
|
rz_str_replace_char(tmp, '/', '.');
|
|
return tmp;
|
|
} 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) {
|
|
rz_str_replace_char(s1, '/', '.');
|
|
return s1;
|
|
}
|
|
tmp = rz_str_newf("%s:%s", s0, s1);
|
|
rz_str_replace_char(tmp, '/', '.');
|
|
free(s0);
|
|
free(s1);
|
|
return tmp;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
RZ_API void rz_bin_java_class_as_source_code(RzBinJavaClass *bin, RzStrBuf *sb) {
|
|
rz_return_if_fail(bin && sb);
|
|
|
|
char *tmp;
|
|
ut16 index;
|
|
|
|
RzListIter *iter;
|
|
RzList *list = rz_bin_java_class_as_libraries(bin);
|
|
rz_list_foreach (list, iter, tmp) {
|
|
rz_str_replace_char(tmp, '/', '.');
|
|
rz_strbuf_appendf(sb, "import %s;\n", tmp);
|
|
}
|
|
if (rz_list_length(list) > 0) {
|
|
rz_strbuf_appendf(sb, "\n");
|
|
}
|
|
rz_list_free(list);
|
|
|
|
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);
|
|
rz_str_replace_char(tmp, '/', '.');
|
|
rz_strbuf_appendf(sb, " %s", tmp);
|
|
free(tmp);
|
|
|
|
if (bin->access_flags & ACCESS_FLAG_SUPER) {
|
|
tmp = rz_bin_java_class_super(bin);
|
|
rz_str_replace_char(tmp, '/', '.');
|
|
if (strcmp(tmp, "java.lang.Object") != 0) {
|
|
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);
|
|
rz_str_replace_char(tmp, '/', '.');
|
|
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);
|
|
}
|
|
|
|
tmp = java_class_constant_pool_stringify_at(bin, method->descriptor_index);
|
|
rz_str_replace_char(tmp, '/', '.');
|
|
char *dem = rz_bin_demangle_java(tmp);
|
|
if (!dem) {
|
|
dem = tmp;
|
|
tmp = java_class_constant_pool_stringify_at(bin, method->name_index);
|
|
if (tmp) {
|
|
rz_str_replace_char(tmp, '/', '.');
|
|
rz_strbuf_appendf(sb, "%s ", tmp);
|
|
free(tmp);
|
|
}
|
|
rz_strbuf_append(sb, dem);
|
|
} else {
|
|
free(tmp);
|
|
tmp = java_class_constant_pool_stringify_at(bin, method->name_index);
|
|
if (tmp) {
|
|
rz_str_replace_char(tmp, '/', '.');
|
|
|
|
char *ptr = strchr(dem, '(');
|
|
*(ptr - 1) = 0;
|
|
rz_strbuf_append(sb, dem);
|
|
rz_strbuf_appendf(sb, "%s", tmp);
|
|
rz_strbuf_append(sb, ptr);
|
|
free(tmp);
|
|
}
|
|
}
|
|
free(dem);
|
|
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");
|
|
}
|
|
|
|
RZ_API RzBinAddr *rz_bin_java_class_resolve_symbol(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;
|
|
}
|
|
|
|
RZ_API RzList *rz_bin_java_class_entrypoints(RzBinJavaClass *bin) {
|
|
rz_return_val_if_fail(bin, NULL);
|
|
|
|
RzList *list = rz_list_newf(free);
|
|
if (!list) {
|
|
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_list_append(list, entrypoint);
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
RZ_API RzList *rz_bin_java_class_strings(RzBinJavaClass *bin) {
|
|
rz_return_val_if_fail(bin, NULL);
|
|
|
|
RzList *list = rz_list_newf(rz_bin_string_free);
|
|
if (!list) {
|
|
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_TYPE_UTF8;
|
|
rz_list_append(list, bstr);
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
static char *add_class_name_to_name(char *name, char *classname) {
|
|
char *tmp;
|
|
if (classname && name) {
|
|
tmp = rz_str_newf("%s.%s", classname, name);
|
|
if (!tmp) {
|
|
return name;
|
|
}
|
|
free(name);
|
|
rz_str_replace_char(tmp, '/', '.');
|
|
return tmp;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
RZ_API RzList *rz_bin_java_class_methods_as_symbols(RzBinJavaClass *bin) {
|
|
rz_return_val_if_fail(bin, NULL);
|
|
|
|
RzList *list = rz_list_newf((RzListFree)rz_bin_symbol_free);
|
|
if (!list) {
|
|
return NULL;
|
|
}
|
|
|
|
char *sym = 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;
|
|
}
|
|
sym = java_constant_pool_stringify(cpool);
|
|
if (!sym) {
|
|
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(sym);
|
|
continue;
|
|
}
|
|
symbol->classname = rz_bin_java_class_name(bin);
|
|
symbol->name = add_class_name_to_name(sym, 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);
|
|
rz_list_append(list, symbol);
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
RZ_API void rz_bin_java_class_methods_as_text(RzBinJavaClass *bin, 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RZ_API void rz_bin_java_class_methods_as_json(RzBinJavaClass *bin, 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);
|
|
}
|
|
|
|
RZ_API RzList *rz_bin_java_class_fields_as_symbols(RzBinJavaClass *bin) {
|
|
rz_return_val_if_fail(bin, NULL);
|
|
|
|
RzList *list = rz_list_newf((RzListFree)rz_bin_symbol_free);
|
|
if (!list) {
|
|
return NULL;
|
|
}
|
|
|
|
char *sym = 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;
|
|
}
|
|
sym = java_constant_pool_stringify(cpool);
|
|
if (!sym) {
|
|
continue;
|
|
}
|
|
RzBinSymbol *symbol = rz_bin_symbol_new(NULL, field->offset, field->offset);
|
|
if (!symbol) {
|
|
rz_warn_if_reached();
|
|
free(sym);
|
|
continue;
|
|
}
|
|
symbol->classname = rz_bin_java_class_name(bin);
|
|
symbol->name = add_class_name_to_name(sym, symbol->classname);
|
|
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);
|
|
rz_list_append(list, symbol);
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
RZ_API RzList *rz_bin_java_class_fields_as_binfields(RzBinJavaClass *bin) {
|
|
rz_return_val_if_fail(bin, NULL);
|
|
|
|
RzList *list = rz_list_newf((RzListFree)rz_bin_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;
|
|
}
|
|
RzBinField *bf = rz_bin_field_new(field->offset, field->offset, 0, name, NULL, NULL, false);
|
|
if (bf) {
|
|
bf->visibility = field->access_flags;
|
|
bf->type = java_class_constant_pool_stringify_at(bin, field->descriptor_index);
|
|
rz_list_append(list, bf);
|
|
}
|
|
free(name);
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
RZ_API void rz_bin_java_class_fields_as_text(RzBinJavaClass *bin, 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RZ_API void rz_bin_java_class_fields_as_json(RzBinJavaClass *bin, 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;
|
|
}
|
|
|
|
RZ_API RzList *rz_bin_java_class_const_pool_as_symbols(RzBinJavaClass *bin) {
|
|
rz_return_val_if_fail(bin, NULL);
|
|
|
|
RzList *list = rz_list_newf((RzListFree)rz_bin_symbol_free);
|
|
if (!list) {
|
|
return NULL;
|
|
}
|
|
char *sym, *classname;
|
|
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 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;
|
|
}
|
|
sym = java_class_constant_pool_stringify_at(bin, name_index);
|
|
is_main = sym && !strcmp(sym, "main");
|
|
classname = java_class_constant_pool_stringify_at(bin, class_name_index);
|
|
symbol->name = add_class_name_to_name(sym, classname);
|
|
symbol->classname = classname;
|
|
symbol->bind = RZ_BIN_BIND_IMPORT_STR;
|
|
symbol->type = is_main ? RZ_BIN_TYPE_FUNC_STR : import_type(cpool);
|
|
symbol->ordinal = i;
|
|
symbol->is_imported = true;
|
|
rz_list_append(list, symbol);
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
RZ_API RzList *rz_bin_java_class_const_pool_as_imports(RzBinJavaClass *bin) {
|
|
rz_return_val_if_fail(bin, NULL);
|
|
|
|
RzList *imports = rz_list_newf((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;
|
|
}
|
|
|
|
RzBinImport *import = RZ_NEW0(RzBinImport);
|
|
if (!import) {
|
|
rz_warn_if_reached();
|
|
continue;
|
|
}
|
|
import->classname = java_class_constant_pool_stringify_at(bin, class_name_index);
|
|
rz_str_replace_char(import->classname, '/', '.');
|
|
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_list_append(imports, import);
|
|
}
|
|
}
|
|
|
|
if (bin->interfaces) {
|
|
for (ut32 i = 0; i < bin->interfaces_count; ++i) {
|
|
if (!bin->interfaces[i]) {
|
|
continue;
|
|
}
|
|
|
|
RzBinImport *import = RZ_NEW0(RzBinImport);
|
|
if (!import) {
|
|
rz_warn_if_reached();
|
|
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);
|
|
rz_bin_import_free(import);
|
|
continue;
|
|
}
|
|
|
|
import->classname = java_class_constant_pool_stringify_at(bin, class_index);
|
|
rz_str_replace_char(import->classname, '/', '.');
|
|
import->name = strdup("*");
|
|
import->bind = RZ_BIN_BIND_WEAK_STR;
|
|
import->type = RZ_BIN_TYPE_IFACE_STR;
|
|
import->ordinal = i;
|
|
rz_list_append(imports, import);
|
|
}
|
|
}
|
|
|
|
return imports;
|
|
}
|
|
|
|
RZ_API void rz_bin_java_class_const_pool_as_text(RzBinJavaClass *bin, 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) {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
RZ_API void rz_bin_java_class_const_pool_as_json(RzBinJavaClass *bin, 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;
|
|
section->add = true;
|
|
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);
|
|
}
|
|
|
|
RZ_API RzList *rz_bin_java_class_as_sections(RzBinJavaClass *bin) {
|
|
rz_return_val_if_fail(bin, NULL);
|
|
|
|
RzList *sections = rz_list_newf(section_free);
|
|
if (!sections) {
|
|
return NULL;
|
|
}
|
|
ut32 iname;
|
|
char *tmp;
|
|
char secname[512];
|
|
ut64 end_offset;
|
|
if (bin->constant_pool) {
|
|
rz_list_append(sections,
|
|
new_section("class.constant_pool",
|
|
bin->constant_pool_offset,
|
|
bin->interfaces_offset,
|
|
RZ_PERM_R));
|
|
}
|
|
if (bin->interfaces) {
|
|
rz_list_append(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_list_find(sections, secname, compare_section_names); iname++) {
|
|
snprintf(secname, sizeof(secname), "class.fields.%s_%d.attr", tmp, iname);
|
|
}
|
|
free(tmp);
|
|
rz_list_append(sections, new_section(secname, field->offset, end_offset, RZ_PERM_R));
|
|
}
|
|
rz_list_append(sections,
|
|
new_section("class.fields",
|
|
bin->fields_offset,
|
|
bin->methods_offset,
|
|
RZ_PERM_R));
|
|
}
|
|
if (bin->methods) {
|
|
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_list_find(sections, secname, compare_section_names); iname++) {
|
|
snprintf(secname, sizeof(secname), "class.methods.%s_%d.attr", tmp, iname);
|
|
}
|
|
|
|
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_list_append(sections, new_section(secname, ac->code_offset, size, RZ_PERM_R | RZ_PERM_X));
|
|
break;
|
|
}
|
|
}
|
|
|
|
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_list_append(sections, new_section(secname, method->offset, end_offset, RZ_PERM_R));
|
|
free(tmp);
|
|
}
|
|
rz_list_append(sections,
|
|
new_section("class.methods",
|
|
bin->methods_offset,
|
|
bin->attributes_offset,
|
|
RZ_PERM_R));
|
|
}
|
|
if (bin->attributes) {
|
|
rz_list_append(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) {
|
|
return strcmp((const char *)a, (const char *)b);
|
|
}
|
|
|
|
RZ_API RzList *rz_bin_java_class_as_libraries(RzBinJavaClass *bin) {
|
|
rz_return_val_if_fail(bin, NULL);
|
|
|
|
RzList *list = rz_list_newf(free);
|
|
if (!list) {
|
|
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_list_find(list, tmp, compare_strings)) {
|
|
rz_list_append(list, tmp);
|
|
} else {
|
|
free(tmp);
|
|
}
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
RZ_API void rz_bin_java_class_interfaces_as_text(RzBinJavaClass *bin, 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
RZ_API void rz_bin_java_class_interfaces_as_json(RzBinJavaClass *bin, 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);
|
|
}
|