• 调试器编写第一讲,调试器基本框架


                      调试器编写第一讲,调试器基本框架


    作者:IBinary
    出处:http://www.cnblogs.com/iBinary/
    版权所有,欢迎保留原文链接进行转载:)

    今天开始调试器第一讲,调试器的基本框架,我们用过很多调试器,比如 WinDbg,OllyDbg,那为什么我们还要自己编写调试器哪?

    原因是,OllyDbg等等的各种调试器都太容易被针对了,写调试器,主要是理解别人怎么反调试,并且我们怎么在安全开发的时候,让我们的软件针对调试器.今天就开始调试器第一讲,调试器的基本框架

    很多人认为调试器怎么写,没思路,其实调试器就是调用API,熟练运用这些API,则可以进行软件调试

    一丶写调试器注意的问题

    首先,我们思考一个问题,我们要调试我们的程序,要怎么让我们的程序知道被调试了

    是这样的,微软已经帮我们提供了API了,比如我们常用WriteProcessMemory这个API,想一下,微软怎么可能提供在别人进程里面写内存的API哪?

    其实这个就是调试器用的,只不过被我们玩坏了.

    那么我们MSDN搜索一下这个API,就可以找到所有和调试器相关的API

    可以在下方看到,所以和调试器相关的API了.

    API就怎么多,熟练运用即可.

    二丶调试器API各个API的意思

     这里介绍下各个API的意思,并不细讲,等到用到的时候才会细讲怎么用.主要是熟悉一下,算是翻译一下API吧.

    /*
    ContinueDebugEvent      :看名字就知道,继续调试事件,意思就是调试程序的时候有事件来,你处理完了要继续.
    
    DebugActiveProcess      :调试程序,附加进程,这个API就是,如果我们的程序打开了,那么调用这个API,传入进程ID是可以附加调试的.
    
    DebugActiveProcessStop    :停止调试器,调试的指定进程,也就是调试器要停止对某一个进程的调试
    
    debugBreak           :如果程序处于调试的状态,,如果发生断点异常(下断点),允许线程,通知我们的调试器来调试,处理这个异常.否则系统接收
    DebugBreakProcess       :在指定的进程中,产生一个断点异常
    DebugSetProcessKillOnExit  :设置调试的线程,退出的时候进行的操作. FatalExit            :强制退出调用的进程,并将控制权交给调试器 FlushInstructionCache :刷新指令的高速缓存 GetThreadContext    :获取寄存器的信息,具体获取那个,要设置标志位,标志位在注释中(MSDN查不到) GetThreadSelectorEntry  :获取指定选择器和线程的描述的入口表. IsDebuggerPresent   :判断进程是否在调试器下面运行(和我们前面说的反调试那个差不多,都是判断调试是否运行) OutPutDebugString :调试输出字符串. ReadProcessMemory :读取指定进程的某块数据 SetThreadContext      :设置寄存器 WaitForDebugEvent      :等待调试事件 WriteProcessMemory    :往指定进程写某块数据
    */

    三丶查阅MSDN,看下调试器的说明

    我们要调试一个程序,第一步就要创建这个程序,那么创建程序是用CreateProcess,那么我们看下MSDN有没有特别说明

    随便进去一个调试的API,看到下方说了一个基本的调试,点击去看看.

    第一个说,关于基本调试,也就是介绍一下

    第二个说,调试的参考,我们先看下第一个怎么说把

    可以看到,他告诉了我们,关于基本调试个各种步骤.

    第一个: 说的是,调试的函数,也就是上面的我们那些调试API

    第二个: 说的是,调试的时候,进程,线程,和异常函数的各种特性,也就是说调试进程,线程,还有异常的时候,该怎么做.

    第三个: 告诉了我们使用基本的调试函数可以创建一个基本的调试器.这些函数,可以下断点,异常等等.

    第四个: 这个则是告诉了我们,调试程序的时候来的各种事件.

    看下第二个把,调试的时候的各种特性.

    说的是,进程函数,线程函数,异常函数,我们现在首要任务是调试进程,所以看第一个

    告诉了我们,要调试一个进程,你要用CreateProcess,并且要设置标志为上面画框框的地方.

    下面还说了,我们要还可以通过OpenPeocess获得一个进程ID,通过DebugActiveProcess附加这个进程调试.

    那么我们现在知道了,要调试一个程序,首先要创建进程,并且给出特定的参数.

    四丶编写调试器的基本框架(汇编编写,C/C++一样编写)

     我们知道了,要调试一个程序,要先创建进程,然后我们应该要等待事件的到来,进行处理,如果是否继续处理,交给

    ContinueDebugEvent

    开始编写程序:

    RadAsm创建控制台程序

    汇编代码

    1.创建调试进程

    LOCAL @si:STARTUPINFO 
        LOCAL @di:PROCESS_INFORMATION
        mov @si.cb,sizeof STARTUPINFO
        ;创建调试进程
        invoke CreateProcess,offset g_szAppName,NULL,NULL,NULL,FALSE,DEBUG_PROCESS,NULL,NULL,addr @si,addr @di

    CTRL + D 调试,看下计算器是否启动.

    2.调用调试函数,等待事件到来.

    注重第一个参数,说了一个DEBUG_EVENT,看下这个结构体

    typedef 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; 
    } DEBUG_EVENT, *LPDEBUG_EVENT;

    说了异常来的时候,这个结构体会有异常的代码,进程的ID,线程的ID,以及根据不同异常,产生不同的结构体

    (因为是共用体,所以什么异常来了,就会有不同的结构体,保存了不同的异常信息)

    举个例子,第一个:

    异常信息是EXCEPTION_DEBUG_EVENT 表示调试的时候异常信息事件来了,我们看下它的结构体是什么

    typedef struct _EXCEPTION_DEBUG_INFO { 
      EXCEPTION_RECORD ExceptionRecord; 
      DWORD dwFirstChance; 
    } EXCEPTION_DEBUG_INFO, *LPEXCEPTION_DEBUG_INFO; 

    它的结构体则保存了和异常信息,什么的,(和筛选器异常的结构体差不多.)

    上面一个框是一个参数,下面说了,只有线程,被创建调试进程的时候才能用,也就是创建调试进程使用.

    下面有例子,抄例子

    我就不截图看了.

     直接编写汇编代码:

       assume ecx:ptr DEBUG_EVENT
        
        
        ;EXCEPTION_DEBUG_EVENT  代表我不处理,DBG_CONTINUE代表我处理,这就是OD的F9运行起来的功能
        .while TRUE
              invoke WaitForDebugEvent,addr @DebugEv,INFINITE         ;等待事件到来
              lea ecx,@DebugEv                                        ;结果放到ecx当中,因为ecx已经假设为DebugEvent结构体了
              mov ebx,[ecx].dwDebugEventCode                          ;将结构体的异常代码给ebx,然后下方通过ebx判断是哪个事件来了
            .if     ebx == EXCEPTION_DEBUG_EVENT    ;检测事件是什么
             invoke  crt_puts,offset g_szEcpt
             mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
             
            .elseif ebx == CREATE_THREAD_DEBUG_EVENT
            
            invoke  crt_puts,offset g_szEcpt
            mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
            
            .elseif ebx == CREATE_PROCESS_DEBUG_EVENT
            
            invoke  crt_puts,offset g_szEcpt
            mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
            
            .elseif ebx == EXIT_THREAD_DEBUG_EVENT
            
            invoke  crt_puts,offset g_szEcpt
            mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
            
            .elseif ebx == EXIT_PROCESS_DEBUG_EVENT
            mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
            
            .elseif ebx == LOAD_DLL_DEBUG_EVENT
            
            mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
            
            .elseif ebx == UNLOAD_DLL_DEBUG_EVENT
            mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
            
            .elseif ebx == OUTPUT_DEBUG_STRING_EVENT
            mov @dwContinueStatus,EXCEPTION_DEBUG_EVENT
            .endif
            invoke ContinueDebugEvent,[ecx].dwProcessId,
                                      [ecx].dwThreadId,       ;继续处理事件
                                       @dwContinueStatus
        .endw

    代码很简单,利用waitforDebugEvent获取异常代码,然后异常代码给ebx,通过ebx模拟switch,看看那个异常事件回来.当然,汇编代码会放到课堂资料中,带着C代码一起发布,这里只是简单解释一下.

    五丶异常事件是什么

    上面说了,异常事件和ebx(异常代码比较)那么分别代表什么意思?

    EXCEPTION_DEBUG_EVENT          :被调试的调试程序的时候来,会在调试的程序中下一个int3断点.如果被调试的时候,则回来,属于系统断点
    
    CREATE_THREAD_DEBUG_EVENT      :被调试的程序创建线程的时候会来
    
    CREATE_PROCESS_DEBUG_EVENT     :被调试的程序创建进程的时候会来
    
    EXIT_THREAD_DEBUG_EVENT       :被调试的程序退出线程的时候会来
    
    EXIT_PROCESS_DEBUG_EVENT      :被调试的程序退出进程的时候会来
    
    LOAD_DLL_DEBUG_EVENT        :被调试的程序加载DLL的时候会来
    
    UNLOAD_DLL_DEBUG_EVENT       :被调试的程序卸载DLL会来
    
    OUTPUT_DEBUG_STRING_EVENT     :被调试的程序调试输出的时候会来
    
    RIP_EVENT                :64位系统的事件,如果编写32位调试器,这个则不重要.

    今天主要是讲了一个框架,具体可以回去自己写一下就明白,很简单.

    课堂资料:

    链接:http://pan.baidu.com/s/1jHDJOUU 密码:mbn4

    作者:IBinary
    出处:http://www.cnblogs.com/iBinary/
    版权所有,欢迎保留原文链接进行转载:)

  • 相关阅读:
    c# 复制整个文件夹的内容,Copy所有文件
    c# 创建文件夹
    c# 访问共享文件
    sublimit 编辑器 设置默认的编码
    WPF xml配置文件里面的大于小于号转义
    c# datatable 分组
    WPF 耗时操作时,加载loging 动画 (BackgroundWorker 使用方法)
    WPF DEV gridcontrol 自定义计算列(TotalSummary)
    postgresql 创建gin索引
    WPF DEV gridcontrol当前项的数据导出为mdb文件
  • 原文地址:https://www.cnblogs.com/iBinary/p/7604693.html
Copyright © 2020-2023  润新知