2011年4月14日
摘要: 在接下来的文章中会讲解如何在调试器中显示局部变量和全局变量的类型和值。实现这个功能一定要有调试符号的支持,因为调试符号记录了每个变量的名称,类型,地址,长度等信息。这不是一件轻松的事情,因为你首先要对符号模型有一定的了解。所以本文的主要目的就是介绍DbgHelp中的符号模型。符号模型这里所说的“符号模型”指的是各种符号之间的逻辑关系,虽然微软定义了各种不同格式的符号文件,但是它们使用的符号模型都是相同的。正因为如此,使用DbgHelp可以读取各种格式的符号文件,而且DIA文档中关于符号模型的那部分内容也适用于DbgHelp,正好弥补了DbgHelp文档中缺少的内容。遗憾的是,这部分内容非常少, 阅读全文
posted @ 2011-04-14 12:42 Zplutor 阅读(5388) 评论(4) 推荐(3) 编辑
  2011年4月9日
摘要: 上回讲解了如何实现断点功能,这回讲解如何实现与断点紧密相关的单步执行功能。单步执行有三种类型:StepIn,StepOver和StepOut,它们的实现方式比较多样化,单独实现它们的话并不困难,但是将它们整合到一起就比较困难了,特别是加上断点功能之后,程序的逻辑更加难以理解。本文首先单独讲解每种单步执行的原理,最后讲解如何将它们整合到一起。这都是我个人的实现方法,大家可以用来参考。(注意:本文所讲的单步执行是源代码级别的,而不是指令级别的。)StepIn原理StepIn即逐条语句执行,遇到函数调用时进入函数内部。当用户对调试器下达StepIn命令时,调试器的实现方式如下:①通过调试符号获取当前 阅读全文
posted @ 2011-04-09 19:35 Zplutor 阅读(7036) 评论(1) 推荐(2) 编辑
  2011年4月2日
摘要: 断点是最基本和最重要的调试技术之一,本文讲解了如何在调试器中实现断点功能。什么是断点在进行调试的时候,只有被调试进程暂停执行时调试器才可以对它执行操作,例如观察内存内容等。如果被调试进程不停下来的话,调试器是什么也做不了的。要使被调试进程停下来,除了几个在特定时刻才发生的调试事件外,唯一的途径就是引发异常。断点正是用来达到上述目的的异常,在第三篇文章的异常代码表中,有一种EXCEPTION_BREAKPOINT异常,它就是断点异常。虽然断点是一种异常,但并不意味着被调试进程发生了问题,它只是用来调试的一种手段,所以调试器应该将它和别的异常明显区分开来。实际上Windows对断点异常的处理也有一 阅读全文
posted @ 2011-04-02 23:10 Zplutor 阅读(9391) 评论(0) 推荐(2) 编辑
  2011年3月27日
摘要: 上一篇文章介绍了调试符号以及DbgHelp的加载和清理,这回我们使用它来实现一个显示源代码的功能。该功能的实际使用效果如下图所示:该功能不仅仅是显示源代码,还要显示每一行代码对应的地址。实现该功能大概需要进行以下的步骤:①获取下一条要执行的指令的地址。②通过调试符号获取该地址对应哪个源文件的哪一行。③对于其它的行,通过调试符号获取它对应的地址。第一步可以通过获取EIP寄存器的值来完成,相关的内容已经在第四篇文章中进行了讲解,这里不再重复。下面讲一下如何实现第二个和第三个步骤。获取源文件以及行号在调试符号中,记录了每一行源代码对应的地址。通过DbgHelp的SymGetLineFromAddr6 阅读全文
posted @ 2011-03-27 21:32 Zplutor 阅读(7974) 评论(4) 推荐(1) 编辑
  2011年3月20日
摘要: 一个调试器应该可以跟踪被调试程序执行到了什么地方,显示下一条将要执行的语句,显示各个变量的值,设置断点,进行单步执行等等,这些功能都需要一个基础设施的支持,那就是调试符号。什么是调试符号我们知道,在exe、dll等可执行文件中保存的数据大部分都是二进制指令,CPU直接读取这些指令并执行。那么调试器是如何知道每条指令对应哪个源文件的哪一行代码呢?它又是如何知道每个变量和函数的名称,并显示变量的值呢?很显然,可执行文件的二进制数据中不可能包含这么多信息,这一切都是由调试符号来支持的。所谓符号,简单来说就是源代码中每个对象的名称。例如变量、函数、类型等,它们都有一个名称,以及其它的相关信息:变量有类 阅读全文
posted @ 2011-03-20 22:51 Zplutor 阅读(11162) 评论(8) 推荐(3) 编辑
  2011年3月13日
摘要: 在前几篇文章中,我实现的那个调试器只能被动接收调试事件并输出这些事件的信息。现在,我要将它修改成可以接收命令,并根据命令对被调试进程进行各种操作。首先从最基本的操作开始。获取寄存器的值每个线程都有一个上下文环境,它包含了有关线程的大部分信息,例如线程栈的地址,线程当前正在执行的指令地址等。上下文环境保存在寄存器中,系统进行线程调度的时候会发生上下文切换,实际上就是将一个线程的上下文环境保存到内存中,然后将另一个线程的上下文环境装入寄存器。获取某个线程的上下文环境需要使用GetThreadContext函数,该函数声明如下:1BOOLWINAPIGetThreadContext(2HANDLEh 阅读全文
posted @ 2011-03-13 21:57 Zplutor 阅读(10025) 评论(16) 推荐(3) 编辑
  2011年3月8日
摘要: 这回接着处理上一篇文章留下的问题:如何处理EXCEPTION_DEBUG_EVENT这类调试事件。这类调试事件是调试器与被调试进程进行交互的最主要手段,在后面的文章中你会看到调试器如何使用它完成断点、单步执行等操作。所以,关于这类调试事件的处理很自由,调试器的作者可以根据需要进行不同的处理。但是,在对其进行处理之前必须要了解一些关于异常的知识,这也是本文的重点。(本文的内容参考了《软件调试》一书)异常的分类根据异常发生时是否可以恢复执行,可以将异常分为三种类型,分别是错误异常,陷阱异常以及中止异常。错误异常和陷阱异常一般都可以修复,并且在修复后程序可以恢复执行。两者的不同之处在于,错误异常恢复 阅读全文
posted @ 2011-03-08 22:39 Zplutor 阅读(16577) 评论(8) 推荐(1) 编辑
  2011年3月6日
摘要: 上一篇文章说到了调试循环的写法,这回讲一下调试器应该如何处理各种调试事件。RIP_EVENT关于这种调试事件的文档资料非常少,即使提到也只是用“系统错误”或者“内部错误”一笔带过。既然如此,我们也不需要对其进行什么处理,只要输出一条信息或者干脆忽略它即可。OUTPUT_DEBUG_STRING_EVENT当被调试进程调用OutputDebugString时就会引发该类调试事件,OUTPUT_DEBUG_STRING_INFO结构体描述了关于该事件的详细信息。在MSDN中,对该结构体各字段的解释是:lpDebugStringData字段是字符串在被调试进程的进程空间内的地址;nDebugStri 阅读全文
posted @ 2011-03-06 21:47 Zplutor 阅读(8583) 评论(2) 推荐(1) 编辑
  2011年3月4日
摘要: 前言程序员离不开调试器,它可以动态显示程序的执行过程,对于解决程序问题有极大的帮助。如果你和我一样对调试器的工作原理很感兴趣,那么这一系列文章很适合你,这些文章记录了我开发一个调试器雏形的过程,希望对你有帮助。或许我写的代码很拙劣,还请大家多多见谅!这个调试器使用Visual Studio 2010作为开发工具,是一个控制台程序。为了简化,一切输入输出都使用C++标准库的相关类,而且省略了很多错误检查和处理的过程。启动被调试程序要想对一个程序进行调试,首先要做的当然是启动这个程序,这要使用CreateProcess这个Windows API来完成。例如,下面的代码以记事本作为被调试程序:1#i 阅读全文
posted @ 2011-03-04 22:26 Zplutor 阅读(16077) 评论(3) 推荐(6) 编辑
  2011年2月20日
摘要: 什么是映像劫持也许你曾遇到过这种情况:无论你将软件安装在什么地方,在运行的时候总会出现“系统找不到指定的文件”的错误提示,导致软件无法运行。如果你将软件的exe文件改个名称,就可以正常运行了。这种现象就叫做映像劫持。映像劫持是一种影响系统正常运转的手段,很多病毒、木马都会使用这种手段阻止安全软件的运行。映像劫持的原理映像劫持是利用Windows的IFEO(Image File Execution Options)功能来实现的。IFEO实际上是Windows的一项正常功能,主要用于调试程序,其初衷是在程序启动的时候开启调试器来调试程序,这样一来可以在调试器中观察程序在难以 阅读全文
posted @ 2011-02-20 17:24 Zplutor 阅读(3511) 评论(0) 推荐(2) 编辑