Repository navigation
Expand file tree
/
Copy pathMemoryHelper.cs
More file actions
265 lines (226 loc) · 12.9 KB
/
Copy pathMemoryHelper.cs
File metadata and controls
265 lines (226 loc) · 12.9 KB
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
using System;
using System.Collections.Generic;
using System.ComponentModel;
using Process.NET.Marshaling;
using Process.NET.Native;
using Process.NET.Native.Types;
namespace Process.NET.Utilities
{
/// <summary>
/// Static core class providing tools for memory editing.
/// </summary>
public static class MemoryHelper
{
/// <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="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 IntPtr Allocate(SafeMemoryHandle processHandle, int size,
MemoryProtectionFlags protectionFlags = MemoryProtectionFlags.ExecuteReadWrite,
MemoryAllocationFlags allocationFlags = MemoryAllocationFlags.Commit)
{
// Check if the handle is valid
HandleManipulator.ValidateAsArgument(processHandle, "processHandle");
// Allocate a memory page
var ret = Kernel32.VirtualAllocEx(processHandle, IntPtr.Zero, size, allocationFlags, protectionFlags);
// Check whether the memory page is valid
if (ret != IntPtr.Zero)
return ret;
// If the pointer isn't valid, throws an exception
throw new Win32Exception($"Couldn't allocate memory of {size} byte(s).");
}
/// <summary>
/// Closes an open object handle.
/// </summary>
/// <param name="handle">A valid handle to an open object.</param>
public static void CloseHandle(IntPtr handle)
{
// Check if the handle is valid
HandleManipulator.ValidateAsArgument(handle, "handle");
// Close the handle
if (!Kernel32.CloseHandle(handle))
throw new Win32Exception($"Couldn't close he handle 0x{handle}.");
}
/// <summary>
/// Releases a region of memory within the virtual address space of a specified process.
/// </summary>
/// <param name="processHandle">A handle to a process.</param>
/// <param name="address">A pointer to the starting address of the region of memory to be freed.</param>
public static void Free(SafeMemoryHandle processHandle, IntPtr address)
{
// Check if the handles are valid
HandleManipulator.ValidateAsArgument(processHandle, "processHandle");
HandleManipulator.ValidateAsArgument(address, "address");
// Free the memory
if (!Kernel32.VirtualFreeEx(processHandle, address, 0, MemoryReleaseFlags.Release))
// If the memory wasn't correctly freed, throws an exception
throw new Win32Exception($"The memory page 0x{address.ToString("X")} cannot be freed.");
}
/// <summary>
/// etrieves information about the specified process.
/// </summary>
/// <param name="processHandle">A handle to the process to query.</param>
/// <returns>A <see cref="ProcessBasicInformation" /> structure containg process information.</returns>
public static ProcessBasicInformation NtQueryInformationProcess(SafeMemoryHandle processHandle)
{
// Check if the handle is valid
HandleManipulator.ValidateAsArgument(processHandle, "processHandle");
// Create a structure to store process info
var info = new ProcessBasicInformation();
// Get the process info
var ret = Nt.NtQueryInformationProcess(processHandle, ProcessInformationClass.ProcessBasicInformation,
ref info, info.Size, IntPtr.Zero);
// If the function succeeded
if (ret == 0)
return info;
// Else, couldn't get the process info, throws an exception
throw new ApplicationException($"Couldn't get the information from the process, error code '{ret}'.");
}
/// <summary>
/// Opens an existing local process object.
/// </summary>
/// <param name="accessFlags">The access level to the process object.</param>
/// <param name="processId">The identifier of the local process to be opened.</param>
/// <returns>An open handle to the specified process.</returns>
public static SafeMemoryHandle OpenProcess(ProcessAccessFlags accessFlags, int processId)
{
// Get an handle from the remote process
var handle = Kernel32.OpenProcess(accessFlags, false, processId);
// Check whether the handle is valid
if (!handle.IsInvalid && !handle.IsClosed)
return handle;
// Else the handle isn't valid, throws an exception
throw new Win32Exception($"Couldn't open the process {processId}.");
}
/// <summary>
/// Reads an array of bytes in the memory form the target process.
/// </summary>
/// <param name="processHandle">A handle to the process with memory that is being read.</param>
/// <param name="address">A pointer to the base address in the specified process from which to read.</param>
/// <param name="size">The number of bytes to be read from the specified process.</param>
/// <returns>The collection of read bytes.</returns>
public static byte[] ReadBytes(SafeMemoryHandle processHandle, IntPtr address, int size)
{
// Check if the handles are valid
HandleManipulator.ValidateAsArgument(processHandle, "processHandle");
HandleManipulator.ValidateAsArgument(address, "address");
// Allocate the buffer
var buffer = new byte[size];
int nbBytesRead;
// Read the data from the target process
if (Kernel32.ReadProcessMemory(processHandle, address, buffer, size, out nbBytesRead) && size == nbBytesRead)
return buffer;
// Else the data couldn't be read, throws an exception
throw new Win32Exception($"Couldn't read {size} byte(s) from 0x{address.ToString("X")}.");
}
/// <summary>
/// Changes the protection on a region of committed pages in the virtual address space of a specified process.
/// </summary>
/// <param name="processHandle">A handle to the process whose memory protection is to be changed.</param>
/// <param name="address">
/// A pointer to the base address of the region of pages whose access protection attributes are to be
/// changed.
/// </param>
/// <param name="size">The size of the region whose access protection attributes are changed, in bytes.</param>
/// <param name="protection">The memory protection option.</param>
/// <returns>The old protection of the region in a <see cref="MemoryBasicInformation" /> structure.</returns>
public static MemoryProtectionFlags ChangeProtection(SafeMemoryHandle processHandle, IntPtr address, int size,
MemoryProtectionFlags protection)
{
// Check if the handles are valid
HandleManipulator.ValidateAsArgument(processHandle, "processHandle");
HandleManipulator.ValidateAsArgument(address, "address");
// Create the variable storing the old protection of the memory page
MemoryProtectionFlags oldProtection;
// Change the protection in the target process
if (Kernel32.VirtualProtectEx(processHandle, address, size, protection, out oldProtection))
// Return the old protection
return oldProtection;
// Else the protection couldn't be changed, throws an exception
throw new Win32Exception(
$"Couldn't change the protection of the memory at 0x{address.ToString("X")} of {size} byte(s) to {protection}.");
}
/// <summary>
/// Retrieves information about a range of pages within the virtual address space of a specified process.
/// </summary>
/// <param name="processHandle">A handle to the process whose memory information is queried.</param>
/// <param name="baseAddress">A pointer to the base address of the region of pages to be queried.</param>
/// <returns>
/// A <see cref="MemoryBasicInformation" /> structures in which information about the specified page range is
/// returned.
/// </returns>
public static MemoryBasicInformation Query(SafeMemoryHandle processHandle, IntPtr baseAddress)
{
// Allocate the structure to store information of memory
MemoryBasicInformation memoryInfo;
// Query the memory region
if (
Kernel32.VirtualQueryEx(processHandle, baseAddress, out memoryInfo,
MarshalType<MemoryBasicInformation>.Size) != 0)
return memoryInfo;
// Else the information couldn't be got
throw new Win32Exception($"Couldn't query information about the memory region 0x{baseAddress.ToString("X")}");
}
/// <summary>
/// Retrieves information about a range of pages within the virtual address space of a specified process.
/// </summary>
/// <param name="processHandle">A handle to the process whose memory information is queried.</param>
/// <param name="addressFrom">A pointer to the starting address of the region of pages to be queried.</param>
/// <param name="addressTo">A pointer to the ending address of the region of pages to be queried.</param>
/// <returns>A collection of <see cref="MemoryBasicInformation" /> structures.</returns>
public static IEnumerable<MemoryBasicInformation> Query(SafeMemoryHandle processHandle, IntPtr addressFrom,
IntPtr addressTo)
{
// Check if the handle is valid
HandleManipulator.ValidateAsArgument(processHandle, "processHandle");
// Convert the addresses to Int64
var numberFrom = addressFrom.ToInt64();
var numberTo = addressTo.ToInt64();
// The first address must be lower than the second
if (numberFrom >= numberTo)
throw new ArgumentException("The starting address must be lower than the ending address.", "addressFrom");
// Create the variable storing the result of the call of VirtualQueryEx
int ret;
// Enumerate the memory pages
do
{
// Allocate the structure to store information of memory
MemoryBasicInformation memoryInfo;
// Get the next memory page
ret = Kernel32.VirtualQueryEx(processHandle, new IntPtr(numberFrom), out memoryInfo,
MarshalType<MemoryBasicInformation>.Size);
// Increment the starting address with the size of the page
numberFrom += memoryInfo.RegionSize;
// Return the memory page
if (memoryInfo.State != MemoryStateFlags.Free)
yield return memoryInfo;
} while (numberFrom < numberTo && ret != 0);
}
/// <summary>
/// Writes data to an area of memory in a specified process.
/// </summary>
/// <param name="processHandle">A handle to the process memory to be modified.</param>
/// <param name="address">A pointer to the base address in the specified process to which data is written.</param>
/// <param name="byteArray">A buffer that contains data to be written in the address space of the specified process.</param>
/// <returns>The number of bytes written.</returns>
public static int WriteBytes(SafeMemoryHandle processHandle, IntPtr address, byte[] byteArray)
{
// Check if the handles are valid
HandleManipulator.ValidateAsArgument(processHandle, "processHandle");
HandleManipulator.ValidateAsArgument(address, "address");
// Create the variable storing the number of bytes written
int nbBytesWritten;
// Write the data to the target process
if (Kernel32.WriteProcessMemory(processHandle, address, byteArray, byteArray.Length, out nbBytesWritten))
// Check whether the length of the data written is equal to the inital array
if (nbBytesWritten == byteArray.Length)
return nbBytesWritten;
// Else the data couldn't be written, throws an exception
throw new Win32Exception($"Couldn't write {byteArray.Length} bytes to 0x{address.ToString("X")}");
}
}
}