-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.cs
More file actions
199 lines (150 loc) · 8.29 KB
/
Program.cs
File metadata and controls
199 lines (150 loc) · 8.29 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
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using DebugNET;
// Avoid conflict with .Net Debugger class in System.Diagnostics
using Debugger = DebugNET.Debugger;
namespace DebugNETExample {
public static class Program {
struct Vector {
public int x, y;
}
public const string name = "DebugeeProgram";
/// <summary>
/// <para>The main entry point for the application.</para>
/// <para>This method demonstrates the usage of the debugger.</para>
/// </summary>
[STAThread]
public static void Main() {
// Get process instance.
Process[] processes = Process.GetProcessesByName(name);
Process process = processes.Length > 0 ? processes[0] : null;
// Start the process when not running.
if (process == null) process = Process.Start($"{ name }.exe");
// Attaching too fast after starting results in an exception.
Thread.Sleep(1000);
try {
// Using statement to take care of disposing the debugger.
using (Debugger debugger = new Debugger(process)) {
// Register events.
debugger.Attached += (sender, e) => Console.WriteLine($"Attached to { e.Process.ProcessName }!");
debugger.Detached += (sender, e) => Console.WriteLine($"Detached from { e.Process.ProcessName }!");
debugger.ProcessExited += (sender, e) => Console.WriteLine($"{ e.Process.ProcessName } exited!");
// Retrieve address by code.
IntPtr codeAddress = debugger.Seek($"{ name }.exe", 0x89, 0x45, 0xD0);
// Retrieve address by module-offset pair.
IntPtr address = debugger.GetAddress($"\"{ name }.exe\"+13648");
// Resolving pointers should be wrapped in a try catch block.
try {
IntPtr invalidAddress = debugger.GetAddress(IntPtr.Zero);
} catch (AccessViolationException) { // Cannot read pointer.
} catch (ArgumentException) { // Provided an invalid pointer.
}
// Allocate memory in the process with rwx access.
IntPtr memory = debugger.AllocateMemory();
debugger.WriteByte(memory, 0xFF);
debugger.FreeMemory(memory);
// Loading a library into the process.
IntPtr handle = debugger.InjectLibrary("inject.dll");
IntPtr parameter = debugger.AllocateMemory();
debugger.WriteUInt32(parameter, 22);
debugger.WriteUInt32(parameter + 4, 20);
// Executing remote functions. See inject project to see how the functions have to be defined for this to work.
Task<uint> echoTask = debugger.ExecuteRemoteFunctionAsync("inject.dll", handle, "echo", 12345678);
Task<uint> fibonacciTask = debugger.ExecuteRemoteFunctionAsync("inject.dll", handle, "fibonacci", 10);
Task<uint> addTask = debugger.ExecuteRemoteFunctionAsync("inject.dll", handle, "add", parameter);
IntPtr paramMemory = debugger.AllocateMemory();
debugger.WriteStruct(paramMemory, new Vector() { x = 2, y = 3 });
Task<uint> plus2Task = debugger.ExecuteRemoteFunctionAsync("inject.dll", handle, "plus2", paramMemory);
plus2Task.ContinueWith(t => {
Vector v = debugger.ReadStruct<Vector>((IntPtr)t.Result);
Console.WriteLine(v.x + " -- " + v.y);
});
// Tell the tasks what to do when they are done
void printTaskResult(Task<uint> t) => Console.WriteLine($"Function executed: { t.Result }");
echoTask.ContinueWith(printTaskResult);
fibonacciTask.ContinueWith(printTaskResult);
addTask.ContinueWith(printTaskResult);
// When all functions ran, release the library and the allocated memory
Task.WhenAll(echoTask, fibonacciTask, addTask).ContinueWith(t => {
debugger.FreeMemory(parameter);
debugger.FreeRemoteLibrary(handle);
});
try {
/*
Listening can be wrapped in a DebugListener class with the following members:
private CancellationTokenSource TokenSource;
private DebugListener()
public Task GetListener()
public void Cancel()
~DebugListener()
*/
// Using statement around a CancellationTokenSource
using (CancellationTokenSource tokenSource = new CancellationTokenSource()) {
// Attaching to the process.
Task listener = debugger.AttachAsync(tokenSource.Token);
// Preferred way to create a breakpoint.
debugger.Breakpoints.Add(codeAddress,
(sender, e) => Console.WriteLine(e.Context.Eax),
e => e.Context.Eax < 200);
// Stopping the debugger.
tokenSource.CancelAfter(3000);
listener.Wait();
}
} catch (AggregateException ex) {
foreach (Exception exception in ex.InnerExceptions) {
if (exception is AttachException) {
// Cannot attach to process.
Console.WriteLine(exception.Message);
} else throw exception;
}
}
}
// We can also inherit from the debugger.
// This way we can hardcode the process name and wrap many methods to simplify the whole thing.
using (var programDebugger = new DebugeeProgramDebugger()) {
programDebugger.OutputNumber += (sender, e) => Console.WriteLine(e);
programDebugger.Listen();
// Equals to:
/*
IntPtr address = programDebugger.Seek("DebugeeProgram.exe", 0x89, 0x45, 0xD0);
programDebugger.Breakpoints.Add(address, (sender, eventArgs) => {
Console.WriteLine(eventArgs.Context.Eax);
});
programDebugger.Listen();
programDebugger.Breakpoints[address].Enable(programDebugger, address);
*/
// Do work here.
programDebugger.DetachAfter(2500);
programDebugger.Listener.Wait();
}
Console.WriteLine("Done.");
Console.ReadKey();
} catch (ProcessNotFoundException ex) {
Console.WriteLine($"Process cannot be found. { ex.InnerException.Message }");
Console.ReadKey();
}
}
}
public class DebugeeProgramDebugger : Debugger {
public event EventHandler<int> OutputNumber;
public Task Listener { get; private set; }
public CancellationTokenSource TokenSource { get; private set; }
public DebugeeProgramDebugger() : base("DebugeeProgram") {}
public void Listen() {
if (!IsAttached) {
TokenSource = new CancellationTokenSource();
Listener = AttachAsync(TokenSource.Token);
}
}
public void Detach() => TokenSource.Cancel();
public void DetachAfter(int ms) => TokenSource.CancelAfter(ms);
protected override void OnAttached(AttachedEventArgs e) {
IntPtr address = Seek("DebugeeProgram.exe", 0x89, 0x45, 0xD0);
Breakpoints.Add(address, (sender, eventArgs) => {
OutputNumber?.Invoke(this, (int)eventArgs.Context.Eax);
});
}
}
}