Some people ask me where did I add encrypt/decrypt functions in OTClient, so it's hard to run OTClient with modified files (or get content of files).
I post my functions with some weird files encoding (all files as 1 string), but every programmer will find interesting things in my code.
Encryption is very weak (XOR https://en.wikipedia.org/wiki/XOR_cipher), but as it's already implemented in OTClient (crypt.cpp) and I got compatible version of XOR in PHP it's good enough.
My encryption code is in '#ifdef AB' parts (so you can compile client to work with not encrypted files - for tests).
I made modifications in 5 files:
Verify content of .otmod file on HDD, ignore it and load copy of file that is stored encrypted inside .exe (RAM):
src/framework/core/module.cpp
Load only modules listed by PHP (block possibility to add new module like candy_bot):
src/framework/core/modulemanager.cpp
I post my functions with some weird files encoding (all files as 1 string), but every programmer will find interesting things in my code.
Encryption is very weak (XOR https://en.wikipedia.org/wiki/XOR_cipher), but as it's already implemented in OTClient (crypt.cpp) and I got compatible version of XOR in PHP it's good enough.
My encryption code is in '#ifdef AB' parts (so you can compile client to work with not encrypted files - for tests).
I made modifications in 5 files:
Verify content of .otmod file on HDD, ignore it and load copy of file that is stored encrypted inside .exe (RAM):
src/framework/core/module.cpp
PHP:
bool Module::load()
{
if(m_loaded)
return true;
try {
// add to package.loaded
g_lua.getGlobalField("package", "loaded");
g_lua.getRef(m_sandboxEnv);
g_lua.setField(m_name);
g_lua.pop();
for(const std::string& depName : m_dependencies) {
if(depName == m_name)
stdext::throw_exception("cannot depend on itself");
ModulePtr dep = g_modules.getModule(depName);
if(!dep)
stdext::throw_exception(stdext::format("dependency '%s' was not found", depName));
if(dep->hasDependency(m_name, true))
stdext::throw_exception(stdext::format("dependency '%s' is recursively depending on itself", depName));
if(!dep->isLoaded() && !dep->load())
stdext::throw_exception(stdext::format("dependency '%s' has failed to load", depName));
}
#ifdef AB
std::string allData = "Here put text of all encrypted files with special encoding made by PHP (compatible with this script decoding)"; /* ABUPDATE */
//std::string allData = g_resources.readFileContents("/data/moduleLuaLoader_fakeFile_checkIntegrity.txt");
Crypt* x = new Crypt();
std::string fakeFilesSaltKey = x->sha256Encode("345435", false); /* ABUPDATE */
std::string fakeFilesXorKey = x->sha256Encode("#%%gf\"\";{", false); /* ABUPDATE */
std::string a = "";
#endif // AB
if(m_sandboxed)
g_lua.setGlobalEnvironment(m_sandboxEnv);
for(const std::string& script : m_scripts) {
#ifdef AB
int len = 2;
char* t = new char[len];
std::string source = script;
if(!stdext::starts_with(script, "/"))
source = g_lua.getCurrentSourcePath() + "/" + source;
source = g_resources.guessFilePath(source, "lua");
std::string buffer = g_resources.readFileContents(source);
std::string bufferMD5 = g_resources.readFileContents(source + ".md5hashf");
std::istringstream stream(allData);
std::string key = x->sha512Encode(buffer + fakeFilesSaltKey, false);
std::string d;
bool v = false;
while(!stream.eof())
{
delete[] t;
t = new char[9];
stream.read(t, 8);
t[8] = 0;
len = atoi(t);
delete[] t;
t = new char[len+1];
stream.read(t, len);
t[len] = 0;
d = t;
a = x->xorCrypt(x->base64Decode(d), key);
delete[] t;
t = new char[9];
stream.read(t, 8);
t[8] = 0;
len = atoi(t);
delete[] t;
t = new char[len+1];
stream.read(t, len);
t[len] = 0;
if(a == source)
{
d = t;
a = x->xorCrypt(x->base64Decode(d), source + key).substr(0, buffer.length() + 2 * fakeFilesSaltKey.length());
v = true;
break;
}
}
d.clear();
delete[] t;
//g_logger.error(stdext::format("code loaded %s", a));
if(!v) // file not found in array
{
g_logger.error(stdext::format(":|%s", source));
stdext::throw_exception(stdext::format(":|%s", source));
//*((unsigned int*)0) = 0x0000;
}
if(x->sha512Encode(buffer + fakeFilesSaltKey, false) != bufferMD5) // fake file sha key is invalid
{
g_logger.error(stdext::format(".%s", source));
stdext::throw_exception(stdext::format(".%s", source));
//*((unsigned int*)0) = 0x0000;
}
if(x->sha256Encode(fakeFilesSaltKey + buffer + fakeFilesSaltKey, false) != x->sha256Encode(a, false)) // modified file, cannot load RAM copy
{
g_logger.error(stdext::format("-%s", source));
stdext::throw_exception(stdext::format("-%s", source));
//*((unsigned int*)0) = 0x0000;
}
a = a.substr(fakeFilesSaltKey.length(), a.length() - 2 * fakeFilesSaltKey.length());
a = x->xorCrypt(x->base64Decode(a), source + fakeFilesXorKey);
g_lua.loadBuffer(a, "@" + source);
a.clear();
#else
g_lua.loadScript(script);
#endif // AB
g_lua.safeCall(0, 0);
}
const std::string& onLoadBuffer = std::get<0>(m_onLoadFunc);
const std::string& onLoadSource = std::get<1>(m_onLoadFunc);
if(!onLoadBuffer.empty()) {
g_lua.loadBuffer(onLoadBuffer, onLoadSource);
if(m_sandboxed) {
g_lua.getRef(m_sandboxEnv);
g_lua.setEnv();
}
g_lua.safeCall(0, 0);
}
if(m_sandboxed)
g_lua.resetGlobalEnvironment();
m_loaded = true;
g_logger.debug(stdext::format("Loaded module '%s'", m_name));
} catch(stdext::exception& e) {
// remove from package.loaded
g_lua.getGlobalField("package", "loaded");
g_lua.pushNil();
g_lua.setField(m_name);
g_lua.pop();
if(m_sandboxed)
g_lua.resetGlobalEnvironment();
g_logger.error(stdext::format("Unable to load module '%s': %s", m_name, e.what()));
return false;
}
g_modules.updateModuleLoadOrder(asModule());
for(const std::string& modName : m_loadLaterModules) {
ModulePtr dep = g_modules.getModule(modName);
if(!dep)
g_logger.error(stdext::format("Unable to find module '%s' required by '%s'", modName, m_name));
else if(!dep->isLoaded())
dep->load();
}
return true;
}
Load only modules listed by PHP (block possibility to add new module like candy_bot):
src/framework/core/modulemanager.cpp
PHP:
void ModuleManager::discoverModules()
{
// remove modules that are not loaded
m_autoLoadModules.clear();
#ifdef AB
Crypt* x = new Crypt();
std::string a = "";
int len = 2;
char* t;
std::string allData = "Here put text of all encrypted files with special encoding made by PHP (compatible with this script decoding)"; /* ABUPDATE */
//std::string allData = g_resources.readFileContents("data/discoverModules_fakeFile_allowedList.txt");
std::string fakeFilesDiscoverXorKey = x->sha256Encode("12343", false);/* ABUPDATE */
allData = x->base64Decode(x->xorCrypt(x->base64Decode(allData), fakeFilesDiscoverXorKey));
#endif
auto moduleDirs = g_resources.listDirectoryFiles("/");
for(const std::string& moduleDir : moduleDirs) {
auto moduleFiles = g_resources.listDirectoryFiles("/" + moduleDir);
for(const std::string& moduleFile : moduleFiles) {
if(g_resources.isFileType(moduleFile, "otmod")) {
#ifdef AB
//g_logger.error(stdext::format("mod %s", "/" + moduleDir + "/" + moduleFile));
t = new char[len];
std::istringstream stream(allData);
std::string d;
bool v = false;
while(!stream.eof())
{
delete[] t;
t = new char[9];
stream.read(t, 8);
t[8] = 0;
len = atoi(t);
delete[] t;
t = new char[len+1];
stream.read(t, len);
t[len] = 0;
if(t == "/" + moduleDir + "/" + moduleFile)
{
v = true;
break;
}
}
delete[] t;
if(!v)
{
g_logger.error(stdext::format("failed to load %s", "/" + moduleDir + "/" + moduleFile));
continue;
}
#endif
ModulePtr module = discoverModule("/" + moduleDir + "/" + moduleFile);
if(module && module->isAutoLoad())
m_autoLoadModules.insert(std::make_pair(module->getAutoLoadPriority(), module));
}
}
}
}