diff --git a/src/Components/Modules/FastFiles.cpp b/src/Components/Modules/FastFiles.cpp index ce9db973..cd10f1a6 100644 --- a/src/Components/Modules/FastFiles.cpp +++ b/src/Components/Modules/FastFiles.cpp @@ -9,6 +9,8 @@ namespace Components bool FastFiles::IsIW4xZone = false; bool FastFiles::StreamRead = false; + char FastFiles::LastByteRead = 0; + unsigned int FastFiles::CurrentZone; unsigned int FastFiles::MaxZones; @@ -332,6 +334,7 @@ namespace Components void FastFiles::ReadHeaderStub(unsigned int* header, int size) { FastFiles::IsIW4xZone = false; + FastFiles::LastByteRead = 0; Game::DB_ReadXFileUncompressed(header, size); if (header[0] == XFILE_HEADER_IW4X) @@ -439,6 +442,36 @@ namespace Components Utils::Hook::Call(0x5BBAC0)(zoneInfo, zoneCount); } + __declspec(naked) void FastFiles::ReadXFile(void* /*buffer*/, int /*size*/) + { + __asm + { + mov eax, [esp + 4] + mov ecx, [esp + 8] + + push 445468h + retn + } + } + + void FastFiles::ReadXFileStub(char* buffer, int size) + { + FastFiles::ReadXFile(buffer, size); + + if(FastFiles::IsIW4xZone) + { + for(int i = 0; i < size; ++i) + { + buffer[i] ^= FastFiles::LastByteRead; + Utils::RotLeft(buffer[i], 4); + buffer[i] ^= -1; + Utils::RotRight(buffer[i], 6); + + FastFiles::LastByteRead = buffer[i]; + } + } + } + #ifdef DEBUG void FastFiles::LogStreamRead(int len) { @@ -466,6 +499,9 @@ namespace Components // Allow loading IW4x zones Utils::Hook(0x4157B8, FastFiles::ReadHeaderStub, HOOK_CALL).install()->quick(); + // Obfuscate zone data + Utils::Hook(Game::DB_ReadXFile, FastFiles::ReadXFileStub, HOOK_JUMP).install()->quick(); + // Allow custom zone loading if (!ZoneBuilder::IsEnabled()) { diff --git a/src/Components/Modules/FastFiles.hpp b/src/Components/Modules/FastFiles.hpp index fdbc87c6..76175219 100644 --- a/src/Components/Modules/FastFiles.hpp +++ b/src/Components/Modules/FastFiles.hpp @@ -41,6 +41,8 @@ namespace Components static bool IsIW4xZone; static bool StreamRead; + static char LastByteRead; + static Key CurrentKey; static symmetric_CTR CurrentCTR; static std::vector ZonePaths; @@ -62,6 +64,9 @@ namespace Components static void LoadZonesStub(Game::XZoneInfo *zoneInfo, unsigned int zoneCount); + static void ReadXFile(void* buffer, int size); + static void ReadXFileStub(char* buffer, int size); + #ifdef DEBUG static void LogStreamRead(int len); #endif diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index 7cbae8cf..1fee5afd 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -445,6 +445,18 @@ namespace Components #ifndef DEBUG // Insert a random byte, this will destroy the whole alignment and result in a crash, if not handled zoneBuffer.insert(zoneBuffer.begin(), static_cast(Utils::Cryptography::Rand::GenerateInt())); + + char lastByte = 0; + for(unsigned int i = 0; i < zoneBuffer.size(); ++i ) + { + char oldLastByte = lastByte; + lastByte = zoneBuffer[i]; + + Utils::RotLeft(zoneBuffer[i], 6); + zoneBuffer[i] ^= -1; + Utils::RotRight(zoneBuffer[i], 4); + zoneBuffer[i] ^= oldLastByte; + } #endif zoneBuffer = Utils::Compression::ZLib::Compress(zoneBuffer); @@ -988,4 +1000,37 @@ namespace Components assert(ZoneBuilder::MemAllocator.empty()); ZoneBuilder::CommonAssets.clear(); } + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + bool ZoneBuilder::unitTest() + { + printf("Testing circular bit shifting (left)..."); + + unsigned int integer = 0x80000000; + Utils::RotLeft(integer, 1); + + if(integer != 1) + { + printf("Error\n"); + printf("Bit shifting failed: %X\n", integer); + return false; + } + + printf("Success\n"); + printf("Testing circular bit shifting (right)..."); + + unsigned char byte = 0b00000011; + Utils::RotRight(byte, 2); + + if (byte != 0b11000000) + { + printf("Error\n"); + printf("Bit shifting failed %X\n", byte & 0xFF); + return false; + } + + printf("Success\n"); + return true; + } +#endif } diff --git a/src/Components/Modules/ZoneBuilder.hpp b/src/Components/Modules/ZoneBuilder.hpp index 395a6abe..f1b0834b 100644 --- a/src/Components/Modules/ZoneBuilder.hpp +++ b/src/Components/Modules/ZoneBuilder.hpp @@ -4,7 +4,7 @@ #define XFILE_VERSION 276 #define XFILE_HEADER_IW4X 0x78345749 // 'IW4x' -#define XFILE_VERSION_IW4X 1 +#define XFILE_VERSION_IW4X 2 namespace Components { @@ -97,6 +97,7 @@ namespace Components #if defined(DEBUG) || defined(FORCE_UNIT_TESTS) const char* getName() override { return "ZoneBuilder"; }; + bool unitTest() override; #endif static bool IsEnabled(); diff --git a/src/Utils/Utils.hpp b/src/Utils/Utils.hpp index 58ae8180..050158ae 100644 --- a/src/Utils/Utils.hpp +++ b/src/Utils/Utils.hpp @@ -15,6 +15,25 @@ namespace Utils bool HasIntercection(unsigned int base1, unsigned int len1, unsigned int base2, unsigned int len2); + template inline void RotLeft(T& object, size_t bits) + { + bits %= sizeof(T) * 8; + + T sign = 1; + sign = sign << (sizeof(T) * 8 - 1); + + bool negative = (object & sign) != 0; + object &= ~sign; + object = (object << bits) | (object >> (sizeof(T) * 8 - bits)); + object |= T(negative) << ((sizeof(T) * 8 - 1 + bits) % (sizeof(T) * 8)); + } + + template inline void RotRight(T& object, size_t bits) + { + bits %= (sizeof(T) * 8); + RotLeft(object, ((sizeof(T) * 8) - bits)); + } + template inline void Merge(std::vector* target, T* source, size_t length) { if (source)