|
|
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
verifier.c
Abstract:
This module implements the core support for application verifier.
Author:
Silviu Calinoiu (SilviuC) 2-Feb-2001
Revision History:
--*/
#include "ntos.h"
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <heap.h>
#include "ldrp.h"
#define AVRF_FLG_EXPORT_DLL_LOADED 0x0001
ULONG AVrfpDebug = 0x0000;
#define AVRF_DBG_SHOW_SNAPS 0x0001
#define AVRF_DBG_SHOW_VERIFIED_EXPORTS 0x0002
#define AVRF_DBG_SHOW_DLLS_WITH_EXPORTS 0x0004
#define AVRF_DBG_SHOW_PROVIDER_LOADS 0x0008
#define AVRF_DBG_SHOW_CHAIN_ACTIVITY 0x0010
#define AVRF_DBG_SHOW_CHAIN_DETAILS 0x0020
BOOLEAN AVrfpEnabled;
//
// Default system-wide settings
//
#define RTL_VRF_FLG_SYSTEM_WIDE_SETTINGS \
( RTL_VRF_FLG_LOCK_CHECKS \ | RTL_VRF_FLG_HANDLE_CHECKS \ )
//
// Local vars
//
ULONG AVrfpVerifierFlags; WCHAR AVrfpVerifierDllsString [512]; LIST_ENTRY AVrfpVerifierProvidersList;
RTL_CRITICAL_SECTION AVrfpVerifierLock;
#define VERIFIER_LOCK() RtlEnterCriticalSection(&AVrfpVerifierLock)
#define VERIFIER_UNLOCK() RtlLeaveCriticalSection(&AVrfpVerifierLock)
//
// Local types
//
typedef struct _AVRF_VERIFIER_DESCRIPTOR {
LIST_ENTRY List; UNICODE_STRING VerifierName; PVOID VerifierHandle; PVOID VerifierEntryPoint; PRTL_VERIFIER_DLL_DESCRIPTOR VerifierDlls; RTL_VERIFIER_DLL_LOAD_CALLBACK VerifierLoadHandler; RTL_VERIFIER_DLL_UNLOAD_CALLBACK VerifierUnloadHandler;
} AVRF_VERIFIER_DESCRIPTOR, *PAVRF_VERIFIER_DESCRIPTOR;
//
// Local functions
//
BOOLEAN AVrfpSnapDllImports ( PLDR_DATA_TABLE_ENTRY LdrDataTableEntry );
BOOLEAN AVrfpDetectVerifiedExports ( PRTL_VERIFIER_DLL_DESCRIPTOR Dll, PRTL_VERIFIER_THUNK_DESCRIPTOR Thunks );
BOOLEAN AVrfpParseVerifierDllsString ( PWSTR Dlls );
VOID AVrfpSnapAlreadyLoadedDlls ( );
VOID AVrfpMoveProviderToEndOfInitializationList ( PWSTR ProviderName );
BOOLEAN AVrfpLoadAndInitializeProvider ( PAVRF_VERIFIER_DESCRIPTOR Provider );
BOOLEAN AVrfpIsVerifierProviderDll ( PVOID Handle );
VOID AVrfpDumpProviderList ( );
PVOID AVrfpFindClosestThunkDuplicate ( PAVRF_VERIFIER_DESCRIPTOR Verifier, PWCHAR DllName, PCHAR ThunkName );
VOID AVrfpChainDuplicateVerificationLayers ( );
VOID AVrfpDllLoadNotificationInternal ( PLDR_DATA_TABLE_ENTRY LoadedDllData );
PWSTR AVrfpGetProcessName ( );
BOOLEAN AVrfpEnableVerifierOptions ( );
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
VOID AVrfInitializeVerifier ( BOOLEAN EnabledSystemWide, PUNICODE_STRING ImageName, ULONG Phase ) /*++
Routine description:
This routine initializes the verifier package. Reads options from registry, loads verifier dlls, etc.
Parameters:
EnabledSystemWide - true if all processes are supposed to run with application verifier enabled. If this is the case we will scale down our memory-demanding checks so that we can boot.
ImageName - unicode name of the current process Phase - initialization happens in several stages. 0 - we read registry settings under image file execution options. in this phase the other two parameters have a meaning. 1 - we parse the verifier dlls and load them. Return value:
None. --*/ { BOOLEAN Result; PLIST_ENTRY Entry; PAVRF_VERIFIER_DESCRIPTOR Provider; NTSTATUS Status; BOOLEAN LoadSuccess;
switch (Phase) { case 0: // Phase 0
AVrfpVerifierFlags = RTL_VRF_FLG_SYSTEM_WIDE_SETTINGS; AVrfpVerifierDllsString[0] = L'\0';
//
// Attempt to read verifier registry settings even if verifier
// is enabled system wide. In the worst case no values are there
// and nothing will be read. If we have some options per process
// this will override system wide settings.
//
LdrQueryImageFileExecutionOptions (ImageName, L"VerifierFlags", REG_DWORD, &AVrfpVerifierFlags, sizeof(AVrfpVerifierFlags), NULL);
LdrQueryImageFileExecutionOptions (ImageName, L"VerifierDebug", REG_DWORD, &AVrfpDebug, sizeof(AVrfpDebug), NULL);
LdrQueryImageFileExecutionOptions (ImageName, L"VerifierDlls", REG_SZ, AVrfpVerifierDllsString, 512, NULL);
AVrfpEnableVerifierOptions ();
break;
case 1: // Phase 1
InitializeListHead (&AVrfpVerifierProvidersList); RtlInitializeCriticalSection (&AVrfpVerifierLock);
DbgPrint ("AVRF: %ws: pid 0x%X: flags 0x%X: application verifier enabled\n", AVrfpGetProcessName(), HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess), AVrfpVerifierFlags);
Result = AVrfpParseVerifierDllsString (AVrfpVerifierDllsString);
if (Result == FALSE) { DbgPrint ("AVRF: %ws: pid 0x%X: application verifier will be disabled due to an initialization error.\n", AVrfpGetProcessName(), HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess));
NtCurrentPeb()->NtGlobalFlag &= ~FLG_APPLICATION_VERIFIER; }
Entry = AVrfpVerifierProvidersList.Flink;
while (Entry != &AVrfpVerifierProvidersList) {
Provider = CONTAINING_RECORD (Entry, AVRF_VERIFIER_DESCRIPTOR, List);
//
// Load provider, probe it to make sure it is really a
// provider, call initialize routine with PROCESS_VERIFIER, etc.
//
LoadSuccess = AVrfpLoadAndInitializeProvider (Provider);
//
// Move to next provider
//
Entry = Provider->List.Flink;
//
// Get this provider out of the providers list if we
// encountered an error while loading
//
if (! LoadSuccess) {
RemoveEntryList (&(Provider->List));
RtlFreeHeap (RtlProcessHeap(), 0, Provider); } }
//
// Chain duplicate verification functions.
//
AVrfpChainDuplicateVerificationLayers ();
//
// Enable verifier. Resnap already loaded dlls.
// Now we will start processing dll load
// notifications coming from loader.
//
AVrfpEnabled = TRUE;
AVrfpSnapAlreadyLoadedDlls ();
if ((AVrfpDebug & AVRF_DBG_SHOW_PROVIDER_LOADS)) {
DbgPrint ("AVRF: -*- final list of providers -*- \n"); AVrfpDumpProviderList (); }
break;
default:
break; } }
BOOLEAN AVrfpParseVerifierDllsString ( PWSTR Dlls ) { PWSTR Current; PWSTR Start; WCHAR Save; PAVRF_VERIFIER_DESCRIPTOR Entry;
//
// Create by default an entry for the standard provider "verifier.dll"
//
Entry = RtlAllocateHeap (RtlProcessHeap (), 0, sizeof *Entry);
if (Entry == NULL) { return FALSE; }
RtlZeroMemory (Entry, sizeof *Entry);
RtlInitUnicodeString (&(Entry->VerifierName), L"verifier.dll");
InsertTailList (&AVrfpVerifierProvidersList, &(Entry->List));
//
// Parse the string
//
Current = Dlls;
while (*Current != L'\0') { while (*Current == L' ' || *Current == L'\t') { Current += 1; }
Start = Current;
while (*Current && *Current != L' ' && *Current != L'\t') { Current += 1; }
if (Start == Current) { break; }
Save = *Current; *Current = L'\0';
//
// Check if standard provider was specified explicitely.
// In this case we ignore it because we already have it
// in the list.
//
if (_wcsicmp (Start, L"verifier.dll") != 0) { Entry = RtlAllocateHeap (RtlProcessHeap (), 0, sizeof *Entry);
if (Entry == NULL) { return FALSE; }
RtlZeroMemory (Entry, sizeof *Entry);
RtlInitUnicodeString (&(Entry->VerifierName), Start);
InsertTailList (&AVrfpVerifierProvidersList, &(Entry->List)); }
// *Current = Save;
Current += 1; }
return TRUE; }
VOID AVrfpSnapAlreadyLoadedDlls ( ) { PPEB_LDR_DATA Ldr; PLIST_ENTRY Head; PLIST_ENTRY Next; PLDR_DATA_TABLE_ENTRY Entry;
Ldr = NtCurrentPeb()->Ldr; Head = &(Ldr->InLoadOrderModuleList); Next = Head->Flink;
while (Next != Head) {
Entry = CONTAINING_RECORD (Next, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); Next = Next->Flink;
if (! AVrfpIsVerifierProviderDll (Entry->DllBase)) {
if ((AVrfpDebug & AVRF_DBG_SHOW_SNAPS)) { DbgPrint ("AVRF: resnapping %ws ... \n", Entry->BaseDllName.Buffer); }
AVrfpDllLoadNotificationInternal (Entry); } else {
if ((AVrfpDebug & AVRF_DBG_SHOW_SNAPS)) { DbgPrint ("AVRF: skipped resnapping provider %ws ... \n", Entry->BaseDllName.Buffer); } } } }
VOID AVrfpMoveProviderToEndOfInitializationList ( PWSTR ProviderName ) { PPEB_LDR_DATA Ldr; PLIST_ENTRY Head; PLIST_ENTRY Next; PLDR_DATA_TABLE_ENTRY Entry; BOOLEAN Done = FALSE;
Ldr = NtCurrentPeb()->Ldr; Head = &(Ldr->InInitializationOrderModuleList); Next = Head->Flink;
while (Next != Head) {
Entry = CONTAINING_RECORD (Next, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks); if (_wcsicmp (Entry->BaseDllName.Buffer, ProviderName) == 0) {
RemoveEntryList (Next); InsertTailList (Head, Next); Done = TRUE; break; }
Next = Next->Flink; }
if (! Done) { DbgPrint ("AVRF: provider %ws was not found in the initialization list \n", ProviderName);
DbgBreakPoint (); } }
BOOLEAN AVrfpLoadAndInitializeProvider ( PAVRF_VERIFIER_DESCRIPTOR Provider ) { PIMAGE_NT_HEADERS NtHeaders; BOOLEAN LoadError = FALSE; NTSTATUS Status; ULONG_PTR Descriptor; PRTL_VERIFIER_PROVIDER_DESCRIPTOR Dscr; BOOLEAN InitStatus; static WCHAR SystemDllPathBuffer[DOS_MAX_PATH_LENGTH]; UNICODE_STRING SystemDllPath;
if ((AVrfpDebug & AVRF_DBG_SHOW_SNAPS)) { DbgPrint ("AVRF: verifier dll `%ws' \n", Provider->VerifierName.Buffer); }
//
// Prepare the system search path (%windir%\system32).
// Verifier providers can be loaded only from this directory.
//
SystemDllPath.Buffer = SystemDllPathBuffer; SystemDllPath.Length = 0; SystemDllPath.MaximumLength = sizeof(SystemDllPathBuffer);
RtlAppendUnicodeToString (&SystemDllPath, USER_SHARED_DATA->NtSystemRoot); RtlAppendUnicodeToString (&SystemDllPath, L"\\System32\\");
//
// Load provider dll
//
Status = LdrLoadDll (SystemDllPath.Buffer, NULL, &(Provider->VerifierName), &(Provider->VerifierHandle));
if (! NT_SUCCESS(Status)) {
DbgPrint ("AVRF: %ws: failed to load provider `%ws' (status %08X) from %ws\n", AVrfpGetProcessName(), Provider->VerifierName.Buffer, Status, SystemDllPath.Buffer);
LoadError = TRUE; goto Error; } //
// Make sure we have a dll.
//
try { NtHeaders = RtlImageNtHeader (Provider->VerifierHandle);
if (! NtHeaders) {
LoadError = TRUE; goto Error; }
if ((NtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) {
DbgPrint ("AVRF: provider %ws is not a DLL image \n", Provider->VerifierName.Buffer);
LoadError = TRUE; goto Error; } } except (EXCEPTION_EXECUTE_HANDLER) {
DbgPrint ("AVRF: exception raised while probing provider %ws \n", Provider->VerifierName.Buffer);
LoadError = TRUE; goto Error; }
//
// We loaded the provider successfully. We will move it to the end
// of the initialization list so that code from other system dlls
// on which the provider relies gets initialized first. For normal
// DLLs this is not an issue but a verifier provider gets loaded
// before any normal DLL no matter what dependencies has.
//
AVrfpMoveProviderToEndOfInitializationList (Provider->VerifierName.Buffer);
//
// Now call the initialization routine with the special
// PROCESS_VERIFIER reason.
//
Provider->VerifierEntryPoint = LdrpFetchAddressOfEntryPoint(Provider->VerifierHandle);
if (Provider->VerifierEntryPoint == NULL) {
DbgPrint ("AVRF: cannot find an entry point for provider %ws \n", Provider->VerifierName.Buffer); LoadError = TRUE; goto Error; } try {
Descriptor = 0;
InitStatus = LdrpCallInitRoutine ((PDLL_INIT_ROUTINE)(Provider->VerifierEntryPoint), Provider->VerifierHandle, DLL_PROCESS_VERIFIER, (PCONTEXT)(&Descriptor));
if (InitStatus && Descriptor) {
Dscr = (PRTL_VERIFIER_PROVIDER_DESCRIPTOR)Descriptor;
//
// Check if this is really a provider descriptor.
//
if (Dscr->Length != sizeof (*Dscr)) {
LoadError = TRUE;
DbgPrint ("AVRF: provider %ws passed an invalid descriptor @ %p \n", Provider->VerifierName.Buffer, Descriptor); } else {
if ((AVrfpDebug & AVRF_DBG_SHOW_PROVIDER_LOADS)) {
DbgPrint ("AVRF: initialized provider %ws (descriptor @ %p) \n", Provider->VerifierName.Buffer, Descriptor); }
Provider->VerifierDlls = Dscr->ProviderDlls; Provider->VerifierLoadHandler = Dscr->ProviderDllLoadCallback; Provider->VerifierUnloadHandler = Dscr->ProviderDllUnloadCallback;
//
// Fill out the provider descriptor structure with goodies.
//
Dscr->VerifierImage = AVrfpGetProcessName(); Dscr->VerifierFlags = AVrfpVerifierFlags; Dscr->VerifierDebug = AVrfpDebug; } } else {
LoadError = TRUE;
DbgPrint ("AVRF: provider %ws did not initialize correctly \n", Provider->VerifierName.Buffer); } } except (EXCEPTION_EXECUTE_HANDLER) {
DbgPrint ("AVRF: exception raised in provider %ws initialization routine \n", Provider->VerifierName.Buffer); LoadError = TRUE; goto Error; }
Error:
return !LoadError; }
BOOLEAN AVrfpIsVerifierProviderDll ( PVOID Handle ) { PLIST_ENTRY Current; PAVRF_VERIFIER_DESCRIPTOR Entry;;
Current = AVrfpVerifierProvidersList.Flink;
while (Current != &AVrfpVerifierProvidersList) {
Entry = CONTAINING_RECORD (Current, AVRF_VERIFIER_DESCRIPTOR, List);
Current = Current->Flink;
if (Entry->VerifierHandle == Handle) { return TRUE; } }
return FALSE; }
VOID AVrfpDumpProviderList ( ) { PLIST_ENTRY Current; PAVRF_VERIFIER_DESCRIPTOR Entry;
Current = AVrfpVerifierProvidersList.Flink;
while (Current != &AVrfpVerifierProvidersList) {
Entry = CONTAINING_RECORD (Current, AVRF_VERIFIER_DESCRIPTOR, List);
Current = Current->Flink;
DbgPrint ("AVRF: provider %ws \n", Entry->VerifierName.Buffer); } }
PVOID AVrfpFindClosestThunkDuplicate ( PAVRF_VERIFIER_DESCRIPTOR Verifier, PWCHAR DllName, PCHAR ThunkName ) /*++
Routine description:
This function searches the list of providers backwards (reverse load order) for a function that verifies original export ThunkName from DllName. This is necessary to implement chaining of verification layers.
Parameters:
Verifier - verifier provider descriptor for which we want to find duplicates. DllName - name of a dll containing a verified export ThunkName - name of the verified export Return value:
Address of a verification function for the same thunk. Null if none was found. --*/ { PLIST_ENTRY Current; PAVRF_VERIFIER_DESCRIPTOR Entry; PRTL_VERIFIER_DLL_DESCRIPTOR Dlls; PRTL_VERIFIER_THUNK_DESCRIPTOR Thunks; ULONG Di; ULONG Ti;
Current = Verifier->List.Blink;
while (Current != &AVrfpVerifierProvidersList) {
Entry = CONTAINING_RECORD (Current, AVRF_VERIFIER_DESCRIPTOR, List);
Current = Current->Blink;
//
// Search in this provider for the thunk.
//
if ((AVrfpDebug & AVRF_DBG_SHOW_CHAIN_DETAILS)) { DbgPrint ("AVRF: chain: searching in %ws\n", Entry->VerifierName.Buffer); } Dlls = Entry->VerifierDlls;
for (Di = 0; Dlls[Di].DllName; Di += 1) {
if ((AVrfpDebug & AVRF_DBG_SHOW_CHAIN_DETAILS)) { DbgPrint ("AVRF: chain: dll: %ws\n", Dlls[Di].DllName); } if (_wcsicmp(Dlls[Di].DllName, DllName) == 0) {
Thunks = Dlls[Di].DllThunks;
for (Ti = 0; Thunks[Ti].ThunkName; Ti += 1) {
if ((AVrfpDebug & AVRF_DBG_SHOW_CHAIN_DETAILS)) { DbgPrint ("AVRF: chain: thunk: %s == %s ?\n", Thunks[Ti].ThunkName, ThunkName); }
if (_stricmp(Thunks[Ti].ThunkName, ThunkName) == 0) { if ((AVrfpDebug & AVRF_DBG_SHOW_CHAIN_DETAILS)) {
DbgPrint ("AVRF: Found duplicate for (%ws: %s) in %ws\n", DllName, ThunkName, Dlls[Di].DllName); }
return Thunks[Ti].ThunkNewAddress; } } } } }
return NULL; }
VOID AVrfpChainDuplicateVerificationLayers ( ) /*++
Routine description:
This routines is called in the final stage of verifier initialization, after all provider dlls have been loaded, and makes a final sweep to detect providers that attempt to verify the same interface. This will be chained together so that they will be called in reverse load order (last declared will be first called). Parameters:
None. Return value:
None. --*/ { PLIST_ENTRY Current; PAVRF_VERIFIER_DESCRIPTOR Entry; PRTL_VERIFIER_DLL_DESCRIPTOR Dlls; PRTL_VERIFIER_THUNK_DESCRIPTOR Thunks; ULONG Di; ULONG Ti; PVOID Duplicate;
Current = AVrfpVerifierProvidersList.Flink;
while (Current != &AVrfpVerifierProvidersList) {
Entry = CONTAINING_RECORD (Current, AVRF_VERIFIER_DESCRIPTOR, List);
Current = Current->Flink;
//
// Search in this provider for duplicate thunks.
//
Dlls = Entry->VerifierDlls;
for (Di = 0; Dlls[Di].DllName; Di += 1) {
Thunks = Dlls[Di].DllThunks;
for (Ti = 0; Thunks[Ti].ThunkName; Ti += 1) {
if ((AVrfpDebug & AVRF_DBG_SHOW_CHAIN_DETAILS)) {
DbgPrint ("AVRF: Checking %ws for duplicate (%ws: %s) \n", Entry->VerifierName.Buffer, Dlls[Di].DllName, Thunks[Ti].ThunkName); }
Duplicate = AVrfpFindClosestThunkDuplicate (Entry, Dlls[Di].DllName, Thunks[Ti].ThunkName);
if (Duplicate) {
if ((AVrfpDebug & AVRF_DBG_SHOW_CHAIN_ACTIVITY)) {
DbgPrint ("AVRF: Chaining (%ws: %s) to %ws\n", Dlls[Di].DllName, Thunks[Ti].ThunkName, Entry->VerifierName.Buffer); } Thunks[Ti].ThunkOldAddress = Duplicate; } } } } }
VOID AVrfDllLoadNotification ( PLDR_DATA_TABLE_ENTRY LoadedDllData ) /*++
Routine description:
This routine is the DLL load hook of application verifier. It gets called whenever a dll got loaded in the process space and after its import descriptors have been walked.
Parameters:
LoadedDllData - LDR loader structure for the dll. Return value:
None. --*/ { ULONG Index; PLIST_ENTRY Current; PAVRF_VERIFIER_DESCRIPTOR Entry;
//
// Do nothing if application verifier is not enabled. The function
// should not even get called if the flag is not set but we
// double check just in case.
//
if ((NtCurrentPeb()->NtGlobalFlag & FLG_APPLICATION_VERIFIER) == 0) { return; } //
// Get verifier global lock.
//
VERIFIER_LOCK ();
//
// We skip verifier providers. Otherwise we get into infinite loops.
//
if (AVrfpIsVerifierProviderDll (LoadedDllData->DllBase)) {
VERIFIER_UNLOCK (); return; }
//
// Call internal function.
//
AVrfpDllLoadNotificationInternal (LoadedDllData);
//
// Iterate the verifier provider list and notify each one of the
// load event.
Current = AVrfpVerifierProvidersList.Flink;
while (Current != &AVrfpVerifierProvidersList) {
Entry = CONTAINING_RECORD (Current, AVRF_VERIFIER_DESCRIPTOR, List);
Current = Current->Flink;
if (Entry->VerifierLoadHandler) {
Entry->VerifierLoadHandler (LoadedDllData->BaseDllName.Buffer, LoadedDllData->DllBase, LoadedDllData->SizeOfImage, LoadedDllData); } } VERIFIER_UNLOCK (); }
VOID AVrfpDllLoadNotificationInternal ( PLDR_DATA_TABLE_ENTRY LoadedDllData ) /*++
Routine description:
This routine is the DLL load hook of application verifier. It gets called whenever a dll got loaded in the process space and after its import descriptors have been walked. It is also called internally in early stages of process initialization when we just loaded verifier providers and we need to resnap dlls already loaded (.exe, ntdll.dll (although on ntdll this will have zero effect because it does not import anything)).
Parameters:
LoadedDllData - LDR loader structure for the dll. Return value:
None. --*/ { ULONG Index; PLIST_ENTRY Current; PAVRF_VERIFIER_DESCRIPTOR Entry;;
//
// If verifier is disabled skip.
//
if (AVrfpEnabled == FALSE) { return; } //
// Iterate the verifier provider list and for each one determine
// if one of the dlls that has exports to be verified is loaded.
// If this is the case we need to look at its export table in order
// to find out real addresses for functions being redirected.
//
Current = AVrfpVerifierProvidersList.Flink;
while (Current != &AVrfpVerifierProvidersList) {
PRTL_VERIFIER_DLL_DESCRIPTOR Dlls; PRTL_VERIFIER_THUNK_DESCRIPTOR Thunks;
Entry = CONTAINING_RECORD (Current, AVRF_VERIFIER_DESCRIPTOR, List);
Current = Current->Flink;
Dlls = Entry->VerifierDlls;
for (Index = 0; Dlls[Index].DllName; Index += 1) {
if ((Dlls[Index].DllFlags & AVRF_FLG_EXPORT_DLL_LOADED) == 0) {
int CompareResult;
CompareResult = _wcsicmp (LoadedDllData->BaseDllName.Buffer, Dlls[Index].DllName);
if (CompareResult == 0) {
if ((AVrfpDebug & AVRF_DBG_SHOW_DLLS_WITH_EXPORTS)) { DbgPrint ("AVRF: pid 0x%X: found dll descriptor for `%ws' with verified exports \n", HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess), LoadedDllData->BaseDllName.Buffer); }
AVrfpDetectVerifiedExports (&(Dlls[Index]), Dlls[Index].DllThunks); } } } }
//
// Note. We do not have to snap other DLLs already loaded because they cannot
// possibly have a dependence on a verifier export just discovered in
// current DLL. If this had been the case, the DLL would have been loaded
// earlier (before the current one).
//
AVrfpSnapDllImports (LoadedDllData); }
VOID AVrfDllUnloadNotification ( PLDR_DATA_TABLE_ENTRY DllData ) /*++
Routine description:
This routine is the DLL unload hook of application verifier. It gets called whenever a dll gets unloaded from the process space. The hook is called after the DllMain routine of the DLL got called with PROCESS_DETACH therefore this is the right moment to check for leaks. The function will call DllUnload notification routines for all providers loaded into the process space.
Parameters:
LoadedDllData - LDR loader structure for the dll. Return value:
None. --*/ { ULONG Index; PLIST_ENTRY Current; PAVRF_VERIFIER_DESCRIPTOR Entry;;
//
// Do nothing if application verifier is not enabled. The function
// should not even get called if the flag is not set but we
// double check just in case.
//
if ((NtCurrentPeb()->NtGlobalFlag & FLG_APPLICATION_VERIFIER) == 0) { return; }
//
// If verifier is disabled skip.
//
if (AVrfpEnabled == FALSE) { return; } //
// Get verifier global lock.
//
VERIFIER_LOCK ();
//
// We should never get this call for a verifier provider DLL because
// these are never unloaded.
//
if (AVrfpIsVerifierProviderDll (DllData->DllBase)) {
DbgPrint ("AVrfDllUnloadNotification called for a provider (%p) \n", DllData); DbgBreakPoint (); VERIFIER_UNLOCK (); return; }
//
// Iterate the verifier provider list and notify each one of the
// unload event.
Current = AVrfpVerifierProvidersList.Flink;
while (Current != &AVrfpVerifierProvidersList) {
Entry = CONTAINING_RECORD (Current, AVRF_VERIFIER_DESCRIPTOR, List);
Current = Current->Flink;
if (Entry->VerifierUnloadHandler) {
Entry->VerifierUnloadHandler (DllData->BaseDllName.Buffer, DllData->DllBase, DllData->SizeOfImage, DllData); } } VERIFIER_UNLOCK (); }
BOOLEAN AVrfpSnapDllImports ( PLDR_DATA_TABLE_ENTRY LdrDataTableEntry ) /*++
Routine description:
This routine walks the already resolved import tables of a loaded dll and modifies the addresses of all functions that need to be verifier. The dll has just been loaded, imports resolved but dll main function has not been called. Parameters:
LdrDataTableEntry - loader descriptor for a loaded dll Return value:
True if we checked all imports of the dll and modified the ones that need to be verified. False if an error was encountered along the way. --*/ { PVOID IATBase; SIZE_T BigIATSize; ULONG LittleIATSize; PVOID *ProcAddresses; ULONG NumberOfProcAddresses; ULONG OldProtect; USHORT TagIndex; NTSTATUS st; ULONG Pi; // procedure index
ULONG Di; // dll index
ULONG Ti; // thunk index
PLIST_ENTRY Current; PAVRF_VERIFIER_DESCRIPTOR Entry; PRTL_VERIFIER_DLL_DESCRIPTOR Dlls; PRTL_VERIFIER_THUNK_DESCRIPTOR Thunks;
//
// Determine the location and size of the IAT. If found, scan the
// IAT address to see if any are pointing to functions that should be
// verified and replace those thunks.
//
IATBase = RtlImageDirectoryEntryToData (LdrDataTableEntry->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_IAT, &LittleIATSize);
if (IATBase == NULL) { return FALSE; } BigIATSize = LittleIATSize;
//
// Make table read/write.
//
st = NtProtectVirtualMemory (NtCurrentProcess(), &IATBase, &BigIATSize, PAGE_READWRITE, &OldProtect);
if (!NT_SUCCESS (st)) {
DbgPrint( "AVRF: Unable to unprotect IAT to modify thunks (status %08X).\n", st); return FALSE; }
ProcAddresses = (PVOID *)IATBase; NumberOfProcAddresses = (ULONG)(BigIATSize / sizeof(PVOID));
for (Pi = 0; Pi < NumberOfProcAddresses; Pi += 1) { //
// If we find a null in the import table we skip over it.
//
if (*ProcAddresses == NULL) { ProcAddresses += 1; continue; }
Current = AVrfpVerifierProvidersList.Flink;
while (Current != &AVrfpVerifierProvidersList) {
Entry = CONTAINING_RECORD (Current, AVRF_VERIFIER_DESCRIPTOR, List);
Current = Current->Flink;
Dlls = Entry->VerifierDlls; for (Di = 0; Dlls[Di].DllName; Di += 1) {
Thunks = Dlls[Di].DllThunks;
for (Ti = 0; Thunks[Ti].ThunkName; Ti += 1) {
if (*ProcAddresses == Thunks[Ti].ThunkOldAddress) {
if (Thunks[Ti].ThunkNewAddress) {
*ProcAddresses = Thunks[Ti].ThunkNewAddress; } else {
DbgPrint ("AVRF:SilviuC: New thunk for %s is null. \n", Thunks[Ti].ThunkName); DbgBreakPoint (); }
if ((AVrfpDebug & AVRF_DBG_SHOW_SNAPS)) {
DbgPrint ("AVRF: Snapped (%ws: %s) with (%ws: %p). \n", LdrDataTableEntry->BaseDllName.Buffer, Thunks[Ti].ThunkName, Entry->VerifierName.Buffer, Thunks[Ti].ThunkNewAddress); } } } } }
ProcAddresses += 1; }
//
// Restore old protection for the table.
//
NtProtectVirtualMemory (NtCurrentProcess(), &IATBase, &BigIATSize, OldProtect, &OldProtect);
return TRUE; }
BOOLEAN AVrfpDetectVerifiedExports ( PRTL_VERIFIER_DLL_DESCRIPTOR Dll, PRTL_VERIFIER_THUNK_DESCRIPTOR Thunks ) /*++
Routine description:
This routine checks if `DllString' is the name of a dll that has exports that need to be verifier. If it does then we detect the addresses of all those exports. We need the addresses to detect what imports need to be modified by application verifier. Parameters:
DlString - name of a dll exporting verified interfaces. Thunks - array of thunk descriptors for our dll Return value:
True if verified exports have been detected. False if an error has been encountered. --*/ { UNICODE_STRING DllName; PLDR_DATA_TABLE_ENTRY DllData; PIMAGE_EXPORT_DIRECTORY Directory; ULONG Size; PCHAR NameAddress; PCHAR FunctionAddress; PCHAR Base; PCHAR IndexAddress; ULONG Index; ULONG RealIndex; BOOLEAN Result = FALSE; NTSTATUS Status; WCHAR StaticRedirectionBuffer[DOS_MAX_PATH_LENGTH]; UNICODE_STRING StaticRedirectionString; UNICODE_STRING DynamicRedirectionString; PUNICODE_STRING DllNameToUse; BOOLEAN Redirected = FALSE; ULONG Fi, Ti;
//
// "Fusion-ize" the dll name.
//
RtlInitUnicodeString (&DllName, Dll->DllName);
DynamicRedirectionString.Buffer = NULL;
StaticRedirectionString.Length = 0; StaticRedirectionString.MaximumLength = sizeof(StaticRedirectionBuffer); StaticRedirectionString.Buffer = StaticRedirectionBuffer;
DllNameToUse = &DllName;
Status = RtlDosApplyFileIsolationRedirection_Ustr( RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL, &DllName, &DefaultExtension, &StaticRedirectionString, &DynamicRedirectionString, &DllNameToUse, NULL, NULL, NULL);
if (NT_SUCCESS(Status)) { Redirected = TRUE; } else if (Status == STATUS_SXS_KEY_NOT_FOUND) { Status = STATUS_SUCCESS; }
//
// Get the loader descriptor for this dll.
//
if (NT_SUCCESS(Status)) {
Result = LdrpCheckForLoadedDll (NULL, DllNameToUse, TRUE, Redirected, &DllData);
if (DynamicRedirectionString.Buffer != NULL) RtlFreeUnicodeString(&DynamicRedirectionString); }
if (Result == FALSE) {
//
// We exit of we failed to fusionize name or did not find
// the dll among the loaded ones.
//
return FALSE; }
//
// Get the export directory for the dll.
//
Base = DllData->DllBase;
Directory = RtlImageDirectoryEntryToData (DllData->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &Size);
if (Directory == NULL) { return FALSE; }
//
// Iterate the exports for the dll and replace all those that
// need to be verified.
//
for (Ti = 0; Thunks[Ti].ThunkName; Ti += 1) { //
// If old thunk already filled (can happen due to chaining)
// then skip search for the original address.
//
if (Thunks[Ti].ThunkOldAddress) { continue; }
for (Fi = 0; Fi < Directory->NumberOfFunctions; Fi += 1) { NameAddress = Base + Directory->AddressOfNames; NameAddress = Base + ((ULONG *)NameAddress)[Fi];
IndexAddress = Base + Directory->AddressOfNameOrdinals; RealIndex = (ULONG)(((USHORT *)IndexAddress)[Fi]);
if (_stricmp (NameAddress, Thunks[Ti].ThunkName) == 0) {
FunctionAddress = Base + Directory->AddressOfFunctions; FunctionAddress = Base + ((ULONG *)FunctionAddress)[RealIndex];
Thunks[Ti].ThunkOldAddress = FunctionAddress;
if ((AVrfpDebug & AVRF_DBG_SHOW_VERIFIED_EXPORTS)) { DbgPrint ("AVRF: found verified export %s @ %p \n", NameAddress, FunctionAddress); } } } }
Dll->DllFlags |= AVRF_FLG_EXPORT_DLL_LOADED;
return TRUE; }
PWSTR AVrfpGetProcessName ( ) { PPEB_LDR_DATA Ldr; PLIST_ENTRY Head; PLIST_ENTRY Next; PLDR_DATA_TABLE_ENTRY Entry;
Ldr = NtCurrentPeb()->Ldr; Head = &(Ldr->InLoadOrderModuleList); Next = Head->Flink;
Entry = CONTAINING_RECORD (Next, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
return Entry->BaseDllName.Buffer; }
/////////////////////////////////////////////////////////////////////
///////////////////////////////////// Verifier options initialization
/////////////////////////////////////////////////////////////////////
BOOLEAN AVrfpEnableHandleVerifier ( ) { PROCESS_HANDLE_TRACING_ENABLE HandleCheckEnable; NTSTATUS Status;
RtlZeroMemory (&HandleCheckEnable, sizeof HandleCheckEnable);
Status = NtSetInformationProcess (NtCurrentProcess(), ProcessHandleTracing, &HandleCheckEnable, sizeof HandleCheckEnable);
if (!NT_SUCCESS (Status)) {
DbgPrint ("AVRF: failed to enable handle checking (status %X) \n", Status);
return FALSE; }
return TRUE; }
BOOLEAN AVrfpEnableStackVerifier ( ) { NtCurrentPeb()->NtGlobalFlag |= FLG_DISABLE_STACK_EXTENSION; return TRUE; }
BOOLEAN AVrfpEnableLockVerifier ( ) { RtlpCriticalSectionVerifier = TRUE; return TRUE; }
BOOLEAN AVrfpEnableHeapVerifier ( ) { extern ULONG RtlpDphGlobalFlags;
NtCurrentPeb()->NtGlobalFlag |= FLG_HEAP_PAGE_ALLOCS;
if (AVrfpVerifierFlags & RTL_VRF_FLG_FULL_PAGE_HEAP) { RtlpDphGlobalFlags |= PAGE_HEAP_ENABLE_PAGE_HEAP; } else {
//
// Nothing. Light heap is the default.
//
}
return TRUE; }
BOOLEAN AVrfpEnableVerifierOptions ( ) { BOOLEAN Result; BOOLEAN Failures = FALSE;
//
// Heap verifier in some form is enabled always.
//
Result = AVrfpEnableHeapVerifier ();
if (Result == FALSE) { Failures = TRUE; }
//
// Handle checks
//
if (AVrfpVerifierFlags & RTL_VRF_FLG_HANDLE_CHECKS) {
Result = AVrfpEnableHandleVerifier ();
if (Result == FALSE) { Failures = TRUE; } }
//
// Stack overflow checks
//
if (AVrfpVerifierFlags & RTL_VRF_FLG_STACK_CHECKS) {
Result = AVrfpEnableStackVerifier ();
if (Result == FALSE) { Failures = TRUE; } }
//
// Lock checks
//
if (AVrfpVerifierFlags & RTL_VRF_FLG_LOCK_CHECKS) {
Result = AVrfpEnableLockVerifier ();
if (Result == FALSE) { Failures = TRUE; } }
return !Failures; }
/////////////////////////////////////////////////////////////////////
////////////////////////////////////////// Application verifier stops
/////////////////////////////////////////////////////////////////////
ULONG_PTR AVrfpPreviousStopData[5]; ULONG_PTR AVrfpStopData[5];
VOID RtlApplicationVerifierStop ( ULONG_PTR Code, PCHAR Message, ULONG_PTR Param1, PCHAR Description1, ULONG_PTR Param2, PCHAR Description2, ULONG_PTR Param3, PCHAR Description3, ULONG_PTR Param4, PCHAR Description4 ) { BOOLEAN DoNotBreak = FALSE;
if ((Code & APPLICATION_VERIFIER_NO_BREAK)) {
DoNotBreak = TRUE; Code &= ~APPLICATION_VERIFIER_NO_BREAK; } //
// Make it easy for a debugger to pick up the failure info.
//
RtlMoveMemory (AVrfpPreviousStopData, AVrfpStopData, sizeof AVrfpStopData);
AVrfpStopData[0] = Code; AVrfpStopData[1] = Param1; AVrfpStopData[2] = Param2; AVrfpStopData[3] = Param3; AVrfpStopData[4] = Param4;
//
// Internal warnings/errors will not cause a break if app verifier is on.
// SilviuC: should make sure we really need this.
//
if ((Code & APPLICATION_VERIFIER_INTERNAL_ERROR)) { if (! (NtCurrentPeb()->NtGlobalFlag & FLG_APPLICATION_VERIFIER)) {
DbgPrint ("\n\n===========================================================\n"); DbgPrint ("VERIFIER INTERNAL ERROR %p: pid 0x%X: %s \n" "\n\t%p : %s\n\t%p : %s\n\t%p : %s\n\t%p : %s\n", Code, HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess), Message, Param1, Description1, Param2, Description2, Param3, Description3, Param4, Description4); DbgPrint ("===========================================================\n\n"); DbgBreakPoint (); } } else if ((Code & APPLICATION_VERIFIER_INTERNAL_WARNING)) { if (! (NtCurrentPeb()->NtGlobalFlag & FLG_APPLICATION_VERIFIER)) { DbgPrint ("\n\n===========================================================\n"); DbgPrint ("VERIFIER INTERNAL WARNING %p: pid 0x%X: %s \n" "\n\t%p : %s\n\t%p : %s\n\t%p : %s\n\t%p : %s\n", Code, HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess), Message, Param1, Description1, Param2, Description2, Param3, Description3, Param4, Description4); DbgPrint ("===========================================================\n\n"); DbgBreakPoint (); } } else { DbgPrint ("\n\n===========================================================\n"); DbgPrint ("VERIFIER STOP %p: pid 0x%X: %s \n" "\n\t%p : %s\n\t%p : %s\n\t%p : %s\n\t%p : %s\n", Code, HandleToUlong(NtCurrentTeb()->ClientId.UniqueProcess), Message, Param1, Description1, Param2, Description2, Param3, Description3, Param4, Description4); DbgPrint ("===========================================================\n\n");
if (! DoNotBreak) { DbgBreakPoint (); } } }
/////////////////////////////////////////////////////////////////////
////////////////////////////////////////// Page heap target dll logic
/////////////////////////////////////////////////////////////////////
//
// ISSUE: SilviuC: pageheap per dll code should move into verifier.dll
//
BOOLEAN AVrfpDphKernel32Snapped; BOOLEAN AVrfpDphMsvcrtSnapped;
#define SNAP_ROUTINE_GLOBALALLOC 0
#define SNAP_ROUTINE_GLOBALREALLOC 1
#define SNAP_ROUTINE_GLOBALFREE 2
#define SNAP_ROUTINE_LOCALALLOC 3
#define SNAP_ROUTINE_LOCALREALLOC 4
#define SNAP_ROUTINE_LOCALFREE 5
#define SNAP_ROUTINE_HEAPALLOC 6
#define SNAP_ROUTINE_HEAPREALLOC 7
#define SNAP_ROUTINE_HEAPFREE 8
#define SNAP_ROUTINE_HEAPCREATE 9
#define SNAP_ROUTINE_MALLOC 10
#define SNAP_ROUTINE_CALLOC 11
#define SNAP_ROUTINE_REALLOC 12
#define SNAP_ROUTINE_FREE 13
#define SNAP_ROUTINE_NEW 14
#define SNAP_ROUTINE_DELETE 15
#define SNAP_ROUTINE_NEW_ARRAY 16
#define SNAP_ROUTINE_DELETE_ARRAY 17
#define SNAP_ROUTINE_MAX_INDEX 18
PVOID AVrfpDphSnapRoutines [SNAP_ROUTINE_MAX_INDEX];
typedef struct _DPH_SNAP_NAME {
PSTR Name; ULONG Index;
} DPH_SNAP_NAME, * PDPH_SNAP_NAME;
DPH_SNAP_NAME AVrfpDphSnapNamesForKernel32 [] = {
{ "GlobalAlloc", 0 }, { "GlobalReAlloc", 1 }, { "GlobalFree", 2 }, { "LocalAlloc", 3 }, { "LocalReAlloc", 4 }, { "LocalFree", 5 }, { "HeapAlloc", 6 }, { "HeapReAlloc", 7 }, { "HeapFree", 8 }, { "HeapCreate", 9 }, { NULL, 0 } };
DPH_SNAP_NAME AVrfpDphSnapNamesForMsvcrt [] = {
{ "malloc", 10}, { "calloc", 11}, { "realloc", 12}, { "free", 13}, { "??2@YAPAXI@Z", 14}, // operator new
{ "??3@YAXPAX@Z", 15}, // operator delete
{ "??_U@YAPAXI@Z", 16}, // operator new[]
{ "??_V@YAXPAX@Z", 17}, // operator delete[]
{ NULL, 0 } };
//
// Declarations for replacement functions
//
PVOID AVrfpDphDllHeapAlloc ( IN PVOID HeapHandle, IN ULONG Flags, IN SIZE_T Size );
PVOID AVrfpDphDllHeapReAlloc ( IN PVOID HeapHandle, IN ULONG Flags, IN PVOID Address, IN SIZE_T Size );
BOOLEAN AVrfpDphDllHeapFree( IN PVOID HeapHandle, IN ULONG Flags, IN PVOID Address );
PVOID AVrfpDphDllLocalAlloc ( IN ULONG Flags, IN SIZE_T Size );
PVOID AVrfpDphDllLocalReAlloc ( IN PVOID Address, IN SIZE_T Size, IN ULONG Flags );
PVOID AVrfpDphDllLocalFree( IN PVOID Address );
PVOID AVrfpDphDllGlobalAlloc ( IN ULONG Flags, IN SIZE_T Size );
PVOID AVrfpDphDllGlobalReAlloc ( IN PVOID Address, IN SIZE_T Size, IN ULONG Flags );
PVOID AVrfpDphDllGlobalFree( IN PVOID Address );
PVOID __cdecl AVrfpDphDllmalloc ( IN SIZE_T Size );
PVOID __cdecl AVrfpDphDllcalloc ( IN SIZE_T Number, IN SIZE_T Size );
PVOID __cdecl AVrfpDphDllrealloc ( IN PVOID Address, IN SIZE_T Size );
VOID __cdecl AVrfpDphDllfree ( IN PVOID Address );
PVOID __cdecl AVrfpDphDllNew ( IN SIZE_T Size );
VOID __cdecl AVrfpDphDllDelete ( IN PVOID Address );
PVOID __cdecl AVrfpDphDllNewArray ( IN SIZE_T Size );
VOID __cdecl AVrfpDphDllDeleteArray ( IN PVOID Address );
//
// Replacement function for msvcrt HeapCreate used to intercept
// the CRT heap creation.
//
PVOID AVrfpDphDllHeapCreate ( ULONG Options, SIZE_T InitialSize, SIZE_T MaximumSize );
//
// Address of heap created by msvcrt. This is needed
// by the replacements of malloc/free etc.
//
PVOID AVrfpDphMsvcrtHeap;
//
// Snap implementation
//
BOOLEAN AVrfpDphDetectSnapRoutines ( PWSTR DllString, PDPH_SNAP_NAME SnapNames ) { PLDR_DATA_TABLE_ENTRY DllData; PIMAGE_EXPORT_DIRECTORY Directory; ULONG Size; PCHAR NameAddress; PCHAR FunctionAddress; PCHAR Base; PCHAR IndexAddress; ULONG Index; ULONG RealIndex; BOOLEAN Result = FALSE; UNICODE_STRING DllName; PDPH_SNAP_NAME CurrentSnapName; NTSTATUS Status; WCHAR StaticRedirectionBuffer[DOS_MAX_PATH_LENGTH]; UNICODE_STRING StaticRedirectionString; UNICODE_STRING DynamicRedirectionString; PUNICODE_STRING DllNameToUse; BOOLEAN Redirected = FALSE;
RtlInitUnicodeString ( &DllName, DllString);
DynamicRedirectionString.Buffer = NULL;
StaticRedirectionString.Length = 0; StaticRedirectionString.MaximumLength = sizeof(StaticRedirectionBuffer); StaticRedirectionString.Buffer = StaticRedirectionBuffer;
DllNameToUse = &DllName;
Status = RtlDosApplyFileIsolationRedirection_Ustr( RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL, &DllName, &DefaultExtension, &StaticRedirectionString, &DynamicRedirectionString, &DllNameToUse, NULL, NULL, NULL); if (NT_SUCCESS(Status)) { Redirected = TRUE; } else if (Status == STATUS_SXS_KEY_NOT_FOUND) { Status = STATUS_SUCCESS; }
if (NT_SUCCESS(Status)) { Result = LdrpCheckForLoadedDll ( NULL, DllNameToUse, TRUE, Redirected, &DllData);
if (DynamicRedirectionString.Buffer != NULL) RtlFreeUnicodeString(&DynamicRedirectionString); }
if (Result == FALSE) { return FALSE; }
Base = DllData->DllBase;
Directory = RtlImageDirectoryEntryToData ( DllData->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &Size );
if (Directory == NULL) { return FALSE; }
for (CurrentSnapName = SnapNames; CurrentSnapName->Name; CurrentSnapName += 1) {
for (Index = 0; Index < Directory->NumberOfFunctions; Index += 1) {
NameAddress = Base + Directory->AddressOfNames; NameAddress = Base + ((ULONG *)NameAddress)[Index];
IndexAddress = Base + Directory->AddressOfNameOrdinals; RealIndex = (ULONG)(((USHORT *)IndexAddress)[Index]);
if (_stricmp (NameAddress, CurrentSnapName->Name) == 0) {
FunctionAddress = Base + Directory->AddressOfFunctions; FunctionAddress = Base + ((ULONG *)FunctionAddress)[RealIndex];
AVrfpDphSnapRoutines[CurrentSnapName->Index] = FunctionAddress; DbgPrint ("Page heap: found %s @ address %p \n", NameAddress, FunctionAddress); } } }
return TRUE; }
BOOLEAN AVrfpDphSnapImports ( PLDR_DATA_TABLE_ENTRY LdrDataTableEntry, BOOLEAN CallToDetectCrtHeap ) { PVOID IATBase; SIZE_T BigIATSize; ULONG LittleIATSize; PVOID *ProcAddresses; ULONG NumberOfProcAddresses; ULONG OldProtect; USHORT TagIndex; NTSTATUS st;
//
// Determine the location and size of the IAT. If found, scan the
// IAT address to see if any are pointing to alloc/free functions
// and replace those thunks.
//
IATBase = RtlImageDirectoryEntryToData( LdrDataTableEntry->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_IAT, &LittleIATSize);
if (IATBase != NULL) {
BigIATSize = LittleIATSize;
st = NtProtectVirtualMemory( NtCurrentProcess(), &IATBase, &BigIATSize, PAGE_READWRITE, &OldProtect);
if (!NT_SUCCESS(st)) { DbgPrint( "LDR: Unable to unprotect IAT to enable per DLL page heap.\n" ); return FALSE; } else { ProcAddresses = (PVOID *)IATBase; NumberOfProcAddresses = (ULONG)(BigIATSize / sizeof(PVOID)); while (NumberOfProcAddresses--) {
//
// If we find a null in the import table we skip over it.
// Otherwise we will erroneously think it is a malloc() routine
// to be replaced. This can happen if msvcrt was not loaded yet
// and therefore the address of malloc() is also null.
//
if (*ProcAddresses == NULL) { ProcAddresses += 1; continue; }
if (CallToDetectCrtHeap) { if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_HEAPCREATE]) { *ProcAddresses = AVrfpDphDllHeapCreate; DbgPrint ("Snapped (%ws) HeapCreate ... \n", LdrDataTableEntry->BaseDllName.Buffer); } } else {
//
// ntdll imports
//
if (*ProcAddresses == RtlAllocateHeap) { *ProcAddresses = AVrfpDphDllHeapAlloc; DbgPrint ("Snapped (%ws) RtlAllocateHeap ... \n", LdrDataTableEntry->BaseDllName.Buffer); } else if (*ProcAddresses == RtlReAllocateHeap) { *ProcAddresses = AVrfpDphDllHeapReAlloc; DbgPrint ("Snapped (%ws) RtlReAllocateHeap ... \n", LdrDataTableEntry->BaseDllName.Buffer); } else if (*ProcAddresses == RtlFreeHeap) { *ProcAddresses = AVrfpDphDllHeapFree; DbgPrint ("Snapped (%ws) RtlFreeHeap ... \n", LdrDataTableEntry->BaseDllName.Buffer); }
//
// kernel32 imports
//
else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_HEAPALLOC]) { *ProcAddresses = AVrfpDphDllHeapAlloc; DbgPrint ("Snapped (%ws) HeapAlloc ... \n", LdrDataTableEntry->BaseDllName.Buffer); } else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_HEAPREALLOC]) { *ProcAddresses = AVrfpDphDllHeapReAlloc; DbgPrint ("Snapped (%ws) HeapReAlloc ... \n", LdrDataTableEntry->BaseDllName.Buffer); } else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_HEAPFREE]) { *ProcAddresses = AVrfpDphDllHeapFree; DbgPrint ("Snapped (%ws) HeapFree ... \n", LdrDataTableEntry->BaseDllName.Buffer); }
else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_LOCALALLOC]) { *ProcAddresses = AVrfpDphDllLocalAlloc; DbgPrint ("Snapped (%ws) LocalAlloc ... \n", LdrDataTableEntry->BaseDllName.Buffer); } else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_LOCALREALLOC]) { *ProcAddresses = AVrfpDphDllLocalReAlloc; DbgPrint ("Snapped (%ws) LocalReAlloc ... \n", LdrDataTableEntry->BaseDllName.Buffer); } else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_LOCALFREE]) { *ProcAddresses = AVrfpDphDllLocalFree; DbgPrint ("Snapped (%ws) LocalFree ... \n", LdrDataTableEntry->BaseDllName.Buffer); }
else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_GLOBALALLOC]) { *ProcAddresses = AVrfpDphDllGlobalAlloc; DbgPrint ("Snapped (%ws) GlobalAlloc ... \n", LdrDataTableEntry->BaseDllName.Buffer); } else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_GLOBALREALLOC]) { *ProcAddresses = AVrfpDphDllGlobalReAlloc; DbgPrint ("Snapped (%ws) GlobalReAlloc ... \n", LdrDataTableEntry->BaseDllName.Buffer); } else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_GLOBALFREE]) { *ProcAddresses = AVrfpDphDllGlobalFree; DbgPrint ("Snapped (%ws) GlobalFree ... \n", LdrDataTableEntry->BaseDllName.Buffer); }
//
// msvcrt imports
//
else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_MALLOC]) { *ProcAddresses = AVrfpDphDllmalloc; DbgPrint ("Snapped (%ws) malloc ... \n", LdrDataTableEntry->BaseDllName.Buffer); } else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_REALLOC]) { *ProcAddresses = AVrfpDphDllrealloc; DbgPrint ("Snapped (%ws) realloc ... \n", LdrDataTableEntry->BaseDllName.Buffer); } else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_CALLOC]) { *ProcAddresses = AVrfpDphDllcalloc; DbgPrint ("Snapped (%ws) calloc ... \n", LdrDataTableEntry->BaseDllName.Buffer); } else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_FREE]) { *ProcAddresses = AVrfpDphDllfree; DbgPrint ("Snapped (%ws) free ... \n", LdrDataTableEntry->BaseDllName.Buffer); }
else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_NEW]) { *ProcAddresses = AVrfpDphDllNew; DbgPrint ("Snapped (%ws) operator new ... \n", LdrDataTableEntry->BaseDllName.Buffer); } else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_DELETE]) { *ProcAddresses = AVrfpDphDllDelete; DbgPrint ("Snapped (%ws) operator delete ... \n", LdrDataTableEntry->BaseDllName.Buffer); } else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_NEW_ARRAY]) { *ProcAddresses = AVrfpDphDllNewArray; DbgPrint ("Snapped (%ws) operator new[] ... \n", LdrDataTableEntry->BaseDllName.Buffer); } else if (*ProcAddresses == AVrfpDphSnapRoutines[SNAP_ROUTINE_DELETE_ARRAY]) { *ProcAddresses = AVrfpDphDllDeleteArray; DbgPrint ("Snapped (%ws) operator delete[] ... \n", LdrDataTableEntry->BaseDllName.Buffer); } }
ProcAddresses += 1; }
NtProtectVirtualMemory( NtCurrentProcess(), &IATBase, &BigIATSize, OldProtect, &OldProtect); } }
return TRUE; }
VOID AVrfPageHeapDllNotification ( PLDR_DATA_TABLE_ENTRY LoadedDllData ) /*++
Routine description:
This routine is the DLL load hook for page heap per dll. It gets called whenever a dll got loaded in the process space and after its import descriptors have been walked.
Parameters:
LoadedDllData - LDR loader structure for the dll. Return value:
None. --*/ { BOOLEAN Kernel32JustSnapped = FALSE; BOOLEAN MsvcrtJustSnapped = FALSE;
//
// If we do not have per dll page heap feature enabled
// we return immediately.
//
if (! (RtlpDphGlobalFlags & PAGE_HEAP_USE_DLL_NAMES)) { return; }
if (! AVrfpDphKernel32Snapped) {
Kernel32JustSnapped = AVrfpDphDetectSnapRoutines ( Kernel32String.Buffer, AVrfpDphSnapNamesForKernel32);
AVrfpDphKernel32Snapped = Kernel32JustSnapped; }
if (! AVrfpDphMsvcrtSnapped) {
MsvcrtJustSnapped = AVrfpDphDetectSnapRoutines ( L"msvcrt.dll", AVrfpDphSnapNamesForMsvcrt);
AVrfpDphMsvcrtSnapped = MsvcrtJustSnapped; }
//
// Snap everything already loaded if we just managed
// to detect snap routines.
//
if (Kernel32JustSnapped || MsvcrtJustSnapped) {
PWSTR Current; PWSTR End; WCHAR SavedChar; PLDR_DATA_TABLE_ENTRY DllData; BOOLEAN Result; UNICODE_STRING DllName; WCHAR StaticRedirectionBuffer[DOS_MAX_PATH_LENGTH]; UNICODE_STRING StaticRedirectionString; UNICODE_STRING DynamicRedirectionString; PUNICODE_STRING DllNameToUse; BOOLEAN Redirected = FALSE; NTSTATUS Status;
DynamicRedirectionString.Buffer = NULL;
StaticRedirectionString.Length = 0; StaticRedirectionString.MaximumLength = sizeof(StaticRedirectionBuffer); StaticRedirectionString.Buffer = StaticRedirectionBuffer;
Current = RtlpDphTargetDlls;
while (*Current) {
while (*Current == L' ') { Current += 1; }
End = Current;
while (*End && *End != L' ') { End += 1; }
if (*Current == L'\0') { break; }
SavedChar = *End; *End = L'\0';
RtlInitUnicodeString ( &DllName, Current);
DllNameToUse = &DllName;
Status = RtlDosApplyFileIsolationRedirection_Ustr( RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL, &DllName, &DefaultExtension, &StaticRedirectionString, &DynamicRedirectionString, &DllNameToUse, NULL, NULL, NULL); if (NT_SUCCESS(Status)) { Redirected = TRUE; } else if (Status == STATUS_SXS_KEY_NOT_FOUND) { Status = STATUS_SUCCESS; }
if (NT_SUCCESS(Status)) { Result = LdrpCheckForLoadedDll ( NULL, DllNameToUse, TRUE, Redirected, &DllData);
if (DynamicRedirectionString.Buffer != NULL) RtlFreeUnicodeString(&DynamicRedirectionString); }
if (Result) {
if (DllData->DllBase == LoadedDllData->DllBase) { #if DBG
DbgPrint ("Page heap: oversnapping %ws \n", DllData->BaseDllName); #endif
}
AVrfpDphSnapImports (DllData, FALSE); }
*End = SavedChar; Current = End; } }
//
// If we just loaded msvcrt.dll we need to redirect HeapCreate call
// in order to detect when the CRT heap gets created.
//
if (_wcsicmp (LoadedDllData->BaseDllName.Buffer, L"msvcrt.dll") == 0) {
AVrfpDphSnapImports (LoadedDllData, TRUE); }
//
// Call back into page heap manager to figure out if the
// currently loaded dll is a target for page heap.
//
if (RtlpDphIsDllTargeted (LoadedDllData->BaseDllName.Buffer)) {
AVrfpDphSnapImports (LoadedDllData, FALSE); } }
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////// Snap routines
/////////////////////////////////////////////////////////////////////
//
// A biased heap pointer signals to the page heap manager that
// this allocation needs to get into page heap (not normal heap).
// This needs to happen only for allocation function (not free, delete).
//
#define BIAS_POINTER(p) ((PVOID)((ULONG_PTR)(p) | 0x01))
PVOID AVrfpDphDllHeapAlloc ( IN PVOID HeapHandle, IN ULONG Flags, IN SIZE_T Size ) { return RtlpDebugPageHeapAllocate ( BIAS_POINTER(HeapHandle), Flags, Size); }
PVOID AVrfpDphDllHeapReAlloc ( IN PVOID HeapHandle, IN ULONG Flags, IN PVOID Address, IN SIZE_T Size ) { return RtlpDebugPageHeapReAllocate ( BIAS_POINTER(HeapHandle), Flags, Address, Size); }
BOOLEAN AVrfpDphDllHeapFree( IN PVOID HeapHandle, IN ULONG Flags, IN PVOID Address ) { return RtlpDebugPageHeapFree ( HeapHandle, Flags, Address); }
//
// LocalAlloc, LocalReAlloc, LocalFree
// GlobalAlloc, GlobalReAlloc, GlobalFree
//
// The following macros are copied from sdk\inc\winbase.h
// There is very low probability that anybody will ever change
// these values for application compatibility reasons.
//
#define LMEM_MOVEABLE 0x0002
#define LMEM_ZEROINIT 0x0040
#if defined(_AMD64_) || defined(_IA64_)
#define BASE_HANDLE_MARK_BIT 0x08
#else
#define BASE_HANDLE_MARK_BIT 0x04
#endif
typedef PVOID (* FUN_LOCAL_ALLOC) ( IN ULONG Flags, IN SIZE_T Size );
typedef PVOID (* FUN_LOCAL_REALLOC) ( IN PVOID Address, IN SIZE_T Size, IN ULONG Flags );
typedef PVOID (* FUN_LOCAL_FREE)( IN PVOID Address );
typedef PVOID (* FUN_GLOBAL_ALLOC) ( IN ULONG Flags, IN SIZE_T Size );
typedef PVOID (* FUN_GLOBAL_REALLOC) ( IN PVOID Address, IN SIZE_T Size, IN ULONG Flags );
typedef PVOID (* FUN_GLOBAL_FREE)( IN PVOID Address );
PVOID AVrfpDphDllLocalAlloc ( IN ULONG Flags, IN SIZE_T Size ) { PVOID Block; FUN_LOCAL_ALLOC Original;
if (!(Flags & LMEM_MOVEABLE)) {
Block = RtlpDebugPageHeapAllocate ( BIAS_POINTER(RtlProcessHeap()), 0, Size);
if (Block && (Flags & LMEM_ZEROINIT)) { RtlZeroMemory (Block, Size); }
return Block; } else {
Original = (FUN_LOCAL_ALLOC)(AVrfpDphSnapRoutines[SNAP_ROUTINE_LOCALALLOC]); return (* Original) (Flags, Size); } }
PVOID AVrfpDphDllLocalReAlloc ( IN PVOID Address, IN SIZE_T Size, IN ULONG Flags ) { PVOID Block; FUN_LOCAL_REALLOC Original;
if (!(Flags & LMEM_MOVEABLE)) {
Block = RtlpDebugPageHeapReAllocate ( BIAS_POINTER(RtlProcessHeap()), 0, Address, Size);
return Block; } else {
Original = (FUN_LOCAL_REALLOC)(AVrfpDphSnapRoutines[SNAP_ROUTINE_LOCALREALLOC]); return (* Original) (Address, Size, Flags); } }
PVOID AVrfpDphDllLocalFree( IN PVOID Address ) { BOOLEAN Result; FUN_LOCAL_FREE Original;
if ((ULONG_PTR)Address & BASE_HANDLE_MARK_BIT) {
Original = (FUN_LOCAL_FREE)(AVrfpDphSnapRoutines[SNAP_ROUTINE_LOCALFREE]); return (* Original) (Address); } else {
Result = RtlpDebugPageHeapFree ( RtlProcessHeap(), 0, Address);
if (Result) { return NULL; } else { return Address; } } }
PVOID AVrfpDphDllGlobalAlloc ( IN ULONG Flags, IN SIZE_T Size ) { PVOID Block; FUN_GLOBAL_ALLOC Original;
if (!(Flags & LMEM_MOVEABLE)) {
Block = RtlpDebugPageHeapAllocate ( BIAS_POINTER(RtlProcessHeap()), 0, Size);
if (Block && (Flags & LMEM_ZEROINIT)) { RtlZeroMemory (Block, Size); }
return Block; } else {
Original = (FUN_GLOBAL_ALLOC)(AVrfpDphSnapRoutines[SNAP_ROUTINE_GLOBALALLOC]); return (* Original) (Flags, Size); } }
PVOID AVrfpDphDllGlobalReAlloc ( IN PVOID Address, IN SIZE_T Size, IN ULONG Flags ) { PVOID Block; FUN_GLOBAL_REALLOC Original;
if (!(Flags & LMEM_MOVEABLE)) {
Block = RtlpDebugPageHeapReAllocate ( BIAS_POINTER(RtlProcessHeap()), 0, Address, Size);
return Block; } else {
Original = (FUN_GLOBAL_REALLOC)(AVrfpDphSnapRoutines[SNAP_ROUTINE_GLOBALREALLOC]); return (* Original) (Address, Size, Flags); } }
PVOID AVrfpDphDllGlobalFree( IN PVOID Address ) { BOOLEAN Result; FUN_GLOBAL_FREE Original;
if ((ULONG_PTR)Address & BASE_HANDLE_MARK_BIT) {
Original = (FUN_GLOBAL_FREE)(AVrfpDphSnapRoutines[SNAP_ROUTINE_GLOBALFREE]); return (* Original) (Address); } else {
Result = RtlpDebugPageHeapFree ( RtlProcessHeap(), 0, Address);
if (Result) { return NULL; } else { return Address; } } }
//
// malloc, calloc, realloc, free
//
PVOID __cdecl AVrfpDphDllmalloc ( IN SIZE_T Size ) { PVOID Block;
ASSERT(AVrfpDphMsvcrtHeap != NULL);
Block = RtlpDebugPageHeapAllocate ( BIAS_POINTER(AVrfpDphMsvcrtHeap), 0, Size);
return Block; }
PVOID __cdecl AVrfpDphDllcalloc ( IN SIZE_T Number, IN SIZE_T Size ) { PVOID Block;
ASSERT(AVrfpDphMsvcrtHeap != NULL);
Block = RtlpDebugPageHeapAllocate ( BIAS_POINTER(AVrfpDphMsvcrtHeap), 0, Size * Number);
if (Block) { RtlZeroMemory (Block, Size * Number); }
return Block; }
PVOID __cdecl AVrfpDphDllrealloc ( IN PVOID Address, IN SIZE_T Size ) { PVOID Block;
ASSERT(AVrfpDphMsvcrtHeap != NULL);
if (Address == NULL) {
Block = RtlpDebugPageHeapAllocate ( BIAS_POINTER(AVrfpDphMsvcrtHeap), 0, Size); } else {
Block = RtlpDebugPageHeapReAllocate ( BIAS_POINTER(AVrfpDphMsvcrtHeap), 0, Address, Size); }
return Block; }
VOID __cdecl AVrfpDphDllfree ( IN PVOID Address ) { ASSERT(AVrfpDphMsvcrtHeap != NULL);
RtlpDebugPageHeapFree ( AVrfpDphMsvcrtHeap, 0, Address); }
//
// operator new, delete
// operator new[], delete[]
//
PVOID __cdecl AVrfpDphDllNew ( IN SIZE_T Size ) { PVOID Block;
ASSERT(AVrfpDphMsvcrtHeap != NULL);
Block = RtlpDebugPageHeapAllocate ( BIAS_POINTER(AVrfpDphMsvcrtHeap), 0, Size);
return Block; }
VOID __cdecl AVrfpDphDllDelete ( IN PVOID Address ) { ASSERT(AVrfpDphMsvcrtHeap != NULL);
RtlpDebugPageHeapFree ( AVrfpDphMsvcrtHeap, 0, Address); }
PVOID __cdecl AVrfpDphDllNewArray ( IN SIZE_T Size ) { ASSERT(AVrfpDphMsvcrtHeap != NULL);
return RtlpDebugPageHeapAllocate ( BIAS_POINTER(AVrfpDphMsvcrtHeap), 0, Size); }
VOID __cdecl AVrfpDphDllDeleteArray ( IN PVOID Address ) { ASSERT(AVrfpDphMsvcrtHeap != NULL);
RtlpDebugPageHeapFree ( AVrfpDphMsvcrtHeap, 0, Address); }
//
// HeapCreate
//
typedef PVOID (* FUN_HEAP_CREATE) ( ULONG Options, SIZE_T InitialSize, SIZE_T MaximumSize );
PVOID AVrfpDphDllHeapCreate ( ULONG Options, SIZE_T InitialSize, SIZE_T MaximumSize ) { PVOID Heap; FUN_HEAP_CREATE Original;
Original = (FUN_HEAP_CREATE)(AVrfpDphSnapRoutines[SNAP_ROUTINE_HEAPCREATE]); Heap = (* Original) (Options, InitialSize, MaximumSize);
AVrfpDphMsvcrtHeap = Heap; DbgPrint ("Page heap: detected CRT heap @ %p \n", AVrfpDphMsvcrtHeap);
return Heap; }
|