1

I'm looking for a way to get notified when some block of memory of the current process change.

To be more specific, I want to track when some fields of a struct change.

Let's say I have one instance of this struct in memory:

struct {
   int field_a;
   int field_b;
} my_struct;

Is there any way to register a callback to notify me when any field has changed?

I know some debuggers provide "data breakpoints" which pauses execution when a specified variable change.

Is there any way, maybe some win32 debug api or interrupt that make this possible ?

5
  • Hi and welcome to RE.SE. Hmm, it really depends on what you're actually aiming for. As in so many questions (i.e. not just yours) we only are told what route you have already decided on and you want help with that. But potentially there are other ways. It always helps to provide context. Also, this isn't exactly RCE, but it's a related topic, so I'll defer to the community. Either way, you could use VirtualProtect to set up a guard page, catch the exception via SEH/VEH and then resume execution ...
    – 0xC0000022L
    Jul 10, 2021 at 21:08
  • Hi @0xC0000022L , I'll add more context to my question. With VirtualProtect I can lock a whole memory page, but I'm looking for a way to lock something smaller, like a struct. Jul 10, 2021 at 22:53
  • Sound like you are searching for something like a "break on access" debugger breakpoint: stackoverflow.com/questions/51221203/…
    – Robert
    Jul 11, 2021 at 12:45
  • @Robert those are the mentioned data breakpoints and there is a limited number of them available "in hardware". Depending on the debugger in use, it can offer more than those "in hardware", but this will typically slow down the debugging considerably.
    – 0xC0000022L
    Jul 11, 2021 at 21:06
  • 1
    @ClaytonA.Alves while you are right, you can see from the context record where the code attempted to perform the access, as blabb shows.
    – 0xC0000022L
    Jul 11, 2021 at 21:07

1 Answer 1

3

VirtualProtect locks whole page and Raises an Exception on access and removes the PAGE_GUARD Memory Protection.
In the Exception Handler you watch for a your small block
If except->ExceptionRecord->ExceptionAddress is not within your watch block
you reset the protection within Your Exception Handler.
reset the ContextRecord->Rip to next instruction and return EXECEPTION_CONTINUE_EXECUTION

see below for a sample code

#include <stdio.h>
#include <windows.h>
typedef struct _S1{
    int field_a;
    int field_b;
} S1, *PS1;
//putting guarded data in a seperate section for convenience 
#pragma data_seg(push, MyGuardedSection, ".guarded")
S1 t1 = {0, 0};
#pragma data_seg(pop, MyGuardedSection)
DWORD oldprot = 0;
int filter(unsigned int code, struct _EXCEPTION_POINTERS *except){
    if (code == EXCEPTION_GUARD_PAGE &&
        except->ExceptionRecord->ExceptionAddress != (&(t1.field_b)))    {
        VirtualProtect(&t1.field_b, sizeof(t1.field_b), 0x140, &oldprot);
        except->ContextRecord->Rip += 6;
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    printf("%x\n%p\n",
           except->ExceptionRecord->ExceptionCode,
           except->ExceptionRecord->ExceptionAddress);
    return EXCEPTION_EXECUTE_HANDLER;
}
int main(void){
    DWORD counter = 0;
    DWORD loopcount = 0;
    VirtualProtect(&t1.field_b, sizeof(t1.field_b), 0x140, &oldprot);
    __try    {
        while (loopcount < 2)        {
            while (counter < 0x30)            {
                t1.field_a++; //this will raise guard page exception
                counter++;   // we return here from handler after reguarding
                printf("%x ", counter);
                Sleep(0x10);
            }
            counter = 0;
            loopcount += 1;
            printf("\nwe have reset guard page exception 0x60 times \n");
        }
        printf("we access our field now \n");
        t1.field_b = 0xdead; //this again will raise exception and we execute handler 
        
    }
    __except (filter(GetExceptionCode(), GetExceptionInformation()))    {
        //removing page guard
        VirtualProtect(&t1.field_b, sizeof(t1.field_b), 0x40, &oldprot);
        printf("t1.field_b = %x\n", t1.field_b);
        t1.field_b = 0xdead;
        printf("t1.field_b = %x\n", t1.field_b); // no exception 
        printf("Handler for PG %x\n", GetExceptionCode());
    }
}

compiled and executed

:\>cl /Zi /W4 /analyze /EHsc /nologo vlock.cpp /link /release
vlock.cpp

:\>vlock.exe
1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30
we have reset guard page exception 0x60 times
1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30
we have reset guard page exception 0x60 times
we access our field now
c0000005
00007FF7CA131159
t1.field_b = 0
t1.field_b = dead
Handler for PG c0000005

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.