rizin/librz/bin/format/luac/luac_bin.c

372 lines
9.6 KiB
C

// SPDX-License-Identifier: LGPL-3.0-only
// SPDX-FileCopyrightText: 2021 Heersin <teablearcher@gmail.com>
#include "luac_common.h"
void luac_add_section(RzPVector /*<RzBinSection *>*/ *section_vec, char *name, ut64 offset, ut32 size, bool is_func) {
RzBinSection *bin_sec = RZ_NEW0(RzBinSection);
if (!bin_sec || !name) {
free(bin_sec);
return;
}
bin_sec->name = rz_str_new(name);
bin_sec->vaddr = bin_sec->paddr = offset;
bin_sec->size = bin_sec->vsize = size;
bin_sec->is_data = false;
bin_sec->bits = is_func ? sizeof(LUA_INSTRUCTION) * 8 : 8;
// bin_sec->has_strings = !is_func;
bin_sec->has_strings = false;
bin_sec->arch = "luac";
if (is_func) {
bin_sec->perm = RZ_PERM_R | RZ_PERM_X;
} else {
bin_sec->perm = RZ_PERM_R;
}
if (!rz_pvector_push(section_vec, bin_sec)) {
rz_bin_section_free(bin_sec);
}
}
void luac_add_symbol(RzList /*<RzBinSymbol *>*/ *symbol_list, char *name, ut64 offset, ut64 size, const char *type) {
RzBinSymbol *bin_sym = RZ_NEW0(RzBinSymbol);
if (!bin_sym) {
return;
}
bin_sym->name = rz_str_new(name);
bin_sym->vaddr = bin_sym->paddr = offset;
bin_sym->size = size;
bin_sym->type = type;
rz_list_append(symbol_list, bin_sym);
}
void luac_add_entry(RzList /*<RzBinAddr *>*/ *entry_list, ut64 offset, int entry_type) {
RzBinAddr *entry = RZ_NEW0(RzBinAddr);
if (!entry) {
return;
}
entry->vaddr = offset;
entry->paddr = offset;
entry->type = entry_type;
rz_list_append(entry_list, entry);
}
void luac_add_string(RzList /*<RzBinString *>*/ *string_list, char *string, ut64 offset, ut64 size) {
RzBinString *bin_string = RZ_NEW0(RzBinString);
if (!bin_string) {
return;
}
bin_string->paddr = offset;
bin_string->vaddr = offset;
bin_string->size = size;
bin_string->length = size;
bin_string->string = rz_str_new(string);
bin_string->type = RZ_STRING_ENC_UTF8;
rz_list_append(string_list, bin_string);
}
static void free_rz_section(RzBinSection *section) {
if (!section) {
return;
}
if (section->name) {
RZ_FREE(section->name);
}
if (section->format) {
RZ_FREE(section->format);
}
RZ_FREE(section);
}
static void free_rz_string(RzBinString *string) {
if (!string) {
return;
}
if (string->string) {
RZ_FREE(string->string);
}
RZ_FREE(string);
}
static void free_rz_addr(RzBinAddr *addr) {
if (!addr) {
return;
}
RZ_FREE(addr);
}
void luac_build_info_free(LuacBinInfo *bin_info) {
if (!bin_info) {
return;
}
rz_list_free(bin_info->entry_list);
rz_list_free(bin_info->symbol_list);
rz_pvector_free(bin_info->section_vec);
rz_list_free(bin_info->string_list);
free(bin_info);
}
LuacBinInfo *luac_build_info(LuaProto *proto) {
if (!proto) {
RZ_LOG_ERROR("Invalid luac file\n");
return NULL;
}
LuacBinInfo *ret = RZ_NEW0(LuacBinInfo);
if (!ret) {
return NULL;
}
ret->entry_list = rz_list_newf((RzListFree)free_rz_addr);
ret->symbol_list = rz_list_newf((RzListFree)rz_bin_symbol_free);
ret->section_vec = rz_pvector_new((RzPVectorFree)free_rz_section);
ret->string_list = rz_list_newf((RzListFree)free_rz_string);
if (!(ret->entry_list && ret->symbol_list && ret->section_vec && ret->string_list)) {
rz_list_free(ret->entry_list);
rz_list_free(ret->symbol_list);
rz_pvector_free(ret->section_vec);
rz_list_free(ret->string_list);
}
_luac_build_info(proto, ret);
// add entry of main
ut64 main_entry_offset;
main_entry_offset = proto->code_offset + proto->code_skipped;
luac_add_entry(ret->entry_list, main_entry_offset, RZ_BIN_ENTRY_TYPE_PROGRAM);
return ret;
}
static const char *get_tag_string(ut8 tag) {
switch (tag) {
case LUA_VNIL:
return "CONST_NIL";
case LUA_VTRUE:
case LUA_VFALSE:
return "CONST_BOOL";
case LUA_VSHRSTR:
case LUA_VLNGSTR:
return "CONST_STRING";
case LUA_VNUMFLT:
case LUA_VNUMINT:
return "CONST_NUM";
default:
return "CONST_UNKNOWN";
}
}
/* Heap allocated string */
static char *get_constant_symbol_name(char *proto_name, LuaConstEntry *entry) {
rz_return_val_if_fail(entry || proto_name, NULL);
ut8 tag = entry->tag;
char *ret;
int integer_value;
double float_value;
switch (tag) {
case LUA_VNIL:
ret = rz_str_newf("%s_const_nil", proto_name);
break;
case LUA_VTRUE:
ret = rz_str_newf("%s_const_true", proto_name);
break;
case LUA_VFALSE:
ret = rz_str_newf("%s_const_false", proto_name);
break;
case LUA_VSHRSTR:
case LUA_VLNGSTR:
rz_return_val_if_fail(entry->data, NULL);
ret = rz_str_newf("%s_const_%s", proto_name, (char *)entry->data);
break;
case LUA_VNUMFLT:
rz_return_val_if_fail(entry->data, NULL);
if (entry->data_len < sizeof(double)) {
return NULL;
}
float_value = rz_read_le_double(entry->data);
ret = rz_str_newf("%s_const_%f", proto_name, float_value);
break;
case LUA_VNUMINT:
rz_return_val_if_fail(entry->data, NULL);
if (entry->data_len < sizeof(st32)) {
return NULL;
}
integer_value = (st32)rz_read_le32(entry->data);
ret = rz_str_newf("%s_const_%d", proto_name, integer_value);
break;
default:
ret = rz_str_newf("%s_const_0x%llx", proto_name, entry->offset);
break;
}
return ret;
}
/* Heap allocated string */
static char *simple_build_upvalue_symbol(char *proto_name, LuaUpvalueEntry *entry) {
return rz_str_newf("%s_upvalue_0x%llx", proto_name, entry->offset);
}
static char *get_upvalue_symbol_name(char *proto_name, LuaUpvalueEntry *entry, char *debug_name) {
rz_return_val_if_fail(proto_name || entry, NULL);
if (debug_name == NULL) {
return simple_build_upvalue_symbol(proto_name, entry);
}
return rz_str_newf("%s_upvalue_%s", proto_name, debug_name);
}
void _luac_build_info(LuaProto *proto, LuacBinInfo *info) {
/* process proto header info */
char *section_name;
char *symbol_name;
char *proto_name;
char **upvalue_names = NULL;
RzListIter *iter;
int i = 0; // iter
ut64 current_offset;
ut64 current_size;
// 0. check if stripped (proto name is lost)
if (proto->name_size == 0 || proto->proto_name == NULL) {
// replace name with current offset
proto_name = rz_str_newf("fcn.%08llx", proto->offset);
} else {
proto_name = rz_str_new((char *)proto->proto_name);
}
// 1.1 set section name as function_name.header
current_offset = proto->offset;
current_size = proto->size;
section_name = rz_str_newf("%s.header", proto_name);
luac_add_section(info->section_vec, section_name, current_offset, current_size, false);
RZ_FREE(section_name);
// 1.2 set section name as function_name.code
current_offset = proto->code_offset;
current_size = proto->code_size;
section_name = rz_str_newf("%s.code", proto_name);
luac_add_section(info->section_vec, section_name, current_offset, current_size, true);
RZ_FREE(section_name);
// 1.3 set const section
current_offset = proto->const_offset;
current_size = proto->const_size;
section_name = rz_str_newf("%s.const", proto_name);
luac_add_section(info->section_vec, section_name, current_offset, current_size, false);
RZ_FREE(section_name);
// 1.4 upvalue section
current_offset = proto->upvalue_offset;
current_size = proto->upvalue_size;
section_name = rz_str_newf("%s.upvalues", proto_name);
luac_add_section(info->section_vec, section_name, current_offset, current_size, false);
RZ_FREE(section_name);
// 1.5 inner protos section
current_offset = proto->inner_proto_offset;
current_size = proto->inner_proto_size;
section_name = rz_str_newf("%s.protos", proto_name);
luac_add_section(info->section_vec, section_name, current_offset, current_size, false);
RZ_FREE(section_name);
// 1.6 debug section
current_offset = proto->debug_offset;
current_size = proto->debug_size;
section_name = rz_str_newf("%s.debug", proto_name);
luac_add_section(info->section_vec, section_name, current_offset, current_size, false);
RZ_FREE(section_name);
// 2.1 parse local var info
LuaLocalVarEntry *local_var_entry;
rz_list_foreach (proto->local_var_info_entries, iter, local_var_entry) {
luac_add_string(
info->string_list,
(char *)local_var_entry->varname,
local_var_entry->offset,
local_var_entry->varname_len);
}
// 2.2 parse debug_upvalues
size_t real_upvalue_cnt = RZ_MAX(rz_list_length(proto->upvalue_entries), rz_list_length(proto->dbg_upvalue_entries));
if (real_upvalue_cnt > 0) {
LuaDbgUpvalueEntry *debug_upv_entry;
upvalue_names = RZ_NEWS0(char *, real_upvalue_cnt);
if (!upvalue_names) {
free(proto_name);
return;
}
i = 0;
rz_list_foreach (proto->dbg_upvalue_entries, iter, debug_upv_entry) {
upvalue_names[i] = (char *)debug_upv_entry->upvalue_name;
luac_add_string(
info->string_list,
upvalue_names[i],
debug_upv_entry->offset,
debug_upv_entry->name_len);
i++;
}
}
// 3.1 construct constant symbols
LuaConstEntry *const_entry;
rz_list_foreach (proto->const_entries, iter, const_entry) {
symbol_name = get_constant_symbol_name(proto_name, const_entry);
if (!symbol_name) {
continue;
}
luac_add_symbol(
info->symbol_list,
symbol_name,
const_entry->offset,
const_entry->data_len,
get_tag_string(const_entry->tag));
if (const_entry->tag == LUA_VLNGSTR || const_entry->tag == LUA_VSHRSTR) {
luac_add_string(
info->string_list,
(char *)const_entry->data,
const_entry->offset,
const_entry->data_len);
}
RZ_FREE(symbol_name);
}
// 3.2 construct upvalue symbols
LuaUpvalueEntry *upvalue_entry;
i = 0;
rz_list_foreach (proto->upvalue_entries, iter, upvalue_entry) {
symbol_name = get_upvalue_symbol_name(proto_name, upvalue_entry, upvalue_names[i++]);
luac_add_symbol(
info->symbol_list,
symbol_name,
upvalue_entry->offset,
3,
"UPVALUE");
RZ_FREE(symbol_name);
}
// 4. parse sub proto
LuaProto *sub_proto;
rz_list_foreach (proto->proto_entries, iter, sub_proto) {
_luac_build_info(sub_proto, info);
}
free(upvalue_names);
free(proto_name);
}