mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
976 lines
25 KiB
976 lines
25 KiB
#include "master.hxx"
|
|
#pragma hdrstop
|
|
|
|
LPSTR DebuggerName = "Heaper";
|
|
LPSTR SymbolSearchPath;
|
|
LPSTR SrcDrive;
|
|
BOOLEAN fLazyLoad = FALSE;
|
|
|
|
#define TRUSTED_THRESHHOLD 0
|
|
|
|
|
|
BOOL
|
|
ChangePartialRemoteHeapGuardedness
|
|
(
|
|
IN PCHILD_PROCESS_INFO pProcessInfo,
|
|
IN BOOL fGuard
|
|
);
|
|
|
|
|
|
DWORD
|
|
GetContextReturnValue
|
|
(
|
|
IN HANDLE hThread
|
|
)
|
|
{
|
|
CONTEXT Context;
|
|
|
|
Context.ContextFlags = CONTEXT_INTEGER;
|
|
|
|
GetThreadContext( hThread, &Context );
|
|
|
|
return( Context.Eax );
|
|
}
|
|
|
|
int HeapCheck( LPSTR pszProgram, BOOLEAN fVerifyReadAccess )
|
|
{
|
|
PROCESS_INFORMATION ProcessInfo;
|
|
STARTUPINFO StartupInfo;
|
|
DWORD dwProcessId;
|
|
|
|
dwProcessId = ( DWORD )atol( pszProgram );
|
|
|
|
if ( dwProcessId == 0L && pszProgram[0]!='0' )
|
|
{
|
|
RtlZeroMemory( &StartupInfo, sizeof( StartupInfo ) );
|
|
|
|
StartupInfo.cb = sizeof( StartupInfo );
|
|
|
|
if ( !CreateProcess( NULL,
|
|
pszProgram,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
DEBUG_PROCESS | NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
&StartupInfo,
|
|
&ProcessInfo ) )
|
|
{
|
|
DebugPrintf( "Couldn't create process \"%s\": %lu.\n",
|
|
pszProgram,
|
|
GetLastError() );
|
|
return( 0 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !DebugActiveProcess( dwProcessId ) )
|
|
{
|
|
DebugPrintf( "Couldn't debug process %lu: %lu.\n",
|
|
dwProcessId,
|
|
GetLastError() );
|
|
return( 0 );
|
|
}
|
|
}
|
|
|
|
SetSymbolSearchPath( FALSE );
|
|
|
|
DebugLoop( fVerifyReadAccess );
|
|
|
|
return(0);
|
|
}
|
|
|
|
BOOL
|
|
UnguardPartialRemoteHeap
|
|
(
|
|
IN PCHILD_PROCESS_INFO pProcessInfo,
|
|
IN PBYTE pbAccessAddress,
|
|
IN ULONG cbAccessLength
|
|
)
|
|
{
|
|
switch ( pProcessInfo->HeapState )
|
|
{
|
|
case HEAP_UNGUARDED:
|
|
return( TRUE );
|
|
case HEAP_PARTIAL_UNGUARDED:
|
|
CHKPT();
|
|
return( UnGuardRemoteHeap( pProcessInfo ));
|
|
}
|
|
|
|
pProcessInfo->pbStartUnguardAddress = pbAccessAddress;
|
|
pProcessInfo->cbUnguardLength = cbAccessLength;
|
|
|
|
return( ChangePartialRemoteHeapGuardedness( pProcessInfo, FALSE ));
|
|
}
|
|
|
|
BOOL
|
|
ReguardPartialRemoteHeap
|
|
(
|
|
IN PCHILD_PROCESS_INFO pProcessInfo
|
|
)
|
|
{
|
|
BOOL bRetval;
|
|
|
|
ASSERT( pProcessInfo->HeapState == HEAP_PARTIAL_UNGUARDED );
|
|
|
|
bRetval = ChangePartialRemoteHeapGuardedness( pProcessInfo, TRUE );
|
|
|
|
pProcessInfo->pbStartUnguardAddress = NULL;
|
|
pProcessInfo->cbUnguardLength = 0;
|
|
|
|
return( bRetval );
|
|
}
|
|
|
|
|
|
BOOL
|
|
ChangePartialRemoteHeapGuardedness
|
|
(
|
|
IN PCHILD_PROCESS_INFO pProcessInfo,
|
|
IN BOOL fGuard
|
|
)
|
|
{
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
ULONG Base;
|
|
ULONG End;
|
|
PVOID pvBasePage;
|
|
PVOID pvEndPage;
|
|
PVOID pvPage;
|
|
DWORD dwOldProtection;
|
|
|
|
if ( Verbosity>1 )
|
|
{
|
|
DebugPrintf( "Range at 0x%08X, %d bytes becoming: %s...",
|
|
pProcessInfo->pbStartUnguardAddress,
|
|
pProcessInfo->cbUnguardLength,
|
|
fGuard ? "GUARDED" : "UNguarded" );
|
|
}
|
|
|
|
Base = (ULONG)pProcessInfo->pbStartUnguardAddress;
|
|
End = Base + pProcessInfo->cbUnguardLength;
|
|
|
|
pvBasePage = (PVOID)(Base - ( Base % SystemInfo.dwPageSize ));
|
|
pvEndPage = (PVOID)((End - ( End % SystemInfo.dwPageSize )) + SystemInfo.dwPageSize );
|
|
|
|
if ( Verbosity>1 )
|
|
{
|
|
DebugPrintf( "Pages [ %08X, %08X )\n", pvBasePage, pvEndPage );
|
|
}
|
|
|
|
for ( pvPage = pvBasePage; pvPage !=pvEndPage; pvPage = (PVOID)((ULONG)pvPage + SystemInfo.dwPageSize) )
|
|
{
|
|
if ( VirtualQueryEx( pProcessInfo->hProcess,
|
|
pvPage,
|
|
&mbi,
|
|
sizeof( mbi ) ) != sizeof( mbi ) )
|
|
{
|
|
DebugPrintf( "Last Error: %d", GetLastError() );
|
|
ASSERT( !"VirtualQueryEx() failed." );
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( !VirtualProtectEx( pProcessInfo->hProcess,
|
|
pvPage,
|
|
SystemInfo.dwPageSize,
|
|
fGuard ? ( mbi.Protect | PAGE_GUARD ) : ( mbi.Protect & (~PAGE_GUARD) ),
|
|
&dwOldProtection ) )
|
|
{
|
|
DebugPrintf( "Last Error: %d", GetLastError() );
|
|
ASSERT( !"VirtualProtectEx() failed." );
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
pProcessInfo->HeapState = fGuard ? HEAP_GUARDED : HEAP_PARTIAL_UNGUARDED;
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOL
|
|
ChangeRemoteHeapGuardedness
|
|
(
|
|
IN PCHILD_PROCESS_INFO pProcessInfo,
|
|
IN BOOL fGuard
|
|
)
|
|
{
|
|
int index;
|
|
ULONG cPagesGuardedOrNotGuardable;
|
|
MEMORY_BASIC_INFORMATION mbi;
|
|
DWORD dwOldProtection;
|
|
PHEAP pHeap;
|
|
PHEAP_SEGMENT pHeapSegment;
|
|
PVOID pvBase;
|
|
HANDLE const hProcess=pProcessInfo->hProcess;
|
|
HEAP Heap;
|
|
|
|
|
|
if ( Verbosity>1 )
|
|
{
|
|
DebugPrintf( "Heap becoming: %s...", fGuard ? "GUARDED" : "UNguarded" );
|
|
}
|
|
|
|
pHeap = CopyRemoteProcessHeap( hProcess,
|
|
(PHEAP)pProcessInfo->Peb.ProcessHeap,
|
|
&Heap );
|
|
|
|
// CHKPT();
|
|
|
|
if ( pHeap == NULL )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
//CHKPT();
|
|
|
|
for ( index=0; index < HEAP_MAXIMUM_SEGMENTS; index++ )
|
|
{
|
|
// CHKPT();
|
|
|
|
pHeapSegment = pHeap->Segments[ index ];
|
|
|
|
if ( pHeapSegment == NULL )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// CHKPT();
|
|
|
|
if ( pHeapSegment->Signature != HEAP_SEGMENT_SIGNATURE )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// CHKPT();
|
|
|
|
for ( pvBase = pHeapSegment->BaseAddress, cPagesGuardedOrNotGuardable = 0;
|
|
cPagesGuardedOrNotGuardable < pHeapSegment->NumberOfPages;
|
|
pvBase = ( PVOID )((ULONG)pvBase + mbi.RegionSize ))
|
|
{
|
|
if ( VirtualQueryEx( hProcess,
|
|
pvBase,
|
|
&mbi,
|
|
sizeof( mbi ) ) == sizeof( mbi ) )
|
|
{
|
|
cPagesGuardedOrNotGuardable += mbi.RegionSize / SystemInfo.dwPageSize;
|
|
|
|
if ( cPagesGuardedOrNotGuardable > pHeapSegment->NumberOfPages )
|
|
{
|
|
mbi.RegionSize -= (cPagesGuardedOrNotGuardable - pHeapSegment->NumberOfPages)
|
|
* SystemInfo.dwPageSize;
|
|
cPagesGuardedOrNotGuardable = pHeapSegment->NumberOfPages;
|
|
}
|
|
|
|
if ( mbi.State != MEM_COMMIT )
|
|
{
|
|
// ISSUE: ASSERT on various image types
|
|
continue;
|
|
}
|
|
|
|
if ( !VirtualProtectEx( hProcess,
|
|
pvBase,
|
|
mbi.RegionSize,
|
|
fGuard ? ( mbi.Protect | PAGE_GUARD ) : ( mbi.Protect & (~PAGE_GUARD) ),
|
|
&dwOldProtection ) )
|
|
{
|
|
ASSERT( !"VirtualProtectEx() failed!" );
|
|
return( FALSE );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
ASSERT( !"VirtualQueryEx() failed!" );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if ( Verbosity>1 )
|
|
{
|
|
DebugPrintf( "Done\n" );
|
|
}
|
|
|
|
|
|
|
|
pProcessInfo->HeapState = fGuard ? HEAP_GUARDED : HEAP_UNGUARDED;
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
BOOL UnGuardRemoteHeap
|
|
(
|
|
IN PCHILD_PROCESS_INFO pProcessInfo
|
|
)
|
|
{
|
|
// ASSERT( pProcessInfo->HeapState == HEAP_GUARDED || pProcessInfo->HeapState == HEAP_PARTIAL_UNGUARDED );
|
|
|
|
if ( pProcessInfo->HeapState == HEAP_UNGUARDED )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
|
|
if ( pProcessInfo->HeapState == HEAP_PARTIAL_UNGUARDED )
|
|
{
|
|
if ( Verbosity>2 )
|
|
{
|
|
DebugPrintf( "Reguarding open portion in order to unguard entire heap.\n" );
|
|
}
|
|
ReguardPartialRemoteHeap( pProcessInfo );
|
|
}
|
|
|
|
ASSERT( pProcessInfo->HeapState == HEAP_GUARDED );
|
|
|
|
return( ChangeRemoteHeapGuardedness( pProcessInfo, FALSE ) );
|
|
}
|
|
|
|
BOOL
|
|
GuardRemoteHeap
|
|
(
|
|
IN PCHILD_PROCESS_INFO pProcessInfo
|
|
)
|
|
{
|
|
ASSERT( pProcessInfo->HeapState == HEAP_UNGUARDED || pProcessInfo->HeapState == HEAP_PARTIAL_UNGUARDED );
|
|
|
|
if ( pProcessInfo->HeapState == HEAP_PARTIAL_UNGUARDED )
|
|
{
|
|
return( ReguardPartialRemoteHeap( pProcessInfo ));
|
|
}
|
|
|
|
return( ChangeRemoteHeapGuardedness( pProcessInfo, TRUE ) );
|
|
}
|
|
|
|
VOID
|
|
CalculateHeapEntryValues
|
|
(
|
|
IN const PBYTE premoteHeapEntry,
|
|
IN const PHEAP_ENTRY pEntry,
|
|
IN OUT PBYTE *ppremoteNextHeapEntry,
|
|
IN OUT PBYTE *ppremoteFirstDataAddress,
|
|
IN OUT PBYTE *ppremoteLastDataAddress
|
|
)
|
|
{
|
|
*ppremoteNextHeapEntry = ( premoteHeapEntry + ( pEntry->Size << HEAP_GRANULARITY_SHIFT ) );
|
|
*ppremoteFirstDataAddress = ( premoteHeapEntry + sizeof( HEAP_ENTRY ) );
|
|
|
|
if ( pEntry->Flags & HEAP_ENTRY_BUSY )
|
|
{
|
|
*ppremoteLastDataAddress = *ppremoteNextHeapEntry
|
|
- 1
|
|
- ( pEntry->UnusedBytes - sizeof( HEAP_ENTRY ) );
|
|
}
|
|
else
|
|
{
|
|
*ppremoteLastDataAddress = (*ppremoteFirstDataAddress) - 1;
|
|
}
|
|
|
|
if ( Debug>0 )
|
|
{
|
|
ASSERT( premoteHeapEntry < *ppremoteNextHeapEntry );
|
|
ASSERT( *ppremoteFirstDataAddress < *ppremoteNextHeapEntry );
|
|
ASSERT( *ppremoteLastDataAddress < *ppremoteNextHeapEntry );
|
|
ASSERT( *ppremoteFirstDataAddress > premoteHeapEntry );
|
|
ASSERT( *ppremoteLastDataAddress > premoteHeapEntry );
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
IsAccessInHeapValidAreas
|
|
(
|
|
IN PCHILD_PROCESS_INFO pProcessInfo,
|
|
IN PCHILD_THREAD_INFO pThreadInfo,
|
|
IN const PBYTE pAddress,
|
|
IN ULONG cbAccessLength
|
|
)
|
|
{
|
|
ULONG index;
|
|
|
|
if ( pProcessInfo->cHeapValidAreas == 0 ||
|
|
pProcessInfo->pdwHeapValidAreas == NULL )
|
|
{
|
|
if ( Debug>0 )
|
|
{
|
|
ASSERT( pProcessInfo->cHeapValidAreas == 0 && pProcessInfo->pdwHeapValidAreas == NULL );
|
|
ASSERT( pThreadInfo->pdwRecentValidArea == NULL );
|
|
}
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( pThreadInfo->pdwRecentValidArea != NULL )
|
|
{
|
|
if ( pAddress >= (PBYTE)(pThreadInfo->pdwRecentValidArea[0]) )
|
|
{
|
|
if ( ( pAddress + ( cbAccessLength - 1 ) )
|
|
<=
|
|
(PBYTE)(pThreadInfo->pdwRecentValidArea[1]) )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( index = 0; index < pProcessInfo->cHeapValidAreas; index ++ )
|
|
{
|
|
if ( ( ( index + 1 ) < pProcessInfo->cHeapValidAreas ) &&
|
|
pAddress > (PBYTE)(pProcessInfo->pdwHeapValidAreas[ ( index + 1 ) * 2 ] ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( pAddress < (PBYTE)(pProcessInfo -> pdwHeapValidAreas[ index * 2 ]) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( ( pAddress + ( cbAccessLength - 1 ) )
|
|
<=
|
|
(PBYTE)(pProcessInfo -> pdwHeapValidAreas[ index * 2 + 1 ]) )
|
|
{
|
|
pThreadInfo->pdwRecentValidArea = &pProcessInfo->pdwHeapValidAreas[ index * 2 ];
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
VOID
|
|
DiscardHeapValidAreasData
|
|
(
|
|
IN PCHILD_PROCESS_INFO pProcessInfo
|
|
)
|
|
{
|
|
PLIST_ENTRY pEntry;
|
|
PCHILD_THREAD_INFO pThreadInfo;
|
|
|
|
pProcessInfo->cHeapValidAreas = 0;
|
|
|
|
if ( pProcessInfo->pdwHeapValidAreas != NULL )
|
|
{
|
|
free( pProcessInfo->pdwHeapValidAreas );
|
|
pProcessInfo->pdwHeapValidAreas = NULL;
|
|
}
|
|
|
|
pEntry = pProcessInfo->listChildThreads.Flink;
|
|
|
|
while ( pEntry != &pProcessInfo->listChildThreads )
|
|
{
|
|
pThreadInfo = CONTAINING_RECORD( pEntry, CHILD_THREAD_INFO, Linkage );
|
|
|
|
pThreadInfo->pdwRecentValidArea = NULL;
|
|
|
|
pEntry = pEntry -> Flink;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
DetermineHeapValidAreas
|
|
(
|
|
IN PCHILD_PROCESS_INFO pProcessInfo
|
|
)
|
|
{
|
|
PHEAP pHeap;
|
|
int index;
|
|
PHEAP_SEGMENT pHeapSegment;
|
|
PHEAP_ENTRY pHeapEntry;
|
|
PHEAP_ENTRY pNextHeapEntry;
|
|
HEAP_SEGMENT HeapSegment;
|
|
HEAP_ENTRY HeapEntry;
|
|
PBYTE pFirstDataAddress;
|
|
PBYTE pLastDataAddress;
|
|
HEAP Heap;
|
|
HANDLE const hProcess = pProcessInfo->hProcess;
|
|
PVOID pv;
|
|
|
|
DiscardHeapValidAreasData( pProcessInfo );
|
|
|
|
pHeap = CopyRemoteProcessHeap( hProcess,
|
|
(PHEAP)pProcessInfo->Peb.ProcessHeap,
|
|
&Heap );
|
|
|
|
if ( pHeap != &Heap )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( Verbosity>0 )
|
|
{
|
|
DebugPrintf( "Walking heap to build fastcheck information...\n");
|
|
}
|
|
|
|
for ( index = 0; index < HEAP_MAXIMUM_SEGMENTS; index++ )
|
|
{
|
|
pHeapSegment = pHeap->Segments[ index ];
|
|
|
|
if ( pHeapSegment == NULL )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( Verbosity>2 )
|
|
{
|
|
DebugPrintf( "Reading heap segment %08X!%08X (%d)\n", hProcess, pHeapSegment, index );
|
|
}
|
|
|
|
if ( !ReadProcessMemory( hProcess,
|
|
pHeapSegment,
|
|
&HeapSegment,
|
|
sizeof( HEAP_SEGMENT ),
|
|
NULL ) )
|
|
{
|
|
DebugPrintf( "DetermineHeapValidAreas: cannot read heap segment, error %lu\n", GetLastError() );
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( HeapSegment.Signature != HEAP_SEGMENT_SIGNATURE )
|
|
{
|
|
DebugPrintf( "Bad segment signature!\n");
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
for ( pHeapEntry = HeapSegment.FirstEntry;
|
|
;
|
|
pHeapEntry = pNextHeapEntry )
|
|
{
|
|
if ( pHeapEntry == NULL )
|
|
{
|
|
DebugPrintf("HeapEntry is NULL pointer.\n");
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( !ReadProcessMemory( hProcess,
|
|
pHeapEntry,
|
|
&HeapEntry,
|
|
sizeof( HEAP_ENTRY ),
|
|
NULL ) )
|
|
{
|
|
DebugPrintf( "DetermineHeapValidAreas: cannot read heap entry at %08X, error %lu\n", pHeapEntry, GetLastError() );
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( HeapEntry.SegmentIndex != index )
|
|
{
|
|
DebugPrintf( "DetermineHeapValidAreas: Heap entry segment index %d\n"
|
|
"does not match current index %d into segment table.\n",
|
|
HeapEntry.SegmentIndex,
|
|
index );
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
if ( Verbosity > 2 )
|
|
{
|
|
DebugPrintf( "Entry @ %08X, [%X] [%X] %02x\n",
|
|
pHeapEntry,
|
|
( HeapEntry.PreviousSize ),
|
|
( HeapEntry.Size << HEAP_GRANULARITY_SHIFT ),
|
|
HeapEntry.Flags << HEAP_GRANULARITY_SHIFT );
|
|
}
|
|
|
|
if ( HeapEntry.Flags & HEAP_ENTRY_EXTRA_PRESENT )
|
|
{
|
|
if ( Verbosity>2 || Debug>0 )
|
|
{
|
|
DebugPrintf("[Extra]\n");
|
|
}
|
|
}
|
|
|
|
CalculateHeapEntryValues( (PBYTE)pHeapEntry,
|
|
&HeapEntry,
|
|
(PBYTE *)&pNextHeapEntry,
|
|
&pFirstDataAddress,
|
|
&pLastDataAddress );
|
|
|
|
if ( Verbosity>2 )
|
|
{
|
|
DebugPrintf( "Next entry @ %08X\n", pNextHeapEntry );
|
|
}
|
|
|
|
if ( HeapEntry.Flags & ( HEAP_ENTRY_LAST_ENTRY ) )
|
|
{
|
|
if ( Verbosity>2 || Debug>1 )
|
|
{
|
|
DebugPrintf("LAST ENTRY\n");
|
|
}
|
|
}
|
|
|
|
if ( pLastDataAddress >= (PBYTE)pNextHeapEntry )
|
|
{
|
|
if ( Debug>0 )
|
|
{
|
|
DebugBreak();
|
|
}
|
|
pLastDataAddress = ((PBYTE)pNextHeapEntry)-1;
|
|
}
|
|
|
|
if ( pLastDataAddress < pFirstDataAddress )
|
|
{
|
|
if ( Debug>1 )
|
|
{
|
|
DebugPrintf( "Entry has negative length.\n" );
|
|
}
|
|
|
|
if ( pHeapEntry >= HeapSegment.LastValidEntry ||
|
|
HeapEntry.Flags & HEAP_ENTRY_LAST_ENTRY )
|
|
{
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if ( HeapEntry.Flags & HEAP_ENTRY_BUSY )
|
|
{
|
|
pProcessInfo->cHeapValidAreas++;
|
|
|
|
if ( pProcessInfo->pdwHeapValidAreas != NULL )
|
|
{
|
|
pv = realloc( pProcessInfo->pdwHeapValidAreas,
|
|
pProcessInfo->cHeapValidAreas * 2 * sizeof( DWORD ) );
|
|
}
|
|
else
|
|
{
|
|
if ( Debug>0 )
|
|
{
|
|
ASSERT( pProcessInfo->cHeapValidAreas==1 );
|
|
}
|
|
pv = malloc( 2 * sizeof( DWORD ) );
|
|
}
|
|
|
|
if ( pv == NULL )
|
|
{
|
|
DebugPrintf( "Allocation failed.\n" );
|
|
if ( Debug>0 )
|
|
{
|
|
DebugBreak();
|
|
}
|
|
DiscardHeapValidAreasData( pProcessInfo );
|
|
return( FALSE );
|
|
}
|
|
|
|
pProcessInfo->pdwHeapValidAreas = (PDWORD)pv;
|
|
|
|
pProcessInfo->pdwHeapValidAreas
|
|
[ ( pProcessInfo->cHeapValidAreas-1 ) * 2 ] = (DWORD)pFirstDataAddress;
|
|
|
|
pProcessInfo->pdwHeapValidAreas
|
|
[ ( pProcessInfo->cHeapValidAreas-1 ) * 2 + 1 ] = (DWORD)pLastDataAddress;
|
|
}
|
|
|
|
if ( pHeapEntry >= HeapSegment.LastValidEntry ||
|
|
HeapEntry.Flags & HEAP_ENTRY_LAST_ENTRY )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
if ( Verbosity>0 )
|
|
{
|
|
DebugPrintf( "Done.\n" );
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
BOOL
|
|
VerifyRemoteHeapAccess
|
|
(
|
|
IN PCHILD_PROCESS_INFO pProcessInfo,
|
|
IN const PBYTE pAddress,
|
|
IN ULONG cbAccessLength
|
|
)
|
|
{
|
|
PHEAP pHeap;
|
|
int index;
|
|
PHEAP_SEGMENT pHeapSegment;
|
|
PHEAP_ENTRY pHeapEntry;
|
|
PHEAP_ENTRY pNextHeapEntry;
|
|
HEAP_SEGMENT HeapSegment;
|
|
HEAP_ENTRY HeapEntry;
|
|
PBYTE pFirstDataAddress;
|
|
PBYTE pLastDataAddress;
|
|
HEAP Heap;
|
|
HANDLE const hProcess = pProcessInfo->hProcess;
|
|
|
|
PBYTE pLastAddress;
|
|
|
|
pLastAddress = pAddress + cbAccessLength - 1;
|
|
|
|
pHeap = CopyRemoteProcessHeap( hProcess,
|
|
(PHEAP)pProcessInfo->Peb.ProcessHeap,
|
|
&Heap );
|
|
|
|
if ( pHeap != &Heap )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( Verbosity>0 )
|
|
{
|
|
DebugPrintf( "Doing detailed verify...");
|
|
}
|
|
|
|
for ( index=0; index < HEAP_MAXIMUM_SEGMENTS; index++ )
|
|
{
|
|
pHeapSegment = pHeap->Segments[ index ];
|
|
|
|
if ( pHeapSegment == NULL )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( Verbosity>2 )
|
|
{
|
|
DebugPrintf( "Reading heap segment %08X!%08X (%d)\n", hProcess, pHeapSegment, index );
|
|
}
|
|
|
|
if ( !ReadProcessMemory( hProcess,
|
|
pHeapSegment,
|
|
&HeapSegment,
|
|
sizeof( HEAP_SEGMENT ),
|
|
NULL ) )
|
|
{
|
|
DebugPrintf( "VerifyRemoteAccess: cannot read heap segment, error %lu\n", GetLastError() );
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( HeapSegment.Signature != HEAP_SEGMENT_SIGNATURE )
|
|
{
|
|
DebugPrintf( "Bad segment signature!\n");
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( Verbosity>2 )
|
|
{
|
|
DebugPrintf( "%08X in %08X + %08X ?\n",
|
|
pAddress,
|
|
pHeapSegment->BaseAddress,
|
|
pHeapSegment->NumberOfPages );
|
|
}
|
|
|
|
if ( ( pLastAddress < (PBYTE)HeapSegment.BaseAddress ) ||
|
|
( pAddress > ((PBYTE)HeapSegment.BaseAddress + ( HeapSegment.NumberOfPages * SystemInfo.dwPageSize ) ) ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( Verbosity>2 )
|
|
{
|
|
DebugPrintf( "Access is within heap segment %d.\n", index );
|
|
}
|
|
|
|
pHeapEntry = HeapSegment.FirstEntry;
|
|
do
|
|
{
|
|
if ( pHeapEntry == NULL )
|
|
{
|
|
DebugPrintf("HeapEntry is NULL pointer.\n");
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( !ReadProcessMemory( hProcess,
|
|
pHeapEntry,
|
|
&HeapEntry,
|
|
sizeof( HEAP_ENTRY ),
|
|
NULL ) )
|
|
{
|
|
DebugPrintf( "VerifyRemoteAccess: cannot read heap entry, error %lu\n", GetLastError() );
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( HeapEntry.SegmentIndex != index )
|
|
{
|
|
DebugPrintf( "VerifyRemoteAccess: Heap entry segment index %d\n"
|
|
"does not match current index %d into segment table.\n",
|
|
HeapEntry.SegmentIndex,
|
|
index );
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
if ( Verbosity > 2 )
|
|
{
|
|
DebugPrintf( "Entry @ %08X, [%X] [%X] %02x\n",
|
|
pHeapEntry,
|
|
( HeapEntry.PreviousSize ),
|
|
( HeapEntry.Size << HEAP_GRANULARITY_SHIFT ),
|
|
HeapEntry.Flags );
|
|
}
|
|
|
|
if ( HeapEntry.Flags & HEAP_ENTRY_EXTRA_PRESENT )
|
|
{
|
|
if ( Verbosity>2 || Debug>0 )
|
|
{
|
|
DebugPrintf("[Extra]\n");
|
|
}
|
|
}
|
|
|
|
|
|
CalculateHeapEntryValues( (PBYTE)pHeapEntry,
|
|
&HeapEntry,
|
|
(PBYTE *)&pNextHeapEntry,
|
|
&pFirstDataAddress,
|
|
&pLastDataAddress );
|
|
|
|
if ( Debug>0 )
|
|
{
|
|
ASSERT( pLastDataAddress < (PBYTE)pNextHeapEntry );
|
|
// ASSERT( pFirstDataAddress < pLastDataAddress );
|
|
ASSERT( pNextHeapEntry > pHeapEntry );
|
|
}
|
|
|
|
if ( Verbosity>2 )
|
|
{
|
|
DebugPrintf( "Next entry @ %08X\n", pNextHeapEntry );
|
|
}
|
|
|
|
if ( HeapEntry.Flags & ( HEAP_ENTRY_LAST_ENTRY ) )
|
|
{
|
|
if ( Verbosity>2 || Debug>1 )
|
|
{
|
|
DebugPrintf("LAST ENTRY\n");
|
|
}
|
|
}
|
|
|
|
if ( pAddress >= (PBYTE)pHeapEntry && pAddress < (PBYTE)pNextHeapEntry)
|
|
{
|
|
if ( pAddress < pFirstDataAddress)
|
|
{
|
|
DebugPrintf( "Access to head of heap block, access is in reserved area.\n");
|
|
goto Fail;
|
|
}
|
|
|
|
if ( pAddress > pLastDataAddress )
|
|
{
|
|
DebugPrintf( "Access to tail of heap block, access is in unallocated area.\n");
|
|
goto Fail;
|
|
}
|
|
|
|
if ( pLastAddress > pLastDataAddress )
|
|
{
|
|
DebugPrintf( "Access spans end of heap data region.\n");
|
|
goto Fail;
|
|
}
|
|
|
|
if ( ! ( HeapEntry.Flags & HEAP_ENTRY_BUSY ) )
|
|
{
|
|
DebugPrintf( "Access to data region of free block.\n" );
|
|
goto Fail;
|
|
}
|
|
|
|
if ( Verbosity>0 )
|
|
{
|
|
DebugPrintf( "OK: %08X within block %08X.\n", pAddress, pHeapEntry );
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
pHeapEntry = pNextHeapEntry;
|
|
}
|
|
while ( pHeapEntry!=NULL &&
|
|
pHeapEntry < HeapSegment.LastValidEntry &&
|
|
! ( HeapEntry.Flags & HEAP_ENTRY_LAST_ENTRY ));
|
|
|
|
DebugPrintf( "Access to %08X is within a heap segment, but not an entry.\n", pAddress );
|
|
goto Fail;
|
|
}
|
|
|
|
DebugPrintf( "Access to %08X appears to be unrelated to heap.\n", pAddress );
|
|
|
|
if ( Debug>0 )
|
|
{
|
|
DebugBreak();
|
|
}
|
|
|
|
Fail:
|
|
|
|
if ( HeapEntry.Flags & HEAP_ENTRY_BUSY )
|
|
{
|
|
DebugPrintf( "Heap block: [ %08X -HEAD- [ %08X -DATA- %08X ] -TAIL- %08X ]",
|
|
pHeapEntry,
|
|
pFirstDataAddress,
|
|
pLastDataAddress,
|
|
((PBYTE)pNextHeapEntry)-1 );
|
|
if ( pFirstDataAddress>=pLastDataAddress )
|
|
{
|
|
DebugPrintf(" (Empty range)\n");
|
|
}
|
|
else
|
|
{
|
|
DebugPrintf( "\n" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DebugPrintf( "Heap block: [ %08X -HEAD- [ %08X -FREE- %08X ] -TAIL- %08X ]\n",
|
|
pHeapEntry,
|
|
pFirstDataAddress,
|
|
pLastDataAddress,
|
|
((PBYTE)pNextHeapEntry)-1 );
|
|
}
|
|
|
|
DebugPrintf( "Access: [ %08X ... %08X ]\n", pAddress, pLastAddress );
|
|
|
|
if ( HeapEntry.Flags & HEAP_ENTRY_EXTRA_PRESENT )
|
|
{
|
|
DebugPrintf("Caveat: entry contains extra data, not sure how to deal with that.\n");
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
PCHILD_PROCESS_INFO
|
|
GetProcessRecord
|
|
(
|
|
IN PLIST_ENTRY pList,
|
|
IN DWORD dwProcessId
|
|
)
|
|
{
|
|
PCHILD_PROCESS_INFO pChildInfo;
|
|
PLIST_ENTRY pEntry;
|
|
|
|
pEntry = pList->Flink;
|
|
|
|
while ( pEntry != pList )
|
|
{
|
|
pChildInfo = CONTAINING_RECORD( pEntry, CHILD_PROCESS_INFO, Linkage );
|
|
|
|
if ( pChildInfo->dwProcessId == dwProcessId )
|
|
{
|
|
return( pChildInfo );
|
|
}
|
|
|
|
pEntry = pEntry -> Flink;
|
|
}
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
PCHILD_THREAD_INFO
|
|
GetThreadRecord
|
|
(
|
|
IN PLIST_ENTRY pList,
|
|
IN DWORD dwThreadId
|
|
)
|
|
{
|
|
PCHILD_THREAD_INFO pChildInfo;
|
|
PLIST_ENTRY pEntry;
|
|
|
|
pEntry = pList->Flink;
|
|
|
|
while ( pEntry != pList )
|
|
{
|
|
pChildInfo = CONTAINING_RECORD( pEntry, CHILD_THREAD_INFO, Linkage );
|
|
|
|
if ( pChildInfo->dwThreadId == dwThreadId )
|
|
{
|
|
return( pChildInfo );
|
|
}
|
|
|
|
pEntry = pEntry -> Flink;
|
|
}
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
|