diff options
-rw-r--r-- | src/plugins/debugger/cdb/cdbdebugengine.cpp | 45 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbdebugengine_p.h | 5 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbdebugeventcallback.cpp | 20 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbdumperhelper.cpp | 263 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbdumperhelper.h | 56 | ||||
-rw-r--r-- | src/plugins/debugger/cdb/cdbstackframecontext.cpp | 3 | ||||
-rw-r--r-- | src/plugins/debugger/win/sharedlibraryinjector.cpp | 479 | ||||
-rw-r--r-- | src/plugins/debugger/win/sharedlibraryinjector.h | 109 | ||||
-rw-r--r-- | src/plugins/debugger/win/win.pri | 10 |
9 files changed, 864 insertions, 126 deletions
diff --git a/src/plugins/debugger/cdb/cdbdebugengine.cpp b/src/plugins/debugger/cdb/cdbdebugengine.cpp index e79d9dfdb5e..a7d60ad7fe7 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine.cpp +++ b/src/plugins/debugger/cdb/cdbdebugengine.cpp @@ -594,21 +594,7 @@ void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG6 // Clear any saved breakpoints and set initial breakpoints m_engine->executeDebuggerCommand(QLatin1String("bc")); if (m_debuggerManagerAccess->breakHandler()->hasPendingBreakpoints()) - m_engine->attemptBreakpointSynchronization(); - // At any event, we want a temporary breakpoint at main() to load - // the dumpers. - if (m_dumper->state() == CdbDumperHelper::NotLoaded) { - if (!hasBreakPointAtMain(m_debuggerManagerAccess->breakHandler())) { - QString errorMessage; - CDBBreakPoint mainBP; - // Do not resolve at this point in the rare event someone - // has main in a module - mainBP.funcName = QLatin1String("main"); - mainBP.oneShot = true; - if (!mainBP.add(m_cif.debugControl, &errorMessage)) - m_debuggerManagerAccess->showQtDumperLibraryWarning(errorMessage); - } - } + m_engine->attemptBreakpointSynchronization(); } void CdbDebugEngine::processTerminated(unsigned long exitCode) @@ -1356,6 +1342,12 @@ void CdbDebugEngine::slotConsoleStubTerminated() exitDebugger(); } +void CdbDebugEnginePrivate::notifyCrashed() +{ + // Cannot go over crash point to execute calls. + m_dumper->disable(); +} + void CdbDebugEnginePrivate::handleDebugEvent() { if (debugCDB) @@ -1367,18 +1359,9 @@ void CdbDebugEnginePrivate::handleDebugEvent() switch (mode) { case BreakEventHandle: - case BreakEventMain: - if (mode == BreakEventMain) - m_dumper->load(m_debuggerManager); m_debuggerManagerAccess->notifyInferiorStopped(); updateThreadList(); - updateStackTrace(); - break; - case BreakEventMainLoadDumpers: - // Temp stop to load dumpers - m_dumper->load(m_debuggerManager); - m_engine->startWatchTimer(); - continueInferiorProcess(); + updateStackTrace(); break; case BreakEventIgnoreOnce: m_engine->startWatchTimer(); @@ -1492,6 +1475,7 @@ void CdbDebugEnginePrivate::handleModuleLoad(const QString &name) { if (debugCDB>2) qDebug() << Q_FUNC_INFO << "\n " << name; + m_dumper->moduleLoadHook(name, m_hDebuggeeProcess); updateModules(); } @@ -1500,17 +1484,6 @@ void CdbDebugEnginePrivate::handleBreakpointEvent(PDEBUG_BREAKPOINT2 pBP) Q_UNUSED(pBP) if (debugCDB) qDebug() << Q_FUNC_INFO; - // Did we hit main() and did the user want that or is that just - // our internal BP to load the dumpers? - QString errorMessage; - CDBBreakPoint bp; - if (bp.retrieve(pBP, &errorMessage) && !bp.funcName.isEmpty()) { - if (bp.funcName == QLatin1String("main") || bp.funcName.endsWith(QLatin1String("!main"))) { - m_breakEventMode = bp.oneShot ? BreakEventMainLoadDumpers : BreakEventMain; - } - if (debugCDB) - qDebug() << bp << " b-mode=" << m_breakEventMode; - } } void CdbDebugEngine::reloadSourceFiles() diff --git a/src/plugins/debugger/cdb/cdbdebugengine_p.h b/src/plugins/debugger/cdb/cdbdebugengine_p.h index 04636a72167..373257bd8f4 100644 --- a/src/plugins/debugger/cdb/cdbdebugengine_p.h +++ b/src/plugins/debugger/cdb/cdbdebugengine_p.h @@ -98,10 +98,6 @@ struct CdbDebugEnginePrivate enum HandleBreakEventMode { // Special modes for break event handler. BreakEventHandle, BreakEventIgnoreOnce, - // We hit main (and the user intended it) - BreakEventMain, - // We hit main (and the user did not intend it, just load dumpers) - BreakEventMainLoadDumpers, BreakEventSyncBreakPoints, }; @@ -134,6 +130,7 @@ struct CdbDebugEnginePrivate bool continueInferior(QString *errorMessage); bool attemptBreakpointSynchronization(QString *errorMessage); + void notifyCrashed(); static bool executeDebuggerCommand(CIDebugControl *ctrl, const QString &command, QString *errorMessage); static bool evaluateExpression(CIDebugControl *ctrl, const QString &expression, DEBUG_VALUE *v, QString *errorMessage); diff --git a/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp b/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp index 07eab3215e0..42e48b3dc91 100644 --- a/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp +++ b/src/plugins/debugger/cdb/cdbdebugeventcallback.cpp @@ -300,7 +300,7 @@ void formatException(const EXCEPTION_RECORD64 *e, QTextStream &str) str << "attempt to continue execution after noncontinuable exception"; break; case EXCEPTION_PRIV_INSTRUCTION: - str << "priviledged instruction"; + str << "privileged instruction"; break; case EXCEPTION_SINGLE_STEP: str << "single step"; @@ -336,6 +336,19 @@ void formatException(const EXCEPTION_RECORD64 *e, } } +static bool isFatalException(LONG code) +{ + switch (code) { + case EXCEPTION_BREAKPOINT: + case EXCEPTION_SINGLE_STEP: + case 0x406d1388: // Mysterious exception at start of application + return false; + default: + break; + } + return true; +} + STDMETHODIMP CdbDebugEventCallback::Exception( THIS_ __in PEXCEPTION_RECORD64 Exception, @@ -347,9 +360,12 @@ STDMETHODIMP CdbDebugEventCallback::Exception( QTextStream str(&msg); formatException(Exception, m_pEngine->m_d->m_dumper, str); } + const bool fatal = isFatalException(Exception->ExceptionCode); if (debugCDB) - qDebug() << Q_FUNC_INFO << '\n' << msg; + qDebug() << Q_FUNC_INFO << '\n' << fatal << msg; m_pEngine->m_d->m_debuggerManagerAccess->showApplicationOutput(msg); + if (fatal) + m_pEngine->m_d->notifyCrashed(); return S_OK; } diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.cpp b/src/plugins/debugger/cdb/cdbdumperhelper.cpp index 3f4ffdab8fd..541aa7ea1d0 100644 --- a/src/plugins/debugger/cdb/cdbdumperhelper.cpp +++ b/src/plugins/debugger/cdb/cdbdumperhelper.cpp @@ -35,6 +35,8 @@ #include "cdbsymbolgroupcontext.h" #include "watchhandler.h" +#include "sharedlibraryinjector.h" + #include <QtCore/QRegExp> #include <QtCore/QCoreApplication> #include <QtCore/QTextStream> @@ -45,9 +47,47 @@ static const char *dumperModuleNameC = "gdbmacros"; static const char *qtCoreModuleNameC = "QtCore"; static const ULONG waitTimeOutMS = 30000; static const char *dumperPrefixC = "dumper:"; + +/* Loading the dumpers is 2 step process: + * 1) The library must be loaded into the debuggee, for which there are + * 2 approaches: + * - Injection loading using the SharedLibraryInjector which + * launches a remote thread in the debuggee which loads the + * library. Drawbacks: + * * The remote thread must not starve. + * * It is not possible to wait loading and loading occurs late, + * after entering main() + * - Debugger Call loading, which has the debuggee execute + * a LoadLibrary call via debugger commands. Drawbacks: + * * Slow + * * Requires presence of a symbol of the same prototype as + * LoadLibraryA as the original prototype is not sufficient. + * 2) Call a query function (protocol 1 of the dumper) to obtain a list + * of handled types and a map of known sizes. + * + * The class currently launches injection loading from the module + * load hook as soon as it sees a Qt module. + * The dumpType() function performs the rest of the [delayed] initialization. + * If the load has not finished, it attempts call loading and + * executes the initial query protocol. + * + * Note: The main technique here is having the debuggee call functions + * using the .call command (which takes a function with a known + * prototype and simple, integer parameters). + * This does not work from an IDebugEvent callback, as it will cause + * WaitForEvent() to fail with unknown errors. + * It mostly works from breakpoints, with the addditional restriction + * that complex functions only work from 'well-defined' breakpoints + * (such as main()) and otherwise cause access violation exceptions + * (for example LoadLibrary). + * Exceptions occuring in the functions to be called must be handled + * by __try/__except (they show up in the debugger and must acknowledged + * by gN (go not handled). */ + namespace Debugger { namespace Internal { +// ------- Call load helpers // Alloc memory in debuggee using the ".dvalloc" command as // there seems to be no API for it. static bool allocDebuggeeMemory(CdbComInterfaces *cif, @@ -144,13 +184,44 @@ static bool debuggeeLoadLibrary(IDebuggerManagerAccessForEngines *access, return true; } +// ---- Load messages +static inline QString msgMethod(bool injectOrCall) +{ + return injectOrCall ? + QCoreApplication::translate("CdbDumperHelper", "injection") : + QCoreApplication::translate("CdbDumperHelper", "debugger call"); +} + +static QString msgLoading(const QString &library, bool injectOrCall) +{ + return QCoreApplication::translate("CdbDumperHelper", + "Loading the custom dumper library '%1' (%2) ..."). + arg(library, msgMethod(injectOrCall)); +} + +static QString msgLoadFailed(const QString &library, bool injectOrCall, const QString &why) +{ + return QCoreApplication::translate("CdbDumperHelper", + "Loading of the custom dumper library '%1' (%2) failed: %3"). + arg(library, msgMethod(injectOrCall), why); +} + +static QString msgLoadSucceeded(const QString &library, bool injectOrCall) +{ + return QCoreApplication::translate("CdbDumperHelper", + "Loaded the custom dumper library '%1' (%2)."). + arg(library, msgMethod(injectOrCall)); +} + // ------------------- CdbDumperHelper -CdbDumperHelper::CdbDumperHelper(IDebuggerManagerAccessForEngines *access, +CdbDumperHelper::CdbDumperHelper(DebuggerManager *manager, CdbComInterfaces *cif) : + m_tryInjectLoad(true), m_messagePrefix(QLatin1String(dumperPrefixC)), m_state(NotLoaded), - m_access(access), + m_manager(manager), + m_access(manager), m_cif(cif), m_inBufferAddress(0), m_inBufferSize(0), @@ -165,6 +236,14 @@ CdbDumperHelper::~CdbDumperHelper() clearBuffer(); } +void CdbDumperHelper::disable() +{ + if (loadDebug) + qDebug() << Q_FUNC_INFO; + m_access->showDebuggerOutput(m_messagePrefix, QCoreApplication::translate("CdbDumperHelper", "Disabling dumpers due to debuggee crash...")); + m_state = Disabled; +} + void CdbDumperHelper::clearBuffer() { if (m_buffer) { @@ -175,6 +254,8 @@ void CdbDumperHelper::clearBuffer() void CdbDumperHelper::reset(const QString &library, bool enabled) { + if (loadDebug) + qDebug() << Q_FUNC_INFO << '\n' << library << enabled; m_library = library; m_state = enabled ? NotLoaded : Disabled; m_dumpObjectSymbol = QLatin1String("qDumpObjectData440"); @@ -185,63 +266,113 @@ void CdbDumperHelper::reset(const QString &library, bool enabled) clearBuffer(); } -// Attempt to load and initialize dumpers, give feedback -// to user. -void CdbDumperHelper::load(DebuggerManager *manager) +void CdbDumperHelper::moduleLoadHook(const QString &module, HANDLE debuggeeHandle) { - enum Result { Failed, Succeeded, NoQtApp }; - - if (m_state != NotLoaded) - return; - manager->showStatusMessage(QCoreApplication::translate("CdbDumperHelper", "Loading dumpers..."), 10000); - QString message; - Result result = Failed; - do { - // Do we have Qt and are we already loaded by accident? - QStringList modules; - if (!getModuleNameList(m_cif->debugSymbols, &modules, &message)) - break; - if (modules.filter(QLatin1String(qtCoreModuleNameC)).isEmpty()) { - message = QCoreApplication::translate("CdbDumperHelper", "The debugger does not appear to be Qt application."); - result = NoQtApp; - } - // Make sure the dumper lib is loaded. - if (modules.filter(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive).isEmpty()) { - // Try to load - if (!debuggeeLoadLibrary(m_access, m_cif, m_library, &message)) { - break; + if (loadDebug > 1) + qDebug() << "moduleLoadHook" << module << m_state << debuggeeHandle; + switch (m_state) { + case Disabled: + case Initialized: + break; + case NotLoaded: + // Try an inject load as soon as a Qt lib is loaded. + // for the thread to finish as this would lock up. + if (m_tryInjectLoad && module.contains(QLatin1String("Qt"), Qt::CaseInsensitive)) { + // Also shows up in the log window. + m_manager->showStatusMessage(msgLoading(m_library, true), 10000); + QString errorMessage; + SharedLibraryInjector sh(GetProcessId(debuggeeHandle)); + if (sh.remoteInject(m_library, false, &errorMessage)) { + m_state = InjectLoading; + } else { + m_state = InjectLoadFailed; + // Ok, try call loading... + m_access->showDebuggerOutput(m_messagePrefix, msgLoadFailed(m_library, true, errorMessage)); } - } else { - m_access->showDebuggerOutput(m_messagePrefix, QCoreApplication::translate("CdbDumperHelper", "The dumper module appears to be already loaded.")); } - // Resolve symbols and do call to get types - if (!resolveSymbols(&message)) - break; - if (!getKnownTypes(&message)) - break; - message = QCoreApplication::translate("CdbDumperHelper", "Dumper library '%1' loaded.").arg(m_library); - result = Succeeded; - } while (false); - // eval state and notify user - switch (result) { - case Failed: - message = QCoreApplication::translate("CdbDumperHelper", "The dumper library '%1' could not be loaded:\n%2").arg(m_library, message); - m_access->showDebuggerOutput(m_messagePrefix, message); - m_access->showQtDumperLibraryWarning(message); - m_state = Disabled; - break; - case Succeeded: - m_access->showDebuggerOutput(m_messagePrefix, message); - m_access->showDebuggerOutput(m_messagePrefix, m_helper.toString()); - m_state = Loaded; break; - case NoQtApp: - m_access->showDebuggerOutput(m_messagePrefix, message); - m_state = Disabled; + case InjectLoading: + // check if gdbmacros.dll loaded + if (module.contains(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive)) { + m_state = Loaded; + m_access->showDebuggerOutput(m_messagePrefix, msgLoadSucceeded(m_library, true)); + } break; - } + } +} + +// Try to load dumpers by triggering calls using the debugger +CdbDumperHelper::CallLoadResult CdbDumperHelper::initCallLoad(QString *errorMessage) +{ if (loadDebug) - qDebug() << Q_FUNC_INFO << "\n<" << result << '>' << m_state << message; + qDebug() << Q_FUNC_INFO; + // Do we have Qt and are we already loaded by accident? + QStringList modules; + if (!getModuleNameList(m_cif->debugSymbols, &modules, errorMessage)) + return CallLoadError; + // Are we already loaded by some accident? + if (!modules.filter(QLatin1String(dumperModuleNameC), Qt::CaseInsensitive).isEmpty()) + return CallLoadAlreadyLoaded; + // Is that Qt application at all? + if (modules.filter(QLatin1String(qtCoreModuleNameC), Qt::CaseInsensitive).isEmpty()) + return CallLoadNoQtApp; + // Try to load + if (!debuggeeLoadLibrary(m_access, m_cif, m_library, errorMessage)) + return CallLoadError; + return CallLoadOk; +} + +bool CdbDumperHelper::ensureInitialized(QString *errorMessage) +{ + if (loadDebug) + qDebug() << Q_FUNC_INFO << '\n' << m_state; + + switch (m_state) { + case Disabled: + *errorMessage = QLatin1String("Internal error, attempt to call disabled dumper"); + return false; + case Initialized: + return true; + // Injection load failed or disabled: Try a call load. + case NotLoaded: + case InjectLoading: + case InjectLoadFailed: + // Also shows up in the log window. + m_manager->showStatusMessage(msgLoading(m_library, false), 10000); + switch (initCallLoad(errorMessage)) { + case CallLoadOk: + case CallLoadAlreadyLoaded: + m_access->showDebuggerOutput(m_messagePrefix, msgLoadSucceeded(m_library, false)); + m_state = Loaded; + break; + case CallLoadError: + *errorMessage = msgLoadFailed(m_library, false, *errorMessage); + m_access->showDebuggerOutput(m_messagePrefix, *errorMessage); + m_access->showQtDumperLibraryWarning(*errorMessage); + return false; + case CallLoadNoQtApp: + m_access->showDebuggerOutput(m_messagePrefix, QCoreApplication::translate("CdbDumperHelper", "The debuggee does not appear to be Qt application.")); + disable(); + return true; + } + break; + case Loaded: // Injection load succeeded, ideally + break; + } + // Perform remaining initialization + m_manager->showStatusMessage(QCoreApplication::translate("CdbDumperHelper", "Initializing dumpers..."), 10000); + const bool ok = initResolveSymbols(errorMessage) && initKnownTypes(errorMessage); + if (ok) { + m_access->showDebuggerOutput(m_messagePrefix, QCoreApplication::translate("CdbDumperHelper", "Custom dumper library initialized.")); + m_access->showDebuggerOutput(m_messagePrefix, m_helper.toString()); + m_state = Initialized; + } else { + disable(); + *errorMessage = QCoreApplication::translate("CdbDumperHelper", "The custom dumper library could not be initialized: %1").arg(*errorMessage); + m_access->showDebuggerOutput(m_messagePrefix, *errorMessage); + m_access->showQtDumperLibraryWarning(*errorMessage); + } + return ok; } // Retrieve address and optionally size of a symbol. @@ -275,7 +406,7 @@ static inline bool getSymbolAddress(CIDebugSymbols *sg, return true; } -bool CdbDumperHelper::resolveSymbols(QString *errorMessage) +bool CdbDumperHelper::initResolveSymbols(QString *errorMessage) { // Resolve the symbols we need (potentially namespaced). // There is a 'qDumpInBuffer' in QtCore as well. @@ -302,7 +433,8 @@ bool CdbDumperHelper::resolveSymbols(QString *errorMessage) return true; } -bool CdbDumperHelper::getKnownTypes(QString *errorMessage) +// Call query protocol to retrieve known types and sizes +bool CdbDumperHelper::initKnownTypes(QString *errorMessage) { QByteArray output; QString callCmd; @@ -398,15 +530,29 @@ bool CdbDumperHelper::callDumper(const QString &callCmd, const QByteArray &inBuf return true; } +static inline QString msgDumpFailed(const WatchData &wd, const QString *why) +{ + return QString::fromLatin1("Unable to dump '%1' (%2): %3").arg(wd.name, wd.type, *why); +} + CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool dumpChildren, int source, QList<WatchData> *result, QString *errorMessage) { // Check failure cache and supported types - if (m_failedTypes.contains(wd.type)) + if (m_state == Disabled || m_failedTypes.contains(wd.type)) return DumpNotHandled; + + // Ensure types are parsed and known. + if (!ensureInitialized(errorMessage)) { + *errorMessage = msgDumpFailed(wd, errorMessage); + m_access->showDebuggerOutput(m_messagePrefix, *errorMessage); + return DumpError; + } + + // Known type? const QtDumperHelper::TypeData td = m_helper.typeData(wd.type); if (loadDebug) - qDebug() << wd.type << td; + qDebug() << "dumpType" << wd.type << td; if (td.type == QtDumperHelper::UnknownType) return DumpNotHandled; @@ -415,6 +561,7 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool "Querying dumpers for '%1'/'%2' (%3)"). arg(wd.name, wd.exp, wd.type); m_access->showDebuggerOutput(m_messagePrefix, message); + const DumpExecuteResult der = executeDump(wd, td, dumpChildren, source, result, errorMessage); if (der == DumpExecuteOk) return DumpOk; @@ -424,7 +571,7 @@ CdbDumperHelper::DumpResult CdbDumperHelper::dumpType(const WatchData &wd, bool if (der == DumpExecuteSizeFailed) m_failedTypes.push_back(wd.type); // log error - *errorMessage = QString::fromLatin1("Unable to dump '%1' (%2): %3").arg(wd.name, wd.type, *errorMessage); + *errorMessage = *errorMessage = msgDumpFailed(wd, errorMessage); m_access->showDebuggerOutput(m_messagePrefix, *errorMessage); return DumpError; } diff --git a/src/plugins/debugger/cdb/cdbdumperhelper.h b/src/plugins/debugger/cdb/cdbdumperhelper.h index 9500d4d4e01..8ff890630d1 100644 --- a/src/plugins/debugger/cdb/cdbdumperhelper.h +++ b/src/plugins/debugger/cdb/cdbdumperhelper.h @@ -46,54 +46,64 @@ class DebuggerManager; /* For code clarity, all the stuff related to custom dumpers goes here. * "Custom dumper" is a library compiled against the current * Qt containing functions to evaluate values of Qt classes - * (such as QString, taking pointers to their addresses). - * The library must be loaded into the debuggee. - * Loading the dumpers requires making the debuggee call functions - * (LoadLibrary() and the dumper functions). This only works if the - * debuggee is in a 'well-defined' breakpoint state (such as at 'main()'). - * Calling the load functions from an IDebugEvent callback causes - * WaitForEvent() to fail with unknown errors. Calling the load functions from an - * non 'well-defined' (arbitrary) breakpoint state will cause LoadLibrary - * to trigger an access violations. - * Currently, we call the load function when stopping at 'main()' for which - * we set a temporary break point if the user does not want to stop there. */ + * (such as QString), taking pointers to their addresses. + * The dumper functions produce formatted string output which is + * converted into WatchData items with the help of QtDumperHelper. + * + * Usage: When launching the debugger, call reset() with path to dumpers + * and enabled flag. From the module load event callback, call + * moduleLoadHook() to initialize. + * dumpType() is the main query function to obtain a list of WatchData from + * WatchData item produced by the smbol context. + * Call disable(), should the debuggee crash (as performing debuggee + * calls is no longer possible, then).*/ class CdbDumperHelper { Q_DISABLE_COPY(CdbDumperHelper) public: enum State { - Disabled, + Disabled, // Disabled or failed NotLoaded, + InjectLoadFailed, + InjectLoading, Loaded, - Failed + Initialized, // List of types, etc. retrieved }; - explicit CdbDumperHelper(IDebuggerManagerAccessForEngines *access, + explicit CdbDumperHelper(DebuggerManager *manager, CdbComInterfaces *cif); ~CdbDumperHelper(); - State state() const { return m_state; } - operator bool() const { return m_state == Loaded; } + State state() const { return m_state; } + bool isEnabled() const { return m_state != Disabled; } + + // Disable in case of a debuggee crash. + void disable(); // Call before starting the debugger void reset(const QString &library, bool enabled); - // Call in a temporary breakpoint state to actually load. - void load(DebuggerManager *manager); + // Call from the module load callback to perform initialization. + void moduleLoadHook(const QString &module, HANDLE debuggeeHandle); + // Dump a WatchData item. enum DumpResult { DumpNotHandled, DumpOk, DumpError }; DumpResult dumpType(const WatchData &d, bool dumpChildren, int source, QList<WatchData> *result, QString *errorMessage); - // bool handlesType(const QString &typeName) const; - inline CdbComInterfaces *comInterfaces() const { return m_cif; } private: + enum CallLoadResult { CallLoadOk, CallLoadError, CallLoadNoQtApp, CallLoadAlreadyLoaded }; + void clearBuffer(); - bool resolveSymbols(QString *errorMessage); - bool getKnownTypes(QString *errorMessage); + + bool ensureInitialized(QString *errorMessage); + CallLoadResult initCallLoad(QString *errorMessage); + bool initResolveSymbols(QString *errorMessage); + bool initKnownTypes(QString *errorMessage); + bool getTypeSize(const QString &typeName, int *size, QString *errorMessage); bool runTypeSizeQuery(const QString &typeName, int *size, QString *errorMessage); bool callDumper(const QString &call, const QByteArray &inBuffer, const char **outputPtr, QString *errorMessage); @@ -105,8 +115,10 @@ private: static bool writeToDebuggee(CIDebugDataSpaces *ds, const QByteArray &buffer, quint64 address, QString *errorMessage); + const bool m_tryInjectLoad; const QString m_messagePrefix; State m_state; + DebuggerManager *m_manager; IDebuggerManagerAccessForEngines *m_access; CdbComInterfaces *m_cif; diff --git a/src/plugins/debugger/cdb/cdbstackframecontext.cpp b/src/plugins/debugger/cdb/cdbstackframecontext.cpp index be04856ccc0..fd59bd0fa6b 100644 --- a/src/plugins/debugger/cdb/cdbstackframecontext.cpp +++ b/src/plugins/debugger/cdb/cdbstackframecontext.cpp @@ -117,8 +117,7 @@ WatchHandlerSorterInserter &WatchHandlerSorterInserter::operator=(WatchData wd) // -----------CdbStackFrameContext CdbStackFrameContext::CdbStackFrameContext(const QSharedPointer<CdbDumperHelper> &dumper, CdbSymbolGroupContext *symbolContext) : - m_useDumpers(dumper->state() == CdbDumperHelper::Loaded - && theDebuggerBoolSetting(UseDebuggingHelpers)), + m_useDumpers(dumper->isEnabled() && theDebuggerBoolSetting(UseDebuggingHelpers)), m_dumper(dumper), m_symbolContext(symbolContext) { diff --git a/src/plugins/debugger/win/sharedlibraryinjector.cpp b/src/plugins/debugger/win/sharedlibraryinjector.cpp new file mode 100644 index 00000000000..628263d3e15 --- /dev/null +++ b/src/plugins/debugger/win/sharedlibraryinjector.cpp @@ -0,0 +1,479 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at [email protected]. +** +**************************************************************************/ + +// +// WinANSI - An ANSI implementation for the modern Windows +// +// Copyright (C) 2008- Marius Storm-Olsen <[email protected]> +// +// Based on work by Robert Kuster in article +// http://software.rkuster.com/articles/winspy.htm +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// ------------------------------------------------------------------------------------------------- + +#include "sharedlibraryinjector.h" +#include <utils/winutils.h> + +#include <QtCore/QDebug> + +enum { debug = 0 }; + +static QString msgFuncFailed(const char *f, unsigned long error) +{ + return QString::fromLatin1("%1 failed: %2").arg(QLatin1String(f), Core::Utils::winErrorMessage(error)); +} + +// Resolve a symbol from a library handle +template <class SymbolType> +inline bool resolveSymbol(const char *libraryName, HMODULE libraryHandle, const char *symbolName, SymbolType *s, QString *errorMessage) +{ + *s = 0; + void *vs = ::GetProcAddress(libraryHandle, symbolName); + if (vs == 0) { + *errorMessage = QString::fromLatin1("Unable to resolve '%2' in '%1'.").arg(QString::fromAscii(symbolName), QString::fromAscii(libraryName)); + return false; + } + *s = reinterpret_cast<SymbolType>(vs); + return true; +} + +// Resolve a symbol from a library +template <class SymbolType> +inline bool resolveSymbol(const char *library, const char *symbolName, SymbolType *s, QString *errorMessage) +{ + *s = 0; + const HMODULE hm = ::GetModuleHandleA(library); + if (hm == NULL) { + *errorMessage = QString::fromLatin1("Module '%1' does not exist.").arg(QString::fromAscii(library)); + return false; + } + return resolveSymbol(library, hm , symbolName, s, errorMessage); +} + + +namespace Debugger { +namespace Internal { + +SharedLibraryInjector::SharedLibraryInjector(unsigned long processId, + unsigned long threadId) : + m_processId(processId), + m_threadId(threadId), + m_hasEscalatedPrivileges(false) +{ +} + +SharedLibraryInjector::~SharedLibraryInjector() +{ +} + +void SharedLibraryInjector::setPid(unsigned long pid) +{ + m_processId = pid; +} + +void SharedLibraryInjector::setThreadId(unsigned long tid) +{ + m_threadId = tid; +} + +void SharedLibraryInjector::setModulePath(const QString &modulePath) +{ + m_modulePath = modulePath; +} + +bool SharedLibraryInjector::remoteInject(const QString &modulePath, + bool waitForThread, QString *errorMessage) +{ + setModulePath(modulePath); + return doRemoteInjection(m_processId, NULL, m_modulePath, waitForThread, errorMessage); +} + +bool SharedLibraryInjector::stubInject(const QString &modulePath, unsigned long entryPoint, QString *errorMessage) +{ + setModulePath(modulePath); + return doStubInjection(m_processId, m_modulePath, entryPoint, errorMessage); +} + +bool SharedLibraryInjector::unload(HMODULE hFreeModule, QString *errorMessage) +{ + return doRemoteInjection(m_processId, hFreeModule, QString(), true, errorMessage); // Always use remote thread to unload +} + +bool SharedLibraryInjector::unload(const QString &modulePath, QString *errorMessage) +{ + const HMODULE hMod = modulePath.isEmpty() ? + findModuleHandle(m_modulePath, errorMessage) : + findModuleHandle(modulePath, errorMessage); + if (!hMod) + return false; + + return doRemoteInjection(m_processId, hMod, NULL, true, errorMessage); // Always use remote thread to unload +} + +bool SharedLibraryInjector::hasLoaded(const QString &modulePath) +{ + QString errorMessage; + return findModuleHandle(modulePath.isEmpty() ? m_modulePath : modulePath, &errorMessage) != NULL; +} + +QString SharedLibraryInjector::findModule(const QString &moduleName) +{ + const TCHAR *moduleNameC = moduleName.utf16(); + if (GetFileAttributesW(moduleNameC) != INVALID_FILE_ATTRIBUTES) + return moduleName; + + TCHAR testpathC[MAX_PATH]; + // Check application path first + GetModuleFileNameW(NULL, testpathC, MAX_PATH); + QString testPath = QString::fromUtf16(testpathC); + const int lastSlash = testPath.lastIndexOf(QLatin1Char('\\')); + if (lastSlash != -1) + testPath.truncate(lastSlash + 1); + testPath += moduleName; + if (GetFileAttributesW(testPath.utf16()) != INVALID_FILE_ATTRIBUTES) + return testPath; + // Path Search + if (SearchPathW(NULL, moduleName.utf16(), NULL, sizeof(testpathC)/2, testpathC, NULL)) + return QString::fromUtf16(testpathC); + // Last chance, if the module has already been loaded in this process, then use that path + const HMODULE loadedModule = GetModuleHandleW(moduleName.utf16()); + if (loadedModule) { + GetModuleFileNameW(loadedModule, testpathC, sizeof(testpathC)); + if (GetFileAttributes(testpathC) != INVALID_FILE_ATTRIBUTES) + return QString::fromUtf16(testpathC); + } + return QString(); +} + +unsigned long SharedLibraryInjector::getModuleEntryPoint(const QString &moduleName) +{ + // If file doesn't exist, just treat it like we cannot figure out the entry point + if (moduleName.isEmpty() || GetFileAttributesW(moduleName.utf16()) == INVALID_FILE_ATTRIBUTES) + return 0; + + // Read the first 1K of data from the file + unsigned char peData[1024]; + unsigned long peDataSize = 0; + const HANDLE hFile = CreateFileW(moduleName.utf16(), FILE_READ_DATA, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (hFile == INVALID_HANDLE_VALUE + || !ReadFile(hFile, peData, sizeof(peData), &peDataSize, NULL)) + return 0; + CloseHandle(hFile); + + // Now we check to see if there is an optional header we can read + IMAGE_DOS_HEADER *dosHeader = reinterpret_cast<IMAGE_DOS_HEADER *>(peData); + if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE // DOS signature is incorrect, or + || dosHeader->e_lfanew == 0) // executable's PE data contains no offset to New EXE header + return 0; + + IMAGE_NT_HEADERS *ntHeaders = (IMAGE_NT_HEADERS *)(peData + dosHeader->e_lfanew); + if (ntHeaders->Signature != IMAGE_NT_SIGNATURE // NT signature is incorrect, or + || !ntHeaders->OptionalHeader.ImageBase // ImageBase or EntryPoint addresses are incorrect + || !ntHeaders->OptionalHeader.AddressOfEntryPoint) + return 0; + + return ntHeaders->OptionalHeader.ImageBase + + ntHeaders->OptionalHeader.AddressOfEntryPoint; +} + +bool SharedLibraryInjector::escalatePrivileges(QString *errorMessage) +{ + if (debug) + qDebug() << Q_FUNC_INFO << m_hasEscalatedPrivileges; + + if (m_hasEscalatedPrivileges) + return true; + + bool success = false; + HANDLE hToken = 0; + do { + TOKEN_PRIVILEGES Debug_Privileges; + if (!LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &Debug_Privileges.Privileges[0].Luid)) { + *errorMessage = msgFuncFailed("LookupPrivilegeValue", GetLastError()); + break; + } + + Debug_Privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; // set to enable privilege + Debug_Privileges.PrivilegeCount = 1; // working with only 1 + + if (!OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) { + *errorMessage = msgFuncFailed("OpenProcessToken", GetLastError()); + break; + } + if (!AdjustTokenPrivileges(hToken, FALSE, &Debug_Privileges, 0, NULL, NULL)) { + *errorMessage = msgFuncFailed("AdjustTokenPrivileges", GetLastError()); + break; + + } + success = true; + } while (false); + + if (hToken) + CloseHandle (hToken); + + m_hasEscalatedPrivileges = success; + return success; +} + +static void *writeDataToProcess(HANDLE process, void *data, unsigned size, QString *errorMessage) +{ + void *memory = VirtualAllocEx(process, NULL, size, MEM_COMMIT, PAGE_READWRITE); + if (!memory) { + *errorMessage = msgFuncFailed("VirtualAllocEx", GetLastError()); + return 0; + } + if (!WriteProcessMemory(process, memory, data, size, NULL)) { + *errorMessage = msgFuncFailed("WriteProcessMemory", GetLastError()); + return 0; + } + return memory; +} + +static void *writeUtf16StringToProcess(HANDLE process, const QString &what, QString *errorMessage) +{ + QByteArray whatData = QByteArray(reinterpret_cast<const char*>(what.utf16()), what.size() * 2); + whatData += '\0'; + whatData += '\0'; + return writeDataToProcess(process, whatData.data(), whatData.size(), errorMessage); +} + +bool SharedLibraryInjector::doStubInjection(unsigned long pid, + const QString &modulePath, + unsigned long entryPoint, + QString *errorMessage) +{ + if (debug) + qDebug() << pid << modulePath << entryPoint; + if (modulePath.isEmpty()) + return false; + + if (!escalatePrivileges(errorMessage)) + return false; + +#if (defined(WIN64) || defined(_WIN64) || defined(__WIN64__)) + *errorMessage = QLatin1String("Not implemented for this architecture."); + return false; +#else + byte stubCode[] = { + 0x68, 0, 0, 0, 0, // push 0x00000000 - Placeholder for the entrypoint address + 0x9C, // pushfd - Save the flags and registers + 0x60, // pushad + 0x68, 0, 0, 0, 0, // push 0x00000000 - Placeholder for the string address + 0xB8, 0, 0, 0, 0, // mov eax, 0x00000000 - Placeholder for the LoadLibrary address + 0xFF, 0xD0, // call eax - Call LoadLibrary with string address + 0x61, // popad - Restore flags and registry + 0x9D, // popfd + 0xC3 // retn - Return (pops the entrypoint, and executes) + }; + + const HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + if (!hProcess) + return false; + + const HANDLE hThread = OpenThread(THREAD_GET_CONTEXT | THREAD_SET_CONTEXT, false, m_threadId); + if (!hThread) { + CloseHandle(hProcess); + return false; + } + + // Get address of LoadLibraryW in kernel32.dll + unsigned long funcPtr; + if (!resolveSymbol("kernel32.dll", "LoadLibraryW", &funcPtr, errorMessage)) + return false; + + // Allocate and write DLL path to target process' memory + // 0-terminated Utf16 string. + void *dllString = writeUtf16StringToProcess(hProcess, modulePath, errorMessage); + if (!dllString) + return false; + + // Allocate memory in target process, for the stubCode which will load the module, and execute + // the process' entry-point + const unsigned long stubLen = sizeof(stubCode); + // Modify the stubCode to have the proper entry-point, DLL module string, and function pointer + *(unsigned long*)(stubCode+1) = entryPoint; + *(unsigned long*)(stubCode+8) = reinterpret_cast<unsigned long>(dllString); + *(unsigned long*)(stubCode+13) = funcPtr; + + // If we cannot write the stubCode into the process, we simply bail out, and the process will + // continues execution as normal + void *stub = writeDataToProcess(hProcess, stubCode, stubLen, errorMessage); + if (!stub) + return false; + // Set the process' main thread to start executing from the stubCode address which we've just + // allocated in the process' memory.. If we cannot do that, bail out + CONTEXT ctx; + ctx.ContextFlags = CONTEXT_CONTROL; + if(!GetThreadContext(hThread, &ctx)) + return false; + ctx.Eip = reinterpret_cast<DWORD>(stub); + ctx.ContextFlags = CONTEXT_CONTROL; + if(!SetThreadContext(hThread, &ctx)) + return false; + + CloseHandle(hProcess); + CloseHandle(hThread); + return true; +#endif +} + +// ------------------------------------------------------------------------------------------------- + +bool SharedLibraryInjector::doRemoteInjection(unsigned long pid, + HMODULE hFreeModule, + const QString &modulePath, + bool waitForThread, + QString *errorMessage) +{ + if (debug) + qDebug() << Q_FUNC_INFO << '\n' << hFreeModule << pid << waitForThread<< modulePath; + + if (!hFreeModule && modulePath.isEmpty()) + return false; + + escalatePrivileges(errorMessage); + + const HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + if (hProcess == NULL) { + *errorMessage = msgFuncFailed("OpenProcess", GetLastError()); + qDebug() << "Failed to open process PID %d with all access\n" << pid; + return false; + } + + void *pszLibFileRemote = 0; + HANDLE hThread = 0; + // Call "FreeLibrary(hFreeModule)" to unload + if (hFreeModule) { + PTHREAD_START_ROUTINE pfnThreadRtn; + if (!resolveSymbol("Kernel32", "FreeLibrary", &pfnThreadRtn, errorMessage)) + return false; + hThread = ::CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn, hFreeModule, 0, NULL); + } else { + pszLibFileRemote = writeUtf16StringToProcess(hProcess, modulePath, errorMessage); + if (!pszLibFileRemote) + return false; + // Loadlibrary routine + PTHREAD_START_ROUTINE pfnThreadRtn; + if (!resolveSymbol("Kernel32", "LoadLibraryW", &pfnThreadRtn, errorMessage)) + return false; + hThread = ::CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn, pszLibFileRemote, 0, NULL); + } + if (hThread == NULL) { + *errorMessage = msgFuncFailed("CreateRemoteThread", GetLastError()); + return false; + } + + if (!SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST)) { + *errorMessage = msgFuncFailed("SetThreadPriority", GetLastError()); + return false; + } + + if (waitForThread) { + if (::WaitForSingleObject(hThread, 20000) != WAIT_OBJECT_0) { + *errorMessage = QString::fromLatin1("WaitForSingleObject timeout"); + ::CloseHandle(hThread); + ::CloseHandle(hProcess); + return false; + } + + if (pszLibFileRemote) + ::VirtualFreeEx(hProcess, pszLibFileRemote, 0, MEM_RELEASE); + } + + if (hThread) + ::CloseHandle(hThread); + + if (hProcess) + ::CloseHandle(hProcess); + if (debug) + qDebug() << "success" << Q_FUNC_INFO; + return true; +} + +typedef BOOL (WINAPI *PFNENUMPROCESSMODULES) (HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded); +typedef DWORD (WINAPI *PFNGETMODULEFILENAMEEXW) (HANDLE hProcess, HMODULE hModule, LPTSTR lpFilename, DWORD nSize); + +// We will require this function to get a module handle of our original module +HMODULE SharedLibraryInjector::findModuleHandle(const QString &modulePath, QString *errorMessage) +{ + if (debug) + qDebug() << Q_FUNC_INFO << modulePath; + + if (!escalatePrivileges(errorMessage)) + return 0; + + HMODULE hMods[1024]; + DWORD cbNeeded; + + HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, m_processId); + if (hProcess == NULL) + return 0; + + const char *psAPI_Lib = "PSAPI.DLL"; + HMODULE m_hModPSAPI = ::LoadLibraryA(psAPI_Lib); + PFNENUMPROCESSMODULES m_pfnEnumProcessModules; + PFNGETMODULEFILENAMEEXW m_pfnGetModuleFileNameExW; + if (!resolveSymbol(psAPI_Lib, m_hModPSAPI, "EnumProcessModules", &m_pfnEnumProcessModules, errorMessage) + || !resolveSymbol(psAPI_Lib, m_hModPSAPI, "GetModuleFileNameExW", &m_pfnGetModuleFileNameExW, errorMessage)) + return false; + + if(m_pfnEnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) { + const unsigned count = cbNeeded / sizeof(HMODULE); + for (unsigned i = 0; i < count; i++) { + TCHAR szModName[MAX_PATH]; + if (m_pfnGetModuleFileNameExW(hProcess, hMods[i], szModName, sizeof(szModName))) { + if (QString::fromUtf16(szModName) == modulePath) { + ::FreeLibrary(m_hModPSAPI); + ::CloseHandle(hProcess); + return hMods[i]; + } + } + } + } + + ::CloseHandle(hProcess); + return 0; +} + +} // namespace Internal +} // namespace Debugger diff --git a/src/plugins/debugger/win/sharedlibraryinjector.h b/src/plugins/debugger/win/sharedlibraryinjector.h new file mode 100644 index 00000000000..f1356b03f19 --- /dev/null +++ b/src/plugins/debugger/win/sharedlibraryinjector.h @@ -0,0 +1,109 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information ([email protected]) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at [email protected]. +** +**************************************************************************/ + +// +// WinANSI - An ANSI implementation for the modern Windows +// +// Copyright (C) 2008- Marius Storm-Olsen <[email protected]> +// +// Based on work by Robert Kuster in article +// http://software.rkuster.com/articles/winspy.htm +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// ------------------------------------------------------------------------------------------------- + +#ifndef SHAREDLIBRARYINJECTOR_H +#define SHAREDLIBRARYINJECTOR_H + +#include <windows.h> +#include <QtCore/QString> + +namespace Debugger { +namespace Internal { + +/* SharedLibraryInjector: Injects a DLL into a remote process. + * Escalates the calling process rights. */ +class SharedLibraryInjector { + Q_DISABLE_COPY(SharedLibraryInjector) +public: + + explicit SharedLibraryInjector(unsigned long remotePid, unsigned long remoteThreadId = 0); + ~SharedLibraryInjector(); + + void setModulePath(const QString &modulePath); + bool hasLoaded(const QString &modulePath = QString()); + + // Remote injection, to be used for running processes + bool remoteInject(const QString &modulePath, bool waitForThread, QString *errorMessage); + + // Stub injection, to be used before execution starts + bool stubInject(const QString &modulePath, unsigned long entryPoint, QString *errorMessage); + + bool unload(const QString &modulePath /* = QString()*/, QString *errorMessage); + bool unload(HMODULE hFreeModule, QString *errorMessage); + + void setPid(unsigned long pid); + void setThreadId(unsigned long tid); + + static QString findModule(const QString &moduleName); + static unsigned long getModuleEntryPoint(const QString &moduleName); + +private: + bool escalatePrivileges(QString *errorMessage); + bool doRemoteInjection(unsigned long pid, HMODULE hFreeModule, + const QString &modulePath, bool waitForThread, + QString *errorMessage); + bool doStubInjection(unsigned long pid, const QString &modulePath, + unsigned long entryPoint, QString *errorMessage); + + HMODULE findModuleHandle(const QString &modulePath, QString *errorMessage); + + unsigned long m_processId; + unsigned long m_threadId; + QString m_modulePath; + bool m_hasEscalatedPrivileges; +}; + +} // namespace Internal +} // namespace Debugger + +#endif // SHAREDLIBRARYINJECTOR_H diff --git a/src/plugins/debugger/win/win.pri b/src/plugins/debugger/win/win.pri index b1edeac9299..a0b1ef45cbb 100644 --- a/src/plugins/debugger/win/win.pri +++ b/src/plugins/debugger/win/win.pri @@ -1,5 +1,11 @@ INCLUDEPATH+=$$PWD SOURCES += $$PWD/peutils.cpp \ - $$PWD/dbgwinutils.cpp + $$PWD/dbgwinutils.cpp \ + $$PWD/sharedlibraryinjector.cpp + HEADERS += $$PWD/peutils.h \ - $$PWD/dbgwinutils.h + $$PWD/dbgwinutils.h \ + $$PWD/sharedlibraryinjector.h + +# For the Privilege manipulation functions in sharedlibraryinjector.cpp +LIBS += advapi32.lib |