/// <summary> /// Reserves a region of memory within the virtual address space of a specified process. /// </summary> /// <param name="processHandle">The handle to a process.</param> /// <param name="allocAddress">The rough address of where the allocation should take place.</param> /// <param name="size">The size of the region of memory to allocate, in bytes.</param> /// <param name="protectionFlags">The memory protection for the region of pages to be allocated.</param> /// <param name="allocationFlags">The type of memory allocation.</param> /// <returns>The base address of the allocated region</returns> private static UInt64 Allocate( IntPtr processHandle, UInt64 allocAddress, Int32 size, MemoryProtectionFlags protectionFlags = MemoryProtectionFlags.ExecuteReadWrite, MemoryAllocationFlags allocationFlags = MemoryAllocationFlags.Commit | MemoryAllocationFlags.Reserve) { if (allocAddress != 0) { /* A specific address has been given. We will modify it to support the following constraints: * - Aligned by 0x10000 / 65536 * - Pointing to an unallocated region of memory * - Within +/- 2GB (using 1GB for safety) of address space of the originally specified address, such as to always be in range of a far jump instruction * Note: A retry count has been put in place because VirtualAllocEx with an allocAddress specified may be invalid by the time we request the allocation. */ UInt64 result = 0; Int32 retryCount = 0; // Request all chunks of unallocated memory. These will be very large in a 64-bit process. IEnumerable <MemoryBasicInformation64> freeMemory = WindowsMemoryQuery.QueryUnallocatedMemory( processHandle, allocAddress.Subtract(Int32.MaxValue >> 1, wrapAround: false), allocAddress.Add(Int32.MaxValue >> 1, wrapAround: false)); // Convert to normalized regions IEnumerable <NormalizedRegion> regions = freeMemory.Select(x => new NormalizedRegion(x.BaseAddress.ToUInt64(), x.RegionSize.ToInt32())); // Chunk the large regions into smaller regions based on the allocation size (minimum size is the alloc alignment to prevent creating too many chunks) List <NormalizedRegion> subRegions = new List <NormalizedRegion>(); foreach (NormalizedRegion region in regions) { region.BaseAddress = region.BaseAddress.Subtract(region.BaseAddress.Mod(WindowsMemoryAllocator.AllocAlignment), wrapAround: false); IEnumerable <NormalizedRegion> chunkedRegions = region.ChunkNormalizedRegion(Math.Max(size, WindowsMemoryAllocator.AllocAlignment)).Take(128).Where(x => x.RegionSize >= size); subRegions.AddRange(chunkedRegions); } do { // Sample a random chunk and attempt to allocate the memory result = subRegions.ElementAt(StaticRandom.Next(0, subRegions.Count())).BaseAddress; result = NativeMethods.VirtualAllocEx(processHandle, result.ToIntPtr(), size, allocationFlags, protectionFlags).ToUInt64(); if (result != 0 || retryCount >= WindowsMemoryAllocator.AllocateRetryCount) { break; } retryCount++; }while (result == 0); return(result); } else { // Allocate a memory page return(NativeMethods.VirtualAllocEx(processHandle, allocAddress.ToIntPtr(), size, allocationFlags, protectionFlags).ToUInt64()); } }
/// <summary> /// Gets regions of memory allocated in the remote process based on provided parameters. /// </summary> /// <param name="requiredProtection">Protection flags required to be present.</param> /// <param name="excludedProtection">Protection flags that must not be present.</param> /// <param name="allowedTypes">Memory types that can be present.</param> /// <param name="startAddress">The start address of the query range.</param> /// <param name="endAddress">The end address of the query range.</param> /// <returns>A collection of pointers to virtual pages in the target process.</returns> public IEnumerable <NormalizedRegion> GetVirtualPages( Process process, MemoryProtectionEnum requiredProtection, MemoryProtectionEnum excludedProtection, MemoryTypeEnum allowedTypes, UInt64 startAddress, UInt64 endAddress) { MemoryProtectionFlags requiredFlags = 0; MemoryProtectionFlags excludedFlags = 0; if ((requiredProtection & MemoryProtectionEnum.Write) != 0) { requiredFlags |= MemoryProtectionFlags.ExecuteReadWrite; requiredFlags |= MemoryProtectionFlags.ReadWrite; } if ((requiredProtection & MemoryProtectionEnum.Execute) != 0) { requiredFlags |= MemoryProtectionFlags.Execute; requiredFlags |= MemoryProtectionFlags.ExecuteRead; requiredFlags |= MemoryProtectionFlags.ExecuteReadWrite; requiredFlags |= MemoryProtectionFlags.ExecuteWriteCopy; } if ((requiredProtection & MemoryProtectionEnum.CopyOnWrite) != 0) { requiredFlags |= MemoryProtectionFlags.WriteCopy; requiredFlags |= MemoryProtectionFlags.ExecuteWriteCopy; } if ((excludedProtection & MemoryProtectionEnum.Write) != 0) { excludedFlags |= MemoryProtectionFlags.ExecuteReadWrite; excludedFlags |= MemoryProtectionFlags.ReadWrite; } if ((excludedProtection & MemoryProtectionEnum.Execute) != 0) { excludedFlags |= MemoryProtectionFlags.Execute; excludedFlags |= MemoryProtectionFlags.ExecuteRead; excludedFlags |= MemoryProtectionFlags.ExecuteReadWrite; excludedFlags |= MemoryProtectionFlags.ExecuteWriteCopy; } if ((excludedProtection & MemoryProtectionEnum.CopyOnWrite) != 0) { excludedFlags |= MemoryProtectionFlags.WriteCopy; excludedFlags |= MemoryProtectionFlags.ExecuteWriteCopy; } IEnumerable <MemoryBasicInformation64> memoryInfo = WindowsMemoryQuery.VirtualPages(process == null ? IntPtr.Zero : process.Handle, startAddress, endAddress, requiredFlags, excludedFlags, allowedTypes); IList <NormalizedRegion> regions = new List <NormalizedRegion>(); foreach (MemoryBasicInformation64 next in memoryInfo) { if (next.RegionSize < ChunkSize) { regions.Add(new NormalizedRegion(next.BaseAddress.ToUInt64(), next.RegionSize.ToInt32())); } else { // This region requires chunking Int64 remaining = next.RegionSize; UInt64 currentBaseAddress = next.BaseAddress.ToUInt64(); while (remaining >= ChunkSize) { regions.Add(new NormalizedRegion(currentBaseAddress, ChunkSize)); remaining -= ChunkSize; currentBaseAddress = currentBaseAddress.Add(ChunkSize, wrapAround: false); } if (remaining > 0) { regions.Add(new NormalizedRegion(currentBaseAddress, remaining.ToInt32())); } } } return(regions); }