From 4d414edb7747da48118a179616a6bbb5bc8dcf81 Mon Sep 17 00:00:00 2001 From: Maxime Meignan Date: Tue, 23 Aug 2022 19:59:47 +0200 Subject: [PATCH] Implements a check on PDB files to avoid using an invalid one and crash the machine When loading a PDB that was already on disk (not downloaded) for a specific PE, verifies that the PDB file is indeed for the current version of the target PE. (Did I just started to write a PDB file parser ?) --- EDRSandblast/EDRSandblast.vcxproj | 2 + EDRSandblast/EDRSandblast.vcxproj.filters | 6 ++ EDRSandblast/Includes/PdbParser.h | 4 + EDRSandblast/Includes/PdbSymbols.h | 2 + EDRSandblast/Utils/PdbParser.c | 115 ++++++++++++++++++++++ EDRSandblast/Utils/PdbSymbols.c | 20 +++- 6 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 EDRSandblast/Includes/PdbParser.h create mode 100644 EDRSandblast/Utils/PdbParser.c diff --git a/EDRSandblast/EDRSandblast.vcxproj b/EDRSandblast/EDRSandblast.vcxproj index a982537..c40556d 100644 --- a/EDRSandblast/EDRSandblast.vcxproj +++ b/EDRSandblast/EDRSandblast.vcxproj @@ -178,6 +178,7 @@ + @@ -208,6 +209,7 @@ + diff --git a/EDRSandblast/EDRSandblast.vcxproj.filters b/EDRSandblast/EDRSandblast.vcxproj.filters index ce385a3..ad5b31d 100644 --- a/EDRSandblast/EDRSandblast.vcxproj.filters +++ b/EDRSandblast/EDRSandblast.vcxproj.filters @@ -126,6 +126,9 @@ Source Files + + Source Files + @@ -248,6 +251,9 @@ Header Files + + Header Files + diff --git a/EDRSandblast/Includes/PdbParser.h b/EDRSandblast/Includes/PdbParser.h new file mode 100644 index 0000000..74306b5 --- /dev/null +++ b/EDRSandblast/Includes/PdbParser.h @@ -0,0 +1,4 @@ +#pragma once +#include + +PVOID extractGuidFromPdb(LPWSTR filepath); \ No newline at end of file diff --git a/EDRSandblast/Includes/PdbSymbols.h b/EDRSandblast/Includes/PdbSymbols.h index 89819cd..bbd171a 100644 --- a/EDRSandblast/Includes/PdbSymbols.h +++ b/EDRSandblast/Includes/PdbSymbols.h @@ -1,4 +1,5 @@ #pragma once +#include "PEParser.h" typedef struct symbol_ctx_t { LPWSTR pdb_name_w; @@ -6,6 +7,7 @@ typedef struct symbol_ctx_t { HANDLE sym_handle; } symbol_ctx; +symbol_ctx* LoadSymbolsFromPE(PE* pe); symbol_ctx* LoadSymbolsFromImageFile(LPCWSTR image_file_path); DWORD64 GetSymbolOffset(symbol_ctx* ctx, LPCSTR symbol_name); DWORD GetFieldOffset(symbol_ctx* ctx, LPCSTR struct_name, LPCWSTR field_name); diff --git a/EDRSandblast/Utils/PdbParser.c b/EDRSandblast/Utils/PdbParser.c new file mode 100644 index 0000000..0f72ca5 --- /dev/null +++ b/EDRSandblast/Utils/PdbParser.c @@ -0,0 +1,115 @@ +#include + +typedef DWORD ulittle32_t; + +typedef struct SuperBlock_t { + char FileMagic[0x20]; + ulittle32_t BlockSize; + ulittle32_t FreeBlockMapBlock; + ulittle32_t NumBlocks; + ulittle32_t NumDirectoryBytes; + ulittle32_t Unknown; + ulittle32_t BlockMapAddr; +}SuperBlock; + + +/* +struct StreamDirectory { + ulittle32_t NumStreams; + ulittle32_t StreamSizes[NumStreams]; + ulittle32_t StreamBlocks[NumStreams][]; +}; +*/ + +typedef struct PdbInfoStreamHeader_t { + DWORD Version; + DWORD Signature; + DWORD Age; + GUID UniqueId; +} PdbInfoStreamHeader; + +PVOID extractGuidFromPdb(LPWSTR filepath) { + GUID* guid = NULL; + HANDLE hFile = CreateFileW(filepath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return NULL; + } + HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (hMapping == NULL) { + goto clean_file; + } + PBYTE filemap = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0); + if (filemap == NULL) { + goto clean_mapping; + } + SuperBlock* superblock = (SuperBlock*)filemap; + DWORD blockSize = superblock->BlockSize; + DWORD* StreamDirectoryBlockMap = (DWORD*)(filemap + (ULONG_PTR)superblock->BlockMapAddr * blockSize); + DWORD* StreamDirectory = calloc(superblock->NumDirectoryBytes, 1); + if (StreamDirectory == NULL) { + goto clean_viewoffile; + } + DWORD StreamDirectoryBlockIndex = 0; + DWORD StreamDirectoryRemainingSize = superblock->NumDirectoryBytes; + while (StreamDirectoryRemainingSize) { + DWORD SizeToCopy = min(StreamDirectoryRemainingSize, blockSize); + memcpy( + ((PBYTE)StreamDirectory) + (ULONG_PTR)StreamDirectoryBlockIndex * blockSize, + ((PBYTE)filemap) + (ULONG_PTR)blockSize * StreamDirectoryBlockMap[StreamDirectoryBlockIndex], + SizeToCopy); + StreamDirectoryBlockIndex++; + StreamDirectoryRemainingSize -= SizeToCopy; + } + DWORD NumStreams = StreamDirectory[0]; + if (NumStreams < 2) { + goto clean_StreamDirectory; + } + DWORD** StreamBlocks = calloc(NumStreams, sizeof(DWORD*)); + if (StreamBlocks == NULL) { + goto clean_StreamDirectory; + } + DWORD* StreamBlocksFlat = &StreamDirectory[1 + NumStreams]; + DWORD i = 0; + if ((1 + NumStreams) >= superblock->NumDirectoryBytes / 4) { + goto clean_StreamBlocks; + } + for (DWORD stream_i = 0; stream_i < NumStreams; stream_i++) { + DWORD StreamSize = StreamDirectory[1 + stream_i]; + DWORD StreamBlockCount = 0; + while (StreamBlockCount * blockSize < StreamSize) { + PVOID tmp = realloc(StreamBlocks[stream_i], ((SIZE_T)StreamBlockCount + 1) * sizeof(DWORD)); + if (tmp == NULL) { + goto clean_StreamBlocks; + } + StreamBlocks[stream_i] = tmp; + StreamBlocks[stream_i][StreamBlockCount] = StreamBlocksFlat[i]; + i++; + StreamBlockCount++; + } + } + DWORD PdbInfoStreamSize = StreamDirectory[1 + 1]; + if (PdbInfoStreamSize == 0) { + goto clean_StreamBlocks; + } + PdbInfoStreamHeader* PdbInfoStream = (PdbInfoStreamHeader*)(filemap + (ULONG_PTR)StreamBlocks[1][0] * blockSize); + guid = calloc(1, sizeof(GUID)); + if (guid == NULL) { + goto clean_StreamBlocks; + } + memcpy(guid, &PdbInfoStream->UniqueId, sizeof(GUID)); +clean_StreamBlocks: + for (DWORD stream_i = 0; stream_i < NumStreams; stream_i++) { + free(StreamBlocks[stream_i]); + } + free(StreamBlocks); +clean_StreamDirectory: + free(StreamDirectory); +clean_viewoffile: + UnmapViewOfFile(filemap); +clean_mapping: + CloseHandle(hMapping); +clean_file: + CloseHandle(hFile); + return guid; +} + diff --git a/EDRSandblast/Utils/PdbSymbols.c b/EDRSandblast/Utils/PdbSymbols.c index e2198c7..73d5634 100644 --- a/EDRSandblast/Utils/PdbSymbols.c +++ b/EDRSandblast/Utils/PdbSymbols.c @@ -7,6 +7,7 @@ #include "HttpClient.h" #include "PEParser.h" #include "PrintFunctions.h" +#include "PdbParser.h" #include "PdbSymbols.h" @@ -59,10 +60,26 @@ symbol_ctx* LoadSymbolsFromPE(PE* pe) { if (ctx == NULL) { return NULL; } + if (strchr(pe->codeviewDebugInfo->pdbName, '\\')) { + // path is strange, PDB file won't be found on Microsoft Symbol Server, better give up... + return NULL; + } int size_needed = MultiByteToWideChar(CP_UTF8, 0, pe->codeviewDebugInfo->pdbName, -1, NULL, 0); ctx->pdb_name_w = calloc(size_needed, sizeof(WCHAR)); MultiByteToWideChar(CP_UTF8, 0, pe->codeviewDebugInfo->pdbName, -1, ctx->pdb_name_w, size_needed); + BOOL needPdbDownload = FALSE; if (!FileExistsW(ctx->pdb_name_w)) { + needPdbDownload = TRUE; + } + else { + // PDB file exists, but is it the right version ? + GUID* guid = extractGuidFromPdb(ctx->pdb_name_w); + if (!guid || memcmp(guid, &pe->codeviewDebugInfo->guid, sizeof(GUID))) { + needPdbDownload = TRUE; + } + free(guid); + } + if (needPdbDownload){ PBYTE file; SIZE_T file_size; BOOL res = DownloadPDBFromPE(pe, &file, &file_size); @@ -73,9 +90,6 @@ symbol_ctx* LoadSymbolsFromPE(PE* pe) { WriteFullFileW(ctx->pdb_name_w, file, file_size); free(file); } - else { - //TODO : check if exisiting PDB corresponds to the file version - } DWORD64 asked_pdb_base_addr = 0x1337000; DWORD pdb_image_size = MAXDWORD; HANDLE cp = GetCurrentProcess();