Salut !
Пытаюсь сделать что-то вроде обработчика обращений (r/w) по заданному адресу,
этакий "address watch", используя VirtualProtectEx для нужного адреса с
флагом PAGE_GUARD | PAGE_READWRITE, вроде как добился требуемого,
но только с одним большим "Но!"...обработчик срабатывает, после чего,
как и сказанно в MSDN PAGE_GUARD слетает. При повторном обращщении по заданному
адресу, обработчик ессно не срабатывает. Надо снова устанавливать этот флаг, а
это не интересно, т.к. целью является реакция на изменение значения адреса ( положим это
некий счётчик ) в чужой программе.
Один выход нашёлся, но мне он не совсем нравится.
Может есть какие соображения по этому поводу ?
Читайте каменты в приведённом ниже коде.
#include "stdafx.h"
#include <windows.h>
// Переменная, которую хотим мониторить
DWORD g_dwGlobe = 2008;
// Юзать ли поток реинициализации ?
BOOL g_bUseReinitThread = TRUE; // Если FALSE, то зациклимся при обращении к g_dwGlobe
// Форвард на установку монитора
template<typename _VALTYPE>
BOOL SetWatchAddress ( LPVOID lpAddress,DWORD dwAccessMode = PAGE_READWRITE );
/*
#define EXCEPTION_EXECUTE_HANDLER 1
#define EXCEPTION_CONTINUE_SEARCH 0
#define EXCEPTION_CONTINUE_EXECUTION -1
*/
DWORD WINAPI ReinitWatchThread ( VOID *lpAddress )
{
fprintf(stdout,"RE-INIT: [ %p ]\n",lpAddress);
if ( SetWatchAddress<DWORD>((LPVOID)lpAddress) )
fprintf(stdout,"RE-INIT: Ok\n");
else
fprintf(stdout,"RE-INIT: Failed...\n");
return 0;
}
LONG WINAPI ExceptionHandler( struct _EXCEPTION_POINTERS *p )
{
DWORD dwOldProtect = 0;
if ( p->ExceptionRecord->ExceptionCode != 0x80000001 ) // PAGE_GUARD
return EXCEPTION_CONTINUE_SEARCH;
// адрес нашей переменной в ExceptionInformation[1] ( &dwGlobe )
LPVOID lpAddress = (LPVOID)p->ExceptionRecord->ExceptionInformation[1];
// значение dwGlobe
DWORD dwValue = *(DWORD*)lpAddress;
#ifdef _DEBUG
fprintf(stdout,"HANDLER: Access handled to %p [ Initial Value: %d ]\n",lpAddress,dwValue);
fprintf(stdout,"HANDLER: EXCEPTION_RECORD { \n");
fprintf(stdout,"HANDLER: \tExceptionAddress: %p\n",p->ExceptionRecord->ExceptionAddress);
fprintf(stdout,"HANDLER: \tExceptionCode: %X\n",p->ExceptionRecord->ExceptionCode);
fprintf(stdout,"HANDLER: \tExceptionFlags: %X\n",p->ExceptionRecord->ExceptionFlags);
fprintf(stdout,"HANDLER: \tExceptoinRecord: %p\n",p->ExceptionRecord->ExceptionRecord);
fprintf(stdout,"HANDLER: \tNumberParameters: %d\n",p->ExceptionRecord->NumberParameters);
for ( int nParam = 0; nParam < p->ExceptionRecord->NumberParameters; nParam++ )
fprintf(stdout,"HANDLER: \t ExceptionInformation[%d]: %p\n",nParam,p->ExceptionRecord->ExceptionInformation[nParam]);
fprintf(stdout,"HANDLER: };\n");
fprintf(stdout,"HANDLER: CONTEXT { \n");
fprintf(stdout,"HANDLER: \tEAX=%08X, EBX=%08X,ECX=%08X,EDX=%08X,EDI=%08X,EBP=%08X,pESP=%08X,ESI=%08X,EIP=%08X\n",
p->ContextRecord->Eax,p->ContextRecord->Ebx,p->ContextRecord->Ecx,
p->ContextRecord->Edx,p->ContextRecord->Edi,p->ContextRecord->Ebp,
p->ContextRecord->Esp,p->ContextRecord->Esi,p->ContextRecord->Eip);
fprintf(stdout,"HANDLER: };\n");
#endif
// флаг PAGE_GUARD слетел, нужна его повторная установка,
// но только в потоке,
// т.к. если вызвать SetWatchAddress прямо тут, то зациклимся нахер...
if ( g_bUseReinitThread )
{
DWORD dwTid;
HANDLE hThread = CreateThread(NULL,1024,ReinitWatchThread,lpAddress,0,&dwTid);
CloseHandle ( hThread );
} else
{
// косяк конечно с шаблоном, но щас это не главное
// всё равно зациклимся, эх... просто для наглядности
SetWatchAddress<DWORD> ( lpAddress ); // READ-WRITE
}
fprintf(stdout,"HANDLER: Done\n");
return EXCEPTION_CONTINUE_EXECUTION;
}
// Установка монитора на lpAddress,
// шаблон, имхо, для понта...т.к. в потоке реинициализации,
// мы явно указываем DWORD...временно ?!
template<typename _VALTYPE>
BOOL SetWatchAddress ( LPVOID lpAddress , DWORD dwAccessMode /* = PAGE_READWRITE */)
{
HANDLE hProcess = GetCurrentProcess();
DWORD dwError = 0;
DWORD dwOldProtect = 0;
DWORD dwNewProtect = PAGE_GUARD | dwAccessMode;
fprintf(stdout,"SETUP: Watching lpAddress: %p [ Value: %d ]\n",lpAddress,*(_VALTYPE*)lpAddress);
VirtualProtectEx(hProcess,lpAddress,sizeof ( _VALTYPE ),dwNewProtect ,&dwOldProtect);
if ( ( dwError = GetLastError() ) != ERROR_SUCCESS )
fprintf(stdout,"SETUP: Cannot VirtualProtectEx,Error => %d\n",dwError );
else
{
LPTOP_LEVEL_EXCEPTION_FILTER pTopFilter = SetUnhandledExceptionFilter(ExceptionHandler);
fprintf(stdout,"SETUP: Previous Exception Filter %p\n",pTopFilter);
}
return dwError == 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
fprintf(stdout,"\nMAIN: Watching address %p, Initial Value %d\n",&g_dwGlobe,g_dwGlobe);
// Хотим наблюдать за g_dwGlobe... ( READ-WRITE )
SetWatchAddress<DWORD> ( &g_dwGlobe );
fprintf(stdout,"MAIN: - WRITE test ( 1 )\n");
// Сработает обработчик, !!! сбросит флаг PAGE_GUARD !!!
// и вернёт нас сюда же
g_dwGlobe = 2000;
fprintf(stdout,"MAIN: - WRITE test ( 1 ) done..., New Value = %d\n",g_dwGlobe);
fprintf(stdout,"MAIN: - WRITE test ( 2 )\n");
// спим 50ms, что бы поток реинициализации смог отработать ( Если юзам поток )
if ( g_bUseReinitThread )
SleepEx(50,TRUE); // 50 - вроде хватает для потока
// Если тут повторно не вызвать SetWatchAddress<DWORD> ( &g_dwGlobe );
// то ....
g_dwGlobe = 2001; // ... не вылетет в обработчик... ( PAGE_GUARD - сброшен )
// а Повторно вызывать SetWatchAddress в обработчике нельзя, т.к уйдём в цикл !
// ...как вариант ( g_bUseReinitThread = TRUE ), в обработчике создаём поток,
// который и вызвет SetWatchAddress...
fprintf(stdout,"MAIN: - WRITE test ( 2 ) done..., New Value = %d\n",g_dwGlobe);
#if 0
// Всё тоже самое касается в случае с чтеним по этому адресу...
fprintf(stdout,"MAIN: - READ test\n");
DWORD dwRead = g_dwGlobe;
// Тут спим секундочку, чтобы поток успел реинициализировать адресс...ччччиорд !
SleepEx(1000,1);
dwRead = g_dwGlobe;
fprintf(stdout,"MAIN: - READ test done , value %d...\n",dwRead);
#endif
fprintf(stdout,"MAIN: - Complete...\n");
return 0;
}
Буду рад, если кому-то это покажется интересным... ))
Удачи!