diff --git a/Dalamud.Bootstrap/Bootstrapper.cs b/Dalamud.Bootstrap/Bootstrapper.cs
index a9f63297d..c4ccd48c6 100644
--- a/Dalamud.Bootstrap/Bootstrapper.cs
+++ b/Dalamud.Bootstrap/Bootstrapper.cs
@@ -24,6 +24,13 @@ namespace Dalamud.Bootstrap
throw new NotImplementedException("TODO");
}
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Thrown when it could not relaunch FINAL FANTASY XIV or inject Dalamud.
+ ///
public void Relaunch(uint pid)
{
// TODO
@@ -48,10 +55,11 @@ namespace Dalamud.Bootstrap
using var process = Process.Open(pid);
var commandLine = process.ReadCommandLine();
- if (!EncodedArgument.Parse(commandLine[1], out var container))
- {
+ // Recover the key
- }
+ // TODO: check if contains arg[1]
+
+ if EncryptedArgument.Extract(commandLine[1], )
// TODO:
@@ -67,6 +75,20 @@ namespace Dalamud.Bootstrap
process.Terminate();
}
+ private static uint RecoverKey(Process gameProcess)
+ {
+ var createdTime = gameProcess.GetCreationTime();
+
+ var currentDt = DateTime.Now;
+ var currentTick = Environment.TickCount;
+
+ var delta = currentDt - createdTime;
+ var createdTick = (uint)currentTick - (uint)delta.TotalMilliseconds;
+
+ // only the high nibble is used.
+ return createdTick & 0xFFFF_0000;
+ }
+
///
/// Injects Dalamud into the process. See remarks for process state prerequisites.
///
diff --git a/Dalamud.Bootstrap/SqexArg/EncodedArgument.cs b/Dalamud.Bootstrap/SqexArg/EncodedArgument.cs
deleted file mode 100644
index 32be71571..000000000
--- a/Dalamud.Bootstrap/SqexArg/EncodedArgument.cs
+++ /dev/null
@@ -1,190 +0,0 @@
-using System;
-using System.Buffers;
-using System.Buffers.Text;
-using System.Runtime.InteropServices;
-using System.Text;
-using Dalamud.Bootstrap.Crypto;
-
-namespace Dalamud.Bootstrap.SqexArg
-{
- internal sealed class EncodedArgument : IDisposable
- {
- private static char[] ChecksumTable = new char[]
- {
- 'f', 'X', '1', 'p', 'G', 't', 'd', 'S',
- '5', 'C', 'A', 'P', '4', '_', 'V', 'L'
- };
-
- ///
- /// Denotes that no checksum is encoded.
- ///
- private const char NoChecksumMarker = '!';
-
- ///
- /// A data that is not encrypted.
- ///
- private IMemoryOwner m_data;
-
- ///
- /// Creates an object that can take (e.g. /T=1234)
- ///
- /// A data that is not encrypted.
- ///
- /// This takes the ownership of the data.
- ///
- public EncodedArgument(IMemoryOwner data)
- {
- m_data = data;
- }
-
-
- public EncodedArgument(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!;
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- /// Thrown when the function could not parse the encoded argument.
- /// Message property will carry additional information.
- ///
- public static EncodedArgument Parse(string argument)
- {
- // check if argument contains is large enough to contain start marker, checksum and end marker.
- if (argument.Length < "//**sqex0003!**//".Length)
- {
- var exMessage = $"The string ({argument}) is too short to parse the encoded argument."
- + $" It should be atleast large enough to store the start marker,checksum and end marker..";
- throw new SqexArgException(exMessage);
- }
-
- if (!argument.StartsWith("//**sqex0003") || !argument[13..].EndsWith("**//"))
- {
- var exMessage = $"The string ({argument}) doesn't look like the valid argument."
- + $" It should start with //**sqex0003 and end with **// string.";
- throw new SqexArgException(exMessage);
- }
-
- // Extract the data
- var checksum = argument[^5];
- var encryptedData = DecodeUrlSafeBase64(argument.Substring(12, argument.Length - 1 - 12 - 4)); // //**sqex0003, checksum, **//
-
- // Dedice a partial key from the checksum
- var (partialKey, recoverStep) = RecoverKeyFragmentFromChecksum(checksum);
-
- var decryptedData = MemoryPool.Shared.Rent(encryptedData.Length);
- if (!RecoverKey(encryptedData, decryptedData.Memory.Span, partialKey, recoverStep))
- {
- // we need to free the memory to avoid a memory leak.
- decryptedData.Dispose();
-
- var exMessage = $"Could not find a valid key to decrypt the encoded argument.";
- throw new SqexArgException(exMessage);
- }
-
- return new EncodedArgument(decryptedData);
- }
-
- private static bool RecoverKey(ReadOnlySpan encryptedData, Span decryptedData, uint partialKey, uint recoverStep)
- {
-
- Span keyBytes = stackalloc byte[8];
- var keyCandicate = partialKey;
-
- while (true)
- {
- CreateKey(keyBytes, keyCandicate);
-
- var blowfish = new Blowfish(keyBytes);
- blowfish.Decrypt(encryptedData, decryptedData);
-
- // Check if the decrypted data looks valid
- if (CheckDecryptedData(decryptedData))
- {
- return true;
- }
-
- // Try again with the next key.
- try
- {
- keyCandicate = checked(keyCandicate + recoverStep);
- }
- catch (OverflowException)
- {
- // We've exhausted the key space and could not find a valid key.
- return false;
- }
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- private static bool CheckDecryptedData(ReadOnlySpan decryptedData)
- {
- // TODO
- return false;
- }
-
- ///
- /// Formats the key.
- ///
- /// A secret key.
- /// A buffer where formatted key will be stored. This must be larger than 8 bytes.
- private static void CreateKey(uint key, Span destination)
- {
- if (!Utf8Formatter.TryFormat(key, destination, out var _, new StandardFormat('X', 8)))
- {
- var message = $"BUG: Could not create a key"; // This should not fail but..
- throw new InvalidOperationException(message);
- }
- }
-
- ///
- /// Deduces a partial key from the checksum.
- ///
- ///
- /// `partialKey` can be or'd (a | partialKey) to recover some bits from the key.
- ///
- ///
- /// The partialKey here is very useful because it can further reduce the number of possible key
- /// from 0xFFFF to 0xFFF which is 16 times smaller. (and therefore we can initialize the blowfish 16 times less which is quite expensive to do so.)
- ///
- private static (uint partialKey, uint step) RecoverKeyFragmentFromChecksum(char checksum)
- {
- return MemoryExtensions.IndexOf(ChecksumTable, checksum) switch
- {
- -1 => (0x0001_0000, 0x0001_0000), // This covers '!' as well (no checksum are encoded)
- var index => ((uint) (index << 16), 0x0010_0000)
- };
- }
-
- ///
- /// Converts the url-safe variant of base64 string to bytes.
- ///
- /// A url-safe variant of base64 string.
- private static byte[] DecodeUrlSafeBase64(string payload)
- {
- var base64Str = payload
- .Replace('-', '+')
- .Replace('_', '/');
-
- return Convert.FromBase64String(base64Str);
- }
- }
-}
diff --git a/Dalamud.Bootstrap/Windows/Process.cs b/Dalamud.Bootstrap/Windows/Process.cs
index 9972b934e..a18af5e0e 100644
--- a/Dalamud.Bootstrap/Windows/Process.cs
+++ b/Dalamud.Bootstrap/Windows/Process.cs
@@ -155,6 +155,24 @@ namespace Dalamud.Bootstrap
}
}
+ ///
+ /// Returns a time when the process was started.
+ ///
+ public DateTime GetCreationTime()
+ {
+ unsafe
+ {
+ FileTime creationTime, exitTime, kernelTime, userTime;
+
+ if (Win32.GetProcessTimes(m_handle, &creationTime, &exitTime, &kernelTime, &userTime))
+ {
+ ProcessException.ThrowLastOsError(GetPid());
+ }
+
+ return (DateTime)creationTime;
+ }
+ }
+
private string[] ParseCommandLine(ReadOnlySpan commandLine)
{
unsafe
diff --git a/Dalamud.Bootstrap/Windows/Win32.cs b/Dalamud.Bootstrap/Windows/Win32.cs
index 6fa8104a9..b986561f0 100644
--- a/Dalamud.Bootstrap/Windows/Win32.cs
+++ b/Dalamud.Bootstrap/Windows/Win32.cs
@@ -32,16 +32,17 @@ namespace Dalamud.Bootstrap.Windows
[DllImport("kernel32", CallingConvention = CallingConvention.Winapi)]
public static extern uint GetProcessId(SafeProcessHandle hProcess);
+
+ [DllImport("kernel32", CallingConvention = CallingConvention.Winapi, SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool GetProcessTimes(SafeProcessHandle hProcess, FileTime* lpCreationTime, FileTime* lpExitTime, FileTime* lpKernelTime, FileTime* lpUserTime);
}
[StructLayout(LayoutKind.Sequential)]
- internal partial struct NtStatus
+ internal struct NtStatus
{
public uint Value { get; }
- }
- internal partial struct NtStatus
- {
public NtStatus(uint value)
{
Value = value;
@@ -70,6 +71,21 @@ namespace Dalamud.Bootstrap.Windows
public override string ToString() => $"0x{Value:X8}";
}
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct FileTime
+ {
+ public uint LowDateTime;
+
+ public uint HighDateTime;
+
+ public static explicit operator DateTime(FileTime value)
+ {
+ var time = ((long)value.HighDateTime << 32) | value.LowDateTime;
+
+ return DateTime.FromFileTime(time);
+ }
+ }
+
[StructLayout(LayoutKind.Sequential)]
internal struct UNICODE_STRING
{