Files
sigmaker-ida/idasdk76/plugins/pdb/msdia.cpp
2021-10-31 21:20:46 +02:00

1151 lines
32 KiB
C++

// This file is included from 4 places:
// - efd/pdb.cpp efd: to dump pdb contents
// - base/pdb2til.cpp tilib: to convert pdb to til
// - plugins/pdb/pdb.cpp ida: read pdb info and populate idb
// - dbg/win32_server/tilfuncs.cpp win32_server: read pdb info and send it to ida
//
// The following symbols may be defined:
// PDB_PLUGIN pdb
// PDB_WIN32_SERVER win32_server
#include <diskio.hpp>
#include "msdia.hpp"
#include "../../ldr/pe/pe.h"
#include "pdblocal.cpp"
//lint -esym(843, g_diadlls, g_pdb_errors, PathIsUNC) could be declared as const
int pdb_session_t::session_count = 0;
bool pdb_session_t::co_initialized = false;
typedef BOOL (__stdcall *PathIsUNC_t)(LPCTSTR pszPath);
static PathIsUNC_t PathIsUNC = NULL;
static bool check_for_odd_paths(const char *fname);
//---------------------------------------------------------------------------
class msdia_reader_t
{
public:
virtual ~msdia_reader_t() {}
virtual bool read(uint64 offset, void *buf, uint32 count, uint32 *nread) = 0;
virtual bool setup(void) { return true; }
};
//---------------------------------------------------------------------------
class local_exe_msdia_reader_t : public msdia_reader_t
{
LPCWSTR FileName;
HANDLE hFile;
public:
local_exe_msdia_reader_t(LPCWSTR _FileName)
{
FileName = _FileName;
hFile = INVALID_HANDLE_VALUE;
}
~local_exe_msdia_reader_t(void)
{
if ( hFile != INVALID_HANDLE_VALUE )
CloseHandle(hFile);
}
virtual bool setup(void) override
{
hFile = CreateFileW(
FileName,
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
return hFile != INVALID_HANDLE_VALUE;
}
virtual bool read(uint64 offset, void *buf, uint32 count, uint32 *nread) override
{
if ( hFile == INVALID_HANDLE_VALUE )
return false;
LARGE_INTEGER pos;
pos.QuadPart = (LONGLONG) offset;
if ( SetFilePointerEx(hFile, pos, NULL, FILE_BEGIN) == 0 )
return false;
if ( ReadFile(hFile, buf, count, (DWORD *) nread, NULL) == 0 )
return false;
return true;
}
};
#ifdef PDB_PLUGIN
//---------------------------------------------------------------------------
class local_mem_msdia_reader_t : public msdia_reader_t
{
public:
virtual bool read(uint64 offset, void *buf, uint32 count, uint32 *nread) override
{
if ( get_bytes(buf, count, offset) != count )
return false;
*nread = count;
return true;
}
};
#elif defined(PDB_WIN32_SERVER)
//---------------------------------------------------------------------------
class win32_msdia_reader_t : public msdia_reader_t
{
pdb_remote_session_t *pdb_rsess;
pdb_rr_kind_t kind;
public:
win32_msdia_reader_t(void *_pdb_rsess, pdb_rr_kind_t _kind)
{
pdb_rsess = (pdb_remote_session_t *) _pdb_rsess;
kind = _kind;
}
virtual bool read(uint64 offset, void *buf, uint32 count, uint32 *nread) override
{
return pdb_rsess->client_read_request.request_read(kind, offset, count, buf, nread);
}
};
#endif
//----------------------------------------------------------------------
// Common code for PDB handling
//----------------------------------------------------------------------
class CCallback : public IDiaLoadCallback2,
public IDiaReadExeAtRVACallback,
public IDiaReadExeAtOffsetCallback
{
unsigned int m_nRefCount;
ea_t m_load_address;
msdia_reader_t *msdia_reader;
pdb_session_t *pdb_session;
DWORDLONG last_cv_off;
ea_t last_cv_rva;
public:
CCallback(pdb_session_t *_pdb_session,
msdia_reader_t *_msdia_reader,
ea_t _load_address)
: msdia_reader(_msdia_reader),
m_load_address(_load_address),
// Note: we initialize the reference count to 1 since the only
// instance of this object is created in the stack, and
// the destructor will take care of the cleanup.
m_nRefCount(1),
pdb_session(_pdb_session),
last_cv_off(0),
last_cv_rva(BADADDR)
{
}
// IUnknown
ULONG STDMETHODCALLTYPE AddRef()
{
return InterlockedIncrement(&m_nRefCount);
}
ULONG STDMETHODCALLTYPE Release()
{
// Note: we don't check the reference count and delete the object
// (see comment for the m_nRefCount field).
return InterlockedDecrement(&m_nRefCount);
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID rid, void **ppUnk)
{
if ( ppUnk == NULL )
return E_INVALIDARG;
*ppUnk = NULL;
if ( rid == __uuidof(IDiaLoadCallback2) || rid == __uuidof(IDiaLoadCallback) )
{
*ppUnk = (IDiaLoadCallback2 *)this;
}
else if ( rid == __uuidof(IDiaReadExeAtRVACallback) )
{
// we may use only one of IDiaReadExeAtRVACallback and IDiaReadExeAtOffsetCallback
// claiming that both are supported will lead to crashes in MSDIA
if ( m_load_address != BADADDR )
*ppUnk = (IDiaReadExeAtRVACallback *)this;
}
else if ( rid == __uuidof(IDiaReadExeAtOffsetCallback) )
{
// see the comment above about IDiaReadExeAtRVACallback
if ( m_load_address == BADADDR )
*ppUnk = (IDiaReadExeAtOffsetCallback *)this;
}
else if ( rid == __uuidof(IUnknown) )
{
*ppUnk = (IUnknown *)(IDiaLoadCallback *)this;
}
if ( *ppUnk == NULL )
return E_NOINTERFACE;
AddRef();
return S_OK;
}
HRESULT STDMETHODCALLTYPE NotifyDebugDir(
BOOL fExecutable,
DWORD cbData,
BYTE data[])
{
// msdia90.dll can crash on bogus CV data
// so we remember the offset here and check it in ReadFileAt
if ( fExecutable && cbData >= sizeof(debug_entry_t) )
{
debug_entry_t &de = *(debug_entry_t *)data;
if ( de.type == DBG_CV )
{
last_cv_off = de.seek;
last_cv_rva = de.rva;
}
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE NotifyOpenDBG(
LPCOLESTR dbgPath,
HRESULT resultCode)
{
if ( resultCode == S_OK )
deb(IDA_DEBUG_DEBUGGER, "MSDIA: dbg file \"%S\" matched\n", dbgPath);
else
deb(IDA_DEBUG_DEBUGGER, "MSDIA: \"%S\": %s\n", dbgPath, pdberr(resultCode));
return S_OK;
}
HRESULT STDMETHODCALLTYPE NotifyOpenPDB(
LPCOLESTR pdbPath,
HRESULT resultCode)
{
if ( resultCode == S_OK )
deb(IDA_DEBUG_DEBUGGER, "MSDIA: pdb file \"%S\" matched\n", pdbPath);
else
deb(IDA_DEBUG_DEBUGGER, "MSDIA: \"%S\": %s\n", pdbPath, pdberr(resultCode));
#ifdef _DEBUG
qstring spath;
utf16_utf8(&spath, pdbPath);
pdb_session->_pdb_path = spath;
#endif
return S_OK;
}
HRESULT STDMETHODCALLTYPE RestrictRegistryAccess()
{
// return hr != S_OK to prevent querying the registry for symbol search paths
return S_OK;
}
HRESULT STDMETHODCALLTYPE RestrictSymbolServerAccess()
{
// return hr != S_OK to prevent accessing a symbol server
return S_OK;
}
HRESULT STDMETHODCALLTYPE RestrictOriginalPathAccess()
{
// return hr != S_OK to prevent querying the registry for symbol search paths
return S_OK;
}
HRESULT STDMETHODCALLTYPE RestrictReferencePathAccess()
{
// return hr != S_OK to prevent accessing a symbol server
return S_OK;
}
HRESULT STDMETHODCALLTYPE RestrictDBGAccess()
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE RestrictSystemRootAccess()
{
return S_OK;
}
bool check_codeview_data(BYTE pbData[], DWORD cbData)
{
bool ok = true;
if ( cbData > 4 )
{
// check that the data has a valid NB or RSDS signature and PDB path doesn't look suspicious
ok = false;
if ( pbData[0] == 'N' && pbData[1] == 'B' && cbData >= sizeof(cv_info_pdb20_t) )
{
char *pdbname = (char*)pbData + sizeof(cv_info_pdb20_t);
pbData[cbData-1] = '\0';
ok = check_for_odd_paths(pdbname);
}
else if ( memcmp(pbData, "RSDS", 4) == 0 && cbData >= sizeof(rsds_t) )
{
char *pdbname = (char*)pbData + sizeof(rsds_t);
pbData[cbData-1] = '\0';
ok = check_for_odd_paths(pdbname);
}
}
return ok;
}
// IDiaReadExeAtRVACallback
HRESULT STDMETHODCALLTYPE ReadExecutableAtRVA(
DWORD relativeVirtualAddress,
DWORD cbData,
DWORD *pcbData,
BYTE data[])
{
ea_t ea = m_load_address + relativeVirtualAddress;
if ( !msdia_reader->read(ea, data, cbData, (uint32 *) pcbData) )
return E_FAIL;
// are we reading the CV debug directory entry?
if ( relativeVirtualAddress == last_cv_rva )
return check_codeview_data(data, cbData) ? S_OK : E_FAIL;
return S_OK;
}
// IDiaReadExeAtOffsetCallback
HRESULT STDMETHODCALLTYPE ReadExecutableAt(
DWORDLONG fileOffset,
DWORD cbData,
DWORD *pcbData,
BYTE data[])
{
if ( !msdia_reader->read(fileOffset, data, cbData, (uint32 *) pcbData) )
return E_FAIL;
// are we reading the CV debug directory entry?
if ( fileOffset != 0 && last_cv_off == fileOffset )
return check_codeview_data(data, cbData) ? S_OK : E_FAIL;
return S_OK;
}
};
//---------------------------------------------------------------------------
template<class T> void print_generic(T t)
{
IDiaPropertyStorage *pPropertyStorage;
HRESULT hr = t->QueryInterface(__uuidof(IDiaPropertyStorage), (void **)&pPropertyStorage);
if ( hr == S_OK )
{
print_property_storage(pPropertyStorage);
pPropertyStorage->Release();
}
}
//---------------------------------------------------------------------------
static const char *const g_pdb_errors[] =
{
"Operation successful (E_PDB_OK)",
"(E_PDB_USAGE)",
"Out of memory (E_PDB_OUT_OF_MEMORY)",
"(E_PDB_FILE_SYSTEM)",
"Failed to open the file, or the file has an invalid format (E_PDB_NOT_FOUND)",
"Signature does not match (E_PDB_INVALID_SIG)",
"Age does not match (E_PDB_INVALID_AGE)",
"(E_PDB_PRECOMP_REQUIRED)",
"(E_PDB_OUT_OF_TI)",
"(E_PDB_NOT_IMPLEMENTED)",
"(E_PDB_V1_PDB)",
"Attempted to access a file with an obsolete format (E_PDB_FORMAT)",
"(E_PDB_LIMIT)",
"(E_PDB_CORRUPT)",
"(E_PDB_TI16)",
"(E_PDB_ACCESS_DENIED)",
"(E_PDB_ILLEGAL_TYPE_EDIT)",
"(E_PDB_INVALID_EXECUTABLE)",
"(E_PDB_DBG_NOT_FOUND)",
"(E_PDB_NO_DEBUG_INFO)",
"(E_PDB_INVALID_EXE_TIMESTAMP)",
"(E_PDB_RESERVED)",
"(E_PDB_DEBUG_INFO_NOT_IN_PDB)",
"(E_PDB_SYMSRV_BAD_CACHE_PATH)",
"(E_PDB_SYMSRV_CACHE_FULL)",
};
//---------------------------------------------------------------------------
inline void pdberr_suggest_vs_runtime(HRESULT hr)
{
if ( hr == E_NOINTERFACE )
{
msg("<< It appears that MS DIA SDK is not installed.\n");
#ifdef __X86__
msg("Please try installing \"Microsoft Visual C++ 2008 Redistributable Package / x86\" >>\n");
#else
msg("Please try installing \"Microsoft Visual C++ 2008 Redistributable Package / x64\" >>\n");
#endif
}
}
//---------------------------------------------------------------------------
const char *pdberr(int code)
{
switch ( code )
{ // tab in first pos is flag for replace warning to msg
case E_INVALIDARG: return "Invalid parameter.";
case E_UNEXPECTED: return "Data source has already been prepared.";
default:
if ( code >= E_PDB_OK && (code - E_PDB_OK) < qnumber(g_pdb_errors) )
return g_pdb_errors[code - E_PDB_OK];
}
return winerr(code);
}
//----------------------------------------------------------------------
class DECLSPEC_UUID("4C41678E-887B-4365-A09E-925D28DB33C2") DiaSource90;
class DECLSPEC_UUID("1fbd5ec4-b8e4-4d94-9efe-7ccaf9132c98") DiaSource80;
class DECLSPEC_UUID("31495af6-0897-4f1e-8dac-1447f10174a1") DiaSource71;
static const GUID *const g_d90 = &__uuidof(DiaSource90); // msdia90.dll
static const GUID *const g_d80 = &__uuidof(DiaSource80); // msdia80.dll
static const GUID *const g_d71 = &__uuidof(DiaSource71); // msdia71.dll
static const GUID *const g_msdiav[] = { g_d90, g_d80, g_d71 };
static const int g_diaver[] = { 900, 800, 710 };
static const char *const g_diadlls[] = { "msdia90.dll", "msdia80.dll", "msdia71.dll" };
//----------------------------------------------------------------------
HRESULT __stdcall CoCreateInstanceNoReg(
LPCTSTR szDllName,
IN REFCLSID rclsid,
IUnknown *pUnkOuter,
IN REFIID riid,
OUT LPVOID FAR *ppv,
OUT HMODULE *phMod)
{
// http://lallousx86.wordpress.com/2007/01/29/emulating-cocreateinstance/
HRESULT hr = REGDB_E_CLASSNOTREG;
HMODULE hDll;
do
{
hDll = LoadLibrary(szDllName);
if ( hDll == NULL )
break;
HRESULT (__stdcall *GetClassObject)(REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv);
*(FARPROC*)&GetClassObject = GetProcAddress(hDll, "DllGetClassObject");
if ( GetClassObject == NULL )
break;
IClassFactory *pIFactory;
hr = GetClassObject(rclsid, IID_IClassFactory, (LPVOID *)&pIFactory);
if ( FAILED(hr) )
break;
hr = pIFactory->CreateInstance(pUnkOuter, riid, ppv);
pIFactory->Release();
}
while ( false );
if ( FAILED(hr) && hDll != NULL )
FreeLibrary(hDll);
else
*phMod = hDll;
return hr;
}
//----------------------------------------------------------------------------
// Note: This will return the machine type, as it is known
// by the IDB, which might not be what you think. For example,
// if you need to tell x86 and x64 apart, you're out of luck.
// You may want to consider looking at pdbaccess_t's
// get_machine_type().
static DWORD get_machine_type(DWORD dwMachType)
{
DWORD machine;
switch ( dwMachType )
{
default:
machine = CV_CFL_80386;
break;
case IMAGE_FILE_MACHINE_IA64:
machine = CV_CFL_IA64;
break;
case IMAGE_FILE_MACHINE_AMD64:
machine = CV_CFL_AMD64;
break;
case IMAGE_FILE_MACHINE_THUMB:
case IMAGE_FILE_MACHINE_ARM:
machine = CV_CFL_ARM6;
break;
case PECPU_ARMV7:
machine = CV_CFL_ARM7;
break;
case PECPU_PPC:
machine = CV_CFL_PPC620;
break;
case PECPU_PPCFP:
machine = CV_CFL_PPCFP;
break;
case PECPU_PPCBE:
machine = CV_CFL_PPCBE;
break;
}
return machine;
}
//----------------------------------------------------------------------
pdb_session_t::~pdb_session_t()
{
if ( --session_count == 0 && co_initialized )
{
CoUninitialize();
co_initialized = false;
}
}
//----------------------------------------------------------------------
void pdb_session_t::close()
{
if ( pdb_access != NULL )
{
delete pdb_access;
pdb_access = NULL;
}
if ( dia_hmod != NULL )
{
FreeLibrary(dia_hmod);
dia_hmod = NULL;
}
#ifdef _DEBUG
if ( !_pdb_path.empty() && qfileexist(_pdb_path.begin() ) )
{
HANDLE hFile = CreateFileA(_pdb_path.begin(), GENERIC_READ, /*FILE_SHARE_READ*/ 0, 0, OPEN_EXISTING, 0, 0);
if ( hFile == INVALID_HANDLE_VALUE )
warning("Couldn't acquire probing lock to \"%s\"; file might be still locked by IDA", _pdb_path.begin());
else
CloseHandle(hFile);
}
#endif
}
//----------------------------------------------------------------------
typedef BOOL (CALLBACK *SymbolServerSetOptions_t)(UINT_PTR options, ULONG64 data);
typedef BOOL (CALLBACK *SymbolServerGetOptionData_t)(UINT_PTR option, PULONG64 pData);
#include "dbghelp.h"
// copied from dbghelp.h
#ifndef SSRVOPT_CALLBACK
#define SSRVOPT_CALLBACK 0x000001
#endif
#ifndef SSRVOPT_SETCONTEXT
#define SSRVOPT_SETCONTEXT 0x000800
#endif
#ifndef SSRVOPT_TRACE
#define SSRVOPT_TRACE 0x000400
#endif
#ifndef SSRVACTION_TRACE
#define SSRVACTION_TRACE 1
#define SSRVACTION_QUERYCANCEL 2
#define SSRVACTION_EVENT 3
#define SSRVACTION_EVENTW 4
#endif
#ifndef SSRVACTION_SIZE
#define SSRVACTION_SIZE 5
#endif
//----------------------------------------------------------------------
static void symsrv_dprint(const char *str)
{
qstring qbuf(str);
qbuf.replace("\b", ""); // remove backspaces
if ( qbuf.empty() )
return;
// strings usually already start with "SYMSRV: "
if ( strncmp(qbuf.c_str(), "SYMSRV: ", 9) != 0 )
qbuf.insert(0, "SYMSRV: ");
// strings usually already end with '\n'
if ( qbuf.last() != '\n' )
qbuf.append('\n');
deb(IDA_DEBUG_DEBUGGER, "%s", qbuf.c_str());
}
//----------------------------------------------------------------------
static BOOL CALLBACK SymbolServerCallback(
UINT_PTR action,
ULONG64 data,
ULONG64 context)
{
switch ( action )
{
case SSRVACTION_SIZE:
{
bool *wait_box_shown = (bool *) context;
if ( !*wait_box_shown )
show_wait_box("Downloading pdb...");
*wait_box_shown = true;
}
break;
case SSRVACTION_QUERYCANCEL:
{
BOOL *do_cancel = (BOOL *) data;
if ( user_cancelled() )
*do_cancel = TRUE;
}
break;
case SSRVACTION_TRACE:
symsrv_dprint((const char *)data);
break;
case SSRVACTION_EVENT:
IMAGEHLP_CBA_EVENT *pev = (IMAGEHLP_CBA_EVENT*)data;
// Event information is usually all zero.
if ( pev->severity != 0 || pev->code != 0 || pev->object != NULL )
deb(IDA_DEBUG_DEBUGGER, "SYMSRV: event severity: %d code: %d object: %p\n", pev->severity, pev->code, pev->object);
symsrv_dprint(pev->desc);
break;
}
return TRUE;
}
//----------------------------------------------------------------------------
class symsrv_cb_t
{
HMODULE symsrv_hmod;
bool wait_box_shown;
SymbolServerGetOptionData_t get_option_data; // "DbgHelp.dll 10.0 or later"
SymbolServerSetOptions_t set_options;
ULONG64 was_context;
ULONG64 was_callback;
public:
symsrv_cb_t(void)
{
symsrv_hmod = LoadLibrary("symsrv.dll");
wait_box_shown = false;
get_option_data = NULL;
set_options = NULL;
was_context = 0;
was_callback = 0;
}
void init(void)
{
if ( symsrv_hmod != NULL )
{
get_option_data = (SymbolServerGetOptionData_t)(void *)GetProcAddress(symsrv_hmod, "SymbolServerGetOptionData");
if ( get_option_data != NULL )
{
was_context = get_option_data(SSRVOPT_SETCONTEXT, &was_context);
was_callback = get_option_data(SSRVOPT_CALLBACK, &was_callback);
}
set_options = (SymbolServerSetOptions_t)(void *)GetProcAddress(symsrv_hmod, "SymbolServerSetOptions");
if ( set_options != NULL )
{
set_options(SSRVOPT_SETCONTEXT, (ULONG64) (intptr_t) &wait_box_shown);
set_options(SSRVOPT_CALLBACK, (ULONG64) SymbolServerCallback);
if ( (debug & IDA_DEBUG_DEBUGGER) != 0 )
{
set_options(SSRVOPT_TRACE, (ULONG64) TRUE);
}
}
}
}
void term(void)
{
if ( symsrv_hmod != NULL )
{
if ( set_options != NULL )
{
set_options(SSRVOPT_SETCONTEXT, was_context);
set_options(SSRVOPT_CALLBACK, was_callback);
}
FreeLibrary(symsrv_hmod);
symsrv_hmod = NULL;
if ( wait_box_shown )
hide_wait_box();
}
}
};
//----------------------------------------------------------------------------
static qstring print_guid(GUID *guid)
{
qstring guid_str;
if ( guid != NULL )
{
OLECHAR *guid_wstr = NULL;
StringFromCLSID(*guid, &guid_wstr);
if ( guid_wstr != NULL )
{
utf16_utf8(&guid_str, guid_wstr);
CoTaskMemFree(guid_wstr);
}
}
if ( guid_str.empty() )
guid_str = "{00000000-0000-0000-0000-000000000000}";
return guid_str;
}
//----------------------------------------------------------------------------
static HRESULT check_and_load_pdb(
IDiaDataSource *pSource,
LPCOLESTR pdb_path,
const pdb_signature_t &pdb_sign,
bool load_anyway)
{
HRESULT hr = E_FAIL;
if ( !load_anyway )
{
uint32 sig = pdb_sign.sig;
uint32 age = pdb_sign.age;
GUID *pcsig70 = NULL;
for ( int i=0; i < qnumber(pdb_sign.guid); i++ )
{
if ( pdb_sign.guid[i] != 0 )
{
pcsig70 = (GUID *)&pdb_sign.guid;
break;
}
}
if ( sig == 0 && age == 0 && pcsig70 == NULL )
return E_FAIL;
qstring guid_str = print_guid(pcsig70);
deb(IDA_DEBUG_DEBUGGER, "PDB: Trying to load PDB \"%S\" (guid %s, sig 0x%08X, age 0x%08X)\n", pdb_path, guid_str.c_str(), sig, age);
hr = pSource->loadAndValidateDataFromPdb(pdb_path, pcsig70, sig, age);
deb(IDA_DEBUG_DEBUGGER, "PDB: loadAndValidateDataFromPdb(\"%S\"): %s\n", pdb_path, pdberr(hr));
if ( hr == E_PDB_INVALID_SIG || hr == E_PDB_INVALID_AGE )
{
load_anyway = ask_yn(ASKBTN_NO,
"HIDECANCEL\nICON WARNING\nAUTOHIDE NONE\n"
"PDB signature and/or age does not match the input file.\n"
"Do you want to load it anyway?") == ASKBTN_YES;
}
}
if ( load_anyway )
{
hr = pSource->loadDataFromPdb(pdb_path);
deb(IDA_DEBUG_DEBUGGER, "PDB: loadDataFromPdb(\"%S\"): %s\n", pdb_path, pdberr(hr));
}
return hr;
}
//----------------------------------------------------------------------------
// warn the user about eventual UNC or other problematic paths
static bool check_for_odd_paths(const char *fname)
{
if ( PathIsUNC == NULL )
{
HMODULE h = GetModuleHandle("shlwapi.dll");
if ( h != NULL )
PathIsUNC = (PathIsUNC_t)(void*)GetProcAddress(h, "PathIsUNCA");
}
if ( fname[0] == '\\'
|| fname[0] == '/'
|| PathIsUNC != NULL && PathIsUNC(fname) )
{
if ( ask_yn(ASKBTN_NO,
"AUTOHIDE NONE\nHIDECANCEL\n"
"Please be careful, the debug path looks odd!\n"
"\"%s\"\n"
"Do you really want IDA to access this path (possibly a remote server)?",
fname) != ASKBTN_YES )
{
return false;
}
}
return true;
}
//---------------------------------------------------------------------------
HRESULT pdb_session_t::load_data_for_exe(
const pdbargs_t &pdbargs,
load_data_type_t type)
{
// First check for load address.
ea_t load_address = BADADDR;
if ( type == MEM_LOCAL || type == MEM_WIN32 )
{
load_address = pdbargs.loaded_base;
if ( load_address == BADADDR )
return E_FAIL;
}
msdia_reader_t *msdia_reader = NULL;
HRESULT hr = E_FAIL;
switch ( type )
{
case EXE_LOCAL:
msdia_reader = new local_exe_msdia_reader_t(winput.c_str());
break;
#ifdef PDB_PLUGIN
case MEM_LOCAL:
msdia_reader = new local_mem_msdia_reader_t;
break;
#elif defined(PDB_WIN32_SERVER)
case EXE_WIN32:
msdia_reader = new win32_msdia_reader_t(pdbargs.user_data, READ_INPUT_FILE);
break;
case MEM_WIN32:
msdia_reader = new win32_msdia_reader_t(pdbargs.user_data, READ_MEMORY);
break;
#endif
default:
break;
}
if ( msdia_reader->setup() )
{
qstring buf;
if ( load_address != BADADDR )
buf.sprnt(" with load address %a", load_address);
deb(IDA_DEBUG_DEBUGGER, "PDB: Trying loadDataForExe(\"%S\", \"%S\")%s\n", winput.c_str(), wspath.c_str(), buf.c_str());
CCallback callback(this, msdia_reader, load_address);
hr = pSource->loadDataForExe(winput.c_str(), wspath.c_str(), (IDiaLoadCallback *)&callback);
deb(IDA_DEBUG_DEBUGGER, "PDB: %s\n", pdberr(hr));
}
delete msdia_reader;
return hr;
}
//----------------------------------------------------------------------------
HRESULT pdb_session_t::load_input_path(
const pdbargs_t &pdbargs,
const char *input_path)
{
utf8_utf16(&wspath, pdbargs.spath.c_str());
utf8_utf16(&winput, input_path);
qvector<load_data_type_t> methods;
#ifdef PDB_PLUGIN
// Is the debugger active?
if ( get_process_state() != DSTATE_NOTASK )
{
// First try using program data from debugger memory.
methods.push_back(MEM_LOCAL);
// Then try reading the executable (unless we're remote debugging).
if ( !dbg->is_remote() )
methods.push_back(EXE_LOCAL);
}
else // debugger not active
{
// First try reading the executable.
methods.push_back(EXE_LOCAL);
// Then try using program data from the IDB.
methods.push_back(MEM_LOCAL);
}
#elif defined(PDB_WIN32_SERVER)
// First try reading the executable.
if ( pdbargs.is_dbg_module() )
{
// If the module has been loaded by the debugger itself, we can
// read the file locally on the server side.
// TODO isn't this a security issue? the user can specify
// any input_path to be read on the server.
methods.push_back(EXE_LOCAL);
}
else
{
// Otherwise we want to read the input file from the remote stub.
methods.push_back(EXE_WIN32);
}
// Then try reading memory locally on the server side (the process
// being debugged).
methods.push_back(MEM_WIN32);
#else
// For efd and tilib, only try loading the executable locally.
methods.push_back(EXE_LOCAL);
#endif
HRESULT hr = E_FAIL;
for ( size_t i = 0; i < methods.size(); i++ )
{
hr = load_data_for_exe(pdbargs, methods[i]);
if ( hr == S_OK )
break;
if ( hr == E_PDB_NOT_FOUND )
break; // another address won't help
}
return hr;
}
//----------------------------------------------------------------------------
HRESULT pdb_session_t::open_session(const pdbargs_t &pdbargs)
{
// Already open?
if ( pdb_access != NULL )
return S_OK;
// Not initialized yet?
if ( !co_initialized )
{
// Initialize COM
CoInitialize(NULL);
co_initialized = true;
}
int dia_version;
HRESULT hr;
IDiaSession *pSession = NULL;
IDiaSymbol *pGlobal = NULL;
bool pdb_loaded = false;
// No interface was created?
hr = create_dia_source(&dia_version);
if ( FAILED(hr) )
goto fail;
// First try to open PDB file if it was specified.
const qstring &pdb_path = pdbargs.pdb_path;
if ( !pdb_path.empty()
&& check_for_odd_paths(pdb_path.c_str())
&& qfileexist(pdb_path.c_str()) )
{
qwstring wpdb_path;
utf8_utf16(&wpdb_path, pdb_path.c_str());
bool force_load = (pdbargs.flags & (PDBFLG_ONLY_TYPES|PDBFLG_EFD)) != 0;
hr = check_and_load_pdb(pSource, wpdb_path.c_str(), pdbargs.pdb_sign, force_load);
if ( hr == E_PDB_INVALID_SIG || hr == E_PDB_INVALID_AGE ) // Mismatching PDB
goto fail;
pdb_loaded = (hr == S_OK);
used_fname = pdb_path; // TODO is this needed?
}
// Failed? Try to load input_path as EXE if it was specified.
const qstring &input_path = pdbargs.input_path;
if ( !pdb_loaded && !input_path.empty() )
{
qstring path = input_path;
if ( !qfileexist(path.c_str()) )
{
// If the input path came from a remote system, it is unlikely to be
// correct on our system. DIA does not care about the exact file name
// but uses the directory path to locate the PDB file. It combines
// the name of the pdb file from the debug directory and the directory
// from the input path.
// Since we cannot rely on remote paths, we simply use the current dir
char buf[QMAXPATH];
qgetcwd(buf, sizeof(buf));
path.sprnt("%s\\%s", buf, qbasename(input_path.c_str()));
msg("PDB: \"%s\": not found, trying \"%s\"\n", path.c_str(), buf);
}
if ( !check_for_odd_paths(path.c_str()) )
return E_PDB_NOT_FOUND;
used_fname = path;
// Setup symsrv callback to show wait box for pdb downloading
symsrv_cb_t symsrv_cb;
symsrv_cb.init();
// Try searching for PDB information from the debug directory in a
// PE file. Either the input file is read directly or the contents
// of a loaded module are read from memory.
hr = load_input_path(pdbargs, path.c_str());
pdb_loaded = (hr == S_OK);
// Hide wait box for pdb downloading if needed
symsrv_cb.term();
}
// Failed? Then nothing else to try, quit
if ( !pdb_loaded )
{
// make sure we do return an error
if ( hr == S_OK )
hr = E_FAIL;
goto fail;
}
// Open a session for querying symbols
hr = pSource->openSession(&pSession);
deb(IDA_DEBUG_DEBUGGER, "PDB: openSession(): %s\n", pdberr(hr));
if ( FAILED(hr) )
goto fail;
// Set load address
// TODO check if load_address should be set when loading PDB works directly.
ea_t load_address = pdbargs.loaded_base;
if ( load_address != BADADDR )
{
msg("PDB: using load address %a\n", load_address);
pSession->put_loadAddress(load_address);
}
// Retrieve a reference to the global scope
hr = pSession->get_globalScope(&pGlobal); //-V595 The 'pSession' pointer was utilized before it was verified against nullptr
if ( hr != S_OK )
goto fail;
pdb_access = new local_pdb_access_t(pdbargs, pSource, pSession, pGlobal);
DWORD pdb_machType, machType;
if ( pGlobal->get_machineType(&pdb_machType) != S_OK ) //-V595 The 'pGlobal' pointer was utilized before it was verified against nullptr
pdb_machType = IMAGE_FILE_MACHINE_I386;
machType = get_machine_type(pdb_machType);
pdb_access->set_machine_type(machType);
pdb_access->set_dia_version(dia_version);
hr = pdb_access->init();
if ( hr == S_OK )
return hr;
// TODO clear pdb_access since above test failed
fail:
// In the event of an error, this will be reached.
if ( pdb_access == NULL )
{
if ( pGlobal != NULL )
pGlobal->Release();
if ( pSession != NULL )
pSession->Release();
if ( pSource != NULL )
pSource->Release();
}
return hr;
}
//----------------------------------------------------------------------
HRESULT pdb_session_t::create_dia_source(int *dia_version)
{
HRESULT hr;
// VC80/90 CRT installs msdiaNN.dll in this folder:
// "C:\Program Files (x86)\Common Files\microsoft shared\VC"
char common_files[QMAXPATH];
qstring vc_shared;
if ( get_special_folder(common_files, sizeof(common_files), CSIDL_PROGRAM_FILES_COMMON) )
{
vc_shared = common_files;
vc_shared.append("\\Microsoft Shared\\VC");
}
for ( size_t i=0; i < qnumber(g_msdiav); i++ )
{
// Try to create using CoCreateInstance()
hr = CoCreateInstance(*g_msdiav[i],
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IDiaDataSource),
(void**)&pSource);
// Try to create with CoCreateInstanceNoReg()
if ( FAILED(hr) )
{
// Search for this interface in DIA dlls
char path[QMAXPATH];
if ( !search_path(path, sizeof(path), g_diadlls[i], false)
&& (vc_shared.empty()
|| SearchPathA(vc_shared.c_str(), g_diadlls[i], NULL,
qnumber(path), path, NULL) == 0) )
{
continue;
}
for ( size_t j=0; j < qnumber(g_msdiav); j++ )
{
hr = CoCreateInstanceNoReg(path,
*g_msdiav[j],
NULL,
__uuidof(IDiaDataSource),
(void**)&pSource,
&dia_hmod);
if ( hr == S_OK )
{
static bool displayed = false;
if ( !displayed )
{
displayed = true;
msg("PDB: using DIA dll \"%s\"\n", path);
}
i = j;
break;
}
}
}
if ( hr == S_OK )
{
*dia_version = g_diaver[i];
static bool displayed = false;
if ( !displayed )
{
displayed = true;
msg("PDB: DIA interface version %d.%d\n", (*dia_version)/100, (*dia_version)%100);
}
return hr;
}
else
{
*dia_version = 0;
}
}
return E_NOINTERFACE;
}
//----------------------------------------------------------------------
pdb_session_ref_t::pdb_session_ref_t(const pdb_session_ref_t &r)
: session(r.session)
{
if ( session != NULL )
session->refcount++;
}
//----------------------------------------------------------------------
pdb_session_ref_t &pdb_session_ref_t::operator=(const pdb_session_ref_t &r)
{
if ( &r != this )
{
this->~pdb_session_ref_t();
new (this) pdb_session_ref_t(r);
}
return *this;
}
//----------------------------------------------------------------------------
pdb_session_ref_t::~pdb_session_ref_t()
{
close();
if ( session != NULL )
{
delete session;
session = NULL;
}
}
//----------------------------------------------------------------------
void pdb_session_ref_t::create_session(void)
{
QASSERT(30462, session == NULL);
session = new pdb_session_t();
}
//----------------------------------------------------------------------
void pdb_session_ref_t::close()
{
if ( session != NULL )
{
// shared instance? then detach
if ( session->refcount > 1 )
{ // unlink
session->refcount--;
session = NULL;
}
else
{
session->close();
}
}
}
//----------------------------------------------------------------------
HRESULT pdb_session_ref_t::open_session(const pdbargs_t &pdbargs)
{
if ( opened() )
return S_OK;
if ( empty() )
create_session();
return session->open_session(pdbargs);
}