diff options
author | Friedemann Kleint <[email protected]> | 2009-05-05 09:52:24 +0200 |
---|---|---|
committer | Friedemann Kleint <[email protected]> | 2009-05-05 09:52:24 +0200 |
commit | eb63bebc3ee0c8ec958e4e1b628aca4e2f388220 (patch) | |
tree | 36bbfe657907d0bb6ee24338134977ff55558592 | |
parent | aa6c55260fe2823f549ebcab90f41af46c3926db (diff) |
Introduced shared library injection as dumper load method.
Try to load custom dumpers using shared library injection
first as it is faster and does not rely on the symbol
for LoadLibrary being known to the debugger. Keep
call loading as fallback. Move the rest of the initalization
to the first dumper call, removing the need to break at main().
-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 |