rizin/librz/core/golang.c
Giovanni b5fee3471c
Move RzAnalysis as private and force usage of C API (#6123)
* Hide RzAnalysis structure to force the usage of the C APIs.

* Fix rop code & test

* Fix linter

* Fix rz_analysis_free to return void
2026-04-06 16:07:53 +00:00

1965 lines
67 KiB
C

// SPDX-FileCopyrightText: 2022-2024 deroad <wargio@libero.it>
// 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 n_recovered;
} 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 {
GoAsmPattern *pasm;
GoDecodeCb decode;
} GoSignature;
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;
}
static bool analyse_golang_symgo_function(RzFlagItem *fi, void *user) {
RzCore *core = (RzCore *)user;
rz_core_analysis_fcn(core, fi->offset, UT64_MAX, RZ_ANALYSIS_XREF_TYPE_NULL, 1);
return true;
}
/**
* \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;
}
rz_flag_foreach_space_glob(core->flags, "sym.go.*", symbols, analyse_golang_symgo_function, core);
}
/**
* \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.*)");
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, 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) {
free(string);
return false;
}
bstr = rz_bin_object_get_string_at(bf->o, vaddr, true);
if (bstr && bstr->vaddr == vaddr && bstr->size == size) {
free(string);
return true;
}
ut64 paddr = rz_io_v2p(core->io, vaddr);
bstr = RZ_NEW0(RzBinString);
if (!bstr) {
RZ_LOG_ERROR("Failed allocate new go string\n");
free(string);
return false;
}
bstr->paddr = paddr;
bstr->vaddr = vaddr;
bstr->length = bstr->size = size;
bstr->string = string;
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;
}
// 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;
}
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;
}
const size_t n_prefix = strlen("str.");
// string size + strlen('str.') + \0
char *flag = malloc(str_size + n_prefix + 1);
char *raw = malloc(str_size + 1);
if (!flag || !raw) {
RZ_LOG_ERROR("Cannot allocate buffer to read string.\n");
free(flag);
free(raw);
return false;
}
// set prefix and zero-terminator
flag[0] = 's';
flag[1] = 't';
flag[2] = 'r';
flag[3] = '.';
flag[str_size + 4] = 0;
raw[str_size] = 0;
if (0 > rz_io_nread_at(ctx->core->io, str_addr, (ut8 *)raw, str_size)) {
RZ_LOG_ERROR("Failed to read string value at address %" PFMT64x "\n", str_addr);
free(flag);
free(raw);
return false;
} else if (rz_str_utf8_ansi_cols(raw) != str_size) {
free(flag);
free(raw);
return false;
}
memcpy(flag + n_prefix, raw, str_size);
// 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) {
free(flag);
free(raw);
return false;
}
// add new string to string list (raw is freed/owned by add_new_bin_string)
if (!add_new_bin_string(ctx->core, raw, str_addr, str_size)) {
free(flag);
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);
free(flag);
ctx->n_recovered++;
return true;
}
static bool go_is_sign_match(GoStrRecover *ctx, GoStrInfo *info, GoSignature *sigs, const size_t n_sigs) {
ut8 copy[32]; // big enough to handle any pattern.
ut32 nlen = 0;
memset(info, 0, sizeof(GoStrInfo));
for (size_t i = 0; i < n_sigs; ++i) {
if (nlen >= ctx->size) {
return false;
}
GoSignature *sig = &sigs[i];
ut8 *bytes = ctx->bytes + nlen;
ut32 size = ctx->size - nlen;
if (sig->pasm->size > size) {
return false;
}
// copy opcodes
memcpy(copy, bytes, sig->pasm->size);
// apply mask
for (ut32 j = 0; j < sig->pasm->size; ++j) {
copy[j] = copy[j] & sig->pasm->mask[j];
}
// verify the masked input matches the pattern
if (memcmp(copy, sig->pasm->pattern, sig->pasm->size)) {
return false;
}
// 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;
}
#define go_is_sign_match_autosize(ctx, info, sigs) go_is_sign_match(ctx, info, sigs, RZ_ARRAY_SIZE(sigs))
#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 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) {
ut8 tmp[16];
if (0 > rz_io_nread_at(core->io, info->addr, tmp, 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;
}
info->addr = aop.ptr;
rz_analysis_op_fini(&aop);
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;
}
info->addr = aop.disp;
rz_analysis_op_fini(&aop);
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 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 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 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 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 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 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 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 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 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 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 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 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 ut32 golang_recover_string_x64(GoStrRecover *ctx) {
RzAnalysis *analysis = ctx->core->analysis;
ut32 oplen = decode_one_opcode_size(ctx);
GoStrInfo info = { 0 };
if (!go_is_sign_match_autosize(ctx, &info, go_x64_lea_mov0_mov_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x64_lea_mov1_mov_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x64_lea_mov0_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x64_lea_mov1_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x64_lea_mov2_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x64_lea_mov3_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x64_mov0_lea_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x64_mov1_lea_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x64_mov2_lea_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x64_mov3_lea_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x64_table0_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x64_table1_signature)) {
return oplen;
}
// try to recover the string.
if (!recover_string_at(ctx, info.addr, info.size)) {
return oplen;
}
rz_analysis_xrefs_set(analysis, info.xref, info.addr, RZ_ANALYSIS_XREF_TYPE_STRING);
return oplen;
}
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 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 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 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 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 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 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 ut32 golang_recover_string_x86(GoStrRecover *ctx) {
RzAnalysis *analysis = ctx->core->analysis;
ut32 oplen = decode_one_opcode_size(ctx);
GoStrInfo info = { 0 };
if (!go_is_sign_match_autosize(ctx, &info, go_x86_lea_mov0_mov_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x86_lea_mov1_mov_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x86_lea_mov0_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x86_lea_mov1_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x86_mov_lea_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_x86_table_signature)) {
return oplen;
}
// try to recover the string.
if (!recover_string_at(ctx, info.addr, info.size)) {
return oplen;
}
rz_analysis_xrefs_set(analysis, info.xref, info.addr, RZ_ANALYSIS_XREF_TYPE_STRING);
return oplen;
}
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 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 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 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 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 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 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 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 ut32 golang_recover_string_arm64(GoStrRecover *ctx) {
RzAnalysis *analysis = ctx->core->analysis;
GoStrInfo info = { 0 };
if (!go_is_sign_match_autosize(ctx, &info, go_arm64_adrp_add_str_orr_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_arm64_adrp_add_str_movz_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_arm64_orr_str_adrp_add_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_arm64_movz_str_adrp_add_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_arm64_adrp_add_orr_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_arm64_adrp_add_movz_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_arm64_table_signature)) {
return 4;
}
// try to recover the string.
if (!recover_string_at(ctx, info.addr, info.size)) {
return 4;
}
rz_analysis_xrefs_set(analysis, info.xref, info.addr, RZ_ANALYSIS_XREF_TYPE_STRING);
return 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 (0 > rz_io_nread_at(core->io, addr, tmp, 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 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 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 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 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 ut32 golang_recover_string_arm32(GoStrRecover *ctx) {
RzAnalysis *analysis = ctx->core->analysis;
GoStrInfo info = { 0 };
if (!go_is_sign_match_autosize(ctx, &info, go_arm32_ldr_str_mov_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_arm32_mov_str_ldr_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_arm32_ldr_mov_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_arm32_table_signature)) {
return 4;
}
// try to recover the string.
if (!recover_string_at(ctx, info.addr, info.size)) {
return 4;
}
rz_analysis_xrefs_set(analysis, info.xref, info.addr, RZ_ANALYSIS_XREF_TYPE_STRING);
return 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 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 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 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 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 },
};
// ---- BE ----
static 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 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 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 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 ut32 golang_recover_string_mips32(GoStrRecover *ctx) {
bool big_endian = rz_asm_is_big_endian_set(ctx->core->rasm);
GoStrInfo info = { 0 };
if (big_endian &&
!go_is_sign_match_autosize(ctx, &info, go_mipsbe32_lui_addiu_sw_addiu_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_mipsbe32_addiu_sw_lui_addiu_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_mipsbe32_lui_addiu_addiu_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_mipsbe32_table_signature)) {
return 4;
} else if (!big_endian &&
!go_is_sign_match_autosize(ctx, &info, go_mipsle32_lui_addiu_sw_addiu_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_mipsle32_addiu_sw_lui_addiu_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_mipsle32_lui_addiu_addiu_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_mipsle32_table_signature)) {
return 4;
}
// try to recover the string.
if (!recover_string_at(ctx, info.addr, info.size)) {
return 4;
}
rz_analysis_xrefs_set(ctx->core->analysis, info.xref, info.addr, RZ_ANALYSIS_XREF_TYPE_STRING);
return 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 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 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 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 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 },
};
// ---- BE ----
static 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 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 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 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 ut32 golang_recover_string_mips64(GoStrRecover *ctx) {
bool big_endian = rz_asm_is_big_endian_set(ctx->core->rasm);
GoStrInfo info = { 0 };
if (big_endian &&
!go_is_sign_match_autosize(ctx, &info, go_mipsbe64_lui_daddu_daddiu_sd_daddiu_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_mipsbe64_daddiu_sd_lui_daddu_daddiu_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_mipsbe64_lui_daddu_daddiu_daddiu_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_mipsbe64_table_signature)) {
return 4;
} else if (!big_endian &&
!go_is_sign_match_autosize(ctx, &info, go_mipsle64_lui_daddu_daddiu_sd_daddiu_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_mipsle64_daddiu_sd_lui_daddu_daddiu_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_mipsle64_lui_daddu_daddiu_daddiu_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_mipsle64_table_signature)) {
return 4;
}
// try to recover the string.
if (!recover_string_at(ctx, info.addr, info.size)) {
return 4;
}
rz_analysis_xrefs_set(ctx->core->analysis, info.xref, info.addr, RZ_ANALYSIS_XREF_TYPE_STRING);
return 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 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 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 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 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 },
};
// ---- BE ----
static 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 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 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 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 ut32 golang_recover_string_ppc64(GoStrRecover *ctx) {
bool big_endian = rz_asm_is_big_endian_set(ctx->core->rasm);
GoStrInfo info = { 0 };
if (big_endian &&
!go_is_sign_match_autosize(ctx, &info, go_ppcbe64_lis_addi_std_li_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_ppcbe64_li_std_lis_addi_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_ppcbe64_table_signature)) {
return 4;
} else if (!big_endian &&
!go_is_sign_match_autosize(ctx, &info, go_ppcle64_lis_addi_std_li_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_ppcle64_li_std_lis_addi_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_ppcle64_table_signature)) {
return 4;
}
// try to recover the string.
if (!recover_string_at(ctx, info.addr, info.size)) {
if (big_endian && !go_is_sign_match_autosize(ctx, &info, go_ppcbe64_lis_addi_li_signature)) {
return 4;
} else if (!big_endian && !go_is_sign_match_autosize(ctx, &info, go_ppcle64_lis_addi_li_signature)) {
return 4;
} else if (!recover_string_at(ctx, info.addr, info.size)) {
return 4;
}
}
rz_analysis_xrefs_set(ctx->core->analysis, info.xref, info.addr, RZ_ANALYSIS_XREF_TYPE_STRING);
return 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 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 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 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 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 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 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 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 ut32 golang_recover_string_riscv64(GoStrRecover *ctx) {
RzAnalysis *analysis = ctx->core->analysis;
GoStrInfo info = { 0 };
if (!go_is_sign_match_autosize(ctx, &info, go_riscv64_auipc_add_sd_addiw_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_riscv64_auipc_add_sd_li_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_riscv64_li_sd_auipc_add_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_riscv64_addiw_sd_auipc_add_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_riscv64_auipc_add_addiw_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_riscv64_auipc_add_li_signature) &&
!go_is_sign_match_autosize(ctx, &info, go_riscv64_table_signature)) {
return 4;
}
// try to recover the string.
if (!recover_string_at(ctx, info.addr, info.size)) {
return 4;
}
rz_analysis_xrefs_set(analysis, info.xref, info.addr, RZ_ANALYSIS_XREF_TYPE_STRING);
return 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 *bytes = NULL;
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.*");
return;
}
if (!recover_cb) {
rz_core_notify_error(core, "Cannot resolve go strings because arch '%s:%u' is not supported.", asm_arch, asm_bits);
return;
}
rz_list_foreach (fcns, lit, func) {
if (rz_cons_is_breaked()) {
break;
}
rz_pvector_foreach (func->bbs, vit) {
block = (RzAnalysisBlock *)*vit;
bytes = malloc(block->size);
if (!bytes) {
RZ_LOG_ERROR("Failed allocate basic block bytes buffer\n");
return;
} else if (0 > rz_io_nread_at(core->io, block->addr, bytes, block->size)) {
free(bytes);
RZ_LOG_ERROR("Failed to read function basic block at address %" PFMT64x "\n", block->addr);
return;
}
for (ut32 i = 0; i < block->size;) {
ctx.pc = block->addr + i;
ctx.bytes = bytes + i;
ctx.size = block->size - i;
ut32 nlen = recover_cb(&ctx);
i += RZ_MAX(nlen, min_op_size);
}
free(bytes);
}
}
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);
}