memexcept.c
#define _WIN32_WINNT 0x500 #include<windows.h> #include"memexcept.h" #define roundup(x,n) (((x)+((n)-1))&(~((n)-1))) #define rounddown(x,n) ((x)&(~((n)-1))) #define BusyWait(x) while(InterlockedCompareExchange((void**)&(x),0,0))SwitchToThread() #define Acquire(x) while(InterlockedExchange(&(x),1))SwitchToThread() #define Release(x) InterlockedExchange(&(x),0) typedef PVOID(WINAPI*PAddVectoredExceptionHandler)(ULONG FirstHandler,PVOID VectoredHandler); typedef ULONG(WINAPI*PRemoveVectoredExceptionHandler)(PVOID Handler); static PAddVectoredExceptionHandler pAddVectoredExceptionHandler=0; static PRemoveVectoredExceptionHandler pRemoveVectoredExceptionHandler=0; typedef struct _ADDRRANGE { struct _ADDRRANGE*Next,*Previous; char*ProtectStart,*ProtectEnd,*WatchStart,*WatchEnd; DWORD OldProtect; PMemoryWatchCallback Callback; BOOL NotifyAfterRead:1,NotifyAfterWrite:1,Pending:1,Enabled:1,IsWrite:1; void*Address; UINT_PTR Tag; CRITICAL_SECTION CriticalSection; }ADDRRANGE,*PADDRRANGE; static SYSTEM_INFO SystemInfo; static PVOID HandlerHandle=0,RangeHeap=0; static PADDRRANGE RangeListHead=0; static LONG RangeListRead=0,RangeListWrite=0; BOOL CanRead(void*Address) { __try { *(volatile char*)Address; return TRUE; }__except(EXCEPTION_EXECUTE_HANDLER) { return FALSE; } } BOOL CanWrite(void*Address) { __try { *(volatile char*)Address=*(volatile char*)Address; return TRUE; }__except(EXCEPTION_EXECUTE_HANDLER) { return FALSE; } } HANDLE RegisterMemoryWatcher(PVOID BaseAddress,SIZE_T Length,PMemoryWatchCallback Callback,BOOL NotifyAfterRead,BOOL NotifyAfterWrite,UINT_PTR Tag) { PADDRRANGE range; range=HeapAlloc(RangeHeap,0,sizeof(ADDRRANGE)); if(range) { range->Previous=0; range->Next=RangeListHead; range->WatchStart=(char*)BaseAddress; range->WatchEnd=(char*)BaseAddress+Length; range->ProtectStart=(char*)rounddown((SIZE_T)BaseAddress,SystemInfo.dwPageSize); range->ProtectEnd=(char*)roundup((SIZE_T)BaseAddress+Length,SystemInfo.dwPageSize); range->Callback=Callback; range->NotifyAfterRead=NotifyAfterRead; range->NotifyAfterWrite=NotifyAfterWrite; range->Pending=0; range->Enabled=1; range->Tag=Tag; InitializeCriticalSection(&range->CriticalSection); if(VirtualProtectEx(GetCurrentProcess(),BaseAddress,Length,PAGE_NOACCESS,&range->OldProtect)) { if(RangeListHead) RangeListHead->Previous=range; RangeListHead=range; return (HANDLE)range; }else { DeleteCriticalSection(&range->CriticalSection); HeapFree(RangeHeap,0,range); } } return NULL; } VOID UnregisterMemoryWatcher(HANDLE Handle) { PADDRRANGE range; if(Handle) { range=(PADDRRANGE)Handle; VirtualProtectEx(GetCurrentProcess(),range->WatchStart,range->WatchEnd-range->WatchStart,range->OldProtect,&range->OldProtect); Acquire(RangeListWrite); BusyWait(RangeListRead); if(range->Previous) range->Previous->Next=range->Next; if(range->Next) range->Next->Previous=range->Previous; DeleteCriticalSection(&range->CriticalSection); HeapFree(RangeHeap,0,range); Release(RangeListWrite); } } VOID EnableMemoryWatcher(HANDLE Handle,BOOL Enable) { PADDRRANGE range; if(Handle) { DWORD OldProtect; range=(PADDRRANGE)Handle; if(range->Enabled=Enable) VirtualProtectEx(GetCurrentProcess(),range->WatchStart,range->WatchEnd-range->WatchStart,PAGE_NOACCESS,&OldProtect); else VirtualProtectEx(GetCurrentProcess(),range->WatchStart,range->WatchEnd-range->WatchStart,range->OldProtect,&OldProtect); } } LONG CALLBACK MemExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) { PEXCEPTION_RECORD ExceptionRecord;PCONTEXT Context; ExceptionRecord=ExceptionInfo->ExceptionRecord; Context=ExceptionInfo->ContextRecord; if(ExceptionRecord->ExceptionCode==EXCEPTION_ACCESS_VIOLATION) { PADDRRANGE range;DWORD OldProtect;BOOLEAN Pending,Found=0; char*addr=(char*)ExceptionRecord->ExceptionInformation[1]; Acquire(RangeListWrite); InterlockedIncrement(&RangeListRead); Release(RangeListWrite); for(range=RangeListHead;range;range=range->Next) { if(addr>=range->ProtectStart&&addr<range->ProtectEnd) { EnterCriticalSection(&range->CriticalSection); VirtualProtectEx(GetCurrentProcess(),range->WatchStart,range->WatchEnd-range->WatchStart,range->OldProtect,&OldProtect); Found=1; if(addr>=range->WatchStart&&addr<range->WatchEnd&&range->Enabled) { range->IsWrite=(ExceptionRecord->ExceptionInformation[0]==1); range->Address=addr; if((!range->NotifyAfterRead&&!range->IsWrite)||(!range->NotifyAfterWrite&&range->IsWrite)) if(range->Callback(range->Address,range->IsWrite,&range->Tag)) { range->Enabled=0; LeaveCriticalSection(&range->CriticalSection); range->Pending=0; continue; } range->Pending=1; Pending=1; } } } InterlockedDecrement(&RangeListRead); if(Pending)Context->EFlags|=256; return Found?EXCEPTION_CONTINUE_EXECUTION:EXCEPTION_CONTINUE_SEARCH; }else if(ExceptionRecord->ExceptionCode==EXCEPTION_SINGLE_STEP) { PADDRRANGE range;DWORD OldProtect; Acquire(RangeListWrite); InterlockedIncrement(&RangeListRead); Release(RangeListWrite); for(range=RangeListHead;range;range=range->Next) if(range->Pending) { if((range->NotifyAfterRead&&!range->IsWrite)||(range->NotifyAfterWrite&&range->IsWrite)) range->Enabled=!range->Callback(range->Address,range->IsWrite,&range->Tag); VirtualProtectEx(GetCurrentProcess(),range->WatchStart,range->WatchEnd-range->WatchStart,PAGE_NOACCESS,&OldProtect); LeaveCriticalSection(&range->CriticalSection); range->Pending=0; } InterlockedDecrement(&RangeListRead); return EXCEPTION_CONTINUE_EXECUTION; } return EXCEPTION_CONTINUE_SEARCH; } BOOL StartupMemExcept(void) { GetSystemInfo(&SystemInfo); RangeHeap=HeapCreate(0,0,0); if(!pAddVectoredExceptionHandler) pAddVectoredExceptionHandler=(PAddVectoredExceptionHandler)GetProcAddress(GetModuleHandleW(L"kernel32.dll"),"AddVectoredExceptionHandler"); if(pAddVectoredExceptionHandler) HandlerHandle=pAddVectoredExceptionHandler((ULONG)-1,MemExceptionHandler); return HandlerHandle!=0; } BOOL CleanupMemExcept(void) { BOOL r=FALSE; if(!pRemoveVectoredExceptionHandler) pRemoveVectoredExceptionHandler=(PRemoveVectoredExceptionHandler)GetProcAddress(GetModuleHandleW(L"kernel32.dll"),"RemoveVectoredExceptionHandler"); if(pRemoveVectoredExceptionHandler) r=pRemoveVectoredExceptionHandler(HandlerHandle); if(r) { PADDRRANGE range; Acquire(RangeListWrite); BusyWait(RangeListRead); for(range=RangeListHead;range;range=range->Next) VirtualProtectEx(GetCurrentProcess(),range->WatchStart,range->WatchEnd-range->WatchStart,range->OldProtect,&range->OldProtect); for(range=RangeListHead;range;range=range->Next) { DeleteCriticalSection(&range->CriticalSection); } HeapDestroy(RangeHeap); Release(RangeListWrite); RangeListHead=0; RangeHeap=0; } return r; }
typedef int(CALLBACK*PMemoryWatchCallback)(void*Address,BOOL Write,UINT_PTR*Tag); #ifdef __cplusplus extern"C"{ #endif extern BOOL CanRead(void*Address); extern BOOL CanWrite(void*Address); extern BOOL StartupMemExcept(void); extern BOOL CleanupMemExcept(void); extern HANDLE RegisterMemoryWatcher(PVOID BaseAddress,SIZE_T Length,PMemoryWatchCallback Callback,BOOL NotifyAfterRead,BOOL NotifyAfterWrite,UINT_PTR Tag); extern VOID UnregisterMemoryWatcher(HANDLE Handle); extern VOID EnableMemoryWatcher(HANDLE Handle,BOOL Enable); #ifdef __cplusplus } #endif
#include<stdio.h> #include<windows.h> #include"memexcept.h" int CALLBACK cb(void*addr,BOOL w,UINT_PTR*Tag) { int*p=(int*)addr; printf("address:%p,write:%d,value:%d\n",p,w,CanRead(&p)?*p:0); return 0;//return 1 to disable watcher } int main() { int(*p)[4]; p=(int(*)[4])VirtualAlloc(0,sizeof(*p),MEM_COMMIT,PAGE_READWRITE); (*p)[0]=0; (*p)[1]=1; (*p)[2]=2; StartupMemExcept();//initialize RegisterMemoryWatcher(p,sizeof(*p),cb,FALSE,TRUE,0); printf("v:%d\n",(*p)[0]); printf("v:%d\n",(*p)[1]); puts("dd"); __try { printf("v:%d\n",(*p)[2]); (*p)[3]=6; *(volatile char*)0;//not watched }__except(EXCEPTION_EXECUTE_HANDLER) { puts("except"); } CleanupMemExcept();//cleanup return 0; }