rizin/librz/bin/format/luac/parse.c
Khairul Azhar Kasmiran c8878d3139
Convert %ll format specifiers to PFMT64 (#6267)
* Convert `%ll` format specifiers to PFMT64
* Remove ` ""` at end of some PFMT64
2026-04-22 05:57:04 +08:00

1000 lines
29 KiB
C

// SPDX-License-Identifier: LGPL-3.0-only
// SPDX-FileCopyrightText: 2017 pancake <pancake@nopcode.org>
// SPDX-FileCopyrightText: 2021 Heersin <teablearcher@gmail.com>
// SPDX-FileCopyrightText: 2025-2026 Sergey Sharshunov <s.sharshunov@gmail.com>
#include "librz/bin/format/luac/luac_common.h"
#ifdef RZ_DEBUG
#define CHECK_SIZE \
if (offset >= bf->size) { \
RZ_LOG_ERROR("Truncated Header (line: %d)\n", __LINE__); \
return 0; \
}
#else
#define CHECK_SIZE rz_return_val_if_fail((offset < bf->size), 0)
#endif
static ut8 read8(RzBuffer *buffer, ut64 *offset, ut8 *receiver) {
ut8 tmp;
if (!rz_buf_read_ble8_offset(buffer, offset, &tmp, false)) {
return 0;
}
*receiver = tmp;
return tmp;
}
#define READ8_CHECK_SIZE(buffer, offset, receiver) \
{ \
read8(buffer, &offset, &receiver); \
CHECK_SIZE; \
}
static int read8_check_val(RzBuffer *buffer, ut64 *offset, const ut16 val, const char *err) {
ut8 tmp = 0;
read8(buffer, offset, &tmp);
if (tmp != val) {
RZ_LOG_ERROR("%s\n", err);
return 0;
}
return 1;
}
#define READ8_CHECK_VAL(buffer, offset, val, err) \
read8_check_val(buffer, offset, val, err); \
CHECK_SIZE;
static ut64 lua_load_integer(RzBuffer *buffer, ut64 offset) {
ut64 x = 0;
rz_buf_read_le64_at(buffer, offset, &x);
return x;
}
static double lua_load_number(RzBuffer *buffer, ut64 offset) {
double x = 0;
rz_buf_read_le64_at(buffer, offset, (ut64 *)&x);
return x;
}
static ut32 lua_load_int(RzBuffer *buffer, ut64 offset) {
ut32 x = 0;
rz_buf_read_le32_at(buffer, offset, &x);
return x;
}
// return an offset to skip string, return 1 if no string (0x80)
static ut64 lua_parse_szint(RzBuffer *buffer, st32 *size, ut64 offset, ut64 data_size, ut8 minor) {
st32 result = 0;
st32 i = 0;
ut8 b;
st32 shift = 0;
while (offset + i < data_size) {
if (!rz_buf_read8_at(buffer, offset + i, &b)) {
return 0;
}
i++;
if (minor == 4) {
result = (result << 7) | (b & 0x7f);
if (b & 0x80) {
*size = result;
return i;
}
} else if (minor == 5) {
result |= (b & 0x7f) << shift;
if (!(b & 0x80)) {
*size = result;
return i;
}
shift += 7;
if (shift >= 64)
break;
}
}
return i;
}
static ut64 lua_parse_number(RzBuffer *buffer, st32 *size, ut64 offset, ut64 data_size, ut8 minor) {
if (minor >= 4) {
return lua_parse_szint(buffer, size, offset, data_size, minor);
}
if (minor <= 3) {
const size_t size_offset = sizeof(LUA_INT);
if (size_offset + offset > data_size) {
RZ_LOG_ERROR("file truncated, offset: %" PFMT64u ", data size: %" PFMT64u "\n", offset, data_size);
return 0;
}
*size = (st32)lua_load_int(buffer, offset);
return size_offset;
}
return 0;
}
static ut64 lua_parse_line_defined(LuaProto *proto, RzBuffer *buffer, ut64 offset, ut64 data_size, ut8 minor) {
ut64 size_offset = 0;
int line_defined = 0;
int last_line_defined = 0;
size_offset = lua_parse_number(buffer, &line_defined, offset, data_size, minor);
lua_check_error_offset(size_offset);
/* Set Proto Member */
proto->line_defined = (ut32)line_defined;
if (minor == 0) {
return size_offset;
}
const ut64 delta_offset = lua_parse_number(buffer, &last_line_defined, offset + size_offset, data_size, minor);
lua_check_error_offset(delta_offset);
size_offset += delta_offset;
proto->lastline_defined = (ut32)last_line_defined;
return size_offset;
}
static ut64 lua_parse_string(RzBuffer *buffer, ut8 **dest, int *str_len, ut64 offset, ut64 data_size, ut8 minor, ut8 num_size) {
ut64 size_offset = 0;
int ret_buf_size = 0;
int string_len = 0;
ut8 *ret = 0x00;
const ut64 base_offset = offset;
if (minor <= 2) {
ut16 tmp16 = 0;
if (!rz_buf_read_le16_at(buffer, offset, &tmp16)) {
return 0;
}
ret_buf_size = (int)tmp16;
size_offset = num_size;
offset += size_offset;
} else if (minor == 3) {
ut8 tmp = 0;
if (!rz_buf_read8_at(buffer, offset, &tmp)) {
return 0;
}
ret_buf_size = (int)tmp;
size_offset = 1;
if (ret_buf_size == 0xFF) {
offset += size_offset;
ut16 tmp16 = 0;
if (!rz_buf_read_le16_at(buffer, offset, &tmp16)) {
return 0;
}
ret_buf_size = (int)tmp16;
size_offset = num_size;
}
offset += size_offset;
} else if (minor >= 4) {
size_offset = lua_parse_szint(buffer, &ret_buf_size, offset, data_size, minor);
lua_check_error_offset(size_offset);
offset += size_offset;
}
/* no string */
if (ret_buf_size == 0) {
ret = NULL;
string_len = 0;
} else {
/* skip size byte */
string_len = ret_buf_size - 1;
ret = RZ_NEWS(ut8, ret_buf_size);
if (ret == NULL) {
string_len = 0;
} else {
rz_buf_read_at(buffer, offset, ret, string_len);
ret[string_len] = 0x00;
}
offset += string_len;
}
/* set to outside vars */
if (dest && str_len) {
*dest = ret;
*str_len = string_len;
} else {
RZ_LOG_ERROR("Cannot store string\n");
}
if (((minor == 0) && (ret_buf_size > 0)) || (minor == 2) || (minor == 5))
offset++;
return offset - base_offset;
}
static ut64 lua_parse_name(LuaProto *proto, RzBuffer *buffer, ut64 offset, ut64 data_size, ut8 minor) {
return lua_parse_string(buffer, &proto->proto_name, &proto->name_size, offset, data_size, minor, proto->num_size);
}
static ut64 lua_parse_code(LuaProto *proto, RzBuffer *buffer, ut64 offset, ut64 data_size, ut8 minor) {
ut64 size_offset = 0;
int code_size = 0; ///< instructions
size_offset = lua_parse_number(buffer, &code_size, offset, data_size, minor);
lua_check_error_offset(size_offset);
if (minor == 5) {
const ut64 aligned_size = (offset + size_offset + 3) & ~3;
size_offset = aligned_size - offset;
}
const ut8 instruction_size = (minor == 0) ? 8 : 4;
const ut64 total_size = code_size * instruction_size + size_offset;
if (total_size + offset > data_size) {
RZ_LOG_ERROR("Truncated Code at [0x%" PFMT64x "]\n", offset);
return 0;
}
/* Set Proto Member */
proto->code_size = code_size * instruction_size;
proto->code_skipped = size_offset;
return total_size;
}
static ut64 lua_parse_const_entry(const LuaProto *proto, RzBuffer *buffer, int index, ut64 offset, ut64 prev_offset, ut64 data_size, ut8 minor) {
ut8 *recv_data = NULL;
int data_len = 0;
LuaConstEntry *current_entry = lua_new_const_entry();
const ut64 k_vaddr_base = K_VADDRESS_BASE(proto->index);
current_entry->offset = offset;
current_entry->prev_offset = prev_offset;
current_entry->voffset = k_vaddr_base + prev_offset;
current_entry->string_start = current_entry->voffset + 2;
const ut64 base_offset = offset;
ut64 delta_offset = 0;
/* read TAG byte */
if (offset + 1 > data_size) {
return 0;
}
if (!rz_buf_read8_at(buffer, offset, &current_entry->tag)) {
return 0;
}
offset += 1;
ut8 tmp;
/* read data */
switch (current_entry->tag) {
case LUA_VNUMINT:
data_len = sizeof(LUA_NUMBER);
recv_data = RZ_NEWS(ut8, data_len);
rz_buf_read_le64_at(buffer, offset, (ut64 *)recv_data);
if (offset + data_len > data_size) {
RZ_FREE(recv_data);
return 0;
}
delta_offset = data_len;
if (minor == 3) {
current_entry->tag = LUA_VNUMFLT; // keep the same with 5.4 tag
break;
}
if ((minor == 0) || (minor == 1) || (minor == 2)) {
double intPart1;
const double r = *(double *)recv_data;
const double fractionalPart = modf(r, &intPart1);
if (fractionalPart == 0.0) {
current_entry->tag = LUA_VNUMINT; // keep the same with 5.4 tag
*(ut64 *)recv_data = (ut64)intPart1;
} else {
current_entry->tag = LUA_VNUMFLT; // keep the same with 5.4 tag
}
}
break;
case LUA_VNUMFLT:
#if 0
// TODO: need check on other platforms
if (minor >= 4) {
data_len = 8;
} else if (minor == 2) {
data_len = sizeof(LUA_NUMBER);
} else if (minor == 3) {
data_len = sizeof(LUA_INTEGER);
}
#else
data_len = sizeof(LUA_NUMBER);
if (minor == 3) {
data_len = sizeof(LUA_INTEGER);
}
#endif
recv_data = RZ_NEWS(ut8, data_len);
rz_buf_read_le64_at(buffer, offset, (ut64 *)recv_data);
if (offset + data_len > data_size) {
RZ_FREE(recv_data);
return 0;
}
delta_offset = data_len;
current_entry->tag = LUA_VNUMINT; // keep the same with 5.4 tag
break;
case LUA_VSHRSTR:
case LUA_VLNGSTR:
delta_offset = lua_parse_string(buffer, &recv_data, &data_len, offset, data_size, minor, proto->num_size);
lua_check_error_offset(delta_offset);
if (minor == 1)
delta_offset++;
if (!recv_data) {
if (!rz_buf_read8_at(buffer, offset + 1, &tmp)) {
return 0;
}
tmp -= 1;
data_len = 1;
recv_data = RZ_NEWS(ut8, data_len);
rz_write_ble8(recv_data, tmp);
current_entry->tag = LUA_VSTRING_IDX;
}
break;
case LUA_VFALSE:
case LUA_VTRUE:
recv_data = RZ_NEWS(ut8, 1);
data_len = 1;
delta_offset = 0;
if (minor == 4 || minor == 5) {
*recv_data = current_entry->tag == LUA_VTRUE ? 1 : 0;
} else {
if (!rz_buf_read8_at(buffer, offset, (ut8 *)recv_data)) {
return 0;
}
delta_offset++;
}
break;
case LUA_OPENWRT_INT32:
data_len = 4;
recv_data = RZ_NEWS(ut8, data_len);
rz_buf_read_le16_at(buffer, offset, (ut16 *)recv_data);
if (offset + data_len > data_size) {
RZ_FREE(recv_data);
return 0;
}
delta_offset += data_len;
break;
case LUA_TNIL:
default:
recv_data = NULL;
data_len = 0;
delta_offset = 0;
break;
}
offset += delta_offset;
current_entry->data = recv_data;
current_entry->data_len = data_len;
/* add to list */
rz_pvector_push(proto->const_entries, current_entry);
return offset - base_offset;
}
static ut64 lua_parse_consts(LuaProto *proto, RzBuffer *buffer, ut64 offset, ut64 data_size, ut8 minor) {
int consts_cnt = 0;
ut64 delta_offset = 0;
const ut64 base_offset = offset;
/* parse number of constants */
delta_offset = lua_parse_number(buffer, &consts_cnt, offset, data_size, minor);
lua_check_error_offset(delta_offset);
offset += delta_offset;
ut64 prev_offset = 0;
for (int i = 0; i < consts_cnt; ++i) {
delta_offset = lua_parse_const_entry(proto, buffer, i, offset, prev_offset, data_size, minor);
lua_check_error_offset(delta_offset);
offset += delta_offset;
prev_offset += delta_offset;
}
proto->const_size = offset - (base_offset + 1);
return offset - base_offset;
}
static ut64 lua_parse_upvalue_entry(const LuaProto *proto, RzBuffer *buffer, ut64 offset, ut64 data_size, ut8 minor) {
const ut64 base_offset = offset;
LuaUpvalueEntry *current_entry = lua_new_upvalue_entry();
current_entry->offset = base_offset;
current_entry->voffset = proto->index * 0x1000 + UPVALUE_OFFSET;
// no kind in lua 5.3
const int need_kind = (minor <= 3) ? 2 : 3;
if (offset + need_kind > data_size) {
return 0;
}
/* read instack/idx attr */
if (!rz_buf_read8_at(buffer, offset + 0, &current_entry->instack)) {
return 0;
}
if (!rz_buf_read8_at(buffer, offset + 1, &current_entry->idx)) {
return 0;
}
current_entry->kind = 0;
if (minor <= 3) {
if (!rz_buf_read8_at(buffer, offset + 2, &current_entry->kind)) {
return 0;
}
}
offset += need_kind;
/* add to list */
rz_pvector_push(proto->upvalue_entries, current_entry);
return offset - base_offset;
}
static ut64 lua_parse_upvalues(LuaProto *proto, RzBuffer *buffer, ut64 offset, ut64 data_size, ut8 minor) { // ?
int upvalues_cnt = 0;
ut64 delta_offset = 0;
const ut64 base_offset = offset;
/* parse number of upvalues */
delta_offset = lua_parse_number(buffer, &upvalues_cnt, offset, data_size, minor);
lua_check_error_offset(delta_offset);
offset += delta_offset;
for (int i = 0; i < upvalues_cnt; ++i) {
delta_offset = lua_parse_upvalue_entry(proto, buffer, offset, data_size, minor);
lua_check_error_offset(delta_offset);
offset += delta_offset;
}
proto->upvalue_size = offset - base_offset + 1;
if (minor == 2)
proto->upvalue_size++; // TODO: check this
return offset - base_offset;
}
static ut64 lua_parse_debug(LuaProto *proto, RzBuffer *buffer, ut64 offset, ut64 data_size, ut8 minor) {
int entries_cnt = 0;
ut64 delta_offset = 0;
ut64 base_offset = offset;
/* parse line info */
delta_offset = lua_parse_number(buffer, &entries_cnt, offset, data_size, minor);
lua_check_error_offset(delta_offset);
offset += delta_offset;
ut32 line_num = proto->line_defined;
for (int i = 0; i < entries_cnt; ++i) {
LuaLineinfoEntry *info_entry = lua_new_lineinfo_entry();
if (minor > 3) {
ut64 ad = PROTO_VADDRESS(proto->index);
// READ8(buffer, offset, info_entry->info_data);
read8(buffer, &offset, &info_entry->info_data);
info_entry->info_data = line_num += DEBUG_LINE_OFFSET((st32)info_entry->info_data);
info_entry->offset = ad += i * 4;
} else {
info_entry->offset = offset;
info_entry->info_data = lua_load_int(buffer, offset);
offset += sizeof(LUA_INT);
}
rz_list_append(proto->line_info_entries, info_entry);
}
if (minor >= 4) {
/* parse absline info */
delta_offset = lua_parse_szint(buffer, &entries_cnt, offset, data_size, minor);
lua_check_error_offset(delta_offset);
RZ_LOG_DEBUG("abs_info_entry entries_cnt: %d (offset: 0x%" PFMT64x ")\n", entries_cnt, offset);
offset += delta_offset;
for (int i = 0; i < entries_cnt; ++i) {
LuaAbsLineinfoEntry *abs_info_entry = lua_new_abs_lineinfo_entry();
abs_info_entry->offset = offset;
delta_offset = lua_parse_szint(buffer, &abs_info_entry->pc, offset, data_size, minor);
lua_check_error_offset(delta_offset);
offset += delta_offset;
delta_offset = lua_parse_szint(buffer, &abs_info_entry->line, offset, data_size, minor);
lua_check_error_offset(delta_offset);
offset += delta_offset;
rz_list_append(proto->abs_line_info_entries, abs_info_entry);
}
}
/* parse local vars */
delta_offset = lua_parse_number(buffer, &entries_cnt, offset, data_size, minor);
lua_check_error_offset(delta_offset);
offset += delta_offset;
proto->local_var_offset = offset;
proto->local_var_size = 0;
ut64 prev_offset = 0;
for (int i = 0; i < entries_cnt; ++i) {
LuaLocalVarEntry *var_entry = lua_new_local_var_entry();
var_entry->offset = offset;
const ut64 k_vaddr_base = VAR_VADDRESS_BASE(proto->index);
var_entry->voffset = k_vaddr_base + prev_offset;
/* string */
delta_offset = lua_parse_string(
buffer,
&var_entry->varname, &var_entry->varname_len,
offset, data_size, minor, proto->num_size);
lua_check_error_offset(delta_offset);
if (minor == 1)
delta_offset++;
offset += delta_offset;
/* start pc -- int */
delta_offset = lua_parse_number(buffer, &var_entry->start_pc, offset, data_size, minor);
lua_check_error_offset(delta_offset);
offset += delta_offset;
/* end pc -- int */
delta_offset = lua_parse_number(buffer, &var_entry->end_pc, offset, data_size, minor);
lua_check_error_offset(delta_offset);
offset += delta_offset;
prev_offset += (offset - var_entry->offset);
var_entry->var_size = (offset - var_entry->offset);
proto->local_var_size += var_entry->var_size;
rz_pvector_push(proto->local_var_info_entries, var_entry);
}
/* parse upvalue */
delta_offset = lua_parse_number(buffer, &entries_cnt, offset, data_size, minor);
lua_check_error_offset(delta_offset);
offset += delta_offset;
for (int i = 0; i < entries_cnt; ++i) {
LuaUpvalueEntry *up = rz_pvector_at(proto->upvalue_entries, i);
if (!up) {
RZ_LOG_ERROR("invalid upvalue");
continue;
}
up->name_offset = offset;
delta_offset = lua_parse_string(
buffer,
&up->upvalue_name,
&up->name_len,
offset, data_size, minor, proto->num_size);
lua_check_error_offset(delta_offset);
offset += delta_offset;
}
proto->debug_size = offset - base_offset + 1;
return offset - base_offset;
}
static ut64 lua_parse_protos(LuaProto *proto, RzBuffer *buffer, LuaHeaderInfo *header, ut64 offset, ut64 data_size) {
rz_return_val_if_fail(proto, 0);
int proto_cnt = 0;
ut64 delta_offset = 0;
const ut64 base_offset = offset; // store origin offset
const ut8 minor = header->minor;
delta_offset = lua_parse_number(buffer, &proto_cnt, offset, data_size, minor);
lua_check_error_offset(delta_offset);
offset += delta_offset;
for (int i = 0; i < proto_cnt; ++i) {
LuaProto *current_proto = lua_parse_body(buffer, header, offset, data_size);
lua_return_if_null(current_proto);
rz_list_append(proto->proto_entries, current_proto);
offset += current_proto->size - 1; // update offset
}
// return the delta between offset and base_offset
return offset - base_offset;
}
LuaProto *lua_parse_body(RzBuffer *buffer, LuaHeaderInfo *header, ut64 base_offset, ut64 data_size) {
LuaProto *ret_proto = lua_new_proto_entry(); /* constructed proto for return */
rz_return_val_if_fail(ret_proto, NULL);
ret_proto->index = header->proto_count;
header->proto_count++;
ret_proto->num_size = header->is_openwrt ? 4 : sizeof(LUA_NUMBER);
const ut8 minor = header->minor;
ut64 delta_offset = 0;
// start parsing
ut64 offset = base_offset; /* record offset */
/* record offset of main proto */
ret_proto->offset = offset;
if ((minor == 0) || (minor == 1) || (minor == 3) || (minor == 4)) {
/* parse proto name of main proto */
delta_offset = lua_parse_name(ret_proto, buffer, offset, data_size, minor);
if (minor == 1) {
if (delta_offset > sizeof(LUA_NUMBER)) {
delta_offset += 2;
}
}
lua_check_error_offset_proto(delta_offset, ret_proto);
offset += delta_offset;
}
/* parse line defined info */
delta_offset = lua_parse_line_defined(ret_proto, buffer, offset, data_size, minor);
lua_check_error_offset_proto(delta_offset, ret_proto);
offset += delta_offset;
if ((minor == 1) && (ret_proto->line_defined > 0)) {
offset++;
}
if (minor == 0) {
int size_upvalues = 0;
ut8 tmp;
if (!rz_buf_read8_at(buffer, offset, &tmp)) {
return 0;
}
offset++;
size_upvalues = tmp;
(void)size_upvalues;
}
/* parse num params max_stack_size */
if (offset + 3 > data_size) {
lua_free_proto_entry(ret_proto);
return NULL;
}
if (!rz_buf_read8_at(buffer, offset + 0, &ret_proto->num_params) ||
!rz_buf_read8_at(buffer, offset + 1, &ret_proto->is_vararg) ||
!rz_buf_read8_at(buffer, offset + 2, &ret_proto->max_stack_size)) {
lua_free_proto_entry(ret_proto);
return NULL;
}
offset += 3;
if (minor == 0) {
/* parse debug */
ret_proto->debug_offset = offset;
delta_offset = lua_parse_debug(ret_proto, buffer, offset, data_size, minor);
lua_check_error_offset_proto(delta_offset, ret_proto);
offset += delta_offset;
/* parse constants */
ret_proto->const_offset = offset;
delta_offset = lua_parse_consts(ret_proto, buffer, offset, data_size, minor);
lua_check_error_offset_proto(delta_offset, ret_proto);
offset += delta_offset;
/* parse inner protos */
ret_proto->inner_proto_offset = offset;
delta_offset = lua_parse_protos(ret_proto, buffer, header, offset, data_size);
lua_check_error_offset_proto(delta_offset, ret_proto);
offset += delta_offset;
/* specially handle recursive protos size */
ret_proto->inner_proto_size = offset - ret_proto->inner_proto_offset;
}
/* parse code */
ret_proto->code_offset = offset;
delta_offset = lua_parse_code(ret_proto, buffer, offset, data_size, minor);
lua_check_error_offset_proto(delta_offset, ret_proto);
offset += delta_offset;
if (minor == 0) {
ret_proto->size = offset - base_offset + 1;
return ret_proto;
}
/* parse constants */
ret_proto->const_offset = offset + 1;
delta_offset = lua_parse_consts(ret_proto, buffer, offset, data_size, minor);
lua_check_error_offset_proto(delta_offset, ret_proto);
offset += delta_offset;
if ((minor == 1) || (minor == 2)) {
/* parse inner protos */
ret_proto->inner_proto_offset = offset;
delta_offset = lua_parse_protos(ret_proto, buffer, header, offset, data_size);
lua_check_error_offset_proto(delta_offset, ret_proto);
offset += delta_offset;
/* specially handle recursive protos size */
ret_proto->inner_proto_size = offset - ret_proto->inner_proto_offset;
}
if (minor > 2) {
/* parse upvalues */
ret_proto->upvalue_offset = offset;
delta_offset = lua_parse_upvalues(ret_proto, buffer, offset, data_size, minor);
lua_check_error_offset_proto(delta_offset, ret_proto);
offset += delta_offset;
/* parse inner protos */
ret_proto->inner_proto_offset = offset;
delta_offset = lua_parse_protos(ret_proto, buffer, header, offset, data_size);
lua_check_error_offset_proto(delta_offset, ret_proto);
offset += delta_offset;
/* specially handle recursive protos size */
ret_proto->inner_proto_size = offset - ret_proto->inner_proto_offset;
}
if (minor == 5) {
/* parse proto name of main proto */
delta_offset = lua_parse_name(ret_proto, buffer, offset, data_size, minor);
lua_check_error_offset_proto(delta_offset, ret_proto);
if (ret_proto->proto_name) {
header->src_file_name = rz_str_dup((char *)ret_proto->proto_name);
}
offset += delta_offset;
}
if (minor == 2) {
/* parse upvalues */
ret_proto->upvalue_offset = offset;
delta_offset = lua_parse_upvalues(ret_proto, buffer, offset, data_size, minor);
lua_check_error_offset_proto(delta_offset, ret_proto);
offset += delta_offset;
/* parse proto name of main proto */
delta_offset = lua_parse_name(ret_proto, buffer, offset, data_size, minor);
lua_check_error_offset_proto(delta_offset, ret_proto);
offset += delta_offset;
}
/* parse debug */
ret_proto->debug_offset = offset;
delta_offset = lua_parse_debug(ret_proto, buffer, offset, data_size, minor);
lua_check_error_offset_proto(delta_offset, ret_proto);
offset += delta_offset;
ret_proto->size = offset - base_offset + 1;
return ret_proto;
}
size_t parse_header(const RzBinFile *bf, LuaHeaderInfo *header) {
RzBuffer *buffer = bf->buf;
ut8 major_minor_version = 0x00;
ut64 offset = strlen(LUAC_MAGIC);
CHECK_SIZE;
rz_buf_read8_at(buffer, offset, &major_minor_version); /* 1-byte in fact */
const ut8 major = (ut8)(major_minor_version & 0xF0) >> 4;
const ut8 minor = (ut8)(major_minor_version & 0x0F);
if (major != 5) {
RZ_LOG_ERROR("currently support lua 5.x only\n");
return 0;
}
if (minor > 5) {
RZ_LOG_ERROR("lua 5.%c not support now\n", header->minor + '0');
return 0;
}
offset++;
CHECK_SIZE;
/* read header members from work buffer */
/* is official compiler (minor > 0) */
if (minor > 0) {
READ8_CHECK_SIZE(buffer, offset, header->format);
}
/* check luac data if minor > 2 */
if (minor > 2) {
offset += strlen(LUAC_DATA);
CHECK_SIZE;
} else {
READ8_CHECK_SIZE(buffer, offset, header->endianness);
}
/* get int size on 5.1, 5.2, 5.3, 5.5 */
if (minor == 5) {
st32 tmp = 0;
const ut64 size_offset = lua_parse_szint(buffer, &tmp, offset, bf->size, minor);
header->int_size = (ut32)tmp;
offset += size_offset;
CHECK_SIZE;
const ut32 test_valid = lua_load_int(buffer, offset);
(void)test_valid;
offset += header->int_size;
} else if (minor <= 3) { ///< TODO: ????? need 3?
READ8_CHECK_SIZE(buffer, offset, header->int_size);
header->integer_size = 4;
} else {
header->int_size = 4;
}
CHECK_SIZE;
/* get size_t size on 5.1, 5.2, 5.3 */
if (minor <= 3) {
READ8_CHECK_SIZE(buffer, offset, header->size_t_size);
}
/* get instruction size */
if (minor == 5) {
st32 tmp = 0;
const ut64 size_offset = lua_parse_szint(buffer, &tmp, offset, bf->size, minor);
header->instruction_size = (ut32)tmp;
offset += size_offset;
CHECK_SIZE;
const ut32 test_valid = lua_load_int(buffer, offset);
offset += header->instruction_size;
CHECK_SIZE;
if (test_valid != LUAC5_INT_VALIDATION) {
RZ_LOG_ERROR("Integer format does not match with the expected integer\n");
return 0;
}
} else {
READ8_CHECK_SIZE(buffer, offset, header->instruction_size);
}
if (minor == 0) {
READ8_CHECK_VAL(buffer, &offset, 6, "Wrong size of SIZE_OP");
READ8_CHECK_VAL(buffer, &offset, 8, "Wrong size of SIZE_A");
READ8_CHECK_VAL(buffer, &offset, 9, "Wrong size of SIZE_B");
READ8_CHECK_VAL(buffer, &offset, 9, "Wrong size of SIZE_C");
}
/* get lua integer size on luac > 5.3 */
if (minor == 5) {
st32 tmp = 0;
const ut64 size_offset = lua_parse_szint(buffer, &tmp, offset, bf->size, minor);
header->integer_size = (ut32)tmp;
offset += size_offset;
CHECK_SIZE;
const ut64 test_valid = lua_load_integer(buffer, offset);
offset += header->integer_size;
(void)test_valid;
CHECK_SIZE;
/*
if (test_valid != LUAC5_INT_VALIDATION) {
RZ_LOG_ERROR("Integer format does not match with the expected integer\n");
return NULL;
}
*/
} else if ((minor == 3) || (minor == 4)) {
READ8_CHECK_SIZE(buffer, offset, header->integer_size);
}
/* get lua number size */
if (minor == 5) {
st32 tmp = 0;
const ut64 size_offset = lua_parse_szint(buffer, &tmp, offset, bf->size, minor);
header->number_size = (ut32)tmp;
offset += size_offset;
CHECK_SIZE;
const double test_valid = lua_load_number(buffer, offset);
offset += header->number_size;
CHECK_SIZE;
if (test_valid != LUAC5_NUMBER_VALIDATION) {
RZ_LOG_ERROR("Number format does not match with the expected number\n");
return 0;
}
} else {
READ8_CHECK_SIZE(buffer, offset, header->number_size);
}
if (minor == 0) {
const double number_valid = lua_load_number(buffer, offset);
if (number_valid != LUAC0_NUMBER_VALIDATION) {
RZ_LOG_ERROR("Number format does not match with the expected number (expected: %f, actual: %f)\n", LUAC0_NUMBER_VALIDATION, number_valid);
return 0;
}
offset += header->number_size;
CHECK_SIZE;
}
/* check lua number is integral on 5.1, 5.2 */
if ((minor == 1) || (minor == 2)) {
if (!rz_buf_read8_at(buffer, offset, &header->is_number_integral)) {
return 0;
}
offset++;
if (header->is_number_integral > 0x01) {
header->is_openwrt = true;
if (!rz_buf_read8_at(buffer, offset, &header->is_number_integral)) {
return 0;
}
offset += 1;
}
}
if (minor == 2) {
offset += strlen(LUAC_DATA);
}
CHECK_SIZE;
/* check int and num values on 5.3, 5.4 */
if ((minor == 3) || (minor == 4)) {
ut64 integer_valid = lua_load_integer(buffer, offset);
if (integer_valid != LUAC_INT_VALIDATION) {
RZ_LOG_ERROR("Integer format does not match with the expected integer\n");
return 0;
}
offset += header->integer_size;
CHECK_SIZE;
double number_valid = lua_load_number(buffer, offset);
if (number_valid != LUAC_NUMBER_VALIDATION) {
RZ_LOG_ERROR("Number format does not match with the expected number (expected: %f, actual: %f)\n", LUAC_NUMBER_VALIDATION, number_valid);
return 0;
}
offset += header->number_size;
CHECK_SIZE;
}
header->psize = offset;
header->major = major;
header->minor = minor;
if (header->minor >= 3) {
/* size upvalues */
if ((header->minor == 3) || (header->minor == 4)) {
ut8 size_upvalues = 0;
READ8_CHECK_SIZE(buffer, offset, size_upvalues);
} else {
st32 size_upvalues = 0;
const ut64 delta_offset = lua_parse_szint(buffer, &size_upvalues, offset, bf->size, minor);
lua_check_error_offset(delta_offset);
offset += delta_offset;
}
}
/* parse source file name */
if ((minor == 2) || (minor == 5))
return offset;
int name_len;
lua_parse_string(buffer, ((ut8 **)&(header->src_file_name)), &name_len, offset, bf->size, header->minor, header->is_openwrt ? 4 : sizeof(LUA_NUMBER));
(void)name_len;
return offset;
}
RzBinInfo *lua_parse_bin_info(const RzBinFile *bf, const LuaHeaderInfo *header) {
/* Common Ret */
RzBinInfo *ret = RZ_NEW0(RzBinInfo);
rz_return_val_if_fail(ret, NULL);
ret->has_va = true;
ret->file = rz_str_dup(bf->file);
ret->type = rz_str_newf("Lua %c.%c compiled file", header->major + '0', header->minor + '0');
ret->bclass = rz_str_dup("Lua compiled file");
ret->rclass = rz_str_dup("luac");
ret->arch = rz_str_dup("luac");
ret->machine = rz_str_newf("Lua %c.%c VM%s", header->major + '0', header->minor + '0', header->is_openwrt ? " (openwrt)" : "");
ret->os = rz_str_newf("%c.%c", header->major + '0', header->minor + '0');
ret->cpu = rz_str_newf("%c.%c", header->major + '0', header->minor + '0');
ret->bits = 32;
/* official format */
if (header->format != LUAC_FORMAT) {
ret->compiler = rz_str_dup("Unofficial Lua Compiler");
return ret;
}
ret->compiler = rz_str_dup("Official Lua Compiler");
if ((header->minor == 2) || (header->minor == 1))
return ret;
/* Check Size */
// TODO : remove this check and process different compiler options
if (header->minor == 0) {
if ((header->instruction_size != sizeof(ut64)) ||
(header->number_size != sizeof(LUA_NUMBER))) {
RZ_LOG_ERROR("Size definition does not match with the expected size (minor = 0)\n");
return ret;
}
} else {
if ((header->instruction_size != sizeof(LUA_INSTRUCTION)) ||
(header->integer_size != sizeof(LUA_INTEGER)) ||
(header->number_size != sizeof(LUA_NUMBER))) {
RZ_LOG_ERROR("Size definition does not match with the expected size\n");
return ret;
}
}
if (header->minor < 4) {
if (header->size_t_size != sizeof(size_t)) {
RZ_LOG_ERROR("Size definition does not match with the expected size\n");
return ret;
}
}
if (header->minor == 4) {
if (header->int_size != sizeof(LUA_INT)) {
RZ_LOG_ERROR("Int size definition does not match with the expected size\n");
return ret;
}
}
/* put source file info into GUID */
ret->guid = rz_str_dup(header->src_file_name ? header->src_file_name + 1 : "stripped");
return ret;
}