Files
rizin/librz/core/golang.c
T
doomedraven d94fee433c perf(golang): optimize string recovery and basic block scanning
- Refactor rz_core_analysis_resolve_golang_strings to maintain persistent dynamically resized basic block and string buffers, eliminating per-block malloc/free churn
- Unify multi-instruction signature matching across all 8 architectures with fast bitmask pre-filtering before decode callbacks
- Implement table-driven GoSignatureTable dispatch and const-qualify GoAsmPattern and GoSignature definitions
- Reuse basic block instruction offsets (block->ninstr) to avoid redundant instruction disassembly during string recovery
- Guard rz_io_nread_at against errors and short reads, and reject invalid candidate addresses in decode callbacks
2026-10-11 17:58:50 +08:00

2147 lines
73 KiB
C

// SPDX-FileCopyrightText: 2022-2024 deroad <[email protected]>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_core.h>
#define GO_MAX_STRING_SIZE 0x4000
#define GO_MAX_TABLE_SIZE 0x10000
#define GO_1_2 (12)
#define GO_1_16 (116)
#define GO_1_18 (118)
#define GO_1_20 (120)
#define IS_GOPCLNTAB_1_2_LE(x) (x[0] == 0xfb && x[1] == 0xff && x[2] == 0xff && x[3] == 0xff)
#define IS_GOPCLNTAB_1_2_BE(x) (x[3] == 0xfb && x[2] == 0xff && x[1] == 0xff && x[0] == 0xff)
#define IS_GOPCLNTAB_1_16_LE(x) (x[0] == 0xfa && x[1] == 0xff && x[2] == 0xff && x[3] == 0xff)
#define IS_GOPCLNTAB_1_16_BE(x) (x[3] == 0xfa && x[2] == 0xff && x[1] == 0xff && x[0] == 0xff)
#define IS_GOPCLNTAB_1_18_LE(x) (x[0] == 0xf0 && x[1] == 0xff && x[2] == 0xff && x[3] == 0xff)
#define IS_GOPCLNTAB_1_18_BE(x) (x[3] == 0xf0 && x[2] == 0xff && x[1] == 0xff && x[0] == 0xff)
#define IS_GOPCLNTAB_1_20_LE(x) (x[0] == 0xf1 && x[1] == 0xff && x[2] == 0xff && x[3] == 0xff)
#define IS_GOPCLNTAB_1_20_BE(x) (x[3] == 0xf1 && x[2] == 0xff && x[1] == 0xff && x[0] == 0xff)
typedef struct go_pc_line_table_t {
RzIO *io;
ut64 vaddr;
ut32 size;
ut16 version;
bool big_endian;
ut64 text_start;
// quantum is the min instruction size for the program counter.
// i386: 1, amd64: 1, wasm: 1, s390x: 2, arm: 4, arm64: 4, mips: 4, mips: 4, ppc: 4, riscv: 4
ut32 quantum;
ut32 ptrsize;
ut32 nfunctab;
ut32 nfiletab;
ut32 functabsize;
// data offsets
ut64 funcnametab;
ut64 cutab;
ut64 filetab;
ut64 pctab;
ut64 funcdata;
ut64 functab;
} GoPcLnTab;
typedef struct go_string_recover_t {
RzCore *core;
ut64 pc;
ut8 *bytes;
ut32 size;
ut32 op_size;
ut32 n_recovered;
char *raw_buf;
char *flag_buf;
size_t buf_cap;
} GoStrRecover;
typedef struct go_string_info_t {
ut64 addr;
ut64 size;
ut64 xref;
} GoStrInfo;
typedef struct go_asm_pattern_t {
const ut8 *pattern;
const ut8 *mask;
ut32 size;
bool xrefs;
} GoAsmPattern;
typedef bool (*GoDecodeCb)(RzCore *core, GoStrInfo *info, ut64 pc, const ut8 *buffer, const ut32 size);
typedef struct go_signature_t {
const GoAsmPattern *pasm;
GoDecodeCb decode;
} GoSignature;
typedef struct go_signature_table_t {
const GoSignature *sigs;
size_t n_sigs;
} GoSignatureTable;
#define GO_SIG_TABLE_ENTRY(s) { (s), RZ_ARRAY_SIZE(s) }
typedef ut32 (*GoStrRecoverCb)(GoStrRecover *ctx);
ut32 go_func_tab_field_size(GoPcLnTab *pclntab) {
if (pclntab->version >= GO_1_18) {
return 4;
}
return pclntab->ptrsize;
}
ut64 go_uintptr(GoPcLnTab *pclntab, ut8 *bytes) {
if (pclntab->ptrsize == 4) {
return rz_read_ble32(bytes, pclntab->big_endian);
}
return rz_read_ble64(bytes, pclntab->big_endian);
}
ut64 go_offset(GoPcLnTab *pclntab, ut32 n_word) {
ut8 bytes[8];
ut64 location = pclntab->vaddr + 8 + (n_word * pclntab->ptrsize);
if (0 > rz_io_nread_at(pclntab->io, location, bytes, sizeof(bytes))) {
return UT64_MAX;
}
return go_uintptr(pclntab, bytes);
}
ut64 go_data(GoPcLnTab *pclntab, ut32 n_word) {
ut64 offset = go_offset(pclntab, n_word);
if (offset == UT64_MAX) {
return UT64_MAX;
}
return pclntab->vaddr + offset;
}
static const char *pclntab_version_str(GoPcLnTab *pclntab) {
switch (pclntab->version) {
case GO_1_2:
return "go 1.2";
case GO_1_16:
return "go 1.16-1.17";
case GO_1_18:
return "go 1.18-1.19";
case GO_1_20:
return "go 1.20+";
default:
return "go unknown";
}
}
#define is_addr_outside(x) ((x) <= begin || (x) >= end)
static bool is_pclntab_valid(GoPcLnTab *pclntab) {
ut64 begin = pclntab->vaddr + 8;
ut64 end = pclntab->vaddr + pclntab->size;
if (pclntab->version > GO_1_2) {
if (is_addr_outside(pclntab->funcnametab)) {
return false;
} else if (is_addr_outside(pclntab->cutab)) {
return false;
} else if (is_addr_outside(pclntab->pctab)) {
return false;
} else if (is_addr_outside(pclntab->funcdata)) {
return false;
}
}
if (pclntab->version >= GO_1_18 && !pclntab->text_start) {
return false;
}
if (is_addr_outside(pclntab->filetab)) {
return false;
} else if (is_addr_outside(pclntab->functab)) {
return false;
} else if (pclntab->functabsize >= (pclntab->size - 8)) {
return false;
}
return true;
}
#undef is_addr_outside
static void add_new_func_symbol(RzCore *core, const char *name, ut64 vaddr) {
RzBinFile *bf = rz_bin_cur(core->bin);
if (!bf || !bf->o || !bf->o->symbols) {
return;
}
ut64 paddr = rz_io_v2p(core->io, vaddr);
RzBinSymbol *symbol = rz_bin_symbol_new(name, paddr, vaddr);
if (!symbol) {
RZ_LOG_ERROR("Failed allocate new go symbol\n");
return;
}
symbol->bind = RZ_BIN_BIND_GLOBAL_STR;
symbol->type = RZ_BIN_TYPE_FUNC_STR;
if (!rz_pvector_push(bf->o->symbols, symbol)) {
RZ_LOG_ERROR("Failed append new go symbol to symbols list\n");
rz_bin_symbol_free(symbol);
}
if (!strcmp(name, "main.main")) {
rz_flag_set(core->flags, "main", vaddr, 1);
}
}
static char *detect_go_package_from_name(const char *string) {
/**
* Ignore names that starts with:
* - `main.` because is related to the main package.
* - `type..` because is just the definition of a function linked to a defined go `typedef`
*/
if (rz_str_startswith(string, "main.") ||
rz_str_startswith(string, "type:") ||
rz_str_startswith(string, "type..")) {
return NULL;
}
// remove `vendor/` because is useless.
if (rz_str_startswith(string, "vendor/")) {
string += strlen("vendor/");
}
size_t length = strlen(string);
const char *end = NULL;
for (size_t i = 0; i < length; i++) {
if (string[i] == '.' && !end) {
end = string + i;
} else if (string[i] == '.' && end) {
break;
} else if (string[i] == '/') {
end = NULL;
} else if (string[i] == '(') {
if (!end) {
end = string + i;
}
break;
} else if (string[i] == ':') {
if (!end) {
end = string + i;
}
break;
} else if (string[i] == '{') {
if (!end) {
end = string + i;
}
break;
}
}
if (!end) {
// a end was not found, so we fail.
return NULL;
}
size_t new_len = end - string;
char *libname = rz_str_ndup(string, new_len);
if (!libname) {
RZ_LOG_ERROR("Failed to duplicate libname\n");
return NULL;
}
return libname;
}
static int compare_string(const char *s1, const char *s2, void *user) {
return strcmp(s1, s2);
}
static void add_new_library_from_name(RzCore *core, const char *name) {
char *libname = detect_go_package_from_name(name);
if (!libname) {
return;
}
RzBinFile *bf = rz_bin_cur(core->bin);
if (!bf || !bf->o) {
free(libname);
return;
}
if (!bf->o->libs) {
bf->o->libs = rz_pvector_new(free);
}
if (rz_pvector_find(bf->o->libs, libname, (RzPVectorComparator)compare_string, NULL)) {
free(libname);
return;
}
if (!rz_pvector_push(bf->o->libs, libname)) {
RZ_LOG_ERROR("Failed append new go libname to libs list\n");
free(libname);
}
}
static ut32 core_recover_golang_functions_go_1_18_plus(RzCore *core, GoPcLnTab *pclntab) {
const char *go_ver = pclntab_version_str(pclntab);
rz_core_notify_done(core, "Found %s pclntab data.", go_ver);
ut8 tmp8[8];
char name[256];
char *flag = NULL;
ut32 num_syms = 0;
ut64 func_ptr = 0, func_off = 0, name_ptr = 0, name_off = 0;
pclntab->nfunctab = (ut32)go_offset(pclntab, 0);
pclntab->nfiletab = (ut32)go_offset(pclntab, 1);
pclntab->funcnametab = go_data(pclntab, 3);
pclntab->cutab = go_data(pclntab, 4);
pclntab->filetab = go_data(pclntab, 5);
pclntab->pctab = go_data(pclntab, 6);
pclntab->funcdata = go_data(pclntab, 7);
pclntab->functab = go_data(pclntab, 7);
pclntab->functabsize = ((pclntab->nfunctab * 2) + 1) * go_func_tab_field_size(pclntab);
pclntab->ptrsize = 4; // GO 1.18+ uses ut32 words.
if (!is_pclntab_valid(pclntab)) {
rz_core_notify_error(core, "Invalid %s pclntab (invalid table).", go_ver);
return 0;
}
rz_flag_space_push(core->flags, RZ_FLAGS_FS_SYMBOLS);
for (ut32 i = 0, ptr = 0; i < pclntab->nfunctab; ++i, ptr += (pclntab->ptrsize * 2)) {
ut64 offset = pclntab->functab + ptr;
// reads the value of the function pointer
if (0 > rz_io_nread_at(pclntab->io, offset, tmp8, sizeof(tmp8))) {
RZ_LOG_ERROR("Failed to read go function address at 0x%08" PFMT64x "\n", offset);
break;
}
func_ptr = pclntab->text_start + go_uintptr(pclntab, tmp8);
// reads the value of the function data offset
if (0 > rz_io_nread_at(pclntab->io, offset + pclntab->ptrsize, tmp8, sizeof(tmp8))) {
RZ_LOG_ERROR("Failed to read go function data address at 0x%08" PFMT64x "\n", offset);
break;
}
func_off = go_uintptr(pclntab, tmp8);
name_ptr = pclntab->functab + func_off + pclntab->ptrsize;
// reads the location of the function name within funcnametab
if (0 > rz_io_nread_at(pclntab->io, name_ptr, tmp8, sizeof(ut32))) {
RZ_LOG_ERROR("Failed to read go function name address at 0x%08" PFMT64x "\n", offset);
break;
}
name_off = pclntab->funcnametab + rz_read_ble32(tmp8, pclntab->big_endian);
// ignore failures, we can always create a new name.
memset(name, 0, sizeof(name));
(void)rz_io_nread_at(pclntab->io, name_off, (ut8 *)name, sizeof(name));
name[sizeof(name) - 1] = 0;
RZ_LOG_INFO("Recovered symbol at 0x%08" PFMT64x " with name '%s'\n", func_ptr, name);
add_new_library_from_name(core, name);
if (rz_str_utf8_ansi_cols(name) > 0) {
// always add it before filtering the name.
add_new_func_symbol(core, name, func_ptr);
rz_name_filter(name, 0, true);
} else {
rz_strf(name, "fcn.pclntab.unknown.%08" PFMT64x, func_ptr);
add_new_func_symbol(core, name, func_ptr);
}
flag = rz_str_newf("sym.go.%s", name);
rz_flag_set(core->flags, flag, func_ptr, 1);
free(flag);
num_syms++;
}
rz_flag_space_pop(core->flags);
return num_syms;
}
static ut32 core_recover_golang_functions_go_1_16(RzCore *core, GoPcLnTab *pclntab) {
const char *go_ver = pclntab_version_str(pclntab);
rz_core_notify_done(core, "Found %s pclntab data.", go_ver);
ut8 tmp8[8];
char name[256];
char *flag = NULL;
ut32 num_syms = 0;
ut64 func_ptr = 0, func_off = 0, name_ptr = 0, name_off = 0;
pclntab->nfunctab = (ut32)go_offset(pclntab, 0);
pclntab->nfiletab = (ut32)go_offset(pclntab, 1);
pclntab->funcnametab = go_data(pclntab, 2);
pclntab->cutab = go_data(pclntab, 3);
pclntab->filetab = go_data(pclntab, 4);
pclntab->pctab = go_data(pclntab, 5);
pclntab->funcdata = go_data(pclntab, 6);
pclntab->functab = go_data(pclntab, 6);
pclntab->functabsize = ((pclntab->nfunctab * 2) + 1) * go_func_tab_field_size(pclntab);
if (!is_pclntab_valid(pclntab)) {
rz_core_notify_error(core, "Invalid %s pclntab (invalid table).", go_ver);
return 0;
}
rz_flag_space_push(core->flags, RZ_FLAGS_FS_SYMBOLS);
for (ut32 i = 0, ptr = 0; i < pclntab->nfunctab; ++i, ptr += (pclntab->ptrsize * 2)) {
ut64 offset = pclntab->functab + ptr;
// reads the value of the function pointer
if (0 > rz_io_nread_at(pclntab->io, offset, tmp8, sizeof(tmp8))) {
RZ_LOG_ERROR("Failed to read go function address at 0x%08" PFMT64x "\n", offset);
break;
}
func_ptr = go_uintptr(pclntab, tmp8);
// reads the value of the function data offset
if (0 > rz_io_nread_at(pclntab->io, offset + pclntab->ptrsize, tmp8, sizeof(tmp8))) {
RZ_LOG_ERROR("Failed to read go function data address at 0x%08" PFMT64x "\n", offset);
break;
}
func_off = go_uintptr(pclntab, tmp8);
name_ptr = pclntab->functab + func_off + pclntab->ptrsize;
// reads the location of the function name within funcnametab
if (0 > rz_io_nread_at(pclntab->io, name_ptr, tmp8, sizeof(ut32))) {
RZ_LOG_ERROR("Failed to read go function name address at 0x%08" PFMT64x "\n", offset);
break;
}
name_off = pclntab->funcnametab + rz_read_ble32(tmp8, pclntab->big_endian);
// ignore failures, we can always create a new name.
memset(name, 0, sizeof(name));
(void)rz_io_nread_at(pclntab->io, name_off, (ut8 *)name, sizeof(name));
name[sizeof(name) - 1] = 0;
RZ_LOG_INFO("Recovered symbol at 0x%08" PFMT64x " with name '%s'\n", func_ptr, name);
add_new_library_from_name(core, name);
if (rz_str_utf8_ansi_cols(name) > 0) {
// always add it before filtering the name.
add_new_func_symbol(core, name, func_ptr);
rz_name_filter(name, 0, true);
} else {
rz_strf(name, "fcn.pclntab.unknown.%08" PFMT64x, func_ptr);
add_new_func_symbol(core, name, func_ptr);
}
flag = rz_str_newf("sym.go.%s", name);
rz_flag_set(core->flags, flag, func_ptr, 1);
free(flag);
num_syms++;
}
rz_flag_space_pop(core->flags);
return num_syms;
}
// Valid for golang 1.2 -> 1.15
static ut32 core_recover_golang_functions_go_1_2(RzCore *core, GoPcLnTab *pclntab) {
const char *go_ver = pclntab_version_str(pclntab);
rz_core_notify_done(core, "Found %s pclntab data.", go_ver);
ut8 tmp8[8];
char name[256];
char *flag = NULL;
ut32 num_syms = 0;
ut64 func_ptr = 0, func_off = 0, name_ptr = 0, name_off = 0;
if (0 > rz_io_nread_at(pclntab->io, pclntab->vaddr + 8, tmp8, sizeof(tmp8))) {
return 0;
}
pclntab->nfunctab = (ut32)go_uintptr(pclntab, tmp8);
pclntab->functab = pclntab->vaddr + 8 + pclntab->ptrsize;
pclntab->functabsize = ((pclntab->nfunctab * 2) + 1) * go_func_tab_field_size(pclntab);
if (0 > rz_io_nread_at(pclntab->io, pclntab->functab + pclntab->functabsize, tmp8, sizeof(ut32))) {
RZ_LOG_ERROR("Failed to read go functab at 0x%08" PFMT64x "\n", pclntab->functab);
return 0;
}
pclntab->filetab = pclntab->vaddr + rz_read_ble32(tmp8, pclntab->big_endian);
if (0 > rz_io_nread_at(pclntab->io, pclntab->filetab, tmp8, sizeof(ut32))) {
RZ_LOG_ERROR("Failed to read go filetab at 0x%08" PFMT64x "\n", pclntab->filetab);
return 0;
}
pclntab->nfiletab = rz_read_ble32(tmp8, pclntab->big_endian);
if (!is_pclntab_valid(pclntab)) {
rz_core_notify_error(core, "Invalid %s pclntab (invalid table).", go_ver);
return 0;
}
rz_flag_space_push(core->flags, RZ_FLAGS_FS_SYMBOLS);
for (ut32 i = 0, ptr = 0; i < pclntab->nfunctab; ++i, ptr += (pclntab->ptrsize * 2)) {
ut64 offset = pclntab->functab + ptr;
// reads the value of the function pointer
if (0 > rz_io_nread_at(pclntab->io, offset, tmp8, sizeof(tmp8))) {
RZ_LOG_ERROR("Failed to read go function address at 0x%08" PFMT64x "\n", offset);
break;
}
func_ptr = go_uintptr(pclntab, tmp8);
// reads the value of the function data offset
if (0 > rz_io_nread_at(pclntab->io, offset + pclntab->ptrsize, tmp8, sizeof(tmp8))) {
RZ_LOG_ERROR("Failed to read go function address data at 0x%08" PFMT64x "\n", offset);
break;
}
func_off = go_uintptr(pclntab, tmp8);
name_ptr = pclntab->vaddr + func_off + pclntab->ptrsize;
// reads the location of the function name within funcnametab
if (0 > rz_io_nread_at(pclntab->io, name_ptr, tmp8, sizeof(ut32))) {
RZ_LOG_ERROR("Failed to read go function name address at 0x%08" PFMT64x "\n", offset);
break;
}
name_off = pclntab->vaddr + rz_read_ble32(tmp8, pclntab->big_endian);
// ignore failures, we can always create a new name.
memset(name, 0, sizeof(name));
(void)rz_io_nread_at(pclntab->io, name_off, (ut8 *)name, sizeof(name));
name[sizeof(name) - 1] = 0;
RZ_LOG_INFO("Recovered symbol at 0x%08" PFMT64x " with name '%s'\n", func_ptr, name);
add_new_library_from_name(core, name);
if (rz_str_utf8_ansi_cols(name) > 0) {
// always add it before filtering the name.
add_new_func_symbol(core, name, func_ptr);
rz_name_filter(name, 0, true);
} else {
rz_strf(name, "fcn.pclntab.unknown.%08" PFMT64x, func_ptr);
add_new_func_symbol(core, name, func_ptr);
}
flag = rz_str_newf("sym.go.%s", name);
rz_flag_set(core->flags, flag, func_ptr, 1);
free(flag);
num_syms++;
}
rz_flag_space_pop(core->flags);
return num_syms;
}
#define GO_ABI_PROLOGUE_WINDOW 24
#define GO_ABI_MAX_SAMPLES 512
/**
* \brief Checks if a Go symbol is compiler boilerplate (ABI wrappers, closures, inittasks).
*/
static bool rz_golang_is_compiler_wrapper_symbol(const char *name) {
if (RZ_STR_ISEMPTY(name)) {
return true;
}
if (!strncmp(name, "sym.go.", 7)) {
name += 7;
}
return strstr(name, ".abi0") ||
strstr(name, ".abiinternal") ||
strstr(name, ".deferwrap") ||
strstr(name, "..inittask") ||
!strncmp(name, "type:", 5) ||
!strncmp(name, "go:", 3);
}
/**
* \brief Sample a function's prologue to vote on register ABI (R14 holds g) vs stack ABI (g from TLS).
*/
static void rz_golang_vote_abi_prologue(RzIO *io, ut64 func_addr, ut32 *reg_votes, ut32 *stack_votes) {
ut8 code[GO_ABI_PROLOGUE_WINDOW + 8] = { 0 };
if (0 > rz_io_nread_at(io, func_addr, code, sizeof(code))) {
return;
}
for (ut32 k = 0; k <= GO_ABI_PROLOGUE_WINDOW; k++) {
// 49 3B 66 10 / 4D 3B 66 10 (CMPQ SP|R12, 16(R14)) -> Register ABI (R14 holds g)
if ((code[k] == 0x49 || code[k] == 0x4d) && code[k + 1] == 0x3b && code[k + 2] == 0x66 && code[k + 3] == 0x10) {
(*reg_votes)++;
return;
}
// 64|65 48|4C 8B /r with SIB 0x25 (MOVQ FS|GS:[disp32], reg) -> Stack ABI (g loaded from TLS)
if ((code[k] == 0x64 || code[k] == 0x65) && (code[k + 1] == 0x48 || code[k + 1] == 0x4c) && code[k + 2] == 0x8b &&
(code[k + 3] & 0xc7) == 0x04 && code[k + 4] == 0x25) {
(*stack_votes)++;
return;
}
}
}
typedef struct {
RzCore *core;
ut32 samples_count;
ut32 reg_votes;
ut32 stack_votes;
} GolangVoteCtx;
static bool rz_golang_sample_flag_for_abi(RzFlagItem *fi, void *user) {
GolangVoteCtx *ctx = (GolangVoteCtx *)user;
if (rz_golang_is_compiler_wrapper_symbol(fi->name)) {
return true;
}
if (ctx->samples_count >= GO_ABI_MAX_SAMPLES) {
return false;
}
ctx->samples_count++;
rz_golang_vote_abi_prologue(ctx->core->io, fi->offset, &ctx->reg_votes, &ctx->stack_votes);
return true;
}
typedef struct {
RzCore *core;
const char *cc;
} GolangAnalyseCtx;
static bool analyse_golang_symgo_function(RzFlagItem *fi, void *user) {
GolangAnalyseCtx *ctx = (GolangAnalyseCtx *)user;
RzCore *core = ctx->core;
if (rz_golang_is_compiler_wrapper_symbol(fi->name)) {
return true;
}
rz_core_analysis_fcn(core, fi->offset, UT64_MAX, RZ_ANALYSIS_XREF_TYPE_NULL, 1);
if (ctx->cc) {
RzAnalysisFunction *fcn = rz_analysis_get_function_at(core->analysis, fi->offset);
if (fcn) {
fcn->cc = ctx->cc;
}
}
return true;
}
static const char *golang_detect_cc(RzCore *core, const RzSpace *symbols) {
const char *asm_arch = rz_config_get(core->config, "asm.arch");
ut32 asm_bits = rz_config_get_i(core->config, "asm.bits");
if (RZ_STR_ISEMPTY(asm_arch) || asm_bits == 32 || strcmp(asm_arch, "x86") != 0) {
return "golang";
}
// Run statistical prologue vote across sampled functions to determine active ABI
GolangVoteCtx vote_ctx = {
.core = core,
.samples_count = 0,
.reg_votes = 0,
.stack_votes = 0
};
rz_flag_foreach_space_glob(core->flags, "sym.go.*", symbols, rz_golang_sample_flag_for_abi, &vote_ctx);
ut32 total_votes = vote_ctx.reg_votes + vote_ctx.stack_votes;
if (total_votes >= 8 && vote_ctx.stack_votes * 10 >= total_votes * 9) {
RZ_LOG_INFO("Golang ABI prologue vote: Stack ABI (votes: %u reg, %u stack)\n",
vote_ctx.reg_votes, vote_ctx.stack_votes);
return "golang_abi0";
}
if (total_votes >= 8 && vote_ctx.reg_votes * 10 >= total_votes * 9) {
RZ_LOG_INFO("Golang ABI prologue vote: Register ABI (votes: %u reg, %u stack)\n",
vote_ctx.reg_votes, vote_ctx.stack_votes);
}
return "golang";
}
/**
* \brief Analyse Golang symbols matching "sym.go.*"
* \param core RzCore Pointer
*/
static void analyse_golang_symbols(RzCore *core) {
const RzSpace *symbols = rz_flag_space_get(core->flags, RZ_FLAGS_FS_SYMBOLS);
if (!symbols) {
return;
}
const char *detected_cc = golang_detect_cc(core, symbols);
if (detected_cc && !rz_analysis_cc_exist(core->analysis, detected_cc)) {
detected_cc = NULL;
}
RzStrConstPool *cpool = rz_analysis_get_const_pool(core->analysis);
GolangAnalyseCtx actx = {
.core = core,
.cc = (detected_cc && cpool) ? rz_str_constpool_get(cpool, detected_cc) : NULL
};
rz_flag_foreach_space_glob(core->flags, "sym.go.*", symbols, analyse_golang_symgo_function, &actx);
}
/**
* \brief Loads Golang base runtime types (string, slice, eface, iface, hmap) into type database.
*
* \param core RzCore Pointer
*/
static void rz_golang_load_types(RzCore *core) {
RzTypeDB *typedb = rz_analysis_get_type_db(core->analysis);
if (!typedb) {
return;
}
char *types_dir = rz_path_system(core->sys_path, RZ_SDB_TYPES);
if (!types_dir) {
return;
}
int bits = rz_analysis_get_bits(core->analysis);
char tmp[64];
char *dbpath = rz_file_path_join(types_dir, rz_strf(tmp, "types-golang-%d.sdb", bits));
if (rz_type_db_load_sdb(typedb, dbpath)) {
RZ_LOG_DEBUG("golang: loaded type database \"%s\"\n", dbpath);
}
free(dbpath);
free(types_dir);
}
/**
* \brief Sorts the recovered libraries.
*
* \param core RzCore Pointer
* \return Number of recovered libraries
*/
static ut32 sort_recovered_library(RzCore *core) {
RzBinFile *bf = rz_bin_cur(core->bin);
if (!bf || !bf->o || !bf->o->libs) {
return 0;
}
rz_pvector_sort(bf->o->libs, (RzPVectorComparator)compare_string, NULL);
return rz_pvector_len(bf->o->libs);
}
/**
* \brief reads pclntab table in go binaries and recovers functions.
* Follows the code https://github.com/golang/go/blob/master/src/debug/gosym/pclntab.go#L188
* \param core The RzCore to use
*
* \return Returns true when 1 or more symbols have been recovered.
*/
RZ_API bool rz_core_analysis_recover_golang_functions(RzCore *core) {
rz_return_val_if_fail(core && core->bin && core->io, false);
RzBinObject *o = rz_bin_cur_object(core->bin);
const RzPVector *sections = o ? rz_bin_object_get_sections_all(o) : NULL;
RzPVector *symbols_vec = o ? (RzPVector *)rz_bin_object_get_symbols(o) : NULL;
void **iter;
RzBinSection *section;
ut32 num_syms = 0;
GoPcLnTab pclntab = { 0 };
ut8 header[8] = { 0 };
rz_pvector_foreach (sections, iter) {
section = *iter;
// on ELF files the pclntab sections is named .gopclntab, but on macho is __gopclntab
if (section->vsize >= 16 && strstr(section->name, "gopclntab")) {
pclntab.vaddr = section->vaddr;
pclntab.size = section->vsize;
} else if (!pclntab.text_start &&
(!strcmp(section->name, ".text") || strstr(section->name, "__text"))) {
pclntab.text_start = section->vaddr;
}
}
if (!pclntab.vaddr) {
RzBinSymbol *symbol;
rz_pvector_foreach (symbols_vec, iter) {
symbol = *iter;
// on PE files the pclntab sections is inside .rdata, so rizin creates a symbol for it
if (symbol->size >= 16 && !strcmp(symbol->name, "gopclntab")) {
pclntab.vaddr = symbol->vaddr;
pclntab.size = symbol->size;
break;
}
}
}
if (!pclntab.vaddr) {
rz_core_notify_done(core, "Could not find go pclntab section");
return false;
} else if (0 > rz_io_nread_at(core->io, pclntab.vaddr, header, sizeof(header))) {
RZ_LOG_ERROR("Failed to read gopclntab at address %" PFMT64x "\n", pclntab.vaddr);
return false;
} else if (header[4] != 0 || header[5] != 0 ||
(header[6] != 1 && header[6] != 2 && header[6] != 4) || // pc quantum
(header[7] != 4 && header[7] != 8)) { // pointer size
rz_core_notify_error(core, "Invalid go pclntab (invalid pc quantum or pointer size).");
return false;
}
pclntab.io = core->io;
pclntab.quantum = header[6];
pclntab.ptrsize = header[7];
if (IS_GOPCLNTAB_1_20_BE(header) || IS_GOPCLNTAB_1_20_LE(header)) {
pclntab.version = GO_1_20;
pclntab.big_endian = IS_GOPCLNTAB_1_20_BE(header);
num_syms = core_recover_golang_functions_go_1_18_plus(core, &pclntab);
} else if (IS_GOPCLNTAB_1_18_BE(header) || IS_GOPCLNTAB_1_18_LE(header)) {
pclntab.version = GO_1_18;
pclntab.big_endian = IS_GOPCLNTAB_1_18_BE(header);
num_syms = core_recover_golang_functions_go_1_18_plus(core, &pclntab);
} else if (IS_GOPCLNTAB_1_16_BE(header) || IS_GOPCLNTAB_1_16_LE(header)) {
pclntab.version = GO_1_16;
pclntab.big_endian = IS_GOPCLNTAB_1_16_BE(header);
num_syms = core_recover_golang_functions_go_1_16(core, &pclntab);
} else if (IS_GOPCLNTAB_1_2_BE(header) || IS_GOPCLNTAB_1_2_LE(header)) {
pclntab.version = GO_1_2;
pclntab.big_endian = IS_GOPCLNTAB_1_2_BE(header);
num_syms = core_recover_golang_functions_go_1_2(core, &pclntab);
} else {
ut32 magic = rz_read_be32(header);
rz_core_notify_error(core, "Invalid go pclntab (unknown version: 0x%x). Please open an issue.", magic);
return false;
}
if (num_syms) {
ut32 num_libs = sort_recovered_library(core);
rz_core_notify_done(core, "Recovered %u symbols and saved them at sym.go.*", num_syms);
rz_core_notify_done(core, "Recovered %u go packages", num_libs);
rz_core_notify_begin(core, "Analyze all flags starting with sym.go. (aF @@f:sym.go.*)");
analyse_golang_symbols(core);
rz_core_notify_done(core, "Analyze all flags starting with sym.go. (aF @@f:sym.go.*)");
rz_golang_load_types(core);
return true;
}
rz_core_notify_error(core, "Could not recover any symbol from the go pclntab.");
return false;
}
static bool add_new_bin_string(RzCore *core, const char *string, ut64 vaddr, ut32 size) {
RzBinString *bstr;
RzBin *bin = core->bin;
RzBinFile *bf = rz_bin_cur(bin);
if (!bf || !bf->o || !bf->o->strings) {
return false;
}
bstr = rz_bin_object_get_string_at(bf->o, vaddr, true);
if (bstr && bstr->vaddr == vaddr && bstr->size == size) {
return true;
}
char *dup_str = rz_str_ndup(string, size);
if (!dup_str) {
RZ_LOG_ERROR("Failed allocate new go string buffer\n");
return false;
}
ut64 paddr = rz_io_v2p(core->io, vaddr);
bstr = RZ_NEW0(RzBinString);
if (!bstr) {
RZ_LOG_ERROR("Failed allocate new go string\n");
free(dup_str);
return false;
}
bstr->paddr = paddr;
bstr->vaddr = vaddr;
bstr->length = bstr->size = size;
bstr->string = dup_str;
bstr->type = RZ_STRING_ENC_UTF8;
if (!rz_bin_string_database_add(bf->o->strings, bstr)) {
RZ_LOG_ERROR("Failed append new go string to strings database\n");
rz_bin_string_free(bstr);
return false;
}
return true;
}
static bool recover_string_at(GoStrRecover *ctx, ut64 str_addr, ut64 str_size) {
// check that the values are acceptable.
if (str_size < 2 || str_size > GO_MAX_STRING_SIZE || str_addr < 1 || str_addr == UT64_MAX) {
return false;
}
RzBinObject *obj = rz_bin_cur_object(ctx->core->bin);
if (!obj || !rz_bin_get_section_at(obj, str_addr, true)) {
// skip any possible string from invalid sections.
return false;
}
// skip possible pointers that matches to symbols flags, because these are already handled.
RzFlagItem *fi = rz_flag_get_by_spaces(ctx->core->flags, str_addr, RZ_FLAGS_FS_SYMBOLS, NULL);
if (fi && !strncmp(fi->name, "sym.", 4)) {
return false;
}
const size_t n_prefix = strlen("str.");
size_t req_cap = (size_t)str_size + n_prefix + 1;
if (req_cap > ctx->buf_cap) {
size_t new_cap = RZ_MAX(req_cap, 256);
char *new_raw = realloc(ctx->raw_buf, new_cap);
if (!new_raw) {
return false;
}
ctx->raw_buf = new_raw;
char *new_flag = realloc(ctx->flag_buf, new_cap);
if (!new_flag) {
return false;
}
ctx->flag_buf = new_flag;
ctx->buf_cap = new_cap;
}
char *raw = ctx->raw_buf;
int nread = rz_io_nread_at(ctx->core->io, str_addr, (ut8 *)raw, str_size);
if (nread < 0) {
RZ_LOG_ERROR("Failed to read string value at address %" PFMT64x "\n", str_addr);
return false;
}
if (!ctx->core->io->ff && (ut64)nread != str_size) {
return false;
}
raw[str_size] = 0;
if (rz_str_utf8_ansi_cols(raw) != str_size) {
return false;
}
char *flag = ctx->flag_buf;
memcpy(flag, "str.", n_prefix);
memcpy(flag + n_prefix, raw, str_size);
flag[str_size + n_prefix] = 0;
// apply any filter to the new flag name
rz_name_filter(flag + n_prefix, str_size, true);
// verify is a valid flag.
if (rz_str_utf8_ansi_cols(flag) < 5) {
return false;
}
// add new string to string list
if (!add_new_bin_string(ctx->core, raw, str_addr, str_size)) {
return false;
}
// remove any flag already set at this address
rz_flag_unset_all_off(ctx->core->flags, str_addr);
// add string to string flag space.
rz_flag_space_push(ctx->core->flags, RZ_FLAGS_FS_STRINGS);
rz_flag_set(ctx->core->flags, flag, str_addr, str_size);
rz_flag_space_pop(ctx->core->flags);
ctx->n_recovered++;
return true;
}
static inline bool go_asm_pattern_match(const GoAsmPattern *pasm, const ut8 *bytes) {
const ut8 *pat = pasm->pattern;
const ut8 *msk = pasm->mask;
switch (pasm->size) {
case 3:
return ((rz_read_le16(bytes) & rz_read_le16(msk)) == rz_read_le16(pat)) &&
((bytes[2] & msk[2]) == pat[2]);
case 4:
return (rz_read_le32(bytes) & rz_read_le32(msk)) == rz_read_le32(pat);
case 5:
return ((rz_read_le32(bytes) & rz_read_le32(msk)) == rz_read_le32(pat)) &&
((bytes[4] & msk[4]) == pat[4]);
case 6:
return ((rz_read_le32(bytes) & rz_read_le32(msk)) == rz_read_le32(pat)) &&
((rz_read_le16(bytes + 4) & rz_read_le16(msk + 4)) == rz_read_le16(pat + 4));
case 7:
return ((rz_read_le32(bytes) & rz_read_le32(msk)) == rz_read_le32(pat)) &&
((rz_read_le16(bytes + 4) & rz_read_le16(msk + 4)) == rz_read_le16(pat + 4)) &&
((bytes[6] & msk[6]) == pat[6]);
case 8:
return (rz_read_le64(bytes) & rz_read_le64(msk)) == rz_read_le64(pat);
default:
for (ut32 j = 0; j < pasm->size; ++j) {
if ((bytes[j] & msk[j]) != pat[j]) {
return false;
}
}
return true;
}
}
static bool go_is_sign_match(GoStrRecover *ctx, GoStrInfo *info, const GoSignature *sigs, const size_t n_sigs) {
ut32 nlen = 0;
// First verify all pattern masks match before executing any expensive decode callbacks
for (size_t i = 0; i < n_sigs; ++i) {
const GoSignature *sig = &sigs[i];
const ut32 psize = sig->pasm->size;
if (nlen >= ctx->size || psize > ctx->size - nlen) {
return false;
}
if (!go_asm_pattern_match(sig->pasm, ctx->bytes + nlen)) {
return false;
}
nlen += psize;
}
// Now decode info once pattern match is confirmed across the whole signature
memset(info, 0, sizeof(GoStrInfo));
nlen = 0;
for (size_t i = 0; i < n_sigs; ++i) {
const GoSignature *sig = &sigs[i];
const ut8 *bytes = ctx->bytes + nlen;
ut32 size = ctx->size - nlen;
// decode info
if (sig->decode && !sig->decode(ctx->core, info, ctx->pc + nlen, bytes, size)) {
return false;
}
// sets from where the xrefs starts.
if (sig->pasm->xrefs) {
info->xref = ctx->pc + nlen;
}
nlen += sig->pasm->size;
}
return true;
}
static ut32 decode_one_opcode_size(GoStrRecover *ctx) {
RzAnalysisOp aop = { 0 };
rz_analysis_op_init(&aop);
if (rz_analysis_op(ctx->core->analysis, &aop, ctx->pc, ctx->bytes, ctx->size, RZ_ANALYSIS_OP_MASK_BASIC) < 1) {
rz_analysis_op_fini(&aop);
return 0;
}
int size = aop.size;
rz_analysis_op_fini(&aop);
return size > 0 ? size : 0;
}
static ut32 golang_recover_string_with_table(GoStrRecover *ctx, const GoSignatureTable *table, size_t n_table, ut32 fixed_op_size) {
ut32 oplen = fixed_op_size ? fixed_op_size : (ctx->op_size ? ctx->op_size : decode_one_opcode_size(ctx));
GoStrInfo info = { 0 };
for (size_t i = 0; i < n_table; ++i) {
if (go_is_sign_match(ctx, &info, table[i].sigs, table[i].n_sigs)) {
if (recover_string_at(ctx, info.addr, info.size)) {
rz_analysis_xrefs_set(ctx->core->analysis, info.xref, info.addr, RZ_ANALYSIS_XREF_TYPE_STRING);
}
break;
}
}
return oplen;
}
#define go_asm_pattern_name(arch, bits, mnemonic) go_##arch##_##bits##_##mnemonic
#define go_asm_pattern_define(arch, bits, mnemonic, pattern, mask, set_xref) \
static const GoAsmPattern go_asm_pattern_name(arch, bits, mnemonic) = { (const ut8 *)pattern, (const ut8 *)mask, (sizeof(pattern) - 1), set_xref }
static bool decode_from_table(RzCore *core, GoStrInfo *info, ut64 pc, const ut8 *buffer, const ut32 size) {
if (!info->addr || info->addr == UT64_MAX) {
return false;
}
ut8 tmp[16];
int nread = rz_io_nread_at(core->io, info->addr, tmp, sizeof(tmp));
if (nread < 0 || (!core->io->ff && (size_t)nread != sizeof(tmp))) {
return false;
}
ut32 bits = rz_asm_get_bits(core->rasm);
bool big_endian = rz_asm_is_big_endian_set(core->rasm);
ut32 offset = bits / 8;
info->addr = rz_read_ble(tmp, big_endian, bits);
info->size = rz_read_ble(tmp + offset, big_endian, bits);
return true;
}
static bool decode_val_set_size(RzCore *core, GoStrInfo *info, ut64 pc, const ut8 *buffer, const ut32 size) {
RzAnalysisOp aop = { 0 };
rz_analysis_op_init(&aop);
if (rz_analysis_op(core->analysis, &aop, pc, buffer, size, RZ_ANALYSIS_OP_MASK_BASIC) < 1) {
rz_analysis_op_fini(&aop);
return false;
}
info->size = aop.val;
rz_analysis_op_fini(&aop);
return true;
}
static bool decode_val_set_addr(RzCore *core, GoStrInfo *info, ut64 pc, const ut8 *buffer, const ut32 size) {
RzAnalysisOp aop = { 0 };
rz_analysis_op_init(&aop);
if (rz_analysis_op(core->analysis, &aop, pc, buffer, size, RZ_ANALYSIS_OP_MASK_BASIC) < 1) {
rz_analysis_op_fini(&aop);
return false;
}
info->addr = aop.val;
rz_analysis_op_fini(&aop);
return true;
}
static bool decode_val_add_addr(RzCore *core, GoStrInfo *info, ut64 pc, const ut8 *buffer, const ut32 size) {
RzAnalysisOp aop = { 0 };
rz_analysis_op_init(&aop);
if (rz_analysis_op(core->analysis, &aop, pc, buffer, size, RZ_ANALYSIS_OP_MASK_BASIC) < 1) {
rz_analysis_op_fini(&aop);
return false;
}
info->addr += aop.val;
rz_analysis_op_fini(&aop);
return true;
}
static bool decode_ptr_set_addr(RzCore *core, GoStrInfo *info, ut64 pc, const ut8 *buffer, const ut32 size) {
RzAnalysisOp aop = { 0 };
rz_analysis_op_init(&aop);
if (rz_analysis_op(core->analysis, &aop, pc, buffer, size, RZ_ANALYSIS_OP_MASK_BASIC) < 1) {
rz_analysis_op_fini(&aop);
return false;
}
ut64 ptr = aop.ptr;
rz_analysis_op_fini(&aop);
if (!ptr || ptr == UT64_MAX) {
return false;
}
info->addr = ptr;
return true;
}
static bool decode_disp_set_addr(RzCore *core, GoStrInfo *info, ut64 pc, const ut8 *buffer, const ut32 size) {
RzAnalysisOp aop = { 0 };
rz_analysis_op_init(&aop);
if (rz_analysis_op(core->analysis, &aop, pc, buffer, size, RZ_ANALYSIS_OP_MASK_BASIC) < 1) {
rz_analysis_op_fini(&aop);
return false;
}
ut64 disp = aop.disp;
rz_analysis_op_fini(&aop);
if (!disp || disp == UT64_MAX) {
return false;
}
info->addr = disp;
return true;
}
// 0x004881da 48c7401003000000 mov qword [rax + 0x10], 3
// 0x004881e2 488d0d8d8c0100 lea rcx, [0x004a0e76]
go_asm_pattern_define(x86, 64, lea, "\x48\x00\x00\x00\x00\x00\x00", "\xff\x00\x00\x00\x00\x00\x00", true);
go_asm_pattern_define(x86, 64, mov_imm0, "\xb9\x00\x00\x00\x00", "\xff\x00\x00\x00\x00", false);
go_asm_pattern_define(x86, 64, mov_imm1, "\x48\xc7\x00\x00\x00\x00\x00\x00", "\xff\xff\x00\x00\x00\x00\x00\x00", false);
go_asm_pattern_define(x86, 64, mov_imm2, "\x41\x00\x00\x00\x00\x00", "\xff\x00\x00\x00\x00\x00", false);
go_asm_pattern_define(x86, 64, mov_imm3, "\xbb\x00\x00\x00\x00", "\xff\x00\x00\x00\x00", false);
go_asm_pattern_define(x86, 64, mov_imm4, "\xbf\x00\x00\x00\x00", "\xff\x00\x00\x00\x00", false);
go_asm_pattern_define(x86, 64, mov_reg0, "\x48\x00\x00\x00", "\xff\x00\x00\x00", false);
go_asm_pattern_define(x86, 64, mov_reg1, "\x48\x00\x00\x00\x00", "\xff\x00\x00\x00\x00", false);
static const GoSignature go_x64_lea_mov0_mov_signature[] = {
// lea reg, [string_offset]
{ &go_asm_pattern_name(x86, 64, lea), &decode_ptr_set_addr },
// mov [esp/rsp + 0x..], reg
{ &go_asm_pattern_name(x86, 64, mov_reg0), NULL },
// mov [esp/rsp + 0x..], string_size
{ &go_asm_pattern_name(x86, 64, mov_imm1), &decode_val_set_size },
};
static const GoSignature go_x64_lea_mov1_mov_signature[] = {
// lea reg, [string_offset]
{ &go_asm_pattern_name(x86, 64, lea), &decode_ptr_set_addr },
// mov [esp/rsp + 0x..], reg
{ &go_asm_pattern_name(x86, 64, mov_reg1), NULL },
// mov [esp/rsp + 0x..], string_size
{ &go_asm_pattern_name(x86, 64, mov_imm1), &decode_val_set_size },
};
static const GoSignature go_x64_lea_mov0_signature[] = {
// lea reg, [string_offset]
{ &go_asm_pattern_name(x86, 64, lea), &decode_ptr_set_addr },
// mov reg, string_size
{ &go_asm_pattern_name(x86, 64, mov_imm0), &decode_val_set_size },
};
static const GoSignature go_x64_lea_mov1_signature[] = {
// lea reg, [string_offset]
{ &go_asm_pattern_name(x86, 64, lea), &decode_ptr_set_addr },
// mov reg, string_size
{ &go_asm_pattern_name(x86, 64, mov_imm2), &decode_val_set_size },
};
static const GoSignature go_x64_lea_mov2_signature[] = {
// lea reg, [string_offset]
{ &go_asm_pattern_name(x86, 64, lea), &decode_ptr_set_addr },
// mov reg, string_size
{ &go_asm_pattern_name(x86, 64, mov_imm3), &decode_val_set_size },
};
static const GoSignature go_x64_lea_mov3_signature[] = {
// lea reg, [string_offset]
{ &go_asm_pattern_name(x86, 64, lea), &decode_ptr_set_addr },
// mov reg, string_size
{ &go_asm_pattern_name(x86, 64, mov_imm4), &decode_val_set_size },
};
static const GoSignature go_x64_mov0_lea_signature[] = {
// mov reg, string_size
{ &go_asm_pattern_name(x86, 64, mov_imm0), &decode_val_set_size },
// lea reg, [string_offset]
{ &go_asm_pattern_name(x86, 64, lea), &decode_ptr_set_addr },
};
static const GoSignature go_x64_mov1_lea_signature[] = {
// mov reg, string_size
{ &go_asm_pattern_name(x86, 64, mov_imm1), &decode_val_set_size },
// lea reg, [string_offset]
{ &go_asm_pattern_name(x86, 64, lea), &decode_ptr_set_addr },
};
static const GoSignature go_x64_mov2_lea_signature[] = {
// mov reg, string_size
{ &go_asm_pattern_name(x86, 64, mov_imm3), &decode_val_set_size },
// lea reg, [string_offset]
{ &go_asm_pattern_name(x86, 64, lea), &decode_ptr_set_addr },
};
static const GoSignature go_x64_mov3_lea_signature[] = {
// mov reg, string_size
{ &go_asm_pattern_name(x86, 64, mov_imm4), &decode_val_set_size },
// lea reg, [string_offset]
{ &go_asm_pattern_name(x86, 64, lea), &decode_ptr_set_addr },
};
static const GoSignature go_x64_table0_signature[] = {
// lea reg, [table_offset]
{ &go_asm_pattern_name(x86, 64, lea), &decode_ptr_set_addr },
// mov reg, reg
{ &go_asm_pattern_name(x86, 64, mov_reg0), &decode_from_table },
};
static const GoSignature go_x64_table1_signature[] = {
// lea reg, [table_offset]
{ &go_asm_pattern_name(x86, 64, lea), &decode_ptr_set_addr },
// mov reg, reg
{ &go_asm_pattern_name(x86, 64, mov_reg1), &decode_from_table },
};
static const GoSignatureTable go_x64_signatures[] = {
GO_SIG_TABLE_ENTRY(go_x64_lea_mov0_mov_signature),
GO_SIG_TABLE_ENTRY(go_x64_lea_mov1_mov_signature),
GO_SIG_TABLE_ENTRY(go_x64_lea_mov0_signature),
GO_SIG_TABLE_ENTRY(go_x64_lea_mov1_signature),
GO_SIG_TABLE_ENTRY(go_x64_lea_mov2_signature),
GO_SIG_TABLE_ENTRY(go_x64_lea_mov3_signature),
GO_SIG_TABLE_ENTRY(go_x64_mov0_lea_signature),
GO_SIG_TABLE_ENTRY(go_x64_mov1_lea_signature),
GO_SIG_TABLE_ENTRY(go_x64_mov2_lea_signature),
GO_SIG_TABLE_ENTRY(go_x64_mov3_lea_signature),
GO_SIG_TABLE_ENTRY(go_x64_table0_signature),
GO_SIG_TABLE_ENTRY(go_x64_table1_signature),
};
static ut32 golang_recover_string_x64(GoStrRecover *ctx) {
return golang_recover_string_with_table(ctx, go_x64_signatures, RZ_ARRAY_SIZE(go_x64_signatures), 0);
}
go_asm_pattern_define(x86, 32, lea, "\x8d\x00\x00\x00\x00\x00", "\xff\x00\x00\x00\x00\x00", true);
go_asm_pattern_define(x86, 32, mov_imm0, "\xc7\x00\x00\x00\x00\x00\x00", "\xff\x00\x00\x00\x00\x00\x00", false);
go_asm_pattern_define(x86, 32, mov_imm1, "\xc7\x00\x00\x00\x00\x00\x00\x00", "\xff\x00\x00\x00\x00\x00\x00\x00", false);
go_asm_pattern_define(x86, 32, mov_reg0, "\x89\x00\x00", "\xff\x00\x00", false);
go_asm_pattern_define(x86, 32, mov_reg1, "\x89\x00\x00\x00", "\xff\x00\x00\x00", false);
static const GoSignature go_x86_lea_mov0_mov_signature[] = {
// lea reg, [string_offset]
{ &go_asm_pattern_name(x86, 32, lea), &decode_disp_set_addr },
// mov [esp/rsp + 0x..], reg
{ &go_asm_pattern_name(x86, 32, mov_reg0), NULL },
// mov [esp/rsp + 0x..], string_size
{ &go_asm_pattern_name(x86, 32, mov_imm1), &decode_val_set_size },
};
static const GoSignature go_x86_lea_mov1_mov_signature[] = {
// lea reg, [string_offset]
{ &go_asm_pattern_name(x86, 32, lea), &decode_disp_set_addr },
// mov [esp/rsp + 0x..], reg
{ &go_asm_pattern_name(x86, 32, mov_reg1), NULL },
// mov [esp/rsp + 0x..], string_size
{ &go_asm_pattern_name(x86, 32, mov_imm1), &decode_val_set_size },
};
static const GoSignature go_x86_lea_mov0_signature[] = {
// lea reg, [string_offset]
{ &go_asm_pattern_name(x86, 32, lea), &decode_disp_set_addr },
// mov reg, string_size
{ &go_asm_pattern_name(x86, 32, mov_imm0), &decode_val_set_size },
};
static const GoSignature go_x86_lea_mov1_signature[] = {
// lea reg, [string_offset]
{ &go_asm_pattern_name(x86, 32, lea), &decode_disp_set_addr },
// mov reg, string_size
{ &go_asm_pattern_name(x86, 32, mov_imm1), &decode_val_set_size },
};
static const GoSignature go_x86_mov_lea_signature[] = {
// mov reg, string_size
{ &go_asm_pattern_name(x86, 32, mov_imm0), &decode_val_set_size },
// lea reg, [string_offset]
{ &go_asm_pattern_name(x86, 32, lea), &decode_disp_set_addr },
};
static const GoSignature go_x86_table_signature[] = {
// lea reg, [table_offset]
{ &go_asm_pattern_name(x86, 32, lea), &decode_disp_set_addr },
// mov reg, reg
{ &go_asm_pattern_name(x86, 32, mov_reg0), &decode_from_table },
};
static const GoSignatureTable go_x86_signatures[] = {
GO_SIG_TABLE_ENTRY(go_x86_lea_mov0_mov_signature),
GO_SIG_TABLE_ENTRY(go_x86_lea_mov1_mov_signature),
GO_SIG_TABLE_ENTRY(go_x86_lea_mov0_signature),
GO_SIG_TABLE_ENTRY(go_x86_lea_mov1_signature),
GO_SIG_TABLE_ENTRY(go_x86_mov_lea_signature),
GO_SIG_TABLE_ENTRY(go_x86_table_signature),
};
static ut32 golang_recover_string_x86(GoStrRecover *ctx) {
return golang_recover_string_with_table(ctx, go_x86_signatures, RZ_ARRAY_SIZE(go_x86_signatures), 0);
}
go_asm_pattern_define(arm, 64, adrp, "\x00\x00\x00\x80", "\x00\x00\x00\x8f", true);
go_asm_pattern_define(arm, 64, add, "\x00\x00\x00\x01", "\x00\x00\x00\x6f", false);
go_asm_pattern_define(arm, 64, orr, "\x00\x00\x00\x22", "\x00\x00\x80\x6f", false);
go_asm_pattern_define(arm, 64, movz, "\x00\x00\x80\x42", "\x00\x00\x80\x6f", false);
go_asm_pattern_define(arm, 64, any, "\x00\x00\x00\x00", "\x00\x00\x00\x00", false);
static const GoSignature go_arm64_adrp_add_str_orr_signature[] = {
// adrp reg0, base_str
{ &go_asm_pattern_name(arm, 64, adrp), &decode_ptr_set_addr },
// add reg0, reg0, offset_str
{ &go_asm_pattern_name(arm, 64, add), &decode_val_add_addr },
// str reg, [sp, ..]
{ &go_asm_pattern_name(arm, 64, any), NULL },
// orr reg1, 0, string_size
{ &go_asm_pattern_name(arm, 64, orr), &decode_val_set_size },
};
static const GoSignature go_arm64_adrp_add_str_movz_signature[] = {
// adrp reg0, base_str
{ &go_asm_pattern_name(arm, 64, adrp), &decode_ptr_set_addr },
// add reg0, reg0, offset_str
{ &go_asm_pattern_name(arm, 64, add), &decode_val_add_addr },
// str reg, [sp, ..]
{ &go_asm_pattern_name(arm, 64, any), NULL },
// movz reg1, string_size
{ &go_asm_pattern_name(arm, 64, movz), &decode_val_set_size },
};
static const GoSignature go_arm64_orr_str_adrp_add_signature[] = {
// orr reg1, 0, string_size
{ &go_asm_pattern_name(arm, 64, orr), &decode_val_set_size },
// str reg, [sp, ..]
{ &go_asm_pattern_name(arm, 64, any), NULL },
// adrp reg0, base_str
{ &go_asm_pattern_name(arm, 64, adrp), &decode_ptr_set_addr },
// add reg0, reg0, offset_str
{ &go_asm_pattern_name(arm, 64, add), &decode_val_add_addr },
};
static const GoSignature go_arm64_movz_str_adrp_add_signature[] = {
// movz reg1, string_size
{ &go_asm_pattern_name(arm, 64, movz), &decode_val_set_size },
// str reg, [sp, ..]
{ &go_asm_pattern_name(arm, 64, any), NULL },
// adrp reg0, base_str
{ &go_asm_pattern_name(arm, 64, adrp), &decode_ptr_set_addr },
// add reg0, reg0, offset_str
{ &go_asm_pattern_name(arm, 64, add), &decode_val_add_addr },
};
static const GoSignature go_arm64_adrp_add_orr_signature[] = {
// adrp reg0, base_str
{ &go_asm_pattern_name(arm, 64, adrp), &decode_ptr_set_addr },
// add reg0, reg0, offset_str
{ &go_asm_pattern_name(arm, 64, add), &decode_val_add_addr },
// orr reg1, 0, string_size
{ &go_asm_pattern_name(arm, 64, orr), &decode_val_set_size },
};
static const GoSignature go_arm64_adrp_add_movz_signature[] = {
// adrp reg0, base_str
{ &go_asm_pattern_name(arm, 64, adrp), &decode_ptr_set_addr },
// add reg0, reg0, offset_str
{ &go_asm_pattern_name(arm, 64, add), &decode_val_add_addr },
// movz reg1, string_size
{ &go_asm_pattern_name(arm, 64, movz), &decode_val_set_size },
};
static const GoSignature go_arm64_table_signature[] = {
// adrp reg0, base_str
{ &go_asm_pattern_name(arm, 64, adrp), &decode_ptr_set_addr },
// add reg0, reg0, offset_str
{ &go_asm_pattern_name(arm, 64, add), &decode_val_add_addr },
// str reg, [sp, ..]
{ &go_asm_pattern_name(arm, 64, any), &decode_from_table },
};
static const GoSignatureTable go_arm64_signatures[] = {
GO_SIG_TABLE_ENTRY(go_arm64_adrp_add_str_orr_signature),
GO_SIG_TABLE_ENTRY(go_arm64_adrp_add_str_movz_signature),
GO_SIG_TABLE_ENTRY(go_arm64_orr_str_adrp_add_signature),
GO_SIG_TABLE_ENTRY(go_arm64_movz_str_adrp_add_signature),
GO_SIG_TABLE_ENTRY(go_arm64_adrp_add_orr_signature),
GO_SIG_TABLE_ENTRY(go_arm64_adrp_add_movz_signature),
GO_SIG_TABLE_ENTRY(go_arm64_table_signature),
};
static ut32 golang_recover_string_arm64(GoStrRecover *ctx) {
return golang_recover_string_with_table(ctx, go_arm64_signatures, RZ_ARRAY_SIZE(go_arm64_signatures), 4);
}
static bool decode_ldr_set_addr(RzCore *core, GoStrInfo *info, ut64 pc, const ut8 *buffer, const ut32 size) {
RzAnalysisOp aop = { 0 };
ut8 tmp[4];
ut64 addr = 0;
bool big_endian = rz_asm_is_big_endian_set(core->rasm);
rz_analysis_op_init(&aop);
if (rz_analysis_op(core->analysis, &aop, pc, buffer, size, RZ_ANALYSIS_OP_MASK_BASIC) < 1) {
rz_analysis_op_fini(&aop);
return false;
}
addr = aop.ptr;
rz_analysis_op_fini(&aop);
if (!addr || addr == UT64_MAX) {
return false;
}
int nread = rz_io_nread_at(core->io, addr, tmp, sizeof(tmp));
if (nread < 0 || (!core->io->ff && (size_t)nread != sizeof(tmp))) {
return false;
}
info->addr = rz_read_ble32(tmp, big_endian);
return true;
}
go_asm_pattern_define(arm, 32, ldr, "\x00\x00\x9f\xe5", "\x00\x00\x9f\xe5", true);
go_asm_pattern_define(arm, 32, mov, "\x00\x00\xa0\xe3", "\x00\x00\xa0\xe3", false);
go_asm_pattern_define(arm, 32, any, "\x00\x00\x00\x00", "\x00\x00\x00\x00", false);
static const GoSignature go_arm32_ldr_str_mov_signature[] = {
// ldr reg0, string_offset
{ &go_asm_pattern_name(arm, 32, ldr), &decode_ldr_set_addr },
// str reg, [sp, ..]
{ &go_asm_pattern_name(arm, 32, any), NULL },
// mov reg1, string_size
{ &go_asm_pattern_name(arm, 32, mov), &decode_val_set_size },
};
static const GoSignature go_arm32_mov_str_ldr_signature[] = {
// mov reg1, string_size
{ &go_asm_pattern_name(arm, 32, mov), &decode_val_set_size },
// str reg, [sp, ..]
{ &go_asm_pattern_name(arm, 32, any), NULL },
// ldr reg0, string_offset
{ &go_asm_pattern_name(arm, 32, ldr), &decode_ldr_set_addr },
};
static const GoSignature go_arm32_ldr_mov_signature[] = {
// ldr reg0, string_offset
{ &go_asm_pattern_name(arm, 32, ldr), &decode_ldr_set_addr },
// mov reg1, string_size
{ &go_asm_pattern_name(arm, 32, mov), &decode_val_set_size },
};
static const GoSignature go_arm32_table_signature[] = {
// ldr reg0, string_offset
{ &go_asm_pattern_name(arm, 32, ldr), &decode_ldr_set_addr },
// str reg, [sp, ..]
{ &go_asm_pattern_name(arm, 32, any), &decode_from_table },
};
static const GoSignatureTable go_arm32_signatures[] = {
GO_SIG_TABLE_ENTRY(go_arm32_ldr_str_mov_signature),
GO_SIG_TABLE_ENTRY(go_arm32_mov_str_ldr_signature),
GO_SIG_TABLE_ENTRY(go_arm32_ldr_mov_signature),
GO_SIG_TABLE_ENTRY(go_arm32_table_signature),
};
static ut32 golang_recover_string_arm32(GoStrRecover *ctx) {
return golang_recover_string_with_table(ctx, go_arm32_signatures, RZ_ARRAY_SIZE(go_arm32_signatures), 4);
}
static bool decode_lui_set_addr(RzCore *core, GoStrInfo *info, ut64 pc, const ut8 *buffer, const ut32 size) {
RzAnalysisOp aop = { 0 };
rz_analysis_op_init(&aop);
if (rz_analysis_op(core->analysis, &aop, pc, buffer, size, RZ_ANALYSIS_OP_MASK_BASIC) < 1) {
rz_analysis_op_fini(&aop);
return false;
}
info->addr = aop.val;
info->addr <<= 16;
rz_analysis_op_fini(&aop);
return true;
}
go_asm_pattern_define(mips, 32, any, "\x00\x00\x00\x00", "\x00\x00\x00\x00", false);
go_asm_pattern_define(mipsbe, 32, addiu, "\x24\x00\x00\x00", "\xff\x00\x00\x00", false);
go_asm_pattern_define(mipsle, 32, addiu, "\x00\x00\x00\x24", "\x00\x00\x00\xff", false);
go_asm_pattern_define(mipsbe, 32, lui, "\x3c\x00\x00\x00", "\xff\x00\x00\x00", true);
go_asm_pattern_define(mipsle, 32, lui, "\x00\x00\x00\x3c", "\x00\x00\x00\xff", true);
// ---- LE ----
static const GoSignature go_mipsle32_lui_addiu_sw_addiu_signature[] = {
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsle, 32, lui), &decode_lui_set_addr },
// addiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsle, 32, addiu), &decode_val_add_addr },
// sw v0, 0x08(at)
{ &go_asm_pattern_name(mips, 32, any), NULL },
// addiu v0, zero, string_size
{ &go_asm_pattern_name(mipsle, 32, addiu), &decode_val_set_size },
};
static const GoSignature go_mipsle32_addiu_sw_lui_addiu_signature[] = {
// addiu v0, zero, string_size
{ &go_asm_pattern_name(mipsle, 32, addiu), &decode_val_set_size },
// sw v0, 0x08(at)
{ &go_asm_pattern_name(mips, 32, any), NULL },
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsle, 32, lui), &decode_lui_set_addr },
// addiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsle, 32, addiu), &decode_val_add_addr },
};
static const GoSignature go_mipsle32_lui_addiu_addiu_signature[] = {
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsle, 32, lui), &decode_lui_set_addr },
// addiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsle, 32, addiu), &decode_val_add_addr },
// addiu v0, zero, string_size
{ &go_asm_pattern_name(mipsle, 32, addiu), &decode_val_set_size },
};
static const GoSignature go_mipsle32_table_signature[] = {
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsle, 32, lui), &decode_lui_set_addr },
// addiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsle, 32, addiu), &decode_val_add_addr },
// sw v0, 0x08(at)
{ &go_asm_pattern_name(mips, 32, any), &decode_from_table },
};
static const GoSignatureTable go_mipsle32_signatures[] = {
GO_SIG_TABLE_ENTRY(go_mipsle32_lui_addiu_sw_addiu_signature),
GO_SIG_TABLE_ENTRY(go_mipsle32_addiu_sw_lui_addiu_signature),
GO_SIG_TABLE_ENTRY(go_mipsle32_lui_addiu_addiu_signature),
GO_SIG_TABLE_ENTRY(go_mipsle32_table_signature),
};
// ---- BE ----
static const GoSignature go_mipsbe32_lui_addiu_sw_addiu_signature[] = {
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsbe, 32, lui), &decode_lui_set_addr },
// addiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsbe, 32, addiu), &decode_val_add_addr },
// sw v0, 0x08(at)
{ &go_asm_pattern_name(mips, 32, any), NULL },
// addiu v0, zero, string_size
{ &go_asm_pattern_name(mipsbe, 32, addiu), &decode_val_set_size },
};
static const GoSignature go_mipsbe32_addiu_sw_lui_addiu_signature[] = {
// addiu v0, zero, string_size
{ &go_asm_pattern_name(mipsbe, 32, addiu), &decode_val_set_size },
// sw v0, 0x08(at)
{ &go_asm_pattern_name(mips, 32, any), NULL },
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsbe, 32, lui), &decode_lui_set_addr },
// addiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsbe, 32, addiu), &decode_val_add_addr },
};
static const GoSignature go_mipsbe32_lui_addiu_addiu_signature[] = {
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsbe, 32, lui), &decode_lui_set_addr },
// addiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsbe, 32, addiu), &decode_val_add_addr },
// addiu v0, zero, string_size
{ &go_asm_pattern_name(mipsbe, 32, addiu), &decode_val_set_size },
};
static const GoSignature go_mipsbe32_table_signature[] = {
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsbe, 32, lui), &decode_lui_set_addr },
// addiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsbe, 32, addiu), &decode_val_add_addr },
// sw v0, 0x08(at)
{ &go_asm_pattern_name(mips, 32, any), &decode_from_table },
};
static const GoSignatureTable go_mipsbe32_signatures[] = {
GO_SIG_TABLE_ENTRY(go_mipsbe32_lui_addiu_sw_addiu_signature),
GO_SIG_TABLE_ENTRY(go_mipsbe32_addiu_sw_lui_addiu_signature),
GO_SIG_TABLE_ENTRY(go_mipsbe32_lui_addiu_addiu_signature),
GO_SIG_TABLE_ENTRY(go_mipsbe32_table_signature),
};
static ut32 golang_recover_string_mips32(GoStrRecover *ctx) {
if (rz_asm_is_big_endian_set(ctx->core->rasm)) {
return golang_recover_string_with_table(ctx, go_mipsbe32_signatures, RZ_ARRAY_SIZE(go_mipsbe32_signatures), 4);
}
return golang_recover_string_with_table(ctx, go_mipsle32_signatures, RZ_ARRAY_SIZE(go_mipsle32_signatures), 4);
}
go_asm_pattern_define(mips, 64, any, "\x00\x00\x00\x00", "\x00\x00\x00\x00", false);
go_asm_pattern_define(mipsbe, 64, daddu, "\x00\x00\x00\x2d", "\x00\x00\x00\xff", false);
go_asm_pattern_define(mipsle, 64, daddu, "\x2d\x00\x00\x00", "\xff\x00\x00\x00", false);
go_asm_pattern_define(mipsbe, 64, daddiu, "\x64\x00\x00\x00", "\xfc\x00\x00\x00", false);
go_asm_pattern_define(mipsle, 64, daddiu, "\x00\x00\x00\x64", "\x00\x00\x00\xfc", false);
go_asm_pattern_define(mipsbe, 64, move, "\x64\x00\x00\x00", "\xff\xe0\x00\x00", false);
go_asm_pattern_define(mipsle, 64, move, "\x00\x00\x00\x64", "\x00\x00\xe0\xff", false);
go_asm_pattern_define(mipsbe, 64, lui, "\x3c\x00\x00\x00", "\xff\x00\x00\x00", true);
go_asm_pattern_define(mipsle, 64, lui, "\x00\x00\x00\x3c", "\x00\x00\x00\xff", true);
// ---- LE ----
static const GoSignature go_mipsle64_lui_daddu_daddiu_sd_daddiu_signature[] = {
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsle, 64, lui), &decode_lui_set_addr },
// daddu v0, v0, gp
{ &go_asm_pattern_name(mipsle, 64, daddu), NULL },
// daddiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsle, 64, daddiu), &decode_val_add_addr },
// sd v0, 8(at)
{ &go_asm_pattern_name(mips, 64, any), NULL },
// daddiu v0, zero, string_size
{ &go_asm_pattern_name(mipsle, 64, move), &decode_val_set_size },
};
static const GoSignature go_mipsle64_daddiu_sd_lui_daddu_daddiu_signature[] = {
// daddiu v0, zero, string_size
{ &go_asm_pattern_name(mipsle, 64, move), &decode_val_set_size },
// sd v0, 8(at)
{ &go_asm_pattern_name(mips, 64, any), NULL },
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsle, 64, lui), &decode_lui_set_addr },
// daddu v0, v0, gp
{ &go_asm_pattern_name(mipsle, 64, daddu), NULL },
// daddiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsle, 64, daddiu), &decode_val_add_addr },
};
static const GoSignature go_mipsle64_lui_daddu_daddiu_daddiu_signature[] = {
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsle, 64, lui), &decode_lui_set_addr },
// daddu v0, v0, gp
{ &go_asm_pattern_name(mipsle, 64, daddu), NULL },
// daddiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsle, 64, daddiu), &decode_val_add_addr },
// daddiu v0, zero, string_size
{ &go_asm_pattern_name(mipsle, 64, move), &decode_val_set_size },
};
static const GoSignature go_mipsle64_table_signature[] = {
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsle, 64, lui), &decode_lui_set_addr },
// daddu v0, v0, gp
{ &go_asm_pattern_name(mipsle, 64, daddu), NULL },
// daddiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsle, 64, daddiu), &decode_val_add_addr },
// sd v0, 8(at)
{ &go_asm_pattern_name(mips, 64, any), &decode_from_table },
};
static const GoSignatureTable go_mipsle64_signatures[] = {
GO_SIG_TABLE_ENTRY(go_mipsle64_lui_daddu_daddiu_sd_daddiu_signature),
GO_SIG_TABLE_ENTRY(go_mipsle64_daddiu_sd_lui_daddu_daddiu_signature),
GO_SIG_TABLE_ENTRY(go_mipsle64_lui_daddu_daddiu_daddiu_signature),
GO_SIG_TABLE_ENTRY(go_mipsle64_table_signature),
};
// ---- BE ----
static const GoSignature go_mipsbe64_lui_daddu_daddiu_sd_daddiu_signature[] = {
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsbe, 64, lui), &decode_lui_set_addr },
// daddu v0, v0, gp
{ &go_asm_pattern_name(mipsbe, 64, daddu), NULL },
// daddiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsbe, 64, daddiu), &decode_val_add_addr },
// sd v0, 8(at)
{ &go_asm_pattern_name(mips, 64, any), NULL },
// daddiu v0, zero, string_size
{ &go_asm_pattern_name(mipsbe, 64, move), &decode_val_set_size },
};
static const GoSignature go_mipsbe64_daddiu_sd_lui_daddu_daddiu_signature[] = {
// daddiu v0, zero, string_size
{ &go_asm_pattern_name(mipsbe, 64, move), &decode_val_set_size },
// sd v0, 8(at)
{ &go_asm_pattern_name(mips, 64, any), NULL },
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsbe, 64, lui), &decode_lui_set_addr },
// daddu v0, v0, gp
{ &go_asm_pattern_name(mipsbe, 64, daddu), NULL },
// daddiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsbe, 64, daddiu), &decode_val_add_addr },
};
static const GoSignature go_mipsbe64_lui_daddu_daddiu_daddiu_signature[] = {
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsbe, 64, lui), &decode_lui_set_addr },
// daddu v0, v0, gp
{ &go_asm_pattern_name(mipsbe, 64, daddu), NULL },
// daddiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsbe, 64, daddiu), &decode_val_add_addr },
// daddiu v0, zero, string_size
{ &go_asm_pattern_name(mipsbe, 64, move), &decode_val_set_size },
};
static const GoSignature go_mipsbe64_table_signature[] = {
// lui v0, high_string_offset
{ &go_asm_pattern_name(mipsbe, 64, lui), &decode_lui_set_addr },
// daddu v0, v0, gp
{ &go_asm_pattern_name(mipsbe, 64, daddu), NULL },
// daddiu v0, v0, low_string_offset
{ &go_asm_pattern_name(mipsbe, 64, daddiu), &decode_val_add_addr },
// sd v0, 8(at)
{ &go_asm_pattern_name(mips, 64, any), &decode_from_table },
};
static const GoSignatureTable go_mipsbe64_signatures[] = {
GO_SIG_TABLE_ENTRY(go_mipsbe64_lui_daddu_daddiu_sd_daddiu_signature),
GO_SIG_TABLE_ENTRY(go_mipsbe64_daddiu_sd_lui_daddu_daddiu_signature),
GO_SIG_TABLE_ENTRY(go_mipsbe64_lui_daddu_daddiu_daddiu_signature),
GO_SIG_TABLE_ENTRY(go_mipsbe64_table_signature),
};
static ut32 golang_recover_string_mips64(GoStrRecover *ctx) {
if (rz_asm_is_big_endian_set(ctx->core->rasm)) {
return golang_recover_string_with_table(ctx, go_mipsbe64_signatures, RZ_ARRAY_SIZE(go_mipsbe64_signatures), 4);
}
return golang_recover_string_with_table(ctx, go_mipsle64_signatures, RZ_ARRAY_SIZE(go_mipsle64_signatures), 4);
}
go_asm_pattern_define(ppc, 64, any, "\x00\x00\x00\x00", "\x00\x00\x00\x00", false);
go_asm_pattern_define(ppcle, 64, lis, "\x00\x00\x00\x3c", "\x00\x00\x00\xfc", true);
go_asm_pattern_define(ppcbe, 64, lis, "\x3c\x00\x00\x00", "\xfc\x00\x00\x00", true);
go_asm_pattern_define(ppcle, 64, addi, "\x00\x00\x00\x38", "\x00\x00\x00\xfc", false);
go_asm_pattern_define(ppcbe, 64, addi, "\x38\x00\x00\x00", "\xfc\x00\x00\x00", false);
go_asm_pattern_define(ppcle, 64, li, "\x00\x00\x00\x38", "\x00\x00\x1f\xfc", false);
go_asm_pattern_define(ppcbe, 64, li, "\x38\x00\x00\x00", "\xfc\x1f\x00\x00", false);
// ---- LE ----
static const GoSignature go_ppcle64_lis_addi_std_li_signature[] = {
// lis r3, high_string_offset
{ &go_asm_pattern_name(ppcle, 64, lis), &decode_val_set_addr },
// addi r3, r3, low_string_offset
{ &go_asm_pattern_name(ppcle, 64, addi), &decode_val_add_addr },
// std r3, 0x20(r1)
{ &go_asm_pattern_name(ppc, 64, any), NULL },
// li r3, string_size
{ &go_asm_pattern_name(ppcle, 64, li), &decode_val_set_size },
};
static const GoSignature go_ppcle64_li_std_lis_addi_signature[] = {
// li r3, string_size
{ &go_asm_pattern_name(ppcle, 64, li), &decode_val_set_size },
// std r3, 0x20(r1)
{ &go_asm_pattern_name(ppc, 64, any), NULL },
// lis r3, high_string_offset
{ &go_asm_pattern_name(ppcle, 64, lis), &decode_val_set_addr },
// addi r3, r3, low_string_offset
{ &go_asm_pattern_name(ppcle, 64, addi), &decode_val_add_addr },
};
static const GoSignature go_ppcle64_lis_addi_li_signature[] = {
// lis r3, high_string_offset
{ &go_asm_pattern_name(ppcle, 64, lis), &decode_val_set_addr },
// addi r3, r3, low_string_offset
{ &go_asm_pattern_name(ppcle, 64, addi), &decode_val_add_addr },
// li r3, string_size
{ &go_asm_pattern_name(ppcle, 64, li), &decode_val_set_size },
};
static const GoSignature go_ppcle64_table_signature[] = {
// lis r3, high_string_offset
{ &go_asm_pattern_name(ppcle, 64, lis), &decode_val_set_addr },
// addi r3, r3, low_string_offset
{ &go_asm_pattern_name(ppcle, 64, addi), &decode_val_add_addr },
// std r3, 0x20(r1)
{ &go_asm_pattern_name(ppc, 64, any), &decode_from_table },
};
static const GoSignatureTable go_ppcle64_signatures[] = {
GO_SIG_TABLE_ENTRY(go_ppcle64_lis_addi_li_signature),
GO_SIG_TABLE_ENTRY(go_ppcle64_lis_addi_std_li_signature),
GO_SIG_TABLE_ENTRY(go_ppcle64_li_std_lis_addi_signature),
GO_SIG_TABLE_ENTRY(go_ppcle64_table_signature),
};
// ---- BE ----
static const GoSignature go_ppcbe64_lis_addi_std_li_signature[] = {
// lis r3, high_string_offset
{ &go_asm_pattern_name(ppcbe, 64, lis), &decode_val_set_addr },
// addi r3, r3, low_string_offset
{ &go_asm_pattern_name(ppcbe, 64, addi), &decode_val_add_addr },
// std r3, 0x20(r1)
{ &go_asm_pattern_name(ppc, 64, any), NULL },
// li r3, string_size
{ &go_asm_pattern_name(ppcbe, 64, li), &decode_val_set_size },
};
static const GoSignature go_ppcbe64_li_std_lis_addi_signature[] = {
// li r3, string_size
{ &go_asm_pattern_name(ppcbe, 64, li), &decode_val_set_size },
// std r3, 0x20(r1)
{ &go_asm_pattern_name(ppc, 64, any), NULL },
// lis r3, high_string_offset
{ &go_asm_pattern_name(ppcbe, 64, lis), &decode_val_set_addr },
// addi r3, r3, low_string_offset
{ &go_asm_pattern_name(ppcbe, 64, addi), &decode_val_add_addr },
};
static const GoSignature go_ppcbe64_lis_addi_li_signature[] = {
// lis r3, high_string_offset
{ &go_asm_pattern_name(ppcbe, 64, lis), &decode_val_set_addr },
// addi r3, r3, low_string_offset
{ &go_asm_pattern_name(ppcbe, 64, addi), &decode_val_add_addr },
// li r3, string_size
{ &go_asm_pattern_name(ppcbe, 64, li), &decode_val_set_size },
};
static const GoSignature go_ppcbe64_table_signature[] = {
// lis r3, high_string_offset
{ &go_asm_pattern_name(ppcbe, 64, lis), &decode_val_set_addr },
// addi r3, r3, low_string_offset
{ &go_asm_pattern_name(ppcbe, 64, addi), &decode_val_add_addr },
// std r3, 0x20(r1)
{ &go_asm_pattern_name(ppc, 64, any), &decode_from_table },
};
static const GoSignatureTable go_ppcbe64_signatures[] = {
GO_SIG_TABLE_ENTRY(go_ppcbe64_lis_addi_li_signature),
GO_SIG_TABLE_ENTRY(go_ppcbe64_lis_addi_std_li_signature),
GO_SIG_TABLE_ENTRY(go_ppcbe64_li_std_lis_addi_signature),
GO_SIG_TABLE_ENTRY(go_ppcbe64_table_signature),
};
static ut32 golang_recover_string_ppc64(GoStrRecover *ctx) {
if (rz_asm_is_big_endian_set(ctx->core->rasm)) {
return golang_recover_string_with_table(ctx, go_ppcbe64_signatures, RZ_ARRAY_SIZE(go_ppcbe64_signatures), 4);
}
return golang_recover_string_with_table(ctx, go_ppcle64_signatures, RZ_ARRAY_SIZE(go_ppcle64_signatures), 4);
}
static bool decode_auipc_set_addr(RzCore *core, GoStrInfo *info, ut64 pc, const ut8 *buffer, const ut32 size) {
RzAnalysisOp aop = { 0 };
rz_analysis_op_init(&aop);
if (rz_analysis_op(core->analysis, &aop, pc, buffer, size, RZ_ANALYSIS_OP_MASK_BASIC) < 1) {
rz_analysis_op_fini(&aop);
return false;
}
info->addr = pc + aop.val;
rz_analysis_op_fini(&aop);
return true;
}
go_asm_pattern_define(riscv, 64, auipc, "\x17\x00\x00\x00", "\x7f\x00\x00\x00", true);
go_asm_pattern_define(riscv, 64, addi, "\x13\x00\x00\x00", "\x7f\x00\x00\x00", false);
go_asm_pattern_define(riscv, 64, addiw, "\x1b\x00\x00\x00", "\x7f\x70\x00\x00", false);
go_asm_pattern_define(riscv, 64, li, "\x13\x00\x00\x00", "\x7f\x80\x0F\x00", false);
go_asm_pattern_define(riscv, 64, any, "\x00\x00\x00\x00", "\x00\x00\x00\x00", false);
static const GoSignature go_riscv64_auipc_add_sd_addiw_signature[] = {
// auipc gp, high_string_offset
{ &go_asm_pattern_name(riscv, 64, auipc), &decode_auipc_set_addr },
// addi gp, gp, low_string_offset
{ &go_asm_pattern_name(riscv, 64, addi), &decode_val_add_addr },
// sd gp, 8(sp)
{ &go_asm_pattern_name(riscv, 64, any), NULL },
// addiw gp, zero, string_size
{ &go_asm_pattern_name(riscv, 64, addiw), &decode_val_set_size },
};
static const GoSignature go_riscv64_auipc_add_sd_li_signature[] = {
// auipc gp, high_string_offset
{ &go_asm_pattern_name(riscv, 64, auipc), &decode_auipc_set_addr },
// addi gp, gp, low_string_offset
{ &go_asm_pattern_name(riscv, 64, addi), &decode_val_add_addr },
// sd gp, 8(sp)
{ &go_asm_pattern_name(riscv, 64, any), NULL },
// li gp, zero, string_size
{ &go_asm_pattern_name(riscv, 64, li), &decode_val_set_size },
};
static const GoSignature go_riscv64_li_sd_auipc_add_signature[] = {
// li gp, zero, string_size
{ &go_asm_pattern_name(riscv, 64, li), &decode_val_set_size },
// sd gp, 8(sp)
{ &go_asm_pattern_name(riscv, 64, any), NULL },
// auipc gp, high_string_offset
{ &go_asm_pattern_name(riscv, 64, auipc), &decode_auipc_set_addr },
// addi gp, gp, low_string_offset
{ &go_asm_pattern_name(riscv, 64, addi), &decode_val_add_addr },
};
static const GoSignature go_riscv64_addiw_sd_auipc_add_signature[] = {
// addiw gp, zero, string_size
{ &go_asm_pattern_name(riscv, 64, addiw), &decode_val_set_size },
// sd gp, 8(sp)
{ &go_asm_pattern_name(riscv, 64, any), NULL },
// auipc gp, high_string_offset
{ &go_asm_pattern_name(riscv, 64, auipc), &decode_auipc_set_addr },
// addi gp, gp, low_string_offset
{ &go_asm_pattern_name(riscv, 64, addi), &decode_val_add_addr },
};
static const GoSignature go_riscv64_auipc_add_addiw_signature[] = {
// auipc gp, high_string_offset
{ &go_asm_pattern_name(riscv, 64, auipc), &decode_auipc_set_addr },
// addi gp, gp, low_string_offset
{ &go_asm_pattern_name(riscv, 64, addi), &decode_val_add_addr },
// addiw gp, zero, string_size
{ &go_asm_pattern_name(riscv, 64, addiw), &decode_val_set_size },
};
static const GoSignature go_riscv64_auipc_add_li_signature[] = {
// auipc gp, high_string_offset
{ &go_asm_pattern_name(riscv, 64, auipc), &decode_auipc_set_addr },
// addi gp, gp, low_string_offset
{ &go_asm_pattern_name(riscv, 64, addi), &decode_val_add_addr },
// li gp, zero, string_size
{ &go_asm_pattern_name(riscv, 64, li), &decode_val_set_size },
};
static const GoSignature go_riscv64_table_signature[] = {
// auipc gp, high_string_offset
{ &go_asm_pattern_name(riscv, 64, auipc), &decode_auipc_set_addr },
// addi gp, gp, low_string_offset
{ &go_asm_pattern_name(riscv, 64, addi), &decode_val_add_addr },
// sd gp, 8(sp)
{ &go_asm_pattern_name(riscv, 64, any), &decode_from_table },
};
static const GoSignatureTable go_riscv64_signatures[] = {
GO_SIG_TABLE_ENTRY(go_riscv64_auipc_add_sd_addiw_signature),
GO_SIG_TABLE_ENTRY(go_riscv64_auipc_add_sd_li_signature),
GO_SIG_TABLE_ENTRY(go_riscv64_li_sd_auipc_add_signature),
GO_SIG_TABLE_ENTRY(go_riscv64_addiw_sd_auipc_add_signature),
GO_SIG_TABLE_ENTRY(go_riscv64_auipc_add_addiw_signature),
GO_SIG_TABLE_ENTRY(go_riscv64_auipc_add_li_signature),
GO_SIG_TABLE_ENTRY(go_riscv64_table_signature),
};
static ut32 golang_recover_string_riscv64(GoStrRecover *ctx) {
return golang_recover_string_with_table(ctx, go_riscv64_signatures, RZ_ARRAY_SIZE(go_riscv64_signatures), 4);
}
// Sometimes the data-structures has strings, but these are stored in tables where
// the first offset is always the pointer to the squashed strings and the next word
// is the size of the string
static void core_recover_golang_strings_from_data_pointers(RzCore *core, GoStrRecover *ctx) {
rz_core_notify_begin(core, "Recovering go strings from bin maps");
ut32 bits = rz_asm_get_bits(core->rasm);
bool big_endian = rz_asm_is_big_endian_set(core->rasm);
const ut32 word_size = bits / 8;
void **iter;
RzBinMap *map;
ut8 *buffer = NULL;
ut64 string_addr, string_size;
RzBinObject *object = rz_bin_cur_object(core->bin);
RzPVector *map_vec = object ? rz_bin_object_get_maps(object) : NULL;
if (!map_vec) {
RZ_LOG_ERROR("Failed to get the RzBinMap list\n");
goto end;
}
buffer = malloc(GO_MAX_TABLE_SIZE);
if (!buffer) {
RZ_LOG_ERROR("Failed to allocate table buffer\n");
goto end;
}
rz_pvector_foreach (map_vec, iter) {
map = *iter;
if (!rz_bin_map_is_data(map) || map->psize < (word_size * 2)) {
continue;
}
ut64 current = map->vaddr;
ut64 end = map->vaddr + map->psize;
do {
size_t length = RZ_MIN((end - current), GO_MAX_TABLE_SIZE);
if (length < (word_size * 2)) {
break;
}
if (rz_io_nread_at(core->io, current, buffer, length) < 0) {
RZ_LOG_ERROR("Failed to read map at address %" PFMT64x "\n", current);
break;
}
length -= word_size;
for (size_t i = 0; i < length; i += word_size) {
string_addr = rz_read_ble(buffer + i, big_endian, bits);
string_size = rz_read_ble(buffer + i + word_size, big_endian, bits);
if (!string_addr || !string_size) {
continue;
} else if (word_size == sizeof(ut32) && string_addr == UT32_MAX) {
continue;
} else if (word_size == sizeof(ut64) && string_addr == UT64_MAX) {
continue;
}
if (recover_string_at(ctx, string_addr, string_size)) {
rz_analysis_xrefs_set(core->analysis, current + i, string_addr, RZ_ANALYSIS_XREF_TYPE_STRING);
}
}
current += length;
} while (current < end);
}
end:
free(buffer);
rz_core_notify_done(core, "Recovering go strings from bin maps");
}
/**
* \brief Attempts to recover all golang string
*
* \param core The RzCore struct to use
*/
RZ_API void rz_core_analysis_resolve_golang_strings(RzCore *core) {
rz_return_if_fail(core && core->analysis && core->io);
RzList *fcns = rz_analysis_function_list(core->analysis);
if (!fcns) {
return;
}
const char *asm_arch = rz_config_get(core->config, "asm.arch");
ut32 asm_bits = rz_config_get_i(core->config, "asm.bits");
RzListIter *lit;
void **vit;
RzAnalysisFunction *func;
RzAnalysisBlock *block;
GoStrRecoverCb recover_cb = NULL;
ut8 *bb_buf = NULL;
ut32 bb_buf_cap = 0;
ut32 min_op_size = rz_analysis_archinfo(core->analysis, RZ_ANALYSIS_ARCHINFO_MIN_OP_SIZE);
GoStrRecover ctx = { 0 };
ctx.core = core;
core_recover_golang_strings_from_data_pointers(core, &ctx);
rz_core_notify_begin(core, "Analyze all instructions to recover all strings used in sym.go.*");
if (!strcmp(asm_arch, "x86")) {
switch (asm_bits) {
case 32:
recover_cb = &golang_recover_string_x86;
break;
case 64:
recover_cb = &golang_recover_string_x64;
break;
default:
RZ_LOG_WARN("golang: string recovery for %s bits %d is not implemented.\n", asm_arch, asm_bits);
break;
}
} else if (!strcmp(asm_arch, "arm")) {
switch (asm_bits) {
case 32:
recover_cb = &golang_recover_string_arm32;
break;
case 64:
recover_cb = &golang_recover_string_arm64;
break;
default:
RZ_LOG_WARN("golang: string recovery for %s bits %d is not implemented.\n", asm_arch, asm_bits);
break;
}
} else if (!strcmp(asm_arch, "mips")) {
switch (asm_bits) {
case 32:
recover_cb = &golang_recover_string_mips32;
break;
case 64:
recover_cb = &golang_recover_string_mips64;
break;
default:
RZ_LOG_WARN("golang: string recovery for %s bits %d is not implemented.\n", asm_arch, asm_bits);
break;
}
} else if (!strcmp(asm_arch, "riscv")) {
switch (asm_bits) {
case 64:
recover_cb = &golang_recover_string_riscv64;
break;
default:
RZ_LOG_WARN("golang: string recovery for %s bits %d is not implemented.\n", asm_arch, asm_bits);
break;
}
} else if (!strcmp(asm_arch, "ppc")) {
switch (asm_bits) {
case 64:
recover_cb = &golang_recover_string_ppc64;
break;
default:
RZ_LOG_WARN("golang: string recovery for %s bits %d is not implemented.\n", asm_arch, asm_bits);
break;
}
} else if (!strcmp(asm_arch, "sysz")) {
// sysz uses strings that are all null terminated
// also they are already handled by rizin.
// example: 'larl %r0, str.XXXX' with the correct length.
rz_core_notify_done(core, "Analyze all instructions to recover all strings used in sym.go.*");
goto end;
}
if (!recover_cb) {
rz_core_notify_error(core, "Cannot resolve go strings because arch '%s:%u' is not supported.", asm_arch, asm_bits);
goto end;
}
rz_list_foreach (fcns, lit, func) {
if (rz_cons_is_breaked()) {
break;
}
rz_pvector_foreach (func->bbs, vit) {
block = (RzAnalysisBlock *)*vit;
if (block->size == 0) {
continue;
}
if (block->size > bb_buf_cap) {
ut8 *new_buf = realloc(bb_buf, block->size);
if (!new_buf) {
RZ_LOG_ERROR("Failed to allocate basic block bytes buffer\n");
goto end;
}
bb_buf = new_buf;
bb_buf_cap = block->size;
}
int nread = rz_io_nread_at(core->io, block->addr, bb_buf, block->size);
if (nread < 0) {
RZ_LOG_ERROR("Failed to read function basic block at address %" PFMT64x "\n", block->addr);
continue;
}
if (!core->io->ff && (ut64)nread != block->size) {
continue;
}
if (block->ninstr > 0) {
ctx.op_size = 1;
for (int idx = 0; idx < block->ninstr; idx++) {
ut16 i = rz_analysis_block_get_op_offset(block, idx);
if (i >= block->size) {
break;
}
ctx.pc = block->addr + i;
ctx.bytes = bb_buf + i;
ctx.size = block->size - i;
recover_cb(&ctx);
}
} else {
ctx.op_size = 0;
for (ut32 i = 0; i < block->size;) {
ctx.pc = block->addr + i;
ctx.bytes = bb_buf + i;
ctx.size = block->size - i;
ut32 nlen = recover_cb(&ctx);
i += RZ_MAX(nlen, min_op_size);
}
}
}
}
rz_core_notify_done(core, "Analyze all instructions to recover all strings used in sym.go.*");
rz_core_notify_done(core, "Recovered %d strings from the sym.go.* functions.", ctx.n_recovered);
end:
free(bb_buf);
free(ctx.raw_buf);
free(ctx.flag_buf);
}