diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml
index b5dbdb69..8cf5badd 100644
--- a/.github/workflows/cd.yaml
+++ b/.github/workflows/cd.yaml
@@ -20,7 +20,7 @@ jobs:
strategy:
matrix:
- targetplatform: [x64]
+ targetplatform: [x86]
runs-on: windows-latest
@@ -97,11 +97,6 @@ jobs:
env:
RuntimeIdentifier: win-${{ matrix.targetplatform }}
- - name: Create publish injector files
- run: dotnet publish .\Daybreak.Injector\Daybreak.Injector.csproj -c $env:Configuration -r $env:RuntimeIdentifier --property:SolutionDir=$env:GITHUB_WORKSPACE -p:PublishReadyToRun=true -p:PublishSingleFile=false --self-contained true -o .\Publish
- env:
- RuntimeIdentifier: win-x86
-
- name: Pack publish files
run: |
Write-Host $env
diff --git a/Daybreak.Injector/Daybreak.Injector.csproj b/Daybreak.Injector/Daybreak.Injector.csproj
deleted file mode 100644
index 96c2a433..00000000
--- a/Daybreak.Injector/Daybreak.Injector.csproj
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
- Exe
- net8.0
- enable
- AnyCPU;x86;x64
- enable
- true
- true
-
-
-
diff --git a/Daybreak.Injector/NativeMethods.cs b/Daybreak.Injector/NativeMethods.cs
deleted file mode 100644
index 8faf6d46..00000000
--- a/Daybreak.Injector/NativeMethods.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-using System.Runtime.InteropServices;
-
-namespace Daybreak.Injector;
-
-internal static class NativeMethods
-{
- [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
- public static extern IntPtr GetModuleHandle(string lpModuleName);
- [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
- public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
- [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
- public static extern IntPtr VirtualAllocEx(
- IntPtr hProcess,
- IntPtr lpAddress,
- IntPtr dwSize,
- uint dwAllocationType,
- uint dwProtect);
-
- [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
- public static extern bool VirtualFreeEx(
- IntPtr hProcess,
- IntPtr lpAddress,
- uint dwSize,
- uint dwFreeType);
- [DllImport("kernel32.dll")]
- public static extern IntPtr CreateRemoteThread(
- IntPtr hProcess,
- IntPtr lpThreadAttributes,
- uint dwStackSize,
- IntPtr lpStartAddress,
- IntPtr lpParameter,
- uint dwCreationFlags,
- out IntPtr lpThreadId);
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern uint GetExitCodeThread(IntPtr hHandle, out IntPtr dwMilliseconds);
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern bool WriteProcessMemory(
- IntPtr hProcess,
- IntPtr lpBaseAddress,
- IntPtr lpBuffer,
- int nSize,
- out IntPtr lpNumberOfBytesWritten);
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern bool ReadProcessMemory(
- IntPtr hProcess,
- IntPtr lpBaseAddress,
- IntPtr lpBuffer,
- int nSize,
- out IntPtr lpNumberOfBytesRead);
-}
diff --git a/Daybreak.Injector/Program.cs b/Daybreak.Injector/Program.cs
deleted file mode 100644
index f49b1922..00000000
--- a/Daybreak.Injector/Program.cs
+++ /dev/null
@@ -1,191 +0,0 @@
-// See https://aka.ms/new-console-template for more information
-using Daybreak.Injector;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Text;
-
-static bool InjectWithWinApi(Process process, string pathToDll)
-{
- string modulefullpath = Path.GetFullPath(pathToDll);
-
- if (!File.Exists(modulefullpath))
- {
- Console.WriteLine("Dll to inject not found");
- return false;
- }
-
- nint hKernel32 = NativeMethods.GetModuleHandle("kernel32.dll");
- if (hKernel32 == IntPtr.Zero)
- {
- Console.WriteLine("Unable to get a handle of kernel32.dll");
- return false;
- }
-
- nint hLoadLib = NativeMethods.GetProcAddress(hKernel32, "LoadLibraryW");
- if (hLoadLib == IntPtr.Zero)
- {
- Console.WriteLine("Unable to get the address of LoadLibraryW");
- return false;
- }
-
- nint hStringBuffer = NativeMethods.VirtualAllocEx(process.Handle, IntPtr.Zero, new IntPtr(2 * (modulefullpath.Length + 1)),
- 0x3000 /* MEM_COMMIT | MEM_RESERVE */, 0x4 /* PAGE_READWRITE */);
- if (hStringBuffer == IntPtr.Zero)
- {
- Console.WriteLine("Unable to allocate memory for module path");
- return false;
- }
-
- WriteWString(process, hStringBuffer, modulefullpath);
- if (ReadWString(process, hStringBuffer, 260) != modulefullpath)
- {
- Console.WriteLine("Module path string is not correct");
- return false;
- }
-
- nint hThread = NativeMethods.CreateRemoteThread(process.Handle, IntPtr.Zero, 0, hLoadLib, hStringBuffer, 0, out _);
- if (hThread == IntPtr.Zero)
- {
- Console.WriteLine("Unable to create remote thread");
- return false;
- }
-
- uint threadResult = NativeMethods.WaitForSingleObject(hThread, 30000u);
- if (threadResult is 0x102 or 0xFFFFFFFF /* WAIT_FAILED */)
- {
- Console.WriteLine($"Exception occurred while waiting for the remote thread. Result is {threadResult}");
- return false;
- }
-
- uint dllResult = NativeMethods.GetExitCodeThread(hThread, out _);
- if (dllResult == 0)
- {
- Console.WriteLine($"Injected dll returned non-success status code {dllResult}");
- return false;
- }
-
- bool memoryFreeResult = NativeMethods.VirtualFreeEx(process.Handle, hStringBuffer, 0, 0x8000 /* MEM_RELEASE */);
- if (!memoryFreeResult)
- {
- Console.WriteLine($"Failed to free dll memory");
- }
-
- return memoryFreeResult;
-}
-
-static void WriteBytes(Process process, IntPtr address, byte[] data)
-{
- int size = data.Length;
- nint buffer = Marshal.AllocHGlobal(size);
- Marshal.Copy(data, 0, buffer, size);
-
- _ = NativeMethods.WriteProcessMemory(
- process.Handle,
- address,
- buffer,
- size,
- out _);
-
- Marshal.FreeHGlobal(buffer);
-}
-
-static void WriteWString(Process process, IntPtr address, string data)
-{
- WriteBytes(process, address, Encoding.Unicode.GetBytes(data));
-}
-
-static string ReadWString(Process process, IntPtr address, int maxsize, Encoding? encoding = null)
-{
- encoding ??= Encoding.Unicode;
- byte[] rawbytes = ReadBytes(process, address, maxsize);
- if (rawbytes.Length == 0)
- {
- return "";
- }
-
- string ret = encoding.GetString(rawbytes);
- if (ret.Contains('\0'))
- {
- ret = ret[..ret.IndexOf('\0')];
- }
-
- return ret;
-}
-
-static byte[] ReadBytes(Process process, IntPtr address, int size)
-{
- nint buffer = Marshal.AllocHGlobal(size);
-
- _ = NativeMethods.ReadProcessMemory(process.Handle,
- address,
- buffer,
- size,
- out _
- );
-
- byte[] ret = new byte[size];
- Marshal.Copy(buffer, ret, 0, size);
- Marshal.FreeHGlobal(buffer);
-
- return ret;
-}
-
-Console.WriteLine("Hello, World!");
-int processId = -1;
-string filePath = string.Empty;
-for (int i = 0; i < args.Length; i++)
-{
- if (args[i] is "-p" or "--processId")
- {
- i++;
- if (!int.TryParse(args[i], out int id))
- {
- Console.WriteLine($"Unable to parce process id {args[i]}");
- return 1;
- }
-
- processId = id;
- }
- else if (args[i] is "-f" or "--filePath")
- {
- i++;
- filePath = args[i];
- }
- else
- {
- Console.WriteLine($"Unknown argument {args[i]} {args[(i + 1) % args.Length]}");
- return 1;
- }
-}
-
-if (processId is -1)
-{
- Console.WriteLine("Must specify target process with -p [id]");
- return 1;
-}
-
-if (string.IsNullOrWhiteSpace(filePath))
-{
- Console.WriteLine("Must specify dll to inject with -f [filePath]");
- return 1;
-}
-
-Process process = default!;
-try
-{
- process = Process.GetProcessById(processId);
-}
-catch
-{
- Console.WriteLine($"Failed to get process with id {processId}");
- return 1;
-}
-
-if (!InjectWithWinApi(process, filePath))
-{
- Console.WriteLine("Injection failed");
- return 1;
-}
-
-Console.WriteLine("Injection success");
-return 0;
diff --git a/Daybreak.sln b/Daybreak.sln
index 110b9e08..b07aaebc 100644
--- a/Daybreak.sln
+++ b/Daybreak.sln
@@ -34,8 +34,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Daybreak.7ZipExtractor", "Daybreak.7ZipExtractor\Daybreak.7ZipExtractor.csproj", "{CA60F9AC-A496-4DB6-970E-ECE73B051C05}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Daybreak.Injector", "Daybreak.Injector\Daybreak.Injector.csproj", "{61CFB42E-947A-48AE-9F3C-4DF685882B1D}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -72,8 +70,8 @@ Global
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Release|x86.Build.0 = Release|x86
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {4E2BB805-135D-4F02-8C53-3D8B6876D323}.Debug|x64.ActiveCfg = Debug|x86
- {4E2BB805-135D-4F02-8C53-3D8B6876D323}.Debug|x64.Build.0 = Debug|x86
+ {4E2BB805-135D-4F02-8C53-3D8B6876D323}.Debug|x64.ActiveCfg = Debug|x64
+ {4E2BB805-135D-4F02-8C53-3D8B6876D323}.Debug|x64.Build.0 = Debug|x64
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Debug|x86.ActiveCfg = Debug|x86
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Debug|x86.Build.0 = Debug|x86
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -84,28 +82,16 @@ Global
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Release|x86.Build.0 = Release|x86
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|x64.ActiveCfg = Debug|x64
- {CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|x64.Build.0 = Debug|x64
+ {CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|x64.Build.0 = Debug|Any CPU
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|x86.ActiveCfg = Debug|x64
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|x86.Build.0 = Debug|x64
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Release|Any CPU.Build.0 = Release|Any CPU
- {CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Release|x64.ActiveCfg = Release|x64
- {CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Release|x64.Build.0 = Release|x64
+ {CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Release|x64.ActiveCfg = Release|Any CPU
+ {CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Release|x64.Build.0 = Release|Any CPU
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Release|x86.ActiveCfg = Release|x64
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Release|x86.Build.0 = Release|x64
- {61CFB42E-947A-48AE-9F3C-4DF685882B1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {61CFB42E-947A-48AE-9F3C-4DF685882B1D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {61CFB42E-947A-48AE-9F3C-4DF685882B1D}.Debug|x64.ActiveCfg = Debug|x86
- {61CFB42E-947A-48AE-9F3C-4DF685882B1D}.Debug|x64.Build.0 = Debug|x86
- {61CFB42E-947A-48AE-9F3C-4DF685882B1D}.Debug|x86.ActiveCfg = Debug|x86
- {61CFB42E-947A-48AE-9F3C-4DF685882B1D}.Debug|x86.Build.0 = Debug|x86
- {61CFB42E-947A-48AE-9F3C-4DF685882B1D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {61CFB42E-947A-48AE-9F3C-4DF685882B1D}.Release|Any CPU.Build.0 = Release|Any CPU
- {61CFB42E-947A-48AE-9F3C-4DF685882B1D}.Release|x64.ActiveCfg = Release|Any CPU
- {61CFB42E-947A-48AE-9F3C-4DF685882B1D}.Release|x64.Build.0 = Release|Any CPU
- {61CFB42E-947A-48AE-9F3C-4DF685882B1D}.Release|x86.ActiveCfg = Release|x86
- {61CFB42E-947A-48AE-9F3C-4DF685882B1D}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Daybreak/Daybreak.csproj b/Daybreak/Daybreak.csproj
index 767f9788..86e9ab7b 100644
--- a/Daybreak/Daybreak.csproj
+++ b/Daybreak/Daybreak.csproj
@@ -12,7 +12,7 @@
preview
Daybreak.ico
true
- 0.9.8.158
+ 0.9.8.159
true
cfb2a489-db80-448d-a969-80270f314c46
True
diff --git a/Daybreak/Services/Injection/ProcessInjector.cs b/Daybreak/Services/Injection/ProcessInjector.cs
index d269eac0..9bc6781a 100644
--- a/Daybreak/Services/Injection/ProcessInjector.cs
+++ b/Daybreak/Services/Injection/ProcessInjector.cs
@@ -1,8 +1,12 @@
-using Microsoft.Extensions.Logging;
+using Daybreak.Utils;
+using Microsoft.Extensions.Logging;
+using System;
using System.Core.Extensions;
using System.Diagnostics;
using System.Extensions;
using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -17,51 +21,138 @@ internal sealed class ProcessInjector : IProcessInjector
this.logger = logger.ThrowIfNull();
}
- public async Task Inject(Process process, string pathToDll, CancellationToken cancellationToken)
+ public Task Inject(Process process, string pathToDll, CancellationToken cancellationToken)
{
- return await this.InjectWithWinApi(process, pathToDll, cancellationToken);
+ return Task.Factory.StartNew(() =>
+ {
+ return this.InjectWithWinApi(process, pathToDll);
+ }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
}
- private async Task InjectWithWinApi(Process process, string pathToDll, CancellationToken cancellationToken)
+ private bool InjectWithWinApi(Process process, string pathToDll)
{
var scopedLogger = this.logger.CreateScopedLogger(nameof(InjectWithWinApi), pathToDll);
- if (!File.Exists(pathToDll))
+ var modulefullpath = Path.GetFullPath(pathToDll);
+
+ if (!File.Exists(modulefullpath))
{
- scopedLogger.LogError("File does not exist");
+ scopedLogger.LogError("Dll to inject not found");
return false;
}
- if (process is null)
+ var hKernel32 = NativeMethods.GetModuleHandle("kernel32.dll");
+ if (hKernel32 == IntPtr.Zero)
{
- scopedLogger.LogError("Provided process is null");
+ scopedLogger.LogError("Unable to get a handle of kernel32.dll");
return false;
}
- var injectionProcess = new Process()
+ var hLoadLib = NativeMethods.GetProcAddress(hKernel32, "LoadLibraryW");
+ if (hLoadLib == IntPtr.Zero)
{
- StartInfo = new ProcessStartInfo
- {
- CreateNoWindow = true,
- RedirectStandardError = true,
- RedirectStandardInput = true,
- RedirectStandardOutput = true,
- FileName = Path.GetFullPath("Daybreak.Injector.exe"),
- UseShellExecute = false,
- Arguments = $"-p {process.Id} -f {pathToDll}"
- }
- };
-
- injectionProcess.Start();
- await injectionProcess.WaitForExitAsync(cancellationToken);
- var output = await injectionProcess.StandardOutput.ReadToEndAsync(cancellationToken);
- var error = await injectionProcess.StandardError.ReadToEndAsync(cancellationToken);
- if (injectionProcess.ExitCode != 0)
- {
- scopedLogger.LogError($"Failed to inject\nOutput: {output}\nError: {error}");
+ scopedLogger.LogError("Unable to get the address of LoadLibraryW");
return false;
}
- scopedLogger.LogInformation("Injected");
- return true;
+ var hStringBuffer = NativeMethods.VirtualAllocEx(process.Handle, IntPtr.Zero, new IntPtr(2 * (modulefullpath.Length + 1)),
+ 0x3000 /* MEM_COMMIT | MEM_RESERVE */, 0x4 /* PAGE_READWRITE */);
+ if (hStringBuffer == IntPtr.Zero)
+ {
+ scopedLogger.LogError("Unable to allocate memory for module path");
+ return false;
+ }
+
+ WriteWString(process, hStringBuffer, modulefullpath);
+ if (ReadWString(process, hStringBuffer, 260) != modulefullpath)
+ {
+ scopedLogger.LogError("Module path string is not correct");
+ return false;
+ }
+
+ var hThread = NativeMethods.CreateRemoteThread(process.Handle, IntPtr.Zero, 0, hLoadLib, hStringBuffer, 0, out _);
+ if (hThread == IntPtr.Zero)
+ {
+ scopedLogger.LogError("Unable to create remote thread");
+ return false;
+ }
+
+ var threadResult = NativeMethods.WaitForSingleObject(hThread, 30000u);
+ if (threadResult is 0x102 or 0xFFFFFFFF /* WAIT_FAILED */)
+ {
+ scopedLogger.LogError($"Exception occurred while waiting for the remote thread. Result is {threadResult}");
+ return false;
+ }
+
+ var dllResult = NativeMethods.GetExitCodeThread(hThread, out _);
+ if (dllResult == 0)
+ {
+ scopedLogger.LogError($"Injected dll returned non-success status code {dllResult}");
+ return false;
+ }
+
+ var memoryFreeResult = NativeMethods.VirtualFreeEx(process.Handle, hStringBuffer, 0, 0x8000 /* MEM_RELEASE */);
+ if (!memoryFreeResult)
+ {
+ scopedLogger.LogError($"Failed to free dll memory");
+ }
+
+ return memoryFreeResult;
+ }
+
+ private static void WriteBytes(Process process, IntPtr address, byte[] data)
+ {
+ var size = data.Length;
+ var buffer = Marshal.AllocHGlobal(size);
+ Marshal.Copy(data, 0, buffer, size);
+
+ NativeMethods.WriteProcessMemory(
+ process.Handle,
+ address,
+ buffer,
+ size,
+ out _);
+
+ Marshal.FreeHGlobal(buffer);
+ }
+
+ private static void WriteWString(Process process, IntPtr address, string data)
+ {
+ WriteBytes(process, address, Encoding.Unicode.GetBytes(data));
+ }
+
+ private static string ReadWString(Process process, IntPtr address, int maxsize, Encoding? encoding = null)
+ {
+ encoding ??= Encoding.Unicode;
+ var rawbytes = ReadBytes(process, address, maxsize);
+ if (rawbytes.Length == 0)
+ {
+ return "";
+ }
+
+ var ret = encoding.GetString(rawbytes);
+ if (ret.Contains('\0'))
+ {
+ ret = ret[..ret.IndexOf('\0')];
+ }
+
+ return ret;
+ }
+
+ private static byte[] ReadBytes(Process process, IntPtr address, int size)
+ {
+ var buffer = Marshal.AllocHGlobal(size);
+
+ NativeMethods.ReadProcessMemory(process.Handle,
+ address,
+ buffer,
+ size,
+ out _
+ );
+
+ var ret = new byte[size];
+ Marshal.Copy(buffer, ret, 0, size);
+ Marshal.FreeHGlobal(buffer);
+
+ return ret;
}
}