mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-02-21 07:17:45 +01:00
Impl encrypt
This commit is contained in:
parent
3e6675adc0
commit
5dd8491d13
4 changed files with 161 additions and 100 deletions
|
|
@ -56,26 +56,19 @@ namespace Dalamud.Bootstrap
|
||||||
|
|
||||||
var argument = ReadArgumentFromProcess(process);
|
var argument = ReadArgumentFromProcess(process);
|
||||||
|
|
||||||
var newTick = Environment.TickCount;
|
var newTick = (uint)Environment.TickCount;
|
||||||
var newKey = newTick & 0xFFFF_0000; // only the high nibble is used
|
var newKey = newTick & 0xFFFF_0000; // only the high nibble is used
|
||||||
|
|
||||||
var newArgument = argument.Remove("T")
|
var newArgument = argument
|
||||||
|
.Remove("T")
|
||||||
.Add("T", $"{newTick}")
|
.Add("T", $"{newTick}")
|
||||||
.ToString();
|
.ToString();
|
||||||
|
|
||||||
// TODO: encode as a encrypted?
|
var encryptedArgument = new EncryptedArgument(newArgument, newKey);
|
||||||
// TODO: launch it
|
|
||||||
|
|
||||||
|
// TODO: launch new exe with the argument from encryptedArgument.ToString()
|
||||||
|
|
||||||
//var newCommandLine =
|
|
||||||
// TODO:
|
|
||||||
// .... if arg1 exists
|
|
||||||
// DecodeSqexArg(arguments[1]);
|
|
||||||
// args = ParseArgument()
|
|
||||||
// FindArguments(args, "T")
|
|
||||||
// RemoveArgs(args, "T")
|
|
||||||
// AddArgs(args, "T", newTick)
|
|
||||||
// str = ToString()
|
|
||||||
// EncodeSqexArg(str, newKey)
|
|
||||||
|
|
||||||
process.Terminate();
|
process.Terminate();
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +104,7 @@ namespace Dalamud.Bootstrap
|
||||||
argument = encryptedArgument.Decrypt(key);
|
argument = encryptedArgument.Decrypt(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ArgumentBuilder.TryParse(argument);
|
return ArgumentBuilder.Parse(argument);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -143,11 +136,11 @@ namespace Dalamud.Bootstrap
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
const string message = "Failed to inject Dalamud library into the process.";
|
var exMessage = $"Failed to inject Dalamud library into the process id {pid}.";
|
||||||
|
|
||||||
// Could not inject Dalamud for whatever reason; it could be process is not actually running, insufficient os privilege, or whatever the thing SE put in their game;
|
// Could not inject Dalamud for whatever reason; it could be process is not actually running, insufficient os privilege, or whatever the thing SE put in their game;
|
||||||
// Therefore there's not much we can do on this side; You have to trobleshoot by yourself somehow.
|
// Therefore there's not much we can do on this side; You have to trobleshoot by yourself somehow.
|
||||||
throw new ProcessException(message, pid, ex);
|
throw new BootstrapException(exMessage, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,11 @@ namespace Dalamud.Bootstrap.SqexArg
|
||||||
m_dict = new Dictionary<string, string>();
|
m_dict = new Dictionary<string, string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ArgumentBuilder Parse(string argument)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public ArgumentBuilder Add(string key, string value)
|
public ArgumentBuilder Add(string key, string value)
|
||||||
{
|
{
|
||||||
m_dict.Add(key, value);
|
m_dict.Add(key, value);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,20 @@
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.Buffers.Text;
|
||||||
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using Dalamud.Bootstrap.Crypto;
|
||||||
|
|
||||||
namespace Dalamud.Bootstrap.SqexArg
|
namespace Dalamud.Bootstrap.SqexArg
|
||||||
{
|
{
|
||||||
internal sealed class EncryptedArgument
|
internal sealed class EncryptedArgument
|
||||||
{
|
{
|
||||||
|
private static readonly char[] ChecksumTable =
|
||||||
|
{
|
||||||
|
'f', 'X', '1', 'p', 'G', 't', 'd', 'S',
|
||||||
|
'5', 'C', 'A', 'P', '4', '_', 'V', 'L'
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A data that is encrypted and encoded in url-safe variant of base64.
|
/// A data that is encrypted and encoded in url-safe variant of base64.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -14,12 +25,127 @@ namespace Dalamud.Bootstrap.SqexArg
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public char Checksum { get; }
|
public char Checksum { get; }
|
||||||
|
|
||||||
public EncryptedArgument(string data, char checksum)
|
/// <summary>
|
||||||
|
/// Creates an encrypted argument.
|
||||||
|
/// Unlike other constructors, this does not encrypt the data passed to encdoedData parameter and assume that process is already done.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="encodedData">A string that is already encrypted and encoded to url-safe variant of base64.</param>
|
||||||
|
/// <param name="checksum">A checksum that is used to validate the encryption key.</param>
|
||||||
|
private EncryptedArgument(string encodedData, char checksum)
|
||||||
{
|
{
|
||||||
Data = data;
|
Data = encodedData;
|
||||||
Checksum = checksum;
|
Checksum = checksum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encrypts a string with given key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="plainText">A data that is not encrypted.</param>
|
||||||
|
/// <param name="key">A key that is used to encrypt the data.</param>
|
||||||
|
public EncryptedArgument(string plainText, uint key)
|
||||||
|
{
|
||||||
|
Span<byte> keyBytes = stackalloc byte[8];
|
||||||
|
CreateKey(key, keyBytes);
|
||||||
|
|
||||||
|
var blowfish = new Blowfish(keyBytes);
|
||||||
|
|
||||||
|
Data = EncodeString(plainText, blowfish);
|
||||||
|
Checksum = GetChecksum(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static char GetChecksum(uint key)
|
||||||
|
{
|
||||||
|
// There's no OoB since ChecksumTable has 16 elements and we mask the key with 0xF
|
||||||
|
var index = (key >> 16) & 0x0000_000F;
|
||||||
|
|
||||||
|
return ChecksumTable[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="argument"></param>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="SqexArgException">Thrown when the data property does not have a valid base64 string.</exception>
|
||||||
|
public string Decrypt(uint key)
|
||||||
|
{
|
||||||
|
Span<byte> keyBytes = stackalloc byte[8];
|
||||||
|
CreateKey(key, keyBytes);
|
||||||
|
|
||||||
|
var blowfish = new Blowfish(keyBytes);
|
||||||
|
|
||||||
|
return DecodeString(Data, blowfish);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="payload"></param>
|
||||||
|
/// <param name="blowfish"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="SqexArgException">Thrown when the data property does not have a valid base64 string.</exception>
|
||||||
|
private static string DecodeString(string payload, Blowfish blowfish)
|
||||||
|
{
|
||||||
|
// plainText <- utf8Bytes <- encryptedBytes(payload) <- base64(payloadStr)
|
||||||
|
|
||||||
|
// base64: 3 bytes per 4 characters
|
||||||
|
// We also want the size to be aligned with the block size.
|
||||||
|
var dataLength = (payload.Length / 4) * 3;
|
||||||
|
var alignedDataLength = AlignBufferLength(dataLength);
|
||||||
|
var encryptedData = new byte[alignedDataLength];
|
||||||
|
var decryptedData = new byte[alignedDataLength];
|
||||||
|
|
||||||
|
// Converts to standard base64 string so that we can feed it to stdlib
|
||||||
|
var base64Str = payload
|
||||||
|
.Replace('-', '+')
|
||||||
|
.Replace('_', '/');
|
||||||
|
|
||||||
|
// base64 -> encryptedBytes
|
||||||
|
if (!Convert.TryFromBase64String(base64Str, encryptedData, out var _))
|
||||||
|
{
|
||||||
|
// We don't care about bytesWritten because we can't handle failure anyway
|
||||||
|
throw new SqexArgException($"A payload {payload} does not look like a valid encrypted argument.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// encryptedBytes -> utf8Bytes (decrypted)
|
||||||
|
blowfish.Decrypt(encryptedData, decryptedData);
|
||||||
|
|
||||||
|
// utf8Bytes -> C# string
|
||||||
|
var plainText = Encoding.UTF8.GetString(decryptedData[..dataLength]);
|
||||||
|
|
||||||
|
return plainText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts plain text string to url-safe variant of base64 string.
|
||||||
|
/// </summary>
|
||||||
|
private static string EncodeString(string plainText, Blowfish blowfish)
|
||||||
|
{
|
||||||
|
// plainText -> utf8Bytes -> encryptedBytes(payload) -> base64(payloadStr)
|
||||||
|
|
||||||
|
// This is needed because we want the size to be aligned with the block size. We'll also need to pad them zero.
|
||||||
|
var utf8BytesLength = AlignBufferLength(Encoding.UTF8.GetByteCount(plainText));
|
||||||
|
var utf8Bytes = new byte[utf8BytesLength];
|
||||||
|
|
||||||
|
// We also need the buffer to store encrypted bytes
|
||||||
|
var encryptedBytes = new byte[utf8Bytes.Length];
|
||||||
|
|
||||||
|
// Now we can the string to UTF8
|
||||||
|
// NOTE: This should fail as GetByteCount returns the exact size required to encode utf8 string, but if this assumption is wrong, please make an issue.
|
||||||
|
Encoding.UTF8.GetBytes(plainText, utf8Bytes);
|
||||||
|
|
||||||
|
// Encrypt it
|
||||||
|
blowfish.Encrypt(utf8Bytes, encryptedBytes);
|
||||||
|
|
||||||
|
// Convert to url-safe variant of base64
|
||||||
|
var base64Str = Convert.ToBase64String(encryptedBytes, Base64FormattingOptions.None)
|
||||||
|
.Replace('+', '-')
|
||||||
|
.Replace('/', '_');
|
||||||
|
|
||||||
|
return base64Str;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -65,5 +191,23 @@ namespace Dalamud.Bootstrap.SqexArg
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString() => $"//**sqex0003{Data}{Checksum}**//";
|
public override string ToString() => $"//**sqex0003{Data}{Checksum}**//";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Formats a key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">A secret key.</param>
|
||||||
|
/// <param name="destination">A buffer where formatted key will be stored. This must be larger than 8 bytes.</param>
|
||||||
|
internal static void CreateKey(uint key, Span<byte> destination)
|
||||||
|
{
|
||||||
|
if (!Utf8Formatter.TryFormat(key, destination, out var _, new StandardFormat('x', 8)))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("BUG: Could not create a key");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rounds to next mutliple of block size (i.e. 8 bytes) to satisfy with Blowfish requirements.
|
||||||
|
/// </summary>
|
||||||
|
internal static int AlignBufferLength(int length) => (length + (Blowfish.BlockSize - 1)) & (-Blowfish.BlockSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Buffers;
|
|
||||||
using System.Buffers.Text;
|
|
||||||
using System.Text;
|
|
||||||
using Dalamud.Bootstrap.Crypto;
|
|
||||||
|
|
||||||
namespace Dalamud.Bootstrap.SqexArg
|
|
||||||
{
|
|
||||||
internal static class EncryptedArgumentExtensions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="argument"></param>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="SqexArgException">Thrown when the data property does not have a valid base64 string.</exception>
|
|
||||||
public static string Decrypt(this EncryptedArgument argument, uint key)
|
|
||||||
{
|
|
||||||
Span<byte> keyBytes = stackalloc byte[8];
|
|
||||||
CreateKey(key, keyBytes);
|
|
||||||
|
|
||||||
var encryptedData = DecodeDataString(argument.Data, out var encryptedDataLength);
|
|
||||||
var decryptedData = new byte[encryptedData.Length];
|
|
||||||
|
|
||||||
var blowfish = new Blowfish(keyBytes);
|
|
||||||
blowfish.Decrypt(encryptedData, decryptedData);
|
|
||||||
|
|
||||||
return Encoding.UTF8.GetString(decryptedData[..encryptedDataLength]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Formats a key.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">A secret key.</param>
|
|
||||||
/// <param name="destination">A buffer where formatted key will be stored. This must be larger than 8 bytes.</param>
|
|
||||||
private static void CreateKey(uint key, Span<byte> destination)
|
|
||||||
{
|
|
||||||
if (!Utf8Formatter.TryFormat(key, destination, out var _, new StandardFormat('x', 8)))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("BUG: Could not create a key");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the data string to bytes. It also allocates more bytes than actual data contained in base64 string for Blowfish.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="payload">A url-safe variant of base64 string.</param>
|
|
||||||
/// <param name="payload">A data length that is actually written to the buffer.</param>
|
|
||||||
private static byte[] DecodeDataString(string payload, out int dataLength)
|
|
||||||
{
|
|
||||||
var base64Str = payload
|
|
||||||
.Replace('-', '+')
|
|
||||||
.Replace('_', '/');
|
|
||||||
|
|
||||||
// base64: 3 bytes per 4 characters
|
|
||||||
dataLength = (payload.Length / 4) * 3;
|
|
||||||
|
|
||||||
// round to next mutliple of block size which is what Blowfish can process. (i.e. 8 bytes)
|
|
||||||
var alignedLength = (dataLength + (Blowfish.BlockSize - 1)) & (-Blowfish.BlockSize);
|
|
||||||
|
|
||||||
var buffer = new byte[alignedLength];
|
|
||||||
|
|
||||||
if (!Convert.TryFromBase64String(base64Str, buffer, out var _))
|
|
||||||
{
|
|
||||||
throw new SqexArgException($"A payload {payload} does not look like a valid encrypted argument.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string EncodeUrlSafeBase64(byte[] payload)
|
|
||||||
{
|
|
||||||
var payloadStr = Convert.ToBase64String(payload);
|
|
||||||
|
|
||||||
return payloadStr
|
|
||||||
.Replace('+', '-')
|
|
||||||
.Replace('/', '_');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue