Skip to content

Instantly share code, notes, and snippets.

@namazso
Created April 18, 2018 17:49
Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save namazso/4bfafdb0233f72f5d13bfee825c203f7 to your computer and use it in GitHub Desktop.
Save namazso/4bfafdb0233f72f5d13bfee825c203f7 to your computer and use it in GitHub Desktop.
A lightweight PDB parser that retrieves type and symbol CodeView streams.
/* MIT License
*
* Copyright (c) namazso 2018
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <cstdio>
#include <cstdint>
#include <vector>
#include <array>
#include <cassert>
#pragma pack(push, 1)
struct SuperBlock
{
constexpr static char kMagic[] =
{
0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66, 0x74, 0x20, 0x43, 0x2F,
0x43, 0x2B, 0x2B, 0x20, 0x4D, 0x53, 0x46, 0x20, 0x37, 0x2E, 0x30, 0x30,
0x0D, 0x0A, 0x1A, 0x44, 0x53, 0x00, 0x00, 0x00
};
char FileMagic[sizeof(kMagic)];
std::uint32_t BlockSize;
std::uint32_t FreeBlockMapBlock;
std::uint32_t NumBlocks;
std::uint32_t NumDirectoryBytes;
std::uint32_t Unknown;
std::uint32_t BlockMapAddr;
bool is_magic_valid() const
{
return 0 == memcmp(FileMagic, kMagic, sizeof(kMagic));
}
};
struct DBIHeader
{
std::int32_t VersionSignature;
std::uint32_t VersionHeader;
std::uint32_t Age;
std::uint16_t GlobalStreamIndex;
std::uint16_t BuildNumber;
std::uint16_t PublicStreamIndex;
std::uint16_t PdbDllVersion;
std::uint16_t SymRecordStream;
std::uint16_t PdbDllRbld;
std::int32_t ModInfoSize;
std::int32_t SectionContributionSize;
std::int32_t SectionMapSize;
std::int32_t SourceInfoSize;
std::int32_t TypeServerSize;
std::uint32_t MFCTypeServerIndex;
std::int32_t OptionalDbgHeaderSize;
std::int32_t ECSubstreamSize;
std::uint16_t Flags;
std::uint16_t Machine;
std::uint32_t Padding;
};
#pragma pack(pop)
static auto get_stream_directory(
void* base
) -> std::vector<std::uint8_t>
{
std::vector<std::uint8_t> stream_dir;
const auto super = static_cast<SuperBlock*>(base);
const auto size = super->NumDirectoryBytes;
const auto block_size = super->BlockSize;
// ceil(NumDirectoryBytes / BlockSize)
const auto block_count = (size + block_size - 1) / block_size;
const auto block_id_array = reinterpret_cast<uint32_t*>(static_cast<uint8_t*>(base) + block_size * super->BlockMapAddr);
stream_dir.reserve(block_count * block_size);
for (auto i = 0u; i < block_count; ++i)
{
const auto block = static_cast<uint8_t*>(base) + block_size * block_id_array[i];
stream_dir.insert(stream_dir.end(), block, block + block_size);
}
stream_dir.resize(size);
return stream_dir;
}
auto get_streams(
void* base
) -> std::vector<std::vector<std::uint8_t>>
{
std::vector<std::vector<std::uint8_t>> streams;
const auto super = static_cast<SuperBlock*>(base);
assert(super->is_magic_valid());
const auto block_size = super->BlockSize;
auto stream_dir = get_stream_directory(base);
auto ui32_iter = reinterpret_cast<uint32_t*>(stream_dir.data());
const auto stream_num = *ui32_iter++;
const auto stream_array = ui32_iter;
ui32_iter += stream_num;
streams.reserve(stream_num);
for (auto i = 0u; i < stream_num; ++i)
{
std::vector<std::uint8_t> current_stream;
const auto current_stream_size = stream_array[i];
const auto current_stream_block_count = (current_stream_size + block_size - 1) / block_size;
current_stream.reserve(current_stream_block_count * block_size);
for (auto j = 0u; j < current_stream_block_count; ++j)
{
const auto block_id = *ui32_iter++;
const auto block = static_cast<uint8_t*>(base) + block_size * block_id;
current_stream.insert(current_stream.end(), block, block + block_size);
}
current_stream.resize(current_stream_size);
streams.push_back(std::move(current_stream));
}
return streams;
}
auto get_types_and_symbols(
void* pdb,
std::vector<uint8_t>& types,
std::vector<uint8_t>& symbols
)
{
auto streams = get_streams(pdb);
types = std::move(streams[2]);
const auto dbi_header = reinterpret_cast<DBIHeader*>(streams[3].data());
const auto symbol_index = dbi_header->SymRecordStream;
symbols = std::move(streams[symbol_index]);
}
int main()
{
std::vector<uint8_t> symbols;
std::vector<uint8_t> types;
{
const auto fp = fopen("test.pdb", "rb");
fseek(fp, 0, SEEK_END);
const auto sz = ftell(fp);
rewind(fp);
const auto pdb = new std::uint8_t[sz];
fread(pdb, 1, sz, fp);
fclose(fp);
get_types_and_symbols(pdb, types, symbols);
delete[] pdb;
}
{
struct PUBSYM32
{
std::uint16_t reclen; // Record length
std::uint16_t rectyp; // S_PUB32
std::uint32_t pubsymflags;
std::uint32_t off;
std::uint16_t seg;
char name[1]; // Length-prefixed name
};
enum { S_PUB32 = 0x110e };
auto it = symbols.data();
const auto end = it + symbols.size();
while(it != end)
{
const auto curr = reinterpret_cast<PUBSYM32*>(it);
if(curr->rectyp == S_PUB32)
{
printf("S_PUB32: [%04X:%08X], Flags : %08X, %s\n", curr->seg, curr->off, curr->pubsymflags, curr->name);
}
it += curr->reclen + 2;
}
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment