-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathInjector.cs
More file actions
467 lines (391 loc) · 19.3 KB
/
Injector.cs
File metadata and controls
467 lines (391 loc) · 19.3 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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace Trinity
{
/// <summary>
/// Mono DLL Injector for Unity games
/// Injects TrinityMenu.dll into the Mono runtime of Unity games
/// Uses shellcode injection to call Mono API functions
/// </summary>
public static class Injector
{
#region Native Methods
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, IntPtr dwSize, uint dwFreeType);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, IntPtr nSize, out IntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, IntPtr nSize, out IntPtr lpNumberOfBytesRead);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, IntPtr dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out IntPtr lpThreadId);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetExitCodeThread(IntPtr hThread, out IntPtr lpExitCode);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, IntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
[DllImport("psapi.dll", SetLastError = true)]
private static extern bool EnumProcessModulesEx(IntPtr hProcess, [Out] IntPtr[] lphModule, uint cb, out uint lpcbNeeded, uint dwFilterFlag);
[DllImport("psapi.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern uint GetModuleBaseName(IntPtr hProcess, IntPtr hModule, StringBuilder lpBaseName, uint nSize);
private const uint PROCESS_ALL_ACCESS = 0x1F0FFF;
private const uint MEM_COMMIT = 0x1000;
private const uint MEM_RESERVE = 0x2000;
private const uint MEM_RELEASE = 0x8000;
private const uint PAGE_EXECUTE_READWRITE = 0x40;
private const uint PAGE_READWRITE = 0x04;
private const uint INFINITE = 0xFFFFFFFF;
private const uint LIST_MODULES_ALL = 0x03;
#endregion
#region Console Colors
private static void WriteColored(string text, ConsoleColor color)
{
Console.ForegroundColor = color;
Console.Write(text);
Console.ResetColor();
}
private static void WriteLineColored(string text, ConsoleColor color)
{
Console.ForegroundColor = color;
Console.WriteLine(text);
Console.ResetColor();
}
private static void PrintBanner()
{
Console.Clear();
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine(@"
████████╗██████╗ ██╗███╗ ██╗██╗████████╗██╗ ██╗
╚══██╔══╝██╔══██╗██║████╗ ██║██║╚══██╔══╝╚██╗ ██╔╝
██║ ██████╔╝██║██╔██╗ ██║██║ ██║ ╚████╔╝
██║ ██╔══██╗██║██║╚██╗██║██║ ██║ ╚██╔╝
██║ ██║ ██║██║██║ ╚████║██║ ██║ ██║
╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝
");
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(" ═══════════════════════════════════════");
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine(" Mono Injector for Unity Games");
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine(" MDickie Infinite Lives");
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(" ═══════════════════════════════════════");
Console.ResetColor();
Console.WriteLine();
}
private static void PrintStatus(string label, string value, ConsoleColor valueColor = ConsoleColor.White)
{
WriteColored(" [", ConsoleColor.DarkGray);
WriteColored("*", ConsoleColor.Yellow);
WriteColored("] ", ConsoleColor.DarkGray);
WriteColored(label + ": ", ConsoleColor.Gray);
WriteLineColored(value, valueColor);
}
private static void PrintSuccess(string message)
{
WriteColored(" [", ConsoleColor.DarkGray);
WriteColored("+", ConsoleColor.Green);
WriteColored("] ", ConsoleColor.DarkGray);
WriteLineColored(message, ConsoleColor.Green);
}
private static void PrintError(string message)
{
WriteColored(" [", ConsoleColor.DarkGray);
WriteColored("!", ConsoleColor.Red);
WriteColored("] ", ConsoleColor.DarkGray);
WriteLineColored(message, ConsoleColor.Red);
}
private static void PrintInfo(string message)
{
WriteColored(" [", ConsoleColor.DarkGray);
WriteColored("i", ConsoleColor.Cyan);
WriteColored("] ", ConsoleColor.DarkGray);
WriteLineColored(message, ConsoleColor.Cyan);
}
private static void PrintWarning(string message)
{
WriteColored(" [", ConsoleColor.DarkGray);
WriteColored("!", ConsoleColor.Yellow);
WriteColored("] ", ConsoleColor.DarkGray);
WriteLineColored(message, ConsoleColor.Yellow);
}
private static void PrintStep(string message)
{
WriteColored(" [", ConsoleColor.DarkGray);
WriteColored("→", ConsoleColor.Magenta);
WriteColored("] ", ConsoleColor.DarkGray);
WriteLineColored(message, ConsoleColor.White);
}
#endregion
#region Mono Injection
private static IntPtr FindMonoModule(IntPtr hProcess)
{
IntPtr[] modules = new IntPtr[1024];
uint cbNeeded;
if (!EnumProcessModulesEx(hProcess, modules, (uint)(modules.Length * IntPtr.Size), out cbNeeded, LIST_MODULES_ALL))
return IntPtr.Zero;
int moduleCount = (int)(cbNeeded / IntPtr.Size);
StringBuilder moduleName = new StringBuilder(260);
for (int i = 0; i < moduleCount; i++)
{
if (modules[i] == IntPtr.Zero) continue;
moduleName.Clear();
if (GetModuleBaseName(hProcess, modules[i], moduleName, 260) > 0)
{
string name = moduleName.ToString().ToLower();
if (name.Contains("mono") && name.EndsWith(".dll"))
{
return modules[i];
}
}
}
return IntPtr.Zero;
}
private static IntPtr GetMonoExport(IntPtr hProcess, IntPtr monoModule, string exportName)
{
// Read DOS header
byte[] dosHeader = new byte[64];
if (!ReadProcessMemory(hProcess, monoModule, dosHeader, (IntPtr)64, out _))
return IntPtr.Zero;
int peOffset = BitConverter.ToInt32(dosHeader, 60);
// Read PE header
byte[] peHeader = new byte[24];
if (!ReadProcessMemory(hProcess, monoModule + peOffset, peHeader, (IntPtr)24, out _))
return IntPtr.Zero;
bool is64Bit = BitConverter.ToUInt16(peHeader, 4) == 0x8664;
int optionalHeaderSize = BitConverter.ToUInt16(peHeader, 20);
// Read optional header to get export directory RVA
byte[] optionalHeader = new byte[optionalHeaderSize];
if (!ReadProcessMemory(hProcess, monoModule + peOffset + 24, optionalHeader, (IntPtr)optionalHeaderSize, out _))
return IntPtr.Zero;
int exportDirRva = BitConverter.ToInt32(optionalHeader, is64Bit ? 112 : 96);
if (exportDirRva == 0)
return IntPtr.Zero;
// Read export directory
byte[] exportDir = new byte[40];
if (!ReadProcessMemory(hProcess, monoModule + exportDirRva, exportDir, (IntPtr)40, out _))
return IntPtr.Zero;
int numFunctions = BitConverter.ToInt32(exportDir, 20);
int numNames = BitConverter.ToInt32(exportDir, 24);
int functionsRva = BitConverter.ToInt32(exportDir, 28);
int namesRva = BitConverter.ToInt32(exportDir, 32);
int ordinalsRva = BitConverter.ToInt32(exportDir, 36);
// Read name pointers
byte[] namePointers = new byte[numNames * 4];
if (!ReadProcessMemory(hProcess, monoModule + namesRva, namePointers, (IntPtr)namePointers.Length, out _))
return IntPtr.Zero;
// Read ordinals
byte[] ordinals = new byte[numNames * 2];
if (!ReadProcessMemory(hProcess, monoModule + ordinalsRva, ordinals, (IntPtr)ordinals.Length, out _))
return IntPtr.Zero;
// Read function pointers
byte[] functions = new byte[numFunctions * 4];
if (!ReadProcessMemory(hProcess, monoModule + functionsRva, functions, (IntPtr)functions.Length, out _))
return IntPtr.Zero;
// Search for export
for (int i = 0; i < numNames; i++)
{
int nameRva = BitConverter.ToInt32(namePointers, i * 4);
byte[] nameBuffer = new byte[256];
if (!ReadProcessMemory(hProcess, monoModule + nameRva, nameBuffer, (IntPtr)256, out _))
continue;
string name = Encoding.ASCII.GetString(nameBuffer).Split('\0')[0];
if (name == exportName)
{
int ordinal = BitConverter.ToUInt16(ordinals, i * 2);
int funcRva = BitConverter.ToInt32(functions, ordinal * 4);
return monoModule + funcRva;
}
}
return IntPtr.Zero;
}
private static IntPtr ExecuteRemoteFunction(IntPtr hProcess, IntPtr funcAddr, IntPtr arg)
{
IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, IntPtr.Zero, funcAddr, arg, 0, out _);
if (hThread == IntPtr.Zero)
return IntPtr.Zero;
WaitForSingleObject(hThread, INFINITE);
GetExitCodeThread(hThread, out IntPtr exitCode);
CloseHandle(hThread);
return exitCode;
}
public static bool InjectMono(int processId, string dllPath, string namespaceName, string className, string methodName)
{
if (!File.Exists(dllPath))
{
PrintError($"DLL not found: {dllPath}");
return false;
}
string fullPath = Path.GetFullPath(dllPath);
// First copy DLL to game folder
try
{
Process process = Process.GetProcessById(processId);
string gameDir = Path.GetDirectoryName(process.MainModule.FileName);
string targetDll = Path.Combine(gameDir, "TrinityMenu.dll");
File.Copy(fullPath, targetDll, true);
fullPath = targetDll;
PrintSuccess($"Copied DLL to game folder");
}
catch (Exception ex)
{
PrintError($"Failed to copy DLL: {ex.Message}");
return false;
}
PrintStep("Initializing Mono injector...");
using (var injector = new MonoInjector())
{
Process targetProcess = Process.GetProcessById(processId);
if (!injector.Attach(targetProcess))
{
PrintError("Failed to attach to process");
return false;
}
PrintSuccess("Attached to process");
PrintStep("Resolving Mono exports...");
IntPtr mono_get_root_domain = injector.GetExport("mono_get_root_domain");
if (mono_get_root_domain == IntPtr.Zero)
{
PrintError("Failed to find mono_get_root_domain");
return false;
}
PrintSuccess("Mono exports resolved");
PrintStep("Injecting assembly...");
bool result = injector.Inject(fullPath, namespaceName, className, methodName);
if (result)
{
PrintSuccess("Assembly injected successfully!");
return true;
}
else
{
PrintError("Injection failed");
return false;
}
}
}
private static bool SetupDoorstop(string dllPath, int processId)
{
try
{
Process process = Process.GetProcessById(processId);
string gameDir = Path.GetDirectoryName(process.MainModule.FileName);
PrintStep("Setting up Unity Doorstop...");
// Copy DLL to game directory
string targetDll = Path.Combine(gameDir, "TrinityMenu.dll");
File.Copy(dllPath, targetDll, true);
PrintSuccess($"Copied DLL to {targetDll}");
// Create doorstop config
string configPath = Path.Combine(gameDir, "doorstop_config.ini");
string config = @"[General]
enabled=true
target_assembly=TrinityMenu.dll
redirect_output_log=false
ignore_disable_switch=false
[UnityMono]
dll_search_path_override=
debug_enabled=false
debug_address=127.0.0.1:10000
debug_suspend=false
";
File.WriteAllText(configPath, config);
PrintSuccess("Created doorstop_config.ini");
// Check if winhttp.dll exists
string winhttpPath = Path.Combine(gameDir, "winhttp.dll");
if (!File.Exists(winhttpPath))
{
PrintWarning("winhttp.dll not found in game folder!");
Console.WriteLine();
PrintInfo("To complete setup:");
WriteLineColored(" 1. Download Unity Doorstop from:", ConsoleColor.White);
WriteLineColored(" https://github.com/NeighTools/UnityDoorstop/releases", ConsoleColor.Cyan);
WriteLineColored(" 2. Extract winhttp.dll to the game folder:", ConsoleColor.White);
WriteLineColored($" {gameDir}", ConsoleColor.Yellow);
WriteLineColored(" 3. Restart the game", ConsoleColor.White);
return false;
}
PrintSuccess("Unity Doorstop is configured!");
Console.WriteLine();
PrintInfo("Restart the game for changes to take effect");
PrintInfo("Press INSERT or F1 in-game to open the menu");
return true;
}
catch (Exception ex)
{
PrintError($"Setup failed: {ex.Message}");
return false;
}
}
#endregion
/// <summary>
/// Main entry point
/// </summary>
public static void Main(string[] args)
{
Console.Title = "Trinity Injector";
PrintBanner();
string processName = "Infinite Lives";
string dllPath = "TrinityMenu.dll";
if (args.Length >= 1)
dllPath = args[0];
if (args.Length >= 2)
processName = args[1];
// Find DLL
if (!File.Exists(dllPath))
{
string exeDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
string altPath = Path.Combine(exeDir, "TrinityMenu.dll");
if (File.Exists(altPath))
dllPath = altPath;
}
PrintStatus("Target Process", processName, ConsoleColor.Cyan);
PrintStatus("DLL Path", Path.GetFullPath(dllPath), ConsoleColor.Cyan);
Console.WriteLine();
// Find process
PrintStep("Searching for game process...");
Process[] processes = Process.GetProcessesByName(processName);
if (processes.Length == 0)
{
PrintError($"Process '{processName}' not found!");
PrintInfo("Make sure the game is running before injecting");
Console.WriteLine();
WriteColored(" Press any key to exit...", ConsoleColor.DarkGray);
Console.ReadKey();
return;
}
Process targetProcess = processes[0];
PrintSuccess($"Found process (PID: {targetProcess.Id})");
Console.WriteLine();
// Inject
bool success = InjectMono(targetProcess.Id, dllPath, "Trinity", "Loader", "Init");
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(" ═══════════════════════════════════════════════════");
Console.ResetColor();
if (success)
{
WriteLineColored(" Injection Complete!", ConsoleColor.Green);
}
else
{
WriteLineColored(" Setup Complete - Restart Game", ConsoleColor.Yellow);
}
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(" ═══════════════════════════════════════════════════");
Console.ResetColor();
Console.WriteLine();
WriteColored(" Press any key to exit...", ConsoleColor.DarkGray);
Console.ReadKey();
}
}
}