第四章 内存断点
1. 函数和结构
//内存断点处理部分
public:
//设置内存读写断点
void SetMemReadWriteBPoint(DWORD StartAddress,DWORD EndAddress);
//查找指定的当前内存页
BOOL FindMemPage(DWORD PageAddress, DWORD EndAddress);
//删除指定内存断点数据 <仅仅用于显示内存链表信息个数>
void DelMemBreakPoint(DWORD StartAddress, DWORD EndAddress, int MemFlag);
//删除已下的内存断点
BOOL DelMemBPoint(DWORD StartAddress, DWORD EndAddress, int MemFlag);
//内存断点处理标记
struct MemBPointFlag
{
BOOL bMemRead;
BOOL bMemWrite;
MemBPointFlag()
{
bMemRead = FALSE;
bMemWrite = FALSE;
}
};
#define READ 0
#define WRITE 1
struct MemBPointInfor
{
BOOL bDelFlag; // 删除标记
BOOL bRWFlag; // READ or WRITE
DWORD beginAddress; //断点开始地址
DWORD endAddress; //断点终止地址
};
//添加用户断点表 <当前页>
struct MemPageBreakPoint
{
DWORD BeginPageAddress; //分页开始地址
DWORD EndPageAddress; //设置内存断点当前分页的最后终止地址
DWORD OldPageProtect; //原来页的保护属性
CList<MemBPointInfor,MemBPointInfor> *RWMemBPList; //保存当前分页中所有读地址断点 <段+偏移 (保存偏移)>
};
3. 设计思路
内存断点和内存分页之间存在多对多的关系。用户下内存断点时,先决定出断点所跨分页,再每个分页中再组建一个读写断点链表,这个链表用来保存多个内存读断点和多个内存写断点。
设置内存断点部分:
SetMemReadWriteBPoint()中进行读或写内存断点的设置,在工程代码中分为三种情况写入到链表保存。
第一种:当下断点的地址的首地址和结束地址相同
可根据下断点的属性<访问或写>设置对应的值 PAGE_NOACCESS, PAGE_EXECUTE_READ,并保存原来的属性,保存在结构体MemBPointInfor中,在写入内存链表中。
第二种:当下断点地址跨分页时。
跨分页情况下,结束地址减去首地址得出跨的分页数,通过一个循环设置内存属性。断点开始地址,结束地址的分页分别保存开始地址断点,结束地址断点,跨分页的过程中保存分页的首地址。
例如:bpmr 405520-408520 跨4个分页,链表中对应产生4个分页结点405000分页中保存断点405520,
406000分页保存406000,
407000分页保存407000,
408000分页保存408520
得出当前分页: (下断的地址/0x1000) * 0x1000
第三种,下断地址已存在但断点的位置不同,即通过查找到对应分页加入断点列表,同时设置相对应属性。
因为当前分页的旧属性已存在,所以在此分页中多次下断点时不在保存分页属性,dwProtect的值即可舍弃。
触发访问异常部分: 当程序执行到访问异常时,触发访问异常处理
在DisposeAccessException(EXCEPTION_RECORD RecordTmp)函数中
RecordTmp.ExceptionInformation[0]是否为空可以判断触发的当前动作时访问数据还是写入数据。RecordTmp.ExceptionInformation[1]的具体地址值与内存链表中的地址进行匹配来判断是否处理访问异常,如果匹配则保存下断地址的《访问或写》属性,然后设置单步进入单步异常重新设置内存页的属性。
单步处理中恢复内存页属性的代码段:
if(m_MemStepFlag == TRUE)
{
DWORD dwProtect = 0;
if (m_RWMemFlag == READ)
::VirtualProtectEx(m_hProcess,(void *)m_StartMemAddr,1,PAGE_NOACCESS,&dwProtect);
if (m_RWMemFlag == WRITE)
::VirtualProtectEx(m_hProcess,(void *)m_StartMemAddr,1,PAGE_EXECUTE_READ,&dwProtect);
//代码跟踪,保存当前程序运行时的EIP值
m_CurCode.pCurCodeEntryAddress = m_Context.Eip;
m_MemStepFlag = FALSE;
}
4.查询内存断点设计
//保存内存链表用于显示 <仅仅用于显示内存链表信息个数>
CList<MemBPointInfor, MemBPointInfor> m_ShowMemList;
考虑到内存断点跨分页的情况,所以依靠保存下断地址的分页查找很麻烦,所以在设计上多增加了一个链表m_ShowMemList保存所有内存断点的链表,主要方便与在查找内存断点和删除内存断点。
5.删除内存断点设计 <删除标记位>
先在m_ShowMemList链表中查到内存断点,将删除标记位清0,通过DelMemBreakPoint在m_MemBPList内存链表将对应的内存断点的删除标记位清0。
注意: 当删除分页中最后一个断点时,必须恢复原来分页的属性。
内容中涉及到的具体代码请见后期发布源代码和相关详细文档!