feat: add dbdcrypt

This commit is contained in:
2026-04-12 20:56:25 -03:00
parent b2dfd9e1e5
commit 3b461cd845
2 changed files with 418 additions and 0 deletions
+369
View File
@@ -0,0 +1,369 @@
#include "dbdcrypt.h"
#include <nerutils/log.h>
#include <algorithm>
#include <zlib.h>
#include <openssl/types.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
std::string DBDCrypt::decrypt(const std::string& data, const std::string& accessKey, PayloadType* outType)
{
if (outType) *outType = NONE;
if (data.starts_with("DbdDAQEB"))
{
if (outType && *outType == NONE) *outType = TYPE_1;
return decType1(data, accessKey, outType);
}
if (data.starts_with("DbdDAgAC"))
{
if (outType && *outType == NONE) *outType = TYPE_2;
return decType2(data, accessKey, outType);
}
if (data.starts_with("DbdDAwAC"))
{
if (outType && *outType == NONE) *outType = TYPE_3;
return decType3(data, accessKey, outType);
}
Log::warning("Attempted to decrypt non encrypted string");
return data;
}
std::string DBDCrypt::encrypt(const std::string& data, const std::string& accessKey, PayloadType type,
std::string keyId)
{
if (type == TYPE_1)
{
auto compressed = zLibCompress(data);
if (compressed.empty()) return "";
std::vector<uint8_t> fullPayload(8, 0);
fullPayload.insert(fullPayload.end(), compressed.begin(), compressed.end());
return "DbdDAQEB" + b64Enc(fullPayload);
}
if (type == TYPE_3)
{
auto decodedKey = b64Dec(accessKey);
if (decodedKey.empty()) return "";
std::string shiftedData = data;
for (char& c : shiftedData)
c = (char)((unsigned char)c - 1);
std::vector<uint8_t> padded(shiftedData.begin(), shiftedData.end());
int padLen = 16 - (padded.size() % 16);
if (padLen < 16) padded.insert(padded.end(), padLen, 0);
auto encryptedBody = aesECBEncrypt(padded, decodedKey);
if (encryptedBody.empty()) return "";
std::string shiftedId = shiftKeyID(keyId, -1);
std::vector<uint8_t> fullData(shiftedId.begin(), shiftedId.end());
fullData.push_back(0); // Null terminator
fullData.insert(fullData.end(), (uint8_t*)encryptedBody.data(),
(uint8_t*)encryptedBody.data() + encryptedBody.size());
return "DbdDAwAC" + b64Enc(fullData);
}
return std::string();
}
std::string DBDCrypt::decType1(const std::string& data, const std::string& key, PayloadType* outType)
{
if (data.length() < 8) return data;
auto decoded = b64Dec(data.substr(8));
if (decoded.size() < 4)
{
Log::error("Type 1 base64 too short ({})", decoded.size());
return "";
}
std::vector<uint8_t> body(decoded.begin() + 4, decoded.end());
std::string decompressed = zlibDecompress(body);
if (decompressed.starts_with("DbdD")) return decrypt(decompressed, key, outType);
return decompressed;
}
std::string DBDCrypt::decType2(const std::string& data, const std::string& key, PayloadType* outType)
{
if (data.length() < 8) return data;
auto decoded = b64Dec(data.substr(8));
if (decoded.empty()) return "";
std::vector<uint8_t> body = decoded;
auto transformedKey = transformCDNKey(CDN_KEY_BASE64);
std::string decrypted = aesECBDecrypt(body, transformedKey);
if (decrypted.empty()) return "";
for (char& c : decrypted)
c = (char)((unsigned char)c + 1);
while (!decrypted.empty() && (unsigned char)decrypted.back() == 1)
decrypted.pop_back();
for (size_t offset : {0ULL, 4ULL})
{
if (offset + 1 < decrypted.size() && (unsigned char)decrypted[offset] == 0x78)
{
std::vector<uint8_t> zlibPart((uint8_t*)decrypted.data() + offset,
(uint8_t*)decrypted.data() + decrypted.size());
std::string decompressed = zlibDecompress(zlibPart);
if (!decompressed.empty()) return decompressed;
}
}
if (decrypted.starts_with("DbdD")) return decrypt(decrypted, key, outType);
return decrypted;
}
std::string DBDCrypt::decType3(const std::string& data, const std::string& key, PayloadType* outType)
{
if (data.length() < 8) return data;
auto rawKey = b64Dec(key);
auto decoded = b64Dec(data.substr(8));
if (decoded.empty()) return "";
auto it = std::find(decoded.begin(), decoded.end(), 0);
if (it == decoded.end()) return "";
std::vector<uint8_t> body(it + 1, decoded.end());
std::string decrypted = aesECBDecrypt(body, rawKey);
if (decrypted.empty())
{
Log::error("AES decryption failed (body size: {})", body.size());
return "";
}
for (char& c : decrypted)
c = (char)((unsigned char)c + 1);
while (!decrypted.empty() && (unsigned char)decrypted.back() == 1)
decrypted.pop_back();
for (size_t offset : {0ULL, 4ULL})
{
if (offset + 1 < decrypted.size() && (unsigned char)decrypted[offset] == 0x78)
{
//Log::verbose("nested zlib at offset {}", offset);
std::vector<uint8_t> zlibPart((uint8_t*)decrypted.data() + offset,
(uint8_t*)decrypted.data() + decrypted.size());
std::string decompressed = zlibDecompress(zlibPart);
if (!decompressed.empty())
{
//Log::verbose("nested zlib decompressed, size: {}", decompressed.length());
return decompressed;
}
}
}
if (decrypted.starts_with("DbdD")) return decrypt(decrypted, key, outType);
return decrypted;
}
std::string DBDCrypt::aesECBDecrypt(const std::vector<uint8_t>& cipherText, const std::vector<uint8_t>& key)
{
if (key.size() < 32) return "";
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit_ex(ctx, EVP_aes_256_ecb(), NULL, key.data(), NULL);
EVP_CIPHER_CTX_set_padding(ctx, 0);
std::string plaintext;
plaintext.resize(cipherText.size());
int len = 0;
if (EVP_DecryptUpdate(ctx, (unsigned char*)plaintext.data(), &len, cipherText.data(), (int)cipherText.size()) != 1)
{
EVP_CIPHER_CTX_free(ctx);
return "";
}
int outLen = len;
int finalLen = 0;
if (EVP_DecryptFinal_ex(ctx, (unsigned char*)plaintext.data() + len, &finalLen) != 1)
{
// ignore?
}
outLen += finalLen;
EVP_CIPHER_CTX_free(ctx);
plaintext.resize(outLen);
return plaintext;
}
std::string DBDCrypt::aesECBEncrypt(const std::vector<uint8_t>& plainText, const std::vector<uint8_t>& key)
{
if (key.size() < 32) return "";
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
EVP_EncryptInit_ex(ctx, EVP_aes_256_ecb(), NULL, key.data(), NULL);
EVP_CIPHER_CTX_set_padding(ctx, 0);
std::string ciphertext;
ciphertext.resize(plainText.size() + 16);
int len = 0;
if (EVP_EncryptUpdate(ctx, (unsigned char*)ciphertext.data(), &len, plainText.data(), (int)plainText.size()) != 1)
{
EVP_CIPHER_CTX_free(ctx);
return "";
}
int outLen = len;
int finalLen = 0;
if (EVP_EncryptFinal_ex(ctx, (unsigned char*)ciphertext.data() + outLen, &finalLen) == 1)
{
outLen += finalLen;
}
EVP_CIPHER_CTX_free(ctx);
ciphertext.resize(outLen);
return ciphertext;
}
std::vector<uint8_t> DBDCrypt::b64Dec(const std::string& input)
{
std::string in = input;
std::replace(in.begin(), in.end(), '-', '+');
std::replace(in.begin(), in.end(), '_', '/');
BIO *bio, *b64;
int decodeLen = (int)in.length();
std::vector<uint8_t> buffer(decodeLen);
bio = BIO_new_mem_buf(in.data(), decodeLen);
b64 = BIO_new(BIO_f_base64());
bio = BIO_push(b64, bio);
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
int len = BIO_read(bio, buffer.data(), decodeLen);
BIO_free_all(bio);
if (len < 0) return {};
buffer.resize(len);
return buffer;
}
std::string DBDCrypt::b64Enc(const std::vector<uint8_t>& input)
{
if (input.empty()) return "";
BIO *bio, *b64;
BUF_MEM* bufferPtr;
b64 = BIO_new(BIO_f_base64());
bio = BIO_new(BIO_s_mem());
bio = BIO_push(b64, bio);
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
BIO_write(bio, input.data(), (int)input.size());
BIO_flush(bio);
BIO_get_mem_ptr(bio, &bufferPtr);
std::string result(bufferPtr->data, bufferPtr->length);
BIO_free_all(bio);
return result;
}
std::string DBDCrypt::zlibDecompress(const std::vector<uint8_t>& compressed)
{
if (compressed.empty()) return "";
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = (uInt)compressed.size();
strm.next_in = (Bytef*)compressed.data();
if (inflateInit(&strm) != Z_OK) return "";
std::string result;
char buffer[32768];
do
{
strm.avail_out = sizeof(buffer);
strm.next_out = (Bytef*)buffer;
int ret = inflate(&strm, Z_NO_FLUSH);
if (ret != Z_OK && ret != Z_STREAM_END)
{
inflateEnd(&strm);
return "";
}
result.append(buffer, sizeof(buffer) - strm.avail_out);
} while (strm.avail_out == 0);
inflateEnd(&strm);
return result;
}
std::vector<uint8_t> DBDCrypt::zLibCompress(const std::string& data)
{
if (data.empty()) return {};
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
if (deflateInit(&strm, Z_DEFAULT_COMPRESSION) != Z_OK) return {};
strm.avail_in = (uInt)data.size();
strm.next_in = (Bytef*)data.data();
std::vector<uint8_t> result;
uint8_t buffer[32768];
do
{
strm.avail_out = sizeof(buffer);
strm.next_out = (Bytef*)buffer;
deflate(&strm, Z_FINISH);
result.insert(result.end(), buffer, buffer + (sizeof(buffer) - strm.avail_out));
} while (strm.avail_out == 0);
deflateEnd(&strm);
return result;
}
std::vector<uint8_t> DBDCrypt::transformCDNKey(const std::string& b64CDNKey)
{
auto encryptedKey = b64Dec(b64CDNKey);
std::vector<uint8_t> uuidKey(32, 0);
std::memcpy(uuidKey.data(), CDN_UUID, std::min((size_t)32, strlen(CDN_UUID)));
std::string decrypted = aesECBDecrypt(encryptedKey, uuidKey);
std::vector<uint8_t> finalKey(decrypted.begin(), decrypted.end());
if (finalKey.size() > 32)
finalKey.resize(32);
else if (finalKey.size() < 32)
finalKey.resize(32, 0);
return finalKey;
}
std::string DBDCrypt::shiftKeyID(const std::string& id, int shift)
{
std::string res = id;
for (char& c : res)
c = (char)((unsigned char)c + shift);
return res;
}
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#include <string>
#include <vector>
// 9.5.2_live
#define ACCESS_KEY "BGz7nwlRX8QP__fzvqrgpNRVqrlEyuY54vuGVAqDO_g="
#define KEY_ID "9.5.2_live"
/*
hardcoded variables (they have been the same since like 2017)
*/
#define CDN_KEY_BASE64 "lEQWeCt51ET+MIuxdTs7Ig/gzVZP2vdkVZA1BDfz+L0="
#define CDN_UUID "6EF35759-454D-4EBC-8041-9A94CB99FD5D"
class DBDCrypt
{
public:
enum PayloadType
{
NONE = 0,
TYPE_1, // compressed
TYPE_2, // CDN
TYPE_3 // dyn / accesskey
};
static std::string decrypt(const std::string& data, const std::string& accessKey,
PayloadType* outType = nullptr);
static std::string encrypt(const std::string& data, const std::string& accessKey, PayloadType type,
std::string keyId);
private:
static std::string decType1(const std::string& data, const std::string& key, PayloadType* outType);
static std::string decType2(const std::string& data, const std::string& key, PayloadType* outType);
static std::string decType3(const std::string& data, const std::string& key, PayloadType* outType);
static std::string aesECBDecrypt(const std::vector<uint8_t>& cipherText, const std::vector<uint8_t>& key);
static std::string aesECBEncrypt(const std::vector<uint8_t>& plainText, const std::vector<uint8_t>& key);
static std::vector<uint8_t> b64Dec(const std::string& input);
static std::string b64Enc(const std::vector<uint8_t>& input);
static std::string zlibDecompress(const std::vector<uint8_t>& compressed);
static std::vector<uint8_t> zLibCompress(const std::string& data);
static std::vector<uint8_t> transformCDNKey(const std::string& b64CDNKey);
static std::string shiftKeyID(const std::string& id, int shift);
};