控制台版调试器

一、主要实现功能:

1、基础功能

    (1)建立调试机制(创建调试进程、附加活动进程)

    (2)显示/修改汇编代码

    (3)查看/修改内存数据、查看栈

    (4)查看/修改寄存器

    (5)查看调试程序模块信息

2、断点功能

    (1)软件永久断点

    (2)硬件永久断点(执行、读、写断点)

    (3)内存永久访问断点(执行、读、写断点)

    (4)单步步入

    (5)单步步过

3、高级功能

    (1)条件断点

    (2)反反调试(隐藏PEB)

    (3)支持插件功能

4、附加功能

    (1)解析被调试程序任意模块的导出表导入表

    (2)解析符号

    (3)DUMP

二、部分功能实现截图

1、显示/修改汇编代码

将41225A修改为nop

 

 代码如下:

2、软件断点

实现原理:软件断点实际就是 `int 3` 指令, CPU在执行这条指令之后, 就会触发异常. 而异常被触发后,通过异常分发流程,调试器将会接收到一个异常事件. 此时, 就相当于'断点断下'到了调试器中. 软件断点的思路是, 不管程序是怎么运行的, 如果想要程序在飞速执行的过程中精准地停在某个位置. 那么必须事先在想要断下来的位置设置断点(将int3指令的机器码写入), 这样一来程序运行到int3指令自然就会停下来.

 实现代码:

//设置int3断点
void CBreakpoint::SetInt3BreakPoint(HANDLE hProcess, LPVOID addr)
{
	bp int3bp{ addr,TRUE };
	DWORD dwRead = 0;
	// 1.保存修改为 0xCC前那个字节的数据
	ReadProcessMemory(hProcess, addr, &int3bp.oldBytes, 4, &dwRead);
	// 2. 修改为 0xCC
	WriteProcessMemory(hProcess, addr, "\xcc", 1, &dwRead);
	g_bp.push_back(int3bp);
}

3、硬件断点

实现原理:使用硬件断点寄存器. 可用的硬件断点寄存器总共有Dr0,Dr1,Dr2,Dr3,Dr6,Dr7, 其中,Dr0到Dr3用于保存断点的地址. Dr7保存断点中断的条件. Dr7是一个32位寄存器, 里面总共保存着Dr0~Dr3四个断点地址寄存器 的中断条件.中断条件有三种: 1. 断点寄存器是否被启用: L0~L3被置1了,表示断点被启用,CPU会检测对应的DR0~DR3寄存器所记录的地址是否产生中断. 2. 断点的范围(长度): LEN0~LEN3记录了断点地址的命中范围, 这个范围只有以下几种: 0:1字节, 1:2字节, 2:4或8字节,3:4字节. 断点的长度会受到断点地址的影响, 当地址是4的倍数时,只能设置4字节, 当地址是2的倍数时,可设置成2字节和4字节,当地址是1的倍数时, 可以设置1/2/4字节. 3. 断点的中断类型 : RW0 ~ RW3记录断点的中断类型, 一般有以下几种: 0:执行断点 , 1:写入断点 , 2:I/O读写断点, 3:数据读写断点当断点中断类型为0时, 长度必须设置为0(表示1字节的长度),否则不会有效果因此, 要设置读写断点, 应将被访问的地址存入Dr0~Dr3中的一个, 将Dr7中对应的L0~L3设置为1,将对应的LEN0~LEN3设置为0/1/3,将对应 的RW0~RW3设置为3. 例如: DR0 = 0x403000 DR7.L0 = 1 DR7.LEN0 = 3

 实现代码:

// 设置硬件执行断点
void CBreakpoint::SetHwBreakPoint(HANDLE hThread, LPVOID addr, int type, int len)
{
	//原理:由CPU提供的Dr系列寄存器做多可以设置4个硬件断点,
	//断点的位置由Dr0~Dr3去保存,相应的Dr7中的Ln表示对应的
	//断点是否有效,Dr7寄存器还提供了RW\LEN标志位,用于设置
	//断点的类型,
	//RW:0(执行断点,它的len也必须为0), 1(写) 3(读写)
	//len:0(1字节), 1(2字节), 2(8字节), 3(4字节)
	hbp hbp{ addr,type,len,FALSE };
	CONTEXT context{ CONTEXT_DEBUG_REGISTERS };
	GetThreadContext(hThread, &context);

	PR7 Dr7 = (PR7)&context.Dr7;
	if (g_hbp[0].survive == 0)
	{
		Dr7->L0 = 1;
		Dr7->RW0 = type;
		Dr7->LEN0 = len;
		context.Dr0 = (DWORD)addr;
		g_hbp[0] = hbp;
	}
	else if (g_hbp[1].survive == 0)
	{
		Dr7->L1 = 1;
		Dr7->RW1 = type;
		Dr7->LEN1 = len;
		context.Dr1 = (DWORD)addr;
		g_hbp[1] = hbp;
	}
	else if (g_hbp[2].survive == 0)
	{
		Dr7->L2 = 1;
		Dr7->RW2 = type;
		Dr7->LEN2 = len;
		context.Dr2 = (DWORD)addr;
		g_hbp[2] = hbp;
	}
	else if (g_hbp[3].survive == 0)
	{
		Dr7->L3 = 1;
		Dr7->RW3 = type;
		Dr7->LEN3 = len;
		context.Dr3 = (DWORD)addr;
		g_hbp[3] = hbp;
	}
	else
	{
		printf("硬件断点已满,不能继续存储!");
	}
	
	SetThreadContext(hThread, &context);
}

3、内存断点

实现原理: 内存以分页作为管理单位, 内存分页具有读,写,执行的分页属性, 如果一 个内存分页中缺少了读权限,但又在这个分页上读取内容,就会产生内存 访问异常. 内存访问断点正是基于这个性质而提出的. 当要下断点时,一 般就是将断点地址所在的内存分页设置为没有任何分页访问属性, 这样 一来, 无论是在这个分页上进行什么操作, 都会产生访问异常. 如果想要 精确的设置,比如要设置地址被写入时才断下, 那么便可以只将内存分页 设置为不可写即可.

实现代码:

// 设置内存断点
void CBreakpoint::SetMrBreakPoint(HANDLE hProcess, LPVOID addr, int type, int len)
{
	mbp Mrbp = { addr,type};
	//设置页内存属性,保存原始属性
	if (type == 0)
	{
		VirtualProtectEx(hProcess, addr, len, PAGE_NOACCESS, &Mrbp.dwOldProtect);
	}
	else if (type == 1)
	{
		VirtualProtectEx(hProcess, addr, len, PAGE_EXECUTE_READ, &Mrbp.dwOldProtect);
	}
	else if (type == 8)
	{
		VirtualProtectEx(hProcess, addr, len, PAGE_READWRITE, &Mrbp.dwOldProtect);
	}
	//保存更改的地址
	g_mbp.push_back(Mrbp);
	return;
}

文件获取地址:MyDebug.zip-系统安全文档类资源-CSDN下载

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值