[分享]调戏:海森堡原理内存访问检测的梗
名字高大上的梗,其实如果知道了具体原因就会觉得很简单。
首先说一下 海森堡原理是什么
从百度百科可以得知
http://baike.baidu.com/view/24947.htm
海森堡原理就是如果测量某个参数必然导致其他参数被改变。
那么当调试器存在时,会改变那些事情呢?
这里不得不提到一个古老的掉渣的东西就是PAGE_GUARD属性。
当任意一个内存页属性被设置上PAGE_GUARD时,
访问该页会抛出一个异常,并消除PAGE_GUARD属性。
但是如果是调试器或者其他进程使用API去访问内存的时候,
不会触发进程内部异常,而且访问后,该属性就会消失。
于是有一种奇葩的逻辑就诞生了。
实现方式:
1.设置一个异常扑捉(VEH或者HOOK)。
2.遍历指定模块的内存分布(块状分布,其实可以直接对整个模块做处理,效率都差不多)。
3.对指定模块的内存进行设置PAGE_GUARD。
4.异常中处理PAGE_GUARD异常的并做标记
5.定时检查指定模块的内存是否PAGE_GUARD属性被去除和重新设置PAGE_GUARD,
如果被去除,则判断是否正常的异常,不是则退出进程。
#include "stdafx.h"
#include <windows.h>
#include <list>
#include "ntdll.h"
typedef struct _MAD_MEM_
{
MEMORY_BASIC_INFORMATION mbi;
BOOL bAccess;
}MAD_MEM,*PMAD_MEM;
typedef ULONG (WINAPI *pfnRtlDispatchException)(PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext);
//////////////////////////////////////////////////////////////////////////
BOOL RtlDispatchException(PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext);
//////////////////////////////////////////////////////////////////////////
static pfnRtlDispatchException m_fnRtlDispatchException=NULL;
//////////////////////////////////////////////////////////////////////////
BOOL g_bHook = FALSE;
std::list<PMAD_MEM> memoryBlocks;
//////////////////////////////////////////////////////////////////////////
ULONG WINAPI _RtlDispatchException( PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext )
{
if(RtlDispatchException(pExcptRec,pContext)) return 1;
return m_fnRtlDispatchException(pExcptRec,pContext);
}
VOID InstallMemAccessDetect()
{
BYTE *pAddr=(BYTE *)::GetProcAddress(::GetModuleHandleA("ntdll.dll"),"KiUserExceptionDispatcher");
if (g_bHook)
{
return;
}
if (pAddr)
{
while (*pAddr!=0xE8)pAddr++;
m_fnRtlDispatchException=(pfnRtlDispatchException)((*(DWORD *)(pAddr+1))+5+(DWORD)pAddr);
DWORD dwNewAddr=(DWORD)_RtlDispatchException-(DWORD)pAddr-5;
DWORD dwOld;
VirtualProtect((LPVOID)pAddr,0x1000,PAGE_EXECUTE_READWRITE,&dwOld);
RtlCopyMemory((PVOID)((DWORD)pAddr+1),(PVOID)&dwNewAddr,4);
g_bHook=TRUE;
}
}
DWORD WINAPI MADThread(LPVOID Param)
{
std::list<PMAD_MEM>::iterator b;
for (b = memoryBlocks.begin(); b != memoryBlocks.end(); b++)
{
DWORD dwOld;
PMAD_MEM m_Block = *b;
m_Block->bAccess=FALSE;
*b=m_Block;
VirtualProtect(m_Block->mbi.BaseAddress,
m_Block->mbi.RegionSize,
m_Block->mbi.Protect|PAGE_GUARD,
&dwOld);
}
while (1)
{
std::list<PMAD_MEM>::iterator b;
for (b = memoryBlocks.begin(); b != memoryBlocks.end(); b++)
{
DWORD dwOld;
PMAD_MEM m_Block = *b;
MEMORY_BASIC_INFORMATION mbi;
VirtualQueryEx(GetCurrentProcess(),
m_Block->mbi.BaseAddress,
&mbi,
sizeof(mbi));
if ((mbi.Protect&PAGE_GUARD)!=PAGE_GUARD)
{
if (!m_Block->bAccess)
{
//发生了外部内存访问!
ExitProcess(-1);
}
}
m_Block->bAccess=FALSE;
*b=m_Block;
VirtualProtect(m_Block->mbi.BaseAddress,
m_Block->mbi.RegionSize,
m_Block->mbi.Protect|PAGE_GUARD,
&dwOld);
}
Sleep(1000*30);
}
}
VOID InitMAD()
{
MEMORY_BASIC_INFORMATION meminfo;
unsigned char *addr = 0;
unsigned char *endaddr =0;
DWORD totalBytes = 0;
InstallMemAccessDetect();
VirtualQuery((LPCVOID)GetModuleHandleA("game.dll"),&meminfo,sizeof(meminfo));
addr = (unsigned char *)meminfo.AllocationBase;
endaddr = addr + RtlImageNtHeader(addr)->OptionalHeader.SizeOfImage;
while (addr<endaddr)
{
if (!VirtualQueryEx(GetCurrentProcess(), addr, &meminfo, sizeof(meminfo)))
break;
bool Commited = meminfo.State & MEM_COMMIT;
bool Readable = meminfo.Protect & (PAGE_READWRITE |PAGE_READONLY);
bool Guarded = meminfo.Protect & PAGE_GUARD;
if (Commited && Readable && !Guarded)
{
totalBytes += (DWORD)meminfo.RegionSize;
PMAD_MEM pNew = new MAD_MEM;
pNew->mbi = meminfo;
pNew->bAccess = FALSE;
memoryBlocks.push_back(pNew);
}
addr = (unsigned char*)meminfo.BaseAddress + meminfo.RegionSize;
}
CreateThread(NULL,0,
MADThread,NULL,
0,NULL);
}
BOOL RtlDispatchException(PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext)
{
ULONG_PTR Eip = (ULONG_PTR)pExcptRec->ExceptionAddress;
if (!memoryBlocks.empty() && pExcptRec->ExceptionCode == EXCEPTION_GUARD_PAGE)
{
std::list<PMAD_MEM>::iterator b;
for (b = memoryBlocks.begin(); b != memoryBlocks.end(); b++)
{
PMAD_MEM m_Block = *b;
if (Eip>=(ULONG_PTR)m_Block->mbi.BaseAddress
&& Eip<=(ULONG_PTR)m_Block->mbi.BaseAddress+m_Block->mbi.RegionSize)
{
m_Block->bAccess =TRUE;
*b = m_Block;
return TRUE;
}
}
}
return FALSE;
}
