草庐IT

windows - 实现 DebugExtensionProvideValue 会破坏 WinDbg 内部状态?

coder 2024-06-06 原文

我正在实现 DebugExtensionProvideValue在我的扩展中,这样我就可以提供自定义伪寄存器。它在 CDB 中完美运行,最初在 WinDbg 中运行良好,但在停止调试并打开一个新的可执行文件后,发生了一些事情,WinDbg 最终处于一种奇怪的不可用状态。

当您触发问题时,WinDbg 将此消息打印到命令窗口:

Unable to deliver callback, 3131

发生这种情况后,WinDbg 似乎会在命令窗口中打印两次所有输出!

我的扩展代码很简单:

EXTERN_C HRESULT CALLBACK DebugExtensionProvideValue(PDEBUG_CLIENT Client, ULONG Flags, IN PCWSTR Name, OUT PULONG64 Value, OUT PULONG64 TypeModBase, OUT PULONG TypeId, OUT PULONG TypeFlags)
{
    HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
    if (!Name || !Value || !TypeFlags) 
    {
        hr = E_INVALIDARG;
    }
    else if (0 == lstrcmpiW(Name, L"$$test"))
    {
        *Value = 0xDeadCafeBabeBeefULL;
        *TypeFlags = DEBUG_EXT_PVTYPE_IS_VALUE;
        if (TypeId) *TypeId = 0; // Where are these types defined?
        if (TypeModBase) *TypeModBase = 0;
        hr = S_OK;
    }
#if 0 // <-- ** Setting this to != 0 fixes the problem **
    Client->Release(); // This does not feel right but it does seem to be required!
#endif
    return hr;
}

EXTERN_C HRESULT CALLBACK DebugExtensionQueryValueNames(PDEBUG_CLIENT Client, ULONG Flags, OUT PWSTR Buffer, ULONG BufferChars, OUT PULONG BufferNeeded)
{
    static const WCHAR pseregs[] = L"$$test\0";
    if (BufferNeeded) *BufferNeeded = ARRAYSIZE(pseregs);
    memcpy(Buffer, pseregs, min(sizeof(pseregs), BufferChars*sizeof(*Buffer)));
    return ARRAYSIZE(pseregs) > BufferChars ? S_FALSE : S_OK;
}

EXTERN_C HRESULT CALLBACK DebugExtensionInitialize(OUT PULONG Version, OUT PULONG Flags)
{
    *Version = DEBUG_EXTENSION_VERSION(1, 0), *Flags = 0;
    return S_OK;
}

重现问题看起来像这样:

0:000> $$ Press Ctrl+E to open a executable, I'm going to open WinVer.exe
0:000> .load c:\test\myext.dll
0:000> ?@$$test
Evaluate expression: -2401039830915039505 = deadcafe`babebeef
0:000> $$ Press Shift+F5 to stop debugging
0:000> $$ Press Ctrl+E and open a executable again, WinDbg will now print "Unable to deliver callback, 3131"

我能够想出一个似乎确实有效的解决方法,但它感觉不对,因为我必须发布一个我从未 QI 或 AddRef 的接口(interface)。在我进行的最少测试中,这个 hack 似乎从来没有崩溃过,通过查看 IDebugClients refcount,它在多次调用中看起来确实是正确的。

据我所知,您无法停止调试并在 CDB 中打开一个新的 .exe,因此问题似乎只会发生在 WinDbg 中。

是我做错了什么还是 DbgEng 中有错误?

最佳答案

@安德斯

我花了一些时间在不同的代码路径中检查它,似乎在 dbgeng ClientListCapture/Find/fill/list/FindExt 中存在错误
增加的引用计数似乎没有减少,导致引用累积在每次关闭和打开时添加多个回调

我使用的代码路径是设置扩展类的 m_ProvidedValue 成员

请注意,我还添加了代码来解决下面的其他两个查询 TypeId 和 TypeModBase

#include <engextcpp.cpp>
ExtProvidedValue pval[];
class EXT_CLASS : public ExtExtension {
public:
    void
    handler (
    _In_ ULONG Flags, _In_ PCWSTR ValueName, _Out_ PULONG64 Value,
    _Out_ PULONG64 TypeModBase, _Out_ PULONG TypeId, _Out_ PULONG TypeFlags
    )   {

        DEBUG_CACHED_SYMBOL_INFO Info;
        ZeroMemory(&Info, sizeof(Info));
        PCSTR Type = "ntdll!_LDR_DATA_TABLE_ENTRY";
        HRESULT Status = E_FAIL;
        if((Status = m_Symbols->GetSymbolTypeId(Type,&Info.Id,&Info.ModBase)) != S_OK) {
            ThrowStatus(Status, "Unable to get type ID of '%s'", Type);
        }
        if (0 == lstrcmpiW(ValueName, L"$$test")) {
            if(Value)       { *Value        = 0xDeadCafeBabeBeefULL; }
            if(TypeModBase) { *TypeModBase  = Info.ModBase; }
            if(TypeId)      { *TypeId       = Info.Id; }
            if(TypeFlags)   { *TypeFlags    = DEBUG_EXT_PVTYPE_IS_POINTER; }
        }
    };
    HRESULT Initialize(void) {
        this->m_ProvidedValues = pval;
        return S_OK;
    }
};
EXT_DECLARE_GLOBALS();
ExtProvideValueMethod mymethod = (ExtProvideValueMethod)&Extension::handler;
ExtProvidedValue pval[] = {L"$$test\0",mymethod,NULL,NULL};

def文件包含

EXPORTS
    DebugExtensionInitialize
    DebugExtensionQueryValueNames
    DebugExtensionProvideValue
    help

编译链接enterprise wdk如下

@echo off
IF "%donesetup%" == "" ( pushd .. )
IF "%donesetup%" == "" ( cd /d E:\ewdk )
IF "%donesetup%" == "" ( @call launchbuildenv.cmd )
IF "%donesetup%" == "" ( popd )
IF "%donesetup%" == "" ( set  "donesetup=donesetup" )

IF "%INCLUDE%"   == "" ( set "INCLUDE=%vcinstalldir%include;%windowssdkdir%Include\10.0.10586.0\ucrt;%windowssdkdir%Include\10.0.10586.0\um;%windowssdkdir%Include\10.0.10586.0\shared;%windowssdkdir%Debuggers\inc;" )
IF "%LIB%"       == "" ( set "LIB=%vcinstalldir%\LIB;%WINDOWSSDKDIR%Lib\10.0.10586.0\ucrt\x86;%WINDOWSSDKDIR%Lib\10.0.10586.0\um\x86;%windowssdkdir%Debuggers\lib\x86")
IF "%LINKLIBS%"  == "" ( set "LINKLIBS=user32.lib kernel32.lib dbgeng.lib dbghelp.lib" )



cl /LD /nologo /W3 /Zi  /EHsc pstest.cpp /link /DEF:pstest.def /RELEASE %linklibs%

move /y pstest.dll "e:\ewdk\Program Files\Windows Kits\10\Debuggers\x86\winext"\.

加载扩展到windbg和调用结果

Microsoft (R) Windows Debugger Version 10.0.10586.567 X86

0:000> .load pstest
0:000> ? @$$test   (masm Evaluate)

Evaluate expression: -2401039830915039505 = deadcafe`babebeef
0:000> ?? @$$test   (c++ Evaluate note the use of TypeId and TypeModbase   
 results in a type Display instead of a Int64 value)

struct _LDR_DATA_TABLE_ENTRY * 0xbabebeef
   +0x000 InLoadOrderLinks : _LIST_ENTRY
   +0x008 InMemoryOrderLinks : _LIST_ENTRY
   +0x010 InInitializationOrderLinks : _LIST_ENTRY
   +0x018 DllBase          : ???? 
   +0x01c EntryPoint       : ???? 
   +0x020 SizeOfImage      : ??
   +0x024 FullDllName      : _UNICODE_STRING 
   +0x02c BaseDllName      : _UNICODE_STRING 
   +0x034 Flags            : ??
   +0x038 LoadCount        : ??
   +0x03a TlsIndex         : ??
   +0x03c HashLinks        : _LIST_ENTRY
   +0x03c SectionPointer   : ???? 
   +0x040 CheckSum         : ??
   +0x044 TimeDateStamp    : ??
   +0x044 LoadedImports    : ???? 
   +0x048 EntryPointActivationContext : ???? 
   +0x04c PatchInformation : ???? 
   +0x050 ForwarderLinks   : _LIST_ENTRY
   +0x058 ServiceTagLinks  : _LIST_ENTRY
   +0x060 StaticLinks      : _LIST_ENTRY
   +0x068 ContextInformation : ???? 
   +0x06c OriginalBase     : ??
   +0x070 LoadTime         : _LARGE_INTEGER

如果我按 Shift+f5 和 ctrl+e,windbg 会提示它无法传递回调,正如你发布的那样
如果重复 shift +f5/ctrl+e/load/invoke 的过程
投诉的数量不断增加,导致 windbg 挂起
尝试在挂起的调用堆栈中处理 PendingMessages

关于windows - 实现 DebugExtensionProvideValue 会破坏 WinDbg 内部状态?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37417335/

有关windows - 实现 DebugExtensionProvideValue 会破坏 WinDbg 内部状态?的更多相关文章

  1. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  2. ruby-on-rails - 跳过状态机方法的所有验证 - 2

    当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested

  3. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  4. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  5. ruby - 字符串文字中的转义状态作为 `String#tr` 的参数 - 2

    对于作为String#tr参数的单引号字符串文字中反斜杠的转义状态,我觉得有些神秘。你能解释一下下面三个例子之间的对比吗?我特别不明白第二个。为了避免复杂化,我在这里使用了'd',在双引号中转义时不会改变含义("\d"="d")。'\\'.tr('\\','x')#=>"x"'\\'.tr('\\d','x')#=>"\\"'\\'.tr('\\\d','x')#=>"x" 最佳答案 在tr中转义tr的第一个参数非常类似于正则表达式中的括号字符分组。您可以在表达式的开头使用^来否定匹配(替换任何不匹配的内容)并使用例如a-f来匹配一

  6. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  7. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  8. Vscode+Cmake配置并运行opencv环境(Windows和Ubuntu大同小异) - 2

    之前在培训新生的时候,windows环境下配置opencv环境一直教的都是网上主流的vsstudio配置属性表,但是这个似乎对新生来说难度略高(虽然个人觉得完全是他们自己的问题),加之暑假之后对cmake实在是爱不释手,且这样配置确实十分简单(其实都不需要配置),故斗胆妄言vscode下配置CV之法。其实极为简单,图比较多所以很长。如果你看此文还配不好,你应该思考一下是不是自己的问题。闲话少说,直接开始。0.CMkae简介有的人到大二了都不知道cmake是什么,我不说是谁。CMake是一个开源免费并且跨平台的构建工具,可以用简单的语句来描述所有平台的编译过程。它能够根据当前所在平台输出对应的m

  9. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  10. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

随机推荐