/
BirdLeon
/
ROBLOX2016
Обзор
Документация
Войти
/
BirdLeon
/
ROBLOX2016
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
Network/NetPmc.cpp
279 строк
9 KB
PatoFlamejanteTV
full source code
19 дек 2024, 19:11
19 дек 2024, 19:11
05db15d
Код
Авторство
О чём код?
#include "stdafx.h" #include "NetPmc.h" // for teaDecrypt #include "security/FuzzyTokens.h" #include <stdlib.h> #include <algorithm> #include "Replicator.h" #include "ServerReplicator.h" #include "ClientReplicator.h" #include "v8datamodel/DataModel.h" #include "util/rbxrandom.h" // Design notes from 4 December 2015: // This is the Networked Program Memory Checker. // In the first implementation, there was no easy way to distribute information to the // RCC server. However, the post-build release patcher did work. // // The flow works like this: // (build) // The patcher will generate a group of 128 different challenges -- different starting/ending/seed values // These will be generated before the main PMC is run, as it will modify the .rdata section. // These will be encrypted using salsa20. This can be generated on the server. // The results from each challenge will be generated and stored as well, but will be encrypted using tea. // // (server) // The server will locally regenerate the decryption key sequence // The server will send a selector + key to the game client // This will be added to the "challengesInFlight" // // (client) // The client will decrypt the info for this challenge // The hash will be performed // The client will send the selector, its response, and the encrypted correct response to the server. // // (server) // The server will decrypt the correct response and compare it to the given response // The inFlight list will be scanned and the entry removed. // // if the inFlight list will have a limit on how large it can grow. // // This security mechanism will also add some checks to avoid running in places where it is likely to // be debugged. // // The choice of tea/salsa was based on the existence of code online. DYNAMIC_FASTINTVARIABLE(HashConfigP3, 4) // players DYNAMIC_FASTINTVARIABLE(HashConfigP4, 1000) // parts DYNAMIC_FASTINTVARIABLE(HashConfigP5, 1) // down DYNAMIC_FASTINTVARIABLE(HashConfigP6, 1) // up DYNAMIC_FASTINTVARIABLE(HashConfigP8, 64) // pending limit namespace RBX { namespace Security{ std::vector<uint32_t> hackFlagVector; const volatile uint32_t kHackFlagVectorDecrypt = 1; // might not be random #ifndef RBX_STUDIO_BUILD const volatile NetPmcChallenge kChallenges[kNumChallenges] = {}; uint32_t netPmcHashCheck(const NetPmcChallenge& challenge) { const uint32_t kMult2 = 0xBADA55; const uint32_t kMult1 = 0xC0FFEE11; unsigned int v1 = challenge.seed; unsigned int v2 = challenge.seed; unsigned int v3 = challenge.seed; unsigned int v4 = challenge.seed; const uintptr_t* p = reinterpret_cast<const uintptr_t*>(challenge.base); for (size_t i = 0; i < challenge.size/16; ++i) { v1 += *(p+0) * kMult2; v2 += *(p+1) * kMult2; v3 += *(p+2) * kMult2; v4 += *(p+3) * kMult2; v1 = _rotl(v1, 17); v2 = _rotl(v2, 17); v3 = _rotl(v3, 17); v4 = _rotl(v4, 17); v1 *= kMult1; v2 *= kMult1; v3 *= kMult1; v4 *= kMult1; p+=4; } return v1^v2+v3-v4; } #endif #ifdef RBX_RCC_SECURITY std::vector<NetPmcChallenge> netPmcKeys = generateNetPmcKeys(); NetPmcServer::NetPmcServer() : // some are set to safe, but odd defaults isGameCreator(true) , userHasActivity(false) , gameHasManyParts(false) , gameHasManyPlayers(false) , challengeIdx(0) , challengesRecv(0) , challengesSent(0) { challenges.resize(kNumChallenges); for (size_t i = 0; i < kNumChallenges; ++i) { challenges[i] = i; } std::random_shuffle(challenges.begin(), challenges.end()); } // the intent it to make it much harder to detect this mechanism by making it // only work in places that are actually games. bool NetPmcServer::canSendChallenge(const RBX::Network::Replicator* rep) /*non-const*/ { auto dm = DataModel::get(rep); if (dm) { auto remotePlayer = rep->getRemotePlayer(); if (remotePlayer) { isGameCreator = (dm->getCreatorID() == remotePlayer->getUserID()); if (!isGameCreator && !gameHasManyPlayers) { // recheck to see if there are enough players gameHasManyPlayers = (dm->getNumPlayers() > DFInt::HashConfigP3); } if (!isGameCreator && !gameHasManyParts) { // recheck to see if there are enough parts gameHasManyParts = (dm->getNumPartInstances() > DFInt::HashConfigP4); } } } userHasActivity = (rep->stats().kiloBytesReceivedPerSecond >= DFInt::HashConfigP5) && (rep->stats().kiloBytesSentPerSecond >= DFInt::HashConfigP6); return (!isGameCreator && gameHasManyPlayers && gameHasManyParts && userHasActivity); } unsigned int NetPmcServer::generateDebugInfo(const RBX::Network::Replicator* rep, uint32_t& sent, uint32_t& recv, uint32_t& pending) const { unsigned int debugInfo = 0; debugInfo |= (isGameCreator ? (1<<0) : 0); debugInfo |= (gameHasManyPlayers ? (1<<1) : 0); debugInfo |= (gameHasManyParts ? (1<<2) : 0); debugInfo |= ((rep->stats().kiloBytesReceivedPerSecond >= DFInt::HashConfigP5) ? (1<<3) : 0); debugInfo |= ((rep->stats().kiloBytesSentPerSecond >= DFInt::HashConfigP6) ? (1<<4) : 0); debugInfo |= ((challengesRecv > 1) ? (1<<5) : 0); debugInfo |= ((challengesSent > 1) ? (1<<6) : 0); debugInfo |= ((challengesRecv > 32) ? (1<<7) : 0); debugInfo |= ((challengesSent > 32) ? (1<<8) : 0); debugInfo |= ((challengesRecv > 128) ? (1<<9) : 0); debugInfo |= ((challengesSent > 128) ? (1<<10) : 0); sent = challengesSent; recv = challengesRecv; pending = challengesInFlight.size(); return debugInfo; } bool NetPmcServer::tooManyPending() const { return (challengesInFlight.size() > static_cast<unsigned int>(DFInt::HashConfigP8)); } uint8_t NetPmcServer::getRandomChallenge() { if (challengeIdx == challenges.size()-1) { std::random_shuffle(challenges.begin(), challenges.end()); challengeIdx = 0; } return challenges[challengeIdx++]; } bool NetPmcServer::sendChallenge(uint8_t idx) { challengesSent++; boost::mutex::scoped_lock lock(flightMutex); challengesInFlight.push_back(idx); return true; } bool NetPmcServer::removeFromList(uint8_t challengeNum) { challengesRecv++; boost::mutex::scoped_lock lock(flightMutex); auto loc = std::find(challengesInFlight.begin(), challengesInFlight.end(), challengeNum); if (loc != challengesInFlight.end()) { challengesInFlight.erase(loc); return true; } return false; } bool NetPmcServer::checkResult(uint8_t idx, uint32_t response, uint64_t correct) const { uint64_t correctDecrypted = RBX::Security::teaDecrypt(correct); if (response != (correctDecrypted&0xFFFFFFFF)) { // hash mismatch return false; } if (correctDecrypted>>32 != idx) { // attempted replay attack return false; } return true; } #endif // From github: https://github.com/andres-erbsen/salsa20/blob/master/salsa20.c // rotate x to left by n bits, the bits that go over the left edge reappear on the right #define R(x,n) (((x) << (n)) | ((x) >> (32-(n)))) // addition wraps modulo 2^32 // the choice of 7,9,13,18 "doesn't seem very important" (spec) #define quarter(a,b,c,d) do {\ b ^= R(d+a, 7);\ c ^= R(a+b, 9);\ d ^= R(b+c, 13);\ a ^= R(c+d, 18);\ } while (0) void salsa20_words(uint32_t *out, uint32_t in[16]) { uint32_t x[4][4]; int i; for (i=0; i<16; ++i) x[i/4][i%4] = in[i]; for (i=0; i<10; ++i) { // 10 double rounds = 20 rounds // column round: quarter round on each column; start at ith element and wrap quarter(x[0][0], x[1][0], x[2][0], x[3][0]); quarter(x[1][1], x[2][1], x[3][1], x[0][1]); quarter(x[2][2], x[3][2], x[0][2], x[1][2]); quarter(x[3][3], x[0][3], x[1][3], x[2][3]); // row round: quarter round on each row; start at ith element and wrap around quarter(x[0][0], x[0][1], x[0][2], x[0][3]); quarter(x[1][1], x[1][2], x[1][3], x[1][0]); quarter(x[2][2], x[2][3], x[2][0], x[2][1]); quarter(x[3][3], x[3][0], x[3][1], x[3][2]); } for (i=0; i<16; ++i) out[i] = x[i/4][i%4] + in[i]; } // inputting a key, message nonce, keystream index and constants to that transormation void salsa20_block(uint8_t *out, uint8_t key[32], uint64_t nonce, uint64_t index) { static const char c[16] = "expand 32byte k"; // arbitrary constant #define LE(p) ( _byteswap_ulong(*reinterpret_cast<const unsigned long*>(p)) ) uint32_t in[16] = {LE(c), LE(key), LE(key+4), LE(key+8), LE(key+12), LE(c+4), nonce&0xffffffff, nonce>>32, index&0xffffffff, index>>32, LE(c+8), LE(key+16), LE(key+20), LE(key+24), LE(key+28), LE(c+12)}; #undef LE uint32_t wordout[16]; salsa20_words(wordout, in); int i; for (i=0; i<64; ++i) out[i] = 0xff & (wordout[i/4] >> (8*(i%4))); } // enc/dec: xor a message with transformations of key, a per-message nonce and block index void salsa20(uint8_t *message, uint64_t mlen, uint8_t key[32], uint64_t nonce) { int i; uint8_t block[64]; for (i=0; i<mlen; i++) { if (i%64 == 0) salsa20_block(block, key, nonce, i/64); message[i] ^= block[i%64]; } } } }