《软件调试》学习笔记——004 (第一章 软件调试基础 part2)

来源:互联网 发布:java程序员为什么转行 编辑:程序博客网 时间:2024/05/21 21:45

《软件调试》学习笔记——004 (第一章 软件调试基础 part2)

常用软件调试技术:

1) 断点(Breakpoint)

断点和单步执行是最基本的调试手段。其基本思想是让被调试程序在执行到该处时,暂停被调试程序的执行;中断到调试器中,让调试人员进行分析。分析结束后,可以让被调试程序继续执行。
根据断点所处的空间,可以分为以下3种断点:

  • 代码断点
    设置在内存空间的代码段(即,断点地址为代码段的某个地址),当CPU执行该地址时,断点命中,中断到调试器。在源代码上(xx文件xx行)设定的断点都是代码断点。
  • 数据断点
    断点地址为数据段的某个地址,某个变量或数据的起始地址。当CPU访问该地址的数据时,断点命中,中断到调试器。
    根据需要,可以定义触发断点的访问方式(读/写)、宽度(BYTE,WORD, DWORD)。

    [EasyVCR@csdn]
    怎样设置数据断点?
    gdb:    可以通过watchpoint来设置,当某个变量值改变(写)时中断到调试器;  (gdb) help watch
    WinDBG: ???

  • I/O断点
    断点地址为某一I/O地址。当CPU访问该I/O地址的端口时,断点命中,中断到调试器。
    和数据断点类似,也可设置断点被触发的访问宽度(BYTE,WORD, DWORD)。

根据断点的设置方法可以,可以分为软件断点和硬件断点:

  • 软件断点
    软件断点通过向断点所在位置插入断点指令来实现。不同平台的断点指令可能不同。比如IA32架构CPU的断点指令为 INT 3 (其对应的机器码为0xCC)。
    断点指令只能设置上面提到的代码断点;而不能设置数据断点或者I/O断点。

    [EasyVCR@csdn]
        VC的Debug版程序,所有的局部变量都被初始化为0xCC;这样当程序"跑飞",将数据作为代码来执行时,就会解析成断点指令,如果是在调试器下被调试,就会中断下来;如果不是以被调试模式运行,操作系统也会给你一个选择加载调试器调试的机会。
        而Release版的程序,局部变量被初始化为0x00; 当程序"跑飞"时,操作系统也不会解析成断点指令。这也是Debug和Release版程序在运行时表现不一致的一个重要方面。            
      
  • 硬件断点
    硬件断点通过CPU的调试寄存器设置。IA32架构CPU定义了8个调试寄存器(DR0 ~ DR7)。 对同一个调试会话,最多可同时设置4个硬件断点。

当中断到调试器时,被调试程序是处于【静止】状态。调试器会将被调试程序的状态保存到执行上下文(CONTEXT)中;当用户输入恢复执行的调试命令时,调试器再恢复被调试程序,使其继续运行。

[EasyVCR@csdn]
这个过程相当于自定义一个SuspendOrResumeProcess函数(遍历被调试进程中的所有线程, 然后调用SuspendTread)

void SuspendOrResumeProcess( DWORD dwPID, BOOL bSuspend ){    HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, dwPID);    if ( INVALID_HANDLE_VALUE != hSnapshot )    {        THREADENTRY32 te32 = { sizeof(THREADENTRY32) };        BOOL bFind = Thread32First( hSnapshot, &te32 );        for ( ; bFind; bFind = Thread32Next(hSnapshot, &te32) )        {            if ( te32.th32OwnerProcessID == dwPID)            {                HANDLE hThread = OpenThread( THREAD_SUSPEND_RESUME, FALSE, te32.th32ThreadID );                if ( NULL != hThread )                {                    bSuspend ? SuspendThread(hThread): ResumeThread(hThread);                }                CloseHandle( hThread );            }        }        CloseHandle( hSnapshot );    }}

ps,采用这种遍历线程的方式,对多线程程序,可能会导致各个线程中的执行情况与真实运行时有差异。
可能需要直接调用ntdll中的 ZwSuspendProcess/ZwResumeProcess 来挂起/恢复整个进程。
下图是Depends看到的ntdll的导出函数列表

typedef DWORD(WINAPI *PFSuspendProcess)(HANDLE hProcess);typedef DWORD(WINAPI *PFResumeProcess)(HANDLE hProcess);void SuspendOrResumeProcess( DWORD dwPID, BOOL bSuspend ){    HMODULE hNtDllLib = LoadLibrary(TEXT("ntdll.dll"));    PFSuspendProcess SuspendProcess = (PFSuspendProcess)GetProcAddress(hNtDllLib, "ZwSuspendProcess");    PFResumeProcess  ResumeProcess  = (PFResumeProcess )GetProcAddress(hNtDllLib, "ZwResumeProcess" );    if ( SuspendProcess  && ResumeProcess )    {        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);        bSuspend? SuspendProcess(hProcess) : ResumeProcess(hProcess);        CloseHandle(hProcess);    }    FreeLibrary(hNtDllLib);}

追踪点与条件断点

  • 追踪点(Tracepoint)
    一种特殊的断点,当调试器中断到追踪点时,自动执行预先定义的追踪行为(通常为收集信息,比如打印某些变量的值),然后立即自动恢复被调试程序,使其继续运行。
    [EasyVCR@csdn]
    gdb:          (gdb) help tracepoints
    WinDBG: 怎样设置Tracepoint?

  • 条件断点(Conditional Breakpoint)
    一种特殊的断点,仅当定义的条件被满足时,调试器才中断被调试程序,让调试人员进行调试。
    实际上,对调试器而言,每次执行到条件断点都会中断,只是中断后处理的行为不同:如果不满足条件断点定义的条件,则立即恢复被调试程序继续运行(调试人员感觉不到);如果满足,则中断被调试程序,让调试人员进行调试。

 


原创粉丝点击