Skip to content

Commit

Permalink
Use new Crypto interface in Warden
Browse files Browse the repository at this point in the history
  • Loading branch information
0blu committed Oct 23, 2024
1 parent 07af1ae commit be39b70
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 42 deletions.
11 changes: 6 additions & 5 deletions src/game/Anticheat/WardenAnticheat/Warden.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,13 +437,14 @@ void Warden::StopScanClock()

uint32 Warden::BuildChecksum(uint8 const* data, size_t size)
{
uint8 hash[SHA_DIGEST_LENGTH];
SHA1(data, size, hash);
auto hash = Crypto::Hash::SHA1::ComputeFrom(data, size);

uint32 checkSum = 0;

for (auto i = 0u; i < sizeof(hash) / sizeof(uint32); ++i)
checkSum ^= *reinterpret_cast<uint32*>(&hash[i * 4]);
checkSum ^= *reinterpret_cast<uint32*>(&hash[sizeof(uint32) * 0]);
checkSum ^= *reinterpret_cast<uint32*>(&hash[sizeof(uint32) * 1]);
checkSum ^= *reinterpret_cast<uint32*>(&hash[sizeof(uint32) * 2]);
checkSum ^= *reinterpret_cast<uint32*>(&hash[sizeof(uint32) * 3]);
checkSum ^= *reinterpret_cast<uint32*>(&hash[sizeof(uint32) * 4]);

return checkSum;
}
Expand Down
38 changes: 16 additions & 22 deletions src/game/Anticheat/WardenAnticheat/WardenScan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@
#include "Util.h"
#include "Crypto/Hash/HMACSHA1.h"
#include "Crypto/Hash/SHA1.h"
#include <openssl/sha.h>
#include <openssl/md5.h>

#include <string>
#include <algorithm>
Expand Down Expand Up @@ -119,7 +117,7 @@ WindowsStringHashScan::WindowsStringHashScan()
GetBuilder(),
// checker
GetChecker(),
128, sizeof(uint8) + SHA_DIGEST_LENGTH + MD5_DIGEST_LENGTH, "Maiev string hash",
128, sizeof(uint8) + Crypto::Hash::SHA1::Digest::size() + Crypto::Hash::MD5::Digest::size(), "Maiev string hash",
ScanFlags::Maiev, 0, UINT16_MAX)
{

Expand All @@ -131,7 +129,7 @@ MacStringHashScan::MacStringHashScan(bool moduleLoaded)
GetBuilder(),
// checker
GetChecker(),
128, sizeof(uint8) + SHA_DIGEST_LENGTH + MD5_DIGEST_LENGTH, moduleLoaded ? "Mac string hash" : "Maiev string hash",
128, sizeof(uint8) + Crypto::Hash::SHA1::Digest::size() + Crypto::Hash::MD5::Digest::size(), moduleLoaded ? "Mac string hash" : "Maiev string hash",
(moduleLoaded ? ScanFlags::None : ScanFlags::Maiev), 0, UINT16_MAX)
{

Expand Down Expand Up @@ -159,7 +157,7 @@ WindowsModuleScan::WindowsModuleScan(std::string const& module, bool wanted, std
{
auto const found = buff.read<uint8>() == ModuleFound;
return found != this->m_wanted;
}, sizeof(uint8) + sizeof(uint32) + SHA_DIGEST_LENGTH, sizeof(uint8), comment, flags, minBuild, maxBuild)
}, sizeof(uint8) + sizeof(uint32) + Crypto::Hash::SHA1::Digest::size(), sizeof(uint8), comment, flags, minBuild, maxBuild)
{
// the game depends on uppercase module names being sent
std::transform(m_module.begin(), m_module.end(), m_module.begin(), ::toupper);
Expand All @@ -182,7 +180,7 @@ WindowsModuleScan::WindowsModuleScan(std::string const& module, CheckT checker,

scan.append(hash.GetDigest(), hash.GetLength());
},
checker, sizeof(uint8) + sizeof(uint32) + SHA_DIGEST_LENGTH, sizeof(uint8), comment, flags, minBuild, maxBuild)
checker, sizeof(uint8) + sizeof(uint32) + Crypto::Hash::SHA1::Digest::size(), sizeof(uint8), comment, flags, minBuild, maxBuild)
{
// the game depends on uppercase module names being sent
std::transform(m_module.begin(), m_module.end(), m_module.begin(), ::toupper);
Expand Down Expand Up @@ -317,13 +315,13 @@ WindowsCodeScan::WindowsCodeScan(uint32 offset, std::vector<uint8> const& patter
{
auto const found = buff.read<uint8>() == PatternFound;
return found != this->m_wanted;
}, sizeof(uint8) + sizeof(uint32) + SHA_DIGEST_LENGTH + sizeof(uint32) + sizeof(uint8), sizeof(uint8), comment, flags, minBuild, maxBuild)
}, sizeof(uint8) + sizeof(uint32) + Crypto::Hash::SHA1::Digest::size() + sizeof(uint32) + sizeof(uint8), sizeof(uint8), comment, flags, minBuild, maxBuild)
{
MANGOS_ASSERT(m_pattern.size() <= 0xFF);
}

WindowsFileHashScan::WindowsFileHashScan(std::string const& file, void const* expected, bool wanted, std::string const& comment, ScanFlags flags, uint32 minBuild, uint32 maxBuild)
: m_file(file), m_wanted(wanted), m_hashMatch(!!expected),
WindowsFileHashScan::WindowsFileHashScan(std::string const& file, nonstd::optional<Crypto::Hash::SHA1::Digest> const& expectedHash, bool wanted, std::string const& comment, ScanFlags flags, uint32 minBuild, uint32 maxBuild)
: m_file(file), m_wanted(wanted), m_expectedHash(expectedHash),
WindowsScan(
// builder
[this](Warden const* warden, std::vector<std::string>& strings, ByteBuffer& scan)
Expand All @@ -350,16 +348,13 @@ WindowsFileHashScan::WindowsFileHashScan(std::string const& file, void const* ex
if (!this->m_wanted && !success)
return false;

uint8 hash[SHA_DIGEST_LENGTH];

buff.read(hash, sizeof(hash));
Crypto::Hash::SHA1::Digest hashFromClient;
buff.read(hashFromClient.data(), hashFromClient.size());

// if a hash was given, check it (some checks may only be interested in existence)
return this->m_hashMatch && !!memcmp(hash, this->m_expected, sizeof(hash));
}, sizeof(uint8) + sizeof(uint8) + file.length(), sizeof(uint8) + SHA_DIGEST_LENGTH, comment, flags | ScanFlags::OffsetsInitialized, minBuild, maxBuild)
return m_expectedHash.has_value() && hashFromClient != m_expectedHash;
}, sizeof(uint8) + sizeof(uint8) + file.length(), sizeof(uint8) + Crypto::Hash::SHA1::Digest::size(), comment, flags | ScanFlags::OffsetsInitialized, minBuild, maxBuild)
{
if (m_hashMatch)
::memcpy(m_expected, expected, sizeof(m_expected));
}

WindowsLuaScan::WindowsLuaScan(std::string const& lua, bool wanted, std::string const& comment, ScanFlags flags, uint32 minBuild, uint32 maxBuild)
Expand Down Expand Up @@ -445,9 +440,9 @@ WindowsLuaScan::WindowsLuaScan(std::string const& lua, CheckT checker, std::stri
MANGOS_ASSERT(checker);
}

WindowsHookScan::WindowsHookScan(std::string const& module, std::string const& proc, void const* hash,
WindowsHookScan::WindowsHookScan(std::string const& module, std::string const& proc, Crypto::Hash::SHA1::Digest const& hash,
uint32 offset, size_t length, std::string const& comment, ScanFlags flags, uint32 minBuild, uint32 maxBuild)
: m_module(module), m_proc(proc), m_offset(offset), m_length(length),
: m_module(module), m_proc(proc), m_hash(hash), m_offset(offset), m_length(length),
WindowsScan(
// builder
[this](Warden const* warden, std::vector<std::string>& strings, ByteBuffer& scan)
Expand All @@ -462,7 +457,7 @@ WindowsHookScan::WindowsHookScan(std::string const& module, std::string const& p

scan << static_cast<uint8>(winWarden->GetModule()->opcodes[API_CHECK] ^ winWarden->GetXor()) << seed;

scan.append(this->m_hash, sizeof(this->m_hash));
scan.append(m_hash.data(), m_hash.size());
scan << static_cast<uint8>(strings.size() - 1)
<< static_cast<uint8>(strings.size())
<< this->m_offset
Expand All @@ -473,11 +468,10 @@ WindowsHookScan::WindowsHookScan(std::string const& module, std::string const& p
{
return buff.read<uint8>() == Detoured;
},
sizeof(uint8) + sizeof(uint32) + SHA_DIGEST_LENGTH + sizeof(uint8) + sizeof(uint8) + sizeof(uint32) + sizeof(uint8),
sizeof(uint8) + sizeof(uint32) + Crypto::Hash::SHA1::Digest::size() + sizeof(uint8) + sizeof(uint8) + sizeof(uint32) + sizeof(uint8),
sizeof(uint8), comment, flags, minBuild, maxBuild)
{
MANGOS_ASSERT(length <= 0xFF);
::memcpy(m_hash, hash, sizeof(m_hash));
}

WindowsDriverScan::WindowsDriverScan(std::string const& name, std::string const& targetPath, bool wanted, std::string const& comment, ScanFlags flags, uint32 minBuild, uint32 maxBuild)
Expand Down Expand Up @@ -508,7 +502,7 @@ WindowsDriverScan::WindowsDriverScan(std::string const& name, std::string const&
auto const found = buff.read<uint8>() == Found;

return found != this->m_wanted;
}, sizeof(uint8) + sizeof(uint32) + SHA_DIGEST_LENGTH + sizeof(uint8) + name.length(), sizeof(uint8), comment, flags, minBuild, maxBuild) {}
}, sizeof(uint8) + sizeof(uint32) + Crypto::Hash::SHA1::Digest::size() + sizeof(uint8) + name.length(), sizeof(uint8), comment, flags, minBuild, maxBuild) {}

WindowsTimeScan::WindowsTimeScan(CheckT checker, std::string const& comment, ScanFlags flags, uint32 minBuild, uint32 maxBuild) :
WindowsScan(
Expand Down
13 changes: 7 additions & 6 deletions src/game/Anticheat/WardenAnticheat/WardenScan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@
#include "ByteBuffer.h"
#include "World.h"
#include "Log.h"
#include <openssl/sha.h>
#include "Crypto/Hash/SHA1.h"

#include <functional>
#include <vector>
#include <memory>
#include <string>

#include "nonstd/optional.hpp"

enum class ScanFlags : uint32
{
None = 0x00000,
Expand Down Expand Up @@ -190,12 +192,11 @@ class WindowsFileHashScan : public WindowsScan
{
private:
std::string m_file;
uint8 m_expected[SHA_DIGEST_LENGTH];
bool m_hashMatch;
nonstd::optional<Crypto::Hash::SHA1::Digest> m_expectedHash;
bool m_wanted;

public:
WindowsFileHashScan(std::string const& file, void const* expected, bool wanted, std::string const& comment, ScanFlags flags, uint32 minBuild, uint32 maxBuild);
WindowsFileHashScan(std::string const& file, nonstd::optional<Crypto::Hash::SHA1::Digest> const& expectedHash, bool wanted, std::string const& comment, ScanFlags flags, uint32 minBuild, uint32 maxBuild);
};

// reads the value of a lua variable and returns it. keep in mind when using this that different states
Expand Down Expand Up @@ -224,12 +225,12 @@ class WindowsHookScan : public WindowsScan

std::string m_module;
std::string m_proc;
uint8 m_hash[SHA_DIGEST_LENGTH];
Crypto::Hash::SHA1::Digest m_hash;
uint32 m_offset;
size_t m_length;

public:
WindowsHookScan(std::string const& module, std::string const& proc, void const* hash, uint32 offset, size_t length, std::string const& comment, ScanFlags flags, uint32 minBuild, uint32 maxBuild);
WindowsHookScan(std::string const& module, std::string const& proc, Crypto::Hash::SHA1::Digest const& hash, uint32 offset, size_t length, std::string const& comment, ScanFlags flags, uint32 minBuild, uint32 maxBuild);
};

// this scan will search for call kernel32!QueryDosDevice() and search for a device with the given name.
Expand Down
25 changes: 18 additions & 7 deletions src/game/Anticheat/WardenAnticheat/WardenScanMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,23 @@ void WardenScanMgr::LoadFromDB()
{
auto const filename = fields[2].GetCppString();

std::vector<uint8> expected;
if (!BuildRawData(fields[6].GetCppString(), expected))
// If you know a good file, you provide `str` and `result` row
// If you know a bad file, you provide `str` and empty `result` row
std::vector<uint8> expectedHashAsVector;
if (!BuildRawData(fields[6].GetCppString(), expectedHashAsVector) || !(expectedHashAsVector.empty() || expectedHashAsVector.size() == Crypto::Hash::SHA1::Digest::size()))
{
sLog.Out(LOG_ANTICHEAT, LOG_LVL_ERROR, "Failed to parse expected value in Warden scan id %u", id);
continue;
}

scan = new WindowsFileHashScan(filename, &expected[0], !expected.empty(), comment, flags, buildMin, buildMax);
nonstd::optional<Crypto::Hash::SHA1::Digest> expectedHash;
if (!expectedHashAsVector.empty())
{
expectedHash = Crypto::Hash::SHA1::CreateZero();
std::copy_n(expectedHashAsVector.begin(), expectedHashAsVector.size(), expectedHash.value().begin());
}

scan = new WindowsFileHashScan(filename, expectedHash, !expectedHashAsVector.empty(), comment, flags, buildMin, buildMax);
break;
}

Expand All @@ -203,15 +212,17 @@ void WardenScanMgr::LoadFromDB()
{
auto const module = fields[2].GetCppString();
auto const proc = fields[3].GetCppString();

std::vector<uint8> hash;
if (!BuildRawData(fields[6].GetCppString(), hash))

std::vector<uint8> expectedHashAsVector;
Crypto::Hash::SHA1::Digest expectedHash;
if (!BuildRawData(fields[6].GetCppString(), expectedHashAsVector) || expectedHashAsVector.size() != expectedHash.size())
{
sLog.Out(LOG_ANTICHEAT, LOG_LVL_ERROR, "Failed to parse expected value in Warden scan id %u", id);
continue;
}
std::copy_n(expectedHashAsVector.begin(), expectedHashAsVector.size(), expectedHash.begin());

scan = new WindowsHookScan(module, proc, &hash[0], offset, length, comment, flags, buildMin, buildMax);
scan = new WindowsHookScan(module, proc, expectedHash, offset, length, comment, flags, buildMin, buildMax);
break;
}

Expand Down
4 changes: 2 additions & 2 deletions src/game/Anticheat/WardenAnticheat/WardenWin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ void WardenWin::LoadScriptedScans()
return false;
},
// TODO: Replace the magic number below with combined driver string lengths
(sizeof(uint8) + sizeof(uint32) + SHA_DIGEST_LENGTH + sizeof(uint8)) * HypervisorCount + 21,
(sizeof(uint8) + sizeof(uint32) + Crypto::Hash::SHA1::Digest::size() + sizeof(uint8)) * HypervisorCount + 21,
sizeof(uint8) * HypervisorCount,
"Hypervisor check",
ScanFlags::InitialLogin, 0, UINT16_MAX));
Expand Down Expand Up @@ -813,7 +813,7 @@ void WardenWin::LoadScriptedScans()

// if this is not found, it means someone has tampered with the function
return !found;
}, sizeof(uint8) + sizeof(uint32) + SHA_DIGEST_LENGTH + sizeof(uint32) + sizeof(uint8), sizeof(uint8),
}, sizeof(uint8) + sizeof(uint32) + Crypto::Hash::SHA1::Digest::size() + sizeof(uint32) + sizeof(uint8), sizeof(uint8),
"Warden Memory Read check",
ScanFlags::None, 0, UINT16_MAX));

Expand Down

0 comments on commit be39b70

Please sign in to comment.