diff --git a/Dalamud.Bootstrap/Bootstrapper.cs b/Dalamud.Bootstrap/Bootstrapper.cs index c4ccd48c6..d90a29b70 100644 --- a/Dalamud.Bootstrap/Bootstrapper.cs +++ b/Dalamud.Bootstrap/Bootstrapper.cs @@ -53,15 +53,11 @@ namespace Dalamud.Bootstrap // Acquire the process handle and read the command line using var process = Process.Open(pid); - var commandLine = process.ReadCommandLine(); - - // Recover the key - - // TODO: check if contains arg[1] - - if EncryptedArgument.Extract(commandLine[1], ) + var argument = ReadGameArgument(process); + argument.Remove("T"); + //var newCommandLine = // TODO: // .... if arg1 exists // DecodeSqexArg(arguments[1]); @@ -89,6 +85,22 @@ namespace Dalamud.Bootstrap return createdTick & 0xFFFF_0000; } + private static ArgumentBuilder ReadGameArgument(Process process) + { + var arguments = process.ReadArguments(); + + if (arguments.Length < 1) + { + throw new BootstrapException($"Process id {process.Id} does not have any arguments to parse."); + } + + if (EncryptedArgument.Extract(arguments[0], out var encryptedPayload, out var _)) + { + var key = RecoverKey(process); + EncryptedArgument.Decry(encryptedPayload, key) + } + } + /// /// Injects Dalamud into the process. See remarks for process state prerequisites. /// diff --git a/Dalamud.Bootstrap/SqexArg/EncryptedArgument.cs b/Dalamud.Bootstrap/SqexArg/EncryptedArgument.cs index 210bc251d..2b4c7a479 100644 --- a/Dalamud.Bootstrap/SqexArg/EncryptedArgument.cs +++ b/Dalamud.Bootstrap/SqexArg/EncryptedArgument.cs @@ -8,7 +8,7 @@ using Dalamud.Bootstrap.Crypto; namespace Dalamud.Bootstrap.SqexArg { - internal sealed class EncryptedArgument : IDisposable + internal sealed class EncryptedArgument { private static char[] ChecksumTable = new char[] { @@ -21,45 +21,26 @@ namespace Dalamud.Bootstrap.SqexArg /// private const char NoChecksumMarker = '!'; - /// - /// A data that is not encrypted. - /// - private IMemoryOwner m_data; + private IMemoryOwner m_encryptedData; /// - /// Creates an object that can take (e.g. /T=1234) + /// Encrypts the argument with given key. /// - /// A data that is not encrypted. - /// - /// This takes the ownership of the data. - /// - public EncryptedArgument(IMemoryOwner data) + /// + /// + public EncryptedArgument(string argument, uint key) { - m_data = data; - } - - public EncryptedArgument(string argument) - { - var buffer = MemoryPool.Shared.Rent(Encoding.UTF8.GetByteCount(argument)); - Encoding.UTF8.GetBytes(argument, buffer.Memory.Span); - - m_data = buffer; - } - - public void Dispose() - { - m_data?.Dispose(); - m_data = null!; + } /// /// Extracts the payload and checksum from the encrypted argument. /// /// - /// An encrypted payload encoded in url-safe base64 string extracted. The value is undefined if the function fails. + /// An encrypted payload extracted. The value is undefined if the function fails. /// A checksum of the key extracted. The value is undefined if the function fails. /// Returns true on success, false otherwise. - public static bool Extract(string argument, out string payload, out char checksum) + public static bool Extract(string argument, out byte[] payload, out char checksum) { // must start with //**sqex0003, some characters, one checksum character and end with **// var regex = new Regex(@"^\/\/\*\*sqex0003(?.+)(?.)\*\*\/\/$"); @@ -67,45 +48,31 @@ namespace Dalamud.Bootstrap.SqexArg var match = regex.Match(argument); if (!match.Success) { - payload = ""; + payload = null!; checksum = '\0'; return false; } - payload = match.Groups["payload"].Value; + // Extract checksum checksum = match.Groups["checksum"].Value[0]; + + // Extract payload + var payloadStr = match.Groups["payload"].Value; + payload = DecodeUrlSafeBase64(payloadStr); + return true; } - public static EncryptedArgument FromEncryptedData(string argument, uint key) + public override string ToString() { - // Create the key - Span keyBytes = stackalloc byte[8]; - CreateKey(key, keyBytes); + var checksum = GetChecksumFromKey(); + } - if (!Extract(argument, out var encryptedStr, out var _)) - { - throw new SqexArgException($"Could not extract the argument and checksum from {argument}"); - } - - var encryptedData = DecodeUrlSafeBase64(encryptedStr); - - // Allocate the buffer to store decrypted data - var decryptedData = CreateAlignedBuffer(encryptedData.Length); + private static char GetChecksumFromKey(uint key) + { + var index = (key & 0x000F_0000) >> 16; - // Decrypt the data with the key - try - { - var blowfish = new Blowfish(keyBytes); - blowfish.Decrypt(encryptedData, decryptedData.Memory.Span); - } - catch (Exception) - { - decryptedData?.Dispose(); // TODO: clean up this thing? - throw; - } - - return new EncryptedArgument(decryptedData); + return ChecksumTable[index]; } /// @@ -147,12 +114,25 @@ namespace Dalamud.Bootstrap.SqexArg .Replace('-', '+') .Replace('_', '/'); - return Convert.FromBase64String(base64Str); + try + { + return Convert.FromBase64String(base64Str); + } + catch (FormatException ex) + { + // This is expected to happen if the argument is ill-formed + throw new SqexArgException($"A payload {payload} does not look like a valid encrypted argument.", ex); + } } - public string Encrypt(uint key) + private static string EncodeUrlSafeBase64(byte[] payload) { + var payloadStr = Convert.ToBase64String(payload); + return payloadStr + .Replace('+', '-') + .Replace('/', '_'); + } } } diff --git a/Dalamud.Bootstrap/Windows/Process.cs b/Dalamud.Bootstrap/Windows/Process.cs index a18af5e0e..19426b74e 100644 --- a/Dalamud.Bootstrap/Windows/Process.cs +++ b/Dalamud.Bootstrap/Windows/Process.cs @@ -140,7 +140,7 @@ namespace Dalamud.Bootstrap } } - public string[] ReadCommandLine() + public string[] ReadArguments() { unsafe { @@ -151,7 +151,8 @@ namespace Dalamud.Bootstrap // Read the command line (which is utf16-like string) var commandLine = ReadMemoryExact(procParam.CommandLine.Buffer, procParam.CommandLine.Length); - return ParseCommandLine(commandLine); + + return ParseCommandLineToArguments(commandLine); } } @@ -173,7 +174,7 @@ namespace Dalamud.Bootstrap } } - private string[] ParseCommandLine(ReadOnlySpan commandLine) + private string[] ParseCommandLineToArguments(ReadOnlySpan commandLine) { unsafe {