VS 2019 compilation ok
This commit is contained in:
parent
2f02d74d26
commit
d130815f0d
@ -2,6 +2,7 @@
|
||||
#include <Utility/InitFunction.h>
|
||||
#include "Global.h"
|
||||
#include "Utility/GameDetect.h"
|
||||
#include "Utility/Hooking.Patterns.h"
|
||||
|
||||
#pragma optimize("", off)
|
||||
void *__cdecl memcpy_0(void *a1, const void *a2, size_t a3)
|
||||
@ -190,9 +191,51 @@ void init_windowHooks(windowHooks* data)
|
||||
}
|
||||
/* END WINDOW HOOKS */
|
||||
|
||||
void InitializeHints()
|
||||
{
|
||||
static std::string modPath;
|
||||
|
||||
if (modPath.empty())
|
||||
{
|
||||
char exeName[512];
|
||||
GetModuleFileNameA(GetModuleHandle(NULL), exeName, sizeof(exeName));
|
||||
|
||||
char* exeBaseName = strrchr(exeName, '\\');
|
||||
exeBaseName[0] = L'\0';
|
||||
|
||||
modPath = exeName;
|
||||
modPath += "\\";
|
||||
|
||||
GetFullPathNameA(modPath.c_str(), sizeof(exeName), exeName, nullptr);
|
||||
|
||||
modPath = exeName;
|
||||
modPath += "\\";
|
||||
}
|
||||
|
||||
std::string hintsFile = modPath + "hints.dat";
|
||||
FILE* hints = fopen(hintsFile.c_str(), "rb");
|
||||
|
||||
if (hints)
|
||||
{
|
||||
while (!feof(hints))
|
||||
{
|
||||
uint64_t hash;
|
||||
uintptr_t hint;
|
||||
|
||||
fread(&hash, 1, sizeof(hash), hints);
|
||||
fread(&hint, 1, sizeof(hint), hints);
|
||||
|
||||
hook::pattern::hint(hash, hint);
|
||||
}
|
||||
|
||||
fclose(hints);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static InitFunction globalFunc([]()
|
||||
{
|
||||
InitializeHints();
|
||||
CreateThread(NULL, 0, QuitGameThread, NULL, 0, NULL);
|
||||
}, GameID::Global);
|
||||
#pragma optimize("", on)
|
@ -5,37 +5,77 @@
|
||||
* regarding licensing.
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
#include "Hooking.Patterns.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <windows.h>
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
#if PATTERNS_USE_HINTS
|
||||
#include <map>
|
||||
|
||||
static void Citizen_PatternSaveHint(uint64_t hash, uintptr_t hint)
|
||||
{
|
||||
static std::string modPath;
|
||||
|
||||
if (modPath.empty())
|
||||
{
|
||||
char exeName[512];
|
||||
GetModuleFileNameA(GetModuleHandle(NULL), exeName, sizeof(exeName));
|
||||
|
||||
char* exeBaseName = strrchr(exeName, '\\');
|
||||
exeBaseName[0] = L'\0';
|
||||
|
||||
modPath = exeName;
|
||||
modPath += "\\";
|
||||
|
||||
GetFullPathNameA(modPath.c_str(), sizeof(exeName), exeName, nullptr);
|
||||
|
||||
modPath = exeName;
|
||||
modPath += "\\";
|
||||
}
|
||||
|
||||
std::string hintsFile = modPath + "hints.dat";
|
||||
|
||||
FILE* hints = fopen(hintsFile.c_str(), "ab");
|
||||
|
||||
if (hints)
|
||||
{
|
||||
fwrite(&hash, 1, sizeof(hash), hints);
|
||||
fwrite(&hint, 1, sizeof(hint), hints);
|
||||
|
||||
fclose(hints);
|
||||
}
|
||||
}
|
||||
#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()(const char *text) const
|
||||
{
|
||||
std::uint64_t hash = OffsetBasis;
|
||||
std::uint64_t operator()(std::string_view text) const
|
||||
{
|
||||
std::uint64_t hash = OffsetBasis;
|
||||
for (auto it : text)
|
||||
{
|
||||
hash *= FnvPrime;
|
||||
hash ^= it;
|
||||
}
|
||||
|
||||
while (*text != 0)
|
||||
{
|
||||
hash *= FnvPrime;
|
||||
hash ^= *(uint8_t *)text;
|
||||
|
||||
++text;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
const std::uint64_t fnv_prime = 1099511628211u;
|
||||
@ -47,304 +87,275 @@ typedef basic_fnv_1<fnv_prime, fnv_offset_basis> fnv_1;
|
||||
|
||||
namespace hook
|
||||
{
|
||||
ptrdiff_t baseAddressDifference;
|
||||
inline std::multimap<uint64_t, uintptr_t>& GetHints()
|
||||
{
|
||||
static std::multimap<uint64_t, uintptr_t> hints;
|
||||
return hints;
|
||||
}
|
||||
|
||||
// sets the base to the process main base
|
||||
void set_base()
|
||||
{
|
||||
set_base((uintptr_t)GetModuleHandle(nullptr));
|
||||
}
|
||||
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.SizeOfImage;
|
||||
}
|
||||
|
||||
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
|
||||
static std::multimap<uint64_t, uintptr_t> g_hints;
|
||||
m_hash = fnv_1()(std::string_view(pattern, length));
|
||||
#endif
|
||||
|
||||
static void TransformPattern(const char *pattern, std::vector<uint8_t>& data, std::vector<uint8_t>& mask)
|
||||
{
|
||||
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');
|
||||
};
|
||||
// transform the base pattern from IDA format to canonical format
|
||||
TransformPattern(std::string_view(pattern, length), m_bytes, m_mask);
|
||||
|
||||
auto is_digit = [](char ch) -> bool
|
||||
{
|
||||
return (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f') || (ch >= '0' && ch <= '9');
|
||||
};
|
||||
|
||||
char temp_string[2]{ 0, 0 };
|
||||
|
||||
data.clear();
|
||||
mask.clear();
|
||||
|
||||
if (!pattern)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const char *patit = pattern;
|
||||
const char *patend = (pattern + strlen(pattern) + 1);
|
||||
|
||||
while (patit != patend)
|
||||
{
|
||||
char ch = *patit;
|
||||
|
||||
if (ch == ' ' || ch == 0)
|
||||
{
|
||||
if (!temp_string[0] && !temp_string[1]) //Continous delimiter
|
||||
{
|
||||
|
||||
}
|
||||
else if (temp_string[0] == '?' && (temp_string[1] == '?' || temp_string[1] == 0)) //??
|
||||
{
|
||||
data.push_back(0);
|
||||
mask.push_back(0u);
|
||||
}
|
||||
else if (temp_string[0] == '?' && is_digit(temp_string[1])) //?x
|
||||
{
|
||||
data.push_back(tol(temp_string[1]));
|
||||
mask.push_back(0x0Fu);
|
||||
}
|
||||
else if (temp_string[1] == '?' && is_digit(temp_string[0])) //x?
|
||||
{
|
||||
data.push_back(tol(temp_string[0]) << 4);
|
||||
mask.push_back(0xF0u);
|
||||
}
|
||||
else if (is_digit(temp_string[0]) && is_digit(temp_string[1])) //xx
|
||||
{
|
||||
data.push_back((tol(temp_string[0]) << 4) | tol(temp_string[1]));
|
||||
mask.push_back(0xFFu);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.clear();
|
||||
mask.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
temp_string[0] = 0;
|
||||
temp_string[1] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (temp_string[0] == 0)
|
||||
{
|
||||
temp_string[0] = ch;
|
||||
}
|
||||
else if (temp_string[1] == 0)
|
||||
{
|
||||
temp_string[1] = ch;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.clear();
|
||||
mask.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
++patit;
|
||||
}
|
||||
}
|
||||
|
||||
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), m_end(0)
|
||||
{
|
||||
static auto getSection = [](const PIMAGE_NT_HEADERS nt_headers, unsigned section) -> PIMAGE_SECTION_HEADER
|
||||
{
|
||||
return reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
||||
(UCHAR*)nt_headers->OptionalHeader.DataDirectory +
|
||||
nt_headers->OptionalHeader.NumberOfRvaAndSizes * sizeof(IMAGE_DATA_DIRECTORY) +
|
||||
section * sizeof(IMAGE_SECTION_HEADER));
|
||||
};
|
||||
|
||||
PIMAGE_DOS_HEADER dosHeader = getRVA<IMAGE_DOS_HEADER>(0);
|
||||
PIMAGE_NT_HEADERS ntHeader = getRVA<IMAGE_NT_HEADERS>(dosHeader->e_lfanew);
|
||||
|
||||
for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++)
|
||||
{
|
||||
auto sec = getSection(ntHeader, i);
|
||||
auto secSize = sec->SizeOfRawData != 0 ? sec->SizeOfRawData : sec->Misc.VirtualSize;
|
||||
//if (sec->Characteristics & IMAGE_SCN_MEM_EXECUTE)
|
||||
m_end = m_begin + sec->VirtualAddress + secSize;
|
||||
|
||||
if ((i == ntHeader->FileHeader.NumberOfSections - 1) && m_end == 0)
|
||||
m_end = m_begin + sec->PointerToRawData + secSize;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// get the hash for the base pattern
|
||||
#if PATTERNS_USE_HINTS
|
||||
m_hash = fnv_1()(pattern);
|
||||
// if there's hints, try those first
|
||||
if (m_module == GetModuleHandle(nullptr))
|
||||
{
|
||||
auto range = GetHints().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
|
||||
}
|
||||
|
||||
// transform the base pattern from IDA format to canonical format
|
||||
TransformPattern(pattern, m_bytes, m_mask);
|
||||
void pattern::EnsureMatches(uint32_t maxCount)
|
||||
{
|
||||
if (m_matched)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_size = m_mask.size();
|
||||
// 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
|
||||
// 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;
|
||||
|
||||
if (!m_rangeStart && !m_rangeEnd && !m_module)
|
||||
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);
|
||||
GetHints().emplace(m_hash, address);
|
||||
Citizen_PatternSaveHint(m_hash, address);
|
||||
#else
|
||||
(void)address;
|
||||
(void)address;
|
||||
#endif
|
||||
|
||||
return (m_matches.size() == maxCount);
|
||||
};
|
||||
return (m_matches.size() == maxCount);
|
||||
};
|
||||
|
||||
ptrdiff_t BadCharacter[256];
|
||||
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('?');
|
||||
|
||||
std::ptrdiff_t index;
|
||||
ptrdiff_t Last[256];
|
||||
|
||||
const std::uint8_t *pbytes = m_bytes.data();
|
||||
const std::uint8_t *pmask = m_mask.data();
|
||||
std::fill(std::begin(Last), std::end(Last), lastWild == std::string::npos ? -1 : static_cast<ptrdiff_t>(lastWild));
|
||||
|
||||
for (std::uint32_t bc = 0; bc < 256; ++bc)
|
||||
{
|
||||
for (index = m_size - 1; index >= 0; --index)
|
||||
{
|
||||
if ((pbytes[index] & pmask[index]) == (bc & pmask[index]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (ptrdiff_t i = 0; i < static_cast<ptrdiff_t>(maskSize); ++i)
|
||||
{
|
||||
if (Last[pattern[i]] < i)
|
||||
{
|
||||
Last[pattern[i]] = i;
|
||||
}
|
||||
}
|
||||
|
||||
BadCharacter[bc] = index;
|
||||
}
|
||||
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;
|
||||
|
||||
__try
|
||||
{
|
||||
for (uintptr_t i = executable.begin(), end = executable.end() - m_size; i <= end;)
|
||||
{
|
||||
uint8_t* ptr = reinterpret_cast<uint8_t*>(i);
|
||||
while ((j >= 0) && (mask[j] == '?' || pattern[j] == ptr[j])) j--;
|
||||
|
||||
for (index = m_size - 1; index >= 0; --index)
|
||||
{
|
||||
if ((pbytes[index] & pmask[index]) != (ptr[index] & pmask[index]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j < 0)
|
||||
{
|
||||
m_matches.emplace_back(ptr);
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
m_matches.emplace_back(ptr);
|
||||
if (matchSuccess(i))
|
||||
{
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
else i += std::max((ptrdiff_t)1, j - Last[ptr[j]]);
|
||||
}
|
||||
|
||||
if (matchSuccess(i))
|
||||
{
|
||||
break;
|
||||
}
|
||||
m_matched = true;
|
||||
}
|
||||
|
||||
i += m_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
i += max(index - BadCharacter[ptr[index]], 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
__except ((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
|
||||
{ }
|
||||
m_matched = true;
|
||||
}
|
||||
bool pattern::ConsiderMatch(uintptr_t offset)
|
||||
{
|
||||
const char* pattern = m_bytes.c_str();
|
||||
const char* mask = m_mask.c_str();
|
||||
|
||||
bool pattern::ConsiderMatch(uintptr_t offset)
|
||||
{
|
||||
const uint8_t* pattern = m_bytes.data();
|
||||
const uint8_t* mask = m_mask.data();
|
||||
char* ptr = reinterpret_cast<char*>(offset);
|
||||
|
||||
char* ptr = reinterpret_cast<char*>(offset);
|
||||
for (size_t i = 0, j = m_mask.size(); i < j; i++)
|
||||
{
|
||||
if (mask[i] == '?')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < m_size; i++)
|
||||
{
|
||||
if ((pattern[i] & mask[i]) != (ptr[i] & mask[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (pattern[i] != ptr[i])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_matches.emplace_back(ptr);
|
||||
m_matches.emplace_back(ptr);
|
||||
|
||||
return true;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
void InitializeHints()
|
||||
{
|
||||
static std::string modPath;
|
||||
|
||||
g_hints.emplace(hash, address);
|
||||
}
|
||||
if (modPath.empty())
|
||||
{
|
||||
char exeName[512];
|
||||
GetModuleFileNameA(GetModuleHandle(NULL), exeName, sizeof(exeName));
|
||||
|
||||
char* exeBaseName = strrchr(exeName, '\\');
|
||||
exeBaseName[0] = L'\0';
|
||||
|
||||
modPath = exeName;
|
||||
modPath += "\\";
|
||||
|
||||
GetFullPathNameA(modPath.c_str(), sizeof(exeName), exeName, nullptr);
|
||||
|
||||
modPath = exeName;
|
||||
modPath += "\\";
|
||||
}
|
||||
|
||||
std::string hintsFile = modPath + "hints.dat";
|
||||
FILE* hints = fopen(hintsFile.c_str(), "rb");
|
||||
|
||||
if (hints)
|
||||
{
|
||||
while (!feof(hints))
|
||||
{
|
||||
uint64_t hash;
|
||||
uintptr_t hint;
|
||||
|
||||
fread(&hash, 1, sizeof(hash), hints);
|
||||
fread(&hint, 1, sizeof(hint), hints);
|
||||
|
||||
hook::pattern::hint(hash, hint);
|
||||
}
|
||||
|
||||
fclose(hints);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void pattern::hint(uint64_t hash, uintptr_t address)
|
||||
{
|
||||
auto range = GetHints().equal_range(hash);
|
||||
|
||||
for (auto it = range.first; it != range.second; it++)
|
||||
{
|
||||
if (it->second == address)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GetHints().emplace(hash, address);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -1,242 +1,201 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define PATTERNS_USE_HINTS 0
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4201)
|
||||
|
||||
#define PATTERNS_USE_HINTS 1
|
||||
|
||||
namespace hook
|
||||
{
|
||||
extern ptrdiff_t baseAddressDifference;
|
||||
class pattern_match
|
||||
{
|
||||
private:
|
||||
void* m_pointer;
|
||||
|
||||
// sets the base address difference based on an obtained pointer
|
||||
inline void set_base(uintptr_t address)
|
||||
{
|
||||
#ifdef _M_IX86
|
||||
uintptr_t addressDiff = (address - 0x400000);
|
||||
#elif defined(_M_AMD64)
|
||||
uintptr_t addressDiff = (address - 0x140000000);
|
||||
#endif
|
||||
public:
|
||||
inline pattern_match(void* pointer)
|
||||
: m_pointer(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
// pointer-style cast to ensure unsigned overflow ends up copied directly into a signed value
|
||||
baseAddressDifference = *(ptrdiff_t*)&addressDiff;
|
||||
}
|
||||
template<typename T>
|
||||
T* get(ptrdiff_t offset = 0) const
|
||||
{
|
||||
char* ptr = reinterpret_cast<char*>(m_pointer);
|
||||
return reinterpret_cast<T*>(ptr + offset);
|
||||
}
|
||||
};
|
||||
|
||||
// sets the base to the process main base
|
||||
void set_base();
|
||||
|
||||
template<typename T>
|
||||
inline T* getRVA(uintptr_t rva)
|
||||
{
|
||||
set_base();
|
||||
#ifdef _M_IX86
|
||||
return (T*)(baseAddressDifference + 0x400000 + rva);
|
||||
#elif defined(_M_AMD64)
|
||||
return (T*)(baseAddressDifference + 0x140000000 + rva);
|
||||
#endif
|
||||
}
|
||||
|
||||
class pattern_match
|
||||
{
|
||||
private:
|
||||
void* m_pointer;
|
||||
|
||||
public:
|
||||
inline pattern_match(void* pointer)
|
||||
: m_pointer(pointer)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* get(ptrdiff_t offset = 0) const
|
||||
{
|
||||
char* ptr = reinterpret_cast<char*>(m_pointer);
|
||||
return reinterpret_cast<T*>(ptr + offset);
|
||||
}
|
||||
};
|
||||
|
||||
class pattern
|
||||
{
|
||||
private:
|
||||
std::vector<uint8_t> m_bytes;
|
||||
std::vector<uint8_t> m_mask;
|
||||
class pattern
|
||||
{
|
||||
private:
|
||||
std::string m_bytes;
|
||||
std::string m_mask;
|
||||
|
||||
#if PATTERNS_USE_HINTS
|
||||
uint64_t m_hash;
|
||||
uint64_t m_hash;
|
||||
#endif
|
||||
|
||||
size_t m_size;
|
||||
std::vector<pattern_match> m_matches;
|
||||
|
||||
std::vector<pattern_match> m_matches;
|
||||
bool m_matched;
|
||||
|
||||
bool m_matched;
|
||||
union
|
||||
{
|
||||
void* m_module;
|
||||
struct
|
||||
{
|
||||
uintptr_t m_rangeStart;
|
||||
uintptr_t m_rangeEnd;
|
||||
};
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
void* m_module;
|
||||
struct
|
||||
{
|
||||
uintptr_t m_rangeStart;
|
||||
uintptr_t m_rangeEnd;
|
||||
};
|
||||
};
|
||||
protected:
|
||||
inline pattern(void* module)
|
||||
: m_rangeStart((uintptr_t)module), m_matched(false), m_rangeEnd(0)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
inline pattern(void* module)
|
||||
: m_module(module), m_rangeEnd(0), m_matched(false)
|
||||
{
|
||||
}
|
||||
inline pattern(uintptr_t begin, uintptr_t end)
|
||||
: m_rangeStart(begin), m_rangeEnd(end), m_matched(false)
|
||||
{
|
||||
}
|
||||
|
||||
inline pattern(uintptr_t begin, uintptr_t end)
|
||||
: m_rangeStart(begin), m_rangeEnd(end), m_matched(false)
|
||||
{
|
||||
}
|
||||
void Initialize(const char* pattern, size_t length);
|
||||
|
||||
void Initialize(const char* pattern);
|
||||
private:
|
||||
bool ConsiderMatch(uintptr_t offset);
|
||||
|
||||
private:
|
||||
bool ConsiderMatch(uintptr_t offset);
|
||||
void EnsureMatches(uint32_t maxCount);
|
||||
|
||||
void EnsureMatches(uint32_t maxCount);
|
||||
inline pattern_match _get_internal(size_t index) const
|
||||
{
|
||||
return m_matches[index];
|
||||
}
|
||||
|
||||
inline const pattern_match& _get_internal(size_t index)
|
||||
{
|
||||
return m_matches[index];
|
||||
}
|
||||
public:
|
||||
template<size_t Len>
|
||||
pattern(const char(&pattern)[Len])
|
||||
: pattern(GetModuleHandle(NULL))
|
||||
{
|
||||
Initialize(pattern, Len);
|
||||
}
|
||||
|
||||
public:
|
||||
pattern()
|
||||
: m_matched(true)
|
||||
{
|
||||
}
|
||||
inline pattern& count(uint32_t expected) &
|
||||
{
|
||||
EnsureMatches(expected);
|
||||
assert(m_matches.size() == expected);
|
||||
return *this;
|
||||
}
|
||||
|
||||
pattern(const char *pattern)
|
||||
: pattern(getRVA<void>(0))
|
||||
{
|
||||
Initialize(pattern);
|
||||
}
|
||||
inline pattern& count_hint(uint32_t expected) &
|
||||
{
|
||||
EnsureMatches(expected);
|
||||
return *this;
|
||||
}
|
||||
|
||||
pattern(std::string& pattern)
|
||||
: pattern(getRVA<void>(0))
|
||||
{
|
||||
Initialize(pattern.c_str());
|
||||
}
|
||||
inline pattern& clear() &
|
||||
{
|
||||
m_matches.clear();
|
||||
m_matched = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline pattern& count(uint32_t expected)
|
||||
{
|
||||
EnsureMatches(expected);
|
||||
assert(m_matches.size() == expected);
|
||||
return *this;
|
||||
}
|
||||
inline pattern&& count(uint32_t expected) &&
|
||||
{
|
||||
EnsureMatches(expected);
|
||||
assert(m_matches.size() == expected);
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
inline pattern& count_hint(uint32_t expected)
|
||||
{
|
||||
EnsureMatches(expected);
|
||||
return *this;
|
||||
}
|
||||
inline pattern&& count_hint(uint32_t expected) &&
|
||||
{
|
||||
EnsureMatches(expected);
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
inline pattern& clear(void* module = nullptr)
|
||||
{
|
||||
if (module)
|
||||
m_module = module;
|
||||
m_matches.clear();
|
||||
m_matched = false;
|
||||
return *this;
|
||||
}
|
||||
inline pattern&& clear() &&
|
||||
{
|
||||
m_matches.clear();
|
||||
m_matched = false;
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
inline size_t size()
|
||||
{
|
||||
EnsureMatches(UINT32_MAX);
|
||||
return m_matches.size();
|
||||
}
|
||||
inline size_t size()
|
||||
{
|
||||
EnsureMatches(UINT32_MAX);
|
||||
return m_matches.size();
|
||||
}
|
||||
|
||||
inline bool empty()
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
inline bool empty()
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
inline const pattern_match& get(size_t index)
|
||||
{
|
||||
EnsureMatches(UINT32_MAX);
|
||||
return _get_internal(index);
|
||||
}
|
||||
inline pattern_match get(size_t index)
|
||||
{
|
||||
EnsureMatches(UINT32_MAX);
|
||||
return _get_internal(index);
|
||||
}
|
||||
|
||||
inline const pattern_match& get_one()
|
||||
{
|
||||
return count(1)._get_internal(0);
|
||||
}
|
||||
inline pattern_match get_one()
|
||||
{
|
||||
return std::forward<pattern>(*this).count(1)._get_internal(0);
|
||||
}
|
||||
|
||||
template<typename T = void>
|
||||
inline auto get_first(ptrdiff_t offset = 0)
|
||||
{
|
||||
return get_one().get<T>(offset);
|
||||
}
|
||||
template<typename T = void>
|
||||
inline auto get_first(ptrdiff_t offset = 0)
|
||||
{
|
||||
return get_one().get<T>(offset);
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
pattern &for_each_result(Fn Pr)
|
||||
{
|
||||
EnsureMatches(UINT32_MAX);
|
||||
|
||||
for (auto &result : this->m_matches)
|
||||
{
|
||||
Pr(result);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
public:
|
||||
#if PATTERNS_USE_HINTS
|
||||
// define a hint
|
||||
static void hint(uint64_t hash, uintptr_t address);
|
||||
// define a hint
|
||||
static void hint(uint64_t hash, uintptr_t address);
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
class module_pattern
|
||||
: public pattern
|
||||
{
|
||||
public:
|
||||
module_pattern(void* module, const char *pattern)
|
||||
: pattern(module)
|
||||
{
|
||||
Initialize(pattern);
|
||||
}
|
||||
class module_pattern
|
||||
: public pattern
|
||||
{
|
||||
public:
|
||||
template<size_t Len>
|
||||
module_pattern(void* module, const char(&pattern)[Len])
|
||||
: pattern(module)
|
||||
{
|
||||
Initialize(pattern, Len);
|
||||
}
|
||||
};
|
||||
|
||||
module_pattern(void* module, std::string& pattern)
|
||||
: pattern(module)
|
||||
{
|
||||
Initialize(pattern.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
class range_pattern
|
||||
: public pattern
|
||||
{
|
||||
public:
|
||||
range_pattern(uintptr_t begin, uintptr_t end, const char *pattern)
|
||||
: pattern(begin, end)
|
||||
{
|
||||
Initialize(pattern);
|
||||
}
|
||||
|
||||
range_pattern(uintptr_t begin, uintptr_t end, std::string& pattern)
|
||||
: pattern(begin, end)
|
||||
{
|
||||
Initialize(pattern.c_str());
|
||||
}
|
||||
};
|
||||
class range_pattern
|
||||
: public pattern
|
||||
{
|
||||
public:
|
||||
template<size_t Len>
|
||||
range_pattern(uintptr_t begin, uintptr_t end, const char(&pattern)[Len])
|
||||
: pattern(begin, end)
|
||||
{
|
||||
Initialize(pattern, Len);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename T = void>
|
||||
auto get_pattern(const char *pattern_string, ptrdiff_t offset = 0)
|
||||
{
|
||||
return pattern(pattern_string).get_first<T>(offset);
|
||||
}
|
||||
template<typename T = void, size_t Len>
|
||||
auto get_pattern(const char(&pattern_string)[Len], ptrdiff_t offset = 0)
|
||||
{
|
||||
return pattern(pattern_string).get_first<T>(offset);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning(pop)
|
||||
|
@ -23,8 +23,8 @@ project "iDmacDrv"
|
||||
|
||||
filter "platforms:x86"
|
||||
targetsuffix "32"
|
||||
linkoptions { "/DEF:iDmacDrv/src/Source32.def" }
|
||||
linkoptions { "/DEF:$(ProjectDir)/src/Source32.def" }
|
||||
|
||||
filter "platforms:x64"
|
||||
targetsuffix "64"
|
||||
linkoptions { "/DEF:iDmacDrv/src/Source64.def" }
|
||||
linkoptions { "/DEF:$(ProjectDir)/src/Source64.def" }
|
@ -1,2 +1 @@
|
||||
premake5 vs2017
|
||||
pause
|
||||
premake5 vs2019
|
BIN
premake5.exe
BIN
premake5.exe
Binary file not shown.
@ -4,7 +4,7 @@ workspace "OpenParrot"
|
||||
|
||||
flags { "StaticRuntime", "No64BitChecks" }
|
||||
|
||||
systemversion "10.0.16299.0"
|
||||
systemversion "latest"
|
||||
|
||||
symbols "On"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user