/// <summary> /// Enumerate committed, accessible, non-guarded memory regions /// </summary> /// <param name="pList">Region list</param> /// <param name="start">Region start</param> /// <param name="end">Region end</param> /// <param name="mapSections">If set to FALSE, section objects will be excluded from list</param> /// <returns>Status code</returns> NTSTATUS BBBuildProcessRegionListForRange( IN PLIST_ENTRY pList, IN ULONG_PTR start, IN ULONG_PTR end, IN BOOLEAN mapSections ) { NTSTATUS status = STATUS_SUCCESS; MEMORY_BASIC_INFORMATION mbi = { 0 }; SIZE_T length = 0; ULONG_PTR memptr = 0; PMAP_ENTRY pEntry = NULL; for (memptr = start; memptr < end; memptr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize) { status = ZwQueryVirtualMemory( ZwCurrentProcess(), (PVOID)memptr, MemoryBasicInformationEx, &mbi, sizeof( mbi ), &length ); if (!NT_SUCCESS( status )) { DPRINT( "BlackBone: %s: ZwQueryVirtualMemory for address 0x%p returned status 0x%X\n", __FUNCTION__, memptr, status ); // STATUS_INVALID_PARAMETER is a normal status for last secured VAD under Win7 return status == STATUS_INVALID_PARAMETER ? STATUS_SUCCESS : status; } // Skip non-committed, no-access and guard pages else if (mbi.State == MEM_COMMIT && mbi.Protect != PAGE_NOACCESS && !(mbi.Protect & PAGE_GUARD)) { // Ignore shared memory if required if (mbi.Type != MEM_PRIVATE && !mapSections) continue; pEntry = ExAllocatePoolWithTag( PagedPool, sizeof( MAP_ENTRY ), BB_POOL_TAG ); if (pEntry == NULL) { DPRINT( "BlackBone: %s: Failed to allocate memory for Remap descriptor\n", __FUNCTION__ ); BBCleanupPageList( FALSE, pList ); return STATUS_NO_MEMORY; } pEntry->originalPtr = (ULONG_PTR)mbi.BaseAddress; pEntry->size = mbi.RegionSize; pEntry->newPtr = 0; pEntry->pMdl = NULL; pEntry->locked = FALSE; pEntry->shared = mbi.Type != MEM_PRIVATE; InsertTailList( pList, &pEntry->link ); } } //if (NT_SUCCESS( status )) //status = BBConsolidateRegionList( pList ); return status; }
HANDLE NTAPI RtlDebugCreateHeap(ULONG Flags, PVOID Addr, SIZE_T ReserveSize, SIZE_T CommitSize, PVOID Lock, PRTL_HEAP_PARAMETERS Parameters) { MEMORY_BASIC_INFORMATION MemoryInfo; NTSTATUS Status; PHEAP Heap; /* Validate parameters */ if (ReserveSize <= HEAP_ENTRY_SIZE) { DPRINT1("HEAP: Incorrect ReserveSize %x\n", ReserveSize); return NULL; } if (ReserveSize < CommitSize) { DPRINT1("HEAP: Incorrect CommitSize %x\n", CommitSize); return NULL; } if (Flags & HEAP_NO_SERIALIZE && Lock) { DPRINT1("HEAP: Can't specify Lock routine and have HEAP_NO_SERIALIZE flag set\n"); return NULL; } /* If the address is specified, check it's virtual memory */ if (Addr) { Status = ZwQueryVirtualMemory(NtCurrentProcess(), Addr, MemoryBasicInformation, &MemoryInfo, sizeof(MemoryInfo), NULL); if (!NT_SUCCESS(Status)) { DPRINT1("HEAP: Specified heap base address %p is invalid, Status 0x%08X\n", Addr, Status); return NULL; } if (MemoryInfo.BaseAddress != Addr) { DPRINT1("HEAP: Specified heap base address %p is not really a base one %p\n", Addr, MemoryInfo.BaseAddress); return NULL; } if (MemoryInfo.State == MEM_FREE) { DPRINT1("HEAP: Specified heap base address %p is free\n", Addr); return NULL; } } /* All validation performed, now call the real routine with skip validation check flag */ Flags |= HEAP_SKIP_VALIDATION_CHECKS | HEAP_TAIL_CHECKING_ENABLED | HEAP_FREE_CHECKING_ENABLED; Heap = RtlCreateHeap(Flags, Addr, ReserveSize, CommitSize, Lock, Parameters); if (!Heap) return NULL; // FIXME: Capture stack backtrace RtlpValidateHeapHeaders(Heap, TRUE); return Heap; }
/// <summary> /// Process section object pages /// Function will attempt to trigger copy-on-write for underlying pages to convert them into private /// If copy-on-write fails, region will be then mapped as read-only /// </summary> /// <param name="pEntry">Region data</param> /// <returns>Status code</returns> NTSTATUS BBHandleSharedRegion( IN PMAP_ENTRY pEntry ) { NTSTATUS status = STATUS_SUCCESS; MEMORY_BASIC_INFORMATION mbi = { 0 }; SIZE_T length = 0; ULONG_PTR memptr = 0; // Iterate underlying memory regions for (memptr = pEntry->originalPtr; memptr < pEntry->originalPtr + pEntry->size; memptr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize) { PVOID pBase = NULL; SIZE_T size = 0; MEMORY_WORKING_SET_EX_INFORMATION wsInfo = { 0 }; ULONG oldProt = 0; BOOLEAN writable = FALSE; status = ZwQueryVirtualMemory( ZwCurrentProcess(), (PVOID)memptr, MemoryBasicInformation, &mbi, sizeof( mbi ), &length ); if (!NT_SUCCESS( status )) { DPRINT( "BlackBone: %s: ZwQueryVirtualMemory for address 0x%p failed\n", __FUNCTION__, memptr ); return status; } writable = (mbi.Protect & (PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE)); pBase = mbi.BaseAddress; size = (SIZE_T)mbi.RegionSize; // Make readonly pages writable if (writable || NT_SUCCESS( ZwProtectVirtualMemory( ZwCurrentProcess(), &pBase, &size, PAGE_EXECUTE_READWRITE, &oldProt ) )) { BOOLEAN failed = FALSE; // Touch pages to trigger copy-on-write and create private copies // This is crucial to prevent changes on section pages __try { ProbeForWrite( mbi.BaseAddress, mbi.RegionSize, PAGE_SIZE ); } __except (EXCEPTION_EXECUTE_HANDLER) { DPRINT( "BlackBone: %s: Exception while touching address 0x%p\n", __FUNCTION__, mbi.BaseAddress ); failed = TRUE; } // Restore protection if (writable == FALSE) ZwProtectVirtualMemory( ZwCurrentProcess(), &pBase, &size, oldProt, &oldProt ); if (failed) return STATUS_SHARING_VIOLATION; // If region was writable, it's safe to map shared pages if (writable) continue; // // Ensure pages were made private // for (ULONG_PTR pPage = (ULONG_PTR)mbi.BaseAddress; pPage < (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize; pPage += PAGE_SIZE) { RtlZeroMemory( &wsInfo, sizeof( wsInfo ) ); wsInfo.VirtualAddress = (PVOID)pPage; // Check page 'shared' flag if (NT_SUCCESS( ZwQueryVirtualMemory( ZwCurrentProcess(), NULL, MemoryWorkingSetExInformation, &wsInfo, sizeof( wsInfo ), &length ) ) && wsInfo.VirtualAttributes.Shared) { DPRINT( "BlackBone: %s: Page at address 0x%p is still shared!", __FUNCTION__, pPage ); return STATUS_SHARING_VIOLATION; } } } else {