调试器第二讲,单步步入/步过功能实现,以及基本的断点功能实现

           调试器第二讲,单步步入/步过功能实现,以及基本的断点功能实现

昨天,我们实现了调试器的基本框架,那么今天我们实现单步功能,还有断点功能,以及使用反汇编引擎

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

一丶反汇编引擎的编译,生成LIB

首先,我们有一个反汇编引擎的代码,现在我们编译连接一下(注意,可以使用GitHub下载,或者百度Google下载)

关于反汇编引擎的介绍: 请参考转载博客 http://blog.csdn.net/earbao/article/details/72857805

先编译成OBJ文件,(不连接),上面两个函数就是我们需要的使用的,准确的来说,是下面的这个,因为上面那个不会给你二进制数据.

比如我们反汇编的结果是

00401000    CC         int 3  那么这样就是用下面这个,有二进制代码显示

如果使用上面那个则是

00401000  int3      具体爱好看你使用那个,这里具体讲解第二个

先编译成OBJ

需要的就这有3个,  除了stdafx.obj 还有MyDisasm.obj 其余的都是我们用的

现在我们使用lib工具,讲编译好的obj打包成一个lib(当然你也可以写)

关于lib工具的使用,请参考我写的博客,32位汇编第七讲,混合编程 连接: http://www.cnblogs.com/iBinary/p/7555503.html

现在回车,生成我们的lib

然后我们还需要这两个函数的头文件.

现在准备齐全了,准备开始我们的代码编写.

注意: 这里我们是生成的静态lib,你也可以生成动态链接库 (也就是俗称的 DLL)

二丶使用反汇编引擎API

直接看代码吧

新建一个工程,控制台的(随你便,啥都行,能写代码,调用API的就可以)

把我们的lib,和.h文件,拷贝到我们的程序目录下(静态使用)

#include "stdafx.h"
#include <WINDOWS.H>
#include <STDLIB.H>
#include "Decode2Asm.h"

#pragma comment(lib,"MyDisAsm.lib")
int main(int argc, char* argv[])
{
    BYTE OpCode[] = {0xcc,0x90};      //解析的地址,地址里面有机器码,我们不知道是什么(这里举例子我们知道)
    char szAsm[256] = {NULL};        //通过地址,得出反汇编字符串,放到这个缓冲区中
    char szBin[256] = {NULL};        //通过地址,得出二进制机器码,放到这个缓冲区中
    UINT binSize = NULL;           //每次只解析一个指令长度,所以我们要遍历数组,加上这个长度,接着输出出来.
    BYTE *pCurCode = OpCode;         //遍历的时候需要给个指针
    do
    {
        Decode2AsmOpcode(pCurCode,szAsm,szBin,&binSize,(UINT)pCurCode);//调用API,解析,
        printf("%-16p%-20s%-20s\r\n",pCurCode,szBin,szAsm);        //输出
        pCurCode = pCurCode + binSize;                    //地址 + 指令长度 = 下一次需要解析的指令的地址
    } while (pCurCode < (OpCode + sizeof(OpCode)));            //判断是否数组越界
    system("pause");
    return 0;
}Decode2AsmOpcode的各个参数意思:第一个参数: 地址,你要给我一个地址,我去解析第二个参数: 反汇编代码的缓冲区,通过地址,解析的反汇编会放到这个缓冲区当中第三个参数: 二进制代码缓冲区,通过地址,解析二进制,放到这个缓冲区当中第四个参数: 是否计算偏移关于这个参数,其实是给EIP的值,(也就是地址),例如我们上面的给的.这个参数的作用是比如你有一个 JMP 指令00401000 JMP 000010   这个是正常的汇编代码,我们知道JMP是JMP的偏移而后面给了这个参数,它则会给我们计算出来00401000  JMP  00401010 

看下输出结果

配合我们昨天写的,则可以反汇编出来调试进程的代码了.

三丶汇编调用反汇编引擎API,显示调试进程的汇编代码.

在上面,我们对API有了一个认识.

下面我们就结合我们昨天写的汇编代码,接着写,显示出来断点位置的反汇编代码.

首先在处理异常的事件中,我们要调用

先加载我们的LIB,以及INC文件(INC文件中,是那个两个API的函数声明,你可以自己通过LIB生成,

具体请参考上面的混合编程的博客链接)

1.加载LIB,以及INC文件

2.使用ReadProcessMemory,和我们的API配合使用

首先使用Read..读取.然后放到我们的数组中,

然后使用API,获取反汇编的各种信息

invoke ReadProcessMemory,g_hProcess,
                             [ebx].u.Exception.pExceptionRecord.ExceptionAddress,
                             @OpCode,sizeof @OpCode,
                              NULL
invoke ShowAsm,addr @OpCode,[ebx].u.Exception.pExceptionRecord.ExceptionAddress

当然,Read的时候,需要注意要保存g_hProcess句柄.这样才可以.

下面的ShowAsm是封装的函数,内部还是调用的Decode2AsmOpco

ShowAsm proc pCode :ptr byte, pAddr:ptr Byte
    LOCAL  @szAsmCode[256]:BYTE
    LOCAL  @szOpCode[256]:BYTE
    LOCAL  @CodeSize1:DWORD

    invoke Decode2AsmOpcode, pCode, addr @szAsmCode, addr @szOpCode,
                             addr @CodeSize1, pAddr
    invoke crt_printf, offset g_szDcode, pAddr, addr @szOpCode, addr @szAsmCode

    ret

ShowAsm endp

这里提一下简单思路,因为真正的都写出来,汇编代码很多,影响观看,所以都是简单思路,没有完整代码,完整代码,在每天的课件资料中,可以去看,这里这说一下核心代码的思路

四丶汇编设置F2断点,以及单步(步入,步过)

简单思路

1.断点的设置:

  1.首先,系统断点第一次来,然后在创建进程的时候会有一个地址,我们使用Read...读取地址内容,然后反汇编出来显示

  2.读取出来之前,使用VirtualProtectEx将保护属性去除,(注意保存旧的)

  3.使用WriteProcessMemory往地址写入CC(注意保存以前的值)

  4.重新修改保护属性,改回去(使用旧的)

然后一个断点即完成了,具体代码,请看课件.

2.单步的设置(步入,进函数)

如果是单步,我们要判断断点是我们设置的还是系统设置的.

1.判断是否使我们设置的断点

2.修改内存保护属性(注意保存旧的)

3.写入CC,(int 3断点)

4.读取内存数据

5.显示反汇编

设置单步(步入)异常

1.打开线程获得线程句柄

2.使用GetThreadContext获取寄存器的值

3.设置单步标志,单步表示是要我们设置的,他是第9个标志

or [esi].regflag,0100h 这样设置即可.设置第九位为1

4.设置寄存器环境 SetThreadContext

设置单步步过异常

但步步过和单步步入一样

只不过遇到Call的时候我们要把他的下一条指令设置一个int3断点才可以.

课堂资料: 链接:http://pan.baidu.com/s/1geBOgQR 密码:5dw9

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

时间: 2024-08-25 03:04:46

调试器第二讲,单步步入/步过功能实现,以及基本的断点功能实现的相关文章

简易调试器的实现(一)

看过SEH结构化异常处理,看了<软件调试>这本书,觉得调试真是一件特别棒的事情,于是在网上搜索调试器怎么做,跟着大牛的脚步慢慢的往前走,于是借用大牛提供的代码,自己也开始慢慢做一个调试器 前期基本按照这个大牛的思路 http://www.cnblogs.com/zplutor/archive/2011/03/04/1971279.html 前期的分析也照着来点0.0 这一部分实现的功能是显示寄存器状态.显示字节码(感觉有一些问题,以后再修正),处理异常,断点处理(本来想在OEP断下来的,结果不

[Win32]一个调试器的实现(三)异常

[Win32]一个调试器的实现(三)异常 作者:Zplutor 出处:http://www.cnblogs.com/zplutor/ 本文版权归作者和博客园共有,欢迎转载.但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 这回接着处理上一篇文章留下的问题:如何处理EXCEPTION_DEBUG_EVENT这类调试事件.这类调试事件是调试器与被调试进程进行交互的最主要手段,在后面的文章中你会看到调试器如何使用它完成断点.单步执行等操作.所以,关于这类调

[Win32]一个调试器的实现(七)断点

[Win32]一个调试器的实现(七)断点 作者:Zplutor 出处:http://www.cnblogs.com/zplutor/ 本文版权归作者和博客园共有,欢迎转载.但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 断点是最基本和最重要的调试技术之一,本文讲解了如何在调试器中实现断点功能. 什么是断点 在进行调试的时候,只有被调试进程暂停执行时调试器才可以对它执行操作,例如观察内存内容等.如果被调试进程不停下来的话,调试器是什么也做不了的.要使

[Win32]一个调试器的实现(九)符号模型

[Win32]一个调试器的实现(九)符号模型 作者:Zplutor 出处:http://www.cnblogs.com/zplutor/ 本文版权归作者和博客园共有,欢迎转载.但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 在接下来的文章中会讲解如何在调试器中显示局部变量和全局变量的类型和值.实现这个功能一定要有调试符号的支持,因为调试符号记录了每个变量的名称,类型,地址,长度等信息.这不是一件轻松的事情,因为你首先要对符号模型有一定的了解.所以本

[Win32]一个调试器的实现(八)单步执行

[Win32]一个调试器的实现(八)单步执行 作者:Zplutor 出处:http://www.cnblogs.com/zplutor/ 本文版权归作者和博客园共有,欢迎转载.但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 上回讲解了如何实现断点功能,这回讲解如何实现与断点紧密相关的单步执行功能.单步执行有三种类型:StepIn,StepOver和StepOut,它们的实现方式比较多样化,单独实现它们的话并不困难,但是将它们整合到一起就比较困难了,

python学习第二讲,pythonIDE介绍以及配置使用

目录 python学习第二讲,pythonIDE介绍以及配置使用 一丶集成开发环境IDE简介,以及配置 1.简介 2.PyCharm 介绍 3.pycharm 的安装 二丶IDE 开发Python,以及配置IDE 1.初始化IDE 2.打开python项目. 3.pycharm设置python的默认解释器版本. 4.pycharm设置为所有用户可以使用,以及启动pycharm 5.设置pycharm启动图标(快捷方式) 6.卸载之前的pycharm python学习第二讲,pythonIDE介绍

[Win32]一个调试器的实现(六)显示源代码

[Win32]一个调试器的实现(六)显示源代码 作者:Zplutor 出处:http://www.cnblogs.com/zplutor/ 本文版权归作者和博客园共有,欢迎转载.但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 上一篇文章介绍了调试符号以及DbgHelp的加载和清理,这回我们使用它来实现一个显示源代码的功能.该功能的实际使用效果如下图所示: 该功能不仅仅是显示源代码,还要显示每一行代码对应的地址.实现该功能大概需要进行以下的步骤: ①

调试器如何工作(2)

调试器如何工作:第二部分--断点 原作者:Eli Bendersky http://eli.thegreenplace.net/2011/01/27/how-debuggers-work-part-2-breakpoints 这是关于调试器如何工作系列文章的第二部分.在这之前确保你读过第一部分. 在这部分 我将展示在调试器中如何实现断点.断点是调试的两大支柱之一--另一个是能够在被调试进程内存里查看值.在第一部分里我们已经预览过另一个支柱,但断点仍然笼罩在神秘的面纱下.看完本文,它们不再是了.

16位汇编语言第二讲系统调用原理,以及各个寄存器详解

昨天已将简单的写了一下汇编代码,并且执行了第一个显示到屏幕的helloworld 问题? helloworld怎么显示出来了. 一丶显卡,显存的概念 1.显示hello就要操作显示器,这是非常原始的,那个时候的程序员,并没有像现在的RGB(红绿蓝)这样的三色真彩色,那个时候就是操作显卡的,定义了一个标准 这个标准就是我们要往固定的地址写入数据,就会显示出来 具体流程 操作显卡 -> 显卡有自己的缓存 -> 把数据写入到显存中, - > 显示数据 (显示到屏幕上) 但是那个时候是没有字的,