Rizin
unix-like reverse engineering framework and cli tools
golang.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2022 deroad <wargio@libero.it>
2 // SPDX-License-Identifier: LGPL-3.0-only
3 
4 #include <rz_bin.h>
5 
70 #define GOLANG_MAX_UVARIANT 10
71 #define GOLANG_MAX_STRING_BUF 0x1000
72 
73 #define GOLANG_MOD_START "\x30\x77\xaf\x0c\x92\x74\x08\x02\x41\xe1\xc1\x07\xe6\xd6\x18\xe6"
74 
75 typedef struct golang_build_info_t {
76  char *version;
77  char *settings;
79 
80 static ut64 go_uvariant(ut8 *buffer, size_t size, ut32 *read) {
81  ut64 x = 0;
82  ut32 s = 0;
83  for (size_t i = 0; i < size; ++i) {
84  ut8 b = buffer[i];
85  if (i == GOLANG_MAX_UVARIANT) {
86  // overflow
87  *read = 0;
88  return 0;
89  } else if (b < 0x80) {
90  if (i == (GOLANG_MAX_UVARIANT - 1) && b > 1) {
91  // overflow
92  *read = 0;
93  return 0;
94  }
95  *read = i + 1;
96  return x | ((ut64)b) << s;
97  }
98  x |= ((ut64)(b & 0x7f)) << s;
99  s += 7;
100  }
101  *read = 0;
102  return 0;
103 }
104 
105 static ut64 go_string(ut8 *buffer, size_t size, char **output) {
106  ut32 read = 0;
108  if (n <= 0 || n > (size - read)) {
109  *output = NULL;
110  return 0;
111  }
112  if (n > 32 && !memcmp(buffer + read, GOLANG_MOD_START, 16)) {
113  n -= 33; // strips away the \n at the end + GOLANG_MOD_END
114  buffer += 16;
115  }
116  char *copy = malloc(n + 1);
117  if (!copy) {
118  *output = NULL;
119  return 0;
120  }
121  copy[n] = 0;
122  memcpy(copy, buffer + read, n);
123  *output = copy;
124  return n + read;
125 }
126 
128  ut64 paddr = rz_bin_object_v2p(bf->o, vaddr);
129  if (paddr == UT64_MAX) {
130  return -1;
131  }
132  return rz_buf_read_at(bf->buf, paddr, buffer, size);
133 }
134 
135 static char *go_string_from_table(RzBinFile *bf, ut32 ptr_size, ut64 offset, bool big_endian, ut32 *str_size) {
136  ut8 buffer[16] = { 0 };
137 
138  // read table entry of max size 16 bytes
139  if (io_read_va_at(bf, offset, (ut8 *)buffer, sizeof(buffer)) < 1) {
140  return NULL;
141  }
142 
143  ut64 paddr = 0, psize = 0;
144  if (ptr_size == 4) {
145  paddr = rz_read_ble32(buffer, big_endian);
146  psize = rz_read_ble32(buffer + ptr_size, big_endian);
147  } else {
148  paddr = rz_read_ble64(buffer, big_endian);
149  psize = rz_read_ble64(buffer + ptr_size, big_endian);
150  }
151 
152  if (!psize || psize > GOLANG_MAX_STRING_BUF) {
153  return NULL;
154  }
155 
156  char *str = malloc(psize + 1);
157  if (!str) {
158  return NULL;
159  } else if (io_read_va_at(bf, paddr, (ut8 *)str, psize) < 1) {
160  free(str);
161  return NULL;
162  }
163  str[psize] = 0;
164  if (str_size) {
165  *str_size = psize;
166  }
167  return str;
168 }
169 
170 static void parse_go_build_info(RzBinFile *bf, GoBuildInfo *go_info, ut64 bi_paddr) {
171  ut8 tmp32[32];
172 
173  // Read build info
174  if (rz_buf_read_at(bf->buf, bi_paddr, tmp32, sizeof(tmp32)) < 1) {
175  RZ_LOG_ERROR("goinfo: Cannot read build info header at 0x%08" PFMT64x " (phy)\n", bi_paddr);
176  return;
177  }
178  ut32 ptr_size = tmp32[14];
179  ut32 setting_sz = 0;
180  if (tmp32[15] & 2) {
182  // Read build info
183  if (rz_buf_read_at(bf->buf, bi_paddr + 32, buffer, GOLANG_MAX_STRING_BUF) < 1) {
184  RZ_LOG_ERROR("goinfo: Cannot read build info header at 0x%08" PFMT64x " (phy)\n", bi_paddr);
185  return;
186  }
187 
189  if (!read) {
190  free(buffer);
191  RZ_LOG_ERROR("goinfo: Cannot read build info version\n");
192  return;
193  }
194 
195  // settings can be NULL.
196  setting_sz = go_string(buffer + read, GOLANG_MAX_STRING_BUF - read, &go_info->settings);
197  free(buffer);
198  } else {
199  bool big_endian = tmp32[15] != 0;
200  ut64 version_offset = 0, setting_offset = 0;
201  if (ptr_size == 4) {
202  version_offset = rz_read_ble32(tmp32 + 16, big_endian);
203  setting_offset = rz_read_ble32(tmp32 + 16 + ptr_size, big_endian);
204  } else {
205  version_offset = rz_read_ble64(tmp32 + 16, big_endian);
206  setting_offset = rz_read_ble64(tmp32 + 16 + ptr_size, big_endian);
207  }
208  go_info->version = go_string_from_table(bf, ptr_size, version_offset, big_endian, NULL);
209  go_info->settings = go_string_from_table(bf, ptr_size, setting_offset, big_endian, &setting_sz);
210  }
211 
212  if (!go_info->settings) {
213  return;
214  }
215 
216  char *str = go_info->settings;
217  // settings contains some whitespaces, we convert them into spaces.
218  for (ut32 i = 0; str[i]; ++i) {
219  if (str[i] < ' ') {
220  str[i] = ' ';
221  }
222  }
223 
224  str = rz_str_replace(str, " build ", " ", 1);
225  go_info->settings = rz_str_replace(str, "path command-line-arguments ", "cmd ", 0);
226 }
227 
228 static bool is_go_build_info(const ut8 *magic) {
229  return !memcmp(magic, "\xff Go buildinf:", 14);
230 }
231 
236 };
237 
238 static ut64 scan_go_build_info(const ut8 *buf, ut64 len, void *user) {
239  const int build_info_align = 16;
240  if (len < build_info_align) {
241  return len;
242  }
243  struct scan_go_info_s *ctx = user;
244  for (ut64 pos = 0; pos <= len - build_info_align; pos += build_info_align) {
245  if (is_go_build_info(buf + pos)) {
246  parse_go_build_info(ctx->bf, ctx->go_info, ctx->section->paddr + pos);
247  return 0;
248  }
249  }
250  return len;
251 }
252 
254  struct scan_go_info_s ctx = { bf, go_info, section };
256 }
257 
267 
268  bool is_pe = false;
269  GoBuildInfo go_info = { 0 };
271  RzListIter *it = NULL;
272  RzList *sections = NULL;
273  const char *plugname = bf->o->plugin->name;
274 
275  if (!strcmp(plugname, "pe") || !strcmp(plugname, "pe64")) {
276  is_pe = true;
277  } else if (strcmp(plugname, "elf") && strcmp(plugname, "elf64") &&
278  strcmp(plugname, "mach0") && strcmp(plugname, "mach064")) {
279  RZ_LOG_INFO("goinfo: unsupported bin format '%s'\n", plugname);
280  return NULL;
281  }
282 
284  if (!sections) {
285  return NULL;
286  }
287 
288  rz_list_foreach (sections, it, section) {
289  if (is_pe && strstr(section->name, "data") && section->size > 16) {
291  } else if (!is_pe && (strstr(section->name, "go_buildinfo") || strstr(section->name, "go.buildinfo"))) {
293  }
294  if (go_info.version) {
295  break;
296  }
297  }
299 
300  if (!go_info.version) {
301  return NULL;
302  } else if (go_info.settings) {
303  char *res = rz_str_newf("%s (%s)", go_info.version, go_info.settings);
306  return res;
307  }
308 
309  return go_info.version;
310 }
size_t len
Definition: 6502dis.c:15
#define RZ_IPI
Definition: analysis_wasm.c:11
lzma_index ** i
Definition: index.h:629
#define GOLANG_MAX_STRING_BUF
Definition: golang.c:71
static ut64 go_string(ut8 *buffer, size_t size, char **output)
Definition: golang.c:105
#define GOLANG_MOD_START
Definition: golang.c:73
static ut64 go_uvariant(ut8 *buffer, size_t size, ut32 *read)
Definition: golang.c:80
static ut64 scan_go_build_info(const ut8 *buf, ut64 len, void *user)
Definition: golang.c:238
static char * go_string_from_table(RzBinFile *bf, ut32 ptr_size, ut64 offset, bool big_endian, ut32 *str_size)
Definition: golang.c:135
static bool is_go_build_info(const ut8 *magic)
Definition: golang.c:228
#define GOLANG_MAX_UVARIANT
Definition: golang.c:70
struct golang_build_info_t GoBuildInfo
RZ_IPI RZ_OWN char * rz_bin_file_golang_compiler(RZ_NONNULL RzBinFile *bf)
Returns the golang compiler info if buildinfo struct is found.
Definition: golang.c:265
static st64 io_read_va_at(RzBinFile *bf, ut64 vaddr, ut8 *buffer, ut64 size)
Definition: golang.c:127
static void find_go_build_info(RzBinFile *bf, GoBuildInfo *go_info, RzBinSection *section)
Definition: golang.c:253
static void parse_go_build_info(RzBinFile *bf, GoBuildInfo *go_info, ut64 bi_paddr)
Definition: golang.c:170
RzList * sections(RzBinFile *bf)
Definition: bin_ne.c:110
RZ_API ut64 rz_bin_object_v2p(RZ_NONNULL RzBinObject *obj, ut64 vaddr)
Convert virtual address to offset in the file according to binary mappings.
Definition: bobj.c:1015
RZ_API RZ_OWN RzList * rz_bin_object_get_sections(RZ_NONNULL RzBinObject *obj)
Get list of RzBinSection representing only the sections of the binary object.
Definition: bobj.c:774
#define NULL
Definition: cris-opc.c:27
uint32_t ut32
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
voidpf void uLong size
Definition: ioapi.h:138
voidpf uLong offset
Definition: ioapi.h:144
voidpf void * buf
Definition: ioapi.h:138
uint8_t ut8
Definition: lh5801.h:11
memcpy(mem, inblock.get(), min(CONTAINING_RECORD(inblock.get(), MEMBLOCK, data) ->size, size))
RZ_API void rz_list_free(RZ_NONNULL RzList *list)
Empties the list and frees the list pointer.
Definition: list.c:137
void * malloc(size_t size)
Definition: malloc.c:123
int x
Definition: mipsasm.c:20
int n
Definition: mipsasm.c:19
static RzSocket * s
Definition: rtr.c:28
#define rz_return_val_if_fail(expr, val)
Definition: rz_assert.h:108
RZ_API st64 rz_buf_read_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL RZ_OUT ut8 *buf, ut64 len)
Read len bytes of the buffer at the specified address.
Definition: buf.c:1136
RZ_API ut64 rz_buf_fwd_scan(RZ_NONNULL RzBuffer *b, ut64 start, ut64 amount, RZ_NONNULL RzBufferFwdScan fwd_scan, RZ_NULLABLE void *user)
Scans buffer linearly in chunks calling fwd_scan for each chunk.
Definition: buf.c:1303
static ut64 rz_read_ble64(const void *src, bool big_endian)
Definition: rz_endian.h:501
static ut32 rz_read_ble32(const void *src, bool big_endian)
Definition: rz_endian.h:497
#define RZ_LOG_INFO(fmtstr,...)
Definition: rz_log.h:54
#define RZ_LOG_ERROR(fmtstr,...)
Definition: rz_log.h:58
RZ_API char * rz_str_newf(const char *fmt,...) RZ_PRINTF_CHECK(1
RZ_API char * rz_str_replace(char *str, const char *key, const char *val, int g)
Definition: str.c:1110
#define RZ_OWN
Definition: rz_types.h:62
#define RZ_NONNULL
Definition: rz_types.h:64
#define PFMT64x
Definition: rz_types.h:393
#define st64
Definition: rz_types_base.h:10
#define UT64_MAX
Definition: rz_types_base.h:86
#define b(i)
Definition: sha256.c:42
Definition: buffer.h:15
char * settings
Definition: golang.c:77
char * version
Definition: golang.c:76
XX curplugin == o->plugin.
Definition: rz_bin.h:298
RzBinObject * o
Definition: rz_bin.h:305
RzBuffer * buf
Definition: rz_bin.h:303
struct rz_bin_plugin_t * plugin
Definition: rz_bin.h:289
char * name
Definition: rz_bin.h:509
RzBinFile * bf
Definition: golang.c:233
RzBinSection * section
Definition: golang.c:235
GoBuildInfo * go_info
Definition: golang.c:234
uint32_t size
int pos
Definition: main.c:11
ut64(WINAPI *w32_GetEnabledXStateFeatures)()
diff_output_t output
Definition: zipcmp.c:237
int read(izstream &zs, T *x, Items items)
Definition: zstream.h:115