#include #include #include #include #include #include #include #define MAX_STR 256 #define WILD_UNDERSCORE 1 #define SYM_BUFFER_SIZE (sizeof(IMAGEHLP_SYMBOL64) + MAX_SYM_NAME) typedef struct { char mask[MAX_STR]; DWORD64 base; } ENUMSYMDATA, *PENUMSYMDATA; typedef enum { cmdQuit = 0, cmdHelp, cmdVerbose, cmdLoad, cmdUnload, cmdEnum, cmdName, cmdAddr, cmdBase, cmdNext, cmdPrev, cmdLine, cmdSymInfo, cmdDiaVer, cmdUndec, cmdFindFile, cmdEnumSrcFiles, cmdMax }; typedef BOOL (*CMDPROC)(char *params); typedef struct _CMD { char token[MAX_STR + 1]; char shorttoken[4]; CMDPROC fn; } CMD, *PCMD; BOOL fnQuit(char *); BOOL fnHelp(char *); BOOL fnVerbose(char *); BOOL fnLoad(char *); BOOL fnUnload(char *); BOOL fnEnum(char *); BOOL fnName(char *); BOOL fnAddr(char *); BOOL fnBase(char *); BOOL fnNext(char *); BOOL fnPrev(char *); BOOL fnLine(char *); BOOL fnSymInfo(char *); BOOL fnDiaVer(char *); BOOL fnUndec(char *); BOOL fnFindFile(char *); BOOL fnEnumSrcFiles(char *); CMD gCmd[cmdMax] = { {"quit", "q", fnQuit}, {"help", "h", fnHelp}, {"verbose", "v", fnVerbose}, {"load", "l", fnLoad}, {"unload", "u", fnUnload}, {"enum", "x", fnEnum}, {"name", "n", fnName}, {"addr", "a", fnAddr}, {"base", "b", fnBase}, {"next", "t", fnNext}, {"prev", "v", fnPrev}, {"line", "i", fnLine}, {"sym" , "s", fnSymInfo}, {"dia", "d", fnDiaVer}, {"undec", "n", fnUndec}, {"ff", "f", fnFindFile}, {"src", "r", fnEnumSrcFiles} }; char gModName[MAX_STR]; char gImageName[MAX_STR]; char gSymbolSearchPath[MAX_STR]; DWORD64 gBase; DWORD64 gDefaultBase; DWORD64 gDefaultBaseForPDB; DWORD gOptions; HANDLE gHP; int WINAPIV dprintf( LPSTR Format, ... ) { static char buf[1000] = "DBGHELP: "; va_list args; if ((gOptions & SYMOPT_DEBUG) == 0) return 1; va_start(args, Format); _vsnprintf(buf, sizeof(buf)-9, Format, args); va_end(args); printf(buf); return 1; } __inline int ucase(int c) { return (gOptions & SYMOPT_CASE_INSENSITIVE) ? toupper(c) : c; } void dumpsym( PIMAGEHLP_SYMBOL64 sym ) { printf(" name : %s\n", sym->Name); printf(" addr : 0x%I64x\n", sym->Address); printf(" size : 0x%x\n", sym->Size); printf("flags : 0x%x\n", sym->Flags); } BOOL MatchPattern( char *sz, char *pattern ) { char c, p, l; if (!*pattern) return TRUE; for (; ;) { p = *pattern++; p = (char)ucase(p); switch (p) { case 0: // end of pattern return *sz ? FALSE : TRUE; // if end of string TRUE case '*': while (*sz) { // match zero or more char if (MatchPattern (sz++, pattern)) { return TRUE; } } return MatchPattern (sz, pattern); case '?': if (*sz++ == 0) { // match any one char return FALSE; // not end of string } break; case WILD_UNDERSCORE: while (*sz == '_') { sz++; } break; case '[': if ( (c = *sz++) == 0) { // match char set return FALSE; // syntax } c = (CHAR)ucase(c); l = 0; while (p = *pattern++) { if (p == ']') { // if end of char set, then return FALSE; // no match found } if (p == '-') { // check a range of chars? p = *pattern; // get high limit of range if (p == 0 || p == ']') { return FALSE; // syntax } if (c >= l && c <= p) { break; // if in range, move on } } l = p; if (c == p) { // if char matches this element break; // move on } } while (p && p != ']') { // got a match in char set p = *pattern++; // skip to end of set } break; default: c = *sz++; if (ucase(c) != p) { // check for exact char return FALSE; // not a match } break; } } } BOOL cbEnumSymbols( PSYMBOL_INFO si, ULONG size, PVOID context ) { PENUMSYMDATA esd = (PENUMSYMDATA)context; printf("0x%I64x : ", si->Address, si->Name); if (si->Flags & SYMF_FORWARDER) printf("%c ", 'F'); else if (si->Flags & SYMF_EXPORT) printf("%c ", 'E'); else printf(" "); printf("%s\n", si->Name); return TRUE; } BOOL cbEnumSym( PTSTR name, DWORD64 address, ULONG size, PVOID context ) { PENUMSYMDATA esd = (PENUMSYMDATA)context; if (MatchPattern(name, esd->mask)) printf("0x%I64x : %s\n", address, name); return TRUE; } BOOL cbSrcFiles( PSOURCEFILE pSourceFile, PVOID UserContext ) { if (!pSourceFile) return FALSE; printf("%s\n", pSourceFile->FileName); return TRUE; } BOOL cbSymbol( HANDLE hProcess, ULONG ActionCode, ULONG64 CallbackData, ULONG64 UserContext ) { PIMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl; PIMAGEHLP_CBA_READ_MEMORY prm; IMAGEHLP_MODULE64 mi; PUCHAR p; ULONG i; idsl = (PIMAGEHLP_DEFERRED_SYMBOL_LOAD64) CallbackData; switch ( ActionCode ) { case CBA_DEBUG_INFO: dprintf("%s", (LPSTR)CallbackData); break; #if 0 case CBA_DEFERRED_SYMBOL_LOAD_CANCEL: if (fControlC) { fControlC = 0; return TRUE; } break; #endif case CBA_DEFERRED_SYMBOL_LOAD_START: dprintf("loading symbols for %s\n", gModName); break; case CBA_DEFERRED_SYMBOL_LOAD_FAILURE: if (idsl->FileName && *idsl->FileName) dprintf( "*** Error: could not load symbols for %s\n", idsl->FileName ); else dprintf( "*** Error: could not load symbols [MODNAME UNKNOWN]\n"); break; case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE: dprintf("loaded symbols for %s\n", gModName); break; case CBA_SYMBOLS_UNLOADED: dprintf("unloaded symbols for %s\n", gModName); break; #if 0 case CBA_READ_MEMORY: prm = (PIMAGEHLP_CBA_READ_MEMORY)CallbackData; return g_Target->ReadVirtual(prm->addr, prm->buf, prm->bytes, prm->bytesread) == S_OK; #endif default: return FALSE; } return FALSE; } PIMAGEHLP_SYMBOL64 SymbolFromName(char *param) { BOOL rc; PIMAGEHLP_SYMBOL64 sym; char name[MAX_STR]; assert(name & *name); sym = malloc(SYM_BUFFER_SIZE); if (!sym) return FALSE; ZeroMemory(sym, SYM_BUFFER_SIZE); sym->MaxNameLength = MAX_SYM_NAME; sprintf(name, "%s!%s", gModName, param); rc = SymGetSymFromName64(gHP, name, sym); if (!rc) { free(sym); return NULL; } return sym; } BOOL fnQuit(char *param) { printf("goodbye\n"); return FALSE; } BOOL fnHelp(char *param) { printf(" dbh commands :\n"); printf(" help : prints this message\n"); printf(" quit : quits this program\n"); printf("verbose : controls debug spew\n"); printf(" load : loads the requested module\n"); printf(" unload : unloads the current module\n"); printf(" enum : enumerates all matching symbols\n"); printf(" name : finds a symbol by it's name\n"); printf(" addr
: finds a symbol by it's hex address\n"); printf(" base
: sets the new default base address\n"); printf(" next : finds the symbol after the passed sym\n"); printf(" prev : finds the symbol before the passed sym\n"); printf(" line : finds the matching line number\n"); printf(" sym : displays type and location of symbols\n"); printf(" dia : displays the DIA version\n"); printf("ff : finds file in path\n"); printf(" src : lists source files\n"); return TRUE; } BOOL fnVerbose(char *param) { int opts = gOptions; if (!param || !*param) printf(""); else if (!_strcmpi(param, "on")) opts |= SYMOPT_DEBUG; else if (!_strcmpi(param, "off")) opts = gOptions & ~SYMOPT_DEBUG; else printf("verbose \n"); gOptions = SymSetOptions(opts); printf("verbose mode %s.\n", gOptions & SYMOPT_DEBUG ? "on" : "off"); return TRUE; } BOOL fnLoad(char *param) { DWORD64 addr; char ext[MAX_STR]; char mod[MAX_STR]; if (!param || !*param || !strchr(param, '.')) { printf("load must specify a file to load symbols for.\n"); return TRUE; } _splitpath(param, NULL, NULL, mod, ext); addr = 0; if (gDefaultBase) addr = gDefaultBase; else if (!_strcmpi(ext, ".pdb")) addr = gDefaultBaseForPDB; fnUnload(NULL); addr = SymLoadModule64(gHP, NULL, // hFile, param, // ImageName, mod, // ModuleName, addr, // BaseOfDll, 0x1000000); // SizeOfDll if (!addr) { printf("error 0x%x loading %s\n", GetLastError(), param); return TRUE; } if (gBase && !SymUnloadModule64(gHP, gBase)) printf("error unloading %s at 0x%x\n", gModName, gBase); strcpy(gModName, mod); strcpy(gImageName, param); gBase = addr; return TRUE; } BOOL fnUnload(char *param) { if (!gBase) return TRUE; if (!SymUnloadModule64(gHP, gBase)) printf("error unloading %s at 0x%x\n", gModName, gBase); gBase = 0; *gModName = 0; return TRUE; } BOOL fnEnum(char *param) { BOOL rc; ENUMSYMDATA esd; esd.base = gBase; strcpy(esd.mask, param ? param : ""); rc = SymEnumSymbols(gHP, gBase, param, cbEnumSymbols, &esd); if (!rc) printf("error 0x%0 calling SymEnumerateSymbols()\n", GetLastError()); return TRUE; } BOOL fnEnumSrcFiles(char *param) { BOOL rc; rc = SymEnumSourceFiles(gHP, gBase, param, cbSrcFiles, NULL); if (!rc) printf("error 0x%0 calling SymEnumSourceFiles()\n", GetLastError()); return TRUE; } BOOL fnName(char *param) { BOOL rc; PIMAGEHLP_SYMBOL64 sym; if (!param || !*param) { printf("name - finds a symbol by it's name\n"); return TRUE; } sym = SymbolFromName(param); if (!sym) return TRUE; dumpsym(sym); free(sym); return TRUE; } BOOL fnAddr(char *param) { BOOL rc; PIMAGEHLP_SYMBOL64 sym; DWORD64 addr; DWORD64 disp; char *p; addr = 0; if (param && *param) { p = param; if (*(p + 1) == 'x' || *(p + 1) == 'X') p += 2; sscanf(p, "%I64x", &addr); } if (!addr) { printf("addr
: finds a symbol by it's hex address\n"); return TRUE; } sym = malloc(SYM_BUFFER_SIZE); if (!sym) return FALSE; ZeroMemory(sym, SYM_BUFFER_SIZE); sym->MaxNameLength = MAX_SYM_NAME; rc = SymGetSymFromAddr64(gHP, addr, &disp, sym); if (rc) { printf("%s", sym->Name); if (disp) printf("+0x%I64x", disp); printf("\n"); dumpsym(sym); } free(sym); return TRUE; } BOOL fnNextPrev(int direction, char *param) { BOOL rc; PIMAGEHLP_SYMBOL64 sym; DWORD64 addr; char name[MAX_STR]; char *p; addr = 0; if (param && *param) { p = param; if (*(p + 1) == 'x' || *(p + 1) == 'X') p += 2; sscanf(p, "%I64x", &addr); } if (!addr) { sym = SymbolFromName(param); if (!sym) return TRUE; addr = sym->Address; if (!addr) { free(sym); return TRUE; } } else { sym = malloc(SYM_BUFFER_SIZE); if (!sym) return FALSE; } if (direction > 0) rc = SymGetSymNext64(gHP, sym); else rc = SymGetSymPrev64(gHP, sym); if (rc) dumpsym(sym); free(sym); return TRUE; } BOOL fnNext(char *param) { return fnNextPrev(1, param); } BOOL fnPrev(char *param) { return fnNextPrev(-1, param); } BOOL fnBase(char *param) { BOOL rc; PIMAGEHLP_SYMBOL64 sym; DWORD64 addr; DWORD64 disp; char *p; addr = 0; if (param && *param) { p = param; if (*(p + 1) == 'x' || *(p + 1) == 'X') p += 2; sscanf(p, "%I64x", &addr); } if (!addr) { printf("base
: sets the base address for module loads\n"); return TRUE; } gDefaultBase = addr; if (gBase) fnLoad(gImageName); return TRUE; } BOOL fnLine(char *param) { char *file; DWORD linenum; BOOL rc; IMAGEHLP_LINE64 line; LONG disp; if (!param || !*param) return TRUE; file = param; while (*param != ':') { if (!*param) return TRUE; param++; } *param++ = 0; linenum = atoi(param); if (!linenum) return TRUE; memset(&line, 0, sizeof(line)); line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); rc = SymGetLineFromName64(gHP, gModName, file, linenum, &disp, &line); if (!rc) { printf("line: error 0x%x looking for %s#%d\n", GetLastError(), file, linenum); return TRUE; } printf("file : %s\n", line.FileName); printf("line : %d\n", linenum); printf("addr : 0x%I64x\n", line.Address); printf("disp : 0x%x\n", disp); return TRUE; } BOOL fnSymInfo(char *param) { DBH_MODSYMINFO msi; if (!gBase) return TRUE; msi.function = dbhModSymInfo; msi.sizeofstruct = sizeof(msi); msi.addr = gBase; if (!dbghelp(gHP, (PVOID)&msi)) printf("error grabbing symbol info\n"); printf("%s: symtype=0x%x, src=%s\n", gModName, msi.type, msi.file); return TRUE; } BOOL fnDiaVer(char *param) { DBH_DIAVERSION dv; dv.function = dbhDiaVersion; dv.sizeofstruct = sizeof(dv); if (!dbghelp(0, (PVOID)&dv)) printf("error grabbing dia version info\n"); printf("DIA version 0x%x\n", dv.ver); return TRUE; } BOOL fnUndec(char *param) { DWORD rc; char uname[MAX_SYM_NAME + 1]; if (!param || !*param) { printf("undec - undecorates a C++ mangled symbol name\n"); return TRUE; } rc = UnDecorateSymbolName(param, uname, MAX_SYM_NAME, UNDNAME_COMPLETE); if (!rc) { printf("error 0x%u undecorating %s\n", GetLastError(), param); } else { printf("%s = %s\n", param, uname); } return TRUE; } BOOL fnFindFile(char *param) { DWORD rc; char root[MAX_PATH + 1]; char file[MAX_PATH + 1]; char found[MAX_PATH + 1]; if (!param) { printf("ff - finds file in path\n"); return TRUE; } sscanf(param, "%s %s", root, file); if (!*root || !*file) { printf("ff - finds file in path\n"); return TRUE; } *found = 0; rc = SearchTreeForFile(root, file, found); if (!rc) { printf("error 0x%u looking for %s\n", GetLastError(), file); } else { printf("found %s\n", found); } return TRUE; } char *GetParameters(char *cmd) { char *p = cmd; char *param = NULL; while (*p++) { if (isspace(*p)) { *p++ = 0; return *p ? p : NULL; } } return NULL; } void prompt() { if (!*gModName) printf("dbh: "); else printf("%s [0x%I64x]: ", gModName, gBase); } int InputLoop() { char cmd[MAX_STR + 1]; char *params; int i; BOOL rc; printf("\n"); do { prompt(); gets(cmd); params = GetParameters(cmd); // printf("cmd[%s] params[%s]\n", cmd, params); for (i = 0; i < cmdMax; i++) { if (!_strcmpi(cmd, gCmd[i].token) || !_strcmpi(cmd, gCmd[i].shorttoken)) break; } if (i == cmdMax) { printf("[%s] is an unrecognized command.\n", cmd); rc = TRUE; continue; } else rc = gCmd[i].fn(params); } while (rc); return 0; } BOOL init() { int i; BOOL rc; *gModName = 0; gBase = 0;; gDefaultBaseForPDB = 0x1000000; printf("dbh: initializing...\n"); i = GetEnvironmentVariable("_NT_SYMBOL_PATH", gSymbolSearchPath, MAX_STR); if (i < 1) *gSymbolSearchPath = 0; printf("Symbol Path = [%s]\n", gSymbolSearchPath); gHP = GetCurrentProcess(); rc = SymInitialize(gHP, gSymbolSearchPath, FALSE); if (!rc) { printf("error 0x%x from SymInitialize()\n", GetLastError()); return rc; } gOptions = SymSetOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_NO_CPP | SYMOPT_LOAD_LINES); printf("SymOpts = 0x%x\n", gOptions); rc = SymRegisterCallback64(gHP, cbSymbol, 0); if (!rc) { printf("error 0x%x from SymRegisterCallback64()\n", GetLastError()); return rc; } return rc; } void cleanup() { fnUnload(NULL); SymCleanup(gHP); } BOOL cmdline(int argc, char *argv[]) { int i; char *p; for (i = 1; i < argc; i++) { p = argv[i]; switch (*p) { case '/': case '-': p++; switch (tolower(*p)) { case 'v': fnVerbose("on"); break; default: printf("%s is an unknown switch\n", argv[i]); break; } break; default: fnLoad(argv[i]); break; } } return TRUE; } #include __cdecl main( int argc, char *argv[], char *envp[] ) { DWORD rc; _CrtSetDbgFlag( ( _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF ) | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG ) ); if (!init()) return 1; cmdline(argc, argv); rc = InputLoop(); cleanup(); _CrtDumpMemoryLeaks(); return rc; }