diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index 0a1bca33..8af89fc3 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=true --self-contained true -o .\Publish - env: - RuntimeIdentifier: win-x86 - - name: Pack publish files run: | Write-Host $env diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2d174def..a4f0493f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -19,7 +19,7 @@ jobs: strategy: matrix: - targetplatform: [x64] + targetplatform: [x86] runs-on: windows-latest diff --git a/Daybreak.Injector/Daybreak.Injector.csproj b/Daybreak.Injector/Daybreak.Injector.csproj deleted file mode 100644 index 28794419..00000000 --- a/Daybreak.Injector/Daybreak.Injector.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - Exe - net8.0 - Daybreak.Injector - enable - enable - AnyCPU;x86 - True - - - - - - - - - diff --git a/Daybreak.Injector/NativeMethods.cs b/Daybreak.Injector/NativeMethods.cs deleted file mode 100644 index 74531eb3..00000000 --- a/Daybreak.Injector/NativeMethods.cs +++ /dev/null @@ -1,469 +0,0 @@ -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; -using System.Text; - -namespace Daybreak.Injector; - -internal static class NativeMethods -{ - public static uint WM_KEYDOWN = 0x0100; - public static uint SWP_SHOWWINDOW = 0x0040; - public static IntPtr HWND_TOPMOST = new(-1); - public static IntPtr HWND_TOP = IntPtr.Zero; - public const int WH_KEYBOARD_LL = 13; - public const uint LIST_MODULES_32BIT = 0x01; - - public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam); - - [StructLayout(LayoutKind.Sequential)] - internal struct ProcessEntry32 - { - public uint dwSize; - public uint cntUsage; - public uint th32ProcessID; - public IntPtr th32DefaultHeapID; - public uint th32ModuleID; - public uint cntThreads; - public uint th32ParentProcessID; - public int pcPriClassBase; - public uint dwFlags; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] - public string szExeFile; - }; - [StructLayout(LayoutKind.Sequential)] - internal struct PEB - { - private readonly byte InheritedAddressSpace; - private readonly byte ReadImageFileExecOptions; - private readonly byte BeingDebugged; - private readonly byte BitField; - private readonly IntPtr Mutant; - internal IntPtr ImageBaseAddress; - } - [StructLayout(LayoutKind.Sequential)] - public struct SecurityAttributes - { - public uint nLength; - public IntPtr lpSecurityDescriptor; - public bool bInheritHandle; - } - [Flags] - public enum MemoryProtection : uint - { - PAGE_EXECUTE = 0x10, - PAGE_EXECUTE_READ = 0x20, - PAGE_EXECUTE_READ_WRITE = 0x40, - PAGE_EXECUTE_WRITECOPY = 0x80, - PAGE_NOACCESS = 0x01, - PAGE_READONLY = 0x02, - PAGE_READWRITE = 0x04, - PAGE_WRITECOPY = 0x08, - PAGE_GUARD = 0x100, - PAGE_NOCACHE = 0x200, - PAGE_WRITECOMBINE = 0x400 - } - [Flags] - public enum MemoryAllocationType : uint - { - MEM_COMMIT = 0x00001000, - MEM_RESERVE = 0x00002000, - MEM_RESET = 0x00080000, - MEM_RESET_UNDO = 0x1000000, - MEM_LARGE_PAGES = 0x20000000, - MEM_PHYSICAL = 0x00400000, - MEM_TOP_DOWN = 0x00100000, - MEM_WRITE_WATCH = 0x00200000 - } - [StructLayout(LayoutKind.Sequential)] - public readonly struct RECT - { - public readonly int Left, Top, Right, Bottom; - - public int Height => this.Bottom - this.Top; - - public int Width => this.Right - this.Left; - - public RECT(int left, int top, int right, int bottom) - { - this.Left = left; - this.Top = top; - this.Right = right; - this.Bottom = bottom; - } - } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct SystemHandleInformation - { - public uint OwnerPID; - public byte ObjectType; - public byte HandleFlags; - public ushort HandleValue; - public UIntPtr ObjectPointer; - public IntPtr AccessMask; - } - [StructLayout(LayoutKind.Sequential)] - public struct ObjectBasicInformation - { - public uint Attributes; - public uint GrantedAccess; - public uint HandleCount; - public uint PointerCount; - public uint PagedPoolUsage; - public uint NonPagedPoolUsage; - public uint Reserved1; - public uint Reserved2; - public uint Reserved3; - public uint NameInformationLength; - public uint TypeInformationLength; - public uint SecurityDescriptorLength; - public FILETIME CreateTime; - } - [StructLayout(LayoutKind.Sequential)] - public struct IoStatusBlock - { - public uint Status; - public ulong Information; - } - [StructLayout(LayoutKind.Sequential)] - public struct WindowInfo - { - public uint cbSize; - public RECT rcWindow; - public RECT rcClient; - public uint dwStyle; - public uint dwExStyle; - public uint dwWindowStatus; - public uint cxWindowBorders; - public uint cyWindowBorders; - public ushort atomWindowType; - public ushort wCreatorVersion; - - public WindowInfo(bool? _) : this() // Allows automatic initialization of "cbSize" with "new WINDOWINFO(null/true/false)". - { - this.cbSize = (uint)(Marshal.SizeOf(typeof(WindowInfo))); - } - - } - [Flags] - public enum DuplicateOptions : uint - { - DUPLICATE_CLOSE_SOURCE = 0x00000001, - DUPLICATE_SAME_ACCESS = 0x00000002 - } - [Flags] - public enum ProcessAccessFlags : uint - { - All = 0x001F0FFF, - Terminate = 0x00000001, - CreateThread = 0x00000002, - VMOperation = 0x00000008, - VMRead = 0x00000010, - VMWrite = 0x00000020, - DupHandle = 0x00000040, - SetInformation = 0x00000200, - QueryInformation = 0x00000400, - QueryLimitedInformation = 0x00001000, - Synchronize = 0x00100000 - } - [Flags] - public enum NtStatus : uint - { - STATUS_SUCCESS = 0x00000000, - STATUS_INFO_LENGTH_MISMATCH = 0xC0000004 - } - [Flags] - public enum ObjectInformationClass : uint - { - ObjectBasicInformation = 0, - ObjectNameInformation = 1, - ObjectTypeInformation = 2, - ObjectAllTypesInformation = 3, - ObjectHandleInformation = 4 - } - [Flags] - public enum SystemInformationClass : uint - { - SystemHandleInformation = 16 - } - [Flags] - public enum FileInformationClass - { - FileNameInformation = 9 - } - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - public struct StartupInfo - { - public int cb; - public string lpReserved; - public string lpDesktop; - public string lpTitle; - public int dwX; - public int dwY; - public int dwXSize; - public int dwYSize; - public int dwXCountChars; - public int dwYCountChars; - public int dwFillAttribute; - public int dwFlags; - public short wShowWindow; - public short cbReserved2; - public IntPtr lpReserved2; - public IntPtr hStdInput; - public IntPtr hStdOutput; - public IntPtr hStdError; - } - [StructLayout(LayoutKind.Sequential)] - public struct ProcessInformation - { - public IntPtr hProcess; - public IntPtr hThread; - public int dwProcessId; - public int dwThreadId; - } - public enum ProcessInfoClass : uint - { - ProcessBasicInformation = 0x00, - ProcessDebugPort = 0x07, - ProcessExceptionPort = 0x08, - ProcessAccessToken = 0x09, - ProcessWow64Information = 0x1A, - ProcessImageFileName = 0x1B, - ProcessDebugObjectHandle = 0x1E, - ProcessDebugFlags = 0x1F, - ProcessExecuteFlags = 0x22, - ProcessInstrumentationCallback = 0x28, - MaxProcessInfoClass = 0x64 - } - [StructLayout(LayoutKind.Sequential)] - internal struct ProcessBasicInformation - { - private readonly IntPtr Reserved1; - internal IntPtr PebBaseAddress; - private readonly IntPtr Reserved2; - private readonly IntPtr Reserved3; - private readonly UIntPtr UniqueProcessId; - private readonly IntPtr Reserved4; - } - public enum SaferLevel : uint - { - Disallowed = 0, - Untrusted = 0x1000, - Constrained = 0x10000, - NormalUser = 0x20000, - FullyTrusted = 0x40000 - } - public enum SaferLevelScope : uint - { - Machine = 1, - User = 2 - } - public enum SaferOpen : uint - { - Open = 1 - } - public enum SaferTokenBehaviour : uint - { - Default = 0x0, - NullIfEqual = 0x1, - CompareOnly = 0x2, - MakeInert = 0x4, - WantFlags = 0x8 - } - public enum TokenInformationClass : uint - { - TokenUser = 1, - TokenGroups, - TokenPrivileges, - TokenOwner, - TokenPrimaryGroup, - TokenDefaultDacl, - TokenSource, - TokenType, - TokenImpersonationLevel, - TokenStatistics, - TokenRestrictedSids, - TokenSessionId, - TokenGroupsAndPrivileges, - TokenSessionReference, - TokenSandBoxInert, - TokenAuditPolicy, - TokenOrigin, - TokenElevationType, - TokenLinkedToken, - TokenElevation, - TokenHasRestrictions, - TokenAccessInformation, - TokenVirtualizationAllowed, - TokenVirtualizationEnabled, - TokenIntegrityLevel, - TokenUiAccess, - TokenMandatoryPolicy, - TokenLogonSid, - MaxTokenInfoClass - } - [StructLayout(LayoutKind.Sequential)] - public struct TokenMandatoryLabel - { - public SidAndAttributes Label; - } - [StructLayout(LayoutKind.Sequential)] - public struct SidAndAttributes - { - public IntPtr Sid; - public int Attributes; - } - [StructLayout(LayoutKind.Sequential)] - public struct Sid_Identifier_Authority - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public byte[] Value; - } - [Flags] - public enum CreationFlags : uint - { - CreateSuspended = 0x00000004, - DetachedProcess = 0x00000008, - CreateNoWindow = 0x08000000, - ExtendedStartupInfoPresent = 0x00080000 - } - - public const int WM_SYSCOMMAND = 0x112; - - [DllImport("kernel32.dll", SetLastError = true)] - public static extern IntPtr CreateToolhelp32Snapshot(uint dwFlags, uint th32ProcessID); - [DllImport("kernel32.dll")] - public static extern bool Process32First(IntPtr hSnapshot, ref ProcessEntry32 lppe); - [DllImport("kernel32.dll")] - public static extern bool Process32Next(IntPtr hSnapshot, ref ProcessEntry32 lppe); - [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] - public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); - [DllImport("kernel32.dll")] - public static extern bool CloseHandle(IntPtr hObject); - [DllImport("kernel32.dll")] - public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, DuplicateOptions dwOptions); - [DllImport("kernel32.dll")] - public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, bool bInheritHandle, uint dwProcessID); - [DllImport("ntdll.dll", SetLastError = true)] - public static extern NtStatus NtQueryInformationFile(IntPtr FileHandle, ref IoStatusBlock IoStatusBlock, IntPtr FileInformation, int FileInformationLength, FileInformationClass FileInformationClass); - [DllImport("ntdll.dll")] - public static extern NtStatus NtQueryObject(IntPtr ObjectHandle, ObjectInformationClass ObjectInformationClass, IntPtr ObjectInformation, int ObjectInformationLength, out int ReturnLength); - [DllImport("ntdll.dll")] - public static extern NtStatus NtQuerySystemInformation(SystemInformationClass SystemInformationClass, IntPtr SystemInformation, int SystemInformationLength, out int ReturnLength); - [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] - public static extern bool QueryFullProcessImageName(IntPtr hProcess, uint dwFlags, StringBuilder lpExeName, ref uint lpdwSize); - [DllImport("user32.dll")] - public static extern bool SetWindowPos(IntPtr hwnd, IntPtr insertAfter, int x, int y, int cx, int cy, uint flags); - [DllImport("user32.dll")] - public static extern bool ShowWindow(IntPtr hwnd, int cmd); - [DllImport("user32.dll", CharSet = CharSet.Auto)] - public static extern int GetWindowTextLength(IntPtr hWnd); - [DllImport("user32.dll", CharSet = CharSet.Unicode)] - public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); - [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] - public static extern IntPtr LoadLibrary(string lpFileName); - [DllImport("kernel32.dll", CharSet = CharSet.Auto)] - public static extern bool FreeLibrary(IntPtr hModule); - [DllImport("user32.dll", SetLastError = true)] - public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId); - [DllImport("user32.dll", SetLastError = true)] - public static extern bool UnhookWindowsHookEx(IntPtr hHook); - [DllImport("user32.dll", SetLastError = true)] - public static extern IntPtr CallNextHookEx(IntPtr hHook, int code, IntPtr wParam, IntPtr lParam); - [DllImport("user32.dll")] - public static extern IntPtr GetForegroundWindow(); - [DllImport("kernel32.dll", SetLastError = true)] - public static extern bool ReadProcessMemory(IntPtr hProcess, uint lpBaseAddress, IntPtr lpBuffer, uint nSize, out uint lpNumberOfBytesRead); - [DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi)] - internal static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead); - [return: MarshalAs(UnmanagedType.Bool)] - [DllImport("user32.dll", SetLastError = true)] - public static extern bool GetWindowInfo(IntPtr hwnd, ref WindowInfo pwi); - [DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi)] - public static extern bool CreateProcess( - string lpApplicationName, string lpCommandLine, ref SecurityAttributes lpProcessAttributes, - ref SecurityAttributes lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, - IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref StartupInfo lpStartupInfo, - out ProcessInformation lpProcessInformation); - [DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi)] - public static extern uint ResumeThread(IntPtr hThread); - [DllImport("kernel32.dll", SetLastError = true)] - public static extern IntPtr LocalFree(IntPtr hMem); - [DllImport("kernel32.dll", CallingConvention = CallingConvention.Winapi)] - public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesWritten); - [DllImport("ntdll.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)] - public static extern int NtQueryInformationProcess(IntPtr hProcess, ProcessInfoClass pic, out ProcessBasicInformation pbi, int cb, out int pSize); - [DllImport("advapi32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)] - public static extern bool SaferCreateLevel(SaferLevelScope scopeId, SaferLevel levelId, SaferOpen openFlags, out IntPtr levelHandle, IntPtr reserved); - [DllImport("advapi32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)] - public static extern bool SaferComputeTokenFromLevel(IntPtr levelHandle, IntPtr inAccessToken, out IntPtr outAccessToken, SaferTokenBehaviour flags, IntPtr lpReserved); - [DllImport("advapi32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)] - public static extern bool SaferCloseLevel(IntPtr levelHandle); - [DllImport("advapi32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)] - public static extern bool SetTokenInformation(IntPtr tokenHandle, TokenInformationClass tokenInformationokenInformationClass, ref TokenMandatoryLabel tokenInformation, uint tokenInformationLength); - [DllImport("advapi32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)] - public static extern bool ConvertStringSidToSid(string stringSid, out IntPtr ptrSid); - [DllImport("advapi32.dll")] - public static extern uint GetLengthSid(IntPtr pSid); - [DllImport("advapi32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)] - public static extern bool CreateProcessAsUser( - IntPtr hToken, - string lpApplicationName, - string lpCommandLine, - ref SecurityAttributes lpProcessAttributes, - ref SecurityAttributes lpThreadAttributes, - bool bInheritHandles, - uint dwCreationFlags, - IntPtr lpEnvironment, - string lpCurrentDirectory, - ref StartupInfo lpStartupInfo, - out ProcessInformation lpProcessInformation); - [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] - public static extern IntPtr GetModuleHandle(string lpModuleName); - [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, 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", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] - public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); - - [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/ProcessInjector.cs b/Daybreak.Injector/ProcessInjector.cs deleted file mode 100644 index 6b716413..00000000 --- a/Daybreak.Injector/ProcessInjector.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Text; - -namespace Daybreak.Injector; -internal static class ProcessInjector -{ - public static Task Inject(Process process, string pathToDll, CancellationToken cancellationToken) - { - return Task.Factory.StartNew(() => - { - return InjectWithWinApi(process, pathToDll); - }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); - } - - private static bool InjectWithWinApi(Process process, string pathToDll) - { - var modulefullpath = Path.GetFullPath(pathToDll); - - if (!File.Exists(modulefullpath)) - { - Console.WriteLine("Dll to inject not found"); - return false; - } - - var hKernel32 = NativeMethods.GetModuleHandle("kernel32.dll"); - if (hKernel32 == IntPtr.Zero) - { - Console.WriteLine("Unable to get a handle of kernel32.dll"); - return false; - } - - var hLoadLib = NativeMethods.GetProcAddress(hKernel32, "LoadLibraryW"); - if (hLoadLib == IntPtr.Zero) - { - Console.WriteLine("Unable to get the address of LoadLibraryW"); - return false; - } - - 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) - { - 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; - } - - var 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; - } - - var 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; - } - - var dllResult = NativeMethods.GetExitCodeThread(hThread, out _); - if (dllResult == 0) - { - Console.WriteLine($"Injected dll returned non-success status code {dllResult}"); - return false; - } - - var memoryFreeResult = NativeMethods.VirtualFreeEx(process.Handle, hStringBuffer, 0, 0x8000 /* MEM_RELEASE */); - if (!memoryFreeResult) - { - Console.WriteLine($"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; - } -} diff --git a/Daybreak.Injector/Program.cs b/Daybreak.Injector/Program.cs deleted file mode 100644 index 2ab72a1a..00000000 --- a/Daybreak.Injector/Program.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Daybreak.Injector; -using System.Diagnostics; - -var processId = -1; -var pathToDll = string.Empty; - -if (args.Length != 4) -{ - Console.WriteLine("Daybreak Injector"); - Console.WriteLine("Usage:"); - Console.WriteLine("Daybreak.Injector.exe -p [PROCESS_ID] -d [PATH_TO_DLL]"); - return -1; -} - -for (var i = 0; i < args.Length - 1; i++) -{ - if (args[i].ToLower() == "-p" && - int.TryParse(args[i + 1], out var parsedId)) - { - processId = parsedId; - i++; - } - else if (args[i].ToLower() == "-d") - { - pathToDll = args[i + 1]; - i++; - } -} - -if (processId == -1) -{ - Console.WriteLine("Error: Process id was not specified"); - return -1; -} - -if (pathToDll == string.Empty) -{ - Console.WriteLine("Error: Path to dll was not specified"); - return -1; -} - -if (!File.Exists(pathToDll)) -{ - Console.WriteLine("Error: Provided dll could not be found"); - return -1; -} - -var maybeProcess = Process.GetProcessById(processId); -if (maybeProcess is null) -{ - Console.WriteLine("Error: Could not find desired process"); - return -1; -} - -var result = await ProcessInjector.Inject(maybeProcess, pathToDll, CancellationToken.None); -if (result is false) -{ - Console.WriteLine("Error: Failed to inject dll"); - return -1; -} - -Console.WriteLine("Injected dll"); -return 0; diff --git a/Daybreak.sln b/Daybreak.sln index 23d2fb88..91c3028b 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", "{2C1AB589-A533-41B6-9F8B-4328AD122437}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -94,18 +92,6 @@ Global {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 - {2C1AB589-A533-41B6-9F8B-4328AD122437}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2C1AB589-A533-41B6-9F8B-4328AD122437}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2C1AB589-A533-41B6-9F8B-4328AD122437}.Debug|x64.ActiveCfg = Debug|x86 - {2C1AB589-A533-41B6-9F8B-4328AD122437}.Debug|x64.Build.0 = Debug|x86 - {2C1AB589-A533-41B6-9F8B-4328AD122437}.Debug|x86.ActiveCfg = Debug|x86 - {2C1AB589-A533-41B6-9F8B-4328AD122437}.Debug|x86.Build.0 = Debug|x86 - {2C1AB589-A533-41B6-9F8B-4328AD122437}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2C1AB589-A533-41B6-9F8B-4328AD122437}.Release|Any CPU.Build.0 = Release|Any CPU - {2C1AB589-A533-41B6-9F8B-4328AD122437}.Release|x64.ActiveCfg = Release|Any CPU - {2C1AB589-A533-41B6-9F8B-4328AD122437}.Release|x64.Build.0 = Release|Any CPU - {2C1AB589-A533-41B6-9F8B-4328AD122437}.Release|x86.ActiveCfg = Release|Any CPU - {2C1AB589-A533-41B6-9F8B-4328AD122437}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Daybreak/Daybreak.csproj b/Daybreak/Daybreak.csproj index 719d8a9b..c750dfb6 100644 --- a/Daybreak/Daybreak.csproj +++ b/Daybreak/Daybreak.csproj @@ -11,7 +11,7 @@ preview Daybreak.ico true - 0.9.9.12 + 0.9.9.13 true cfb2a489-db80-448d-a969-80270f314c46 True diff --git a/Daybreak/Services/ApplicationLauncher/ApplicationLauncher.cs b/Daybreak/Services/ApplicationLauncher/ApplicationLauncher.cs index 33632514..2716e492 100644 --- a/Daybreak/Services/ApplicationLauncher/ApplicationLauncher.cs +++ b/Daybreak/Services/ApplicationLauncher/ApplicationLauncher.cs @@ -411,7 +411,11 @@ public void KillGuildWarsProcess(GuildWarsApplicationLaunchContext guildWarsAppl return; } } - catch (Win32Exception e) + catch (Win32Exception e) when (e.Message.Contains("Only part of a ReadProcessMemory or WriteProcessMemory request was completed")) + { + guildWarsApplicationLaunchContext.GuildWarsProcess.Kill(); + } + catch (Win32Exception e) when (e.Message.Contains("Access is denied")) { this.logger.LogError(e, $"Insuficient privileges to kill GuildWars process with id {guildWarsApplicationLaunchContext.ProcessId}"); this.privilegeManager.RequestAdminPrivileges("Insufficient privileges to kill Guild Wars process. Please restart as administrator and try again."); diff --git a/Daybreak/Services/Injection/ProcessInjector.cs b/Daybreak/Services/Injection/ProcessInjector.cs index d4ac96e5..b6d3be39 100644 --- a/Daybreak/Services/Injection/ProcessInjector.cs +++ b/Daybreak/Services/Injection/ProcessInjector.cs @@ -1,16 +1,18 @@ -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; namespace Daybreak.Services.Injection; internal sealed class ProcessInjector : IProcessInjector { - private const string InjectorExe = "Daybreak.Injector.exe"; - private readonly ILogger logger; public ProcessInjector( @@ -21,51 +23,133 @@ public ProcessInjector( public Task Inject(Process process, string pathToDll, CancellationToken cancellationToken) { - return Task.Factory.StartNew(() => this.InjectWithInjector(process, pathToDll), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + return Task.Factory.StartNew(() => this.InjectWithApi(process, pathToDll), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); } - - private bool InjectWithInjector(Process process, string pathToDll) + + private bool InjectWithApi(Process process, string pathToDll) { - var scopedLogger = this.logger.CreateScopedLogger(nameof(InjectWithInjector), pathToDll); + var scopedLogger = this.logger.CreateScopedLogger(nameof(this.InjectWithApi), pathToDll); var modulefullpath = Path.GetFullPath(pathToDll); + if (!File.Exists(modulefullpath)) { scopedLogger.LogError("Dll to inject not found"); return false; } - var injectorPath = Path.GetFullPath(InjectorExe); - if (!File.Exists(injectorPath)) + var hKernel32 = NativeMethods.GetModuleHandle("kernel32.dll"); + if (hKernel32 == IntPtr.Zero) + { + scopedLogger.LogError("Unable to get a handle of kernel32.dll"); + return false; + } + + var hLoadLib = NativeMethods.GetProcAddress(hKernel32, "LoadLibraryW"); + if (hLoadLib == IntPtr.Zero) + { + scopedLogger.LogError("Unable to get the address of LoadLibraryW"); + return false; + } + + 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("Could not find Daybreak injector"); + scopedLogger.LogError($"Injected dll returned non-success status code {dllResult}"); return false; } - var injectionProcess = new Process + var memoryFreeResult = NativeMethods.VirtualFreeEx(process.Handle, hStringBuffer, 0, 0x8000 /* MEM_RELEASE */); + if (!memoryFreeResult) { - StartInfo = new ProcessStartInfo - { - Arguments = $"-p {process.Id} -d {pathToDll}", - CreateNoWindow = true, - RedirectStandardError = true, - RedirectStandardInput = true, - RedirectStandardOutput = true, - FileName = injectorPath, - } - }; - - injectionProcess.Start(); - injectionProcess.WaitForExit(); - var result = injectionProcess.ExitCode; - if (result == 0) + 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')) { - scopedLogger.LogInformation("Injected into process"); - return true; + ret = ret[..ret.IndexOf('\0')]; } - var stdout = injectionProcess.StandardOutput.ReadToEnd(); - var stderr = injectionProcess.StandardError.ReadToEnd(); - scopedLogger.LogError($"Failed to inject. Details:\n{stdout}\n{stderr}"); - return false; + 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; } }