You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
476 lines
13 KiB
476 lines
13 KiB
#include <windows.h>
|
|
#include <tchar.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <dbgeng.h>
|
|
|
|
#include "debug.h"
|
|
#include "output.h"
|
|
|
|
typedef HRESULT (CALLBACK* PDEBUG_EXTENSION_SET_CLIENT)(LPCSTR RemoteArgs);
|
|
|
|
#if DBG
|
|
CHAR szDefaultExtPath[] = "\\\\JasonHa\\DbgExts\\gdikdxd";
|
|
#else
|
|
CHAR szDefaultExtPath[] = "\\\\JasonHa\\DbgExts\\gdikdxr";
|
|
#endif
|
|
|
|
HMODULE ghGDIExt = NULL;
|
|
PDEBUG_CONTROL Control = NULL;
|
|
BOOL Continue = TRUE;
|
|
PCSTR gRemoteSpec = NULL;
|
|
PCSTR gMainExtPath = szDefaultExtPath;
|
|
|
|
|
|
HMODULE LoadExtension(PDEBUG_CLIENT Client, PCSTR ExtPath);
|
|
BOOL FreeExtension(PDEBUG_CLIENT, HMODULE hExt);
|
|
VOID ProcessCommands(PDEBUG_CLIENT Client, OutputMonitor *Monitor);
|
|
VOID SetOutputCmd(OutputMonitor *Monitor, const char *Args);
|
|
BOOL CtrlHandler(DWORD fdwCtrlType);
|
|
|
|
|
|
int __cdecl main(int argc, char** argv)
|
|
{
|
|
HRESULT hr;
|
|
BOOL CtrlHandlerSet;
|
|
|
|
PDEBUG_CLIENT Client = NULL;
|
|
|
|
OutputMonitor Monitor;
|
|
|
|
if (argc < 2)
|
|
{
|
|
printf("Missing remote specification.\n");
|
|
return 1;
|
|
}
|
|
|
|
if (argc > 3)
|
|
{
|
|
printf("Too many arguments.\n");
|
|
return 1;
|
|
}
|
|
|
|
gRemoteSpec = argv[1];
|
|
|
|
if ((hr = DebugConnect(gRemoteSpec, __uuidof(IDebugClient), (void **)&Client)) != S_OK ||
|
|
Client == NULL)
|
|
{
|
|
printf("Couldn't connect to client: %s, HRESULT: 0x%lx\n", argv[1], hr);
|
|
return 2;
|
|
}
|
|
|
|
if ((hr = Client->QueryInterface(__uuidof(IDebugControl), (void **)&Control)) != S_OK ||
|
|
Control == NULL)
|
|
{
|
|
printf("Couldn't connect to IDebugControl, HRESULT: 0x%lx\n", hr);
|
|
Client->Release();
|
|
return 3;
|
|
}
|
|
|
|
if ((hr = Monitor.Monitor(Client,
|
|
DEBUG_OUTPUT_NORMAL |
|
|
DEBUG_OUTPUT_ERROR |
|
|
DEBUG_OUTPUT_WARNING// | DEBUG_OUTPUT_VERBOSE
|
|
)) != S_OK)
|
|
{
|
|
printf("Output monitor setup failed, HRESULT: 0x%lx\n", hr);
|
|
Control->Release();
|
|
Client->Release();
|
|
return 4;
|
|
}
|
|
|
|
CtrlHandlerSet = SetConsoleCtrlHandler(
|
|
(PHANDLER_ROUTINE) CtrlHandler, // handler function
|
|
TRUE); // add to list
|
|
|
|
if ((hr = Client->ConnectSession(DEBUG_CONNECT_SESSION_NO_VERSION, 0)) != S_OK)
|
|
{
|
|
printf("Couldn't finalize connection. HRESULT 0x%lx\n", hr);
|
|
}
|
|
else
|
|
{
|
|
if (argc > 2)
|
|
{
|
|
gMainExtPath = argv[2];
|
|
}
|
|
|
|
ghGDIExt = LoadExtension(Client, gMainExtPath);
|
|
|
|
if (ghGDIExt != NULL)
|
|
{
|
|
ProcessCommands(Client, &Monitor);
|
|
|
|
FreeExtension(Client, ghGDIExt);
|
|
}
|
|
|
|
Control->Output(DEBUG_OUTPUT_NORMAL, "GDIView disconnecting.\n");
|
|
}
|
|
|
|
if (CtrlHandlerSet)
|
|
{
|
|
SetConsoleCtrlHandler(
|
|
(PHANDLER_ROUTINE) CtrlHandler, // handler function
|
|
FALSE); // remove from list
|
|
}
|
|
|
|
Control->Release();
|
|
Client->Release();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
HMODULE
|
|
LoadExtension(
|
|
PDEBUG_CLIENT Client,
|
|
PCSTR ExtPath
|
|
)
|
|
{
|
|
HMODULE hExt = NULL;
|
|
BOOL bInitComplete = FALSE;
|
|
|
|
PDEBUG_CONTROL2 Control2;
|
|
ULONG Status = DEBUG_STATUS_BREAK;
|
|
|
|
if (Client->QueryInterface(__uuidof(IDebugControl2),
|
|
(void **)&Control2) == S_OK)
|
|
{
|
|
Control2->GetExecutionStatus(&Status);
|
|
Control2->Release();
|
|
}
|
|
|
|
if (Status != DEBUG_STATUS_NO_DEBUGGEE)
|
|
{
|
|
if ((hExt = LoadLibraryA(ExtPath)) != NULL)
|
|
{
|
|
PDEBUG_EXTENSION_SET_CLIENT pfnDbgExtSetClient;
|
|
PDEBUG_EXTENSION_INITIALIZE pfnDbgExtInit;
|
|
PDEBUG_EXTENSION_NOTIFY pfnDbgExtNotify;
|
|
PDEBUG_EXTENSION_UNINITIALIZE pfnDbgExtUninit;
|
|
|
|
pfnDbgExtSetClient = (PDEBUG_EXTENSION_SET_CLIENT)
|
|
GetProcAddress(hExt, "DebugExtensionSetClient");
|
|
pfnDbgExtInit = (PDEBUG_EXTENSION_INITIALIZE)
|
|
GetProcAddress(hExt, "DebugExtensionInitialize");
|
|
pfnDbgExtNotify = (PDEBUG_EXTENSION_NOTIFY)
|
|
GetProcAddress(hExt, "DebugExtensionNotify");
|
|
pfnDbgExtUninit = (PDEBUG_EXTENSION_UNINITIALIZE)
|
|
GetProcAddress(hExt, "DebugExtensionUninitialize");
|
|
|
|
if ((pfnDbgExtSetClient != NULL) &&
|
|
(pfnDbgExtInit != NULL) &&
|
|
(pfnDbgExtNotify != NULL) &&
|
|
(pfnDbgExtUninit != NULL))
|
|
{
|
|
HRESULT hr;
|
|
ULONG Version, Flags;
|
|
|
|
if ((hr = pfnDbgExtSetClient(gRemoteSpec) == S_OK) &&
|
|
(hr = pfnDbgExtInit(&Version, &Flags)) == S_OK)
|
|
{
|
|
pfnDbgExtNotify(DEBUG_NOTIFY_SESSION_ACTIVE, 0);
|
|
|
|
if (Status == DEBUG_STATUS_BREAK)
|
|
{
|
|
pfnDbgExtNotify(DEBUG_NOTIFY_SESSION_ACCESSIBLE, 0);
|
|
}
|
|
|
|
bInitComplete = TRUE;
|
|
}
|
|
else
|
|
{
|
|
printf("Extension init failed: 0x%lx\n", hr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("Couldn't get all required proc addresses.\n");
|
|
}
|
|
|
|
if (!bInitComplete)
|
|
{
|
|
FreeExtension(Client, hExt);
|
|
hExt = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("LoadLibrary for %s failed with 0x%lx.\n",
|
|
ExtPath, GetLastError());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
printf("Extension was not loaded since there is no debuggee.\n");
|
|
}
|
|
|
|
return hExt;
|
|
}
|
|
|
|
|
|
BOOL
|
|
FreeExtension(
|
|
PDEBUG_CLIENT Client,
|
|
HMODULE hExt
|
|
)
|
|
{
|
|
PDEBUG_EXTENSION_UNINITIALIZE pfnDbgExtUninit;
|
|
|
|
if (hExt == NULL) return FALSE;
|
|
|
|
pfnDbgExtUninit = (PDEBUG_EXTENSION_UNINITIALIZE)
|
|
GetProcAddress(ghGDIExt, "DebugExtensionUninitialize");
|
|
|
|
if (pfnDbgExtUninit != NULL)
|
|
{
|
|
pfnDbgExtUninit();
|
|
}
|
|
|
|
return FreeLibrary(hExt);
|
|
}
|
|
|
|
|
|
VOID
|
|
ProcessCommands(
|
|
PDEBUG_CLIENT Client,
|
|
OutputMonitor *Monitor)
|
|
{
|
|
PDEBUG_CONTROL DbgControl;
|
|
CHAR CmdLine[MAX_PATH];
|
|
CHAR *pCmd;
|
|
CHAR *pArgs;
|
|
PDEBUG_EXTENSION_CALL pfnDbgExt;
|
|
|
|
if (Client == NULL ||
|
|
Client->QueryInterface(__uuidof(IDebugControl), (void **)&DbgControl) != S_OK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
while (Continue)
|
|
{
|
|
printf("GDIView> ");
|
|
|
|
if (gets(CmdLine))
|
|
{
|
|
pCmd = CmdLine;
|
|
while (*pCmd && isspace(*pCmd)) pCmd++;
|
|
|
|
if (! *pCmd) continue;
|
|
|
|
if (*pCmd == '.')
|
|
{
|
|
BOOL FoundCmd = FALSE;
|
|
|
|
pCmd++;
|
|
|
|
switch (tolower(*pCmd))
|
|
{
|
|
case 'h':
|
|
if (_strnicmp(pCmd, "help", strlen(pCmd)) == 0)
|
|
{
|
|
printf("GDIView Help:\n"
|
|
" .help This help\n"
|
|
" .output Display/toggle output filtering\n"
|
|
" .quit Exit GDIView\n"
|
|
"\n"
|
|
" <GDIKDX Extension> Execute GDIKDX Extension\n"
|
|
" help GDIKDX help information\n");
|
|
FoundCmd = TRUE;
|
|
}
|
|
break;
|
|
case 'q':
|
|
if (_strnicmp(pCmd, "quit", strlen(pCmd)) == 0)
|
|
{
|
|
Continue = FALSE;
|
|
FoundCmd = TRUE;
|
|
}
|
|
break;
|
|
case 'o':
|
|
{
|
|
ULONG CmdLen;
|
|
|
|
pArgs = pCmd;
|
|
do
|
|
{
|
|
pArgs++;
|
|
} while (*pArgs != '\0' && !isspace(*pArgs));
|
|
|
|
if (_strnicmp(pCmd, "output", pArgs - pCmd) == 0)
|
|
{
|
|
SetOutputCmd(Monitor, pArgs);
|
|
FoundCmd = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!FoundCmd)
|
|
{
|
|
printf("Unknown internal command: .%s\n", pCmd);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pArgs = pCmd;
|
|
while (*pArgs && !isspace(*pArgs)) pArgs++;
|
|
if (*pArgs)
|
|
{
|
|
*pArgs++ = '\0';
|
|
while (*pArgs && isspace(*pArgs)) pArgs++;
|
|
}
|
|
|
|
pfnDbgExt = (PDEBUG_EXTENSION_CALL)GetProcAddress(ghGDIExt, pCmd);
|
|
if (pfnDbgExt != NULL)
|
|
{
|
|
DbgControl->ControlledOutput(DEBUG_OUTCTL_ALL_OTHER_CLIENTS,
|
|
DEBUG_OUTPUT_NORMAL,
|
|
"GDIView> !%s.%s %s\n",
|
|
gMainExtPath,
|
|
pCmd,
|
|
pArgs);
|
|
|
|
pfnDbgExt(Client, pArgs);
|
|
}
|
|
else
|
|
{
|
|
printf("Couldn't find extension: %s\n", pCmd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DbgControl->Release();
|
|
}
|
|
|
|
|
|
VOID
|
|
SetOutputCmd(
|
|
OutputMonitor *Monitor,
|
|
const char *Args
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
ULONG OutputMask;
|
|
ULONG NewMask;
|
|
ULONG ToggleMask;
|
|
BOOL Clear = FALSE;
|
|
|
|
if (Monitor == NULL) return;
|
|
|
|
hr = Monitor->GetOutputMask(&OutputMask);
|
|
if (hr != S_OK)
|
|
{
|
|
printf("Failed to retrieve Monitor's output mask.\n");
|
|
return;
|
|
}
|
|
NewMask = OutputMask;
|
|
|
|
while (isspace(*Args)) Args++;
|
|
|
|
while (hr == S_OK && *Args != '\0')
|
|
{
|
|
switch (tolower(*Args))
|
|
{
|
|
case '+': Clear = FALSE; ToggleMask = 0; break;
|
|
case '-': Clear = TRUE; ToggleMask = 0; break;
|
|
case 'n': ToggleMask |= DEBUG_OUTPUT_NORMAL; break;
|
|
case 'e': ToggleMask |= DEBUG_OUTPUT_ERROR; break;
|
|
case 'w': ToggleMask |= DEBUG_OUTPUT_WARNING; break;
|
|
case 'v': ToggleMask |= DEBUG_OUTPUT_VERBOSE; break;
|
|
case '?':
|
|
printf("Usage: .output [+-][newv]\n");
|
|
return;
|
|
default: hr = E_INVALIDARG; break;
|
|
}
|
|
Args++;
|
|
|
|
if (*Args == '\0' || isspace(*Args))
|
|
{
|
|
if (Clear)
|
|
{
|
|
NewMask &= ~ToggleMask;
|
|
}
|
|
else
|
|
{
|
|
NewMask |= ToggleMask;
|
|
}
|
|
|
|
while (isspace(*Args)) Args++;
|
|
}
|
|
}
|
|
|
|
if (hr != S_OK)
|
|
{
|
|
printf("Invalid arguments to .output.\n");
|
|
}
|
|
else if (NewMask != OutputMask)
|
|
{
|
|
hr = Monitor->SetOutputMask(NewMask);
|
|
if (hr == S_OK)
|
|
{
|
|
OutputMask = NewMask;
|
|
}
|
|
else
|
|
{
|
|
printf("Error while trying to set new monitor mask.\n");
|
|
}
|
|
}
|
|
|
|
printf("Monitoring:");
|
|
if (OutputMask & DEBUG_OUTPUT_NORMAL) printf(" Normal");
|
|
if (OutputMask & DEBUG_OUTPUT_ERROR) printf(" Error");
|
|
if (OutputMask & DEBUG_OUTPUT_WARNING) printf(" Warning");
|
|
if (OutputMask & DEBUG_OUTPUT_VERBOSE) printf(" Verbose");
|
|
|
|
OutputMask &= ~(DEBUG_OUTPUT_NORMAL | DEBUG_OUTPUT_ERROR |
|
|
DEBUG_OUTPUT_WARNING | DEBUG_OUTPUT_VERBOSE);
|
|
if (OutputMask) printf(" Other: 0x%lx", OutputMask);
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
|
|
// CtrlHandler - process Console Control signals
|
|
//
|
|
// Note: Global Control must be available whenever
|
|
// CtrlHandler is registered.
|
|
|
|
BOOL CtrlHandler(DWORD fdwCtrlType)
|
|
{
|
|
switch (fdwCtrlType)
|
|
{
|
|
// Handle the CTRL+C and CTRL+Break signals.
|
|
|
|
case CTRL_C_EVENT:
|
|
|
|
case CTRL_BREAK_EVENT:
|
|
|
|
Control->SetInterrupt(DEBUG_INTERRUPT_PASSIVE);
|
|
return TRUE;
|
|
|
|
|
|
// User wants to exit.
|
|
|
|
case CTRL_CLOSE_EVENT:
|
|
|
|
case CTRL_LOGOFF_EVENT:
|
|
|
|
case CTRL_SHUTDOWN_EVENT:
|
|
|
|
Continue = FALSE;
|
|
Control->SetInterrupt(DEBUG_INTERRUPT_EXIT);
|
|
return TRUE;
|
|
|
|
|
|
// Pass other signals to the next handler.
|
|
|
|
default:
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|