Repository navigation
Expand file tree
/
Copy pathThreadFactory.cs
More file actions
296 lines (268 loc) · 11.2 KB
/
Copy pathThreadFactory.cs
File metadata and controls
296 lines (268 loc) · 11.2 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
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
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Process.NET.Marshaling;
using Process.NET.Native.Types;
using Process.NET.Utilities;
namespace Process.NET.Threads
{
/// <summary>
/// Class providing tools for manipulating threads.
/// </summary>
public class ThreadFactory : IThreadFactory
{
/// <summary>
/// The reference of the <see cref="_process" /> object.
/// </summary>
protected readonly IProcess _process;
/// <summary>
/// Initializes a new instance of the <see cref="ThreadFactory" /> class.
/// </summary>
/// <param name="process">The reference of the <see cref="_process" /> object.</param>
public ThreadFactory(IProcess process)
{
// Save the parameter
_process = process;
}
/// <summary>
/// Gets the main thread of the remote process.
/// </summary>
public IRemoteThread MainThread =>
new RemoteThread(_process, _nativeThreads.Aggregate((current, next) =>
next.StartTime < current.StartTime ? next : current));
/// <summary>
/// Gets the native threads from the remote process.
/// </summary>
public IEnumerable<ProcessThread> _nativeThreads
{
get
{
// Refresh the process info
_process.Native.Refresh();
// Enumerates all threads
return _process.Native.Threads.Cast<ProcessThread>();
}
}
/// <summary>
/// Gets the threads from the remote process.
/// </summary>
public IEnumerable<IRemoteThread> RemoteThreads =>
_nativeThreads.Select(t => new RemoteThread(_process, t));
/// <summary>
/// Gets the thread corresponding to an id.
/// </summary>
/// <param name="threadId">The unique identifier of the thread to get.</param>
/// <returns>A new instance of a <see cref="RemoteThread" /> class.</returns>
public IRemoteThread this[int threadId] =>
new RemoteThread(_process, _nativeThreads.FirstOrDefault(t => t.Id == threadId));
/// <summary>
/// Creates a thread that runs in the remote process.
/// </summary>
/// <param name="address">
/// A pointer to the application-defined function to be executed by the thread and represents
/// the starting address of the thread in the remote process.
/// </param>
/// <param name="parameter">A variable to be passed to the thread function.</param>
/// <param name="isStarted">Sets if the thread must be started just after being created.</param>
/// <returns>A new instance of the <see cref="RemoteThread" /> class.</returns>
public IRemoteThread Create(IntPtr address, dynamic parameter, bool isStarted = true)
{
try
{
// Marshal the parameter
var marshalledParameter = MarshalValue.Marshal(_process, parameter);
using (var threadHandle = ThreadHelper.CreateRemoteThread(_process.Handle, address, marshalledParameter.Reference,
creationFlags: ThreadCreationFlags.Suspended))
{
// Get the thread info
var threadInfo = ThreadHelper.NtQueryInformationThread(threadHandle);
// Find the managed object corresponding to this thread
// TODO (int) cast may be unnecessary and/or problematic. Suggest coming back for proper fix later
var remoteThread = _nativeThreads.FirstOrDefault(t => t.Id == (int)threadInfo.ClientId.UniqueThread);
if (remoteThread == null)
{
throw new ApplicationException("Could not find the managed object corresponding to the created thread.");
}
// Generate the thread
var result = new RemoteThread(_process, remoteThread, marshalledParameter);
// Start the thread
if (isStarted)
{
result.Resume();
}
return result;
}
}
catch (Exception ex)
{
HandleError(ex, "An error occurred while creating the thread.");
return null;
}
}
/// <summary>
/// Creates a thread that runs in the remote process.
/// </summary>
/// <param name="address">
/// A pointer to the application-defined function to be executed by the thread and represents
/// the starting address of the thread in the remote process.
/// </param>
/// <param name="isStarted">Sets if the thread must be started just after being created.</param>
/// <returns>A new instance of the <see cref="RemoteThread" /> class.</returns>
public IRemoteThread Create(IntPtr address, int parameter = 0, bool isStarted = true)
{
try
{
//Create the thread
using (var threadHandle = ThreadHelper.CreateRemoteThread(_process.Handle, address, (IntPtr)parameter,
ThreadCreationFlags.Suspended))
{
// Get the thread info
var threadInfo = ThreadHelper.NtQueryInformationThread(threadHandle);
// Find the managed object corresponding to this thread
// TODO (int) cast may be unnecessary and/or problematic. Suggest coming back for proper fix later
var remoteThread = _nativeThreads.FirstOrDefault(t => t.Id == (int)threadInfo.ClientId.UniqueThread);
if (remoteThread == null)
{
throw new ApplicationException("Could not find the managed object corresponding to the created thread.");
}
// Generate the thread
var result = new RemoteThread(_process, remoteThread);
// Start the thread
if (isStarted)
{
result.Resume();
}
return result;
}
}
catch (Exception ex)
{
HandleError(ex, "An error occurred while creating the thread.");
return null;
}
}
/// Centralized error handler
private void HandleError(Exception ex, string message)
{
Console.WriteLine(message + ": " + ex.Message);
}
/// <summary>
/// Creates a thread in the remote process and blocks the calling thread until the thread terminates.
/// </summary>
/// <param name="address">
/// A pointer to the application-defined function to be executed by the thread and represents
/// the starting address of the thread in the remote process.
/// </param>
/// <param name="parameter">A variable to be passed to the thread function.</param>
/// <returns>A new instance of the <see cref="RemoteThread" /> class.</returns>
public IRemoteThread CreateAndJoin(IntPtr address, dynamic parameter)
{
try
{
// Create the thread
var ret = Create(address, parameter);
// Wait for the end of the thread
ret.Join();
// Return the thread
return ret;
}
catch (Exception ex)
{
// TODO keeps catching a 64bit to 32bit access error (code 5)
// https://stackoverflow.com/questions/34368724/createremotethread-error-5
HandleError(ex, "An error occurred while creating and joining the thread.");
return null;
}
}
/// <summary>
/// Creates a thread in the remote process and blocks the calling thread until the thread terminates.
/// </summary>
/// <param name="address">
/// A pointer to the application-defined function to be executed by the thread and represents
/// the starting address of the thread in the remote process.
/// </param>
/// <returns>A new instance of the <see cref="RemoteThread" /> class.</returns>
public IRemoteThread CreateAndJoin(IntPtr address)
{
try
{
// Create the thread
var ret = Create(address);
// Wait the end of the thread
ret.Join();
// Return the thread
return ret;
}
catch (Exception ex)
{
HandleError(ex, "An error occurred while creating and joining the thread.");
return null;
}
}
/// <summary>
/// Releases all resources used by the <see cref="ThreadFactory" /> object.
/// </summary>
public void Dispose()
{
// Nothing to dispose... yet
}
/// <summary>
/// Gets a thread by its id in the remote process.
/// </summary>
/// <param name="id">The id of the thread.</param>
/// <returns>A new instance of the <see cref="RemoteThread" /> class.</returns>
public IRemoteThread GetThreadById(int id)
{
if (_threadCache.TryGetValue(id, out var remoteThread))
{
return remoteThread;
}
var nativeThread = _nativeThreads.FirstOrDefault(t => t.Id == id);
if (nativeThread == null)
{
throw new ArgumentException($"Thread with id {id} not found.");
}
remoteThread = new RemoteThread(_process, nativeThread);
_threadCache[id] = remoteThread;
return remoteThread;
}
private readonly Dictionary<int, IRemoteThread> _threadCache = new Dictionary<int, IRemoteThread>();
/// <summary>
/// Resumes all threads.
/// </summary>
public void ResumeAll()
{
Parallel.ForEach(RemoteThreads, thread =>
{
try
{
thread.Resume();
}
catch (Exception ex)
{
throw new Win32Exception($"An error occurred while resuming the thread {thread.Id}: {ex.Message}");
}
});
}
/// <summary>
/// Suspends all threads.
/// </summary>
public void SuspendAll()
{
Parallel.ForEach(RemoteThreads, thread =>
{
try
{
thread.Suspend();
}
catch (Exception ex)
{
throw new Win32Exception($"An error occurred while suspending the thread {thread.Id}: {ex.Message}");
}
});
}
}
}