aboutsummaryrefslogtreecommitdiff
path: root/ChocolArm64/Memory/MemoryManagementWindows.cs
blob: 6cee134279be49046ac9f0100e34393158a27ab5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace ChocolArm64.Memory
{
    static class MemoryManagementWindows
    {
        [Flags]
        private enum AllocationType : uint
        {
            Commit     = 0x1000,
            Reserve    = 0x2000,
            Decommit   = 0x4000,
            Release    = 0x8000,
            Reset      = 0x80000,
            Physical   = 0x400000,
            TopDown    = 0x100000,
            WriteWatch = 0x200000,
            LargePages = 0x20000000
        }

        [Flags]
        private enum MemoryProtection : uint
        {
            NoAccess                 = 0x01,
            ReadOnly                 = 0x02,
            ReadWrite                = 0x04,
            WriteCopy                = 0x08,
            Execute                  = 0x10,
            ExecuteRead              = 0x20,
            ExecuteReadWrite         = 0x40,
            ExecuteWriteCopy         = 0x80,
            GuardModifierflag        = 0x100,
            NoCacheModifierflag      = 0x200,
            WriteCombineModifierflag = 0x400
        }

        private enum WriteWatchFlags : uint
        {
            None  = 0,
            Reset = 1
        }

        [DllImport("kernel32.dll")]
        private static extern IntPtr VirtualAlloc(
            IntPtr           lpAddress,
            IntPtr           dwSize,
            AllocationType   flAllocationType,
            MemoryProtection flProtect);

        [DllImport("kernel32.dll")]
        private static extern bool VirtualProtect(
            IntPtr               lpAddress,
            IntPtr               dwSize,
            MemoryProtection     flNewProtect,
            out MemoryProtection lpflOldProtect);

        [DllImport("kernel32.dll")]
        private static extern bool VirtualFree(
            IntPtr         lpAddress,
            IntPtr         dwSize,
            AllocationType dwFreeType);

        [DllImport("kernel32.dll")]
        private static extern int GetWriteWatch(
            WriteWatchFlags dwFlags,
            IntPtr          lpBaseAddress,
            IntPtr          dwRegionSize,
            IntPtr[]        lpAddresses,
            ref ulong       lpdwCount,
            out uint        lpdwGranularity);

        public static IntPtr Allocate(IntPtr size)
        {
            const AllocationType flags =
                AllocationType.Reserve |
                AllocationType.Commit;

            IntPtr ptr = VirtualAlloc(IntPtr.Zero, size, flags, MemoryProtection.ReadWrite);

            if (ptr == IntPtr.Zero)
            {
                throw new OutOfMemoryException();
            }

            return ptr;
        }

        public static IntPtr AllocateWriteTracked(IntPtr size)
        {
            const AllocationType flags =
                AllocationType.Reserve |
                AllocationType.Commit  |
                AllocationType.WriteWatch;

            IntPtr ptr = VirtualAlloc(IntPtr.Zero, size, flags, MemoryProtection.ReadWrite);

            if (ptr == IntPtr.Zero)
            {
                throw new OutOfMemoryException();
            }

            return ptr;
        }

        public static bool Reprotect(IntPtr address, IntPtr size, Memory.MemoryProtection protection)
        {
            MemoryProtection prot = GetProtection(protection);

            return VirtualProtect(address, size, prot, out _);
        }

        private static MemoryProtection GetProtection(Memory.MemoryProtection protection)
        {
            switch (protection)
            {
                case Memory.MemoryProtection.None:           return MemoryProtection.NoAccess;
                case Memory.MemoryProtection.Read:           return MemoryProtection.ReadOnly;
                case Memory.MemoryProtection.ReadAndWrite:   return MemoryProtection.ReadWrite;
                case Memory.MemoryProtection.ReadAndExecute: return MemoryProtection.ExecuteRead;
                case Memory.MemoryProtection.Execute:        return MemoryProtection.Execute;

                default: throw new ArgumentException($"Invalid permission \"{protection}\".");
            }
        }

        public static bool Free(IntPtr address)
        {
            return VirtualFree(address, IntPtr.Zero, AllocationType.Release);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool GetModifiedPages(
            IntPtr    address,
            IntPtr    size,
            IntPtr[]  addresses,
            out ulong count)
        {
            ulong pagesCount = (ulong)addresses.Length;

            int result = GetWriteWatch(
                WriteWatchFlags.Reset,
                address,
                size,
                addresses,
                ref pagesCount,
                out uint granularity);

            count = pagesCount;

            return result == 0;
        }
    }
}