-
Notifications
You must be signed in to change notification settings - Fork 10
/
LoadBinaryPluginExt.cs
291 lines (245 loc) · 7.28 KB
/
LoadBinaryPluginExt.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
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Drawing;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Windows.Forms;
using ReClassNET.Core;
using ReClassNET.Debugger;
using ReClassNET.Extensions;
using ReClassNET.Memory;
using ReClassNET.Plugins;
namespace LoadBinaryPlugin
{
public class LoadBinaryPluginExt : Plugin, ICoreProcessFunctions
{
private readonly object sync = new object();
private IPluginHost host;
private string currentFile;
private Dictionary<IntPtr, MemoryMappedFileInfo> openFiles;
public override Image Icon => Properties.Resources.icon;
public override bool Initialize(IPluginHost host)
{
Contract.Requires(host != null);
this.host = host ?? throw new ArgumentNullException(nameof(host));
host.Process.CoreFunctions.RegisterFunctions("Load Binary", this);
openFiles = new Dictionary<IntPtr, MemoryMappedFileInfo>();
return true;
}
public override void Terminate()
{
foreach (var kv in openFiles)
{
kv.Value.File.Dispose();
}
openFiles.Clear();
host = null;
}
/// <summary>Gets a <see cref="MemoryMappedFileInfo"/> by its plugin internal identifier.</summary>
/// <param name="id">The identifier.</param>
/// <returns>The file or null if the identifier doesn't exist.</returns>
private MemoryMappedFileInfo GetMappedFileById(IntPtr id)
{
openFiles.TryGetValue(id, out var file);
return file;
}
/// <summary>Logs the exception and removes the file.</summary>
/// <param name="id">The identifier.</param>
/// <param name="ex">The exception.</param>
private void LogErrorAndRemoveFile(IntPtr id, Exception ex)
{
Contract.Requires(ex != null);
if (openFiles.TryGetValue(id, out var info))
{
info.File.Dispose();
}
openFiles.Remove(id);
host.Logger.Log(ex);
}
/// <summary>Queries if the file is valid.</summary>
/// <param name="process">The file to check.</param>
/// <returns>True if the file is valid, false if not.</returns>
public bool IsProcessValid(IntPtr process)
{
lock (sync)
{
return GetMappedFileById(process) != null;
}
}
/// <summary>Opens the file.</summary>
/// <param name="id">The file id.</param>
/// <param name="desiredAccess">The desired access. (ignored)</param>
/// <returns>A plugin internal handle to the file.</returns>
public IntPtr OpenRemoteProcess(IntPtr id, ProcessAccess desiredAccess)
{
lock (sync)
{
if (currentFile.GetHashCode() == id.ToInt32())
{
try
{
var mappedFile = MemoryMappedFile.CreateFromFile(currentFile);
var handle = (IntPtr)mappedFile.SafeMemoryMappedFileHandle.GetHashCode();
openFiles.Add(
handle,
new MemoryMappedFileInfo(
currentFile,
(int)new FileInfo(currentFile).Length,
mappedFile
)
);
return handle;
}
catch (Exception ex)
{
host.Logger.Log(ex);
}
}
}
return IntPtr.Zero;
}
/// <summary>Closes the file.</summary>
/// <param name="process">The file to close.</param>
public void CloseRemoteProcess(IntPtr process)
{
lock (sync)
{
if (openFiles.TryGetValue(process, out var info))
{
openFiles.Remove(process);
info.File.Dispose();
}
}
}
/// <summary>Reads memory of the file.</summary>
/// <param name="process">The process to read from.</param>
/// <param name="address">The address to read from.</param>
/// <param name="buffer">[out] The buffer to read into.</param>
/// <param name="offset">The offset into the buffer.</param>
/// <param name="size">The size of the memory to read.</param>
/// <returns>True if it succeeds, false if it fails.</returns>
public bool ReadRemoteMemory(IntPtr process, IntPtr address, ref byte[] buffer, int offset, int size)
{
lock (sync)
{
var info = GetMappedFileById(process);
if (info != null)
{
try
{
using (var stream = info.File.CreateViewStream(address.ToInt64Bits(), size))
{
stream.Read(buffer, 0, size);
return true;
}
}
catch (UnauthorizedAccessException)
{
// address + size >= file size
}
catch (Exception ex)
{
LogErrorAndRemoveFile(process, ex);
}
}
return false;
}
}
/// <summary>Not supported.</summary>
/// <param name="process">The file to write to.</param>
/// <param name="address">The address to write to.</param>
/// <param name="buffer">[in] The memory to write.</param>
/// <param name="offset">The offset into the buffer.</param>
/// <param name="size">The size of the memory to write.</param>
/// <returns>True if it succeeds, false if it fails.</returns>
public bool WriteRemoteMemory(IntPtr process, IntPtr address, ref byte[] buffer, int offset, int size)
{
// Not supported.
return false;
}
/// <summary>Opens a file browser dialog and reports the selected file.</summary>
/// <param name="callbackProcess">The callback which gets called for the selected file.</param>
public void EnumerateProcesses(EnumerateProcessCallback callbackProcess)
{
if (callbackProcess == null)
{
return;
}
using (var ofd = new OpenFileDialog())
{
ofd.Filter = "All|*.*";
if (ofd.ShowDialog() == DialogResult.OK)
{
currentFile = ofd.FileName;
var data = new EnumerateProcessData
{
Id = (IntPtr)currentFile.GetHashCode(),
Name = Path.GetFileName(currentFile),
Path = currentFile
};
callbackProcess(ref data);
}
}
}
/// <summary>Reports a single module and section for the loaded file.</summary>
/// <param name="process">The process.</param>
/// <param name="callbackSection">The callback which gets called for every section.</param>
/// <param name="callbackModule">The callback which gets called for every module.</param>
public void EnumerateRemoteSectionsAndModules(IntPtr process, EnumerateRemoteSectionCallback callbackSection, EnumerateRemoteModuleCallback callbackModule)
{
lock (sync)
{
var info = GetMappedFileById(process);
if (info != null)
{
var module = new EnumerateRemoteModuleData
{
BaseAddress = IntPtr.Zero,
Path = info.Path,
Size = (IntPtr)info.Size
};
callbackModule(ref module);
var section = new EnumerateRemoteSectionData
{
BaseAddress = IntPtr.Zero,
Size = (IntPtr)info.Size,
Type = SectionType.Image,
Category = SectionCategory.Unknown,
ModulePath = info.Path,
Name = string.Empty,
Protection = SectionProtection.Read
};
callbackSection(ref section);
}
}
}
public void ControlRemoteProcess(IntPtr process, ControlRemoteProcessAction action)
{
// Not supported.
}
public bool AttachDebuggerToProcess(IntPtr id)
{
// Not supported.
return false;
}
public void DetachDebuggerFromProcess(IntPtr id)
{
// Not supported.
}
public bool AwaitDebugEvent(ref DebugEvent evt, int timeoutInMilliseconds)
{
// Not supported.
return false;
}
public void HandleDebugEvent(ref DebugEvent evt)
{
// Not supported.
}
public bool SetHardwareBreakpoint(IntPtr id, IntPtr address, HardwareBreakpointRegister register, HardwareBreakpointTrigger trigger, HardwareBreakpointSize size, bool set)
{
// Not supported.
return false;
}
}
}