精华内容
下载资源
问答
  • Windows简单调试器源码

    2015-01-11 18:51:40
    windows简单调试器2700行左右代码,打开被调试程序键入 ? 显示功能及帮助
  • Windows 批处理调试器3.0

    热门讨论 2012-01-08 18:50:05
    .bdc文件是JSON文本格式,可以任何一个文本阅读器打开,不过推荐你在Windows批处理调试器中编辑,以免出错。 添加批处理文件 通过打开文件按钮或者拖拽方式打开批处理文件,就可以把批处理文件添加到当前工程。...
  • windows调试器设置

    千次阅读 2007-12-29 15:33:00
    本文就windows调试器的设置问题,根据个人在windbg上的使用经验进行总结,既算是对自己的总结,也算是份大家一起分享自愿吧。本文的前提是windbg已经安装完毕,适合对windbg一定了解的读者。1.JIT设置。通过JIT...

    编程中,debug既是技术,又是技巧。熟练的掌握调试工具可以加速软件问题的解决,这对于底层开发和高级开发都是十分必要的。本文就windows调试器的设置问题,根据个人在windbg上的使用经验进行总结,既算是对自己的总结,也算是份大家一起分享自愿吧。本文的前提是windbg已经安装完毕,适合对windbg有一定了解的读者。

    1.JIT设置。通过JIT设置,可以改变系统默认的调试器-drwtsn32。当程序抛出或发生异常时,如果没有程序自身进行异常处理,而且没有运到其他调试器的拦截,那么,系统会将此一场交给JIT的设置进行处理。这是 异常陷阱处理的一种情况,详细的原理可以参看"windows internal(4th edition)"。对windbg的设置比较简单,只要运行命令:windbg -I即可。实际上,修改了HKLM/Software/Microsoft/Windows NT/CurrentVersion/AeDebug的健值。如果要支持对托管程序的调试的话就要修改注册表:
    HKLM/Software/Microsoft/.NETFramework/DbgJITDebugLaunchSetting 为2
    HKLM/Software/Microsoft/.NETFramework/DbgManagedDebugger 为Windbg的路径
    在windbg端进一步设置好symbol path和source path就可以在异常发生时进行step by step的调试了。

    2.镜像文件运行设置。通过该设置,可以在制订程序运行时自动附加到windbg上去,太方便了:)。注册表项HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows NT/CurrentVersion/Image File Execution Options的最后一个子项"Your Image File Name Here without a path",修改为镜像名称,如iexplore.exe。健值Debugger默认值ntsd -d,用来调试内核镜像的,可去掉-d,使用ntsd调试,也可以修改为windbg的路径。GlobalFlag健值是通过工具gflags.exe来修改的,具体到应用中再说了。

    本文仅是对设置方法进行了梳理,在具体应用中就灵活一些了。比如关于如何调试 Windows 服务,可以参考http://support.microsoft.com/kb/824344

    展开全文
  • windows调试器之Visual C++

    千次阅读 2013-04-21 19:22:22
    visual c++ 是visual studio中的一个组件。windows下开发C++是一个首选工具。...visual c++作为我们的开发工具,当做调试器当然也不逊色,那他与别的调试器相比哪些优劣点呢? visual c++调试器

    visual c++ 是visual studio中的一个组件。windows下开发C++是一个首选工具。这个工具强大了,每个人都知道哈哈


    我打算给大家介绍一些windows下常用的调试器:

    visual c++

    ollydbg

    windbg

    visual c++作为我们的开发工具,当做调试器当然也不逊色,那他与别的调试器相比有哪些优劣点呢?


    visual c++调试器作为visualc++的一个组件,带源码调试是非常强大的。能显示出全局符号名、局部变量名和结构体等内容。

    但是如果你没有源码或pdb符号文件,那用visual c++调试就是一个痛苦的过程了,由于它的汇编显示器没有对汇编指令加量显示,要想看清楚一个函数的逻辑,很不容易!


    所以,visual c++ 是一个源码级调试器,如果你没有这个程序的源码,那建议你使用ollydbg。

    visual c++ 调试器到底怎么使用呢?他有哪些功能?

    • 1. vc 调试器支持本地和远程调试
    • 2. vc 是一个应用层调试器,不支持对驱动调试
    • 3. vc对符号文件支持很强大,如果有对应的符号,就能显示出其中的局部变量和数据结构等
    • 4. 一次可以调试多个进程
    • 5. 可以对单个线程调试(把其他线程挂起)
    • 6. 可以查看所有线程调用栈
    • 7. 代码和汇编指令之间可以相互切换,很方便
    • 8. 灵活的断点设置


    一、开始调试

    1. 启动调试

    vc调试器调试一个程序有两种方式:启动调试;附加调试

    启动调试是最常用的一种,程序编译完成直接按F5启动调试


    2. 附加调试

    那附加调试怎么启动呢? “工具”-->"附加到进程"(或直接按Ctrl+Art+p)在弹出的对话框中选择要调试的进程,点击“附加”即可。


    二、设置符号(pdb文件)

    如果无法在代码中下断点,有一下几种可能:

    1. 代码与编译时不一致

    2. 没有符号文件

    3. 符号文件不匹配

    第一种情况,如果强制下断点,可能断点下错位置,从而无法在预订的位置断下来。最好找到当时对应的源码文件,或者转到汇编视图,在汇编中下断点。

    第二种情况和第三种情况,需要正确的符号文件和路径

    如果没有符号文件,那建议用ollydbg调试

    如果有符号文件,并且知道位置,则可以按照下面步骤,

    1. 设置符号目录:“工具”-->“选项” -->“调试”--> “符号”,增加一个符号路径。然后点击"加载所有符号",符号就会自动加载。

    2. 如果不想设置符号目录,则,找到模块视图,选中模块点击鼠标右键,“加载符号”-->“符号路径”来手动选择正确的符号文件


    三、断点

    vc支持的断点很丰富

    1. 普通断点:直接在代码中按F9。取消断点,再按F9

    有时候普通断点无法满足调试需求,那看看他支持哪些断点类型

    2. 根据命中次数下断

       如果只希望命中N次时才中断,则可以在断点上点击鼠标右键选择“命中次数”设置

    3. 当产生中断时打印数据

    如果希望命中时打印一些日志,则可以在断点上点击鼠标右键选择“命中条件”设置

    4. 条件断点

    条件断点是常用的断点,如当某个局部变量的值等于多少时才中断。可以在断点上点击鼠标右键选择“条件”设置

    i == 5 && wsinfo.bNew == true (可以对一个结构的字段设置条件)

    字符串条件断点:stricmp( proc32.szExeFile, "system") == 0 (如果进程名是system则中断)

    vc还支持strcmp \strncmp等其他字符串比较函数

    5. 条件筛选

    如果你只想对某一个线程下断,可以使用条件筛选器。在断点上点击鼠标右键选择“筛选器”设置


    四、调试相关的窗口

    这些窗口都可以在"调试"-->“窗口”中的子菜单中找到

    1. 模块列表

    显示了所有模块信息。包括模块加载地址和大小、模块路径、是否加载了符号。

    2. 线程列表

    线程列表中可以查看每个线程的调用栈,并且可以挂起(冻结)个别线程

    3. 监视器

    监视器可以监视某个全局变量或者局部变量的值,当然寄存器的值也可以监视。

    如果想看当前线程的LastError错误码,在监视器中输入 @err

    4. 汇编器

    可以看到源码对应的汇编指令

    5. 内存查看器

    可以根据一个地址,查看内存数据

    6. 输出窗口

    查看日志输出

    7.调用堆栈

    当前线程的调用栈

    8. 自动窗口和局部变量

    显示当前局部变量或全局变量的值,很方便

    9. 断点列表

    所有断点信息

    10. 寄存器窗口

    所有寄存器当前值


    五、远程调试

    如果想调试一个远程电脑上的程序该怎么办呢?

    1. 在远程机器上运行远程调试监视器(vcmsmon.exe)

    远程调试监视器所在目录:C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\Remote Debugger

    (我的VC安装在c盘,并且路径是默认的。vc版本是2010。可能与你的不同)

    然后选择版本,如果目标机器是x86,则吧x86目录中所有文件COPY到目标机,如果是ia64,则COPYia64目录到目标机中

    然后在目标机上启动vcmsmon.exe进程。并设置成TCP链接方式,端口为默认。"工具"-->“选项”中设置

    2. 远程启动调试

    “项目”-->"属性"“配置属性”“调试”

    在“要启动的调试器”中选择“远程 windows 调试器”

    命令”中输入远程机器等待调试的进程全路径

    工作目录”设置进程的工作目录

    "远程服务器名字" 输入 远程机器IP

    链接”中选择“不带身份验证的远程访问”

    设置好后,按F5启动调试

    3. 远程附加调试

    “工具”-->"附加到进程"(或直接按Ctrl+Art+p)在弹出的对话框中的

    传输”中选择“远程(...)

    "限定符"中输入远程机器IP

    然后点“刷新”

    选择要调试的进程,点击“附加”


    六、其他

    1. 调试快捷键:

    F10单步步过

    F11单步步入

    F5继续

    shift+f11 跳出


    转载请注明出处。ddlx studio。点点灵犀。 http://blog.csdn.net/sunyikuyu



    展开全文
  • 编写一个初级的Windows调试器

    千次阅读 2011-01-28 11:13:00
    这是Live Writer写的,很多原来的格式信息都没了,杯具 Writing a basic Windows debugger 编写一个初级的Windows调试器 By Ajay Vijayvargiya | 24 Jan 2011 作者:Ajay Vijayvar

    这是用Live Writer写的,很多原来的格式信息都没了,杯具

    Writing a basic Windows debugger

    编写一个初级的Windows调试器

    By Ajay Vijayvargiya | 24 Jan 2011

    作者:Ajay Vijayvargiya | 2011-01-24

    原文:http://www.codeproject.com/KB/system/writing_debugger_1.aspx

    clip_image001[4]

    Preamble

    前言

    All of us have used some kind of debugger while programming in some language. The debugger you used may be in C++, C#, Java or another language. It might be standalone like WinDbg, or inside an IDE like Visual Studio. But have you been inquisitive(好奇) over how debuggers work?

    在我们使用某些语言时都用过某些调试器。你使用过的调试器可能用C++、C#、Java或者其它语言编写的。它可能是独立的,像WinDbg,或者内嵌在一个像Visual Studio 的IDE中。然而你是否会对“调试器如何工作”的感到好奇?

    Well, this article presents the hidden glory on how debuggers work. This article only covers writing debugger on Windows. Please note that here I am concerned only about the debugger and not about compilers, linkers, or debugging extensions. Thus, we'll only debug executables (like WinDbg). This article assumes a basic understanding of multithreading from the reader (read my article on multithreading).

    好,这篇文档展示了调试器如何工作的隐藏亮点。这篇文章仅包含编写Windows上的调试器。请注意,我在这仅关注“调试器”,而不是编译器、链接器或调试器的扩展。因此,我们仅调试可执行的(像WinDbg)。这篇文章假设读者对多线程有基本的理解(阅读我的关于多线程的文章)

    1. How to Debug a Program?

    1. 怎样调试一个程序?

    Two steps:

    1. Starting the process with DEBUG_ONLY_THIS_PROCESS or DEBUG_PROCESS flags.
    2. Setting up the debugger's loop that will handle debugging events.

    两步:

    1. 使用DEBUG_ONLY_THIS_PROCESSDEBUG_PROCESS标志启动进程。

    2. 设置调试器的循环,那将处理调试事件。

    Before we move further, please remember:

    1. 1.Debugger is the process/program which debugs the other process (target-process).
    2. 2.Debuggee is the process being debugged, by the debugger.
    3. 3.Only one debugger can be attached to a debuggee. However, a debugger can debug multiple processes (in separate threads).
    4. 4.Only the thread that created/spawned(生育) the debuggee can debug the target-process. Thus, CreateProcess and the debugger-loop must be in the same thread.
    5. 5.When the debugger thread terminates, the debuggee terminates as well. The debugger process may keep running, however.
    6. 6.When the debugger's debugging thread is busy processing a debug event, all threads in the debuggee (target-process) stand suspended. More on this later.

    在进一步阅读之前,请记住:

    1. 调试器是调试其它进程(目标进程)的进程/程序。

    2. 被调试者是被调试器调试的进程。

    3. 一个被调试者仅可以与一个调试器关联。然而,一个调试器可以调试多个进程(在不同线程中)。

    4. 仅仅创建/产生被调试者的线程可以调试目标进程。因此,CreateProcess和调试循环必须在同一个线程中。

    5. 当调试线程终止时,被调试者也终止。然而调试进程可能保持运行。

    6. 当调试器的调试线程正在忙于处理一个调试事件时,在被调用者(目标进程)所有的线程保持挂起状态。后面会有更多讨论。

    A. Starting the process with the debugging flag
    A.使用调试标识启动进程

    Use CreateProcess to start the process, specifying DEBUG_ONLY_THIS_PROCESS as the sixth parameter (dwCreationFlags). With this flag, we are asking the Windows OS to communicate this thread for all debugging events, including process creation/termination, thread creation/termination, runtime exceptions, and so on. A detailed explanation is given below. Please note that we'll be using DEBUG_ONLY_THIS_PROCESS in this article. It essentially means we want only to debug the process we are creating, and not any child process(es) that may be created by the process we create.

    使用CreateProcess启动进程,指定DEBUG_ONLY_THIS_PROCESS作为第六个参数(dwCreationFlags)。有了这个标识,我们要求将Windows操作系统所有调试事件与这个线程通信,包括进程创建/终止,线程创建/终止,运行时异常,等等。下面有更详细的解释。请注意在这篇文章中我们将会使用DEBUG_ONLY_THIS_PROCESS。这实际上意味着我们仅仅想要调试我们创建的进程,而不是任何可能被我们创建的进程创建的子进程。

    STARTUPINFO si;

    PROCESS_INFORMATION pi; 
    ZeroMemory( &si, sizeof(si) ); 
    si.cb = sizeof(si); 
    ZeroMemory( &pi, sizeof(pi) );
     
    CreateProcess ( ProcessNameToDebug, NULL, NULL, NULL, FALSE, 
                    DEBUG_ONLY_THIS_PROCESS, NULL,NULL, &si, &pi );

    After this statement, you would see the process in the Task Manager, but the process hasn't started yet. The newly created process is suspended. No, we don't have to call ResumeThread, but write a debugger-loop.

    在这一句之后,你在任务管理器中可以看到那个进程,但是那个进程还没有启动。新创建的进程被挂起了。不,我们不需要调用ResumeThread,而是仅仅写一个调试循环。

    B. The debugger loop
    B. 调试循环

    The debugger-loop is the central area for debuggers! The loop runs around the WaitForDebugEvent API. This API takes two parameters: a pointer to the DEBUG_EVENT structure and the DWORD timeout parameter. For timeout, we would simply specify INFINITE. This API exists in kernel32.dll, thus we need not link to any library.

    调试循环式调试器的中心区域!这个循环围绕着WaitForDebugEvent API运行。这个API需要两个参数:一个指向DEBUG_EVENT结构体的指针和一个DWORD类型的超时参数。对于超时,我们简单的指定无限(INFINITE这个APIkernel32.dll中,因此我们不需要连接任何库了。

    clip_image001[1]Collapse

    BOOL WaitForDebugEvent(DEBUG_EVENT* lpDebugEvent, DWORD dwMilliseconds);

    The DEBUG_EVENT structure contains the debugging event information. It has four members: Debug event code, process-ID, thread-ID, and the event information. As soon as WaitForDebugEvent returns, we process the received debugging event, and then eventually call ContinueDebugEvent. Here is a minimal debugger-loop:

    clip_image001[2]Collapse

    DEBUG_EVENT debug_event = {0};
    for(;;)
    {
        if (!WaitForDebugEvent(&debug_event, INFINITE))
            return;
        ProcessDebugEvent(&debug_event);  // User-defined function, not API
        ContinueDebugEvent(debug_event.dwProcessId,
                          debug_event.dwThreadId,
                          DBG_CONTINUE);
    }

    DEBUG_EVENT结构体包括调试事件信息。它有4个成员:调试事件代码,进程ID,线程ID事件信息。只要WaitForDebugEvent一返回,我们就处理接收到的调试事件,最后调用ContinueDebugEvent。这有一个最小的调试循环:

    DEBUG_EVENT debug_event = {0};
    for(;;)
    {
        if (!WaitForDebugEvent(&debug_event, INFINITE))
            return;
        ProcessDebugEvent(&debug_event);  //用户定义的函数,不是API
        ContinueDebugEvent(debug_event.dwProcessId,
                          debug_event.dwThreadId,
                          DBG_CONTINUE);
    }

    Using the ContinueDebugEvent API, we are asking the OS to continue executing the debuggee. The dwProcessId and dwThreadId specify the process and thread. These values are the same that we received form WaitForDebugEvent. The last parameter specifies if the execution should continue or not. This parameter is relevant only if the exception-event is received. We will cover this later. Until then, we'll utilize(利用) only DBG_CONTINUE (another possible value is DBG_EXCEPTION_NOT_HANDLED).

    使用ContinueDebugEvent API,我们要求操作系统继续运行被调试者。dwProcessIddwThreadId分别指定进程和线程。这些值和我们从WaitForDebugEvent得到的相同。最后一个参数指定是否需要继续运行。这个参数仅用于判断是否有异常事件。我们将在后面讨论。在那之前,我们仅会利用DBG_CONTINUE(另一个可能值是DBG_EXCEPTION_NOT_HANDLED)。

    2. Handling debugging events

    2. 处理调试事件

    There are nine different major debugging events, and 20 different sub-events under the exception-event category. I will discuss them, starting from the simplest. Here is the DEBUG_EVENT structure:

    clip_image001[3]Collapse

    struct DEBUG_EVENT
    {
        DWORD dwDebugEventCode;
        DWORD dwProcessId;
        DWORD dwThreadId;
        union {
            EXCEPTION_DEBUG_INFO Exception;
            CREATE_THREAD_DEBUG_INFO CreateThread;
            CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
            EXIT_THREAD_DEBUG_INFO ExitThread;
            EXIT_PROCESS_DEBUG_INFO ExitProcess;
            LOAD_DLL_DEBUG_INFO LoadDll;
            UNLOAD_DLL_DEBUG_INFO UnloadDll;
            OUTPUT_DEBUG_STRING_INFO DebugString;
            RIP_INFO RipInfo;
        } u;
    };

    这有9种不同的主要调试事件,20种在异常事件分类中不同的子事件。我将会从简单的开始讨论它们。这是DEBUG_EVENT结构体:

    struct DEBUG_EVENT
    {
        DWORD dwDebugEventCode;
        DWORD dwProcessId;
        DWORD dwThreadId;
        union {
            EXCEPTION_DEBUG_INFO Exception;
            CREATE_THREAD_DEBUG_INFO CreateThread;
            CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
            EXIT_THREAD_DEBUG_INFO ExitThread;
            EXIT_PROCESS_DEBUG_INFO ExitProcess;
            LOAD_DLL_DEBUG_INFO LoadDll;
            UNLOAD_DLL_DEBUG_INFO UnloadDll;
            OUTPUT_DEBUG_STRING_INFO DebugString;
            RIP_INFO RipInfo;
        } u;
    };

    WaitForDebugEvent, on successful return, fills-in the values in this structure. dwDebugEventCode specifies which debugging-event has occurred. Depending on the event-code received, one of the members of the union u contains the event information, and we should only use the respective union-member. For example, if the debug event code is OUTPUT_DEBUG_STRING_EVENT, the member OUTPUT_DEBUG_STRING_INFO would be valid.

    WaitForDebugEvent在成功返回时会填充这个结构体的值。dwDebugEventCode指定那个调试事件发生了。根据接收到的事件代码,联合体u 的其中一个成员包含了事件信息,我们需要使用各自的联合体成员。例如,如果调试事件代码是OUTPUT_DEBUG_STRING_EVENTOUTPUT_DEBUG_STRING_INFO成员就是正确的。

    A. Processing OUTPUT_DEBUG_STRING_EVENT
    A. 处理OUTPUT_DEBUG_STRING_EVENT

    Programmers generally use OutputDebugString to generate debugging-text that would be displayed on the debugger's 'Output' window. Depending on the language/framework you use, you might be familiar with the TRACE, ATLTRACE macros. A .NET programmers may use the System.Diagnostics.Debug.Print/System.Diagnostics.Trace.WriteLine methods (or other methods). But with all these methods, the OutputDebugString API is called, and the debugger would receive this event (unless it is buried with the DEBUG symbol undefined!).

    程序员一般使用OutputDebugString产生输出到调试器’输出’窗口的调试文本。根据你使用的语言/框架,你可能对TRACE,ATLTRACE宏很熟悉。一个.NET程序员可能使用System.Diagnostics.Debug.Print/System. Trace.WriteLine方法(或其它方法)。但是对于所有这些方法,OutputDebugString API都会被叫用,调试器也会接收到这个事件(除非它被DEBUG符号取消定义!)。

    When this event is received, we work on the DebugString member variable. The structure OUTPUT_DEBUG_STRING_INFO is defined as:

    struct OUTPUT_DEBUG_STRING_INFO

    {
       LPSTR lpDebugStringData;  // char*
       WORD fUnicode;
       WORD nDebugStringLength;
    };

    当这个事件到达时,我们工作在(work on)DebugString成员变量。OUTPUT_DEBUG_STRING_INFO结构体是这样定义的:

    struct OUTPUT_DEBUG_STRING_INFO
    {
       LPSTR lpDebugStringData;  // char*
       WORD fUnicode;
       WORD nDebugStringLength;
    };

    The member-variable 'nDebugStringLength' specifies the length of the string, including the terminating null, in characters (not bytes). The variable 'fUnicode' specifies if the string is Unicode (non-zero), or ANSI (zero). That means, we read 'nDebugStringLength' bytes from 'lpDebugStringData' if the string is ANSI; otherwise, we read (nDebugStringLength x 2) bytes. But remember, the address pointed by 'lpDebugStringData' is not from the address-space of the debugger's memory. The address is relevant to the debuggee memory. Thus, we need to read the contents from the debuggee's process memory.

    成员变量'nDebugStringLength'指定了字符串的长度,包括终止空字符,以字符编码(不是字节)。变量'fUnicode'指定了字符串是否Unicode(非零)或ANSI(零)。这意味着,如果字符串是ANSI编码,我们从'lpDebugStringData'读取'nDebugStringLength'个字节;否则,我们读取(nDebugStringLength x 2)个字节。但是记住,'lpDebugStringData'指向的地址不是调试器内存的地址空间。这个地址是与被调试者内存相关的。因此,我们需要从被调试者进程内存中读取内容

    To read data from another process' memory, we use the ReadProcessMemory function. It requires that the calling process should have the appropriate permission. Since the debugger only created the process, we do have the rights. Here is the code to process this debugging event:

    clip_image001[5]Collapse

    case OUTPUT_DEBUG_STRING_EVENT:
    {
       CStringW strEventMessage;  // Force Unicode
       OUTPUT_DEBUG_STRING_INFO & DebugString = debug_event.u.DebugString;
     
       WCHAR *msg=new WCHAR[DebugString.nDebugStringLength];
       // Don't care if string is ANSI, and we allocate double...
     
       ReadProcessMemory(pi.hProcess,       // HANDLE to Debuggee
             DebugString.lpDebugStringData, // Target process' valid pointer
             msg,                           // Copy to this address space
             DebugString.nDebugStringLength, NULL);
     
       if ( DebugString.fUnicode )
          strEventMessage = msg;
       else
          strEventMessage = (char*)msg; // char* to CStringW (Unicode) conversion.
     
       delete []msg;
       // Utilize strEventMessage
    }

    要从另一个进程内存中读取数据,我们使用ReadProcessMemory函数。那要求调用进程需要有适当的权限。因为调试器仅创建了那个进程,我们当然有这个权利。这是处理这个调试事件的代码:

    case OUTPUT_DEBUG_STRING_EVENT:
    {
       CStringW strEventMessage;  // 强制 Unicode
       OUTPUT_DEBUG_STRING_INFO & DebugString = debug_event.u.DebugString;
     
       WCHAR *msg=new WCHAR[DebugString.nDebugStringLength];
       // 不用关心字符串是不是ANSI编码,我们分配双倍空间...
     
       ReadProcessMemory(pi.hProcess,       // 被调试者的HANDLE
             DebugString.lpDebugStringData, // 目标进程的正确指针
             msg,                           // 复制这个地址空间
             DebugString.nDebugStringLength, NULL);
     
       if ( DebugString.fUnicode )
          strEventMessage = msg;
       else
          strEventMessage = (char*)msg; // char*到CStringW(Unicode)的转换.
     
       delete []msg;
       // 使用 strEventMessage
    }

    What if the debuggee terminates before the debugger copies the memory contents?

    如果被调试者在调试器复制内存内容时终止了?

    Well... In that case, I would like to remind you: when the debugger is processing a debugging event, all threads in the debuggee are suspended. The process cannot kill itself in anyway at this moment. Also, no other method can terminate the process (Task Manager, Process Explorer, Kill utility...). Attempting to kill the process from these utilities will, however, schedule the terminating process. Thus, the debugger would receive EXIT_PROCESS_DEBUG_EVENT as the next event!

    好……在这种情况下,我想提醒你:当调试器处理一个调试事件时,挂起被调试者的所有线程。这个时候那个进程就无法杀死自己。并且,没有其它的方法可以终止那个进程(任务管理器,进程资源管理器,杀死效用(Kill utility)……)。然而,这些工具的杀死那个进程的企图将会按照调度终止进程。因此,调试器将会接收到的下一个事件是EXIT_PROCESS_DEBUG_EVENT

    B. Processing CREATE_PROCESS_DEBUG_EVENT
    B. 处理CREATE_PROCESS_DEBUG_EVENT

    This event is raised when the process (debuggee) is being spawned. This would be the first event the sebugger receives. For this event, the relevant member of DEBUG_EVENT would be CreateProcessInfo. Here is the structure definition of CREATE_PROCESS_DEBUG_INFO:

    clip_image001[6]Collapse

    struct CREATE_PROCESS_DEBUG_INFO
    {
        HANDLE hFile;   // The handle to the physical file (.EXE)
        HANDLE hProcess; // Handle to the process
        HANDLE hThread;  // Handle to the main/initial thread of process
        LPVOID lpBaseOfImage; // base address of the executable image
        DWORD dwDebugInfoFileOffset;
        DWORD nDebugInfoSize;
        LPVOID lpThreadLocalBase;
        LPTHREAD_START_ROUTINE lpStartAddress;
        LPVOID lpImageName;  // Pointer to first byte of image name (in Debuggee)
        WORD fUnicode; // If image name is Unicode.
    };

    当那个进程(被调试者)产生时会引发这个事件。这是调试器(sedebugger)接收到的第一个事件。对于这个事件,CreateProcessInfoDEBUG_EVENT的相关成员。这是CREATE_PROCESS_DEBUG_INFO结构体的定义:

    struct CREATE_PROCESS_DEBUG_INFO
    {
        HANDLE hFile;   // 物理文件的句柄(.EXE)
        HANDLE hProcess; //进程的句柄
        HANDLE hThread;  // 进程的main/初始化线程的句柄
        LPVOID lpBaseOfImage; // 可执行映像的基址
        DWORD dwDebugInfoFileOffset;
        DWORD nDebugInfoSize;
        LPVOID lpThreadLocalBase;
        LPTHREAD_START_ROUTINE lpStartAddress;
        LPVOID lpImageName;  // 指向映像名称第一个字节的指针(在被调试者中)
        WORD fUnicode; // 映像名称是否Unicode编码.
    };

    Please note that hProcess and hThread may not have the same handle values we have received in pi (PROCESS_INFORMATION). The process-ID and the thread-ID would, however, be the same. Each handle you get by Windows (for the same resource) is different from other handles, and has a different purpose. So, the debugger may choose to display either the handles or the IDs.

    请注意我们接收到的pi (PROCESS_INFORMATION)中的hProcesshThread可能不是一个句柄值。然而进程ID和线程ID可能是相同的。你获取的每个窗口(对于同一个资源)句柄和其它的句柄都是不同的,也有不同的目的。因此,调试器可能选择显示句柄或者ID。

    The hFile as well as lpImageName can both be used to get the file-name of the process being debugged. Although, we already know what the name of the process is, since we only created the debuggee. But locating the module name of the EXE or DLL is important, since we would anyway need to find the name of the DLL while processing the LOAD_DLL_DEBUG_EVENT message.

    通过hFilelpImageName都能获取正在被调试进程的文件名称。尽管我们已经知道了进程的名称是什么,因为我们仅仅创建了被调试者。但是定位的EXE或DLL模块名称是重要的,因为当处理LOAD_DLL_DEBUG_EVENT消息时我们常常需要找到DLL的名称。

    As you can read in MSDN, lpImageName will never return the filename directly, and the name would be in the target-process. Furthermore, it may not have a filename in the target-process too (i.e., via ReadProcessMemory). Also, the filename may not be fully qualified (as I've tested). Thus, we will not use this method. We'll retrieve the filename from the hFile member.

    和你在MSDN上读到的一样,lpImageName从不会直接返回文件名称,而且这个名称会在目标进程中。更进步的说,在目标进程中可能没有文件名称(例如,通过ReadProcessMemory)。还有,文件名称可能不是完全符合要求(就像我曾经测试的)。因此,我们不会使用这个方法。我们将会从hFile成员获取文件名称。

    How to get the name of the file by HANDLE
    怎样通过HANDLE获取文件名称

    Unfortunately, we need to use the method described in MSDN that uses around 10 API calls to get the filename from the handle. I have slightly modified the function GetFileNameFromHandle. The code is not shown here for brevity, it is available in the source file attached with this article. Anyway, here is the basic code to process this event:

    case CREATE_PROCESS_DEBUG_EVENT:

    {
       CString strEventMessage = 
         GetFileNameFromHandle(debug_event.u.CreateProcessInfo.hFile);
       // Use strEventMessage, and other members
       // of CreateProcessInfo to intimate(亲密,暗示) the user of this event.
    }

    不幸的是,我们需要使用MSDN描述的方法,使用将近10个API调用来从句柄获取文件名称。我稍微修改了GetFileNameFromHandle函数。为了简洁,这里没有显示代码,可以从和这篇文章关联的源代码文件中获取。还有,这时处理这个事件的基本代码:

    {
       CString strEventMessage = 
         GetFileNameFromHandle(debug_event.u.CreateProcessInfo.hFile);
       // 使用 strEventMessage和CreateProcessInfo的其它成员
       // 来暗示这个事件的用户.
    }

    You may have noticed that I did not cover a few members of this structure. I would probably cover all of them in the next part of this article.

    你可能已经注意到了我没有讨论这个结构体的一些成员。我可能在这篇文章的下一个部分讨论它们的全部。

    C. Processing LOAD_DLL_DEBUG_EVENT
    C.处理LOAD_DLL_DEBUG_EVENT

    This event is similar to CREATE_PROCESS_DEBUG_EVENT, and as you might have guessed, it is raised when a DLL is loaded by the OS. This event is raised whenever a DLL is loaded, either implicitly or explicitly (when the debuggee calls LoadLibrary). This debugging event only occurs the first time the system attaches a DLL to the virtual address space of a process. For this event processing, we use the 'LoadDll' member of the union. It is of type LOAD_DLL_DEBUG_INFO:

    struct LOAD_DLL_DEBUG_INFO

    {
       HANDLE hFile;         // Handle to the DLL physical file.
       LPVOID lpBaseOfDll;   // The DLL Actual load address in process.
       DWORD dwDebugInfoFileOffset;
       DWORD nDebugInfoSize;
       LPVOID lpImageName;   // These two member are same as CREATE_PROCESS_DEBUG_INFO
       WORD fUnicode;
    };

    这个事件和CREATE_PROCESS_DEBUG_EVENT很相似,就像你能猜到的,这个事件会在OS载入了一个DLL时引发。无论什么时候载入了一个DLL这个都会引发这个事件,不论是隐式的还是明确的(当被调试者调用LoadLibrary时)。这个调试事件仅在系统第一次关联一个DLL到一个进程的虚拟地址空间发生。对于这个事件的处理,我们使用联合体的'LoadDll'成员。它的类型是LOAD_DLL_DEBUG_INFO

    struct LOAD_DLL_DEBUG_INFO
    {
       HANDLE hFile;         //DLL物理文件的句柄.
       LPVOID lpBaseOfDll;   // 进程中DLL实际导入地址.
       DWORD dwDebugInfoFileOffset;
       DWORD nDebugInfoSize;
       LPVOID lpImageName;   // 这两个成员和CREATE_PROCESS_DEBUG_INFO一样 
       WORD fUnicode;
    };

    For retrieving the filename, we would use the same function, GetFileNameFromHandle, as we have used in CREATE_PROCESS_DEBUG_EVENT. I will list out the code for processing this event when I would describe UNLOAD_DLL_DEBUG_EVENT, since the UNLOAD_DLL_DEBUG_EVENT does not have any direct information available to find out the name of the DLL file.

    想要获取这个文件名称,我们使用和CREATE_PROCESS_DEBUG_EVENT事件中使用的相同的函数GetFileNameFromHandle。当我描述UNLOAD_DLL_DEBUG_EVENT时我将会列出处理这个事件的代码,因为UNLOAD_DLL_DEBUG_EVENT没有任何直接的可用信息来查找DLL文件的名称。

    D. Processing CREATE_THREAD_DEBUG_EVENT
    D.处理CREATE_THREAD_DEBUG_EVENT

    This debug event is generated whenever a new thread is created in the debuggee. Like CREATE_PROCESS_DEBUG_EVENT, this event is raised before the thread actually gets to run. To get information about this event, we use the 'CreateThread' union member. This variable is of type CREATE_THREAD_DEBUG_INFO:

    clip_image001[9]Collapse

    struct CREATE_THREAD_DEBUG_INFO
    {
      // Handle to the newly created thread in debuggee
      HANDLE hThread;
      LPVOID lpThreadLocalBase;
      // pointer to the starting address of the thread
      LPTHREAD_START_ROUTINE lpStartAddress;
    };

    无论何时被调试者创建了一个新的线程是这个调试事件就会产生。像CREATE_PROCESS_DEBUG_EVENT,这个事件是在一个线程实际开始运行前触发的。我们使用联合体成员来获取关于这个事件的信息。这个变量的类型是CREATE_THREAD_DEBUG_INFO:

    struct CREATE_THREAD_DEBUG_INFO
    {
      //被调试者创建的新线程的句柄
      HANDLE hThread;
      LPVOID lpThreadLocalBase;
      // 指向线程的起始地址的指针
      LPTHREAD_START_ROUTINE lpStartAddress;
    };

    The thread-ID for a newly arrived thread is available in DEBUG_EVENT::dwThreadId. Using this member to intimate the user is straightforward:

    case CREATE_THREAD_DEBUG_EVENT:

    {
       CString strEventMessage;
       strEventMessage.Format(L"Thread 0x%x (Id: %d) created at: 0x%x",
                debug_event.u.CreateThread.hThread,
                debug_event.dwThreadId,
                debug_event.u.CreateThread.lpStartAddress);
                // Thread 0xc (Id: 7920) created at: 0x77b15e58
    }

    DEBUG_EVENT::dwThreadId中可以得到新到来的线程的线程ID。使用这个成员暗示用户直截了当:

    case CREATE_THREAD_DEBUG_EVENT:
    {
       CString strEventMessage;
       strEventMessage.Format(L"Thread 0x%x (Id: %d) created at: 0x%x",
                debug_event.u.CreateThread.hThread,
                debug_event.dwThreadId,
                debug_event.u.CreateThread.lpStartAddress);
                // 线程0xc(Id:7920)创建在: 0x77b15e58
    }

    The 'lpStartAddress' is relevant to the debuggee and not the debugger; we are just displaying it for completeness. Remember this event is not received for the primary/initial thread of the process. It is received only for the subsequent thread creations in the debuggee.

    'lpStartAddress'和被调试者相关而不是调试器;我们只是为了完整的显示它。记住这个事件不是在进程的主/初始化线程中接收到的。仅在被调试者创建子线程时会接收到。

    E. Processing EXIT_THREAD_DEBUG_EVENT
    E. 处理EXIT_THREAD_DEBUG_EVENT

    This event is raised as soon as the thread returns, and the return code is available to the system. The 'dwThreadId' member of DEBUG_EVENT specifies which thread exited. To get the thread handle and other information that we received in CREATE_THREAD_DEBUG_EVENT, we need to store the information in some map. This event has a relevant member named 'ExitThread', which is of type EXIT_THREAD_DEBUG_INFO:

    struct EXIT_THREAD_DEBUG_INFO

    {
       DWORD dwExitCode; // The thread exit code of DEBUG_EVENT::dwThreadId
    };

    线程一返回就会引发这个事件,系统可以得到返回代码。DEBUG_EVENT的成员'dwThreadId'指定了哪个线程退出了。要获得CREATE_THREAD_DEBUG_EVENT事件中我们接收到的线程句柄和其它信息,我们需要在一些map中存储信息。这个事件和名称为'ExitThread'的成员关联,类型是EXIT_THREAD_DEBUG_INFO:

    struct EXIT_THREAD_DEBUG_INFO

    {
       DWORD dwExitCode; // DEBUG_EVENT::dwThreadId的线程退出代码
    };

    Here is the event handler code:

    case EXIT_THREAD_DEBUG_EVENT:
    {
       CString strEventMessage;
       strEventMessage.Format( _T("The thread %d exited with code: %d"),
          debug_event.dwThreadId,
          debug_event.u.ExitThread.dwExitCode);    // The thread 2760 exited with code: 0
    }

    这是事件处理代码:

    case EXIT_THREAD_DEBUG_EVENT:
    {
       CString strEventMessage;
       strEventMessage.Format( _T("%d 线程退出,代码: %d"),
          debug_event.dwThreadId,
          debug_event.u.ExitThread.dwExitCode);    //2760线程退出,代码:0
    }
    F. Processing UNLOAD_DLL_DEBUG_EVENT
    F.处理UNLOAD_DLL_DEBUG_EVENT

    Of course, this event occurs when a DLL is unloaded from the debuggee's memory. But wait! It is only generated against FreeLibrary calls, and not when the system unloads DLLs. The debuggee may call LoadLibrary multiple times, and thus only the last call to FreeLibrary would raise this event. It means, the implicitly loaded DLLs will not receive this event when they are unloaded, when the process exits. (You can verify this assertion in your favorite debugger!)

    当然,这个事件在一个DLL从被调试者内存中卸载时发生。但是等等!它只FreeLibrary调用时产生,而不是当系统卸载DLL时。被调试者可能调用LoadLibrary多次,因此仅在最后一次调用FreeLibrary时会触发这个事件。这意味着,当进程退出时隐式装载的DLL不会在卸载时接收到这个事件。(你可以在你最喜欢的调试器中验证这个断言!)。

    For this event, you use the 'UnloadDll' member of the union, which is of type UNLOAD_DLL_DEBUG_INFO:

    struct UNLOAD_DLL_DEBUG_INFO

    {
        LPVOID lpBaseOfDll;
    };

    对于这个事件,你使用联合体的'UnloadDll'成员,类型是UNLOAD_DLL_DEBUG_INFO:

    struct UNLOAD_DLL_DEBUG_INFO
    {
        LPVOID lpBaseOfDll;
    };

    As you can see, only the base-address of the DLL (a simple pointer) is available for us to process this event. This is the reason I had delayed giving the code for LOAD_DLL_DEBUG_EVENT. In the DLL loading event, we get the 'lpBaseOfDll' also. We can use the map (or another data structure you like) to store the name of the DLL against the base-address of the DLL. The same base-address would arrive while processing UNLOAD_DLL_DEBUG_EVENT.

    正如你可以看到的,仅可以获得DLL的基址(一个简单的指针)来处理这个事件。这就是我推迟给出LOAD_DLL_DEBUG_EVENT代码的原因。在DLL装载的代码中,我们也获得了'lpBaseOfDll'。我们可以使用map(或你喜欢的其它的数据结构)来存储对应DLL基址的DLL名称。在处理UNLOAD_DLL_DEBUG_EVENT时会接收到相同的基址。

    It should be noted that not all DLL-load events would get the DLL-unload event; still, we have to store all DLL names into the map, since LOAD_DLL_DEBUG_EVENT doesn't provide us info on how the DLL was loaded.

    应该注意到并不是所有的DLL装载事件都会获取DLL卸载事件;还有,我们还得把所有DLL名称保存到map中,因为LOAD_DLL_DEBUG_EVENT不给我们提供DLL是怎么装载的信息。

    Here is the code to process these two events:

    std::map < LPVOID, CString > DllNameMap;

    ...
    case LOAD_DLL_DEBUG_EVENT:
    {
       strEventMessage = GetFileNameFromHandle(debug_event.u.LoadDll.hFile);
     
     
       // Storing the DLL name into map. Map's key is the Base-address
       DllNameMap.insert(
          std::make_pair( debug_event.u.LoadDll.lpBaseOfDll, strEventMessage) );
     
       strEventMessage.AppendFormat(L" - Loaded at %x", debug_event.u.LoadDll.lpBaseOfDll);
    }
    break;
    ...
    case UNLOAD_DLL_DEBUG_EVENT:
    {
       strEventMessage.Format(L"DLL '%s' unloaded.",
          DllNameMap[debug_event.u.UnloadDll.lpBaseOfDll] ); // Get DLL name from map.
    }
    break;

    这是处理这两个事件的代码:

    std::map < LPVOID, CString > DllNameMap;
    ...
    case LOAD_DLL_DEBUG_EVENT:
    {
       strEventMessage = GetFileNameFromHandle(debug_event.u.LoadDll.hFile);
     
     
       // 将DLL名称存储到map中。Map的键值是基址。
       DllNameMap.insert(
          std::make_pair( debug_event.u.LoadDll.lpBaseOfDll, strEventMessage) );
     
       strEventMessage.AppendFormat(L" - Loaded at %x", debug_event.u.LoadDll.lpBaseOfDll);
    }
    break;
    ...
    case UNLOAD_DLL_DEBUG_EVENT:
    {
       strEventMessage.Format(L"DLL '%s' unloaded.",
          DllNameMap[debug_event.u.UnloadDll.lpBaseOfDll] ); // 从map中获取DLL名称
    }
    break;
    G. Processing EXIT_PROCESS_DEBUG_EVENT
    G.处理EXIT_PROCESS_DEBUG_EVENT

    This is one of the simplest debugging event, and as you can assess, would arrive when the process exists. This event would arrive irrespective(不论) of how the process exits - normally, terminated externally (Task Manager etc.), or the application's (debuggee) fault leading it to crash.

    这是最简单的调试事件之一,正如你可以评估的,当进程退出时会发生。不论进程如何退出,这个事件都会发生- 正常的,外部终止的(任务管理器等),或者是应用程序(被调试者)的错误导致的崩溃。

    We use the 'ExitProcess' member, which is of type EXIT_PROCESS_DEBUG_INFO:

    clip_image001[16]Collapse

    struct EXIT_PROCESS_DEBUG_INFO
    {
        DWORD dwExitCode;
    };

    我们使用'ExitProcess'成员,类型是EXIT_PROCESS_DEBUG_INFO:

    struct EXIT_PROCESS_DEBUG_INFO
    {
        DWORD dwExitCode;
    };

    As soon as this event occurs, we also end the debugger-loop and terminate the debugging thread. For this, we can use a variable to control the loop (the 'for' loop shown in the first page), and set its value to indicate loop termination. Please download the attached files to see the entire code.

    bool bContinueDebugging=true;

    ...
    case EXIT_PROCESS_DEBUG_EVENT:
    {
       strEventMessage.Format(L"Process exited with code:  0x%x", 
                              debug_event.u.ExitProcess.dwExitCode);
       bContinueDebugging=false;
    }
    break;

    这个事件一发生,我们就结束调试循环并终止调试线程。对于这些,我们可以使用一个变量控制循环(第一页中显示的'for'循环),并设置它的值来表明循环终止。请下载关联文件来查看整个代码。

    bool bContinueDebugging=true;
    ...
    case EXIT_PROCESS_DEBUG_EVENT:
    {
       strEventMessage.Format(L"进程退出,代码:0x%x", 
                              debug_event.u.ExitProcess.dwExitCode);
       bContinueDebugging=false;
    }
    break;
    H. Processing EXCEPTION_DEBUG_EVENT
    H. 处理EXCEPTION_DEBUG_EVENT

    This is the prodigious(惊人的,很大的) event amongst all the debugging events! From MSDN:

    This is generated whenever an exception occurs in the process being debugged. Possible exceptions include attempting to access inaccessible memory, executing breakpoint instructions, attempting to divide by zero, or any other exception noted in Structured Exception Handling. The DEBUG_EVENT structure contains an EXCEPTION_DEBUG_INFO structure. This structure describes the exception that caused the debugging event.

    在所有调试事件中这是一个很大的事件!摘自MSDN:

    无论何时被调试进程发生异常时就会产生这个事件。可能的异常包括试图访问不可访问内存,执行断点指令,试图被零除,或者在结构化异常处理中注明的其它异常。DEBUG_EVENT结构体包含了一个EXCEPTION_DEBUG_INFO结构体。这个结构体描述了引起调试事件的异常。

    This debugging event needs a separate article to complete it fully (or partially!). Thus, I would discuss only one type of exception event, along with an introduction to this event itself.

    这个调试事件需要一篇单独的文章来完整的(或部分的)描述。因此,我只讨论异常事件的一个类型,连同这个事件本身一起介绍。

    The member variable 'Exception' holds the information regarding the exception just occurred. It is of type EXCEPTION_DEBUG_INFO:

    struct EXCEPTION_DEBUG_INFO

    {
        EXCEPTION_RECORD ExceptionRecord;
        DWORD dwFirstChance;
    };

    成员变量'Exception'包含关于刚刚发生的异常的信息。它的类型是EXCEPTION_DEBUG_INFO:

    struct EXCEPTION_DEBUG_INFO
    {
        EXCEPTION_RECORD ExceptionRecord;
        DWORD dwFirstChance;
    };

    The 'ExceptionRecord' member of this structure contains detailed information regarding the exception. It is of type EXCEPTION_RECORD:

    struct EXCEPTION_RECORD

    {
        DWORD     ExceptionCode;
        DWORD     ExceptionFlags;
        struct _EXCEPTION_RECORD *ExceptionRecord;
        PVOID     ExceptionAddress;
        DWORD     NumberParameters;
        ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];  // 15
    };

    这个结构体的'ExceptionRecord'成员包含关于这个异常的详细信息。它的类型是EXCEPTION_RECORD:

    struct EXCEPTION_RECORD
    {
        DWORD     ExceptionCode;
        DWORD     ExceptionFlags;
        struct _EXCEPTION_RECORD *ExceptionRecord;
        PVOID     ExceptionAddress;
        DWORD     NumberParameters;
        ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];  // 15
    };

    The detailed information is put into this sub-structure, because exceptions may appear nested, and would be linked to each other in a linked-list manner. It is out of topic for now to discuss nested exceptions.

    这个子结构填充了详细信息,因为异常可能嵌套出现,而且以链表方式相互关联。讨论嵌套异常已经超出了的现在的话题。

    Before we delve(钻研) into EXCEPTION_RECORD, it is important to discuss EXCEPTION_DEBUG_INFO::dwFirstChance.

    在我们研究EXCEPTION_RECORD前,讨论一下EXCEPTION_DEBUG_INFO::dwFirstChance是很重要的。

    Are exceptions giving chances?

    异常提供了机会?

    Not exactly! When a process is being debugged, the debugger always receives the exception before the debuggee gets it. You must have seen "First-chance exception at 0x00412882 in SomeModule:..." while debugging your Visual C++ module. This is referred to as First Chance Exception. The same exception may or may not follow with a second chance exception.

    不太准确!当一个进程被调试时,调试器总在被调试者获取它之前接收到。当调试你的Visual C++模块时你一定见过“在一些模块中首次异常发生在0x00412882:...”。这被称为首次机会异常。第二次相同的异常可能会也可能不会随之发生。

    When the debuggee gets the exception, it is termed as Second Chance Exception. The debuggee may handle the exception, or may simply crash down. These types of exceptions are not C++ exceptions, but Windows' SEH (structure exception handling) mechanism. I would cover more about it in the next part of this article.

    当被调试者获取异常时,这称为第二次机会异常。被调试者可能处理这个异常,或可能简单的崩溃了。这些类型的异常不是C++异常,而是Windows的SEH(结构异常处理)机制。我会在这篇文章的下一部分讨论关于这些内容的更多内容。

    The debugger gets exceptions first (First-chance exception), so that it can handle it before giving it to the debuggee. The break-point exception is a kind of exception, which is relevant to the debugger, not the debuggee. Some libraries also generate First chance exceptions to aid the debugger and the debugging process.

    调试器首先获取异常(首次机会异常),以便可以在投递给被调试者前处理它。断点异常时一种与调试器相关的异常,而不是被调试者。一些库也会产生首次机会异常来辅助调试器和调试过程。

    A word for ContinueDebugEvent
    一个关于ContinueDebugEvent的字

    The third parameter (dwContinueStatus) of this function is relevant only after an exception event is received. For non-exception events that we discussed, the system ignores the value passed to this function.

    这个函数的第三个参数(dwContinueStatus)仅在异常事件到达后有用。对于我们讨论的非异常事件,系统会忽略传递给这个函数的值。

    After the exception event processing, ContinueDebugEvent should be called with:

    • DBG_CONTINUE if the exception event was successfully handled by the debugger. No action is required by the debuggee, and the debuggee can run normally.
    • DBG_EXCEPTION_NOT_HANDLED if this event is not handled/resolved by the debuggee. The debugger might just record this event, notify the debugger-user, or do something else.

    在异常事件处理后,ContinueDebugEvent需要这样调用:

    · DBG_CONTINUE如果调试器成功处理异常事件。不要求被调试者有任何动作,被调试者也可以正常运行。

    · DBG_EXCEPTION_NOT_HANDLED如果被调试者没有处理/解决这个事件。调试器可能仅仅记录下这个事件,通知调试器用户,或做其它的。

    Please note that returning DBG_CONTINUE for the improper debugging event would raise the same event in the debugger, and the same event would arrive indefinitely. Since we are in the early stage of writing debuggers, we should play safe, and return EXCEPTION_NOT_HANDLED (give up flag!). The exclusion, for this article, is the Break-point event, which I am discussing next.

    请注意在调试器中对于不合适的调试事件返回DBG_CONTINUE将会引起相同的事件,同样的事件将会无限的发生。因为我们处在编写调试器的早期,因此我们应该安全进行,并且返回EXCEPTION_NOT_HANDLED(放弃标识!)。这篇文章将断点事件排除,我将会在下一篇讨论它。

    Exceptions codes
    异常代码

    The EXCEPTION_RECORD::ExceptionCode variable holds the arrived exception code, and can have one of these codes (ignore nested exceptions!):

    1. EXCEPTION_ACCESS_VIOLATION
    2. EXCEPTION_ARRAY_BOUNDS_EXCEEDED
    3. EXCEPTION_BREAKPOINT
    4. EXCEPTION_DATATYPE_MISALIGNMENT
    5. EXCEPTION_FLT_DENORMAL_OPERAND
    6. EXCEPTION_FLT_DIVIDE_BY_ZERO
    7. EXCEPTION_FLT_INEXACT_RESULT
    8. EXCEPTION_FLT_INVALID_OPERATION
    9. EXCEPTION_FLT_OVERFLOW
    10. EXCEPTION_FLT_STACK_CHECK
    11. EXCEPTION_FLT_UNDERFLOW
    12. EXCEPTION_ILLEGAL_INSTRUCTION
    13. EXCEPTION_IN_PAGE_ERROR
    14. EXCEPTION_INT_DIVIDE_BY_ZERO
    15. EXCEPTION_INT_OVERFLOW
    16. EXCEPTION_INVALID_DISPOSITION
    17. EXCEPTION_NONCONTINUABLE_EXCEPTION
    18. EXCEPTION_PRIV_INSTRUCTION
    19. EXCEPTION_SINGLE_STEP
    20. EXCEPTION_STACK_OVERFLOW

    EXCEPTION_RECORD::ExceptionCode变量保存了到达的异常的代码,并为下列值之一(忽略嵌入异常!):

    1. EXCEPTION_ACCESS_VIOLATION
    2. EXCEPTION_ARRAY_BOUNDS_EXCEEDED
    3. EXCEPTION_BREAKPOINT
    4. EXCEPTION_DATATYPE_MISALIGNMENT
    5. EXCEPTION_FLT_DENORMAL_OPERAND
    6. EXCEPTION_FLT_DIVIDE_BY_ZERO
    7. EXCEPTION_FLT_INEXACT_RESULT
    8. EXCEPTION_FLT_INVALID_OPERATION
    9. EXCEPTION_FLT_OVERFLOW
    10. EXCEPTION_FLT_STACK_CHECK
    11. EXCEPTION_FLT_UNDERFLOW
    12. EXCEPTION_ILLEGAL_INSTRUCTION
    13. EXCEPTION_IN_PAGE_ERROR
    14. EXCEPTION_INT_DIVIDE_BY_ZERO
    15. EXCEPTION_INT_OVERFLOW
    16. EXCEPTION_INVALID_DISPOSITION
    17. EXCEPTION_NONCONTINUABLE_EXCEPTION
    18. EXCEPTION_PRIV_INSTRUCTION
    19. EXCEPTION_SINGLE_STEP
    20. EXCEPTION_STACK_OVERFLOW

    Relax! I am not discussing all of them, but one: EXCEPTION_BREAKPOINT. Okay, here is the code:

    case EXCEPTION_DEBUG_EVENT:

    {
       EXCEPTION_DEBUG_INFO& exception = debug_event.u.Exception;
       switch( exception.ExceptionRecord.ExceptionCode)
       {
          case STATUS_BREAKPOINT:  // Same value as EXCEPTION_BREAKPOINT
     
             strEventMessage= "Break point";
             break;
     
          default:
             if(exception.dwFirstChance == 1)
             {
                strEventMessage.Format(L"First chance exception at %x, exception-code: 0x%08x",
                            exception.ExceptionRecord.ExceptionAddress,
                            exception.ExceptionRecord.ExceptionCode);
             }
             // else
             // { Let the OS handle }
     
             // There are cases where OS ignores the dwContinueStatus,
             // and executes the process in its own way.
             // For first chance exceptions, this parameter is not-important
             // but still we are saying that we have NOT handled this event.
     
             // Changing this to DBG_CONTINUE (for 1st chance exception also),
             // may cause same debugging event to occur continously.
             // In short, this Debugger does not handle debug exception events
             // efficiently, and let's keep it simple for a while!
     
             dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
             }
     
             break;
    }

    放松点!我不会全部讨论它们,除了一个:EXCEPTION_BREAKPOINT。好了,这是代码:

    case EXCEPTION_DEBUG_EVENT:
    {
       EXCEPTION_DEBUG_INFO& exception = debug_event.u.Exception;
       switch( exception.ExceptionRecord.ExceptionCode)
       {
          case STATUS_BREAKPOINT:  // 和EXCEPTION_BREAKPOINT 的值相同
     
             strEventMessage= "Break point";
             break;
     
          default:
             if(exception.dwFirstChance == 1)
             {
                strEventMessage.Format(L"First chance exception at %x, exception-code: 0x%08x",
                            exception.ExceptionRecord.ExceptionAddress,
                            exception.ExceptionRecord.ExceptionCode);
             }
             // 否则
             // {让操作系统处理 }
     
             // 有些情况下操作系统忽略dwContinueStatus,
             // 并以它自己的方式执行进程.
             // 对于首发机会异常,这个参数并不重要
             // 但是我们仍然要说我们还没有处理这个事件.
     
             // 将这个改为DBG_CONTINUE (对于首发机会异常也是),
             // 可能引起同样的调试事件连续出现.
             // 总之,这个调试器没有处理调试异常事件
             // 高效,让我们保持一段时间的简单性!
     
             dwContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
             }
     
             break;
    }

    You might be aware of what a breakpoint is. Out of the standard debugger perspective, the break-pointing can happen with the DebugBreak API, or the {int 3} assembly instruction, or System.Diagnostics.Debugger.Break in the .NET Framework. The debugger would receive the Debug-exception code STATUS_BREAKPOINT (same as EXCEPTION_BREAKPOINT) when any of these occur in the running process. The debuggers generally use this event to break the running process, and may display the source code where the event occurred. But in our basic debugger, we would just display this event to the user. No source code or the instruction location is shown. We'll cover displaying the source code in the next part of this article.

    你可能意识到了什么是断点。在标准调试器场景外,断点可以发生于DebugBreak API,或{int 3}汇编指令,或.NET框架的System.Diagnostics.Debugger.Break。在正在运行的进程中,当这些情况中的任何一个发生时,调试器会收到调试异常代码STATUS_BREAKPOINT(和EXCEPTION_BREAKPOINT相同)。但是在我们的基本的调试器中,我们仅将这个事件展示给用户。不展示任何源代码或指令。我们将会在这篇文章的下一部分介绍显示源代码。

    Raising breakpoint from a process which is not being debugged would crash the application, or may display the JIT dialog box. The is the reason I used:

    if ( !IsDebuggerPresent() )

       AfxMessageBox(L"No debugger is attached currently.");
    else
       DebugBreak();

    从一个没有被调试过的进程中设定一个断点可能会使程序崩溃,或者可能显示JIT对话框。该是我用过的:

    if ( !IsDebuggerPresent() )
       AfxMessageBox(L"当前没有关联调试器.");
    else
       DebugBreak();

    As a final note to this simplest debug-exception event: EXCEPTION_DEBUG_EVENT would be raised first time by the kernel itself, and would always arrive. Debuggers like Visual Studio ignore this very first breakpoint exception, but debuggers like WinDbg would always show you this event too.

    作为最后一个最简单的调试异常事件:EXCEPTION_DEBUG_EVENT会被内核本身发起,并且同时还会到达。像Visual Studio这样的调试器会忽略第一个断点异常,但是像WinDbg这样的调试器则会向你显示这个事件。

    Winding up...

    清盘……

    Use any process to debug or use the attached debuggee named DebugMe:

    使用任何调试的程序或者名称为DebugMe的被关联的被调试者:

    clip_image002

    The binaries (EXEs) attached here are compiled with Visual Studio 2005 Service Pack 1. You may not have the VC++ runtime libraries for the same version. You can download them from Microsoft.com or rebuild the projects from your IDE.

    附加的二进制文件(EXE)使用Visual Studio 2005 Service Pack 1编译。你可能没有相同版本的VC++运行时链接库。你可以从Microsoft.com下载或从你的IDE中重建工程。

    Follow up:

    追加:

    License

    许可

    This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

    这篇文章和相关的源代码和文件,由The Code Project Open License (CPOL)授权。

    About the Author

    关于作者

    Ajay Vijayvargiya

    clip_image003[4]

    Software Developer (Senior)
    clip_image004India
    Member

    Started programming with GwBasic back in 1996 (Those lovely days!). Found the hidden talent!
    早在1996年开始使用GwBasic编程(那些可爱的日子!)。发现了隐藏的天赋!

    Touched COBOL and Quick Basic for a while.
    接触过一段时间的COBOL和Quick Basic。

    Finally learned C and C++ entirely on my own, and fell in love with C++, still in love! Began with Turbo C 2.0/3.0, then to VC6 for 4 years! Finally on VC2008/2010.
    最后自学了C和C++,然后爱上了C++,一直爱着!开始使用Turbo C 2.0/3.0,接着4年VC6 !最后VC2008/2010。

    I enjoy programming, mostly the system programming, but the UI is always on top of MFC! Quite experienced on other environments and platforms, but I prefer Visual C++. Zeal to learn, and to share!

    我喜欢编程,大部分是系统编程,而UI总是在MFC基础上!其它环境与平台下也有些经验,但我更喜欢Visual C++。热爱学习和分享!

    展开全文
  • [调试]_[初级]_[VS本地Windows调试器]

    千次阅读 2014-11-28 10:32:48
    条件断点培训.


    条件断点

    比如一个循环里,当循环变量i达到某个值时,断点才停止.

    -- 先对某行打断点

     

    -- 之后右键这个断点,选择 ”条件”,在弹出的输入框中设置i判断值.

    i==8 ,i>8



    展开全文
  • 设置windows的默认调试器

    千次阅读 2016-07-22 11:35:43
    windows操作系统一个默认调试器设置,使程序出现崩溃时,自动调用这个调试器进行调试。这个设置在windows的注册表中,对于32位操作系统:HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows NT/CurrentVersion/...
  • windows简单调试器源码2700行左右代码

    千次阅读 2015-01-11 18:40:17
    简单调试器项目中on开头的...在异常事件中访问异常、int3异常、单步异常是跟实现调试器功能密切相关的异常事件,这里三个函数分别处理三个函数分别为OnExceptionAccess、OnExceptionBreakPoint、OnExceptionSingle
  • Windows用户态调试器原理

    千次阅读 2013-01-29 08:46:59
    Windows用户态调试器原理 Windows操作系统提供了一组API来支持调试器。 这些API可以分为三类: 创建调试目标的API; 在调试循环中处理调试事件的API。 查看和修改调试目标的API。 接下来将会分别对这三种API...
  • Windows系统默认调试器设置

    千次阅读 2012-06-11 17:47:49
    Windows系统默认调试器设置 1.使用运行打开drwtsn32 -i 注册华生医生到注册表如图: 2.使用运行打开drwtsn32可以进行一些常用的设置如图:   3.   注册表设置: HKEY_LOCAL_MACHINE/SOFTWARE/...
  • Windows基础调试器(BASIC DEBUGGER)

    千次阅读 2009-06-03 17:19:00
    Windows提供了许多调试程序的方法.其中一个叫Basic Debug.提供了基础调试方法.他是一个事件驱动的调试...两种方式运行基础调试器.1.其中一个方法是调试正在运行进程.要对运行中的进程进行调试,可以通过调用
  • 问题讲述:利用visualstudio打开网上down下来的代码,点击本地windows调试器,弹出窗口,说“无法启动程序,系统找不到指定的exe文件”。 原因:未对项目生成解决方案。 解决方法:在资源管理器,选中该项目,右键...
  • Windows内核调试器原理浅析

    千次阅读 2006-02-04 17:36:00
    文摘出处:http://www.xfocus.net/articles/200412/765.html创建时间:2004-12-23文章属性:原创文章提交:SoBeIt (kinsephi_at_hotmail.com)Windows内核调试器原理浅析 
  • Windows10为qt配置64为版本配置调试器

    千次阅读 2019-06-26 09:06:22
    一般对于qt哪怕你是在debug下,不支持调试的。 如图下需要自己下载cdb,添加安装,然后在具体某个项目中,选择x86,x64对应...window10 下载sdk 里面在安装过程自带有调试器,然后在安装目录下 选择具体某位下的cd...
  • 首先在这里下载调试器windbg (Debugging Tools for Windows) : http://download.csdn.net/download/zhang957411207/4750492 蓝后在qt里设置: 路径默认安装就是这个,我的是装的64位的:C:\Program Files\...
  • 如果安装Qt时使用的是Visual Studio的预编译版,那么很可能就会缺少调试器(Debugger),而使用MSVC的Qt对应的原生调试器是CDB(对应MinGW的Qt使用GDB调试器)。本文介绍如何下载安装CDB,并在Qt Creator配置之。 ...
  • Windows中主流的调试器介绍

    千次阅读 2008-09-21 09:18:00
    调试器,顾名思义,就是用于调试的工具。为什么要调试呢?这得从一条虫子(bug)说起。江湖中传说还是在第一代真空管电子计算机时代,一次研究人员发现计算机的行为不正常。于是他们就开始检查每个环节想找到原因,...
  • Windows下Qt creator调试器的安装与配置

    万次阅读 2017-12-03 17:17:13
    最近正在学习Qt,不过在使用Qt时发现没法进行调试。... Unknown debugger type “No engine” Unable to create a debugger engine of the type “No ...我使用的编译环境时MSVC2015 64bit,因此需要配置相应的调试器
  • 不错的关于windows平台下用户模式调试器的原理  所谓调试器实际上是一个很宽泛的概念,凡是能够... 内核态调试器直接工作在操作系统内核一级,在硬件与操作系统之间针对系统核心或驱动进行调试,常见的SoftICE、Wi
  • windows下Qt Creator 调试器配置经历

    千次阅读 2013-03-14 10:11:10
    使用QtCreator约两个多月了,两个月内都是QtCreator与VS两个混用,因为一直是QtCreator的调试器没有配置明白,于是我选用了Visual 2008来调试。VS2008对于编Qt来说感觉就是没有QtCreator好用,不过各取所长吗。...
  • 在线安装Qt后发现使用的是Visual Studio的预编译版,缺少调试器(Debugger),而使用MSVC的Qt对应的原生调试器是CDB(对应MinGW的Qt使用GDB调试器)。本文介绍如何下载安装CDB,并在Qt Creator配置之。 示例环境:...
  • IDE、编辑器、编译器、解释器、调试器分别是什么

    千次阅读 多人点赞 2020-08-10 17:38:11
    IDE、编辑器、编译器、解释器与调试器写在前面IDE编辑器编译器解释器调试器编译器和解释器的区别和优缺点编译器和调试器的区别 写在前面 对于这些概念,我只是记录一下自己的理解,如果不对的地方请多多指正。 IDE...
  • windows下为qt msvc版本配置调试器

    千次阅读 2018-06-18 22:48:13
    根据开发机的环境,下载我的机器是win10,因此下载win 10 sdk。https://developer.microsoft.com/en-US/windows/downloads/windows-10-sdk
  • Windows调试工具集

    千次阅读 2009-06-06 21:44:00
    1.2 Windows调试工具集应用场合一组调试器和工具当前版本6.6.0007.5下载地址http://www.microsoft.com/whdc/ddk/debugging/ Windows调试工具集(Debugging ToolsforWindows)
  • 想调试一个测试程序, 在Win7X86家庭普通版版上, 无法直接使用vs2008远程调试器, 报错提示. 大意是: "vs2008远程调试器无法应用在此版本Windows" 叭啦叭啦. 具体的版本号为: Windows 7 Kernel Version ...
  • VS调试windows服务

    2021-01-09 16:44:17
    调试windows服务,下面是步骤  windows服务启动后的调试  1、在需要调试处加上断点,例如  2、选择项目生成规则,例如  3、修改windows服务的名字和启动方式,也是安装后在资源管理中显示的名称...
  • 超强windows驱动调试工具,超强windows驱动调试工具,超强windows驱动调试工具,超强windows驱动调试工具,超强windows驱动调试工具,超强windows驱动调试工具,超强windows驱动调试工具,超强windows驱动调试工具.
  • QT设置调试器Debugger

    千次阅读 2019-06-27 22:37:17
    QT设置调试器Debugger ...QQ技术交流群:129518033 文章目录QT设置调试器Debugger1....安装本地调试器2.1 Debugging Tools for Windows/CDB下载2.2 Debugging Tools for Windows/CDB安装3.QT配置CDB3.1 手动...
  • cdb经典调试器

    热门讨论 2014-01-02 00:41:07
    cdb调试器

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 235,003
精华内容 94,001
关键字:

windows调试器有什么用