Skip to content

Commit 1d3ccd6

Browse files
authored
fix: #867
1 parent d7481e8 commit 1d3ccd6

File tree

10 files changed

+76
-21
lines changed

10 files changed

+76
-21
lines changed

modules/game_features/features.lua

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ controller:registerEvents(g_game, {
55
-- g_game.enableFeature(GameSmoothWalkElevation)
66
-- g_game.enableFeature(GameNegativeOffset)
77
-- g_game.enableFeature(GameWingsAurasEffectsShader)
8+
-- g_game.enableFeature(GameAllowCustomBotScripts)
89

910
g_game.enableFeature(GameFormatCreatureName)
1011

modules/gamelib/const.lua

+1
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ GameNegativeOffset = 116
200200
GameItemTooltipV8 = 117
201201
GameWingsAurasEffectsShader = 118
202202
GameForgeConvergence = 119
203+
GameAllowCustomBotScripts = 120
203204

204205
TextColors = {
205206
red = '#f55e5e', -- '#c83200'

src/client/const.h

+1
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ namespace Otc
548548
GameItemTooltipV8 = 117,
549549
GameWingsAurasEffectsShader = 118,
550550
GameForgeConvergence = 119,
551+
GameAllowCustomBotScripts = 120,
551552
LastGameFeature
552553
};
553554

src/client/spritemanager.cpp

+2-6
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ void SpriteManager::load() {
5151
if (g_app.isLoadingAsyncTexture()) {
5252
for (auto& file : m_spritesFiles)
5353
file = std::make_unique<FileStream_m>(g_resources.openFile(m_lastFileName));
54-
} else (m_spritesFiles[0] = std::make_unique<FileStream_m>(g_resources.openFile(m_lastFileName)))->file->cache();
54+
} else (m_spritesFiles[0] = std::make_unique<FileStream_m>(g_resources.openFile(m_lastFileName)))->file->cache(true);
5555
}
5656

5757
bool SpriteManager::loadSpr(std::string file)
@@ -63,10 +63,6 @@ bool SpriteManager::loadSpr(std::string file)
6363
m_lastFileName = g_resources.guessFilePath(file, "spr");
6464
load();
6565

66-
if (g_app.isEncrypted()) {
67-
ResourceManager::decrypt(getSpriteFile()->m_data.data(), getSpriteFile()->m_data.size());
68-
}
69-
7066
m_signature = getSpriteFile()->getU32();
7167
m_spritesCount = g_game.getFeature(Otc::GameSpritesU32) ? getSpriteFile()->getU32() : getSpriteFile()->getU16();
7268
m_spritesOffset = getSpriteFile()->tell();
@@ -251,4 +247,4 @@ ImagePtr SpriteManager::getSpriteImage(int id, const FileStreamPtr& file) {
251247
g_logger.error(stdext::format("Failed to get sprite id %d: %s", id, e.what()));
252248
return nullptr;
253249
}
254-
}
250+
}

src/client/thingtypemanager.cpp

+2-6
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,7 @@ bool ThingTypeManager::loadDat(std::string file)
8282
file = g_resources.guessFilePath(file, "dat");
8383

8484
const auto& fin = g_resources.openFile(file);
85-
fin->cache();
86-
87-
#if ENABLE_ENCRYPTION == 1
88-
ResourceManager::decrypt(fin->m_data.data(), fin->m_data.size());
89-
#endif
85+
fin->cache(true);
9086

9187
m_datSignature = fin->getU32();
9288
m_contentRevision = static_cast<uint16_t>(m_datSignature);
@@ -497,4 +493,4 @@ void ThingTypeManager::loadXml(const std::string& file)
497493

498494
#endif
499495

500-
/* vim: set ts=4 sw=4 et: */
496+
/* vim: set ts=4 sw=4 et: */

src/framework/config.h

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#define ENABLE_ENCRYPTION_BUILDER 0
3737
// for security reasons make sure you are using password with at last 100+ characters
3838
#define ENCRYPTION_PASSWORD "SET_YOUR_PASSWORD_HERE"
39+
#define ENCRYPTION_HEADER "SET_YOUR_HEADER_HERE"
3940

4041
// DISCORD RPC (https://discord.com/developers/applications)
4142
// Note: Only for VSSolution, doesn't work with CMAKE

src/framework/core/filestream.cpp

+18-3
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,36 @@ FileStream::~FileStream()
5959
close();
6060
}
6161

62-
void FileStream::cache()
62+
void FileStream::cache(bool useEnc)
6363
{
6464
m_caching = true;
6565

6666
if (!m_writeable) {
6767
if (!m_fileHandle)
6868
return;
6969

70-
// cache entire file into data buffer
70+
// Cache entire file into data buffer
7171
m_pos = PHYSFS_tell(m_fileHandle);
7272
PHYSFS_seek(m_fileHandle, 0);
7373
const int size = PHYSFS_fileLength(m_fileHandle);
7474
m_data.resize(size);
75+
7576
if (PHYSFS_readBytes(m_fileHandle, m_data.data(), size) == -1)
7677
throwError("unable to read file data", true);
78+
79+
#if ENABLE_ENCRYPTION == 1
80+
if (useEnc) {
81+
if (m_data.size() >= std::string(ENCRYPTION_HEADER).size() &&
82+
std::memcmp(m_data.data(), ENCRYPTION_HEADER, std::string(ENCRYPTION_HEADER).size()) == 0) {
83+
m_data.erase(m_data.begin(), m_data.begin() + std::string(ENCRYPTION_HEADER).size());
84+
}
85+
86+
uint8_t* decryptedData = ResourceManager::decrypt(m_data.data(), m_data.size());
87+
m_data.resize(size - std::string(ENCRYPTION_HEADER).size());
88+
memcpy(m_data.data(), decryptedData, m_data.size());
89+
delete[] decryptedData;
90+
}
91+
#endif
7792
PHYSFS_close(m_fileHandle);
7893
m_fileHandle = nullptr;
7994
}
@@ -455,4 +470,4 @@ void FileStream::throwError(const std::string_view message, bool physfsError) co
455470
if (physfsError)
456471
completeMessage += ": "s + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode());
457472
throw Exception(completeMessage);
458-
}
473+
}

src/framework/core/filestream.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class FileStream : public std::enable_shared_from_this<FileStream>
3737
FileStream(std::string name, const std::string_view buffer);
3838
~FileStream();
3939

40-
void cache();
40+
void cache(bool useEnc = false);
4141
void close();
4242
void flush();
4343
void write(const void* buffer, uint32_t count);

src/framework/core/resourcemanager.cpp

+47-5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include <filesystem>
2424

25+
#include <client/game.h>
2526
#include "resourcemanager.h"
2627
#include "filestream.h"
2728

@@ -208,7 +209,7 @@ std::string ResourceManager::readFileContents(const std::string& fileName)
208209
{
209210
const std::string fullPath = resolvePath(fileName);
210211

211-
if (fullPath.find("/downloads") != std::string::npos) {
212+
if (fullPath.find(g_resources.getByteStrings(0)) != std::string::npos) {
212213
auto dfile = g_http.getFile(fullPath.substr(10));
213214
if (dfile)
214215
return std::string(dfile->response.begin(), dfile->response.end());
@@ -223,8 +224,23 @@ std::string ResourceManager::readFileContents(const std::string& fileName)
223224
PHYSFS_readBytes(file, &buffer[0], fileSize);
224225
PHYSFS_close(file);
225226

227+
bool hasHeader = false;
228+
if (buffer.size() >= std::string(ENCRYPTION_HEADER).size() &&
229+
buffer.substr(0, std::string(ENCRYPTION_HEADER).size()) == std::string(ENCRYPTION_HEADER)) {
230+
hasHeader = true;
231+
}
232+
226233
#if ENABLE_ENCRYPTION == 1
227-
buffer = decrypt(buffer);
234+
if (g_game.getFeature(Otc::GameAllowCustomBotScripts)) {
235+
if (fullPath.find(g_resources.getByteStrings(1)) != std::string::npos && !hasHeader) {
236+
return buffer;
237+
}
238+
}
239+
240+
if (hasHeader) {
241+
buffer = buffer.substr(std::string(ENCRYPTION_HEADER).size());
242+
buffer = decrypt(buffer);
243+
}
228244
#endif
229245

230246
return buffer;
@@ -271,7 +287,9 @@ bool ResourceManager::writeFileStream(const std::string& fileName, std::iostream
271287
bool ResourceManager::writeFileContents(const std::string& fileName, const std::string& data)
272288
{
273289
#if ENABLE_ENCRYPTION == 1
274-
return writeFileBuffer(fileName, (const uint8_t*)encrypt(data, std::string(ENCRYPTION_PASSWORD)).c_str(), data.size());
290+
std::string encryptedData = encrypt(data, std::string(ENCRYPTION_PASSWORD));
291+
std::string finalData = std::string(ENCRYPTION_HEADER) + encryptedData;
292+
return writeFileBuffer(fileName, (const uint8_t*)finalData.c_str(), finalData.size());
275293
#else
276294
return writeFileBuffer(fileName, (const uint8_t*)data.c_str(), data.size());
277295
#endif
@@ -539,7 +557,8 @@ void ResourceManager::runEncryption(const std::string& password)
539557
std::string data((std::istreambuf_iterator(ifs)), std::istreambuf_iterator<char>());
540558
ifs.close();
541559
data = encrypt(data, password);
542-
save_string_into_file(data, entry.path().string());
560+
std::string finalData = std::string(ENCRYPTION_HEADER) + data;
561+
save_string_into_file(finalData, entry.path().string());
543562
}
544563
}
545564

@@ -738,4 +757,27 @@ std::unordered_map<std::string, std::string> ResourceManager::decompressArchive(
738757
{
739758
std::unordered_map<std::string, std::string> ret;
740759
return ret;
741-
}
760+
}
761+
762+
std::string ResourceManager::decodificateStrings(const std::vector<unsigned char>& bytes) {
763+
std::string result;
764+
for (unsigned char c : bytes) {
765+
result.push_back(c ^ 0xAA);
766+
}
767+
return result;
768+
}
769+
770+
// used to obfuscate vulnerable strings (provisional)
771+
std::string ResourceManager::getByteStrings(size_t line) {
772+
std::vector<std::vector<unsigned char>> strTable = {
773+
{0x85, 0xCE, 0xC5, 0xDD, 0xC4, 0xC6, 0xC5, 0xCB, 0xCE, 0xD9}, // "/downloads"
774+
{0x85, 0xC8, 0xC5, 0xDE, 0x85}, // "/bot/"
775+
{0xE6, 0xC3, 0xC4, 0xC2, 0xCB, 0x8A, 0xCE, 0xCF, 0x8A, 0xD8, 0xCF, 0xDE, 0xC5, 0xD8, 0xC4, 0xC5, 0x8A, 0xC3, 0xC4, 0xDC, 0xCB, 0xC6, 0xC3, 0xCE, 0xCB}, // "Linha de retorno invalida"
776+
};
777+
778+
if (line < strTable.size()) {
779+
return decodificateStrings(strTable[line]);
780+
} else {
781+
return decodificateStrings(strTable[2]);
782+
}
783+
}

src/framework/core/resourcemanager.h

+2
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ class ResourceManager
9393
bool launchCorrect(std::vector<std::string>& args);
9494
std::string createArchive(const std::unordered_map<std::string, std::string>& files);
9595
std::unordered_map<std::string, std::string> decompressArchive(std::string dataOrPath);
96+
std::string decodificateStrings(const std::vector<unsigned char>& bytes);
97+
std::string getByteStrings(size_t line);
9698

9799
std::string getBinaryPath() { return m_binaryPath.string(); }
98100

0 commit comments

Comments
 (0)