microsoft / ConcordExtensibilitySamples

Visual Studio Debug Engine Extensibility Samples
Other
122 stars 50 forks source link

Is is actually possible to perform function evaluation in IDkmRuntimeBreakpointNotification? #61

Open WheretIB opened 4 years ago

WheretIB commented 4 years ago

I am trying to evaluate a C++ expression with a function call when the runtime breakpoint hits, but the evaluation never completes.

In a debug monitor component, I capture the stack frame data and send it to an IDE component:

void IDkmRuntimeBreakpointNotification.OnRuntimeBreakpoint(DkmRuntimeBreakpoint runtimeBreakpoint, DkmThread thread, bool hasException, DkmEventDescriptorS eventDescriptor)
{
    var process = thread.Process;

    thread.GetCurrentFrameInfo(out ulong retAddr, out ulong frameBase, out ulong vframe);

    var data = new SupportBreakpointHitMessage
    {
        breakpointId = runtimeBreakpoint.UniqueId,
        threadId = thread.UniqueId,
        retAddr = retAddr,
        frameBase = frameBase,
        vframe = vframe
    };

    DkmCustomMessage.Create(process.Connection, process, MessageToLocal.guid, MessageToLocal.supportBreakpointHitNotification, data.Encode(), null).SendHigher();
}

in the IDE component (tested levels 1999000 and 9995000) IDkmCustomMessageCallbackReceiver.SendHigher I try to execute an expression:

const int CV_ALLREG_VFRAME = 0x00007536;
var vFrameRegister = DkmUnwoundRegister.Create(CV_ALLREG_VFRAME, new ReadOnlyCollection<byte>(BitConverter.GetBytes(data.vframe)));
var registers = thread.GetCurrentRegisters(new[] { vFrameRegister });
var instructionAddress = process.CreateNativeInstructionAddress(registers.GetInstructionPointer());
DkmStackWalkFrame frame = DkmStackWalkFrame.Create(thread, instructionAddress, data.frameBase, 0, DkmStackWalkFrameFlags.None, null, registers, null);

var inspectionSession = DkmInspectionSession.Create(process, null);

string expression = $"sethook(0x{stateAddress:x}, 0x{processData.helperHookFunctionAddress_5_1:x}, 7, 0)";

var compilerId = new DkmCompilerId(DkmVendorId.Microsoft, DkmLanguageId.Cpp);
var language = DkmLanguage.Create("C++", compilerId);
var languageExpression = DkmLanguageExpression.Create(language, DkmEvaluationFlags.None, expression, null);
var inspectionContext = DkmInspectionContext.Create(inspectionSession, frame.RuntimeInstance, thread, 2000, DkmEvaluationFlags.None, DkmFuncEvalFlags.None, 10, language, null);
var workList = DkmWorkList.Create(null);

DkmSuccessEvaluationResult result = null;

inspectionContext.EvaluateExpression(workList, languageExpression, frame, res =>
{
    if (res.ErrorCode == 0)
    {
        result = res.ResultObject as DkmSuccessEvaluationResult;

        res.ResultObject.Close();
    }
});

workList.Execute();

workList.Execute(); call never returns. If the same evaluation is performed in a IDkmCallStackFilter.FilterNextFrame there are no issues with it. Expressions without function evaluations can also be successfully performed with this code in IDkmCustomMessageCallbackReceiver.SendHigher.

Documentation for IDkmRuntimeBreakpointNotification.OnRuntimeBreakpoint tells that:

When this
notification is called, the target process is stopped and implementers are
able to either inspect the process or cause it to execute in a controlled
manner (slip, func-eval).

Is this really true?