docs.xiv.zone/content/concept/sqexarg.md

137 lines
5.5 KiB
Markdown
Raw Normal View History

2022-07-16 18:54:44 -04:00
---
title: "SqexArg"
---
I also recommend reading the relevant [xiv.dev page on SqexArg](https://xiv.dev/sqexarg).
## Purpose
This is the "encrypted argument" format used by a lot of FFXIV programs and is used in place of regular plaintext command line arguments. However, this is barely a security measure and just prevents easily snooping stuff like your login token. Despite this, the SqexArg format is well known, reversible and easily breakable.
## Format
When viewing the command line arguments for, say [ffxiv.exe](executable/ffxiv) you will see it's only something like this:
```
//**sqex0003S2_Utl8qdamv3_82SH7Lhtk=S**//
```
_(Yes, I did garble some of the text, so it's not actually decodable :-))_
There are three distinct parts of this string:
```
//**sqex0003S2_Utl8qdamv3_82SH7Lhtk=S**//
^^ ^
version|| |
| base64 string |
| checksum
```
Let's break them down:
* version
* From what I've seen, this is always `3`. I'm not sure if there any more meaning behind this, apart from they revised this 3 times.
* base64 string
* This is your usual base64-encoded string. However, there is a couple of important things to note:
* Use the URL-safe version of Base64.
* You may omit the trailing equal.
* The result is unreadable garbage, but how to encode/decode this will be revealed below.
* checksum
* This is also covered in a later section, but this is always one character long and located at the end of the string.
## Encryption Algorithm
The resulting bytes when you decode the base64 string is going to Blowfish ECB encrypted.
* However, please note that Square Enix does some weird bitflip endian-encoding nonsense which means your out-of-box Blowfish library might not work. I would highly recommend reading up on some existing implementations to get an idea of what to do:
* [XIVQuickLauncher (C#)](https://github.com/goatcorp/FFXIVQuickLauncher/blob/master/src/XIVLauncher.Common/Encryption/LegacyBlowfish.cs)
* [Astra (C++)](https://git.sr.ht/~redstrate/astra/tree/main/item/launcher/core/include/blowfish.h)
* [XIV-on-Mac (Swift)](https://github.com/marzent/XIV-on-Mac/blob/main/XIV%20on%20Mac/Encryption.swift)
* Before encrypting or decrypting, ensure the buffer is padded.
**Note:** In the new Steam launcher update, Square Enix has actually switched to a more sane version of Blowfish ECB without their weird changes. Please look at [XIVQuickLauncher for the changes](https://github.com/goatcorp/FFXIVQuickLauncher/blob/master/src/XIVLauncher.Common/Encryption/BlockCipher/Blowfish.cs) required, as I have not tested this yet.
### Key
The key used for encrypting/decrypting the encrypted arguments is just your **system's uptime clock**. All FFXIV executables just call `GetTickCount()`, and theres about ~50 or so ticks of freedom before the game or launcher considers it invalid. There is a specific transformation you need to do in order to fetch a valid key though:
```
unsigned int rawTicks = TickCount();
unsigned int ticks = rawTicks & 0xFFFFFFFFu;
unsigned int key = ticks & 0xFFFF0000u;
char buffer[9] = {};
sprintf(buffer, "%08x", key);
```
To make this easier, here is a couple of platform-specific implementations of `TickCount()`. Thank you Wine for being easily searchable, as this is what Wine is actually doing under the hood to emulate `GetTickCount()`, so these are exact and have been tested on Astra for all platforms.
#### Windows
```
uint32_t TickCount() {
return GetTickCount();
}
```
#### macOS
```
uint32_t TickCount() {
struct mach_timebase_info timebase;
mach_timebase_info(&timebase);
auto machtime = mach_continuous_time();
auto numer = uint64_t (timebase.numer);
auto denom = uint64_t(timebase.denom);
auto monotonic_time = machtime * numer / denom / 100;
return monotonic_time / 10000;
}
```
#### Linux
```
uint32_t TickCount() {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
}
```
### Checksum
If you're just interested in decrypting game arguments, this is not essential. This is presumably used as a checksum
when the game checks your encrypted string.
```
static char ChecksumTable[] = {
'f', 'X', '1', 'p', 'G', 't', 'd', 'S',
'5', 'C', 'A', 'P', '4', '_', 'V', 'L'
};
static char GetChecksum(unsigned int key) {
auto value = key & 0x000F0000;
return ChecksumTable[value >> 16];
}
```
## Decrypting
You can try the [dedicated argcracker](https://sr.ht/~redstrate/novus/#argcracker) in Novus for this purpose. It allows you to easily
crack any SqexArg enabled program assuming you have access to the string.
## Notes
Every instance where SqexArg is used, the first argument is always `T`, where `T` is set to the value of `ticks` (as shown above). I'm not sure what the purpose of this really is, maybe for verifying the checksum?
The arguments (before encoding of course) must be formatted as `" /%1 =%2"`. The extra space is required, even at the beginning of the arguments. Make sure that any spaces in your string is double padded as well.
## Implementations
* [XIVQuickLauncher (C#)](https://github.com/goatcorp/FFXIVQuickLauncher/blob/master/src/XIVLauncher.Common/Encryption/ArgumentBuilder.cs)
* [Astra (C++)](https://git.sr.ht/~redstrate/astra/tree/main/item/launcher/core/include/encryptedarg.h)
* [XIV-on-Mac (Swift)](https://github.com/marzent/XIV-on-Mac/blob/main/XIV%20on%20Mac/Encryption.swift)