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

2018-01-08 11:37:33 浏览数 (1)

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

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

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

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

关于反汇编引擎的介绍: 请参考转载博客

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

比如我们反汇编的结果是

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

如果使用上面那个则是

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

先编译成OBJ

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

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

关于lib工具的使用,请参考我写的博客,32位汇编第七讲,混合编程 连接

现在回车,生成我们的lib

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

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

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

二丶使用反汇编引擎API

直接看代码吧

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

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

代码语言:c复制
#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%-20srn",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,获取反汇编的各种信息

代码语言:javascript复制
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

代码语言:javascript复制
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

0 人点赞