News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Monitoring a memory address

Started by donkey, May 02, 2011, 03:55:34 PM

Previous topic - Next topic

donkey

I have been working on some extensions to the debug macros for RadAsm3/GoAsm and thought it would be nice to have an alternative to the Spy macro which is a bit slow but useful in its way. I decided to make one called Monitor that will monitor a data section address for changes but have minimal impact on the run of the program. In order to do this I decided that PAGE_GUARD was the best way to go.

There were two major issues when dealing with PAGE_GUARD exceptions. The first was to set the initial page guard and the second was how to deal with the exception. The first issue was fairly easy, I use an INT3 (my favorite instruction these days) to signal the exception handler that I am either starting or stopping the Monitor based on a flag. The second is a little more complicated.

If I am handling PAGE_GUARD exceptions and want the program to continue I simply trap the exception and return EXCEPTION_CONTINUE_EXECUTION from my handler. However since the execution is stopped at the point of the error, EIP is pointing to the address where the exception occurred, I have to execute that instruction because I want the operation to complete. The problem is that how do I set the PAGE_GUARD again ? If I set it during the STATUS_GUARD_PAGE_VIOLATION handling, returning EXCEPTION_CONTINUE_EXECUTION will just execute the same instruction again and another exception will occur, the handler is dumped into an infinite loop. If I advance EIP to the next instruction then I have not allowed the operation to complete and any further information is meaningless. So the answer is to single step the program after the STATUS_GUARD_PAGE_VIOLATION has been trapped and then in the EXCEPTION_SINGLE_STEP handler turn on the PAGE_GUARD again. This satisfies all conditions, the instruction is completed after a STATUS_GUARD_PAGE_VIOLATION and I can continue to monitor the memory address.

Here's a bit of code that demonstrates the solution. It has not been added to the debug library yet as I still have to test under MASM and X64 but it should be ready to go soon. The RDBG_MONxxx variables are global memory variables that store the information across calls to the exception handler. RDBG_MONVARADDR holds the address we are monitoring and is set before the initial INT3.

DbgMonitor FRAME pExcptPointers
uses ebx,edi,esi
LOCAL PageSate :D
LOCAL Excpt_Line[256]:B
LOCAL mbi:MEMORY_BASIC_INFORMATION

mov edi,[pExcptPointers]
mov esi,[edi+EXCEPTION_POINTERS.ContextRecord] // Pointer to context structure
mov edi,[edi+EXCEPTION_POINTERS.ExceptionRecord] // Pointer to exception record

xor ebx,ebx
mov ebx,[edi+EXCEPTION_RECORD.ExceptionCode]
mov eax,[edi+EXCEPTION_RECORD.ExceptionAddress]

// Check for guard violation
cmp ebx,STATUS_GUARD_PAGE_VIOLATION
jne >>.EXCEPTION_SINGLE_STEP
// Page guard violations store the address that was accessed
// in the extra information at the end of the EXCEPTION_RECORD
mov eax,[edi+EXCEPTION_RECORD.ExceptionInformation+4]
// Is this the address we're looking for ?
cmp eax,[RDBG_MONVARADDR]
je >
// If its not the right address then just set Single Step
// Setting RDBG_MONEIP to 0 signals the handler to ignore the instruction
mov D[RDBG_MONEIP],0
or D[esi+CONTEXT.EFlags],0x100
mov eax,EXCEPTION_CONTINUE_EXECUTION
ret
:
// This is the address we're looking for so store the address of the exception in RDBG_MONEIP
// the presence of an address there will signal the handler to process this instruction
mov eax,[edi+EXCEPTION_RECORD.ExceptionAddress]
mov [RDBG_MONEIP],eax
// Set Single Step
or D[esi+CONTEXT.EFlags],0x100
mov eax,EXCEPTION_CONTINUE_EXECUTION
ret

.EXCEPTION_SINGLE_STEP
cmp ebx,EXCEPTION_SINGLE_STEP
jne >>.STARTPAGEGUARD
cmp D[RDBG_MONEIP],0
je >>.EXIT
// The memory address we are monitoring has been accessed
// Print some message that informs the user

.EXIT
// Discard the EIP
mov D[RDBG_MONEIP],0
// We single step the code after a STATUS_GUARD_PAGE_VIOLATION
// then we reset the page guard at the next instruction
invoke VirtualProtect,[RDBG_MONVARADDR],32,[RDBG_MONGRDSTATE],offset PageSate
mov eax,EXCEPTION_CONTINUE_EXECUTION
ret

.STARTPAGEGUARD
cmp ebx,EXCEPTION_BREAKPOINT
jne >>.UNHANDLED
cmp D[RDBG_MONENABLED],0
jne >>
// This is called when the Monitor is turned on
// This has to be zero so make sure
mov D[RDBG_MONEIP],0
// Set the flag
mov D[RDBG_MONENABLED],1
// Get the current protection flags
invoke VirtualQuery,[RDBG_MONVARADDR],offset mbi,SIZEOF MEMORY_BASIC_INFORMATION
mov eax,[mbi.Protect]
// Save the old flags
mov [RDBG_MONOLDSTATE],eax
or eax,PAGE_GUARD
// Save the modified flags
mov [RDBG_MONGRDSTATE],eax
// Turn on PAGE_GUARD
invoke VirtualProtect,[RDBG_MONVARADDR],32,eax,offset PageSate
// Advance EIP to past the INT3
inc D[esi+CONTEXT.Eip]
mov eax,EXCEPTION_CONTINUE_EXECUTION
ret

:
// This is called when the Monitor is turned off
// Reset the flag
mov D[RDBG_MONENABLED],0
mov D[RDBG_MONEIP],0
// Turn off PAGE_GUARD
invoke VirtualProtect,[RDBG_MONVARADDR],32,[RDBG_MONOLDSTATE],offset PageSate
// Advance EIP to past the INT3
inc D[esi+CONTEXT.Eip]
mov eax,EXCEPTION_CONTINUE_EXECUTION
ret

.UNHANDLED
// Pass other exceptions to the next handler
mov eax,EXCEPTION_CONTINUE_SEARCH
ret
endf


This was a bit of a puzzler so I thought that some one might find the information interesting.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

drizz

You have to do that for any sticky breakpoint (hardware, software, memory).
In Win9x you could use resume flag (RF) to continue without any extra work but in WinNT kernel always clears RF (I haven't checked it on a x64 system, in fact I haven't checked any non xp osĀ  :red).
The truth cannot be learned ... it can only be recognized.

MichaelW

Doesn't the system clear the PAGE_GUARD modifier before it calls the exception handler? In my self-expanding buffer code I am depending on this behavior, and for EXCEPTION_GUARD_PAGE I am returning EXCEPTION_CONTINUE_EXECUTION, without modifying EIP in the CONTEXT record, without problems.
eschew obfuscation

drizz

MichaelW, But you don't need to restore protection back.
The truth cannot be learned ... it can only be recognized.

donkey

Hi Michael,

Yes it clears it. However for my purposes I want it set again after the exception is handled because I want to track all access to the memory location. The major problem was that if I set it in the page guard exception handler it would dump it into an infinite loop. So the program is stepped to the next instruction and the page guard is set again. This allows me to continue watching the memory location I am interested in.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

FlySky

Donkey great contribution like always.

Although I have a question regarding PAGE_GUARDS.

Using VirtualProtectEx API I am not able to set the PAGE_GUARD on for example the code section. I am playing with an exception handler,
but the PAGE_GUARD is never set when I use it in my code:

   Mov Ebx, [imagebase]
   Mov Eax, [codesection]
   Add Ebx, Eax
   Invoke VirtualProtectEx, [pinfo.hProcess], Ebx, 1, PAGE_GUARD, Offset oldprot


donkey

Hi FlySky,

That piece of code has changed a lot since I posted it, mainly it now has virtually allocated memory instead of global variables so that the page guard will not be triggered by the debug routine. I have not tried to use page guards in another process using VirtualAllocEx but according to the documentation it should work. However, modifying memory protection is a sticky issue when its applied to other processes and I know that newer versions of Windows have placed restrictions on what you're allowed to do, I will have to read through the docs a little more carefully to have a definite answer to your question.
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

FlySky

Yeah the MSDN isn't giving to much information about PAGE_GUARDS.

I am testing it all on windows 7 64 bit (running the process 32 bit mode).

A bit more detail about the code:

   Mov Ebx, [imagebase] --> Copy imagebase in ebx
   Mov Eax, [codesection] --> codesection is a variable holding the pointer to the base of code section
   Add Ebx, Eax
   Invoke VirtualProtectEx, [pinfo.hProcess], Ebx, 1, PAGE_GUARD, Offset oldprot

FlySky

I fixed the issue. It was an error / misunderstanding on my side.

I created a loader which installs an exception handler inside a new program. But the program was being Created using CreateProcess.

Not a big deal as this is common although I had started the process as a debugged process. According to MSDN when an Page_guard is being set
and a debugger catches the Page_guard it automaticly clears the Page_guard, hence Why I thought the page_guard was not set properly.


FlySky

I am trying to develop a small debugger engine which is able to set and catch breakpoints (atm just very basic things).

It's based on the code supplied by donkey and based on PAGE_GUARDS.

Looking at Olly debugger, it has support for setting a so called memory breakpoint. Basicly what it does is
changing the acces on the memory page.

What I am not fully understanding is the following.

for example:

Let's say you have an value you want to debug like: 401234

and the asm code to read/acces it is:

mov eax, [edx+1234h]

When ever you change the memory protection on the the memory page using PAGE_GUARD. It changes it for the complete page:
from 401000 - 402000. Than when it breaks in the exception handler it breaks at the instruction address reading:
401000 and not reading 401234.

So it breaks on something like mov eax, [eax] -> while I need it to break on mov eax, [edx+1234] the instruction actually reading the memory address I want the handler to read.

Is there any way to do this? OllyDBG works for this, but I am trying to learn more about how debuggers work and how to code my own.


drizz

Use HW breakpoints (debug registers) or differentiate your bp by checking ExceptionRecord.ExceptionInformation.
ExceptionRecord.ExceptionInformation[1*4] holds the address read/written.

Quote
The first element of the array contains a read-write flag
that indicates the type of operation that caused the access
violation.

The second array element specifies the virtual address
of the inaccessible data.
The truth cannot be learned ... it can only be recognized.