打造简单的调试器

本文转自:http://www.freebuf.com/sectool/92279.html

0×1 概述

在Fuzzing过程中,必须要监控程序的执行状态,若程序发生异常,立即保存测试用例,以便将来对其进行重现。监控器的作用就是监控程序的执行,如果程序有异常时通知给Fuzzing主线程。

目前Fuzzing工具各种各样,监控器也有很多种,但大多都是以调试器的方式实现。一个简单的监控器是这样实现的:

(一)以调试模式启动进程,为目标进程开启调试端口,可使用参数DEBUG_ONLY_THIS_PROCESS,意为只调试本进程,更多说明请参照MSDN。

(二)等待调试事件,当进程内部发生调试事件时,通过调试端口向调试器发送事件。调试事件不仅仅指异常事件,创建进程、线程、加载库这些都是调试事件,异常事件是EXCEPTION_DEBUG_EVENT。

(三)处理调试事件,对应不同的调试事件,可作不同处理,若不感兴趣,可通过ContinueDebugEvent来忽略。调试器可对目标进程的操作具有较高的权限,可自由读取、修改目标进程的寄存器、内存信息,我们所使用最多的调试器,像Windbg、VCDebugger、OllyDbg等,也是从这些简单的函数实现而成的。

一个简单的HelloWorld例子位于codeproject,对这方面不太了解的朋友可以下载看看。

0×2 不同的调试器接口

当然,在实际工程中,我们没必要都从hello world开始写起,有很多的调试器接口已经被完善,方便我们直接调用,接下来随便举几个常见调试器的例子。

(1)ImmLib

一个集成在Immunity Debugger的Python库,但仅能在Immunity Debugger中调用。若写Immunity Debugger的插件时,将是不错的选择。

(2)vtrace

Vtrace是一款支持不同平台的调试器,同样也是使用Python编写而成。

(3)OllyPython

OllyPython使用Python编写而成,是OllyDbg的插件,也仅能在OD内使用。

(4)PyDbg

大家对PyDbg应该都很熟悉,在python灰帽子里有大幅的介绍与应用,但仅能用兼容Python2.4和2.5。

(5)PyDbgEng

PyDbgEng与Windbg集成实现而成,且支持强大的内核调试功能。

(6)WinAppDbg

功能比较强大的调试器,与PydbgEng相似,但采用纯原生Win32 API实现而成,不依赖于任何调试器。

(7)PyDbgExt

与PyDbgEng相反,PyDbgEng是通过Python实例化调试器引擎,而它是在调试器中添加Python解释器,在Windbg中使用。

(8)pygdb

Pygdb是一个简单的GNU调试器的封装,提供GTK的接口,可在Linux与OSX下运行。

(9)PyKd

PyKd有点像将PyDbgEng与PyDbgExt混合到一起,可在调试器中调用,也可独立使用,有较大潜力的调试器。

(10)PyMem

PyMem是使用Python编写的Windows上的一个内存插装库。

(11)python-ptrace

python-ptrace调用ptrace实现而成,仅能在POSIX系统上运行,如BSD、Linux。

(12)PythonGdb

PythonGdb 是一个嵌入到GDB中的一个Python解释器。

(13)Radare

Radare是一个控制台程序,支持多平台的反汇编、调试框架。

(14)Universal Hooker (uhooker)

Uhooker实现了函数Hook的Python库,但较长时间未更新。

0×3、实现自己的调试监控器

介绍了这么多的调试器接口,有轻量级的、有支持内核调试的、有支持Linux的、有内嵌到调试器中的,总有一款适合你的实际需要。使用它们也非常简单,只需要基本的编程知识即可。一般实现过程都是实例化一个调试器,然后启动目标进程,并开启事件来监听,如果发生异常,事件被激活,处理调试事件即可。

给我印象比较深刻的是Peach的监控器,它通过Pit的配置后,启动一个本地或远程的Agent的,然后由Agent来监控目标进程的异常情况。当发生异常后,除了保存样本操作外,还会生成StackTrace,记录寄存器、调用栈,调用MSEC生成可利用性的报告,并根据exploitable提供的信息命名、分类、去重。基本上对后期分析有点价值的信息都被记录了,是一种比较好的方案。理所当然,重用它的代码就行。

Peach的结构很简单,通过Peach.py调用engine.run,在run中进行一系列初始化后,调用_runTest开始测试。创建了一个agent的实例用来监控目标程序,创建了一个watcher的实例用来记录异常信息。

Debugger.py中包含了两个调试器接口类,一个是WindowsDebugEngine,用来与Windbg交互,支持Windows操作系统,另一个是UnixDebugger,使用Vtrace实现而成,支持POSIX系统。POSIX系统暂时不感兴趣,把这部分代码先删除掉。

WindowsDebugEngine的构造函数中,首先对从pit文件中传进来的参数进行解析,这部分代码也不需要,删除修改。然后调用_StartDebugger来启动调试器,开启WindowsDebugEngineProcess_run的线程,调用PyDbgEng.ProcessCreator或PyDbgEng.ProcessAttacher来启动或附着到目标进程上,并注册调试事件处理器_DbgEventHandler,当异常事件发生后,在_DbgEventHandler中输出寄存器、调用栈、exploitable信息。

对这个流程理解后,修改它就只是简单的工程了,添加了一个新类DebuggerMonitor作为代理,来处理用户的参数与DebugEngine之间的交互。

使用起来很简单,这是一个简单例子:

运行结果如下,看到了很舒服的命名方式:

StackTrace中保存的内容如下:

DebuggerMonitor类的构造函数接受两个参数,分别是启动程序的命令行,及保存异常信息的目录。通过调用getLogDir来得到当前异常信息保存的目录,方便用户保存测试用例。

在使用前需要四个依赖模块,分别是psutils、comtypes、pywin32、pydbgengine。

下一步工作:还需要添加PageHeap的支持、添加服务、内核等监控接口,读者若需要可自行添加。

原文地址:https://www.cnblogs.com/skyus/p/8677749.html

时间: 2024-08-04 16:42:53

打造简单的调试器的相关文章

与调试器共舞 - LLDB 的华尔兹

你是否曾经苦恼于理解你的代码,而去尝试打印一个变量的值? 1 NSLog(@"%@", whatIsInsideThisThing); 或者跳过一个函数调用来简化程序的行为? 1 NSNumber *n = @7; // 实际应该调用这个函数:Foo(); 或者短路一个逻辑检查? 1 if (1 || theBooleanAtStake) { ... } 或者伪造一个函数实现? 1 2 3 4 5 6 int calculateTheTrickyValue {   return 9;

JPDA(一):使用JDI写一个调试器

Debugging规范 简单来说,Java Platform Debugger Architecture(JPDA)就是Java提供的一套用于开发Java调试工具的规范,任何的JDK实现都需要实现这个规范.JPDA是一个Architecture,它包括了三个不同层次的规范,如下图, / |--------------| / | VM | debuggee - ( |--------------| <------- JVMTI - Java VM Tool Interface \ | back-e

简单调试器的实现(二)使用反汇编引擎&amp;建立第一个程序

让程序停下来: 动态调试器的一个重要特点就是:让程序停下来,这样我们才可以观测到程序的即时情况. 不过现在我们并不需要研究怎么下断点,系统已经帮我们激活了第一个断点.在创建调试进程时,系统会帮我们在ntdll.dll中设置一个INT3断点,我们就让程序在这里断下来. switch (DebugEvent->u.Exception.ExceptionRecord.ExceptionCode)//Int3断点的事件 { case EXCEPTION_BREAKPOINT: // ((DWORD )0

简单调试器的实现(一)调试循环与反汇编引擎

最近对调试器的原理感兴趣,自己写了一个简单的demo 打开调试进程: 要调试一个进程,需要在使用CreateProcess打开一个文件时,将第6个参数设为DEBUG_PROCESS. BOOL WINAPI CreateProcess( _In_opt_     LPCTSTR lpApplicationName, _Inout_opt_  LPTSTR lpCommandLine, _In_opt_     LPSECURITY_ATTRIBUTES lpProcessAttributes,

windows简单调试器源码2700行左右代码

简单调试器项目中on开头的函数为接收系统的调试事件并做相应的处理,简单调试器实现过程中主要的调试事件为异常事件,相应的处理函数为DispatchException. 在异常事件中访问异常.int3异常.单步异常是跟实现调试器功能密切相关的异常事件,这里用三个函数分别处理三个函数分别为OnExceptionAccess.OnExceptionBreakPoint.OnExceptionSingleStep. 异常处理函数中如果为调试器自己设置的异常程序就会停下来接收用户输入等待下一步处理,相应的用

简单调试器的实现(三)

单步的实现 就是之前设置的TF标志位 //设置TF标志位 void SetTrapFlag() { CONTEXT context = {0}; GetDebuggeeContext(&context); context.EFlags |= 0x100; SetDebuggeeContext(&context); } 步出则是在ebp+4的地址设置断点 BOOL MoveOut() { // 获取ebp CONTEXT Context = {0}; GetDebuggeeContext(&

简单调试器的实现(四)断点

Int3断点原理: 在一个指令处设置断点有2步,1.将指令的第1个字节保存起来,2.将这个字节替换成 0xCC 我们可以用我写的调试器看一下: u指令显示了在地址 14e1bbb处的指令   E8 3EAC000;   Call  14ec7fe  . 然后查看这个地址存放的内容. 接着,在14e1bbb设置一个断点, bp 14e1bbb . 再次查看14e1bbb的内容,明显发现  前1个字节设置成了0xCC. 断点实现流程: 以下是设置INT3 断点的实现代码. DWORD SetBrea

学习NodeJS第三天:打造Nodejs的调试环境(中)

2012-12-07 因追加<学习 NodeJS 第三天:打造 Nodejs 的调试环境(下)>的缘故,特此将原来的<下>篇改为<中>篇,如标题所示. 上一期我们为大家介绍了安装 Eclipse 调试插件的情况,这对于还不熟悉 Eclipse 开发平台的用户是至关重要的,希望可以通过一步步的图片加文字说明,把 Nodejs 困难的地方变简单和清晰.友好和轻松. 现在正式进入要调试程序肯定要有调试代码.下面就是我们第一个测试的代码,很小的行数: var sys = req

[连载]《C#通讯(串口和网络)框架的设计与实现》- 11.调试器的设计

目       录 第十一章     调试器设计... 2 11.1         调试接口... 2 11.2         界面方式调试... 3 11.3         命令行方式调试... 5 11.4         小结... 6 第十一章      调试器设计 SuperIO 框架平台设计.开发完毕后,想把代码编译成程序集(DLL),二次开发都通过引用DLL实现接口.继承类库来实现驱动和插件的开发,SuperIO框架的代码不会轻易去改变.这是框架设计最终要达到的效果,但是在二