/// <summary> /// Finds an available already present buffer and writes writes your own memory bytes into /// the specified buffer, giving you back the address of said buffer. If there is no available /// buffer, a new buffer will be automatically created. /// </summary> /// <param name="bytesToWrite">Individual bytes to be written onto a buffer.</param> /// <returns>Pointer to the passed in bytes written to memory.</returns> public static IntPtr Add(byte[] bytesToWrite) { // Get available buffers. List <MemoryBuffer> buffers = GetCachedBuffers(bytesToWrite.Length); // If there are no available buffers, create one. if (buffers.Count < 1) { // Get System Allocation Granularity. GetSystemInfo(out var systemInfo); // Get the desired page size, that being 1 allocation granularity greater than the bytes to be written. // 40 is an (over) estimate on the buffer header size, which cannot be properly read due string. // Retrieves the total size of the object to be added to a buffer, and the buffer header. int totalObjectSize = bytesToWrite.Length + Marshal.SizeOf(typeof(MemoryBufferHeader)); int desiredGranularity = (totalObjectSize / (int)systemInfo.allocationGranularity) + 1; uint desiredBufferSize = (uint)(desiredGranularity * systemInfo.allocationGranularity); // Create new buffer. var memoryPageSet = Bindings.TargetProcess.GetPages(); foreach (var memoryPages in memoryPageSet) { // For clarity, will be optimized out by the JIT compiler. // We have to align the starting address with the allocation granularity, it must be a multiple of it. long pageEnd = (long)memoryPages.BaseAddress + (long)memoryPages.RegionSize; // Place on next 64K boundary (or allocation granularity) long firstPageBase = (((long)memoryPages.BaseAddress / systemInfo.allocationGranularity) + 1) * systemInfo.allocationGranularity; long firstPageEnd = firstPageBase + desiredBufferSize; long lastPageBase = ((pageEnd - desiredBufferSize) / systemInfo.allocationGranularity) * systemInfo.allocationGranularity; long lastPageEnd = lastPageBase + desiredBufferSize; // Check if desired buffer would fit in the free page region. if ((uint)memoryPages.RegionSize > desiredBufferSize && memoryPages.State == PageState.Free) { // 1. Check if inbounds. 2. Check if new page start aligned to 64K/Granularity still fits. if (lastPageBase > (long)memoryPages.BaseAddress) { MemoryBuffer buffer = new MemoryBuffer((IntPtr)lastPageBase, desiredBufferSize); _knownBuffers.Add(buffer); return(buffer.Add(bytesToWrite)); } // 1. Check if inbounds. 2. Check if new page start aligned to 64K/Granularity still fits. else if (firstPageEnd < (long)memoryPages.BaseAddress + (long)memoryPages.RegionSize) { MemoryBuffer buffer = new MemoryBuffer((IntPtr)firstPageBase, desiredBufferSize); _knownBuffers.Add(buffer); return(buffer.Add(bytesToWrite)); } } } // Probably out of memory. return(IntPtr.Zero); } else { return(buffers[0].Add(bytesToWrite)); } }
/// <summary> /// See <see cref="Add(byte[])"/> for details. Intended to use for X64 assembly purely. /// Adds the specified byte contents into a buffer in 32bit signed (2GB) proximity to a specified target address. /// Intended to be used in conjunction with 64bit Assembly, where 64bit immediates are not available. /// </summary> /// <param name="bytesToWrite">Individual bytes to be written onto a buffer.</param> /// <param name="targetAddress"> /// The target address within which the buffer to be written to must lie in 2GB memory proximity of. /// Specify 0 for 32bit address, else near whatever assembly code you are targeting. /// </param> /// <returns>Pointer to the passed in bytes written to memory.</returns> public static unsafe IntPtr Add(byte[] bytesToWrite, IntPtr targetAddress) { // Get available buffers. List <MemoryBuffer> buffers = GetCachedBuffers(bytesToWrite.Length); // Iterate over all buffers to see if any fits the desired target. ulong minimumAddress = (ulong)(targetAddress + -2000000000); // 2GB ulong maximumAddress = (ulong)targetAddress + 2000000000; // 2GB // Check in case minimum address < 0 if (minimumAddress > (ulong)targetAddress) { minimumAddress = 0; } // Find appropriate buffer, add and return. // If buffer not found, fallback is provided below to create a new buffer. foreach (var buffer in buffers) { ulong startAddress = (ulong)buffer.BaseBufferAddress; ulong endAddress = (ulong)buffer.BaseBufferAddress + buffer.BufferHeader.BufferSize; if (startAddress >= minimumAddress && endAddress < maximumAddress) { // Ensure our bytes fit to the buffer. if (buffer.CheckItemSize(bytesToWrite.Length)) { return(buffer.Add(bytesToWrite)); } } } // If we have not returned, no buffer was found in the specified range. // Get a list of all pages. var memoryBasicInformation = Bindings.TargetProcess.GetPages(); // Get System Allocation Granularity. GetSystemInfo(out var systemInfo); // Get the desired page size, that being 1 allocation granularity greater than the bytes to be written. int totalBytesToWrite = bytesToWrite.Length + sizeof(MemoryBufferHeader); int desiredGranularity = (totalBytesToWrite / (int)systemInfo.allocationGranularity) + 1; uint desiredBufferSize = (uint)(desiredGranularity * systemInfo.allocationGranularity); // Iterate each page. foreach (var memoryPages in memoryBasicInformation) { // For clarity, will be optimized out by the JIT compiler. // We have to align the starting address with the allocation granularity, it must be a multiple of it. ulong pageEnd = (ulong)memoryPages.BaseAddress + (ulong)memoryPages.RegionSize; // Place on next 64K boundary (or allocation granularity) ulong firstPageBase = (((ulong)memoryPages.BaseAddress / systemInfo.allocationGranularity) + 1) * systemInfo.allocationGranularity; ulong firstPageEnd = firstPageBase + desiredBufferSize; ulong lastPageBase = ((pageEnd - desiredBufferSize) / systemInfo.allocationGranularity) * systemInfo.allocationGranularity; ulong lastPageEnd = lastPageBase + desiredBufferSize; // Check if desired buffer would fit in the free page region. if ((ulong)memoryPages.RegionSize > desiredBufferSize && memoryPages.State == PageState.Free) { // 1. Check if inbounds. 2. Check if new page start aligned to 64K/Granularity still fits. if (lastPageBase >= minimumAddress && lastPageEnd < maximumAddress && lastPageBase > (ulong)memoryPages.BaseAddress) { MemoryBuffer buffer = new MemoryBuffer((IntPtr)lastPageBase, desiredBufferSize); _knownBuffers.Add(buffer); return(buffer.Add(bytesToWrite)); } // 1. Check if inbounds. 2. Check if new page start aligned to 64K/Granularity still fits. else if (firstPageBase >= minimumAddress && firstPageEnd < maximumAddress && firstPageEnd < (ulong)memoryPages.BaseAddress + (ulong)memoryPages.RegionSize) { MemoryBuffer buffer = new MemoryBuffer((IntPtr)firstPageBase, desiredBufferSize); _knownBuffers.Add(buffer); return(buffer.Add(bytesToWrite)); } } } return(IntPtr.Zero); }