WindowsMemoryAllocator.cs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Runtime.InteropServices;
  5. namespace BepInEx.IL2CPP
  6. {
  7. /// <summary>
  8. /// Based on https://github.com/kubo/funchook
  9. /// </summary>
  10. internal class WindowsMemoryAllocator : MemoryAllocator
  11. {
  12. private readonly LinkedList<IntPtr> allocatedChunks = new LinkedList<IntPtr>();
  13. /// <summary>
  14. /// Allocates a single 64k chunk of memory near the given address
  15. /// </summary>
  16. /// <param name="hint">Address near which to attempt allocate the chunk</param>
  17. /// <returns>Allocated chunk</returns>
  18. /// <exception cref="Win32Exception">Allocation failed</exception>
  19. private IntPtr AllocateChunk(IntPtr hint)
  20. {
  21. while (true)
  22. {
  23. var mbi = new WinApi.MEMORY_BASIC_INFORMATION();
  24. if (WinApi.VirtualQuery(hint, ref mbi, Marshal.SizeOf<WinApi.MEMORY_BASIC_INFORMATION>()) == 0)
  25. throw new Win32Exception(Marshal.GetLastWin32Error());
  26. if (mbi.State == WinApi.PageState.MEM_FREE)
  27. {
  28. long nextAddress = RoundUp(mbi.BaseAddress.ToInt64(), ALLOCATION_UNIT);
  29. long d = nextAddress - mbi.BaseAddress.ToInt64();
  30. if (d >= 0 && mbi.RegionSize.ToInt64() - d >= ALLOCATION_UNIT)
  31. {
  32. hint = (IntPtr)nextAddress;
  33. break;
  34. }
  35. }
  36. hint = (IntPtr)(mbi.BaseAddress.ToInt64() + mbi.RegionSize.ToInt64());
  37. }
  38. var chunk = WinApi.VirtualAlloc(hint, (UIntPtr)ALLOCATION_UNIT, WinApi.AllocationType.MEM_RESERVE, WinApi.ProtectConstant.PAGE_NOACCESS);
  39. if (chunk == null)
  40. throw new Win32Exception(Marshal.GetLastWin32Error());
  41. var addr = WinApi.VirtualAlloc(chunk, (UIntPtr)PAGE_SIZE, WinApi.AllocationType.MEM_COMMIT, WinApi.ProtectConstant.PAGE_READWRITE);
  42. if (addr == IntPtr.Zero)
  43. {
  44. int error = Marshal.GetLastWin32Error();
  45. WinApi.VirtualFree(chunk, UIntPtr.Zero, WinApi.FreeType.MEM_RELEASE);
  46. throw new Win32Exception(error);
  47. }
  48. allocatedChunks.AddFirst(chunk);
  49. return chunk;
  50. }
  51. public override IntPtr Allocate(IntPtr func)
  52. {
  53. throw new NotImplementedException();
  54. }
  55. public override void Free(IntPtr buffer)
  56. {
  57. throw new NotImplementedException();
  58. }
  59. private static class WinApi
  60. {
  61. [Flags]
  62. public enum AllocationType : uint
  63. {
  64. // ReSharper disable InconsistentNaming
  65. MEM_COMMIT = 0x00001000,
  66. MEM_RESERVE = 0x00002000,
  67. MEM_RESET = 0x00080000,
  68. MEM_RESET_UNDO = 0x1000000,
  69. MEM_LARGE_PAGES = 0x20000000,
  70. MEM_PHYSICAL = 0x00400000,
  71. MEM_TOP_DOWN = 0x00100000,
  72. MEM_WRITE_WATCH = 0x00200000
  73. // ReSharper restore InconsistentNaming
  74. }
  75. [Flags]
  76. public enum FreeType : uint
  77. {
  78. // ReSharper disable InconsistentNaming
  79. MEM_DECOMMIT = 0x00004000,
  80. MEM_RELEASE = 0x00008000,
  81. MEM_COALESCE_PLACEHOLDERS = 0x00000001,
  82. MEM_PRESERVE_PLACEHOLDER = 0x00000002
  83. // ReSharper restore InconsistentNaming
  84. }
  85. public enum PageState : uint
  86. {
  87. // ReSharper disable InconsistentNaming
  88. MEM_COMMIT = 0x1000,
  89. MEM_FREE = 0x10000,
  90. MEM_RESERVE = 0x2000
  91. // ReSharper restore InconsistentNaming
  92. }
  93. [Flags]
  94. public enum ProtectConstant : uint
  95. {
  96. // ReSharper disable InconsistentNaming
  97. PAGE_EXECUTE = 0x10,
  98. PAGE_EXECUTE_READ = 0x20,
  99. PAGE_EXECUTE_READWRITE = 0x40,
  100. PAGE_EXECUTE_WRITECOPY = 0x80,
  101. PAGE_NOACCESS = 0x01,
  102. PAGE_READONLY = 0x02,
  103. PAGE_READWRITE = 0x04,
  104. PAGE_WRITECOPY = 0x08,
  105. PAGE_TARGETS_INVALID = 0x40000000,
  106. PAGE_TARGETS_NO_UPDATE = 0x40000000,
  107. PAGE_GUARD = 0x100,
  108. PAGE_NOCACHE = 0x200,
  109. PAGE_WRITECOMBINE = 0x400
  110. // ReSharper restore InconsistentNaming
  111. }
  112. [DllImport("kernel32", SetLastError = true)]
  113. public static extern int VirtualQuery(IntPtr lpAddress, ref MEMORY_BASIC_INFORMATION lpBuffer, int dwLength);
  114. [DllImport("kernel32", SetLastError = true)]
  115. public static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType, ProtectConstant flProtect);
  116. [DllImport("kernel32", SetLastError = true)]
  117. public static extern bool VirtualFree(IntPtr lpAddress, UIntPtr dwSize, FreeType dwFreeType);
  118. [StructLayout(LayoutKind.Sequential)]
  119. // ReSharper disable once InconsistentNaming
  120. public struct MEMORY_BASIC_INFORMATION
  121. {
  122. public readonly IntPtr BaseAddress;
  123. public readonly IntPtr AllocationBase;
  124. public readonly uint AllocationProtect;
  125. public readonly IntPtr RegionSize;
  126. public readonly PageState State;
  127. public readonly uint Protect;
  128. public readonly uint Type;
  129. }
  130. }
  131. }
  132. }