/// <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> public 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 = Memory.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(Memory.AllocAlignment), wrapAround: false); IEnumerable <NormalizedRegion> chunkedRegions = region.ChunkNormalizedRegion(Math.Max(size, Memory.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 >= Memory.AllocateRetryCount) { break; } retryCount++; }while (result == 0); return(result); } else { // Allocate a memory page return(NativeMethods.VirtualAllocEx(processHandle, allocAddress.ToIntPtr(), size, allocationFlags, protectionFlags).ToUInt64()); } }