Rizin
unix-like reverse engineering framework and cli tools
pe_exports.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2008-2019 nibble <nibble.ds@gmail.com>
2 // SPDX-FileCopyrightText: 2008-2019 pancake <pancake@nopcode.org>
3 // SPDX-FileCopyrightText: 2008-2019 inisider <inisider@gmail.com>
4 // SPDX-License-Identifier: LGPL-3.0-only
5 
6 #include "pe.h"
7 
8 typedef struct {
15 } SymbolRecord;
16 
17 static struct rz_bin_pe_export_t *parse_symbol_table(RzBinPEObj *bin, struct rz_bin_pe_export_t *exports, int sz) {
18  ut64 sym_tbl_off, num = 0;
19  const int srsz = COFF_SYMBOL_SIZE; // symbol record size
21  struct rz_bin_pe_export_t *exp;
22  struct rz_bin_pe_export_t *new_exports = NULL;
23  const size_t export_t_sz = sizeof(struct rz_bin_pe_export_t);
24  int bufsz, i, shsz;
25  SymbolRecord sr;
26  ut64 text_off = 0LL;
27  ut64 text_rva = 0LL;
28  int textn = 0;
29  int exports_sz;
30  int symctr = 0;
31  char *buf;
32 
33  if (!bin || !bin->nt_headers) {
34  return NULL;
35  }
36 
37  sym_tbl_off = bin->nt_headers->file_header.PointerToSymbolTable;
38  num = bin->nt_headers->file_header.NumberOfSymbols;
39  shsz = bufsz = num * srsz;
40  if (bufsz < 1 || bufsz > bin->size) {
41  return NULL;
42  }
43  buf = calloc(num, srsz);
44  if (!buf) {
45  return NULL;
46  }
47  exports_sz = export_t_sz * num;
48  if (exports) {
49  int osz = sz;
50  sz += exports_sz;
51  new_exports = realloc(exports, sz + export_t_sz);
52  if (!new_exports) {
53  free(buf);
54  return NULL;
55  }
56  exports = new_exports;
57  new_exports = NULL;
58  exp = (struct rz_bin_pe_export_t *)(((const ut8 *)exports) + osz);
59  } else {
60  sz = exports_sz;
61  exports = malloc(sz + export_t_sz);
62  exp = exports;
63  }
64 
65  sections = bin->sections;
66  for (i = 0; i < bin->num_sections; i++) {
67  // XXX search by section with +x permission since the section can be left blank
68  if (!strcmp((char *)sections[i].name, ".text")) {
69  text_rva = sections[i].vaddr;
70  text_off = sections[i].paddr;
71  textn = i + 1;
72  }
73  }
74  symctr = 0;
75  if (rz_buf_read_at(bin->b, sym_tbl_off, (ut8 *)buf, bufsz) > 0) {
76  for (i = 0; i < shsz; i += srsz) {
77  // sr = (SymbolRecord*) (buf + i);
78  if (i + sizeof(sr) >= bufsz) {
79  break;
80  }
81  memcpy(&sr, buf + i, sizeof(sr));
82  // RZ_LOG_INFO("SECNUM %d\n", sr.secnum);
83  if (sr.secnum == textn) {
84  if (sr.symtype == 32) {
85  char shortname[9];
86  memcpy(shortname, &sr.shortname, 8);
87  shortname[8] = 0;
88  if (*shortname) {
89  strncpy((char *)exp[symctr].name, shortname, PE_NAME_LENGTH - 1);
90  } else {
91  char *longname, name[128];
92  ut32 idx = rz_read_le32(buf + i + 4);
93  if (rz_buf_read_at(bin->b, sym_tbl_off + idx + shsz, (ut8 *)name, 128)) { // == 128) {
94  longname = name;
95  name[sizeof(name) - 1] = 0;
96  strncpy((char *)exp[symctr].name, longname, PE_NAME_LENGTH - 1);
97  } else {
98  sprintf((char *)exp[symctr].name, "unk_%d", symctr);
99  }
100  }
101  exp[symctr].name[PE_NAME_LENGTH] = '\0';
102  exp[symctr].libname[0] = '\0';
103  exp[symctr].vaddr = PE_(bin_pe_rva_to_va)(bin, text_rva + sr.value);
104  exp[symctr].paddr = text_off + sr.value;
105  exp[symctr].ordinal = symctr;
106  exp[symctr].forwarder[0] = 0;
107  exp[symctr].last = 0;
108  symctr++;
109  }
110  }
111  } // for
112  } // if read ok
113  exp[symctr].last = 1;
114  free(buf);
115  return exports;
116 }
117 
118 static int read_image_export_directory(RzBuffer *b, ut64 addr, PE_(image_export_directory) * export_dir) {
119  st64 o_addr = rz_buf_seek(b, 0, RZ_BUF_CUR);
120  if (rz_buf_seek(b, addr, RZ_BUF_SET) < 0) {
121  return -1;
122  }
123  ut8 buf[sizeof(PE_(image_export_directory))];
124  rz_buf_read(b, buf, sizeof(buf));
125  PE_READ_STRUCT_FIELD(export_dir, PE_(image_export_directory), Characteristics, 32);
126  PE_READ_STRUCT_FIELD(export_dir, PE_(image_export_directory), TimeDateStamp, 32);
127  PE_READ_STRUCT_FIELD(export_dir, PE_(image_export_directory), MajorVersion, 16);
128  PE_READ_STRUCT_FIELD(export_dir, PE_(image_export_directory), MinorVersion, 16);
129  PE_READ_STRUCT_FIELD(export_dir, PE_(image_export_directory), Name, 32);
130  PE_READ_STRUCT_FIELD(export_dir, PE_(image_export_directory), Base, 32);
131  PE_READ_STRUCT_FIELD(export_dir, PE_(image_export_directory), NumberOfFunctions, 32);
132  PE_READ_STRUCT_FIELD(export_dir, PE_(image_export_directory), NumberOfNames, 32);
133  PE_READ_STRUCT_FIELD(export_dir, PE_(image_export_directory), AddressOfFunctions, 32);
134  PE_READ_STRUCT_FIELD(export_dir, PE_(image_export_directory), AddressOfNames, 32);
135  PE_READ_STRUCT_FIELD(export_dir, PE_(image_export_directory), AddressOfOrdinals, 32);
136  rz_buf_seek(b, o_addr, RZ_BUF_SET);
137  return sizeof(PE_(image_export_directory));
138 }
139 
141  PE_(image_data_directory) *data_dir_export = &bin->data_directory[PE_IMAGE_DIRECTORY_ENTRY_EXPORT];
142  PE_DWord export_dir_paddr = PE_(bin_pe_rva_to_paddr)(bin, data_dir_export->VirtualAddress);
143  if (!export_dir_paddr) {
144  // This export-dir-paddr should only appear in DLL files
145  // RZ_LOG_INFO("Warning: Cannot find the paddr of the export directory\n");
146  return false;
147  }
148  // sdb_setn (DB, "hdr.exports_directory", export_dir_paddr);
149  // RZ_LOG_INFO("Pexports paddr at 0x%"PFMT64x"\n", export_dir_paddr);
150  if (!(bin->export_directory = malloc(sizeof(PE_(image_export_directory))))) {
151  rz_sys_perror("malloc (export directory)");
152  return false;
153  }
154  if (read_image_export_directory(bin->b, export_dir_paddr, bin->export_directory) < 0) {
155  RZ_LOG_INFO("read (export directory)\n");
156  RZ_FREE(bin->export_directory);
157  return false;
158  }
159  return true;
160 }
161 
164  struct rz_bin_pe_export_t *exp, *exports = NULL;
165  PE_Word function_ordinal = 0;
166  PE_VWord functions_paddr, names_paddr, ordinals_paddr, function_rva, name_vaddr, name_paddr;
167  char function_name[PE_NAME_LENGTH + 1], forwarder_name[PE_NAME_LENGTH + 1];
168  char dll_name[PE_NAME_LENGTH + 1];
169  PE_(image_data_directory) * data_dir_export;
170  PE_VWord export_dir_rva;
171  int n, i, export_dir_size;
172  st64 exports_sz = 0;
173 
174  if (!bin->data_directory) {
175  return NULL;
176  }
177  data_dir_export = &bin->data_directory[PE_IMAGE_DIRECTORY_ENTRY_EXPORT];
178  export_dir_rva = data_dir_export->VirtualAddress;
179  export_dir_size = data_dir_export->Size;
180  PE_VWord *func_rvas = NULL;
181  PE_Word *ordinals = NULL;
182  if (bin->export_directory) {
183  if (bin->export_directory->NumberOfFunctions + 1 <
184  bin->export_directory->NumberOfFunctions) {
185  // avoid integer overflow
186  return NULL;
187  }
188  exports_sz = (bin->export_directory->NumberOfFunctions + 1) * sizeof(struct rz_bin_pe_export_t);
189  // we cant exit with export_sz > bin->size, us rz_bin_pe_export_t is 256+256+8+8+8+4 bytes is easy get over file size
190  // to avoid fuzzing we can abort on export_directory->NumberOfFunctions>0xffff
191  if (exports_sz < 0 || bin->export_directory->NumberOfFunctions + 1 > 0xffff) {
192  return NULL;
193  }
194  if (!(exports = malloc(exports_sz))) {
195  return NULL;
196  }
197  if (rz_buf_read_at(bin->b, PE_(bin_pe_rva_to_paddr)(bin, bin->export_directory->Name), (ut8 *)dll_name, PE_NAME_LENGTH) < 1) {
198  // we dont stop if dll name cant be read, we set dllname to null and continue
199  RZ_LOG_INFO("read (dll name)\n");
200  dll_name[0] = '\0';
201  }
202  functions_paddr = PE_(bin_pe_rva_to_paddr)(bin, bin->export_directory->AddressOfFunctions);
203  names_paddr = PE_(bin_pe_rva_to_paddr)(bin, bin->export_directory->AddressOfNames);
204  ordinals_paddr = PE_(bin_pe_rva_to_paddr)(bin, bin->export_directory->AddressOfOrdinals);
205 
206  const size_t names_sz = bin->export_directory->NumberOfNames * sizeof(PE_Word);
207  const size_t funcs_sz = bin->export_directory->NumberOfFunctions * sizeof(PE_VWord);
208  ordinals = malloc(names_sz);
209  func_rvas = malloc(funcs_sz);
210  if (!ordinals || !func_rvas) {
211  goto beach;
212  }
213  int r = rz_buf_read_at(bin->b, ordinals_paddr, (ut8 *)ordinals, names_sz);
214  if (r != names_sz) {
215  goto beach;
216  }
217  r = rz_buf_read_at(bin->b, functions_paddr, (ut8 *)func_rvas, funcs_sz);
218  if (r != funcs_sz) {
219  goto beach;
220  }
221  for (i = 0; i < bin->export_directory->NumberOfFunctions; i++) {
222  // get vaddr from AddressOfFunctions array
223  function_rva = rz_read_at_ble32((ut8 *)func_rvas, i * sizeof(PE_VWord), bin->endian);
224  // have exports by name?
225  if (bin->export_directory->NumberOfNames > 0) {
226  // search for value of i into AddressOfOrdinals
227  name_vaddr = 0;
228  for (n = 0; n < bin->export_directory->NumberOfNames; n++) {
229  PE_Word fo = rz_read_at_ble16((ut8 *)ordinals, n * sizeof(PE_Word), bin->endian);
230  // if exist this index into AddressOfOrdinals
231  if (i == fo) {
232  function_ordinal = fo;
233  // get the VA of export name from AddressOfNames
234  if (!rz_buf_read_le32_at(bin->b, names_paddr + n * sizeof(PE_VWord), &name_vaddr)) {
235  goto beach;
236  }
237  break;
238  }
239  }
240  // have an address into name_vaddr?
241  if (name_vaddr) {
242  // get the name of the Export
243  name_paddr = PE_(bin_pe_rva_to_paddr)(bin, name_vaddr);
244  if (rz_buf_read_at(bin->b, name_paddr, (ut8 *)function_name, PE_NAME_LENGTH) < 1) {
245  RZ_LOG_INFO("read (function name)\n");
246  exports[i].last = 1;
247  return exports;
248  }
249  } else { // No name export, get the ordinal
250  function_ordinal = i;
251  snprintf(function_name, PE_NAME_LENGTH, "Ordinal_%i", i + bin->export_directory->Base);
252  }
253  } else { // if export by name dont exist, get the ordinal taking in mind the Base value.
254  snprintf(function_name, PE_NAME_LENGTH, "Ordinal_%i", i + bin->export_directory->Base);
255  }
256  // check if VA are into export directory, this mean a forwarder export
257  if (function_rva >= export_dir_rva && function_rva < (export_dir_rva + export_dir_size)) {
258  // if forwarder, the VA point to Forwarded name
259  if (rz_buf_read_at(bin->b, PE_(bin_pe_rva_to_paddr)(bin, function_rva), (ut8 *)forwarder_name, PE_NAME_LENGTH) < 1) {
260  exports[i].last = 1;
261  return exports;
262  }
263  } else { // no forwarder export
264  snprintf(forwarder_name, PE_NAME_LENGTH, "NONE");
265  }
266  dll_name[PE_NAME_LENGTH] = '\0';
267  function_name[PE_NAME_LENGTH] = '\0';
268  exports[i].vaddr = PE_(bin_pe_rva_to_va)(bin, function_rva);
269  exports[i].paddr = PE_(bin_pe_rva_to_paddr)(bin, function_rva);
270  exports[i].ordinal = function_ordinal + bin->export_directory->Base;
271  memcpy(exports[i].forwarder, forwarder_name, PE_NAME_LENGTH);
272  exports[i].forwarder[PE_NAME_LENGTH] = '\0';
273  memcpy(exports[i].name, function_name, PE_NAME_LENGTH);
274  exports[i].name[PE_NAME_LENGTH] = '\0';
275  memcpy(exports[i].libname, dll_name, PE_NAME_LENGTH);
276  exports[i].libname[PE_NAME_LENGTH] = '\0';
277  exports[i].last = 0;
278  }
279  exports[i].last = 1;
280  free(ordinals);
281  free(func_rvas);
282  }
283  exp = parse_symbol_table(bin, exports, exports_sz - sizeof(struct rz_bin_pe_export_t));
284  if (exp) {
285  exports = exp;
286  }
287  return exports;
288 beach:
289  free(exports);
290  free(ordinals);
291  free(func_rvas);
292  return NULL;
293 }
lzma_index ** i
Definition: index.h:629
RzList * sections(RzBinFile *bf)
Definition: bin_ne.c:110
#define NULL
Definition: cris-opc.c:27
#define r
Definition: crypto_rc6.c:12
uint16_t ut16
uint32_t ut32
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
voidpf void * buf
Definition: ioapi.h:138
snprintf
Definition: kernel.h:364
sprintf
Definition: kernel.h:365
uint8_t ut8
Definition: lh5801.h:11
memcpy(mem, inblock.get(), min(CONTAINING_RECORD(inblock.get(), MEMBLOCK, data) ->size, size))
void * realloc(void *ptr, size_t size)
Definition: malloc.c:144
void * malloc(size_t size)
Definition: malloc.c:123
void * calloc(size_t number, size_t size)
Definition: malloc.c:102
static static fork const void static count static fd const char const char static newpath char char char static envp time_t static t const char static mode static whence const char static dir time_t static t unsigned static seconds const char struct utimbuf static buf static inc static sig const char static mode static oldfd struct tms static buf static getgid static geteuid const char static filename static arg static mask struct ustat static ubuf static getppid static setsid static egid sigset_t static set struct timeval struct timezone static tz fd_set fd_set fd_set struct timeval static timeout const char char static bufsiz const char static swapflags void static offset const char static length static mode static who const char struct statfs static buf unsigned unsigned num
Definition: sflib.h:126
int n
Definition: mipsasm.c:19
int idx
Definition: setup.py:197
const char * name
Definition: op.c:541
PE_DWord PE_() bin_pe_rva_to_paddr(RzBinPEObj *bin, PE_DWord rva)
Definition: pe.c:15
PE_DWord PE_() bin_pe_rva_to_va(RzBinPEObj *bin, PE_DWord rva)
Definition: pe.c:28
#define RzBinPEObj
Definition: pe.h:126
#define COFF_SYMBOL_SIZE
Definition: pe.h:176
#define PE_READ_STRUCT_FIELD(var, struct_type, field, size)
Definition: pe.h:177
struct rz_bin_pe_export_t *PE_() rz_bin_pe_get_exports(RzBinPEObj *bin)
Definition: pe_exports.c:162
static int read_image_export_directory(RzBuffer *b, ut64 addr, PE_(image_export_directory) *export_dir)
Definition: pe_exports.c:118
int PE_() bin_pe_init_exports(RzBinPEObj *bin)
Definition: pe_exports.c:140
static struct rz_bin_pe_export_t * parse_symbol_table(RzBinPEObj *bin, struct rz_bin_pe_export_t *exports, int sz)
Definition: pe_exports.c:17
#define PE_(name)
Definition: pe_specs.h:23
#define PE_NAME_LENGTH
Definition: pe_specs.h:36
#define PE_Word
Definition: pe_specs.h:26
#define PE_VWord
Definition: pe_specs.h:28
#define PE_DWord
Definition: pe_specs.h:27
#define PE_IMAGE_DIRECTORY_ENTRY_EXPORT
Definition: pe_specs.h:143
#define rz_return_val_if_fail(expr, val)
Definition: rz_assert.h:108
RZ_API st64 rz_buf_seek(RZ_NONNULL RzBuffer *b, st64 addr, int whence)
Modify the current cursor position in the buffer.
Definition: buf.c:1166
#define RZ_BUF_CUR
Definition: rz_buf.h:15
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 rz_buf_read_le32_at(b, addr, result)
Definition: rz_buf.h:271
#define RZ_BUF_SET
Definition: rz_buf.h:14
RZ_API st64 rz_buf_read(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_OUT ut8 *buf, ut64 len)
static ut32 rz_read_le32(const void *src)
Definition: rz_endian.h:239
static ut16 rz_read_at_ble16(const void *src, size_t offset, bool big_endian)
Definition: rz_endian.h:505
static ut32 rz_read_at_ble32(const void *src, size_t offset, bool big_endian)
Definition: rz_endian.h:509
#define RZ_LOG_INFO(fmtstr,...)
Definition: rz_log.h:54
#define rz_sys_perror(x)
Definition: rz_types.h:336
#define RZ_FREE(x)
Definition: rz_types.h:369
#define st64
Definition: rz_types_base.h:10
#define b(i)
Definition: sha256.c:42
ut64 shortname
Definition: pe_exports.c:9
ut16 symtype
Definition: pe_exports.c:12
Definition: malloc.c:26
Definition: z80asm.h:102
ut8 forwarder[PE_NAME_LENGTH+1]
Definition: pe.h:87
ut8 name[PE_NAME_LENGTH+1]
Definition: pe.h:85
ut64 vaddr
Definition: pe.h:88
ut8 libname[PE_NAME_LENGTH+1]
Definition: pe.h:86
ut64 paddr
Definition: pe.h:89
ut64 ordinal
Definition: pe.h:90
ut64(WINAPI *w32_GetEnabledXStateFeatures)()
static int addr
Definition: z80asm.c:58