diff --git a/src/blowfish.cpp b/src/blowfish.cpp index 12e7003..50a7a3c 100644 --- a/src/blowfish.cpp +++ b/src/blowfish.cpp @@ -4,27 +4,25 @@ BlowfishSession::BlowfishSession() { mbedtls_blowfish_init(&ctx); } -void BlowfishSession::setKey(QString key) { - std::string keyStr = key.toStdString(); - mbedtls_blowfish_setkey(&ctx, (unsigned char*)keyStr.c_str(), key.length() * 8); +void BlowfishSession::setKey(unsigned int key) { + mbedtls_blowfish_setkey(&ctx, (unsigned char*)&key, sizeof(key) * 8); } QByteArray BlowfishSession::encrypt(QString string) { - QByteArray finalArray; - for(int i = 0; i < string.length(); i += 8) { - unsigned char input[MBEDTLS_BLOWFISH_BLOCKSIZE]; - memset(input, 0, 8); + std::string inputStr = string.toStdString(); - std::string inputStr = string.toStdString().substr(i, 8); - strcpy((char*)input, inputStr.c_str()); + QByteArray finalArray; + for(int i = 0; i < inputStr.length(); i += 8) { + unsigned char input[MBEDTLS_BLOWFISH_BLOCKSIZE + 1]; + memset(input, 0, 9); + memcpy(input, (char*)inputStr.c_str() + i, 8); unsigned char output[MBEDTLS_BLOWFISH_BLOCKSIZE]; memset(output, 0, 8); mbedtls_blowfish_crypt_ecb(&ctx, MBEDTLS_BLOWFISH_ENCRYPT, input, output); - QByteArray arr((char*)output, 8); - finalArray.append(arr); + finalArray.append((char*)output, 8); } return finalArray; @@ -33,18 +31,21 @@ QByteArray BlowfishSession::encrypt(QString string) { QString BlowfishSession::decrypt(QByteArray data) { QString finalString; - for(int i = 0; i < data.length(); i += 8) { + for(int i = 0; i < data.size(); i += 8) { + int adjusted_length = 8; + if((i + 8) > data.size()) + adjusted_length = (i + 8) - data.size(); + unsigned char input[MBEDTLS_BLOWFISH_BLOCKSIZE]; memset(input, 0, 8); - memcpy(input, data.data() + i, 8); + memcpy(input, data.data() + i, adjusted_length); - unsigned char output[MBEDTLS_BLOWFISH_BLOCKSIZE]; - memset(output, 0, 8); + unsigned char output[MBEDTLS_BLOWFISH_BLOCKSIZE + 1]; + memset(output, 0, 9); mbedtls_blowfish_crypt_ecb(&ctx, MBEDTLS_BLOWFISH_DECRYPT, input, output); - QString str((char*)output); - finalString.append(str); + finalString.append((char*)output); } return finalString; diff --git a/src/blowfish.h b/src/blowfish.h index 9014b55..eb9bf26 100644 --- a/src/blowfish.h +++ b/src/blowfish.h @@ -8,7 +8,7 @@ class BlowfishSession { public: BlowfishSession(); - void setKey(QString key); + void setKey(unsigned int key); QByteArray encrypt(QString string); QString decrypt(QByteArray data); diff --git a/src/xivlauncher.cpp b/src/xivlauncher.cpp index cd1f9e3..d1e21d7 100755 --- a/src/xivlauncher.cpp +++ b/src/xivlauncher.cpp @@ -15,11 +15,17 @@ #include #include +#if defined(Q_OS_MAC) +#include +#include +#endif + #include "xivlauncher.h" #include "sapphirelauncher.h" #include "squarelauncher.h" #include "squareboot.h" #include "settingswindow.h" +#include "blowfish.h" void LauncherWindow::setSSL(QNetworkRequest& request) { QSslConfiguration config; @@ -39,6 +45,52 @@ void LauncherWindow::buildRequest(QNetworkRequest& request) { request.setRawHeader("Accept-Language", "en-us"); } +// from xivdev +char ChecksumTable[] = { + 'f', 'X', '1', 'p', 'G', 't', 'd', 'S', + '5', 'C', 'A', 'P', '4', '_', 'V', 'L' +}; + +char GetChecksum(unsigned int key) { + auto value = key & 0x000F0000; + return ChecksumTable[value >> 16]; +} + +#if defined(Q_OS_MAC) +// this is pretty much what wine does :-0 +uint32_t TickCount() { + struct mach_timebase_info convfact; + mach_timebase_info(&convfact); + return mach_absolute_time() * convfact.numer / convfact.denom / 100; +} +#endif + +QString encryptGameArg(QString arg) { + unsigned int rawTicks = TickCount() / 10000; + qDebug() << "raw tick count: " << rawTicks; + + unsigned int ticks = rawTicks & 0xFFFFFFFFu; + qDebug() << "ticks: " << ticks; + + unsigned int u = ticks & 0xFFFF0000u; + qDebug() << "key: " << u; + + BlowfishSession session; + session.setKey(u); + + QByteArray encryptedArg = session.encrypt(QString(" T =%1 ").arg(ticks) + arg); + + qDebug() << "decryption attempt: " << session.decrypt(encryptedArg); + + QString base64 = encryptedArg.trimmed().toBase64(QByteArray::Base64Option::Base64UrlEncoding); + + qDebug() << "base64: " << encryptedArg.toBase64(); + + char checksum = GetChecksum(u); + + return QString("//**sqex0003%1%2**//").arg(base64, QString(checksum)); +} + void LauncherWindow::launchGame(const LoginAuth auth) { QList arguments; @@ -64,7 +116,20 @@ void LauncherWindow::launchGame(const LoginAuth auth) { arguments.push_back(QString("DEV.LobbyHost0%1=%2 DEV.LobbyPort0%1=54994").arg(QString::number(i), auth.lobbyhost)); } - launchExecutable(arguments); + bool encryptArguments = true; + if(encryptArguments) { + auto executable = arguments[0]; + arguments.removeFirst(); + + QString argJoined; + for(auto arg : arguments) + argJoined += " " + arg; + + auto earg = encryptGameArg(argJoined); + launchExecutable({executable, earg}); + } else { + launchExecutable(arguments); + } } void LauncherWindow::launchExecutable(const QStringList args) { @@ -221,6 +286,13 @@ LauncherWindow::LauncherWindow(QWidget* parent) : squareLauncher = new SquareLauncher(*this); squareBoot = new SquareBoot(*this, *squareLauncher); + // test case for encryption + BlowfishSession session; + session.setKey(327680); + + auto enc = session.encrypt("a little lamb jumps over the fence, it falls."); + qDebug() << "first decryption attempt: " << session.decrypt(enc); + readInitialInformation(); QMenu* fileMenu = menuBar()->addMenu("File");