rizin/librz/bin/format/java/class_attribute.c
2023-01-25 22:02:26 +08:00

601 lines
16 KiB
C

// SPDX-FileCopyrightText: 2021 deroad <wargio@libero.it>
// SPDX-License-Identifier: LGPL-3.0-only
#include "class_attribute.h"
static ut8 *make_string(RzBuffer *buf, st64 size) {
ut8 *buffer;
st64 buffer_size = rz_buf_size(buf);
if (size < 1 || size >= buffer_size) {
return NULL;
}
buffer = (ut8 *)malloc(size + 1);
if (!buffer || rz_buf_read(buf, buffer, size) < (size - 1)) {
rz_warn_if_reached();
free(buffer);
return NULL;
}
buffer[size] = 0;
return buffer;
}
static char *resolve_const_pool_index(ConstPool **pool, ut32 poolsize, ut32 index) {
const ConstPool *cpool;
if (index >= poolsize || !(cpool = pool[index])) {
return NULL;
}
return java_constant_pool_stringify(cpool);
}
bool java_attribute_set_unknown(Attribute *attr, RzBuffer *buf) {
attr->type = ATTRIBUTE_TYPE_UNKNOWN;
if (attr->attribute_length < 1) {
return true;
}
st64 size = (st64)attr->attribute_length;
rz_buf_seek(buf, size, RZ_BUF_CUR);
return true;
}
bool java_attribute_set_constantvalue(Attribute *attr, RzBuffer *buf) {
rz_warn_if_fail(attr->attribute_length == 2);
AttributeConstantValue *acv = RZ_NEW0(AttributeConstantValue);
if (!acv) {
rz_warn_if_reached();
return false;
}
if (!rz_buf_read_be16(buf, &acv->index)) {
free(acv);
return false;
}
attr->type = ATTRIBUTE_TYPE_CONSTANTVALUE;
attr->info = (void *)acv;
return true;
}
bool java_attribute_set_code(ConstPool **pool, ut32 poolsize, Attribute *attr, RzBuffer *buf, bool is_oak) {
AttributeCode *ac = RZ_NEW0(AttributeCode);
if (!ac) {
rz_warn_if_reached();
return false;
}
if (is_oak) {
ut8 max_stack;
if (!rz_buf_read8(buf, &max_stack)) {
free(ac);
return false;
}
ac->max_stack = max_stack;
ut8 max_locals;
if (!rz_buf_read8(buf, &max_locals)) {
free(ac);
return false;
}
ac->max_locals = max_locals;
ut16 code_length;
if (!rz_buf_read_be16(buf, &code_length)) {
free(ac);
return false;
}
ac->code_length = code_length;
} else {
if (!rz_buf_read_be16(buf, &ac->max_stack) ||
!rz_buf_read_be16(buf, &ac->max_locals) ||
!rz_buf_read_be32(buf, &ac->code_length)) {
free(ac);
return false;
}
}
ac->code_offset = attr->offset + (is_oak ? 10 : 14); // 6 bytes for attribute + 8 as code
rz_buf_seek(buf, ac->code_length, RZ_BUF_CUR);
if (!rz_buf_read_be16(buf, &ac->exceptions_count)) {
free(ac);
return false;
}
if (ac->exceptions_count > 0) {
ac->exceptions = RZ_NEWS0(ExceptionTable, ac->exceptions_count);
if (!ac->exceptions) {
free(ac);
rz_warn_if_reached();
return false;
}
for (ut32 i = 0; i < ac->exceptions_count; ++i) {
if (!rz_buf_read_be16(buf, &ac->exceptions[i].start_pc) ||
!rz_buf_read_be16(buf, &ac->exceptions[i].end_pc) ||
!rz_buf_read_be16(buf, &ac->exceptions[i].handler_pc) ||
!rz_buf_read_be16(buf, &ac->exceptions[i].catch_type)) {
free(ac);
return false;
}
}
}
if (!rz_buf_read_be16(buf, &ac->attributes_count)) {
free(ac->exceptions);
free(ac);
return false;
}
if (ac->attributes_count > 0) {
ac->attributes = RZ_NEWS0(Attribute *, ac->attributes_count);
if (!ac->attributes) {
free(ac->exceptions);
free(ac);
rz_warn_if_reached();
return false;
}
for (ut32 i = 0; i < ac->attributes_count; ++i) {
Attribute *attr = java_attribute_new(buf, UT64_MAX);
if (attr && java_attribute_resolve(pool, poolsize, attr, buf, false)) {
ac->attributes[i] = attr;
} else {
java_attribute_free(attr);
break;
}
}
}
attr->type = ATTRIBUTE_TYPE_CODE;
attr->info = (void *)ac;
return true;
}
bool java_attribute_set_sourcefile(Attribute *attr, RzBuffer *buf) {
rz_warn_if_fail(attr->attribute_length == 2);
AttributeSourceFile *asf = RZ_NEW0(AttributeSourceFile);
if (!asf) {
rz_warn_if_reached();
return false;
}
if (!rz_buf_read_be16(buf, &asf->index)) {
free(asf);
return false;
}
attr->type = ATTRIBUTE_TYPE_SOURCEFILE;
attr->info = (void *)asf;
return true;
}
bool java_attribute_set_sourcedebugextension(Attribute *attr, RzBuffer *buf) {
attr->type = ATTRIBUTE_TYPE_SOURCEDEBUGEXTENSION;
if (attr->attribute_length < 1) {
return true;
}
st64 size = (st64)attr->attribute_length;
/*
The debug_extension array holds a string, which must be in UTF-8 format. There is no terminating zero byte.
The string in the debug_extension item will be interpreted as extended debugging information.
*/
attr->info = make_string(buf, size);
if (!attr->info) {
rz_warn_if_reached();
return false;
}
return true;
}
bool java_attribute_set_linenumbertable(Attribute *attr, RzBuffer *buf) {
rz_warn_if_fail(attr->attribute_length >= 2);
AttributeLineNumberTable *alnt = RZ_NEW0(AttributeLineNumberTable);
if (!alnt) {
rz_warn_if_reached();
return false;
}
if (!rz_buf_read_be16(buf, &alnt->table_length)) {
free(alnt);
return false;
}
if (alnt->table_length > 0) {
alnt->table = RZ_NEWS0(LineNumberTable, alnt->table_length);
if (!alnt->table) {
free(alnt);
rz_warn_if_reached();
return false;
}
for (ut32 i = 0; i < alnt->table_length; ++i) {
if (!rz_buf_read_be16(buf, &alnt->table[i].start_pc) ||
!rz_buf_read_be16(buf, &alnt->table[i].line_number)) {
free(alnt->table);
free(alnt);
return false;
}
}
}
attr->type = ATTRIBUTE_TYPE_LINENUMBERTABLE;
attr->info = (void *)alnt;
return true;
}
bool java_attribute_set_localvariabletable(Attribute *attr, RzBuffer *buf) {
rz_warn_if_fail(attr->attribute_length >= 2);
AttributeLocalVariableTable *alvt = RZ_NEW0(AttributeLocalVariableTable);
if (!alvt) {
rz_warn_if_reached();
return false;
}
if (!rz_buf_read_be16(buf, &alvt->table_length)) {
free(alvt);
return false;
}
if (alvt->table_length > 0) {
alvt->table = RZ_NEWS0(LocalVariableTable, alvt->table_length);
if (!alvt->table) {
free(alvt);
rz_warn_if_reached();
return false;
}
for (ut32 i = 0; i < alvt->table_length; ++i) {
if (!rz_buf_read_be16(buf, &alvt->table[i].start_pc) ||
!rz_buf_read_be16(buf, &alvt->table[i].length) ||
!rz_buf_read_be16(buf, &alvt->table[i].name_index) ||
!rz_buf_read_be16(buf, &alvt->table[i].descriptor_index) ||
!rz_buf_read_be16(buf, &alvt->table[i].index)) {
free(alvt->table);
free(alvt);
return false;
}
}
}
attr->type = ATTRIBUTE_TYPE_LOCALVARIABLETABLE;
attr->info = (void *)alvt;
return true;
}
bool java_attribute_set_localvariabletypetable(Attribute *attr, RzBuffer *buf) {
rz_warn_if_fail(attr->attribute_length >= 2);
AttributeLocalVariableTypeTable *alvtt = RZ_NEW0(AttributeLocalVariableTypeTable);
if (!alvtt) {
rz_warn_if_reached();
return false;
}
if (!rz_buf_read_be16(buf, &alvtt->table_length)) {
free(alvtt);
return false;
}
if (alvtt->table_length > 0) {
alvtt->table = RZ_NEWS0(LocalVariableTypeTable, alvtt->table_length);
if (!alvtt->table) {
free(alvtt);
rz_warn_if_reached();
return false;
}
for (ut32 i = 0; i < alvtt->table_length; ++i) {
if (!rz_buf_read_be16(buf, &alvtt->table[i].start_pc) ||
!rz_buf_read_be16(buf, &alvtt->table[i].length) ||
!rz_buf_read_be16(buf, &alvtt->table[i].name_index) ||
!rz_buf_read_be16(buf, &alvtt->table[i].signature_index) ||
!rz_buf_read_be16(buf, &alvtt->table[i].index)) {
free(alvtt->table);
free(alvtt);
return false;
}
}
}
attr->type = ATTRIBUTE_TYPE_LOCALVARIABLETYPETABLE;
attr->info = (void *)alvtt;
return true;
}
bool java_attribute_set_module(Attribute *attr, RzBuffer *buf) {
rz_warn_if_fail(attr->attribute_length >= 16);
AttributeModule *am = RZ_NEW0(AttributeModule);
if (!am) {
rz_warn_if_reached();
return false;
}
ut16 *tmp16 = NULL;
if (!rz_buf_read_be16(buf, &am->module_name_index) ||
!rz_buf_read_be16(buf, &am->module_flags) ||
!rz_buf_read_be16(buf, &am->module_version_index)) {
goto java_attribute_set_module_bad;
return false;
}
if (!rz_buf_read_be16(buf, &am->requires_count)) {
goto java_attribute_set_module_bad;
}
if (am->requires_count > 0) {
am->
requires
= RZ_NEWS0(ModuleRequire, am->requires_count);
if (!am->requires) {
goto java_attribute_set_module_bad;
}
for (ut32 i = 0; i < am->requires_count; ++i) {
if (!rz_buf_read_be16(buf, &am->requires[i].index) ||
!rz_buf_read_be16(buf, &am->requires[i].flags) ||
!rz_buf_read_be16(buf, &am->requires[i].version_index)) {
goto java_attribute_set_module_bad;
}
}
}
if (!rz_buf_read_be16(buf, &am->exports_count)) {
goto java_attribute_set_module_bad;
}
if (am->exports_count > 0) {
am->exports = RZ_NEWS0(ModuleExport, am->exports_count);
if (!am->exports) {
goto java_attribute_set_module_bad;
}
for (ut32 i = 0; i < am->exports_count; ++i) {
if (!rz_buf_read_be16(buf, &am->exports[i].index) ||
!rz_buf_read_be16(buf, &am->exports[i].flags) ||
!rz_buf_read_be16(buf, &am->exports[i].to_count)) {
goto java_attribute_set_module_bad;
}
tmp16 = RZ_NEWS0(ut16, am->exports[i].to_count);
if (!tmp16) {
goto java_attribute_set_module_bad;
}
am->exports[i].to_indices = tmp16;
for (ut32 k = 0; k < am->exports[i].to_count; ++k) {
if (!rz_buf_read_be16(buf, &tmp16[k])) {
goto java_attribute_set_module_bad;
}
}
}
}
if (!rz_buf_read_be16(buf, &am->opens_count)) {
goto java_attribute_set_module_bad;
}
if (am->opens_count > 0) {
am->opens = RZ_NEWS0(ModuleOpen, am->opens_count);
if (!am->opens) {
goto java_attribute_set_module_bad;
}
for (ut32 i = 0; i < am->opens_count; ++i) {
if (!rz_buf_read_be16(buf, &am->opens[i].index) ||
!rz_buf_read_be16(buf, &am->opens[i].flags) ||
!rz_buf_read_be16(buf, &am->opens[i].to_count)) {
goto java_attribute_set_module_bad;
}
tmp16 = RZ_NEWS0(ut16, am->opens[i].to_count);
if (!tmp16) {
goto java_attribute_set_module_bad;
}
am->opens[i].to_indices = tmp16;
for (ut32 k = 0; k < am->opens[i].to_count; ++k) {
if (!rz_buf_read_be16(buf, &tmp16[k])) {
goto java_attribute_set_module_bad;
}
}
}
}
if (!rz_buf_read_be16(buf, &am->uses_count)) {
goto java_attribute_set_module_bad;
}
if (am->uses_count > 0) {
tmp16 = RZ_NEWS0(ut16, am->uses_count);
if (!tmp16) {
goto java_attribute_set_module_bad;
}
am->uses_index = tmp16;
for (ut32 i = 0; i < am->uses_count; ++i) {
if (!rz_buf_read_be16(buf, &tmp16[i])) {
goto java_attribute_set_module_bad;
}
}
}
if (!rz_buf_read_be16(buf, &am->provides_count)) {
goto java_attribute_set_module_bad;
}
if (am->provides_count > 0) {
am->provides = RZ_NEWS0(ModuleProvide, am->provides_count);
if (!am->provides) {
goto java_attribute_set_module_bad;
}
for (ut32 i = 0; i < am->provides_count; ++i) {
if (!rz_buf_read_be16(buf, &am->provides[i].index) ||
!rz_buf_read_be16(buf, &am->provides[i].with_count)) {
goto java_attribute_set_module_bad;
}
tmp16 = RZ_NEWS0(ut16, am->provides[i].with_count);
if (!tmp16) {
goto java_attribute_set_module_bad;
}
am->provides[i].with_indices = tmp16;
for (ut32 k = 0; k < am->provides[i].with_count; ++k) {
if (!rz_buf_read_be16(buf, &tmp16[k])) {
goto java_attribute_set_module_bad;
}
}
}
}
attr->type = ATTRIBUTE_TYPE_MODULE;
attr->info = (void *)am;
return true;
java_attribute_set_module_bad:
if (am->exports) {
for (ut32 i = 0; i < am->exports_count; ++i) {
free(am->exports[i].to_indices);
}
}
if (am->opens) {
for (ut32 i = 0; i < am->opens_count; ++i) {
free(am->opens[i].to_indices);
}
}
if (am->provides) {
for (ut32 i = 0; i < am->provides_count; ++i) {
free(am->provides[i].with_indices);
}
}
free(am->uses_index);
free(am->exports);
free(am->requires);
free(am);
return false;
}
bool java_attribute_set_modulepackages(Attribute *attr, RzBuffer *buf) {
rz_warn_if_fail(attr->attribute_length >= 2);
AttributeModulePackages *amp = RZ_NEW0(AttributeModulePackages);
if (!amp) {
rz_warn_if_reached();
return false;
}
if (!rz_buf_read_be16(buf, &amp->package_count)) {
free(amp);
return false;
}
if (amp->package_count > 0) {
amp->package_index = RZ_NEWS0(ut16, amp->package_count);
if (!amp->package_index) {
free(amp);
rz_warn_if_reached();
return false;
}
for (ut32 k = 0; k < amp->package_count; ++k) {
if (!rz_buf_read_be16(buf, &amp->package_index[k])) {
free(amp->package_index);
free(amp);
return false;
}
}
}
attr->type = ATTRIBUTE_TYPE_MODULEPACKAGES;
attr->info = (void *)amp;
return true;
}
bool java_attribute_set_modulemainclass(Attribute *attr, RzBuffer *buf) {
rz_warn_if_fail(attr->attribute_length == 2);
AttributeModuleMainClass *ammc = RZ_NEW0(AttributeModuleMainClass);
if (!ammc) {
rz_warn_if_reached();
return false;
}
if (!rz_buf_read_be16(buf, &ammc->main_class_index)) {
free(ammc);
return false;
}
attr->type = ATTRIBUTE_TYPE_MODULEMAINCLASS;
attr->info = (void *)ammc;
return true;
}
bool java_attribute_resolve(ConstPool **pool, ut32 poolsize, Attribute *attr, RzBuffer *buf, bool is_oak) {
char *name = resolve_const_pool_index(pool, poolsize, attr->attribute_name_index);
if (!name) {
return false;
}
bool result = false;
if (!strcmp(name, "ConstantValue")) {
result = java_attribute_set_constantvalue(attr, buf);
} else if (!strcmp(name, "Code")) {
result = java_attribute_set_code(pool, poolsize, attr, buf, is_oak);
} else if (!strcmp(name, "SourceFile")) {
result = java_attribute_set_sourcefile(attr, buf);
} else if (!strcmp(name, "SourceDebugExtension")) {
result = java_attribute_set_sourcedebugextension(attr, buf);
} else if (!strcmp(name, "LineNumberTable")) {
result = java_attribute_set_linenumbertable(attr, buf);
} else if (!strcmp(name, "LocalVariableTable")) {
result = java_attribute_set_localvariabletable(attr, buf);
} else if (!strcmp(name, "LocalVariableTypeTable")) {
result = java_attribute_set_localvariabletypetable(attr, buf);
} else if (!strcmp(name, "Module")) {
result = java_attribute_set_module(attr, buf);
} else if (!strcmp(name, "ModulePackages")) {
result = java_attribute_set_modulepackages(attr, buf);
} else if (!strcmp(name, "ModuleMainClass")) {
result = java_attribute_set_modulemainclass(attr, buf);
}
if (!result) {
result = java_attribute_set_unknown(attr, buf);
}
free(name);
return result;
}
Attribute *java_attribute_new(RzBuffer *buf, ut64 offset) {
Attribute *attr = RZ_NEW0(Attribute);
rz_return_val_if_fail(attr, NULL);
attr->offset = offset;
if (!rz_buf_read_be16(buf, &attr->attribute_name_index)) {
free(attr);
return NULL;
}
if (!rz_buf_read_be32(buf, &attr->attribute_length)) {
free(attr);
return NULL;
}
return attr;
}
void java_attribute_free(Attribute *attr) {
if (!attr || !attr->info) {
free(attr);
return;
}
if (attr->type == ATTRIBUTE_TYPE_CODE) {
AttributeCode *ac = (AttributeCode *)attr->info;
free(ac->exceptions);
if (ac->attributes) {
for (ut32 i = 0; i < ac->attributes_count; ++i) {
java_attribute_free(ac->attributes[i]);
}
free(ac->attributes);
}
} else if (attr->type == ATTRIBUTE_TYPE_LINENUMBERTABLE) {
AttributeLineNumberTable *alnt = (AttributeLineNumberTable *)attr->info;
free(alnt->table);
} else if (attr->type == ATTRIBUTE_TYPE_LOCALVARIABLETABLE) {
AttributeLocalVariableTable *alvt = (AttributeLocalVariableTable *)attr->info;
free(alvt->table);
} else if (attr->type == ATTRIBUTE_TYPE_MODULE) {
AttributeModule *am = (AttributeModule *)attr->info;
for (ut32 i = 0; i < am->exports_count; ++i) {
free(am->exports[i].to_indices);
}
for (ut32 i = 0; i < am->opens_count; ++i) {
free(am->opens[i].to_indices);
}
for (ut32 i = 0; i < am->provides_count; ++i) {
free(am->provides[i].with_indices);
}
free(am->uses_index);
free(am->exports);
free(am->requires);
} else if (attr->type == ATTRIBUTE_TYPE_MODULEPACKAGES) {
AttributeModulePackages *amp = (AttributeModulePackages *)attr->info;
free(amp->package_index);
}
free(attr->info);
free(attr);
}