using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace BepInEx.IL2CPP.Allocator
{
///
/// Based on https://github.com/kubo/funchook
///
internal class WindowsPageAllocator : PageAllocator
{
protected override IntPtr AllocateChunk(IntPtr hint)
{
while (true)
{
var mbi = new WinApi.MEMORY_BASIC_INFORMATION();
if (WinApi.VirtualQuery(hint, ref mbi, Marshal.SizeOf()) == 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
if (mbi.State == WinApi.PageState.MEM_FREE)
{
long nextAddress = RoundUp(mbi.BaseAddress.ToInt64(), ALLOCATION_UNIT);
long d = nextAddress - mbi.BaseAddress.ToInt64();
if (d >= 0 && mbi.RegionSize.ToInt64() - d >= ALLOCATION_UNIT)
{
hint = (IntPtr)nextAddress;
break;
}
}
hint = (IntPtr)(mbi.BaseAddress.ToInt64() + mbi.RegionSize.ToInt64());
}
var chunk = WinApi.VirtualAlloc(hint, (UIntPtr)ALLOCATION_UNIT, WinApi.AllocationType.MEM_RESERVE, WinApi.ProtectConstant.PAGE_NOACCESS);
if (chunk == null)
throw new Win32Exception(Marshal.GetLastWin32Error());
var addr = WinApi.VirtualAlloc(chunk, (UIntPtr)PAGE_SIZE, WinApi.AllocationType.MEM_COMMIT, WinApi.ProtectConstant.PAGE_READWRITE);
if (addr == IntPtr.Zero)
{
int error = Marshal.GetLastWin32Error();
WinApi.VirtualFree(chunk, UIntPtr.Zero, WinApi.FreeType.MEM_RELEASE);
throw new Win32Exception(error);
}
return chunk;
}
public override IntPtr Allocate(IntPtr hint)
{
var pageAddress = base.Allocate(hint);
if (WinApi.VirtualAlloc(pageAddress, (UIntPtr)PAGE_SIZE, WinApi.AllocationType.MEM_COMMIT, WinApi.ProtectConstant.PAGE_READWRITE) == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
return pageAddress;
}
private static class WinApi
{
[Flags]
public enum AllocationType : uint
{
// ReSharper disable InconsistentNaming
MEM_COMMIT = 0x00001000,
MEM_RESERVE = 0x00002000
// ReSharper restore InconsistentNaming
}
[Flags]
public enum FreeType : uint
{
// ReSharper disable InconsistentNaming
MEM_RELEASE = 0x00008000
// ReSharper restore InconsistentNaming
}
public enum PageState : uint
{
// ReSharper disable InconsistentNaming
MEM_FREE = 0x10000
// ReSharper restore InconsistentNaming
}
[Flags]
public enum ProtectConstant : uint
{
// ReSharper disable InconsistentNaming
PAGE_NOACCESS = 0x01,
PAGE_READWRITE = 0x04
// ReSharper restore InconsistentNaming
}
[DllImport("kernel32", SetLastError = true)]
public static extern int VirtualQuery(IntPtr lpAddress, ref MEMORY_BASIC_INFORMATION lpBuffer, int dwLength);
[DllImport("kernel32", SetLastError = true)]
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType, ProtectConstant flProtect);
[DllImport("kernel32", SetLastError = true)]
public static extern bool VirtualFree(IntPtr lpAddress, UIntPtr dwSize, FreeType dwFreeType);
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct MEMORY_BASIC_INFORMATION
{
public readonly IntPtr BaseAddress;
public readonly IntPtr AllocationBase;
public readonly uint AllocationProtect;
public readonly IntPtr RegionSize;
public readonly PageState State;
public readonly uint Protect;
public readonly uint Type;
}
}
}
}