Repository navigation
Expand file tree
/
Copy pathModuleFactory.cs
More file actions
208 lines (183 loc) · 8.24 KB
/
Copy pathModuleFactory.cs
File metadata and controls
208 lines (183 loc) · 8.24 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
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Process.NET.Memory;
namespace Process.NET.Modules
{
/// <summary>
/// Class providing tools for manipulating modules and libraries.
/// </summary>
public class ModuleFactory : IModuleFactory
{
/// <summary>
/// The list containing all injected modules (writable).
/// </summary>
private readonly List<InjectedModule> _internalInjectedModules;
private readonly IProcess _processPlus;
/// <summary>
/// Initializes a new instance of the <see cref="ModuleFactory" /> class.
/// </summary>
/// <param name="processPlus">The reference of the <see cref="IProcess" /> object.</param>
public ModuleFactory(IProcess processPlus)
{
// Save the parameter
_processPlus = processPlus;
// Create a list containing all injected modules
_internalInjectedModules = new List<InjectedModule>();
}
/// <summary>
/// Gets a pointer from the remote process.
/// </summary>
/// <param name="address">The address of the pointer.</param>
/// <returns>A new instance of a <see cref="IPointer" /> class.</returns>
public IPointer this[IntPtr address] => new MemoryPointer(_processPlus, address);
/// <summary>
/// Gets the main module for the remote process.
/// </summary>
public IProcessModule MainModule => FetchModule(_processPlus.Native.MainModule);
/// <summary>
/// Gets the modules that have been loaded in the remote process.
/// </summary>
public IEnumerable<IProcessModule> RemoteModules => NativeModules.Select(FetchModule);
/// <summary>
/// Gets the native modules that have been loaded in the remote process.
/// </summary>
public IEnumerable<ProcessModule> NativeModules => _processPlus.Native.Modules.Cast<ProcessModule>();
/// <summary>
/// Gets the specified module in the remote process.
/// </summary>
/// <param name="moduleName">The name of module (not case sensitive).</param>
/// <returns>A new instance of a <see cref="RemoteModule" /> class.</returns>
public IProcessModule this[string moduleName] => FetchModule(moduleName);
/// <summary>
/// Releases all resources used by the <see cref="ModuleFactory" /> object.
/// </summary>
public virtual void Dispose()
{
try
{
//TODO: This could maybe be simplified perhaps something like this
/*
foreach (var injectedModule in _internalInjectedModules.OfType<IDisposable>().Where(m => m.MustBeDisposed))
{
injectedModule.Dispose();
}
*/
// Release all injected modules which must be disposed
foreach (var injectedModule in _internalInjectedModules.Where(m => m.MustBeDisposed))
injectedModule.Dispose();
// Clean the cached functions related to this process
//TODO: this can trigger a invalid operation exception due to collection object changing
// _processPlus.Handle
foreach (
var cachedFunction in
RemoteModule.CachedFunctions.ToArray()
.Where(cachedFunction => cachedFunction.Key.Item2 == _processPlus.Handle))
RemoteModule.CachedFunctions.Remove(cachedFunction);
// Avoid the finalizer
GC.SuppressFinalize(this);
}
catch (Exception ex)
{
Console.WriteLine("Error occured trying to dispose of Process Module. e:" + ex.Message);
}
}
/// <summary>
/// A collection containing all injected modules.
/// </summary>
public IEnumerable<InjectedModule> InjectedModules => _internalInjectedModules.AsReadOnly();
/// <summary>
/// Frees the loaded dynamic-link library (DLL) module and, if necessary, decrements its reference count.
/// </summary>
/// <param name="moduleName">The name of module to eject.</param>
public void Eject(string moduleName)
{
// Fint the module to eject
var module = RemoteModules.FirstOrDefault(m => m.Name == moduleName);
// Eject the module is it's valid
if (module != null)
RemoteModule.InternalEject(_processPlus, module);
}
/// <summary>
/// Injects the specified module into the address space of the remote process.
/// </summary>
/// <param name="path">
/// The path of the module. This can be either a library module (a .dll file) or an executable module
/// (an .exe file).
/// </param>
/// <param name="mustBeDisposed">The module will be ejected when the finalizer collects the object.</param>
/// <returns>A new instance of the <see cref="InjectedModule" />class.</returns>
public InjectedModule Inject(string path, bool mustBeDisposed = true)
{
// Injects the module
var module = InjectedModule.InternalInject(_processPlus, path);
// Add the module in the list
_internalInjectedModules.Add(module);
// Return the module
return module;
}
/// <summary>
/// Frees the loaded dynamic-link library (DLL) module and, if necessary, decrements its reference count.
/// </summary>
/// <param name="module">The module to eject.</param>
public void Eject(IProcessModule module)
{
#if LAUNCH_MDA
System.Diagnostics.Debugger.Launch();
#endif
// If the module is valid
if (!module.IsValid) return;
// Find if the module is an injected one
var injected = _internalInjectedModules.FirstOrDefault(m => m.Equals(module));
if (injected != null)
_internalInjectedModules.Remove(injected);
// Eject the module
RemoteModule.InternalEject(_processPlus, module);
}
/// <summary>
/// Frees resources and perform other cleanup operations before it is reclaimed by garbage collection.
/// </summary>
~ModuleFactory()
{
Dispose();
}
/// <summary>
/// Fetches a module from the remote process.
/// </summary>
/// <param name="moduleName">
/// A module name (not case sensitive). If the file name extension is omitted, the default library
/// extension .dll is appended.
/// </param>
/// <returns>A new instance of a <see cref="RemoteModule" /> class.</returns>
public IProcessModule FetchModule(string moduleName)
{
// Convert module name with lower case
moduleName = moduleName.ToLower();
// Add the dll extension if it doesnt already have one
if (!Path.HasExtension(moduleName))
moduleName += ".dll";
// Get the matching process module
var matchingModule = NativeModules.FirstOrDefault(m => m.ModuleName.ToLower() == moduleName);
if (matchingModule == null)
{
// Return null if there's no matching module
return null;
}
// Return a new `RemoteModule` instance
return new RemoteModule(_processPlus, matchingModule);
// Fetch and return the module
//return new RemoteModule(_processPlus, NativeModules.First(m => m.ModuleName.ToLower() == moduleName));
}
/// <summary>
/// Fetches a module from the remote process.
/// </summary>
/// <param name="module">A module in the remote process.</param>
/// <returns>A new instance of a <see cref="RemoteModule" /> class.</returns>
private IProcessModule FetchModule(ProcessModule module)
{
return FetchModule(module.ModuleName);
}
}
}