百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

Windows平台调试器原理与编写01.调试框架

csdh11 2025-03-14 15:57 2 浏览


调试框架

调试器最基本功能: 断点,单步

断点分为三类

  1. 软件断点
  2. 硬件断点
  3. 内存断点

window提供了一套机制,帮助用户来实现一套3环的调试器

事件驱动 : 窗口的各种操作(外在的想要对窗口做一下改动,例如点击菜单,点击按钮,按下键盘等)

消息响应 : 这些事件会被封装成结构体,叫做消息

调试器也是类似,调试器有各种跟调试相关的事情发生,这些事情会被封装成一个个结构体传给我们,我们只需要一直处理这样一个个的结构体的信息,这些结构体就是调试事件,调试事件 90% 都是处理异常,调试器本身就是依托于系统的异常机制实现的

步骤

建立调试会话

当调试器开始调试另一个进程的时候称为调试会话的建立,当调试器没有调试进程时,就叫没有调试会话

调试器建立会话的2种方式

启动CreateProcess

调试标志主要2种

附加DebugActiveProcess

脱离DebugActiveProcessStop

结束调试,但是被调试的进程仍然运行,一般用于测试调试时修改的有没有效果,大部分时候用不到

循环接受调试事件

WaitForDebugEvent

超时时间 : INFINITE 一直等待,只能使用控制台程序 ,不能用于窗口程序

处理调试事件

提交处理结果

结果提交给系统

当调试器调试进程时, 被调试进程处于挂起状态,当调试事件处理完之后,被调试进程是处于运行还是挂起要看我们的操作,返回结果就是告诉系统 被调试进程 是 继续挂起还是运行

ContinueDebugEvent

第3个参数: 继续状态

DBG_CONTINUE 继续运行(如果异常应该状态表示异常已处理,进程继续运行)

DBG_EXCEPTION_NOT_HANDLED 只对异常事件有用

代码实现

新建工程

完成代码

.386
.model flat, stdcall
option casemap:none

include windows.inc
include user32.inc
include kernel32.inc
include msvcrt.inc

includelib user32.lib
includelib kernel32.lib
includelib msvcrt.lib
   

.data
    g_szExe db "winmine.exe",0  ;被调试的进程
   
    g_szEXCEPTION_DEBUG_EVENT           db "EXCEPTION_DEBUG_EVENT",0dh,0ah, 0
  
    g_szCREATE_THREAD_DEBUG_EVENT       db "CREATE_THREAD_DEBUG_EVENT",0dh,0ah,0
  
    g_szCREATE_PROCESS_DEBUG_EVENT      db "CREATE_PROCESS_DEBUG_EVENT",0dh,0ah, 0
  
    g_szEXIT_THREAD_DEBUG_EVENT         db "EXIT_THREAD_DEBUG_EVENT",0dh,0ah, 0
  
    g_szEXIT_PROCESS_DEBUG_EVENT        db "EXIT_PROCESS_DEBUG_EVENT",0dh,0ah, 0
  
    g_szLOAD_DLL_DEBUG_EVENT            db "LOAD_DLL_DEBUG_EVENT" , 0dh,0ah, 0
  
    g_szUNLOAD_DLL_DEBUG_EVENT          db "UNLOAD_DLL_DEBUG_EVENT" , 0dh,0ah, 0
  
    g_szOUTPUT_DEBUG_STRING_EVENT       db "OUTPUT_DEBUG_STRING_EVENT" , 0dh,0ah, 0
 
 
.code

main proc
	LOCAL  @si:STARTUPINFO
	LOCAL  @pi:PROCESS_INFORMATION 
	LOCAL  @de:DEBUG_EVENT  ;这个结构体使用后需要清0

	;初始化变量
	invoke  RtlZeroMemory,addr @si, size @si
	invoke  RtlZeroMemory,addr @pi, size @pi
	invoke  RtlZeroMemory,addr @de, size @de  

	;建立调试会话
        invoke CreateProcess,NULL,offset g_szExe,NULL,NULL,NULL,\
              DEBUG_ONLY_THIS_PROCESS,NULL,NULL,addr @si,addr @pi
        
          .if !eax
          	ret  ;建立调试会话失败直接退出
          .endif 
        
	;循环接受调试事件
	.while  TRUE
	  
            invoke WaitForDebugEvent,addr @de,INFINITE
          
            ;处理调试事件
            .if @de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT           ;异常 90%都是在处理这个
            	 invoke crt_printf, offset g_szEXCEPTION_DEBUG_EVENT
            	 
            .elseif @de.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT   ;创建线程
                 invoke crt_printf, offset g_szCREATE_THREAD_DEBUG_EVENT
               
            .elseif @de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT  ;创建进程
            	 invoke crt_printf, offset g_szCREATE_PROCESS_DEBUG_EVENT
          
            .elseif @de.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT     ;线程退出
                 invoke crt_printf, offset g_szEXIT_THREAD_DEBUG_EVENT 
          
            .elseif @de.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT    ;进程退出
                 invoke crt_printf, offset g_szEXIT_PROCESS_DEBUG_EVENT           
            	 
            .elseif @de.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT        ;dll被加载
                invoke crt_printf, offset g_szLOAD_DLL_DEBUG_EVENT  
          
            .elseif @de.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT      ;dll被卸载
               invoke crt_printf, offset g_szUNLOAD_DLL_DEBUG_EVENT            
          	 
            .elseif @de.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT      ;输出信息
               invoke crt_printf, offset g_szOUTPUT_DEBUG_STRING_EVENT 
             
            .endif


	    ;提交事件处理结果
	    invoke ContinueDebugEvent,@de.dwProcessId,@de.dwThreadId,DBG_CONTINUE
	  
	    ;DEBUG_EVENT结构体使用后清0
	    invoke  RtlZeroMemory,addr @de, size @de  
	  
	.endw

	ret

main endp

start:
   
   invoke main

  
end start

处理 LOAD_DLL_DEBUG_EVENT 事件

用winhex 打开 该id 进程,查看内存数据 发现还是搜不到

发现是系统dll,再看一下后面的

在F9执行几次,发现地址是固定的,但是指针指向的dll 会发生变化

所以 lpImageName 存储的是一个二级指针, 拿到 dll 地址就必须跨进程读写内存,但是第一个dll (ntdll.dll)例外,没办法拿到数据

.386
.model flat, stdcall
option casemap:none

include windows.inc
include user32.inc
include kernel32.inc
include msvcrt.inc

includelib user32.lib
includelib kernel32.lib
includelib msvcrt.lib
   

.data
    g_szExe db "winmine.exe",0  ;被调试的进程
    g_hExe dd  0               ;进程句柄
   
    g_szEXCEPTION_DEBUG_EVENT           db "EXCEPTION_DEBUG_EVENT",0dh,0ah, 0
  
    g_szCREATE_THREAD_DEBUG_EVENT       db "CREATE_THREAD_DEBUG_EVENT",0dh,0ah,0
  
    g_szCREATE_PROCESS_DEBUG_EVENT      db "CREATE_PROCESS_DEBUG_EVENT",0dh,0ah, 0
  
    g_szEXIT_THREAD_DEBUG_EVENT         db "EXIT_THREAD_DEBUG_EVENT",0dh,0ah, 0
  
    g_szEXIT_PROCESS_DEBUG_EVENT        db "EXIT_PROCESS_DEBUG_EVENT",0dh,0ah, 0
  
    g_szLOAD_DLL_DEBUG_EVENT            db "LOAD_DLL_DEBUG_EVENT" , 0dh,0ah, 0
  
    g_szUNLOAD_DLL_DEBUG_EVENT          db "UNLOAD_DLL_DEBUG_EVENT" , 0dh,0ah, 0
  
    g_szOUTPUT_DEBUG_STRING_EVENT       db "OUTPUT_DEBUG_STRING_EVENT" , 0dh,0ah, 0
  
    g_szLoadDllFmt   db "%08X  %s", 0dh,0ah, 0
  
    g_szwLoadDllFmt  dw '%','0','8','X',' ', '%','s', 0dh,0ah, 0
 
 
.code

OnLoadDll proc uses esi pDE:ptr DEBUG_EVENT
        LOCAL  @dwAddr:DWORD
        LOCAL  @dwByteReaded:DWORD
        LOCAL  @szwPath[512]:WORD
       

	invoke crt_printf, offset g_szLOAD_DLL_DEBUG_EVENT  
	invoke  RtlZeroMemory,addr @szwPath, size @szwPath

	mov esi,pDE
	assume esi: ptr DEBUG_EVENT

	invoke ReadProcessMemory,g_hExe,[esi].u.LoadDll.lpImageName,addr @dwAddr, size @dwAddr,addr  @dwByteReaded
	 .if !eax
          	ret  
         .endif 
        
	invoke ReadProcessMemory,g_hExe, @dwAddr ,addr @szwPath, sizeof  @szwPath,addr  @dwByteReaded
	.if !eax
          	ret  
        .endif 
        

	.if [esi].u.LoadDll.fUnicode   ;如果是 unicode

	    invoke crt_wprintf, offset g_szwLoadDllFmt,[esi].u.LoadDll.lpBaseOfDll,addr @szwPath
	.elseif

	    invoke crt_printf, offset g_szwLoadDllFmt,[esi].u.LoadDll.lpBaseOfDll,addr @szwPath
	
	.endif

	assume esi:nothing
	ret

OnLoadDll endp


main proc
	LOCAL  @si:STARTUPINFO
	LOCAL  @pi:PROCESS_INFORMATION 
	LOCAL  @de:DEBUG_EVENT  ;这个结构体使用后需要清0

	;初始化变量
	invoke  RtlZeroMemory,addr @si, size @si
	invoke  RtlZeroMemory,addr @pi, size @pi
	invoke  RtlZeroMemory,addr @de, size @de  

	;建立调试会话
        invoke CreateProcess,NULL,offset g_szExe,NULL,NULL,NULL,\
              DEBUG_ONLY_THIS_PROCESS,NULL,NULL,addr @si,addr @pi
        
          .if !eax
          	ret  ;建立调试会话失败直接退出
          .endif 
        
          mov eax,@pi.hProcess
          mov g_hExe,eax
	;循环接受调试事件
	.while  TRUE
	  
            invoke WaitForDebugEvent,addr @de,INFINITE
          
            ;处理调试事件
            .if @de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT           ;异常 90%都是在处理这个
            	 invoke crt_printf, offset g_szEXCEPTION_DEBUG_EVENT
            	 
            .elseif @de.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT   ;创建线程
                 invoke crt_printf, offset g_szCREATE_THREAD_DEBUG_EVENT
               
            .elseif @de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT  ;创建进程
            	 invoke crt_printf, offset g_szCREATE_PROCESS_DEBUG_EVENT
          
            .elseif @de.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT     ;线程退出
                 invoke crt_printf, offset g_szEXIT_THREAD_DEBUG_EVENT 
          
            .elseif @de.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT    ;进程退出
                 invoke crt_printf, offset g_szEXIT_PROCESS_DEBUG_EVENT           
            	 
            .elseif @de.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT        ;dll被加载
                ;invoke crt_printf, offset g_szLOAD_DLL_DEBUG_EVENT  
            	invoke OnLoadDll,addr @de
            .elseif @de.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT      ;dll被卸载
               invoke crt_printf, offset g_szUNLOAD_DLL_DEBUG_EVENT            
          	 
            .elseif @de.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT      ;输出信息
               invoke crt_printf, offset g_szOUTPUT_DEBUG_STRING_EVENT 
             
            .endif


	    ;提交事件处理结果
	    invoke ContinueDebugEvent,@de.dwProcessId,@de.dwThreadId,DBG_CONTINUE
	  
	    ;DEBUG_EVENT结构体使用后清0
	    invoke  RtlZeroMemory,addr @de, size @de  
	  
	.endw

	ret

main endp

start:
   
   invoke main

  
end start

NtQueryProcessInfomation 获取进程信息

相关推荐

PromptDA:4K分辨率精准深度估计!(分辨率4k是多少p)

这里是FoxFeed,一个专注于科技的内容平台。背景介绍在计算机视觉领域,深度估计一直是一个重要的研究方向。近日,由DepthAnything团队开发的...

m4a怎么转换成mp3?教你这样转换音频格式

m4a怎么转换成mp3?M4A是MPEG-4音频标准的文件的扩展名,它可以存储各种类型的音频内容,运用比较广泛,尽管m4a被很多媒体应用兼容,但仍有很多应用无法打开它,将m4a转换成mp3就是一个很不...

“讲述初心故事 传递使命情怀”2019第五届江苏医院微电影节启动

“讲述初心故事传递使命情怀”,2019第五届江苏医院微电影节9月16日启动。江苏医院微电影节由新华网江苏有限公司和江苏省医院协会联合举办,扬子江药业集团协办,秉承“讲述初心故事传递使命情怀”为活动...

短视频宝贝=慢?阿里巴巴工程师这样秒开短视频

前言随着短视频兴起,各大APP中短视频随处可见,feeds流、详情页等等。怎样让用户有一个好的视频观看体验显得越来越重要了。大部分feeds里面滑动观看视频的时候,有明显的等待感,体验不是很好。针对这...

阿里巴巴工程师这样秒开短视频(阿里巴巴的工程师多少钱一个月)

前言随着短视频兴起,各大APP中短视频随处可见,feeds流、详情页等等。怎样让用户有一个好的视频观看体验显得越来越重要了。大部分feeds里面滑动观看视频的时候,有明显的等待感,体验不是很好。针对这...

旗鱼浏览器1.0 RC正式版候选版:增账户同步等

从9月19日发布第一个Beta版至今,约80天的时间便这么飞走了,作为2015年底的一个答卷,今天旗鱼浏览器1.0RC(正式版候选版)发布,如果没有意外,明天我们将发布电脑版和安卓版的第一个1.0正...

5种方法,教你将m3u8转换为mp4格式

m3u8格式在许播放器中不受支持,只能在浏览器中进行在线观看,然而,在线观看可能会不大方便,如果网络卡顿的话就会影响观感。想要将...

kgma格式怎么转换为mp3?试试这5种简单的音频转换方法!

由于kgma格式的特殊性和平台限制,除了专属的音乐平台外,其他设备和网络平台是无法识别或播放kgma格式的音乐的,因此为了方便使用,我们就必须将kgma格式转换为mp3。接下来,小编就为大家推荐5种简...

500+本程序员值得看的书籍,7大类,1大合集,收藏,日后有用

一、Golang书籍推荐入门《Go入门指南》...

教你编写最简单的CM3操作系统,160行实现任务创建与切换

如题,任务创建与上下文切换是跟硬件息息相关的,而这恰恰是RTOS编写的最难点,抛开这些功能,剩下的就是双向链表增删改操作了,本例用最精简的方式实现了任务创建与切换,OS启动等功能,并运用了Cortex...

Hot 3D 人体姿态估计 HPE Demo复现过程

视频讲解...

各编程语言相互调用示例,代码简单,生成的软件体积也很小

aardio支持混入很多不同的编程语言,代码简单,生成的软件体积也很小。下面看示例。...

你知道shell脚本中$0 $1 $# $@ $* $? $$ 都是什么意思吗?

一、概述shell中有两类字符:普通字符、元字符。1.普通字符...

NDK打印调用堆栈(logger.error打印堆栈信息)

虽然android源码里有android::CallStack用来打印堆栈,但是NDK里面并没有包含它,所以不能直接调用它,所以要尝试用动态调用的方式来实现。我测试的手机是安卓8.1.0版本,...

小白都能看得懂的Cgo入门教程(cgo2.0教程)

在Go语言开发过程中,尽管Go本身功能强大,但仍然有许多C语言库可以复用,如操作系统API、高性能计算库、数据库驱动等。Go提供了一种强大的机制——Cgo,让我们可以在Go代码中调用C...