mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-01-02 13:53:40 +01:00
Merge branch 'net5'
This commit is contained in:
commit
a78754ce22
100 changed files with 67306 additions and 923 deletions
|
|
@ -64,7 +64,7 @@
|
|||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="PeNet" Version="2.6.4" />
|
||||
<PackageReference Include="Reloaded.Memory" Version="4.1.1" />
|
||||
<PackageReference Include="Reloaded.Memory.Buffers" Version="1.3.5" />
|
||||
<PackageReference Include="Reloaded.Memory.Buffers" Version="1.4.4" />
|
||||
<PackageReference Include="Serilog" Version="2.10.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
|
@ -38,7 +39,6 @@ namespace Dalamud.Injector
|
|||
public static void Main(int argc, IntPtr argvPtr)
|
||||
{
|
||||
List<string> args = new(argc);
|
||||
Init(args);
|
||||
|
||||
unsafe
|
||||
{
|
||||
|
|
@ -47,6 +47,9 @@ namespace Dalamud.Injector
|
|||
args.Add(Marshal.PtrToStringUni(argv[i]));
|
||||
}
|
||||
|
||||
Init(args);
|
||||
args.Remove("-v"); // Remove "verbose" flag
|
||||
|
||||
if (args.Count >= 2 && args[1].ToLowerInvariant() == "launch-test")
|
||||
{
|
||||
Environment.Exit(ProcessLaunchTestCommand(args));
|
||||
|
|
@ -80,6 +83,8 @@ namespace Dalamud.Injector
|
|||
}
|
||||
|
||||
startInfo = ExtractAndInitializeStartInfoFromArguments(startInfo, args);
|
||||
args.Remove("--console"); // Remove "console" flag, already handled
|
||||
args.Remove("--etw"); // Remove "etw" flag, already handled
|
||||
|
||||
var mainCommand = args[1].ToLowerInvariant();
|
||||
if (mainCommand.Length > 0 && mainCommand.Length <= 6 && "inject"[..mainCommand.Length] == mainCommand)
|
||||
|
|
@ -100,10 +105,23 @@ namespace Dalamud.Injector
|
|||
}
|
||||
}
|
||||
|
||||
private static string GetLogPath(string filename)
|
||||
{
|
||||
var baseDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
|
||||
#if DEBUG
|
||||
var logPath = Path.Combine(baseDirectory, $"{filename}.log");
|
||||
#else
|
||||
var logPath = Path.Combine(baseDirectory, "..", "..", "..", $"{filename}.log");
|
||||
#endif
|
||||
|
||||
return logPath;
|
||||
}
|
||||
|
||||
private static void Init(List<string> args)
|
||||
{
|
||||
InitLogging(args.Any(x => x == "-v"));
|
||||
InitUnhandledException(args);
|
||||
InitLogging();
|
||||
|
||||
var cwd = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory;
|
||||
if (cwd.FullName != Directory.GetCurrentDirectory())
|
||||
|
|
@ -158,23 +176,18 @@ namespace Dalamud.Injector
|
|||
};
|
||||
}
|
||||
|
||||
private static void InitLogging()
|
||||
private static void InitLogging(bool verbose)
|
||||
{
|
||||
var baseDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
|
||||
#if DEBUG
|
||||
var logPath = Path.Combine(baseDirectory, "dalamud.injector.log");
|
||||
#else
|
||||
var logPath = Path.Combine(baseDirectory, "..", "..", "..", "dalamud.injector.log");
|
||||
verbose = true;
|
||||
#endif
|
||||
|
||||
var levelSwitch = new LoggingLevelSwitch();
|
||||
var levelSwitch = new LoggingLevelSwitch
|
||||
{
|
||||
MinimumLevel = verbose ? LogEventLevel.Verbose : LogEventLevel.Information,
|
||||
};
|
||||
|
||||
#if DEBUG
|
||||
levelSwitch.MinimumLevel = LogEventLevel.Verbose;
|
||||
#else
|
||||
levelSwitch.MinimumLevel = LogEventLevel.Information;
|
||||
#endif
|
||||
var logPath = GetLogPath("dalamud.injector");
|
||||
|
||||
CullLogFile(logPath, 1 * 1024 * 1024);
|
||||
|
||||
|
|
@ -236,8 +249,7 @@ namespace Dalamud.Injector
|
|||
int len;
|
||||
string key;
|
||||
|
||||
if (startInfo == null)
|
||||
startInfo = new();
|
||||
startInfo ??= new DalamudStartInfo();
|
||||
|
||||
var workingDirectory = startInfo.WorkingDirectory;
|
||||
var configurationPath = startInfo.ConfigurationPath;
|
||||
|
|
@ -288,7 +300,7 @@ namespace Dalamud.Injector
|
|||
clientLanguage = ClientLanguage.Japanese;
|
||||
else if (languageStr[0..(len = Math.Min(languageStr.Length, (key = "german").Length))] == key[0..len])
|
||||
clientLanguage = ClientLanguage.German;
|
||||
else if (languageStr[0..(len = Math.Min(languageStr.Length, (key = "deutsche").Length))] == key[0..len])
|
||||
else if (languageStr[0..(len = Math.Min(languageStr.Length, (key = "deutsch").Length))] == key[0..len])
|
||||
clientLanguage = ClientLanguage.German;
|
||||
else if (languageStr[0..(len = Math.Min(languageStr.Length, (key = "french").Length))] == key[0..len])
|
||||
clientLanguage = ClientLanguage.French;
|
||||
|
|
@ -299,17 +311,25 @@ namespace Dalamud.Injector
|
|||
else
|
||||
throw new CommandLineException($"\"{languageStr}\" is not a valid supported language.");
|
||||
|
||||
return new()
|
||||
{
|
||||
WorkingDirectory = workingDirectory,
|
||||
ConfigurationPath = configurationPath,
|
||||
PluginDirectory = pluginDirectory,
|
||||
DefaultPluginDirectory = defaultPluginDirectory,
|
||||
AssetDirectory = assetDirectory,
|
||||
Language = clientLanguage,
|
||||
GameVersion = null,
|
||||
DelayInitializeMs = delayInitializeMs,
|
||||
};
|
||||
startInfo.WorkingDirectory = workingDirectory;
|
||||
startInfo.ConfigurationPath = configurationPath;
|
||||
startInfo.PluginDirectory = pluginDirectory;
|
||||
startInfo.DefaultPluginDirectory = defaultPluginDirectory;
|
||||
startInfo.AssetDirectory = assetDirectory;
|
||||
startInfo.Language = clientLanguage;
|
||||
startInfo.DelayInitializeMs = delayInitializeMs;
|
||||
startInfo.GameVersion = null;
|
||||
|
||||
// Set boot defaults
|
||||
startInfo.BootShowConsole = args.Contains("--console");
|
||||
startInfo.BootEnableEtw = args.Contains("--etw");
|
||||
startInfo.BootLogPath = GetLogPath("dalamud.boot");
|
||||
startInfo.BootEnabledGameFixes = new List<string> { "prevent_devicechange_crashes", "disable_game_openprocess_access_check", "redirect_openprocess" };
|
||||
startInfo.BootDotnetOpenProcessHookMode = 0;
|
||||
// startInfo.BootWaitMessageBox = 2;
|
||||
// startInfo.BootUnhookDlls = new List<string>() { "kernel32.dll", "ntdll.dll", "user32.dll" };
|
||||
|
||||
return startInfo;
|
||||
}
|
||||
|
||||
private static int ProcessHelpCommand(List<string> args, string? particularCommand = default)
|
||||
|
|
@ -324,7 +344,7 @@ namespace Dalamud.Injector
|
|||
Console.WriteLine("{0} help [command]", exeName);
|
||||
|
||||
if (particularCommand is null or "inject")
|
||||
Console.WriteLine("{0} inject [-h/--help] [-a/--all] [--warn] [pid1] [pid2] [pid3] ...", exeName);
|
||||
Console.WriteLine("{0} inject [-h/--help] [-a/--all] [--warn] [--fix-acl] [--se-debug-privilege] [pid1] [pid2] [pid3] ...", exeName);
|
||||
|
||||
if (particularCommand is null or "launch")
|
||||
{
|
||||
|
|
@ -332,7 +352,7 @@ namespace Dalamud.Injector
|
|||
Console.WriteLine("{0} [-g path/to/ffxiv_dx11.exe] [--game=path/to/ffxiv_dx11.exe]", exeSpaces);
|
||||
Console.WriteLine("{0} [-m entrypoint|inject] [--mode=entrypoint|inject]", exeSpaces);
|
||||
Console.WriteLine("{0} [--handle-owner=inherited-handle-value]", exeSpaces);
|
||||
Console.WriteLine("{0} [--without-dalamud]", exeSpaces);
|
||||
Console.WriteLine("{0} [--without-dalamud] [--no-fix-acl]", exeSpaces);
|
||||
Console.WriteLine("{0} [-- game_arg1=value1 game_arg2=value2 ...]", exeSpaces);
|
||||
}
|
||||
|
||||
|
|
@ -341,6 +361,10 @@ namespace Dalamud.Injector
|
|||
Console.WriteLine(" [--dalamud-asset-directory=path] [--dalamud-delay-initialize=0(ms)]");
|
||||
Console.WriteLine(" [--dalamud-client-language=0-3|j(apanese)|e(nglish)|d|g(erman)|f(rench)]");
|
||||
|
||||
Console.WriteLine("Verbose logging:\t[-v]");
|
||||
Console.WriteLine("Show Console:\t[--console]");
|
||||
Console.WriteLine("Enable ETW:\t[--etw]");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -351,6 +375,8 @@ namespace Dalamud.Injector
|
|||
var targetProcessSpecified = false;
|
||||
var warnManualInjection = false;
|
||||
var showHelp = args.Count <= 2;
|
||||
var tryFixAcl = false;
|
||||
var tryClaimSeDebugPrivilege = false;
|
||||
|
||||
for (var i = 2; i < args.Count; i++)
|
||||
{
|
||||
|
|
@ -378,6 +404,14 @@ namespace Dalamud.Injector
|
|||
targetProcessSpecified = true;
|
||||
processes.AddRange(Process.GetProcessesByName("ffxiv_dx11"));
|
||||
}
|
||||
else if (args[i] == "--fix-acl" || args[i] == "--acl-fix")
|
||||
{
|
||||
tryFixAcl = true;
|
||||
}
|
||||
else if (args[i] == "--se-debug-privilege")
|
||||
{
|
||||
tryClaimSeDebugPrivilege = true;
|
||||
}
|
||||
else if (args[i] == "--warn")
|
||||
{
|
||||
warnManualInjection = true;
|
||||
|
|
@ -416,8 +450,21 @@ namespace Dalamud.Injector
|
|||
}
|
||||
}
|
||||
|
||||
if (tryClaimSeDebugPrivilege)
|
||||
{
|
||||
try
|
||||
{
|
||||
GameStart.ClaimSeDebug();
|
||||
Log.Information("SeDebugPrivilege claimed.");
|
||||
}
|
||||
catch (Win32Exception e2)
|
||||
{
|
||||
Log.Warning(e2, "Failed to claim SeDebugPrivilege");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var process in processes)
|
||||
Inject(process, AdjustStartInfo(dalamudStartInfo, process.MainModule.FileName));
|
||||
Inject(process, AdjustStartInfo(dalamudStartInfo, process.MainModule.FileName), tryFixAcl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -431,6 +478,7 @@ namespace Dalamud.Injector
|
|||
var showHelp = args.Count <= 2;
|
||||
var handleOwner = IntPtr.Zero;
|
||||
var withoutDalamud = false;
|
||||
var noFixAcl = false;
|
||||
|
||||
var parsingGameArgument = false;
|
||||
for (var i = 2; i < args.Count; i++)
|
||||
|
|
@ -447,6 +495,8 @@ namespace Dalamud.Injector
|
|||
useFakeArguments = true;
|
||||
else if (args[i] == "--without-dalamud")
|
||||
withoutDalamud = true;
|
||||
else if (args[i] == "--no-fix-acl" || args[i] == "--no-acl-fix")
|
||||
noFixAcl = true;
|
||||
else if (args[i] == "-g")
|
||||
gamePath = args[++i];
|
||||
else if (args[i].StartsWith("--game="))
|
||||
|
|
@ -547,7 +597,7 @@ namespace Dalamud.Injector
|
|||
}
|
||||
|
||||
var gameArgumentString = string.Join(" ", gameArguments.Select(x => EncodeParameterArgument(x)));
|
||||
var process = NativeAclFix.LaunchGame(Path.GetDirectoryName(gamePath), gamePath, gameArgumentString, (Process p) =>
|
||||
var process = GameStart.LaunchGame(Path.GetDirectoryName(gamePath), gamePath, gameArgumentString, noFixAcl, (Process p) =>
|
||||
{
|
||||
if (!withoutDalamud && mode == "entrypoint")
|
||||
{
|
||||
|
|
@ -558,14 +608,18 @@ namespace Dalamud.Injector
|
|||
Log.Error("[HOOKS] RewriteRemoteEntryPointW failed");
|
||||
throw new Exception("RewriteRemoteEntryPointW failed");
|
||||
}
|
||||
|
||||
Log.Verbose("RewriteRemoteEntryPointW called!");
|
||||
}
|
||||
});
|
||||
|
||||
Log.Verbose("Game process started with PID {0}", process.Id);
|
||||
|
||||
if (!withoutDalamud && mode == "inject")
|
||||
{
|
||||
var startInfo = AdjustStartInfo(dalamudStartInfo, gamePath);
|
||||
Log.Information("Using start info: {0}", JsonConvert.SerializeObject(startInfo));
|
||||
Inject(process, startInfo);
|
||||
Inject(process, startInfo, false);
|
||||
}
|
||||
|
||||
var processHandleForOwner = IntPtr.Zero;
|
||||
|
|
@ -634,21 +688,26 @@ namespace Dalamud.Injector
|
|||
var gameVerStr = File.ReadAllText(Path.Combine(ffxivDir, "ffxivgame.ver"));
|
||||
var gameVer = GameVersion.Parse(gameVerStr);
|
||||
|
||||
return new()
|
||||
return new DalamudStartInfo(startInfo)
|
||||
{
|
||||
WorkingDirectory = startInfo.WorkingDirectory,
|
||||
ConfigurationPath = startInfo.ConfigurationPath,
|
||||
PluginDirectory = startInfo.PluginDirectory,
|
||||
DefaultPluginDirectory = startInfo.DefaultPluginDirectory,
|
||||
AssetDirectory = startInfo.AssetDirectory,
|
||||
Language = startInfo.Language,
|
||||
GameVersion = gameVer,
|
||||
DelayInitializeMs = startInfo.DelayInitializeMs,
|
||||
};
|
||||
}
|
||||
|
||||
private static void Inject(Process process, DalamudStartInfo startInfo)
|
||||
private static void Inject(Process process, DalamudStartInfo startInfo, bool tryFixAcl = false)
|
||||
{
|
||||
if (tryFixAcl)
|
||||
{
|
||||
try
|
||||
{
|
||||
GameStart.CopyAclFromSelfToTargetProcess(process.SafeHandle.DangerousGetHandle());
|
||||
}
|
||||
catch (Win32Exception e1)
|
||||
{
|
||||
Log.Warning(e1, "Failed to copy ACL");
|
||||
}
|
||||
}
|
||||
|
||||
var bootName = "Dalamud.Boot.dll";
|
||||
var bootPath = Path.GetFullPath(bootName);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ using Serilog;
|
|||
namespace Dalamud.Injector
|
||||
{
|
||||
/// <summary>
|
||||
/// Class responsible for stripping ACL protections from processes.
|
||||
/// Class responsible for starting the game and stripping ACL protections from processes.
|
||||
/// </summary>
|
||||
public static class NativeAclFix
|
||||
public static class GameStart
|
||||
{
|
||||
/// <summary>
|
||||
/// Start a process without ACL protections.
|
||||
|
|
@ -22,41 +22,47 @@ namespace Dalamud.Injector
|
|||
/// <param name="workingDir">The working directory.</param>
|
||||
/// <param name="exePath">The path to the executable file.</param>
|
||||
/// <param name="arguments">Arguments to pass to the executable file.</param>
|
||||
/// <param name="dontFixAcl">Don't actually fix the ACL.</param>
|
||||
/// <param name="beforeResume">Action to execute before the process is started.</param>
|
||||
/// <param name="waitForGameWindow">Wait for the game window to be ready before proceeding.</param>
|
||||
/// <returns>The started process.</returns>
|
||||
/// <exception cref="Win32Exception">Thrown when a win32 error occurs.</exception>
|
||||
/// <exception cref="GameExitedException">Thrown when the process did not start correctly.</exception>
|
||||
public static Process LaunchGame(string workingDir, string exePath, string arguments, Action<Process> beforeResume)
|
||||
/// <exception cref="GameStartException">Thrown when the process did not start correctly.</exception>
|
||||
public static Process LaunchGame(string workingDir, string exePath, string arguments, bool dontFixAcl, Action<Process> beforeResume, bool waitForGameWindow = true)
|
||||
{
|
||||
Process process = null;
|
||||
|
||||
var userName = Environment.UserName;
|
||||
|
||||
var pExplicitAccess = default(PInvoke.EXPLICIT_ACCESS);
|
||||
PInvoke.BuildExplicitAccessWithName(
|
||||
ref pExplicitAccess,
|
||||
userName,
|
||||
PInvoke.STANDARD_RIGHTS_ALL | PInvoke.SPECIFIC_RIGHTS_ALL & ~PInvoke.PROCESS_VM_WRITE,
|
||||
PInvoke.GRANT_ACCESS,
|
||||
0);
|
||||
|
||||
if (PInvoke.SetEntriesInAcl(1, ref pExplicitAccess, IntPtr.Zero, out var newAcl) != 0)
|
||||
var psecDesc = IntPtr.Zero;
|
||||
if (!dontFixAcl)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
var userName = Environment.UserName;
|
||||
|
||||
if (!PInvoke.InitializeSecurityDescriptor(out var secDesc, PInvoke.SECURITY_DESCRIPTOR_REVISION))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
var pExplicitAccess = default(PInvoke.EXPLICIT_ACCESS);
|
||||
PInvoke.BuildExplicitAccessWithName(
|
||||
ref pExplicitAccess,
|
||||
userName,
|
||||
PInvoke.STANDARD_RIGHTS_ALL | PInvoke.SPECIFIC_RIGHTS_ALL & ~PInvoke.PROCESS_VM_WRITE,
|
||||
PInvoke.GRANT_ACCESS,
|
||||
0);
|
||||
|
||||
if (!PInvoke.SetSecurityDescriptorDacl(ref secDesc, true, newAcl, false))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
if (PInvoke.SetEntriesInAcl(1, ref pExplicitAccess, IntPtr.Zero, out var newAcl) != 0)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
var psecDesc = Marshal.AllocHGlobal(Marshal.SizeOf<PInvoke.SECURITY_DESCRIPTOR>());
|
||||
Marshal.StructureToPtr(secDesc, psecDesc, true);
|
||||
if (!PInvoke.InitializeSecurityDescriptor(out var secDesc, PInvoke.SECURITY_DESCRIPTOR_REVISION))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
if (!PInvoke.SetSecurityDescriptorDacl(ref secDesc, true, newAcl, false))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
psecDesc = Marshal.AllocHGlobal(Marshal.SizeOf<PInvoke.SECURITY_DESCRIPTOR>());
|
||||
Marshal.StructureToPtr(secDesc, psecDesc, true);
|
||||
}
|
||||
|
||||
var lpProcessInformation = default(PInvoke.PROCESS_INFORMATION);
|
||||
try
|
||||
|
|
@ -75,7 +81,15 @@ namespace Dalamud.Injector
|
|||
|
||||
var compatLayerPrev = Environment.GetEnvironmentVariable("__COMPAT_LAYER");
|
||||
|
||||
Environment.SetEnvironmentVariable("__COMPAT_LAYER", "RunAsInvoker");
|
||||
if (!string.IsNullOrEmpty(compatLayerPrev) && !compatLayerPrev.Contains("RunAsInvoker"))
|
||||
{
|
||||
Environment.SetEnvironmentVariable("__COMPAT_LAYER", $"RunAsInvoker {compatLayerPrev}");
|
||||
}
|
||||
else if (string.IsNullOrEmpty(compatLayerPrev))
|
||||
{
|
||||
Environment.SetEnvironmentVariable("__COMPAT_LAYER", "RunAsInvoker");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!PInvoke.CreateProcess(
|
||||
|
|
@ -98,7 +112,8 @@ namespace Dalamud.Injector
|
|||
Environment.SetEnvironmentVariable("__COMPAT_LAYER", compatLayerPrev);
|
||||
}
|
||||
|
||||
DisableSeDebug(lpProcessInformation.hProcess);
|
||||
if (!dontFixAcl)
|
||||
DisableSeDebug(lpProcessInformation.hProcess);
|
||||
|
||||
process = new ExistingProcess(lpProcessInformation.hProcess);
|
||||
|
||||
|
|
@ -107,49 +122,40 @@ namespace Dalamud.Injector
|
|||
PInvoke.ResumeThread(lpProcessInformation.hThread);
|
||||
|
||||
// Ensure that the game main window is prepared
|
||||
try
|
||||
if (waitForGameWindow)
|
||||
{
|
||||
do
|
||||
try
|
||||
{
|
||||
process.WaitForInputIdle();
|
||||
var tries = 0;
|
||||
const int maxTries = 420;
|
||||
const int timeout = 50;
|
||||
|
||||
Thread.Sleep(100);
|
||||
do
|
||||
{
|
||||
Thread.Sleep(timeout);
|
||||
|
||||
if (process.HasExited)
|
||||
throw new GameStartException();
|
||||
|
||||
if (tries > maxTries)
|
||||
throw new GameStartException($"Couldn't find game window after {maxTries * timeout}ms");
|
||||
|
||||
tries++;
|
||||
}
|
||||
while (TryFindGameWindow(process) == IntPtr.Zero);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
throw new GameStartException("Could not read process information.");
|
||||
}
|
||||
while (TryFindGameWindow(process) == IntPtr.Zero);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
throw new GameExitedException();
|
||||
}
|
||||
|
||||
if (PInvoke.GetSecurityInfo(
|
||||
PInvoke.GetCurrentProcess(),
|
||||
PInvoke.SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
|
||||
PInvoke.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
out var pACL,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero) != 0)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
if (PInvoke.SetSecurityInfo(
|
||||
lpProcessInformation.hProcess,
|
||||
PInvoke.SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
|
||||
PInvoke.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION | PInvoke.SECURITY_INFORMATION.UNPROTECTED_DACL_SECURITY_INFORMATION,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
pACL,
|
||||
IntPtr.Zero) != 0)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
if (!dontFixAcl)
|
||||
CopyAclFromSelfToTargetProcess(lpProcessInformation.hProcess);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "[NativeAclFix] Uncaught error during initialization, trying to kill process");
|
||||
Log.Error(ex, "[GameStart] Uncaught error during initialization, trying to kill process");
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -157,20 +163,98 @@ namespace Dalamud.Injector
|
|||
}
|
||||
catch (Exception killEx)
|
||||
{
|
||||
Log.Error(killEx, "[NativeAclFix] Could not kill process");
|
||||
Log.Error(killEx, "[GameStart] Could not kill process");
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(psecDesc);
|
||||
if (psecDesc != IntPtr.Zero)
|
||||
Marshal.FreeHGlobal(psecDesc);
|
||||
PInvoke.CloseHandle(lpProcessInformation.hThread);
|
||||
}
|
||||
|
||||
return process;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies ACL of current process to the target process.
|
||||
/// </summary>
|
||||
/// <param name="hProcess">Native handle to the target process.</param>
|
||||
/// <exception cref="Win32Exception">Thrown when a win32 error occurs.</exception>
|
||||
public static void CopyAclFromSelfToTargetProcess(IntPtr hProcess)
|
||||
{
|
||||
if (PInvoke.GetSecurityInfo(
|
||||
PInvoke.GetCurrentProcess(),
|
||||
PInvoke.SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
|
||||
PInvoke.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
out var pACL,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero) != 0)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
if (PInvoke.SetSecurityInfo(
|
||||
hProcess,
|
||||
PInvoke.SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
|
||||
PInvoke.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION | PInvoke.SECURITY_INFORMATION.UNPROTECTED_DACL_SECURITY_INFORMATION,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
pACL,
|
||||
IntPtr.Zero) != 0)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
|
||||
public static void ClaimSeDebug()
|
||||
{
|
||||
var hToken = PInvoke.INVALID_HANDLE_VALUE;
|
||||
try
|
||||
{
|
||||
if (!PInvoke.OpenThreadToken(PInvoke.GetCurrentThread(), PInvoke.TOKEN_QUERY | PInvoke.TOKEN_ADJUST_PRIVILEGES, false, out hToken))
|
||||
{
|
||||
if (Marshal.GetLastWin32Error() != PInvoke.ERROR_NO_TOKEN)
|
||||
throw new Exception("ClaimSeDebug.OpenProcessToken#1", new Win32Exception(Marshal.GetLastWin32Error()));
|
||||
|
||||
if (!PInvoke.ImpersonateSelf(PInvoke.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation))
|
||||
throw new Exception("ClaimSeDebug.ImpersonateSelf", new Win32Exception(Marshal.GetLastWin32Error()));
|
||||
|
||||
if (!PInvoke.OpenThreadToken(PInvoke.GetCurrentThread(), PInvoke.TOKEN_QUERY | PInvoke.TOKEN_ADJUST_PRIVILEGES, false, out hToken))
|
||||
throw new Exception("ClaimSeDebug.OpenProcessToken#2", new Win32Exception(Marshal.GetLastWin32Error()));
|
||||
}
|
||||
|
||||
var luidDebugPrivilege = default(PInvoke.LUID);
|
||||
if (!PInvoke.LookupPrivilegeValue(null, PInvoke.SE_DEBUG_NAME, ref luidDebugPrivilege))
|
||||
throw new Exception("ClaimSeDebug.LookupPrivilegeValue", new Win32Exception(Marshal.GetLastWin32Error()));
|
||||
|
||||
var tpLookup = new PInvoke.TOKEN_PRIVILEGES()
|
||||
{
|
||||
PrivilegeCount = 1,
|
||||
Privileges = new PInvoke.LUID_AND_ATTRIBUTES[1]
|
||||
{
|
||||
new PInvoke.LUID_AND_ATTRIBUTES()
|
||||
{
|
||||
Luid = luidDebugPrivilege,
|
||||
Attributes = PInvoke.SE_PRIVILEGE_ENABLED,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (!PInvoke.AdjustTokenPrivileges(hToken, false, ref tpLookup, 0, IntPtr.Zero, IntPtr.Zero))
|
||||
throw new Exception("ClaimSeDebug.AdjustTokenPrivileges", new Win32Exception(Marshal.GetLastWin32Error()));
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (hToken != PInvoke.INVALID_HANDLE_VALUE && hToken != IntPtr.Zero)
|
||||
PInvoke.CloseHandle(hToken);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DisableSeDebug(IntPtr processHandle)
|
||||
{
|
||||
if (!PInvoke.OpenProcessToken(processHandle, PInvoke.TOKEN_QUERY | PInvoke.TOKEN_ADJUST_PRIVILEGES, out var tokenHandle))
|
||||
|
|
@ -179,7 +263,7 @@ namespace Dalamud.Injector
|
|||
}
|
||||
|
||||
var luidDebugPrivilege = default(PInvoke.LUID);
|
||||
if (!PInvoke.LookupPrivilegeValue(null, "SeDebugPrivilege", ref luidDebugPrivilege))
|
||||
if (!PInvoke.LookupPrivilegeValue(null, PInvoke.SE_DEBUG_NAME, ref luidDebugPrivilege))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
|
@ -211,7 +295,7 @@ namespace Dalamud.Injector
|
|||
tokenPrivileges.Privileges[0].Luid = luidDebugPrivilege;
|
||||
tokenPrivileges.Privileges[0].Attributes = PInvoke.SE_PRIVILEGE_REMOVED;
|
||||
|
||||
if (!PInvoke.AdjustTokenPrivileges(tokenHandle, false, ref tokenPrivileges, 0, IntPtr.Zero, 0))
|
||||
if (!PInvoke.AdjustTokenPrivileges(tokenHandle, false, ref tokenPrivileges, 0, IntPtr.Zero, IntPtr.Zero))
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
|
@ -239,13 +323,14 @@ namespace Dalamud.Injector
|
|||
/// <summary>
|
||||
/// Exception thrown when the process has exited before a window could be found.
|
||||
/// </summary>
|
||||
public class GameExitedException : Exception
|
||||
public class GameStartException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GameExitedException"/> class.
|
||||
/// Initializes a new instance of the <see cref="GameStartException"/> class.
|
||||
/// </summary>
|
||||
public GameExitedException()
|
||||
: base("Game exited prematurely.")
|
||||
/// <param name="message">The message to pass on.</param>
|
||||
public GameStartException(string? message = null)
|
||||
: base(message ?? "Game exited prematurely.")
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -260,6 +345,10 @@ namespace Dalamud.Injector
|
|||
private static class PInvoke
|
||||
{
|
||||
#region Constants
|
||||
public static readonly IntPtr INVALID_HANDLE_VALUE = new(-1);
|
||||
|
||||
public const string SE_DEBUG_NAME = "SeDebugPrivilege";
|
||||
|
||||
public const UInt32 STANDARD_RIGHTS_ALL = 0x001F0000;
|
||||
public const UInt32 SPECIFIC_RIGHTS_ALL = 0x0000FFFF;
|
||||
public const UInt32 PROCESS_VM_WRITE = 0x0020;
|
||||
|
|
@ -278,6 +367,8 @@ namespace Dalamud.Injector
|
|||
public const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002;
|
||||
public const UInt32 SE_PRIVILEGE_REMOVED = 0x00000004;
|
||||
|
||||
public const UInt32 ERROR_NO_TOKEN = 0x000003F0;
|
||||
|
||||
public enum MULTIPLE_TRUSTEE_OPERATION
|
||||
{
|
||||
NO_MULTIPLE_TRUSTEE,
|
||||
|
|
@ -334,6 +425,14 @@ namespace Dalamud.Injector
|
|||
UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000,
|
||||
PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000,
|
||||
}
|
||||
|
||||
public enum SECURITY_IMPERSONATION_LEVEL
|
||||
{
|
||||
SecurityAnonymous,
|
||||
SecurityIdentification,
|
||||
SecurityImpersonation,
|
||||
SecurityDelegation
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
|
@ -384,12 +483,24 @@ namespace Dalamud.Injector
|
|||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern uint ResumeThread(IntPtr hThread);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
public static extern bool ImpersonateSelf(
|
||||
SECURITY_IMPERSONATION_LEVEL impersonationLevel
|
||||
);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
public static extern bool OpenProcessToken(
|
||||
IntPtr processHandle,
|
||||
UInt32 desiredAccess,
|
||||
out IntPtr tokenHandle);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
public static extern bool OpenThreadToken(
|
||||
IntPtr ThreadHandle,
|
||||
uint DesiredAccess,
|
||||
bool OpenAsSelf,
|
||||
out IntPtr TokenHandle);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref LUID lpLuid);
|
||||
|
||||
|
|
@ -404,9 +515,9 @@ namespace Dalamud.Injector
|
|||
IntPtr tokenHandle,
|
||||
bool disableAllPrivileges,
|
||||
ref TOKEN_PRIVILEGES newState,
|
||||
UInt32 bufferLengthInBytes,
|
||||
int cbPreviousState,
|
||||
IntPtr previousState,
|
||||
UInt32 returnLengthInBytes);
|
||||
IntPtr cbOutPreviousState);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
public static extern uint GetSecurityInfo(
|
||||
|
|
@ -432,6 +543,9 @@ namespace Dalamud.Injector
|
|||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr GetCurrentProcess();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
public static extern IntPtr GetCurrentThread();
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr hWndChildAfter, string className, IntPtr windowTitle);
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue