Rizin
unix-like reverse engineering framework and cli tools
golang.c File Reference
#include <rz_bin.h>

Go to the source code of this file.

Classes

struct  golang_build_info_t
 
struct  scan_go_info_s
 

Macros

#define GOLANG_MAX_UVARIANT   10
 
#define GOLANG_MAX_STRING_BUF   0x1000
 
#define GOLANG_MOD_START   "\x30\x77\xaf\x0c\x92\x74\x08\x02\x41\xe1\xc1\x07\xe6\xd6\x18\xe6"
 

Typedefs

typedef struct golang_build_info_t GoBuildInfo
 

Functions

static ut64 go_uvariant (ut8 *buffer, size_t size, ut32 *read)
 
static ut64 go_string (ut8 *buffer, size_t size, char **output)
 
static st64 io_read_va_at (RzBinFile *bf, ut64 vaddr, ut8 *buffer, ut64 size)
 
static char * go_string_from_table (RzBinFile *bf, ut32 ptr_size, ut64 offset, bool big_endian, ut32 *str_size)
 
static void parse_go_build_info (RzBinFile *bf, GoBuildInfo *go_info, ut64 bi_paddr)
 
static bool is_go_build_info (const ut8 *magic)
 
static ut64 scan_go_build_info (const ut8 *buf, ut64 len, void *user)
 
static void find_go_build_info (RzBinFile *bf, GoBuildInfo *go_info, RzBinSection *section)
 
RZ_IPI RZ_OWN char * rz_bin_file_golang_compiler (RZ_NONNULL RzBinFile *bf)
 Returns the golang compiler info if buildinfo struct is found. More...
 

Detailed Description

This is a parser for parsing the buildinfo structure which provides access to information embedded in a Go binary about how it was built. This includes the Go toolchain version, and the set of modules used (for binaries built in module mode).

This structure always starts with a magic "\xff Go buildinf:", followed by pointer size and version (the version also describes the endianness).

This structure contains then the compiler/go version and the cmd arguments/settings provided at compilation time.

Starting from go1.18 the settings area is guarded by two magic numbers:

  • 3077af0c9274080241e1c107e6d618e6 i.e. beginning of the settings
  • f932433186182072008242104116d8f2 i.e. ending of the settings On earlier versions this section is actually pointing to some string structures which contains the virt address and size of the string to parse.

Example of the structure from a go1.18 binary.

ff20 476f 2062 7569 6c64 696e 663a 0802 . Go buildinf:.. 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0667 6f31 2e31 38d5 0130 77af 0c92 7408 .go1.18..0w...t. 0241 e1c1 07e6 d618 e670 6174 6809 636f .A.......path.co 6d6d 616e 642d 6c69 6e65 2d61 7267 756d mmand-line-argum 656e 7473 0a62 7569 6c64 092d 636f 6d70 ents.build.-comp 696c 6572 3d67 630a 6275 696c 6409 4347 iler=gc.build.CG 4f5f 454e 4142 4c45 443d 310a 6275 696c O_ENABLED=1.buil 6409 4347 4f5f 4346 4c41 4753 3d0a 6275 d.CGO_CFLAGS=.bu 696c 6409 4347 4f5f 4350 5046 4c41 4753 ild.CGO_CPPFLAGS 3d0a 6275 696c 6409 4347 4f5f 4358 5846 =.build.CGO_CXXF 4c41 4753 3d0a 6275 696c 6409 4347 4f5f LAGS=.build.CGO_ 4c44 464c 4147 533d 0a62 7569 6c64 0947 LDFLAGS=.build.G 4f41 5243 483d 6172 6d36 340a 6275 696c OARCH=arm64.buil 6409 474f 4f53 3d64 6172 7769 6e0a f932 d.GOOS=darwin..2 4331 8618 2072 0082 4210 4116 d8f2 0000 C1.. r..B.A.....

Example from go version -m mybinary:

devel go1.18-2d1d548 Tue Dec 21 03:55:43 2021 +0000 path github.com/username/repository mod github.com/username/repository (devel) dep golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff h1:XmKBi9R6duxOB3lfc72wyrwiOY7X2Jl1wuI+RFOyMDE= path command-line-arguments build -compiler=gc build CGO_ENABLED=1 build CGO_CFLAGS= build CGO_CPPFLAGS= build CGO_CXXFLAGS= build CGO_LDFLAGS= build GOARCH=amd64 build GOOS=linux build GOAMD64=v1 build vcs=git build vcs.revision=4bd670890aee5a14e36be1a72d19ca8573f2433b build vcs.time=2021-12-06T17:40:21Z build vcs.modified=true

Reference:

Definition in file golang.c.

Macro Definition Documentation

◆ GOLANG_MAX_STRING_BUF

#define GOLANG_MAX_STRING_BUF   0x1000

Definition at line 71 of file golang.c.

◆ GOLANG_MAX_UVARIANT

#define GOLANG_MAX_UVARIANT   10

Definition at line 70 of file golang.c.

◆ GOLANG_MOD_START

#define GOLANG_MOD_START   "\x30\x77\xaf\x0c\x92\x74\x08\x02\x41\xe1\xc1\x07\xe6\xd6\x18\xe6"

Definition at line 73 of file golang.c.

Typedef Documentation

◆ GoBuildInfo

Function Documentation

◆ find_go_build_info()

static void find_go_build_info ( RzBinFile bf,
GoBuildInfo go_info,
RzBinSection section 
)
static

Definition at line 253 of file golang.c.

253  {
254  struct scan_go_info_s ctx = { bf, go_info, section };
256 }
static ut64 scan_go_build_info(const ut8 *buf, ut64 len, void *user)
Definition: golang.c:238
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
RzBuffer * buf
Definition: rz_bin.h:303
RzBinFile * bf
Definition: golang.c:233
GoBuildInfo * go_info
Definition: golang.c:234
uint32_t size

References scan_go_info_s::bf, rz_bin_file_t::buf, scan_go_info_s::go_info, rz_buf_fwd_scan(), scan_go_build_info(), and section::size.

Referenced by rz_bin_file_golang_compiler().

◆ go_string()

static ut64 go_string ( ut8 buffer,
size_t  size,
char **  output 
)
static

Definition at line 105 of file golang.c.

105  {
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 }
#define GOLANG_MOD_START
Definition: golang.c:73
static ut64 go_uvariant(ut8 *buffer, size_t size, ut32 *read)
Definition: golang.c:80
#define NULL
Definition: cris-opc.c:27
uint32_t ut32
voidpf void uLong size
Definition: ioapi.h:138
memcpy(mem, inblock.get(), min(CONTAINING_RECORD(inblock.get(), MEMBLOCK, data) ->size, size))
void * malloc(size_t size)
Definition: malloc.c:123
int n
Definition: mipsasm.c:19
Definition: buffer.h:15
ut64(WINAPI *w32_GetEnabledXStateFeatures)()
diff_output_t output
Definition: zipcmp.c:237
int read(izstream &zs, T *x, Items items)
Definition: zstream.h:115

References go_uvariant(), GOLANG_MOD_START, malloc(), memcpy(), n, NULL, output, read(), and ut64().

Referenced by parse_go_build_info().

◆ go_string_from_table()

static char* go_string_from_table ( RzBinFile bf,
ut32  ptr_size,
ut64  offset,
bool  big_endian,
ut32 str_size 
)
static

Definition at line 135 of file golang.c.

135  {
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 }
#define GOLANG_MAX_STRING_BUF
Definition: golang.c:71
static st64 io_read_va_at(RzBinFile *bf, ut64 vaddr, ut8 *buffer, ut64 size)
Definition: golang.c:127
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
voidpf uLong offset
Definition: ioapi.h:144
uint8_t ut8
Definition: lh5801.h:11
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

References free(), GOLANG_MAX_STRING_BUF, io_read_va_at(), malloc(), NULL, rz_read_ble32(), rz_read_ble64(), cmd_descs_generate::str, and ut64().

Referenced by parse_go_build_info().

◆ go_uvariant()

static ut64 go_uvariant ( ut8 buffer,
size_t  size,
ut32 read 
)
static

Definition at line 80 of file golang.c.

80  {
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 }
lzma_index ** i
Definition: index.h:629
#define GOLANG_MAX_UVARIANT
Definition: golang.c:70
int x
Definition: mipsasm.c:20
static RzSocket * s
Definition: rtr.c:28
#define b(i)
Definition: sha256.c:42

References b, GOLANG_MAX_UVARIANT, i, read(), s, ut64(), and x.

Referenced by go_string().

◆ io_read_va_at()

static st64 io_read_va_at ( RzBinFile bf,
ut64  vaddr,
ut8 buffer,
ut64  size 
)
static

Definition at line 127 of file golang.c.

127  {
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 }
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 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
#define UT64_MAX
Definition: rz_types_base.h:86
RzBinObject * o
Definition: rz_bin.h:305

References rz_bin_file_t::buf, rz_bin_file_t::o, rz_bin_object_v2p(), rz_buf_read_at(), ut64(), and UT64_MAX.

Referenced by go_string_from_table().

◆ is_go_build_info()

static bool is_go_build_info ( const ut8 magic)
static

Definition at line 228 of file golang.c.

228  {
229  return !memcmp(magic, "\xff Go buildinf:", 14);
230 }

Referenced by scan_go_build_info().

◆ parse_go_build_info()

static void parse_go_build_info ( RzBinFile bf,
GoBuildInfo go_info,
ut64  bi_paddr 
)
static

Definition at line 170 of file golang.c.

170  {
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 }
static ut64 go_string(ut8 *buffer, size_t size, char **output)
Definition: golang.c:105
static char * go_string_from_table(RzBinFile *bf, ut32 ptr_size, ut64 offset, bool big_endian, ut32 *str_size)
Definition: golang.c:135
#define RZ_LOG_ERROR(fmtstr,...)
Definition: rz_log.h:58
RZ_API char * rz_str_replace(char *str, const char *key, const char *val, int g)
Definition: str.c:1110
#define PFMT64x
Definition: rz_types.h:393
char * settings
Definition: golang.c:77
char * version
Definition: golang.c:76

References rz_bin_file_t::buf, free(), go_string(), go_string_from_table(), GOLANG_MAX_STRING_BUF, i, malloc(), NULL, PFMT64x, read(), rz_buf_read_at(), RZ_LOG_ERROR, rz_read_ble32(), rz_read_ble64(), rz_str_replace(), golang_build_info_t::settings, cmd_descs_generate::str, ut64(), and golang_build_info_t::version.

Referenced by rz_bin_file_golang_compiler(), and scan_go_build_info().

◆ rz_bin_file_golang_compiler()

RZ_IPI RZ_OWN char* rz_bin_file_golang_compiler ( RZ_NONNULL RzBinFile bf)

Returns the golang compiler info if buildinfo struct is found.

Parameters
RzBinFileThe RzBinFile to use for the search
Returns
Returns a string on success, otherwise NULL

Definition at line 265 of file golang.c.

265  {
266  rz_return_val_if_fail(bf && bf->o, NULL);
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) {
290  find_go_build_info(bf, &go_info, section);
291  } else if (!is_pe && (strstr(section->name, "go_buildinfo") || strstr(section->name, "go.buildinfo"))) {
292  parse_go_build_info(bf, &go_info, section->paddr);
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);
304  free(go_info.version);
305  free(go_info.settings);
306  return res;
307  }
308 
309  return go_info.version;
310 }
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 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
RZ_API void rz_list_free(RZ_NONNULL RzList *list)
Empties the list and frees the list pointer.
Definition: list.c:137
#define rz_return_val_if_fail(expr, val)
Definition: rz_assert.h:108
#define RZ_LOG_INFO(fmtstr,...)
Definition: rz_log.h:54
RZ_API char * rz_str_newf(const char *fmt,...) RZ_PRINTF_CHECK(1

References scan_go_info_s::bf, find_go_build_info(), free(), scan_go_info_s::go_info, rz_bin_plugin_t::name, NULL, rz_bin_file_t::o, parse_go_build_info(), rz_bin_object_t::plugin, rz_bin_object_get_sections(), rz_list_free(), RZ_LOG_INFO, rz_return_val_if_fail, rz_str_newf(), sections(), golang_build_info_t::settings, section::size, and golang_build_info_t::version.

Referenced by rz_bin_object_set_items().

◆ scan_go_build_info()

static ut64 scan_go_build_info ( const ut8 buf,
ut64  len,
void *  user 
)
static

Definition at line 238 of file golang.c.

238  {
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 }
size_t len
Definition: 6502dis.c:15
static bool is_go_build_info(const ut8 *magic)
Definition: golang.c:228
voidpf void * buf
Definition: ioapi.h:138
int pos
Definition: main.c:11

References is_go_build_info(), len, parse_go_build_info(), pos, and ut64().

Referenced by find_go_build_info().