mirror of
https://github.com/CookiePLMonster/SilentPatch.git
synced 2024-12-28 23:03:01 +05:00
273 lines
No EOL
5.3 KiB
C++
273 lines
No EOL
5.3 KiB
C++
/*
|
|
* This file is part of the CitizenFX project - http://citizen.re/
|
|
*
|
|
* See LICENSE and MENTIONS in the root of the source tree for information
|
|
* regarding licensing.
|
|
*/
|
|
|
|
#include "Patterns.h"
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#define NOMINMAX
|
|
#include <windows.h>
|
|
#include <algorithm>
|
|
#include <string_view>
|
|
|
|
#if PATTERNS_USE_HINTS
|
|
#include <map>
|
|
#endif
|
|
|
|
|
|
#if PATTERNS_USE_HINTS
|
|
|
|
// from boost someplace
|
|
template <std::uint64_t FnvPrime, std::uint64_t OffsetBasis>
|
|
struct basic_fnv_1
|
|
{
|
|
std::uint64_t operator()(std::string_view text) const
|
|
{
|
|
std::uint64_t hash = OffsetBasis;
|
|
for (auto it : text)
|
|
{
|
|
hash *= FnvPrime;
|
|
hash ^= it;
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
};
|
|
|
|
const std::uint64_t fnv_prime = 1099511628211u;
|
|
const std::uint64_t fnv_offset_basis = 14695981039346656037u;
|
|
|
|
typedef basic_fnv_1<fnv_prime, fnv_offset_basis> fnv_1;
|
|
|
|
#endif
|
|
|
|
namespace hook
|
|
{
|
|
ptrdiff_t baseAddressDifference;
|
|
|
|
// sets the base to the process main base
|
|
void set_base()
|
|
{
|
|
set_base((uintptr_t)GetModuleHandle(nullptr));
|
|
}
|
|
|
|
|
|
#if PATTERNS_USE_HINTS
|
|
static std::multimap<uint64_t, uintptr_t> g_hints;
|
|
#endif
|
|
|
|
static void TransformPattern(std::string_view pattern, std::string& data, std::string& mask)
|
|
{
|
|
uint8_t tempDigit = 0;
|
|
bool tempFlag = false;
|
|
|
|
auto tol = [] (char ch) -> uint8_t
|
|
{
|
|
if (ch >= 'A' && ch <= 'F') return uint8_t(ch - 'A' + 10);
|
|
if (ch >= 'a' && ch <= 'f') return uint8_t(ch - 'a' + 10);
|
|
return uint8_t(ch - '0');
|
|
};
|
|
|
|
for (auto ch : pattern)
|
|
{
|
|
if (ch == ' ')
|
|
{
|
|
continue;
|
|
}
|
|
else if (ch == '?')
|
|
{
|
|
data.push_back(0);
|
|
mask.push_back('?');
|
|
}
|
|
else if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'))
|
|
{
|
|
uint8_t thisDigit = tol(ch);
|
|
|
|
if (!tempFlag)
|
|
{
|
|
tempDigit = thisDigit << 4;
|
|
tempFlag = true;
|
|
}
|
|
else
|
|
{
|
|
tempDigit |= thisDigit;
|
|
tempFlag = false;
|
|
|
|
data.push_back(tempDigit);
|
|
mask.push_back('x');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class executable_meta
|
|
{
|
|
private:
|
|
uintptr_t m_begin;
|
|
uintptr_t m_end;
|
|
|
|
public:
|
|
template<typename TReturn, typename TOffset>
|
|
TReturn* getRVA(TOffset rva)
|
|
{
|
|
return (TReturn*)(m_begin + rva);
|
|
}
|
|
|
|
explicit executable_meta(void* module)
|
|
: m_begin((uintptr_t)module)
|
|
{
|
|
PIMAGE_DOS_HEADER dosHeader = getRVA<IMAGE_DOS_HEADER>(0);
|
|
PIMAGE_NT_HEADERS ntHeader = getRVA<IMAGE_NT_HEADERS>(dosHeader->e_lfanew);
|
|
|
|
m_end = m_begin + ntHeader->OptionalHeader.SizeOfCode;
|
|
}
|
|
|
|
executable_meta(uintptr_t begin, uintptr_t end)
|
|
: m_begin(begin), m_end(end)
|
|
{
|
|
}
|
|
|
|
inline uintptr_t begin() const { return m_begin; }
|
|
inline uintptr_t end() const { return m_end; }
|
|
};
|
|
|
|
void pattern::Initialize(const char* pattern, size_t length)
|
|
{
|
|
// get the hash for the base pattern
|
|
#if PATTERNS_USE_HINTS
|
|
m_hash = fnv_1()(std::string_view(pattern, length));
|
|
#endif
|
|
|
|
// transform the base pattern from IDA format to canonical format
|
|
TransformPattern(std::string_view(pattern, length), m_bytes, m_mask);
|
|
|
|
#if PATTERNS_USE_HINTS
|
|
// if there's hints, try those first
|
|
if (m_module == GetModuleHandle(nullptr))
|
|
{
|
|
auto range = g_hints.equal_range(m_hash);
|
|
|
|
if (range.first != range.second)
|
|
{
|
|
std::for_each(range.first, range.second, [&] (const std::pair<uint64_t, uintptr_t>& hint)
|
|
{
|
|
ConsiderMatch(hint.second);
|
|
});
|
|
|
|
// if the hints succeeded, we don't need to do anything more
|
|
if (!m_matches.empty())
|
|
{
|
|
m_matched = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void pattern::EnsureMatches(uint32_t maxCount)
|
|
{
|
|
if (m_matched)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// scan the executable for code
|
|
executable_meta executable = m_rangeStart != 0 && m_rangeEnd != 0 ? executable_meta(m_rangeStart, m_rangeEnd) : executable_meta(m_module);
|
|
|
|
auto matchSuccess = [&] (uintptr_t address)
|
|
{
|
|
#if PATTERNS_USE_HINTS
|
|
g_hints.emplace(m_hash, address);
|
|
#else
|
|
(void)address;
|
|
#endif
|
|
|
|
return (m_matches.size() == maxCount);
|
|
};
|
|
|
|
const uint8_t* pattern = reinterpret_cast<const uint8_t*>(m_bytes.c_str());
|
|
const char* mask = m_mask.c_str();
|
|
size_t maskSize = m_mask.size();
|
|
size_t lastWild = m_mask.find_last_of('?');
|
|
|
|
ptrdiff_t Last[256];
|
|
|
|
std::fill(std::begin(Last), std::end(Last), lastWild == std::string::npos ? -1 : static_cast<ptrdiff_t>(lastWild) );
|
|
|
|
for ( ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(maskSize); ++i )
|
|
{
|
|
if ( Last[ pattern[i] ] < i )
|
|
{
|
|
Last[ pattern[i] ] = i;
|
|
}
|
|
}
|
|
|
|
for (uintptr_t i = executable.begin(), end = executable.end() - maskSize; i <= end;)
|
|
{
|
|
uint8_t* ptr = reinterpret_cast<uint8_t*>(i);
|
|
ptrdiff_t j = maskSize - 1;
|
|
|
|
while((j >= 0) && (mask[j] == '?' || pattern[j] == ptr[j])) j--;
|
|
|
|
if(j < 0)
|
|
{
|
|
m_matches.emplace_back(ptr);
|
|
|
|
if (matchSuccess(i))
|
|
{
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
else i += std::max(ptrdiff_t(1), j - Last[ ptr[j] ]);
|
|
}
|
|
|
|
m_matched = true;
|
|
}
|
|
|
|
bool pattern::ConsiderMatch(uintptr_t offset)
|
|
{
|
|
const char* pattern = m_bytes.c_str();
|
|
const char* mask = m_mask.c_str();
|
|
|
|
char* ptr = reinterpret_cast<char*>(offset);
|
|
|
|
for (size_t i = 0, j = m_mask.size(); i < j; i++)
|
|
{
|
|
if (mask[i] == '?')
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (pattern[i] != ptr[i])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
m_matches.emplace_back(ptr);
|
|
|
|
return true;
|
|
}
|
|
|
|
#if PATTERNS_USE_HINTS
|
|
void pattern::hint(uint64_t hash, uintptr_t address)
|
|
{
|
|
auto range = g_hints.equal_range(hash);
|
|
|
|
for (auto it = range.first; it != range.second; it++)
|
|
{
|
|
if (it->second == address)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
g_hints.emplace(hash, address);
|
|
}
|
|
#endif
|
|
} |