#include "pdb.h" #include "cvinfo.h" #include "utils.h" // ======================= // ====== windows.h ====== // ======================= extern u32 GetLastError(void); typedef struct _SECURITY_ATTRIBUTES { u32 nLength; void* lpSecurityDescriptor; i32 bInheritHandle; } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES; typedef struct _OVERLAPPED { u64* Internal; u64* InternalHigh; union { struct { u32 Offset; u32 OffsetHigh; }; void* Pointer; }; void* hEvent; } OVERLAPPED, *LPOVERLAPPED; // // Define access rights to files and directories // // // The FILE_READ_DATA and FILE_WRITE_DATA constants are also defined in // devioctl.h as FILE_READ_ACCESS and FILE_WRITE_ACCESS. The values for these // constants *MUST* always be in sync. // The values are redefined in devioctl.h because they must be available to // both DOS and NT. // #define FILE_READ_DATA ( 0x0001 ) // file & pipe #define FILE_LIST_DIRECTORY ( 0x0001 ) // directory #define FILE_WRITE_DATA ( 0x0002 ) // file & pipe #define FILE_ADD_FILE ( 0x0002 ) // directory #define FILE_APPEND_DATA ( 0x0004 ) // file #define FILE_ADD_SUBDIRECTORY ( 0x0004 ) // directory #define FILE_CREATE_PIPE_INSTANCE ( 0x0004 ) // named pipe #define FILE_READ_EA ( 0x0008 ) // file & directory #define FILE_WRITE_EA ( 0x0010 ) // file & directory #define FILE_EXECUTE ( 0x0020 ) // file #define FILE_TRAVERSE ( 0x0020 ) // directory #define FILE_DELETE_CHILD ( 0x0040 ) // directory #define FILE_READ_ATTRIBUTES ( 0x0080 ) // all #define FILE_WRITE_ATTRIBUTES ( 0x0100 ) // all #define FILE_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF) #define FILE_GENERIC_READ (STANDARD_RIGHTS_READ |\ FILE_READ_DATA |\ FILE_READ_ATTRIBUTES |\ FILE_READ_EA |\ SYNCHRONIZE) #define FILE_GENERIC_WRITE (STANDARD_RIGHTS_WRITE |\ FILE_WRITE_DATA |\ FILE_WRITE_ATTRIBUTES |\ FILE_WRITE_EA |\ FILE_APPEND_DATA |\ SYNCHRONIZE) #define FILE_GENERIC_EXECUTE (STANDARD_RIGHTS_EXECUTE |\ FILE_READ_ATTRIBUTES |\ FILE_EXECUTE |\ SYNCHRONIZE) #define FILE_SHARE_READ 0x00000001 #define FILE_SHARE_WRITE 0x00000002 #define FILE_SHARE_DELETE 0x00000004 #define FILE_ATTRIBUTE_READONLY 0x00000001 #define FILE_ATTRIBUTE_HIDDEN 0x00000002 #define FILE_ATTRIBUTE_SYSTEM 0x00000004 #define FILE_ATTRIBUTE_DIRECTORY 0x00000010 #define FILE_ATTRIBUTE_ARCHIVE 0x00000020 #define FILE_ATTRIBUTE_DEVICE 0x00000040 #define FILE_ATTRIBUTE_NORMAL 0x00000080 #define FILE_ATTRIBUTE_TEMPORARY 0x00000100 #define FILE_ATTRIBUTE_SPARSE_FILE 0x00000200 #define FILE_ATTRIBUTE_REPARSE_POINT 0x00000400 #define FILE_ATTRIBUTE_COMPRESSED 0x00000800 #define FILE_ATTRIBUTE_OFFLINE 0x00001000 #define FILE_ATTRIBUTE_NOT_CONTENT_INDEXED 0x00002000 #define FILE_ATTRIBUTE_ENCRYPTED 0x00004000 #define FILE_ATTRIBUTE_INTEGRITY_STREAM 0x00008000 #define FILE_ATTRIBUTE_VIRTUAL 0x00010000 #define FILE_ATTRIBUTE_NO_SCRUB_DATA 0x00020000 #define FILE_ATTRIBUTE_EA 0x00040000 #define FILE_ATTRIBUTE_PINNED 0x00080000 #define FILE_ATTRIBUTE_UNPINNED 0x00100000 #define FILE_ATTRIBUTE_RECALL_ON_OPEN 0x00040000 #define FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS 0x00400000 #define TREE_CONNECT_ATTRIBUTE_PRIVACY 0x00004000 #define TREE_CONNECT_ATTRIBUTE_INTEGRITY 0x00008000 #define TREE_CONNECT_ATTRIBUTE_GLOBAL 0x00000004 #define TREE_CONNECT_ATTRIBUTE_PINNED 0x00000002 #define FILE_ATTRIBUTE_STRICTLY_SEQUENTIAL 0x20000000 #define FILE_NOTIFY_CHANGE_FILE_NAME 0x00000001 #define FILE_NOTIFY_CHANGE_DIR_NAME 0x00000002 #define FILE_NOTIFY_CHANGE_ATTRIBUTES 0x00000004 #define FILE_NOTIFY_CHANGE_SIZE 0x00000008 #define FILE_NOTIFY_CHANGE_LAST_WRITE 0x00000010 #define FILE_NOTIFY_CHANGE_LAST_ACCESS 0x00000020 #define FILE_NOTIFY_CHANGE_CREATION 0x00000040 #define FILE_NOTIFY_CHANGE_SECURITY 0x00000100 #define FILE_ACTION_ADDED 0x00000001 #define FILE_ACTION_REMOVED 0x00000002 #define FILE_ACTION_MODIFIED 0x00000003 #define FILE_ACTION_RENAMED_OLD_NAME 0x00000004 #define FILE_ACTION_RENAMED_NEW_NAME 0x00000005 #define MAILSLOT_NO_MESSAGE ((DWORD)-1) #define MAILSLOT_WAIT_FOREVER ((DWORD)-1) #define FILE_CASE_SENSITIVE_SEARCH 0x00000001 #define FILE_CASE_PRESERVED_NAMES 0x00000002 #define FILE_UNICODE_ON_DISK 0x00000004 #define FILE_PERSISTENT_ACLS 0x00000008 #define FILE_FILE_COMPRESSION 0x00000010 #define FILE_VOLUME_QUOTAS 0x00000020 #define FILE_SUPPORTS_SPARSE_FILES 0x00000040 #define FILE_SUPPORTS_REPARSE_POINTS 0x00000080 #define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100 #define FILE_RETURNS_CLEANUP_RESULT_INFO 0x00000200 #define FILE_SUPPORTS_POSIX_UNLINK_RENAME 0x00000400 #define FILE_SUPPORTS_BYPASS_IO 0x00000800 #define FILE_SUPPORTS_STREAM_SNAPSHOTS 0x00001000 #define FILE_SUPPORTS_CASE_SENSITIVE_DIRS 0x00002000 #define FILE_VOLUME_IS_COMPRESSED 0x00008000 #define FILE_SUPPORTS_OBJECT_IDS 0x00010000 #define FILE_SUPPORTS_ENCRYPTION 0x00020000 #define FILE_NAMED_STREAMS 0x00040000 #define FILE_READ_ONLY_VOLUME 0x00080000 #define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000 #define FILE_SUPPORTS_TRANSACTIONS 0x00200000 #define FILE_SUPPORTS_HARD_LINKS 0x00400000 #define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000 #define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000 #define FILE_SUPPORTS_USN_JOURNAL 0x02000000 #define FILE_SUPPORTS_INTEGRITY_STREAMS 0x04000000 #define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 #define FILE_SUPPORTS_SPARSE_VDL 0x10000000 #define FILE_DAX_VOLUME 0x20000000 #define FILE_SUPPORTS_GHOSTING 0x40000000 #define FILE_INVALID_FILE_ID ((LONGLONG)-1LL) // // These are the generic rights. // #define GENERIC_READ (0x80000000L) #define GENERIC_WRITE (0x40000000L) #define GENERIC_EXECUTE (0x20000000L) #define GENERIC_ALL (0x10000000L) #define FILE_BEGIN 0 #define FILE_CURRENT 1 #define FILE_END 2 // // Constants // #define CREATE_NEW 1 #define CREATE_ALWAYS 2 #define OPEN_EXISTING 3 #define OPEN_ALWAYS 4 #define TRUNCATE_EXISTING 5 #define INVALID_FILE_SIZE ((DWORD)0xFFFFFFFF) #define INVALID_SET_FILE_POINTER ((DWORD)-1) #define INVALID_FILE_ATTRIBUTES ((DWORD)-1) // // Constants // #define INVALID_HANDLE_VALUE ((void*)(i64*)-1) extern void* CreateFileA(const i8* lpFileName, u32 dwDesiredAccess, u32 dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, u32 dwCreationDisposition, u32 dwFlagsAndAttributes, void* hTemplateFile); extern u32 SetFilePointer(void* hFile, i32 lDistanceToMove, i32* lpDistanceToMoveHigh, u32 dwMoveMethod); extern i32 ReadFile(void* hFile, void* lpBuffer, u32 nNumberOfBytesToRead, u32* lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped); extern i32 CloseHandle(void* hObject); // ======================== // ====== !windows.h ====== // ======================== enum STREAM_INDEX { OLD_DIRECTORY, PDB, TPI, DBI, IPI, }; extern u8* string_stack_base; extern u8* string_stack_current; void open_pdb(const i8* pdb_path, struct module* module) { void* pdb_file = CreateFileA(pdb_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (pdb_file == INVALID_HANDLE_VALUE) { print("Cannot open pdb file at path %s due to error %u\n", pdb_path, GetLastError()); return; } // MSF superblock struct msf_superblock { u8 magic[32U]; u32 block_size; u32 free_block_map_block; u32 num_blocks; u32 num_directory_bytes; u32 unknown; u32 block_map_addr; }; struct msf_superblock superblock = { 0U }; u8 block[4096U] = { 0U }; u32 b_read = 0U; ReadFile(pdb_file, (void*)&superblock, sizeof(superblock), &b_read, NULL); // Stream directory u32 dir_addr = (superblock.block_map_addr - 1U) * sizeof(block); SetFilePointer(pdb_file, dir_addr, NULL, FILE_BEGIN); ReadFile(pdb_file, (void*)block, superblock.num_directory_bytes, &b_read, NULL); // Loog through streams u32 num_streams = ((u32*)block)[0U]; u32* stream_sizes = &((u32*)block)[1U]; u32* stream_blocks = stream_sizes + num_streams; u32* stream_blocks_indices[1024U] = { 0U }; u32 block_index = 0U; for(u32 stream_id = 0u; stream_id < num_streams; stream_id++) { print("PDB stream size : %u\n", stream_sizes[stream_id]); if (stream_sizes[stream_id] == (u32)-1) { print("PDB stream blocks : NONE\n"); continue; } if (stream_sizes[stream_id] == 0U) { continue; } stream_blocks_indices[stream_id] = &stream_blocks[block_index]; for(u32 id = 0u; id < (stream_sizes[stream_id] / sizeof(block)) + 1U; id++) { print("PDB stream blocks : %u\n", stream_blocks[block_index]); block_index++; } } // PDB Info Stream struct pdb_info_stream { u32 version; u32 signature; u32 age; u32 guid[4u]; }; struct pdb_info_stream pdb_info_stream = { 0U }; SetFilePointer(pdb_file, stream_blocks_indices[PDB][0U] * sizeof(block), NULL, FILE_BEGIN); ReadFile(pdb_file, (void*)&pdb_info_stream, sizeof(pdb_info_stream), &b_read, NULL); // DBI Stream struct DBI_stream_header { i32 version_signature; u32 version_header; u32 age; u16 global_stream_index; u16 build_number; u16 public_stream_index; u16 pdb_dll_version; u16 sym_record_stream; u16 pdb_dll_rbld; i32 mod_info_size; i32 section_contribution_size; i32 section_map_size; i32 source_info_size; i32 type_server_map_size; u32 mfc_type_server_index; i32 optional_dbg_header_size; i32 ec_substream_size; u16 flags; u16 machine; u32 padding; }; struct mod_info { u32 Unused1; struct SectionContribEntry { u16 Section; u8 Padding1[2]; i32 Offset; i32 Size; u32 Characteristics; u16 ModuleIndex; u8 Padding2[2]; u32 DataCrc; u32 RelocCrc; } SectionContr; u16 Flags; u16 ModuleSymStream; u32 SymByteSize; u32 C11ByteSize; u32 C13ByteSize; u16 SourceFileCount; u8 Padding[2]; u32 Unused2; u32 SourceFileNameIndex; u32 PdbFilePathNameIndex; } *modules_info[64U]; u8* modules_name[64U] = { 0U }; u8* obj_files_name[64U] = { 0U }; u8 DBI_block[4096U] = { 0U }; SetFilePointer(pdb_file, stream_blocks_indices[DBI][0U] * sizeof(block), NULL, FILE_BEGIN); ReadFile(pdb_file, (void*)&DBI_block, sizeof(DBI_block), &b_read, NULL); struct DBI_stream_header* DBI_stream_header = (struct DBI_stream_header*)DBI_block; // Read Module Info Substreams i32 byte_index = sizeof(*DBI_stream_header); for (u32 module_index = 0U; byte_index < DBI_stream_header->mod_info_size; module_index++) { modules_info[module_index] = (struct mod_info*)&DBI_block[byte_index]; modules_name[module_index] = &DBI_block[byte_index + sizeof(struct mod_info)]; print("Module symbol stream index %x\n", modules_info[module_index]->ModuleSymStream); print("Module name : %s\n", modules_name[module_index]); size_t module_name_length = 0U; module_name_length = str_len((const char*)modules_name[module_index], 256U); obj_files_name[module_index] = &DBI_block[byte_index + sizeof(struct mod_info) + module_name_length + 1U]; print("Obj file name : %s\n", obj_files_name[module_index]); size_t obj_file_name_length = str_len((const char*)obj_files_name[module_index], 256U); u32 module_info_size = sizeof(*modules_info[0U]) + (i32)module_name_length + 1 + (i32)obj_file_name_length + 1; module_info_size = ((module_info_size + 3U) / 4U) * 4U; byte_index += module_info_size; // Read curren Module Info Stream u8 module_info_block[4096U] = { 0U }; SetFilePointer(pdb_file, stream_blocks_indices[modules_info[module_index]->ModuleSymStream][0U] * sizeof(module_info_block), NULL, FILE_BEGIN); ReadFile(pdb_file, (void*)&module_info_block, sizeof(module_info_block), &b_read, NULL); u8* symbols = &module_info_block[4U]; u8* cur_sym = symbols; while ((u32)(cur_sym - symbols) < modules_info[module_index]->SymByteSize - 4U) { struct SYMTYPE* rec = (struct SYMTYPE*)cur_sym; if (rec->rectyp == S_END) { print("END SYMBOL\n"); } else if (rec->rectyp == S_FRAMEPROC) { struct FRAMEPROCSYM* frameproc = (struct FRAMEPROCSYM*)cur_sym; frameproc; print("FRAMEPROC SYMBOL\n"); } else if (rec->rectyp == S_OBJNAME) { struct OBJNAMESYM* objname = (struct OBJNAMESYM*)cur_sym; print("OBJNAME SYMBOL %s\n", objname->name); } else if (rec->rectyp == S_THUNK32) { struct THUNKSYM32* thunk = (struct THUNKSYM32*)cur_sym; print("THUNK SYMBOL %s\n", thunk->name); } else if (rec->rectyp == S_PUB32) { struct PUBSYM32* pub = (struct PUBSYM32*)cur_sym; print("PUBLIC SYMBOL %s\n", pub->name); } else if (rec->rectyp == S_GPROC32) { struct PROCSYM32* proc = (struct PROCSYM32*)cur_sym; print("PROC32 SYMBOL %s\n", proc->name); // TODO: Add string management code assert(string_stack_current - 16U * 4096U < string_stack_base); size_t length = str_len((const char*)proc->name, 256U); str_cat((char*)string_stack_current, (const char*)proc->name, length); string_stack_current[length] = '\0'; u32 start_address = proc->off + module->nt_header.OptionalHeader.BaseOfCode; #ifdef HASHMAP insert_elem(&module->symbols, start_address, proc->len + module->nt_header.OptionalHeader.BaseOfCode, (u64)string_stack_current); #else for (u32 index = 0U; index < module->functions_count; index++) { if (module->functions_start[index] == start_address) { module->functions_name[index] = string_stack_current; break; } } #endif string_stack_current += length + 1U; } else if (rec->rectyp == S_REGREL32) { struct REGREL32* regrel = (struct REGREL32*)cur_sym; print("REGREL32 SYMBOL %s\n", regrel->name); } else if (rec->rectyp == S_COMPILE2) { struct COMPILESYM* compile = (struct COMPILESYM*)cur_sym; compile; print("COMPILE2 SYMBOL\n"); } else if (rec->rectyp == S_SECTION) { struct SECTIONSYM* section = (struct SECTIONSYM*)cur_sym; print("SECTION SYMBOL index %u, name %s, rva %u, size %u\n", section->isec, section->name, section->rva, section->cb); } else if (rec->rectyp == S_COFFGROUP) { struct COFFGROUPSYM* coff = (struct COFFGROUPSYM*)cur_sym; print("COFFGROUP SYMBOL %s\n", coff->name); } else if (rec->rectyp == S_CALLSITEINFO) { struct CALLSITEINFO* callsiteinfo = (struct CALLSITEINFO*)cur_sym; callsiteinfo; print("CALLSITEINFO SYMBOL\n"); } else if (rec->rectyp == S_COMPILE3) { struct COMPILESYM3* compile = (struct COMPILESYM3*)cur_sym; print("COMPILE3 SYMBOL %s\n", compile->verSz); } else if (rec->rectyp == S_BUILDINFO) { struct BUILDINFOSYM* buildinfo = (struct BUILDINFOSYM*)cur_sym; buildinfo; print("BUILDINFO SYMBOL\n"); } else { print("Unknown symbol kind %x\n", rec->rectyp); } cur_sym = (u8*)NextSym((SYMTYPE*)cur_sym); } } // Global Symbol Stream SetFilePointer(pdb_file, stream_blocks_indices[DBI_stream_header->global_stream_index][0U] * sizeof(block), NULL, FILE_BEGIN); ReadFile(pdb_file, (void*)&block, sizeof(DBI_stream_header), &b_read, NULL); // Public Symbol Stream SetFilePointer(pdb_file, stream_blocks_indices[DBI_stream_header->public_stream_index][0U] * sizeof(block), NULL, FILE_BEGIN); ReadFile(pdb_file, (void*)&block, sizeof(block), &b_read, NULL); CloseHandle(pdb_file); }