利用内核知识,自己实现ReadProcessMemory(二)
说了这么多关于表的格式和查表的方法,在实践中我们该如何利用呢?一个用处是我们经常需要把虚拟地址转换成物理地址,明白其转换原理利于分析问题,另外上面说了,每个进程都会有一套自己的分页机制,切换进程实际上是切换CR3中的内容,那么如何实现我们自己的ReadProcessMemory呢?
问题变成了以下4步:
拿到指定进程保存在CR3中的内容
切换当前的CR3
读取指定内存的内容
还原CR3
第一步,如何拿到指定进程保存在CR3中的内容?
我们知道在3环程序里,fs[0]保存的是线程环境块TEB,在0环,保存的则是处理器控制区(_KPCR),部分内核数据结构如下图:
从_EPROCESS中的进程链表我们可以遍历所有进程,当匹配到目标进程时,拿出目标进程DirectoryTableBase中保存的地址。
NTSTATUS GetProcessDirBase(IN DWORD dwPID, OUT PDWORD pDirBase)
{
PEPROCESS Process;
PEPROCESS CurProcess;
CHAR *pszImageName;
DWORD dwCurPID;
DWORD i;
__try
{
__asm
{
//ETHREAD
mov eax, fs:[124h]
//Current EPROCESS
mov eax, [eax + 44h]
mov Process, eax
}
CurProcess = Process;
i = 0;
//traversing EPROCESS
do
{
pszImageName = (char*)CurProcess + 0x174;
dwCurPID = (*(DWORD*)((char*)CurProcess + 0x084));
if (dwCurPID == dwPID)
{
*pDirBase = (*(DWORD*)((char*)CurProcess + 0x018));
return STATUS_SUCCESS;
}
//Next
CurProcess = (*(DWORD*)((char*)CurProcess + 0x088)) - 0x88;
} while (CurProcess != Process);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
dprintf("[MyReadProcessMemory] GetProcessDirBase __except \r\n");
}
return STATUS_INVALID_DEVICE_REQUEST;
}
第二步,切换当前的CR3
切换CR3的值之前,我们需要屏蔽调当前CPU核心的中断,以防线程切换,如果是多核的CPU,每个核心都需要屏蔽掉中断。同时,为了预防内存属性不可写,暂时改掉CR0中表示所有内存属性的标志,让所有内存暂时都可写。
__asm
{
//Shielding interrupt
cli
//close memory protect
mov eax, cr0
and eax, not 10000h
mov cr0, eax
mov eax, cr3
mov dwOldDirBase, eax
//swap CR3
mov eax, dwDirBase
mov cr3, eax
}
第三步,读取指定内存的内容
申请一段空间,暂存一下读取的数据,记得要检查目标内存地址是否有效
//Alloc ring0 Buff
char* szRing0Buf = (char*)MmAllocateNonCachedMemory(dwBufSize);
//check address invalid
if (MmIsAddressValid(dwTargetAdddress))
{
RtlCopyMemory(szRing0Buf, dwTargetAdddress, dwBufSize);
bIsRead = TRUE;
}
第四步,还原CR3
恢复内存属性,恢复中断
__asm
{
mov eax, dwOldDirBase
mov cr3, eax
//Reset memory protect
mov eax, cr0
or eax, 10000h
mov cr0, eax
//Restore interrupt
sti
}
总结
内核的学习也开始进入尾声,温故而知新,整理知识本身也是一种学习的过程。衷心感谢一年多以来钱老师,张老师,姚老师,戚老师,王老师,唐老师的指导。要毕业了,帮科锐宣传下,科锐30期正在招生中
本文由看雪论坛 五行猫 原创
转载请注明来自看雪社区
热门阅读
点击阅读原文/read,
更多干货等着你~