Skip to content

Commit cc3297c

Browse files
committed
idaloader: prompt to load PDB, pass codeview PDB info over to IDA
"pdb incorrect or invalid" message should be fixed if you're using matching PDB
1 parent a4dfd94 commit cc3297c

File tree

5 files changed

+144
-4
lines changed

5 files changed

+144
-4
lines changed

idaloader.cpp

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "xex_headerids.hpp"
1313
#include <typeinf.hpp>
1414
#include <bytes.hpp>
15+
#include <filesystem>
1516

1617
netnode ignore_micro;
1718

@@ -48,7 +49,7 @@ void label_regsaveloads(ea_t start, ea_t end)
4849
savef_pattern,
4950
loadf_pattern
5051
};
51-
char* pattern_labels[] = {
52+
const char* pattern_labels[] = {
5253
"__savegprlr_%d",
5354
"__restgprlr_%d",
5455
"__savefpr_%d",
@@ -158,7 +159,7 @@ void pe_add_sections(XEXFile& file)
158159
bool has_code = (section.Characteristics & IMAGE_SCN_CNT_CODE);
159160
bool has_data = (section.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) || (section.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
160161

161-
char* seg_class = has_code ? "CODE" : "DATA";
162+
const char* seg_class = has_code ? "CODE" : "DATA";
162163
ea_t seg_addr = (ea_t)file.base_address() + (ea_t)section.VirtualAddress;
163164

164165
segment_t segm;
@@ -224,8 +225,9 @@ void pe_add_sections(XEXFile& file)
224225
offset += 8;
225226
}
226227

227-
// display messagebox prompt to user, since pdata labelling can take a little while
228+
msg("Parsing .pdata and creating %lld functions...\n", funcs.size());
228229

230+
// display messagebox prompt to user, since pdata labelling can take a little while
229231
// ida printf formatter tends to crash, too bad, use sprintf to rewrite just the number portion
230232
char msg_text[256] = "Marking functions from .pdata... (";
231233
int num_pos = strlen(msg_text);
@@ -244,7 +246,7 @@ void pe_add_sections(XEXFile& file)
244246
break;
245247

246248
// update every few funcs
247-
if (++num % 10 == 0)
249+
if (++num % 50 == 0)
248250
{
249251
sprintf_s(msg_text_write, 256 - num_pos, "%lld/%lld)", num, funcs.size());
250252
replace_wait_box(msg_text);
@@ -255,6 +257,56 @@ void pe_add_sections(XEXFile& file)
255257
}
256258
}
257259

260+
#define PE_NODE "$ PE header" // netnode name for PE header
261+
// value() -> peheader_t
262+
// altval(segnum) -> s->start_ea
263+
#define PE_ALT_DBG_FPOS nodeidx_t(-1) // altval() -> translated fpos of debuginfo
264+
#define PE_ALT_IMAGEBASE nodeidx_t(-2) // altval() -> loading address (usually pe.imagebase)
265+
#define PE_ALT_PEHDR_OFF nodeidx_t(-3) // altval() -> offset of PE header
266+
#define PE_ALT_NEFLAGS nodeidx_t(-4) // altval() -> neflags
267+
#define PE_ALT_TDS_LOADED nodeidx_t(-5) // altval() -> tds already loaded(1) or invalid(-1)
268+
#define PE_ALT_PSXDLL nodeidx_t(-6) // altval() -> if POSIX(x86) imports from PSXDLL netnode
269+
#define PE_ALT_OVRVA nodeidx_t(-7) // altval() -> overlay rva (if present)
270+
#define PE_ALT_OVRSZ nodeidx_t(-8) // altval() -> overlay size (if present)
271+
#define PE_SUPSTR_PDBNM nodeidx_t(-9) // supstr() -> pdb file name
272+
// supval(segnum) -> pesection_t
273+
// blob(0, PE_NODE_RELOC) -> relocation info
274+
// blob(0, RSDS_TAG) -> rsds_t structure
275+
// blob(0, NB10_TAG) -> cv_info_pdb20_t structure
276+
#define PE_ALT_NTAPI nodeidx_t(-10) // altval() -> uses Native API
277+
#define PE_EMBED_PDB_OFF nodeidx_t(-11) // altval() -> offset of embedded PDB file
278+
#define PE_NODE_RELOC 'r'
279+
#define RSDS_TAG 's'
280+
#define NB10_TAG 'n'
281+
#define UTDS_TAG 't'
282+
283+
void pe_setup_netnode(XEXFile& file)
284+
{
285+
netnode penode;
286+
penode.create(PE_NODE);
287+
288+
penode.altset(PE_ALT_IMAGEBASE, file.base_address());
289+
290+
size_t cv_length = 0;
291+
auto* cv_data = file.codeview_data(0, &cv_length);
292+
if (cv_data)
293+
{
294+
// Set PDB filename to whatever cv_data[0] says
295+
// (only use filename instead of full path, else it may fail to load it)
296+
char* pdb_path_ptr = (char*)(cv_data + sizeof(CV_INFO_PDB70));
297+
std::filesystem::path pdb_path = pdb_path_ptr;
298+
penode.supset(PE_SUPSTR_PDBNM, pdb_path.filename().string().c_str());
299+
300+
// Copy cv_data into RSDS tag
301+
penode.setblob(cv_data, cv_length, 0, RSDS_TAG);
302+
303+
// Prompt for PDB load
304+
msg("Prompting for PDB load...\n(full X360 type loading may require pdb.cfg PDB_PROVIDER = PDB_PROVIDER_MSDIA !)\n");
305+
auto* plugin = find_plugin("pdb", 1LL);
306+
run_plugin(plugin, 1LL);
307+
}
308+
}
309+
258310
bool load_application(linput_t* li)
259311
{
260312
qlseek(li, 0);
@@ -264,6 +316,7 @@ bool load_application(linput_t* li)
264316
if (!file.load(li))
265317
return false;
266318

319+
inf_set_filetype(f_PE);
267320
inf_set_baseaddr(file.base_address() >> 4);
268321
set_imagebase(file.base_address());
269322

@@ -431,6 +484,8 @@ bool load_application(linput_t* li)
431484
}
432485
}
433486

487+
pe_setup_netnode(file);
488+
434489
return true;
435490
}
436491

idaxex.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
<ClCompile>
7878
<AdditionalIncludeDirectories>..\..\include;3rdparty/excrypt;3rdparty/mspack%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
7979
<PreprocessorDefinitions>__NT__;IDALDR;__EA64__;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
80+
<LanguageStandard>stdcpp20</LanguageStandard>
8081
</ClCompile>
8182
<Link>
8283
<AdditionalOptions>/EXPORT:LDSC %(AdditionalOptions)</AdditionalOptions>
@@ -98,6 +99,7 @@
9899
<ClCompile>
99100
<AdditionalIncludeDirectories>..\..\include;3rdparty/excrypt;3rdparty/mspack%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
100101
<PreprocessorDefinitions>__NT__;IDALDR;__EA64__;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
102+
<LanguageStandard>stdcpp20</LanguageStandard>
101103
</ClCompile>
102104
<Link>
103105
<AdditionalOptions>/EXPORT:LDSC %(AdditionalOptions)</AdditionalOptions>

pe_structs.hpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,26 @@ typedef struct _IMAGE_EXPORT_DIRECTORY
162162
uint32_t AddressOfNames;
163163
uint32_t AddressOfNameOrdinals;
164164
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
165+
166+
typedef struct _IMAGE_DEBUG_DIRECTORY {
167+
uint32_t Characteristics;
168+
uint32_t TimeDateStamp;
169+
uint16_t MajorVersion;
170+
uint16_t MinorVersion;
171+
uint32_t Type;
172+
uint32_t SizeOfData;
173+
uint32_t AddressOfRawData;
174+
uint32_t PointerToRawData;
175+
} IMAGE_DEBUG_DIRECTORY, * PIMAGE_DEBUG_DIRECTORY;
176+
static_assert(sizeof(IMAGE_DEBUG_DIRECTORY) == 0x1C, "IMAGE_DEBUG_DIRECTORY");
177+
178+
#define CV_INFO_RSDS_SIGNATURE 0x53445352
179+
struct CV_INFO_PDB70
180+
{
181+
uint32_t CvSignature;
182+
uint8_t Signature[0x10];
183+
uint32_t Age;
184+
// followed by filename
185+
//BYTE PdbFileName[];
186+
};
187+
static_assert(sizeof(CV_INFO_PDB70) == 0x18, "CV_INFO_PDB70");

xex.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,54 @@ bool XEXFile::pe_load(const uint8_t* data)
11541154
pe_load_exports(data);
11551155
}
11561156

1157+
// Read in debug directory if exists (and valid)
1158+
auto& debug_directory = nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
1159+
if (debug_directory.Size)
1160+
{
1161+
int num_directories = debug_directory.Size / sizeof(IMAGE_DEBUG_DIRECTORY);
1162+
1163+
if (pe_data_.size() > debug_directory.VirtualAddress)
1164+
{
1165+
IMAGE_DEBUG_DIRECTORY* dir_ptr = (IMAGE_DEBUG_DIRECTORY*)(data + debug_directory.VirtualAddress);
1166+
for (int i = 0; i < num_directories; i++)
1167+
{
1168+
auto dir = *dir_ptr;
1169+
dir_ptr++;
1170+
1171+
debug_directories_.push_back(dir);
1172+
1173+
if (dir.Type != 2 /* CODEVIEW */ || dir.SizeOfData == 0)
1174+
continue;
1175+
1176+
// Both AddressOfRawData & PointerToRawData usually seem to point to same offset, but not sure if that's always the case
1177+
// Check if either of them point to valid CV_INFO
1178+
CV_INFO_PDB70* cv_ptr = nullptr;
1179+
for(auto& addr : { dir.AddressOfRawData, dir.PointerToRawData })
1180+
{
1181+
if (addr > 0 && pe_data_.size() > (addr + dir.SizeOfData))
1182+
{
1183+
auto* test_ptr = (CV_INFO_PDB70*)(data + addr);
1184+
if (test_ptr->CvSignature == CV_INFO_RSDS_SIGNATURE)
1185+
{
1186+
cv_ptr = test_ptr;
1187+
break;
1188+
}
1189+
}
1190+
}
1191+
1192+
// Bail out if neither are valid
1193+
if (!cv_ptr)
1194+
continue;
1195+
1196+
std::vector<uint8_t> data;
1197+
data.resize(dir.SizeOfData);
1198+
std::copy_n((uint8_t*)cv_ptr, dir.SizeOfData, data.data());
1199+
1200+
codeview_data_.push_back(data);
1201+
}
1202+
}
1203+
}
1204+
11571205
return true;
11581206
}
11591207

xex.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ class XEXFile
100100

101101
// Sections from PE headers (includes XEX sections above)
102102
std::vector<IMAGE_SECTION_HEADER> sections_;
103+
std::vector<IMAGE_DEBUG_DIRECTORY> debug_directories_;
104+
std::vector<std::vector<uint8_t>> codeview_data_;
103105

104106
int load_error_ = 0;
105107

@@ -208,6 +210,16 @@ class XEXFile
208210
const xex_opt::XexVitalStats* vital_stats() { return vital_stats_; }
209211
const xex_opt::XexFileDataDescriptor* data_descriptor() { return data_descriptor_; }
210212

213+
const uint8_t* codeview_data(int idx, size_t* size = nullptr) {
214+
if (codeview_data_.size() > idx)
215+
{
216+
if (size)
217+
*size = codeview_data_[idx].size();
218+
return codeview_data_[idx].data();
219+
}
220+
return nullptr;
221+
}
222+
211223
uint32_t min_kernel_version() {
212224
switch (xex_header_.Magic) {
213225
case MAGIC_XEX0: return 1332;

0 commit comments

Comments
 (0)