First of all, the setup:
- A debuggee (simple console app)
- DLL that is injected into debuggee
- Debugger
- Driver
Both the debugger and the DLL have an open handle to the driver.
What I've been playing around with:
- DLL is injected and notifies the driver.
- Driver takes a page in the driver, gets its PTE and sets the Present bit to 0.
- DLL executes code on this non-present page
- Debugger catches EXCEPTION_ACCESS_VIOLATION and notifies driver
- Driver sets Present bit back to 1.
- Execution is resumed.
* note: yes it looks useless but it's just for personal education.
The problem is:
When the debugger notifies the driver of the exception, and we get the PTE in the driver of the exception address, the PTE is completely different as before(The PTE address is the same: c0080008 but the structure is filled with different data).
The obvious mistake is that when the debugger sends the exception notification we are in a different process's context, but I KeStackAttachProcess to the debuggee before getting the PTE.
The following code works fine:
- Code: Select all
case TR_TEST1:
{
PPTE pPTE;
UINT_PTR testAddr = 0x100010ac;
pPTE = getPTE( (PVOID)testAddr, TRUE );
}break;
case TR_TEST:
{
KAPC_STATE apcState;
UINT_PTR testAddr = 0x100010ac;
try
{
PPTE pPTE;
trAttachProcess( &apcState, TRUE );
pPTE = getPTE( (PVOID)testAddr, TRUE );
} __finally
{
trAttachProcess( &apcState, FALSE );
}
}break;
In this snippet, I 'call' TR_TEST from the DLL and TR_TEST1 from the debugger, both PPTE structs are identical.
On exceptions, the debugger 'calls' the following:
- Code: Select all
case TR_EXCEPTIONHANDLER:
{
TRERROR trError;
PTRPAGE_ENTRY trPage;
KAPC_STATE apcState;
struct input
{
EXCEPTION_DEBUG_INFO exceptInfo;
} *pinp;
struct output
{
BOOLEAN bExceptionHandled;
} *outp;
pinp = inBuffer;
outp = outBuffer;
try
{
PPTE pPTE;
trAttachProcess( &apcState, TRUE );
pPTE = getPTE( pinp->exceptInfo.ExceptionRecord.ExceptionAddress, TRUE );
togPagePresentBit( pPTE, TRUE );
} __finally
{
trAttachProcess( &apcState, FALSE );
}
Here the PPTE is totally different.
*note: all the functions get the lower page boundary of the address before continuing.
*note2: The exception address is definitely in the page that we marked as non-present before (the check is omitted in the pasted code).
Is attaching to a process somehow behaving differently when we are dealing with an exception? As all threads in the debuggee are paused or am I missing something else?