WinDbg:如何为 .net 程序设置本机断点

WinDbg: how to set a native breakpoint for .net program

给定一个简单的 .NET 程序(来自 https://docs.microsoft.com/en-us/dotnet/framework/interop/platform-invoke-examples):

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.Remoting;

class Sample
{
    static void Main(string[] args)
    {
        MessageBox(IntPtr.Zero, "TEST", "TEST", 0);
    }
    [DllImport("user32.dll", SetLastError=true)]
    private static extern int MessageBox(IntPtr hwnd, String text, String title, uint type);
}

程序调用本机 Win32 函数MessageBox。 我想在 WinDbg 中的那个本机函数上设置一个断点。对于 API 文档,函数在 user32.dll 中,所以我使用 bpuser32!MessageBox.

处设置断点

I 运行 WinDbg, File/Open Executable, 打开程序的exe,进入debugger。然后我执行了 bp user32!MessageBoxW 但它说我:"bp expression 'user32!MessageBoxW' could not be resolved, adding deferred bp"。

所以如果我继续 g 我不会进入断点并得到错误:

"断点 0 的偏移量表达式计算失败。 检查无效符号或错误语法。"

更新:
请参阅已接受的问题 - user32!MessageBox 不正确,应该使用 user32!MessageBoxW。该消息 "bp expression 'user32!MessageBoxW' could not be resolved, adding deferred bp" 完全没问题,只是说还没有加载任何符号。

Windows API 带有字符串参数的函数通常有两个版本,后缀为 AW。 C/C++ SDK 定义了在编译器预处理器步骤中选择一个或另一个。

在 WinDbg 中,您可以键入 bp user32!MessageBox,然后键入 Tab 以循环浏览已知的 symbols/exports,但如果您是使用延迟符号查找。

如果你不想处理所有这些,你可以在两者上设置一个断点:

bp user32!MessageBoxA
bp user32!MessageBoxW 

WinDbg 将显示如下内容:

0:000> bp user32!MessageBoxA
Bp expression 'user32!MessageBoxA' could not be resolved, adding deferred bp
0:000> bp user32!MessageBoxW 
Bp expression 'user32!MessageBoxW' could not be resolved, adding deferred bp
0:000> g
ModLoad: 76d90000 76e3e000   C:\windows\SysWOW64\ADVAPI32.dll
...
ModLoad: 763a0000 764b6000   C:\windows\SysWOW64\USER32.dll
...    
ModLoad: 741d0000 74258000   C:\windows\SysWOW64\uxtheme.dll
ModLoad: 72700000 7276e000   C:\Windows\Microsoft.NET\Framework\v4.0.30319\clrjit.dll
Breakpoint 0 hit
...
USER32!MessageBoxA:
76405f69 8bff            mov     edi,edi

当您刚刚使用 Open Executable 启动应用程序时,尚未加载 user32。

0:000> lm
start    end        module name
00d80000 00d88000   MessageBoxWTest   (deferred)             
73750000 737a4000   MSCOREE    (deferred)             
764a0000 76570000   KERNEL32   (deferred)             
76690000 76867000   KERNELBASE   (deferred)             
77b70000 77cfd000   ntdll      (export symbols)       C:\WINDOWS\SYSTEM32\ntdll.dll

因此 WinDBG 将无法加载和查找符号。 在这里你有 2 个选项:

  1. 使用buBreak Unresolved(延迟断点也用它)。在这种情况下,调试器正在等待与您指定的名称完全相同的符号被加载。如果您输入错误,则不会命中断点。就像你的问题一样。
  2. 使用sxe ld name命令等待特定模块加载,然后找到符号并设置断点。

_

0:000> sxe ld user32
0:000> g
ModLoad: 77030000 770a8000   C:\WINDOWS\SysWOW64\ADVAPI32.dll
ModLoad: 74720000 747dd000   C:\WINDOWS\SysWOW64\msvcrt.dll
ModLoad: 763e0000 76423000   C:\WINDOWS\SysWOW64\sechost.dll
ModLoad: 768f0000 769ae000   C:\WINDOWS\SysWOW64\RPCRT4.dll
ModLoad: 74590000 745b0000   C:\WINDOWS\SysWOW64\SspiCli.dll
ModLoad: 74580000 7458a000   C:\WINDOWS\SysWOW64\CRYPTBASE.dll
ModLoad: 761b0000 76207000   C:\WINDOWS\SysWOW64\bcryptPrimitives.dll
ModLoad: 736d0000 7374d000   C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscoreei.dll
ModLoad: 76430000 76475000   C:\WINDOWS\SysWOW64\SHLWAPI.dll
ModLoad: 75e00000 76046000   C:\WINDOWS\SysWOW64\combase.dll
ModLoad: 77a50000 77b67000   C:\WINDOWS\SysWOW64\ucrtbase.dll
ModLoad: 770b0000 770d2000   C:\WINDOWS\SysWOW64\GDI32.dll
ModLoad: 76050000 761ae000   C:\WINDOWS\SysWOW64\gdi32full.dll
ModLoad: 76870000 768ec000   C:\WINDOWS\SysWOW64\msvcp_win.dll
ModLoad: 770e0000 77255000   C:\WINDOWS\SysWOW64\USER32.dll
eax=00000000 ebx=00800000 ecx=00000000 edx=00000000 esi=0337c030 edi=0337bf90
eip=77bdea0c esp=0113d424 ebp=0113d470 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
ntdll!ZwMapViewOfSection+0xc:
77bdea0c c22800          ret     28h

在这里,我们在加载 USER32 后立即停止。现在,WinDBG 可以为它加载符号,我们可以找到它们。

0:000> x user32!MessageBox*
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\USER32.dll - 
7714f8b0          USER32!MessageBoxA (<no parameter info>)
7714f8e0          USER32!MessageBoxExA (<no parameter info>)
7714f910          USER32!MessageBoxExW (<no parameter info>)
7714f940          USER32!MessageBoxIndirectA (<no parameter info>)
7714fa40          USER32!MessageBoxIndirectW (<no parameter info>)
7714faa0          USER32!MessageBoxTimeoutA (<no parameter info>)
7714fb50          USER32!MessageBoxTimeoutW (<no parameter info>)
7714fce0          USER32!MessageBoxW (<no parameter info>)

然后我们可以设置断点,肯定会命中。

0:000> bp USER32!MessageBoxA
0:000> bp USER32!MessageBoxW
0:000> g
Breakpoint 0 hit
eax=7714f8b0 ebx=00000006 ecx=00000000 edx=00000004 esi=03023b48 edi=00d3f350
eip=7714f8b0 esp=00d3f26c ebp=00d3f320 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
USER32!MessageBoxA:
7714f8b0 8bff            mov     edi,edi
0:000> bl
     0 e Disable Clear  7714f8b0     0001 (0001)  0:**** USER32!MessageBoxA
     1 e Disable Clear  7714fce0     0001 (0001)  0:**** USER32!MessageBoxW