diff --git a/Dalamud.Injector/NativeFunctions.cs b/Dalamud.Injector/NativeFunctions.cs new file mode 100644 index 000000000..263ab8332 --- /dev/null +++ b/Dalamud.Injector/NativeFunctions.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.ConstrainedExecution; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; +using System.Threading.Tasks; + +namespace Dalamud.Injector +{ + static class NativeFunctions + { + // OpenProcess signture https://www.pinvoke.net/default.aspx/kernel32.openprocess + [Flags] + public enum ProcessAccessFlags : uint + { + All = 0x001F0FFF, + Terminate = 0x00000001, + CreateThread = 0x00000002, + VirtualMemoryOperation = 0x00000008, + VirtualMemoryRead = 0x00000010, + VirtualMemoryWrite = 0x00000020, + DuplicateHandle = 0x00000040, + CreateProcess = 0x000000080, + SetQuota = 0x00000100, + SetInformation = 0x00000200, + QueryInformation = 0x00000400, + QueryLimitedInformation = 0x00001000, + Synchronize = 0x00100000 + } + + [DllImport("kernel32.dll", SetLastError = true)] + public static extern IntPtr OpenProcess( + ProcessAccessFlags processAccess, + bool bInheritHandle, + int processId); + public static IntPtr OpenProcess(Process proc, ProcessAccessFlags flags) + { + return OpenProcess(flags, false, proc.Id); + } + + // VirtualAllocEx signture https://www.pinvoke.net/default.aspx/kernel32.virtualallocex + [Flags] + public enum AllocationType + { + Commit = 0x1000, + Reserve = 0x2000, + Decommit = 0x4000, + Release = 0x8000, + Reset = 0x80000, + Physical = 0x400000, + TopDown = 0x100000, + WriteWatch = 0x200000, + LargePages = 0x20000000 + } + + // VirtualFreeEx signture https://www.pinvoke.net/default.aspx/kernel32.virtualfreeex + [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] + public static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, + int dwSize, AllocationType dwFreeType); + + [Flags] + public enum MemoryProtection + { + Execute = 0x10, + ExecuteRead = 0x20, + ExecuteReadWrite = 0x40, + ExecuteWriteCopy = 0x80, + NoAccess = 0x01, + ReadOnly = 0x02, + ReadWrite = 0x04, + WriteCopy = 0x08, + GuardModifierflag = 0x100, + NoCacheModifierflag = 0x200, + WriteCombineModifierflag = 0x400 + } + + [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] + public static extern IntPtr VirtualAllocEx( + IntPtr hProcess, + IntPtr lpAddress, + int dwSize, + AllocationType flAllocationType, + MemoryProtection flProtect); + + // WriteProcessMemory signture https://www.pinvoke.net/default.aspx/kernel32/WriteProcessMemory.html + [DllImport("kernel32.dll", SetLastError = true)] + public static extern bool WriteProcessMemory( + IntPtr hProcess, + IntPtr lpBaseAddress, + [MarshalAs(UnmanagedType.AsAny)] object lpBuffer, + int dwSize, + out IntPtr lpNumberOfBytesWritten); + + // GetProcAddress signture https://www.pinvoke.net/default.aspx/kernel32.getprocaddress + [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] + public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); + + // GetModuleHandle signture http://pinvoke.net/default.aspx/kernel32.GetModuleHandle + [DllImport("kernel32.dll", CharSet = CharSet.Auto)] + public static extern IntPtr GetModuleHandle(string lpModuleName); + + // CreateRemoteThread signture https://www.pinvoke.net/default.aspx/kernel32.createremotethread + [DllImport("kernel32.dll")] + public static extern IntPtr CreateRemoteThread( + IntPtr hProcess, + IntPtr lpThreadAttributes, + uint dwStackSize, + IntPtr lpStartAddress, + IntPtr lpParameter, + uint dwCreationFlags, + IntPtr lpThreadId); + + // CloseHandle signture https://www.pinvoke.net/default.aspx/kernel32.closehandle + [DllImport("kernel32.dll", SetLastError = true)] + [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] + [SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseHandle(IntPtr hObject); + } +} diff --git a/Dalamud.Injector/Program.cs b/Dalamud.Injector/Program.cs index f538d714a..53e7e7e2c 100644 --- a/Dalamud.Injector/Program.cs +++ b/Dalamud.Injector/Program.cs @@ -1,8 +1,11 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; +using System.Drawing; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Windows.Forms; @@ -29,7 +32,7 @@ namespace Dalamud.Injector { }; - var pid = -1; + var pid = -1; if (args.Length == 1) { pid = int.Parse(args[0]); } @@ -69,6 +72,9 @@ namespace Dalamud.Injector { // Inject to process Inject(process, startInfo); + + // Inject exception handler + NativeInject(process); } private static void Inject(Process process, DalamudStartInfo info) { @@ -86,6 +92,63 @@ namespace Dalamud.Injector { Console.WriteLine("Injected"); } + private static void NativeInject(Process process) { + var libPath = Path.GetFullPath("DalamudDebugStub.dll"); + + var handle = NativeFunctions.OpenProcess( + NativeFunctions.ProcessAccessFlags.All, + false, + process.Id); + + if (handle == IntPtr.Zero) + throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not OpenProcess"); + + var dllMem = NativeFunctions.VirtualAllocEx( + handle, + IntPtr.Zero, + libPath.Length, + NativeFunctions.AllocationType.Reserve | NativeFunctions.AllocationType.Commit, + NativeFunctions.MemoryProtection.ExecuteReadWrite); + + if (dllMem == IntPtr.Zero) + throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not alloc memory"); + + var pathBytes = Encoding.ASCII.GetBytes(libPath); + if (!NativeFunctions.WriteProcessMemory( + handle, + dllMem, + pathBytes, + pathBytes.Length, + out var bytesread + )) + throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not write DLL"); + + var kernel32 = NativeFunctions.GetModuleHandle("Kernel32.dll"); + var loadLibA = NativeFunctions.GetProcAddress(kernel32, "LoadLibraryA"); + + var remoteThread = NativeFunctions.CreateRemoteThread( + handle, + IntPtr.Zero, + 0, + loadLibA, + dllMem, + 0, + IntPtr.Zero + ); + + if (remoteThread == IntPtr.Zero) + throw new Win32Exception(Marshal.GetLastWin32Error(), "Could not alloc memory"); + + NativeFunctions.VirtualFreeEx( + handle, + dllMem, + 0, + NativeFunctions.AllocationType.Release); + + NativeFunctions.CloseHandle(remoteThread); + NativeFunctions.CloseHandle(handle); + } + private static DalamudStartInfo GetDefaultStartInfo() { var ffxivDir = Path.GetDirectoryName(process.MainModule.FileName); var startInfo = new DalamudStartInfo { diff --git a/DalamudDebugStub/dllmain.cpp b/DalamudDebugStub/dllmain.cpp index 2aa1de7a2..7a579b63b 100644 --- a/DalamudDebugStub/dllmain.cpp +++ b/DalamudDebugStub/dllmain.cpp @@ -5,49 +5,6 @@ #include #include #include -#include - -BOOL ListProcessThreads(DWORD dwOwnerPID) -{ - HANDLE hThreadSnap = INVALID_HANDLE_VALUE; - THREADENTRY32 te32; - - // Take a snapshot of all running threads - hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); - if (hThreadSnap == INVALID_HANDLE_VALUE) - return(FALSE); - - // Fill in the size of the structure before using it. - te32.dwSize = sizeof(THREADENTRY32); - - // Retrieve information about the first thread, - // and exit if unsuccessful - if (!Thread32First(hThreadSnap, &te32)) - { - CloseHandle(hThreadSnap); // Must clean up the snapshot object! - return(FALSE); - } - - // Now walk the thread list of the system, - // and display information about each thread - // associated with the specified process - do - { - if (te32.th32OwnerProcessID == dwOwnerPID && te32.th32ThreadID != GetCurrentThreadId()) - { - _tprintf(TEXT("\n THREAD ID = 0x%08X"), te32.th32ThreadID); - _tprintf(TEXT("\n base priority = %d"), te32.tpBasePri); - _tprintf(TEXT("\n delta priority = %d"), te32.tpDeltaPri); - SuspendThread(OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID)); - } - } while (Thread32Next(hThreadSnap, &te32)); - - _tprintf(TEXT("\n")); - - // Don't forget to clean up the snapshot object. - CloseHandle(hThreadSnap); - return(TRUE); -} bool isExcept = false;