Reputation: 613
I'm writing a small debugger. I'm trying to find the file and line of code when the program has broken out to the debugger by calling SymGetLineFromAddr64
. While the host process is broken out, I call GetThreadContext
to get the registers, StackWalk64
to get the current stack, and SymGetSymFromAddr64
to get symbols, all of this works fine. Then I call SymGetLineFromAddr64
and it fails with an odd series of errors. The relevant portion of the output is:
Loaded 'D:\git\debugging\Debug\debugging.exe' at 10000000 with symbols.
(Later on, for the first auto-breakpoint that gets called when the program first starts running)
Breakpoint.
Thread context successful.
Stack walk successful.
SymGetLineFromAddr64(PC) error: 487/1e7
SymGetLineFromAddr64(Frame) error: 126/7e
SymGetLineFromAddr64(Stack) error: 126/7e
(I type 'go' and the program proceeds to an actual breakpoint, a __debugbreak() call in the program.)
Breakpoint.
Thread context successful.
Stack walk successful.
SymGetSymFromAddr64 failed. error: 126/7e
SymGetLineFromAddr64(PC) error: 126/7e
SymGetLineFromAddr64(Frame) error: 126/7e
SymGetLineFromAddr64(Stack) error: 126/7e
Error 126/7e is ERROR_MOD_NOT_FOUND
, but I've successfully loaded the debug symbols, as you can see from the above output. Error 487/1e7 is ERROR_INVALID_ADDRESS
, not sure what that could mean and why it only shows up for the program counter when pulling up the specified address in the memory window gave me valid memory, with a CC
breakpoint in the address just previous, as you would expect.
Before anyone asks: yes I did look at this question: Why isn't SymGetSymFromAddr64 working? It returns error code 126 As you can see from my code I already have a SymInitialize so the answer didn't help.
Full code listing:
#include <Windows.h>
#include <DbgHelp.h>
#include <Psapi.h>
#include <stdio.h>
#include <string>
#include <locale>
#include <codecvt>
HANDLE process_handle;
DWORD process_id;
HANDLE thread_handle;
DWORD thread_id;
const char* process_file = "D:\\git\\debugging\\Debug\\debugging.exe";
STACKFRAME64 stack;
typedef enum {
DBGFLAG_NONE = 0,
DBGFLAG_BROKEN = (1 << 0),
DBGFLAG_FIRSTBREAK = (1 << 1),
} dbgflags;
dbgflags debug_flags;
std::string GetFileNameFromHandle(HANDLE hFile);
char* EventIDToString(DWORD id)
{
switch (id)
{
case 3: return "CREATE_PROCESS_DEBUG_EVENT";
case 2: return "CREATE_THREAD_DEBUG_EVENT";
case 1: return "EXCEPTION_DEBUG_EVENT";
case 5: return "EXIT_PROCESS_DEBUG_EVENT";
case 4: return "EXIT_THREAD_DEBUG_EVENT";
case 6: return "LOAD_DLL_DEBUG_EVENT";
case 8: return "OUTPUT_DEBUG_STRING_EVENT";
case 9: return "RIP_EVENT";
case 7: return "UNLOAD_DLL_DEBUG_EVENT";
case 0: return "No event";
default: return "Unknown event";
}
}
DWORD ProcessEvent(DEBUG_EVENT* de)
{
switch (de->dwDebugEventCode)
{
default:
printf("Processing event code: %d (%s)\n", de->dwDebugEventCode, EventIDToString(de->dwDebugEventCode));
break;
case CREATE_PROCESS_DEBUG_EVENT:
{
DWORD options = SymGetOptions();
options |= SYMOPT_DEBUG;
::SymSetOptions(options);
std::string directory = process_file;
directory = directory.substr(0, directory.find_last_of('\\'));
BOOL r = SymInitialize(process_handle, directory.c_str(), false);
if (r)
printf("Initialized symbols\n");
else
{
printf("Symbol initialization failed\n");
break;
}
HANDLE hFile = CreateFile(process_file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
DWORD size = GetFileSize(hFile, NULL);
CloseHandle(hFile);
DWORD64 dwBase = SymLoadModule64(process_handle, NULL, process_file, 0, (DWORD64)0x10000000, size);
IMAGEHLP_MODULE64 mi;
mi.SizeOfStruct = sizeof(mi);
r = SymGetModuleInfo64(process_handle, dwBase, &mi);
if (r && mi.SymType == SymPdb)
printf((std::string("Loaded '") + process_file + std::string("' at %x with symbols\n")).c_str(), 0x10000000);
else
printf((std::string("Loaded '") + process_file + std::string("' at %x without symbols\n")).c_str(), 0x10000000);
break;
}
case LOAD_DLL_DEBUG_EVENT:
{
std::string image = GetFileNameFromHandle(de->u.LoadDll.hFile);
DWORD64 dwBase = SymLoadModule64(process_handle, NULL, image.c_str(), 0, (DWORD64)de->u.LoadDll.lpBaseOfDll, 0);
IMAGEHLP_MODULE64 mi;
mi.SizeOfStruct = sizeof(mi);
BOOL r = SymGetModuleInfo64(process_handle, dwBase, &mi);
if (r && mi.SymType == SymPdb)
printf((std::string("Loaded '") + image + std::string("' at %x with symbols\n")).c_str(), de->u.LoadDll.lpBaseOfDll);
else
printf((std::string("Loaded '") + image + std::string("' at %x without symbols\n")).c_str(), de->u.LoadDll.lpBaseOfDll);
}
case EXCEPTION_DEBUG_EVENT:
{
DWORD code = de->u.Exception.ExceptionRecord.ExceptionCode;
switch (code)
{
case 0x80000003:
printf("Breakpoint.\n");
return DBG_CONTROL_BREAK;
break;
default:
printf("Exception code: %x\n", code);
return DBG_EXCEPTION_NOT_HANDLED;
}
break;
}
case OUTPUT_DEBUG_STRING_EVENT:
{
SIZE_T read;
char* buffer = new char[de->u.DebugString.nDebugStringLength*(de->u.DebugString.fUnicode + 1)];
ReadProcessMemory(process_handle, de->u.DebugString.lpDebugStringData, buffer, de->u.DebugString.nDebugStringLength, &read);
std::string debug;
if (de->u.DebugString.fUnicode)
{
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
debug = converter.to_bytes((wchar_t*)buffer);
}
else
debug = buffer;
delete buffer;
printf("Debug: %s\n", debug.c_str());
break;
}
}
return DBG_CONTINUE;
}
void FindCode()
{
IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*)new char[sizeof(IMAGEHLP_SYMBOL64) + MAX_SYM_NAME];
memset(symbol, 0, sizeof(IMAGEHLP_SYMBOL64) + MAX_SYM_NAME);
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
symbol->MaxNameLength = MAX_SYM_NAME;
DWORD64 displacement64;
BOOL b = SymGetSymFromAddr64(process_handle, stack.AddrPC.Offset, &displacement64, symbol);
DWORD e = GetLastError();
if (!b)
printf("SymGetSymFromAddr64 failed. error: %d/%x\n", e, e);
DWORD displacement;
IMAGEHLP_LINE64 line = { 0 };
line.SizeOfStruct = sizeof(line);
BOOL r = SymGetLineFromAddr64(process_handle, stack.AddrPC.Offset, &displacement, &line);
e = GetLastError();
printf("SymGetLineFromAddr64(PC) error: %d/%x\n", e, e);
r = SymGetLineFromAddr64(process_handle, stack.AddrFrame.Offset, &displacement, &line);
e = GetLastError();
printf("SymGetLineFromAddr64(Frame) error: %d/%x\n", e, e);
r = SymGetLineFromAddr64(process_handle, stack.AddrStack.Offset, &displacement, &line);
e = GetLastError();
printf("SymGetLineFromAddr64(Stack) error: %d/%x\n", e, e);
/* const size_t file_length = 1000;
PSYMBOL_INFO si = (PSYMBOL_INFO)new char[sizeof(SYMBOL_INFO) + file_length];
memset(si, 0, sizeof(SYMBOL_INFO) + file_length);
si->SizeOfStruct = sizeof(SYMBOL_INFO);
si->MaxNameLen = file_length;
BOOL r = SymFromAddr(process_handle, stack.AddrPC.Offset, &displacement64, si);*/
delete symbol;
}
void main()
{
STARTUPINFO si = { 0 };
si.cb = sizeof(si);
PROCESS_INFORMATION pi = { 0 };
BOOL r = CreateProcess(NULL, (char*)process_file, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi);
process_handle = pi.hProcess;
process_id = pi.dwProcessId;
while (true)
{
if (debug_flags & DBGFLAG_BROKEN)
{
char input[100];
gets_s(input);
if (strcmp(input, "go") == 0)
{
printf("Continuing...\n");
ContinueDebugEvent(process_id, thread_id, DBG_CONTINUE);
debug_flags = (dbgflags)(debug_flags & ~DBGFLAG_BROKEN);
CloseHandle(thread_handle);
}
}
else
{
DEBUG_EVENT de = { 0 };
WaitForDebugEvent(&de, 0);
if (de.dwDebugEventCode)
{
DWORD control = ProcessEvent(&de);
thread_id = de.dwThreadId;
if (control == DBG_CONTROL_BREAK)
{
thread_handle = OpenThread(READ_CONTROL | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, false, de.dwThreadId);
debug_flags = (dbgflags)(debug_flags | DBGFLAG_BROKEN);
CONTEXT c = { 0 };
c.ContextFlags = CONTEXT_FULL;
BOOL b = GetThreadContext(thread_handle, &c);
if (b)
printf("Thread context successful.\n");
else
printf("Thread context failed.\n");
STACKFRAME64 sf = { 0 };
sf.AddrPC.Mode = sf.AddrFrame.Mode = sf.AddrStack.Mode = AddrModeFlat;
sf.AddrPC.Offset = c.Eip;
sf.AddrFrame.Offset = c.Ebp;
sf.AddrStack.Offset = c.Esp;
BOOL r = StackWalk64(IMAGE_FILE_MACHINE_I386, process_handle, thread_handle, &sf, &c,
NULL, &SymFunctionTableAccess64, &SymGetModuleBase64, NULL);
stack = sf;
if (r)
printf("Stack walk successful.\n");
else
printf("Stack walk failed.\n");
FindCode();
}
else
{
ContinueDebugEvent(de.dwProcessId, de.dwThreadId, control);
if (!control)
process_handle = false;
}
}
}
}
}
std::string GetFileNameFromHandle(HANDLE hFile)
{
BOOL bSuccess = FALSE;
TCHAR pszFilename[MAX_PATH + 1];
HANDLE hFileMap;
std::string strFilename;
// Get the file size.
DWORD dwFileSizeHi = 0;
DWORD dwFileSizeLo = GetFileSize(hFile, &dwFileSizeHi);
if (dwFileSizeLo == 0 && dwFileSizeHi == 0)
{
return FALSE;
}
// Create a file mapping object.
hFileMap = CreateFileMapping(hFile,
NULL,
PAGE_READONLY,
0,
1,
NULL);
if (hFileMap)
{
// Create a file mapping to get the file name.
void* pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1);
if (pMem)
{
if (GetMappedFileName(GetCurrentProcess(),
pMem,
pszFilename,
MAX_PATH))
{
#define BUFSIZE 1000
// Translate path with device name to drive letters.
TCHAR szTemp[BUFSIZE];
szTemp[0] = '\0';
if (GetLogicalDriveStrings(BUFSIZE - 1, szTemp))
{
TCHAR szName[MAX_PATH];
TCHAR szDrive[3] = TEXT(" :");
BOOL bFound = FALSE;
TCHAR* p = szTemp;
do
{
// Copy the drive letter to the template string
*szDrive = *p;
// Look up each device name
if (QueryDosDevice(szDrive, szName, MAX_PATH))
{
size_t uNameLen = strlen(szName);
if (uNameLen < MAX_PATH)
{
bFound = _strnicmp(pszFilename, szName,
uNameLen) == 0;
if (bFound)
{
strFilename = std::string(szDrive) + (pszFilename + uNameLen);
}
}
}
// Go to the next NULL character.
while (*p++);
} while (!bFound && *p); // end of string
}
}
bSuccess = TRUE;
UnmapViewOfFile(pMem);
}
CloseHandle(hFileMap);
}
return(strFilename);
}
Upvotes: 0
Views: 1469
Reputation: 613
It appears that CREATE_PROCESS_DEBUG_EVENT
is too early to execute SymLoadModule64
, the modules must not have been loaded yet at that point. If instead I do it 'just-in-time' when a program breakpoint gets hit (ie in FindCode
) it seems to work with no problems.
Upvotes: 1