forked from XIV-Tools/CustomizePlus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
MemoryService.cs
304 lines (240 loc) · 8.43 KB
/
MemoryService.cs
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
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
// © Anamnesis.
// Developed by W and A Walsh.
// Licensed under the MIT license.
namespace Anamnesis.Memory
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Anamnesis.Core.Memory;
using Serilog;
public static class MemoryService
{
private static readonly Dictionary<string, IntPtr> modules = new Dictionary<string, IntPtr>();
public static IntPtr Handle { get; private set; }
public static SignatureScanner? Scanner { get; private set; }
public static Process? Process { get; private set; }
public static bool IsProcessAlive { get; private set; }
public static string GamePath
{
get
{
if (Process == null)
throw new Exception("No game process");
if (Process.MainModule == null)
throw new Exception("Process has no main module");
return Path.GetDirectoryName(Process.MainModule.FileName) + "\\..\\";
}
}
public static bool GetIsProcessAlive()
{
if (Process == null || Process.HasExited)
return false;
if (!Process.Responding)
return false;
return true;
}
public static IntPtr ReadPtr(IntPtr address)
{
byte[] d = new byte[8];
ReadProcessMemory(Handle, address, d, 8, out _);
long i = BitConverter.ToInt64(d, 0);
IntPtr ptr = (IntPtr)i;
return ptr;
}
public static T? Read<T>(UIntPtr address)
where T : struct
{
unsafe
{
IntPtr ptr = (IntPtr)address.ToPointer();
return Read<T>(ptr);
}
}
public static T Read<T>(IntPtr address)
where T : struct
{
if (address == IntPtr.Zero)
throw new Exception("Invalid address");
int attempt = 0;
while (attempt < 10)
{
int size = Marshal.SizeOf(typeof(T));
IntPtr mem = Marshal.AllocHGlobal(size);
ReadProcessMemory(Handle, address, mem, size, out _);
T? val = Marshal.PtrToStructure<T>(mem);
Marshal.FreeHGlobal(mem);
attempt++;
if (val != null)
return (T)val;
Thread.Sleep(100);
}
throw new Exception($"Failed to read memory {typeof(T)} from address {address}");
}
public static void Write<T>(IntPtr address, T value)
where T : struct
{
if (address == IntPtr.Zero)
return;
// Read the existing memory to oldBuffer
int size = Marshal.SizeOf(typeof(T));
////byte[] oldBuffer = new byte[size];
////ReadProcessMemory(Handle, address, oldBuffer, size, out _);
// Marshal the struct to newBuffer
byte[] newbuffer = new byte[size];
IntPtr mem = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr<T>(value, mem, false);
Marshal.Copy(mem, newbuffer, 0, size);
Marshal.FreeHGlobal(mem);
// Write the oldBuffer (which has now had newBuffer merged over it) to the process
WriteProcessMemory(Handle, address, newbuffer, size, out _);
}
public static bool Read(UIntPtr address, byte[] buffer, UIntPtr size)
{
return ReadProcessMemory(Handle, address, buffer, size, IntPtr.Zero);
}
public static bool Read(IntPtr address, byte[] buffer, int size = -1)
{
if (size <= 0)
size = buffer.Length;
return ReadProcessMemory(Handle, address, buffer, size, out _);
}
public static bool Write(IntPtr address, byte[] buffer)
{
return WriteProcessMemory(Handle, address, buffer, buffer.Length, out _);
}
/// <summary>
/// Open the PC game process with all security and access rights.
/// </summary>
private static void OpenProcess(Process process)
{
Process = process;
if (!Process.Responding)
throw new Exception("Target process id not responding");
if (process.MainModule == null)
throw new Exception("Process has no main module");
Process.EnterDebugMode();
int debugPrivilegeCheck = CheckSeDebugPrivilege(out bool isDebugEnabled);
if (debugPrivilegeCheck != 0)
{
throw new Exception($"ERROR: CheckSeDebugPrivilege failed with error: {debugPrivilegeCheck}");
}
else if (!isDebugEnabled)
{
throw new Exception("ERROR: SeDebugPrivilege not enabled. Please report this!");
}
Handle = OpenProcess(0x001F0FFF, true, process.Id);
if (Handle == IntPtr.Zero)
{
int eCode = Marshal.GetLastWin32Error();
}
// Set all modules
modules.Clear();
foreach (ProcessModule? module in Process.Modules)
{
if (module == null)
continue;
if (string.IsNullOrEmpty(module.ModuleName))
continue;
if (modules.ContainsKey(module.ModuleName))
continue;
modules.Add(module.ModuleName, module.BaseAddress);
}
Scanner = new SignatureScanner(process.MainModule);
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int processId);
[DllImport("kernel32.dll")]
private static extern bool IsWow64Process(IntPtr hProcess, out bool lpSystemInfo);
[DllImport("kernel32.dll")]
private static extern bool ReadProcessMemory(IntPtr hProcess, UIntPtr lpBaseAddress, [Out] byte[] lpBuffer, UIntPtr nSize, IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll")]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll")]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll")]
private static extern bool WriteProcessMemory(IntPtr hProcess, UIntPtr lpBaseAddress, byte[] lpBuffer, UIntPtr nSize, out IntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
private static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out IntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
private static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int dwSize, out IntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool OpenProcessToken(IntPtr processHandle, uint desiredAccess, out IntPtr tokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LookupPrivilegeValue(string? lpSystemName, string lpName, ref LUID lpLuid);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool PrivilegeCheck(IntPtr clientToken, ref PRIVILEGE_SET requiredPrivileges, out bool pfResult);
[DllImport("kernel32.dll")]
private static extern int CloseHandle(IntPtr hObject);
private static int CheckSeDebugPrivilege(out bool isDebugEnabled)
{
isDebugEnabled = false;
if (!OpenProcessToken(GetCurrentProcess(), 0x8 /*TOKEN_QUERY*/, out IntPtr tokenHandle))
return Marshal.GetLastWin32Error();
LUID luidDebugPrivilege = default;
if (!LookupPrivilegeValue(null, "SeDebugPrivilege", ref luidDebugPrivilege))
return Marshal.GetLastWin32Error();
PRIVILEGE_SET requiredPrivileges = new PRIVILEGE_SET
{
PrivilegeCount = 1,
Control = 1 /* PRIVILEGE_SET_ALL_NECESSARY */,
Privilege = new LUID_AND_ATTRIBUTES[1],
};
requiredPrivileges.Privilege[0].Luid = luidDebugPrivilege;
requiredPrivileges.Privilege[0].Attributes = 2 /* SE_PRIVILEGE_ENABLED */;
if (!PrivilegeCheck(tokenHandle, ref requiredPrivileges, out bool bResult))
return Marshal.GetLastWin32Error();
// bResult == true => SeDebugPrivilege is on; otherwise it's off
isDebugEnabled = bResult;
CloseHandle(tokenHandle);
return 0;
}
public static void GetProcess()
{
Process[] processes = Process.GetProcesses();
Process? proc = null;
// Search for ffxiv process
foreach (Process process in processes)
{
if (process.ProcessName.ToLower().Contains("ffxiv_dx11"))
{
proc = process;
}
}
// if still no process, shutdown.
if (proc == null)
{
throw new Exception("No process");
}
OpenProcess(proc);
IsProcessAlive = true;
}
[StructLayout(LayoutKind.Sequential)]
private struct LUID
{
public uint LowPart;
public int HighPart;
}
[StructLayout(LayoutKind.Sequential)]
private struct PRIVILEGE_SET
{
public uint PrivilegeCount;
public uint Control;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public LUID_AND_ATTRIBUTES[] Privilege;
}
private struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public uint Attributes;
}
}
}