如何编写一个样例代码使其崩溃并生成转储文件?

18

我开始学习Windbg,并发现了这篇好文章:如何使用WinDbg分析VC++应用程序的崩溃转储?

现在,我想按照说明一步一步地操作。问题是:我需要编写一些可以立即崩溃并创建可供Windbg使用的转储文件的示例代码。

如何编写这样的代码?

void Example4()
{
    int* i = NULL;
    *i = 80;
}

以上代码会立即崩溃;但我不知道在哪里找到转储文件?

谢谢


请看这个链接:http://www.debuginfo.com/articles/effminidumps.html 我觉得它非常有用。里面还包含了一些示例代码。 - Naveen
9个回答

36
#include <Windows.h>
#include <Dbghelp.h>

void make_minidump(EXCEPTION_POINTERS* e)
{
    auto hDbgHelp = LoadLibraryA("dbghelp");
    if(hDbgHelp == nullptr)
        return;
    auto pMiniDumpWriteDump = (decltype(&MiniDumpWriteDump))GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
    if(pMiniDumpWriteDump == nullptr)
        return;

    char name[MAX_PATH];
    {
        auto nameEnd = name + GetModuleFileNameA(GetModuleHandleA(0), name, MAX_PATH);
        SYSTEMTIME t;
        GetSystemTime(&t);
        wsprintfA(nameEnd - strlen(".exe"),
            "_%4d%02d%02d_%02d%02d%02d.dmp",
            t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond);
    }

    auto hFile = CreateFileA(name, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
    if(hFile == INVALID_HANDLE_VALUE)
        return;

    MINIDUMP_EXCEPTION_INFORMATION exceptionInfo;
    exceptionInfo.ThreadId = GetCurrentThreadId();
    exceptionInfo.ExceptionPointers = e;
    exceptionInfo.ClientPointers = FALSE;

    auto dumped = pMiniDumpWriteDump(
        GetCurrentProcess(),
        GetCurrentProcessId(),
        hFile,
        MINIDUMP_TYPE(MiniDumpWithIndirectlyReferencedMemory | MiniDumpScanMemory),
        e ? &exceptionInfo : nullptr,
        nullptr,
        nullptr);

    CloseHandle(hFile);

    return;
}

LONG CALLBACK unhandled_handler(EXCEPTION_POINTERS* e)
{
    make_minidump(e);
    return EXCEPTION_CONTINUE_SEARCH;
}

int main()
{
    SetUnhandledExceptionFilter(unhandled_handler);

    return *(int*)0;
}

4

谢谢你提供的代码。你能告诉我在哪里找到转储文件吗? - q0987
@q0987 文件将出现在您使用MiniDumpWriteDump()写出的位置。如果您启用了UAC,则可能无法直接写入C:\。在这种情况下,请使用您的用户下的文件夹,或以管理员身份启动应用程序。 - Stefan Dragnev

2
创建崩溃转储时,我不会像@Abyx建议的那样编写未处理异常处理程序,原因如下:
a)在某些缓冲区溢出或堆栈溢出的情况下,处理未处理异常的代码可能会损坏。在OutOfMemoryException的情况下,你如何加载另一个库,比如DbgHelp.dll?
b)你编写的代码可能有漏洞。该代码是否在写入转储之前检查了可用磁盘空间?你如何测试编写崩溃转储的代码?你是否为此编写了单元测试?你的单元测试如何检查转储是否正确?
c)如果Windows可以为你完成这项工作,为什么还要编写代码呢?
MSDN有一篇关于收集用户模式转储的文章。基本上,你可以进行一些注册表设置。优点是:Windows将通过操作系统而不是应用程序内部的某些损坏代码创建崩溃转储。

1
有道理,但引用MSDN文章中的“启用该功能需要管理员权限”可能对软件部署不可行。 - PhilLab

1

如果您想查看崩溃转储,您需要创建一个。请参见Heisenbug: WinApi program crashes on some computers。虽然您可能能够在不经过WinQual的情况下获取用于WER发送的崩溃转储,但这有点混乱(基本上您可以在发送之前从临时位置复制它,具体细节取决于您的操作系统),我建议使用Win API MiniDump提供的函数创建自己的崩溃转储。所有所需的代码都可以在The CodeProject page mentioned in the linked answer找到。


您还可以为应用程序配置一个位置,WER在其中保存Minidump。在此处查看更多细节:https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps - mBardos

1

自动 minidump 生成是由 post-mortem 调试器完成的,因此您需要从那里开始。最重要的是,它是由调试器完成的。因此,如果您只想生成 minidump,则可以使用您典型的调试器(可能是 Visual Studio 或 Windbg)。即使任务管理器也可以创建转储文件。

指定 post-mortem 调试器的注册表设置为 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug

查看 Debugger 字符串,您就可以找到您的 minidumps。


1

可以通过编程或程序错误调试工具创建转储文件。在第一种情况下,您可以使用MiniDumpWriteDump函数,在第二种情况下,您可以使用Dr. Watson(对于XP:请查看此description和这个非常详细的video;对于Vista,请查看here)。


1
大多数情况下,您会发现所有应用程序都会被转储到C:\ windows \ minidumps中。
生成转储文件的方法如下:
  1. 打开Windbg
  2. 文件-> 打开可执行文件
  3. 运行将要崩溃的应用程序
  4. 断点将会触发
  5. 现在您可以在Windbg中使用.dump来创建dmp文件
或者
  1. 运行应用程序并等待崩溃
  2. 打开Windbg并附加到进程(文件->附加到进程)
  3. 运行.dump
这样,您随时都可以分析该崩溃情况 :)

0

我之前在测试WinDbg时使用了下面的代码。

  • 下面的代码可以生成崩溃转储文件。
  • 有两个函数,以便您可以看到一个明显的函数链堆栈。
  • 要查找崩溃转储文件,请在C:\ Users中搜索*.dmp或*.mdmp。
  • 最好让操作系统为您生成转储文件。这可能是您看到的大多数真实崩溃转储文件的生成方式。
  • 该代码首先分配1 KiB的内存,然后将其和后面的1 KiB都写入可识别的十六进制值。这通常会命中由操作系统标记为不可写的内存页,从而触发崩溃。

#include "stdafx.h"
#include "stdio.h"
#include "malloc.h"

void Function2(int * ptr2)
{
    for(int i=0; i < (2 * 1024); i++)
    {
        *ptr2++ = 0xCAFECAFE;
    }
}

void Function1()
{
    int * ptr1 = (int *)malloc(1024 * sizeof(int));

    Function2(ptr1);
}

int _tmain(int argc, _TCHAR* argv[])
{
    printf("Press enter to allocate and corrupt.\r\n");
    getc(stdin);

    printf("Allocating and corrupting...\r\n");
    Function1();

    printf("Done.  Press enter to exit process.\r\n");
    getc(stdin);

    return 0;
}

-3

试试这个:

int main()
{
   int v[5];

   printf("%d", v[10]);
   return 0;
}

或者访问一个随机的内存位置。


6
这可能不会导致崩溃 - v [10] 上的堆栈内存很可能已经被分配,printf 只会打印出垃圾值。 - Stefan Dragnev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接