private static bool TryStartRemoteDllThreadHelper(SafeProcessHandle hRemoteProcess, SafeRemoteBufferHandle hRemoteDllPathBuffer, out SafeRemoteThreadHandle hRemoteThreadOut)
        {
            var hProcessUnsafe = hRemoteProcess.DangerousGetHandle();
             var pRemoteDllPath = hRemoteDllPathBuffer.DangerousGetHandle();

             try {
            uint remoteThreadId;
            var hRemoteThread = WinAPI.CreateRemoteThread(
               hProcessUnsafe,
               IntPtr.Zero,
               0,
               pLoadLibraryA,
               pRemoteDllPath,
               0,
               out remoteThreadId
            );
            if (hRemoteThread == IntPtr.Zero) {
               logger.Warn($"CreateRemoteThread failed with errno {Marshal.GetLastWin32Error()}.");
               hRemoteThreadOut = null;
            } else {
               hRemoteThreadOut = new SafeRemoteThreadHandle(hRemoteThread);
            }
             } catch (Win32Exception e) {
            var errno = Marshal.GetLastWin32Error();
            logger.Warn("Win32Exception thrown when creating remote thread. Errno: " + errno + ".", e);
            hRemoteThreadOut = null;
             }

             return hRemoteThreadOut != null;
        }
        public static SafeRemoteBufferHandle AllocateOrThrow(SafeProcessHandle hProcessSafe, string text)
        {
            var hProcessUnsafe = hProcessSafe.DangerousGetHandle();

             logger.Info($"Allocating remote string buffer for text {text}.");
             uint szText = (uint)(text.Length + 1); // +1 for null string terminator

             IntPtr pRemoteStringBuffer = WinAPI.VirtualAllocEx(
            hProcessUnsafe,
            IntPtr.Zero,
            szText,
            AllocationType.Commit,
            MemoryProtection.ExecuteReadWrite
             );

             if (pRemoteStringBuffer == IntPtr.Zero) {
            throw new Exception(
               "Could not allocate memory inside target process!\r\n" +
               "\r\nDll Path: " + text + " (size " + szText + ")" +
               "\r\nErrno: " + Marshal.GetLastWin32Error()
            );
             }

             logger.Info("Write string in remote process");
             int bytesWritten;
             bool writeSuccessful = WinAPI.WriteProcessMemory(
            hProcessUnsafe,
            pRemoteStringBuffer,
            Encoding.ASCII.GetBytes(text),
            szText,
            out bytesWritten
             );
             if (!writeSuccessful) {
            throw new Exception(
               "Call to WriteProcessMemory failed! \n" +
               "\nDll Path: " + text + " (size " + szText + ")" +
               "\nBytes Written: " + bytesWritten +
               "\nErrno: " + Marshal.GetLastWin32Error()
            );
             } else if (bytesWritten != szText) {
            throw new Exception(
               "WriteProcessMemory did not write expected byte count! \n" +
               "\nDll Path: " + text + " (size " + szText + ")" +
               "\nBytes Written: " + bytesWritten +
               "\nErrno: " + Marshal.GetLastWin32Error()
            );
             }

             return new SafeRemoteBufferHandle(hProcessUnsafe, pRemoteStringBuffer);
        }
        public static SafeRemoteThreadHandle StartRemoteDllThreadOrThrow(SafeProcessHandle hRemoteProcess, SafeRemoteBufferHandle hRemoteDllPathBuffer, int attempts)
        {
            for (var i = 0; i < attempts; i++) {
            SafeRemoteThreadHandle result;
            if (TryStartRemoteDllThreadHelper(hRemoteProcess, hRemoteDllPathBuffer, out result)) {
               return result;
            }
             }

             throw new Exception(
            "Could not create remote thread after " + attempts + " attempts!\n" +
            "\r\nhProcess: " + hRemoteProcess.DangerousGetHandle() +
            "\r\nhRemoteDllPathBuffer: " + hRemoteDllPathBuffer.DangerousGetHandle() +
            "\r\npLoadLibraryA: " + pLoadLibraryA +
            "\r\nErrno: " + Marshal.GetLastWin32Error()
             );
        }
        public static SafeProcessHandle OpenOrThrow(int targetProcessId)
        {
            var hProcess = new SafeProcessHandle(
            WinAPI.OpenProcess(
               ProcessAccessRights.PROCESS_ALL_ACCESS,
               1 /* We do want to own the handle */,
               targetProcessId
            )
             );

             if (hProcess.IsInvalid) {
            throw new UnauthorizedAccessException(
               "Could acquire target process handle!\n" +
               "OpenProcess returned invalid Handle! Is the Process Injector running with elevated privileges?" +
               "\nProcess Id: " + targetProcessId +
               "\nErrno: " + Marshal.GetLastWin32Error()
            );
             }

             return hProcess;
        }