From 60a301a93b6057bb2c54ac04a7c38c38389037b3 Mon Sep 17 00:00:00 2001 From: Dark1-dev Date: Wed, 1 Mar 2023 21:30:57 +0600 Subject: Add files via upload --- src/Protocols/GameSpy0/algorithm.cpp | 96 +++++++++++++++++++++++++++++++ src/Protocols/GameSpy0/gamespy0.cpp | 51 ++++++++++++++++ src/Protocols/GameSpy0/gamespy0.h | 10 ++++ src/Protocols/GameSpy0/securevalidate.cpp | 90 +++++++++++++++++++++++++++++ src/Protocols/GameSpy0/securevalidate.h | 62 ++++++++++++++++++++ src/Protocols/overrides.cpp | 44 ++++++++++++++ src/Protocols/overrides.h | 70 ++++++++++++++++++++++ 7 files changed, 423 insertions(+) create mode 100644 src/Protocols/GameSpy0/algorithm.cpp create mode 100644 src/Protocols/GameSpy0/gamespy0.cpp create mode 100644 src/Protocols/GameSpy0/gamespy0.h create mode 100644 src/Protocols/GameSpy0/securevalidate.cpp create mode 100644 src/Protocols/GameSpy0/securevalidate.h create mode 100644 src/Protocols/overrides.cpp create mode 100644 src/Protocols/overrides.h (limited to 'src/Protocols') diff --git a/src/Protocols/GameSpy0/algorithm.cpp b/src/Protocols/GameSpy0/algorithm.cpp new file mode 100644 index 0000000..5250079 --- /dev/null +++ b/src/Protocols/GameSpy0/algorithm.cpp @@ -0,0 +1,96 @@ +#include "securevalidate.h" + +/* This algorithm is based on Luigi Auriemma's gsmsalg 0.3.3, + * https://aluigi.altervista.org/papers/gsmsalg.h + * under GNU General Public License v2. + */ +QString generateValidateString(const QByteArray &cipher, + const QByteArray &secure, + const int &enctype) +{ + unsigned char cipherLength = static_cast( cipher.length() ); + unsigned char secureLength = static_cast( secure.length() ); + + // fill array with ascii characters + unsigned char enc[256]; + for (unsigned short j = 0; j < 256; j++) + { + enc[j] = static_cast(j); + } + + // cipher shuffle + unsigned char a = 0, x; + for (unsigned short j = 0; j < 256; j++) + { + a += enc[j] + cipher[j % cipherLength]; + x = enc[a]; + enc[a] = enc[j]; + enc[j] = x; + } + + // secure shuffle + unsigned char tmp[66]; + unsigned char i = 0, + y = 0, + b = 0; + a = 0; // reset a + for (i = 0; i < cipherLength; i++) + { + a += secure.at(i) + 1; + x = enc[a]; + b += x; + y = enc[b]; + enc[b] = x; + enc[a] = y; + tmp[i] = static_cast( secure.at(i) ^ enc[ (x+y) & 0xff ] ); + } + + // part of the enctype 1-2 process (uses i from previous loop) + for (secureLength = i; secureLength % 3; secureLength++) + { + tmp[secureLength] = 0; + } + + // enctype 1 shuffle + if (enctype == 1) + { + for (i = 0; i < secureLength; i++) + { + tmp[i] = enctype1_data[ tmp[i] ]; + } + } + else if (enctype == 2 ) + { + for (i = 0; i < secureLength; i++) + { + tmp[i] = static_cast(tmp[i] ^ cipher[i % cipherLength] ); + } + } + + // final shuffle and stitch validate response together + unsigned char z = 0; + QString validate; + for (i = 0; i < secureLength; i += 3) + { + x = (tmp[i]); + y = (tmp[i+1]); + z = (tmp[i+2]); + + validate.append( charshift (x >> 2) ); + validate.append( charshift (static_cast(((x & 3) << 4) | (y >> 4)) )); + validate.append( charshift (static_cast(((y & 15) << 2) | (z >> 6)) )); + validate.append( charshift (z & 63)); + } + return validate; +} + +// part of gsmsalg 0.3.3 and license +unsigned char charshift ( const unsigned char ® ) +{ + if (reg < 26) return (reg + 'A'); + if (reg < 52) return (reg + 'G'); + if (reg < 62) return (reg - 4); + if (reg == 62) return ('+'); + if (reg == 63) return ('/'); + return (0); +} diff --git a/src/Protocols/GameSpy0/gamespy0.cpp b/src/Protocols/GameSpy0/gamespy0.cpp new file mode 100644 index 0000000..6595d4a --- /dev/null +++ b/src/Protocols/GameSpy0/gamespy0.cpp @@ -0,0 +1,51 @@ +#include "gamespy0.h" + +QMultiHash parseGameSpy0Buffer(const QString &bufferString) +{ + // initialise output hash + QMultiHash queryStringHash; + + // split on backslash + QStringList bufferStringList = bufferString.split('\\', QString::KeepEmptyParts); + + // iterate through all items + QListIterator property (bufferStringList); + + // the first element is always empty -- skip it + if ( property.hasNext() ) + { + property.next(); + } + + // store as key -> value + while ( property.hasNext() ) + { + // unify valid keys + QString key = overrideKey( property.next().trimmed() ); + + // see if a value for this key exists + if ( ! property.hasNext() ) + break; + + // get value + QString value = property.next().trimmed(); + + // insert to return hash + queryStringHash.insert(key, value); + } + + // override gamename + if ( queryStringHash.contains("gamename") ) + { + QList gn = queryStringHash.values("gamename"); + queryStringHash.remove("gamename"); + + // read backwards to preserve element order + for (int i = gn.size()-1; i >= 0; i--) + { + queryStringHash.insert("gamename", overrideGamename( gn.value(i) ) ); + } + } + + return queryStringHash; +} diff --git a/src/Protocols/GameSpy0/gamespy0.h b/src/Protocols/GameSpy0/gamespy0.h new file mode 100644 index 0000000..9a28fa8 --- /dev/null +++ b/src/Protocols/GameSpy0/gamespy0.h @@ -0,0 +1,10 @@ +#ifndef GAMESPY0_H +#define GAMESPY0_H + +#include +#include +#include "Protocols/overrides.h" + +QMultiHash parseGameSpy0Buffer(const QString &bufferString); + +#endif // GAMESPY0_H diff --git a/src/Protocols/GameSpy0/securevalidate.cpp b/src/Protocols/GameSpy0/securevalidate.cpp new file mode 100644 index 0000000..0532640 --- /dev/null +++ b/src/Protocols/GameSpy0/securevalidate.cpp @@ -0,0 +1,90 @@ +#include "securevalidate.h" + +AuthResult validateGamename(const bool &isBeacon, + const QString &gamename, + const QString &validate, + const QString &cipherIn, + const QString &secureIn, + const int &enctypeIn) +{ + // output result + AuthResult authResult; + + // override certain cases for UDP beacon + if ( isBeacon and _overrideValidateBeacon.contains(gamename) ) + { + authResult.auth = true; + authResult.validate = "override"; + return authResult; + } + + // override certain cases for TCP client + if ( ! isBeacon and _overrideValidateClient.contains(gamename) ) + { + authResult.auth = true; + authResult.validate = "override"; + return authResult; + } + + // inputs + checks + QByteArray secure = secureIn.toLatin1(); + QByteArray cipher = cipherIn.toLatin1(); + + // get validate value + if ( 6 <= cipher.length() and cipher.length() < 16 and + 6 <= secure.length() and secure.length() < 16 ) + { + // safe to call validation. proceed. + authResult.validate = generateValidateString(cipher, secure, enctypeIn); + } + else + { + // incorrect input, not safe to calculate validation + authResult.auth = false; + authResult.validate = "invalid!"; + } + + // correct validation provided? + authResult.auth = (authResult.validate.compare(validate) == 0); + + // return result as boolean and string + return authResult; +} + +QString returnValidate(const QByteArray &cipher, + const QByteArray &secure, + const int &enctype) +{ + // get validate value + if ( 6 <= cipher.length() and cipher.length() < 16 and + 6 <= secure.length() and secure.length() < 16 ) + { + return generateValidateString(cipher, secure, enctype); + } + else + { + return "invalid!"; + } +} + +QString genChallengeString(const int len, const bool moreChars) +{ + QString randomString; + if (moreChars) + { + // use A-Za-z0-9 + for(unsigned char i = 0; i < len; ++i) + { + randomString += moreCharacters[qrand() % moreCharacters.length()]; + } + } + else + { + // use A-Z only + for(unsigned char i = 0; i < len; ++i) + { + randomString += possibleCharacters[qrand() % possibleCharacters.length()]; + } + } + return randomString; +} diff --git a/src/Protocols/GameSpy0/securevalidate.h b/src/Protocols/GameSpy0/securevalidate.h new file mode 100644 index 0000000..b767bda --- /dev/null +++ b/src/Protocols/GameSpy0/securevalidate.h @@ -0,0 +1,62 @@ +#ifndef SECUREVALIDATE_H +#define SECUREVALIDATE_H + +#include +#include "Protocols/overrides.h" + +// return both status and result string +struct AuthResult { + bool auth = false; + QString validate = ""; +}; + +// authenticate the beacon/client and return the validate string +AuthResult validateGamename(const bool &isBeacon, + const QString &gamename, + const QString &validate, + const QString &cipherIn, + const QString &secureIn, + const int &enctypeIn); + +// return validate string (acccepts unsanitised inputs) +QString returnValidate(const QByteArray &cipher, + const QByteArray &secure, + const int &enctype); + +// generate random challenge strings +QString genChallengeString(const int len, const bool moreChars); + + +// algorithm for the secure/validate challenge +unsigned char charshift ( const unsigned char ® ); +QString generateValidateString(const QByteArray &cipher, + const QByteArray &secure, + const int &enctype); + +// pre-built characters for generating validate string +const QString possibleCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); +const QString moreCharacters("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + +/* Pre-built algorithm data for enctype 1 from Luigi Auriemma's + * gsmsalg 0.3.3, https://aluigi.altervista.org/papers/gsmsalg.h + * under GNU General Public License v2. + */ +const unsigned char enctype1_data[] = ( +"\x01\xba\xfa\xb2\x51\x00\x54\x80\x75\x16\x8e\x8e\x02\x08\x36\xa5" +"\x2d\x05\x0d\x16\x52\x07\xb4\x22\x8c\xe9\x09\xd6\xb9\x26\x00\x04" +"\x06\x05\x00\x13\x18\xc4\x1e\x5b\x1d\x76\x74\xfc\x50\x51\x06\x16" +"\x00\x51\x28\x00\x04\x0a\x29\x78\x51\x00\x01\x11\x52\x16\x06\x4a" +"\x20\x84\x01\xa2\x1e\x16\x47\x16\x32\x51\x9a\xc4\x03\x2a\x73\xe1" +"\x2d\x4f\x18\x4b\x93\x4c\x0f\x39\x0a\x00\x04\xc0\x12\x0c\x9a\x5e" +"\x02\xb3\x18\xb8\x07\x0c\xcd\x21\x05\xc0\xa9\x41\x43\x04\x3c\x52" +"\x75\xec\x98\x80\x1d\x08\x02\x1d\x58\x84\x01\x4e\x3b\x6a\x53\x7a" +"\x55\x56\x57\x1e\x7f\xec\xb8\xad\x00\x70\x1f\x82\xd8\xfc\x97\x8b" +"\xf0\x83\xfe\x0e\x76\x03\xbe\x39\x29\x77\x30\xe0\x2b\xff\xb7\x9e" +"\x01\x04\xf8\x01\x0e\xe8\x53\xff\x94\x0c\xb2\x45\x9e\x0a\xc7\x06" +"\x18\x01\x64\xb0\x03\x98\x01\xeb\x02\xb0\x01\xb4\x12\x49\x07\x1f" +"\x5f\x5e\x5d\xa0\x4f\x5b\xa0\x5a\x59\x58\xcf\x52\x54\xd0\xb8\x34" +"\x02\xfc\x0e\x42\x29\xb8\xda\x00\xba\xb1\xf0\x12\xfd\x23\xae\xb6" +"\x45\xa9\xbb\x06\xb8\x88\x14\x24\xa9\x00\x14\xcb\x24\x12\xae\xcc" +"\x57\x56\xee\xfd\x08\x30\xd9\xfd\x8b\x3e\x0a\x84\x46\xfa\x77\xb8"); + +#endif // SECUREVALIDATE_H diff --git a/src/Protocols/overrides.cpp b/src/Protocols/overrides.cpp new file mode 100644 index 0000000..957c489 --- /dev/null +++ b/src/Protocols/overrides.cpp @@ -0,0 +1,44 @@ +#include "overrides.h" + +// some games use different key/value pairs. override with known key. +QString overrideKey(const QString &rawKey) +{ + // convert to lowercase + QString key = rawKey.toLower(); + + // some keys are indexed, like "player_0". + if ( _index_match.indexIn(key) >= 0 ) + { + // find keyword without index and match key + if ( _validKeys.contains( _index_match.cap(1) ) ) // group starts at 1, not 0 + { + // concat the index back and return found override + return _validKeys.value( _index_match.cap(1), key ) + _index_match.cap(2); + } + } + else + { + // non-indexed key + if ( _validKeys.contains( key ) ) + { + // return found override + return _validKeys.value(key, key); + } + } + + // no override found. return original. + return key; +} + +// some games deviate from the gamename protocol. override with correct gamename +QString overrideGamename(const QString &gamenameIn) +{ + // convert to lowercase + QString gamename = gamenameIn.toLower(); + + if ( _validGamenames.contains(gamename ) ) + { + return _validGamenames.value(gamename, gamename); + } + return gamename; +} diff --git a/src/Protocols/overrides.h b/src/Protocols/overrides.h new file mode 100644 index 0000000..eb6f8b2 --- /dev/null +++ b/src/Protocols/overrides.h @@ -0,0 +1,70 @@ +#ifndef OVERRIDES_H +#define OVERRIDES_H + +#include +#include +#include + +// parse to a valid key/value pair +QString overrideKey(const QString &rawKey); + +// parse to a valid gamename +QString overrideGamename(const QString &gamenameIn); + +// record of gamenames that need to be overridden +const QHash _validGamenames +{ + {"JetFighter IV", "jetfighter4"}, + {"igi2" , "projectigi2r"}, +}; + +// record of query keys that need to be overridden +const QHash _validKeys +{ + {"version", "gamever"}, + {"mingamever", "minnetver"}, + {"admin", "adminname"}, + {"adminname", "adminname"}, + {"admin e-mail", "adminemail"}, + {"friendly fire", "friendlyfire"}, + {"friendly fire?", "friendlyfire"}, + {"mapfilename", "mapname"}, + {"mapid", "mapname"}, + {"skill", "botskill"}, + {"num teams", "maxteams"}, + {"time limit", "timelimit"}, + {"time_limit", "timelimit"}, + {"timetowin", "timelimit"}, + {"roundtime", "timelimit"}, + {"active_mods", "mutators"}, + {"activemod", "mutators"}, + {"Mutator", "mutators"}, + {"Mutator101", "mutators"}, + {"Mutator102", "mutators"}, + {"Mutator103", "mutators"}, + {"Mutator104", "mutators"}, + {"playername", "player"}, + {"teamname", "team"}, + {"score", "score"}, + {"kills", "score"}, +}; + +// some games do not (fully) support secure/validate +const QStringList _overrideValidateBeacon +{ + "deusex", + "rune", + "wot", +}; + +const QStringList _overrideValidateClient +{ + "deusex", + "wot", +}; + +// matching regexes +// TODO: replace with QRegularExpression +const QRegExp _index_match{"^(\\w+)(_\\d+)$"}; + +#endif // OVERRIDES_H -- cgit v1.2.3