Rizin
unix-like reverse engineering framework and cli tools
pdb.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2014-2020 inisider <inisider@gmail.com>
2 // SPDX-FileCopyrightText: 2021 Basstorm <basstorm@nyist.edu.cn>
3 // SPDX-License-Identifier: LGPL-3.0-only
4 
5 #include <rz_bin.h>
6 #include <rz_type.h>
7 #include <string.h>
8 #include <rz_demangler.h>
9 #include <mspack.h>
10 
11 #include "pdb.h"
12 
14  if (!pdb || !stream) {
15  return false;
16  }
17 
18  pdb->s_pdb = RZ_NEW0(RzPdbStream);
19  RzPdbStream *s = pdb->s_pdb;
20  RzBuffer *buf = stream->stream_data;
21  if (!rz_buf_read_le32(buf, &s->hdr.version) ||
22  !rz_buf_read_le32(buf, &s->hdr.signature) ||
23  !rz_buf_read_le32(buf, &s->hdr.age) ||
24  !rz_buf_read_le32(buf, &s->hdr.unique_id.data1) ||
25  !rz_buf_read_le16(buf, &s->hdr.unique_id.data2) ||
26  !rz_buf_read_le16(buf, &s->hdr.unique_id.data3) ||
27  rz_buf_read(buf, s->hdr.unique_id.data4, 8) != 8) {
28  return false;
29  }
30 
31  if (s->hdr.version != VC70) {
32  RZ_LOG_ERROR("Error Unsupported PDB version.\n");
33  return false;
34  }
35  return true;
36 }
37 
38 static bool parse_streams(RzPdb *pdb) {
39  RzListIter *it;
40  RzPdbMsfStream *ms;
41  rz_list_foreach (pdb->streams, it, ms) {
42  switch (ms->stream_idx) {
43  case PDB_STREAM_ROOT:
44  break;
45  case PDB_STREAM_PDB:
46  if (!parse_pdb_stream(pdb, ms)) {
47  RZ_LOG_ERROR("Parse pdb stream failed.");
48  return false;
49  }
50  break;
51  case PDB_STREAM_TPI:
52  if (!parse_tpi_stream(pdb, ms)) {
53  RZ_LOG_ERROR("Parse tpi stream failed.");
54  return false;
55  }
56  break;
57  case PDB_STREAM_DBI:
58  if (!parse_dbi_stream(pdb, ms)) {
59  RZ_LOG_ERROR("Parse dbi stream failed.");
60  return false;
61  }
62  break;
63  default: {
64  if (!pdb->s_dbi) {
65  break;
66  }
67  if (ms->stream_idx == pdb->s_dbi->hdr.sym_record_stream) {
68  if (!parse_gdata_stream(pdb, ms)) {
69  RZ_LOG_ERROR("Parse gdata stream failed.");
70  return false;
71  }
72  } else if (ms->stream_idx == pdb->s_dbi->dbg_hdr.sn_section_hdr ||
74  if (!parse_pe_stream(pdb, ms)) {
75  RZ_LOG_ERROR("Parse pe stream failed.");
76  return false;
77  }
78  } else if (ms->stream_idx == pdb->s_dbi->dbg_hdr.sn_omap_to_src ||
80  if (!parse_omap_stream(pdb, ms)) {
81  RZ_LOG_ERROR("Parse omap stream failed.");
82  return false;
83  }
84  }
85  break;
86  }
87  }
88  }
89  return true;
90 }
91 
92 static void msf_stream_free(void *data) {
93  RzPdbMsfStream *msfstream = data;
94  rz_buf_free(msfstream->stream_data);
95  RZ_FREE(msfstream);
96 }
97 
98 static void msf_stream_directory_free(void *data) {
99  RzPdbMsfStreamDirectory *msd = data;
100  RZ_FREE(msd->StreamSizes);
101  rz_buf_free(msd->sd);
102  RZ_FREE(msd);
103 }
104 
105 static ut64 count_blocks(ut64 length, ut64 block_size) {
106  ut64 num_blocks = 0;
107  if (block_size > 0) {
108  num_blocks = length / block_size;
109  if (length % block_size) {
110  num_blocks++;
111  }
112  }
113  return num_blocks;
114 }
115 
118  if (!streams) {
119  goto error_memory;
120  }
121  for (size_t i = 0; i < msd->NumStreams; i++) {
123  if (!stream) {
125  goto error_memory;
126  }
127  stream->stream_idx = i;
128  stream->stream_size = msd->StreamSizes[i];
129  stream->blocks_num = count_blocks(stream->stream_size, pdb->super_block->block_size);
130  if (!stream->stream_size) {
131  stream->stream_data = NULL;
133  continue;
134  }
135  ut8 *stream_data = (ut8 *)malloc((size_t)stream->blocks_num * pdb->super_block->block_size);
136  if (!stream_data) {
137  RZ_FREE(stream);
139  RZ_LOG_ERROR("Error allocating memory.\n");
140  return NULL;
141  }
142  for (size_t j = 0; j < stream->blocks_num; j++) {
143  ut32 block_idx;
144  if (!rz_buf_read_le32(msd->sd, &block_idx)) {
145  RZ_FREE(stream);
146  RZ_FREE(stream_data);
148  return NULL;
149  }
150  rz_buf_seek(pdb->buf, (long long)block_idx * pdb->super_block->block_size, RZ_BUF_SET);
151  rz_buf_read(pdb->buf, stream_data + j * pdb->super_block->block_size, pdb->super_block->block_size);
152  }
153  stream->stream_data = rz_buf_new_with_pointers(stream_data, stream->stream_size, true);
154  if (!stream->stream_data) {
155  RZ_FREE(stream);
156  RZ_FREE(stream_data);
158  goto error_memory;
159  }
161  }
163  return streams;
164 
165 error_memory:
166  RZ_LOG_ERROR("Error memory allocation.\n");
167  return NULL;
168 }
169 
171  // Get block map
173  if (!block_num) {
174  RZ_LOG_ERROR("Error block map size.\n");
175  goto error;
176  }
177  rz_buf_seek(pdb->buf, (long long)pdb->super_block->block_size * pdb->super_block->block_map_addr, RZ_BUF_SET);
178  ut32 *block_map = (ut32 *)malloc(sizeof(ut32) * block_num);
179  if (!block_map) {
180  goto error_memory;
181  }
182  for (size_t i = 0; i < block_num; i++) {
183  ut32 block_idx;
184  if (!rz_buf_read_le32(pdb->buf, &block_idx)) {
185  RZ_FREE(block_map);
186  goto error;
187  }
188  if (block_idx > pdb->super_block->num_blocks) {
189  RZ_LOG_ERROR("Error block index.\n");
190  RZ_FREE(block_map);
191  goto error;
192  }
193  block_map[i] = block_idx;
194  }
195 
196  ut32 stream_directory_len = block_num * pdb->super_block->block_size;
197  ut8 *stream_directory = (ut8 *)malloc(stream_directory_len);
198  if (!stream_directory) {
199  RZ_FREE(block_map);
200  goto error_memory;
201  }
202  for (size_t i = 0; i < block_num; i++) {
203  rz_buf_seek(pdb->buf, (long long)block_map[i] * pdb->super_block->block_size, RZ_BUF_SET);
204  rz_buf_read(pdb->buf, stream_directory + i * pdb->super_block->block_size, pdb->super_block->block_size);
205  }
206  RzBuffer *sd = rz_buf_new_with_pointers(stream_directory, stream_directory_len, true);
207  if (!sd) {
208  RZ_FREE(stream_directory);
209  RZ_FREE(block_map);
210  goto error_memory;
211  }
212  RZ_FREE(block_map);
213 
215  if (!msd) {
216  goto error_memory;
217  }
218  if (!rz_buf_read_le32(sd, &msd->NumStreams)) {
219  RZ_FREE(msd);
220  goto error;
221  }
222  msd->StreamSizes = (ut32 *)malloc(msd->NumStreams * sizeof(ut32));
223  msd->sd = sd;
224  if (!msd->StreamSizes) {
225  RZ_FREE(msd);
226  goto error_memory;
227  }
228  ut32 total_blocks = 0;
229  for (size_t i = 0; i < msd->NumStreams; i++) {
230  ut32 stream_size;
231  if (!rz_buf_read_le32(sd, &stream_size)) {
232  RZ_FREE(msd);
233  goto error;
234  }
235  if (stream_size == UT32_MAX) {
236  msd->StreamSizes[i] = 0;
237  continue;
238  }
239  msd->StreamSizes[i] = stream_size;
240  ut32 blocks = count_blocks(stream_size, pdb->super_block->block_size);
241  total_blocks += blocks;
242  }
243  // NumStreams StreamsSizes StreamsBlockMap
244  ut32 msd_size = sizeof(ut32) + msd->NumStreams * sizeof(ut32) + total_blocks * sizeof(ut32);
245  if (msd_size != pdb->super_block->num_directory_bytes) {
246  RZ_LOG_ERROR("Error stream directory size.\n");
247  RZ_FREE(msd);
248  goto error;
249  }
250  return msd;
251 
252 error_memory:
253  RZ_LOG_ERROR("Error memory allocation.\n");
254 error:
255  return NULL;
256 }
257 
258 static bool pdb7_parse(RzPdb *pdb) {
260  if (!msd) {
261  RZ_LOG_ERROR("Error extracting stream directory.\n");
262  goto error;
263  }
264  pdb->streams = pdb7_extract_streams(pdb, msd);
265  if (!pdb->streams) {
267  RZ_LOG_ERROR("Error extracting streams.\n");
268  goto error;
269  }
270  return parse_streams(pdb);
271 error:
272  return false;
273 }
274 
276  ut8 magic[4] = { 0 };
277  // avoids to seek back, when using rz_buf_read_at
278  if (rz_buf_read_at(buf, 0, magic, sizeof(magic)) != sizeof(magic)) {
279  return false;
280  } else if (memcmp(magic, CAB_SIGNATURE, sizeof(magic))) {
281  return false;
282  }
283  return true;
284 }
285 
295  if (!buf) {
296  RZ_LOG_ERROR("%s: Error reading file \"%s\"\n", __FUNCTION__, filename);
297  return false;
298  }
299 
300  if (is_compressed_pdb(buf)) {
301  rz_buf_free(buf);
302  RZ_LOG_ERROR("The pdb file %s seems to be compressed, please use idpx command to extract the contents.\n", filename);
303  return NULL;
304  }
305 
307 }
308 
317  RzPdb *pdb = RZ_NEW0(RzPdb);
318  if (!pdb) {
319  goto error;
320  }
321  pdb->buf = (RzBuffer *)buf;
324  if (len != PDB_SIGNATURE_LEN) {
325  RZ_LOG_ERROR("Wrong magic length!\n");
326  goto error;
327  }
329  RZ_LOG_ERROR("PDB Signature Error!\n");
330  goto error;
331  }
332  if (!rz_buf_read_le32(pdb->buf, &pdb->super_block->block_size) ||
334  !rz_buf_read_le32(pdb->buf, &pdb->super_block->num_blocks) ||
336  !rz_buf_read_le32(pdb->buf, &pdb->super_block->unknown) ||
338  goto error;
339  }
340  ut64 bufsize = rz_buf_size((RzBuffer *)buf); // length of whole PDB file
341  bool valid =
342  pdb->super_block->num_blocks > 0 &&
343  (ut64)pdb->super_block->num_blocks * pdb->super_block->block_size == bufsize &&
346  if (!valid) {
347  RZ_LOG_ERROR("Invalid MSF superblock!\n");
348  goto error;
349  }
350  if (!pdb7_parse(pdb)) {
351  goto error;
352  }
353  return pdb;
354 error:
355 
356  rz_bin_pdb_free(pdb);
357  return NULL;
358 }
359 
367  if (!pdb) {
368  return;
369  }
370  rz_buf_free(pdb->buf);
371  free(pdb->super_block);
372  rz_list_free(pdb->streams);
373  free(pdb->s_pdb);
374  free_dbi_stream(pdb->s_dbi);
376  free_omap_stream(pdb->s_omap);
377  free_tpi_stream(pdb->s_tpi);
378  free_pe_stream(pdb->s_pe);
379  free(pdb);
380 }
size_t len
Definition: 6502dis.c:15
lzma_index ** i
Definition: index.h:629
#define RZ_API
#define NULL
Definition: cris-opc.c:27
static static sync static getppid static getegid const char static filename char static len const char char static bufsiz static mask static vfork const void static prot static getpgrp const char static swapflags static arg static fd static protocol static who struct sockaddr static addrlen static backlog struct timeval struct timezone static tz const struct iovec static count static mode const void const struct sockaddr static tolen const char static pathname void static offset struct stat static buf void long static basep static whence static length const void static len static semflg const void static shmflg const struct timespec struct timespec static rem const char static group const void length
Definition: sflib.h:133
RZ_IPI bool parse_dbi_stream(RzPdb *pdb, RzPdbMsfStream *stream)
Definition: dbi.c:151
RZ_IPI void free_dbi_stream(RzPdbDbiStream *stream)
Definition: dbi.c:7
uint32_t ut32
RZ_IPI bool parse_gdata_stream(RzPdb *pdb, RzPdbMsfStream *stream)
Definition: gdata.c:33
RZ_IPI void free_gdata_stream(RzPdbGDataStream *stream)
Definition: gdata.c:80
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
const char * filename
Definition: ioapi.h:137
voidpf stream
Definition: ioapi.h:138
voidpf void * buf
Definition: ioapi.h:138
uint8_t ut8
Definition: lh5801.h:11
RZ_API RZ_OWN RzList * rz_list_newf(RzListFree f)
Returns a new initialized RzList pointer and sets the free method.
Definition: list.c:248
RZ_API RZ_BORROW RzListIter * rz_list_append(RZ_NONNULL RzList *list, void *data)
Appends at the end of the list a new element.
Definition: list.c:288
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
RZ_IPI bool parse_omap_stream(RzPdb *pdb, RzPdbMsfStream *stream)
Definition: omap.c:8
RZ_IPI void free_omap_stream(RzPdbOmapStream *stream)
Definition: omap.c:42
static bool parse_streams(RzPdb *pdb)
Definition: pdb.c:38
static RzPdbMsfStreamDirectory * pdb7_extract_msf_stream_directory(RzPdb *pdb)
Definition: pdb.c:170
static void msf_stream_directory_free(void *data)
Definition: pdb.c:98
static bool pdb7_parse(RzPdb *pdb)
Definition: pdb.c:258
RZ_API RZ_OWN RzPdb * rz_bin_pdb_parse_from_file(RZ_NONNULL const char *filename)
Parse PDB file given the path.
Definition: pdb.c:292
bool is_compressed_pdb(RzBuffer *buf)
Definition: pdb.c:275
static ut64 count_blocks(ut64 length, ut64 block_size)
Definition: pdb.c:105
static void msf_stream_free(void *data)
Definition: pdb.c:92
RZ_API void rz_bin_pdb_free(RzPdb *pdb)
Free PDB instance.
Definition: pdb.c:366
RZ_API RZ_OWN RzPdb * rz_bin_pdb_parse_from_buf(RZ_NONNULL const RzBuffer *buf)
Parse PDB from the buffer.
Definition: pdb.c:315
static RzList * pdb7_extract_streams(RzPdb *pdb, RzPdbMsfStreamDirectory *msd)
Definition: pdb.c:116
static bool parse_pdb_stream(RzPdb *pdb, RzPdbMsfStream *stream)
Definition: pdb.c:13
RZ_IPI bool parse_tpi_stream(RzPdb *pdb, RzPdbMsfStream *stream)
Definition: tpi.c:1799
RZ_IPI void free_pe_stream(RzPdbPeStream *stream)
Definition: stream_pe.c:53
RZ_IPI bool parse_pe_stream(RzPdb *pdb, RzPdbMsfStream *stream)
Definition: stream_pe.c:20
RZ_IPI void free_tpi_stream(RzPdbTpiStream *stream)
Definition: tpi.c:705
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_seek(RZ_NONNULL RzBuffer *b, st64 addr, int whence)
Modify the current cursor position in the buffer.
Definition: buf.c:1166
RZ_API RZ_OWN RzBuffer * rz_buf_new_slurp(const char *file)
Creates a new buffer from a file.
Definition: buf.c:384
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 RZ_OWN RzBuffer * rz_buf_new_with_pointers(const ut8 *bytes, ut64 len, bool steal)
Creates a new buffer with a bytes array.
Definition: buf.c:552
#define rz_buf_read_le16(b, result)
Read a big endian or little endian (ut16, ut32, ut64) at the specified offset in the buffer and shift...
Definition: rz_buf.h:266
#define RZ_BUF_SET
Definition: rz_buf.h:14
#define rz_buf_read_le32(b, result)
Definition: rz_buf.h:267
RZ_API void rz_buf_free(RzBuffer *b)
Free all internal data hold by the buffer and the buffer.
Definition: buf.c:1253
RZ_API st64 rz_buf_read(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_OUT ut8 *buf, ut64 len)
RZ_API ut64 rz_buf_size(RZ_NONNULL RzBuffer *b)
Return the size of the buffer.
Definition: buf.c:1225
#define RZ_LOG_ERROR(fmtstr,...)
Definition: rz_log.h:58
#define PDB_SIGNATURE
Definition: rz_pdb.h:16
#define PDB_SIGNATURE_LEN
Definition: rz_pdb.h:17
#define CAB_SIGNATURE
Definition: rz_pdb.h:15
@ VC70
Definition: rz_pdb.h:187
@ PDB_STREAM_TPI
Definition: rz_pdb.h:164
@ PDB_STREAM_PDB
Definition: rz_pdb.h:163
@ PDB_STREAM_DBI
Definition: rz_pdb.h:165
@ PDB_STREAM_ROOT
Definition: rz_pdb.h:162
#define RZ_OWN
Definition: rz_types.h:62
#define RZ_NEW0(x)
Definition: rz_types.h:284
#define RZ_NONNULL
Definition: rz_types.h:64
#define RZ_FREE(x)
Definition: rz_types.h:369
#define st64
Definition: rz_types_base.h:10
#define UT32_MAX
Definition: rz_types_base.h:99
RzBuffer * stream_data
Definition: rz_pdb.h:232
ut32 stream_idx
Definition: rz_pdb.h:229
MSF file format header https://llvm.org/docs/PDB/MsfFile.html#the-superblock.
Definition: rz_pdb.h:218
ut32 num_directory_bytes
The size of the stream directory, in bytes.
Definition: rz_pdb.h:223
ut32 num_blocks
The total number of blocks in the file.
Definition: rz_pdb.h:222
ut32 block_size
The block size of the internal file system.
Definition: rz_pdb.h:220
ut32 block_map_addr
The index of a block within the MSF file.
Definition: rz_pdb.h:225
char file_magic[PDB_SIGNATURE_LEN]
Must be equal to "Microsoft C / C++ MSF 7.00\\r\\n" followed by the bytes 1A 44 53 00 00 00.
Definition: rz_pdb.h:219
ut32 free_block_map_block
The index of a block within the file, the data within that block is not used.
Definition: rz_pdb.h:221
ut16 sym_record_stream
Definition: rz_pdb.h:30
RzPdbRzPdbDbiStreamHdr hdr
Definition: rz_pdb.h:60
RzPdbRzPdbDbiStreamDbgHeader dbg_hdr
Definition: rz_pdb.h:62
RzPdbDbiStream * s_dbi
Definition: rz_pdb.h:246
RzPdbGDataStream * s_gdata
Definition: rz_pdb.h:248
RzPdbTpiStream * s_tpi
Definition: rz_pdb.h:247
RzList * streams
Definition: rz_pdb.h:244
RzPdbPeStream * s_pe
Definition: rz_pdb.h:250
RzPdbMsfSuperBlock * super_block
Definition: rz_pdb.h:243
RzPdbOmapStream * s_omap
Definition: rz_pdb.h:249
RzPdbStream * s_pdb
Definition: rz_pdb.h:245
RzBuffer * buf
Definition: rz_pdb.h:242
bool valid
Definition: core.c:77
uint64_t streams
Definition: list.c:103
uint64_t blocks
Definition: list.c:104
void error(const char *msg)
Definition: untgz.c:593
if(dbg->bits==RZ_SYS_BITS_64)
Definition: windows-arm64.h:4
ut64(WINAPI *w32_GetEnabledXStateFeatures)()