Improved pattern scanner (#470)

This commit is contained in:
Forever Gone 2022-10-15 19:50:28 -04:00 committed by GitHub
parent f75ee5cbf8
commit dc60780e27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 131 additions and 76 deletions

View File

@ -95,6 +95,10 @@ namespace big
FreeConsole();
}
void file_out(std::stringstream& str)
{
m_file_out << str.str() << '\n';
}
private:
void create_backup()
{

View File

@ -20,7 +20,10 @@ namespace memory
if (entry.m_callback)
{
std::invoke(std::move(entry.m_callback), result);
LOG(INFO) << "Found '" << entry.m_name << "' GTA5.exe+" << HEX_TO_UPPER(result.as<DWORD64>() - region.begin().as<DWORD64>());
std::stringstream file_out{}; //I hate this, but not logging it except on fail really helps on speed.
file_out << "Found '" << entry.m_name << "' GTA5.exe+" << HEX_TO_UPPER(result.as<DWORD64>() - region.begin().as<DWORD64>());
big::g_log->file_out(file_out);
file_out.clear();
}
else
{

View File

@ -3,59 +3,66 @@
namespace memory
{
std::optional<std::uint8_t> to_hex(char const c)
{
switch (c)
{
case '0':
return static_cast<std::uint8_t>(0x0);
case '1':
return static_cast<std::uint8_t>(0x1);
case '2':
return static_cast<std::uint8_t>(0x2);
case '3':
return static_cast<std::uint8_t>(0x3);
case '4':
return static_cast<std::uint8_t>(0x4);
case '5':
return static_cast<std::uint8_t>(0x5);
case '6':
return static_cast<std::uint8_t>(0x6);
case '7':
return static_cast<std::uint8_t>(0x7);
case '8':
return static_cast<std::uint8_t>(0x8);
case '9':
return static_cast<std::uint8_t>(0x9);
case 'a':
return static_cast<std::uint8_t>(0xa);
case 'b':
return static_cast<std::uint8_t>(0xb);
case 'c':
return static_cast<std::uint8_t>(0xc);
case 'd':
return static_cast<std::uint8_t>(0xd);
case 'e':
return static_cast<std::uint8_t>(0xe);
case 'f':
return static_cast<std::uint8_t>(0xf);
case 'A':
return static_cast<std::uint8_t>(0xA);
case 'B':
return static_cast<std::uint8_t>(0xB);
case 'C':
return static_cast<std::uint8_t>(0xC);
case 'D':
return static_cast<std::uint8_t>(0xD);
case 'E':
return static_cast<std::uint8_t>(0xE);
case 'F':
return static_cast<std::uint8_t>(0xF);
default:
return std::nullopt;
}
}
pattern::pattern(std::string_view ida_sig)
{
auto to_upper = [](char c) -> char
{
return c >= 'a' && c <= 'z' ? static_cast<char>(c + ('A' - 'a')) : static_cast<char>(c);
};
auto to_hex = [&](char c) -> std::optional<std::uint8_t>
{
switch (to_upper(c))
{
case '0':
return static_cast<std::uint8_t>(0);
case '1':
return static_cast<std::uint8_t>(1);
case '2':
return static_cast<std::uint8_t>(2);
case '3':
return static_cast<std::uint8_t>(3);
case '4':
return static_cast<std::uint8_t>(4);
case '5':
return static_cast<std::uint8_t>(5);
case '6':
return static_cast<std::uint8_t>(6);
case '7':
return static_cast<std::uint8_t>(7);
case '8':
return static_cast<std::uint8_t>(8);
case '9':
return static_cast<std::uint8_t>(9);
case 'A':
return static_cast<std::uint8_t>(10);
case 'B':
return static_cast<std::uint8_t>(11);
case 'C':
return static_cast<std::uint8_t>(12);
case 'D':
return static_cast<std::uint8_t>(13);
case 'E':
return static_cast<std::uint8_t>(14);
case 'F':
return static_cast<std::uint8_t>(15);
default:
return std::nullopt;
}
};
for (std::size_t i = 0; i < ida_sig.size(); ++i)
const auto size = ida_sig.size();
for (std::size_t i{}; i != size; ++i)
{
if (ida_sig[i] == ' ')
continue;
bool last = (i == ida_sig.size() - 1);
if (ida_sig[i] != '?')
{
@ -63,7 +70,6 @@ namespace memory
{
auto c1 = to_hex(ida_sig[i]);
auto c2 = to_hex(ida_sig[i + 1]);
if (c1 && c2)
{
m_bytes.emplace_back(static_cast<std::uint8_t>((*c1 * 0x10) + *c2));
@ -72,14 +78,15 @@ namespace memory
}
else
{
m_bytes.push_back(std::nullopt);
m_bytes.push_back({});
}
}
}
pattern::pattern(const void *bytes, std::string_view mask)
{
for (std::size_t i = 0; i < mask.size(); ++i)
const auto size = mask.size();
for (std::size_t i{}; i != size; ++i)
{
if (mask[i] != '?')
m_bytes.emplace_back(static_cast<const std::uint8_t*>(bytes)[i]);

View File

@ -29,39 +29,90 @@ namespace memory
return h.as<std::uintptr_t>() >= begin().as<std::uintptr_t>() && h.as<std::uintptr_t>() <= end().as<std::uintptr_t>();
}
static bool pattern_matches(std::uint8_t* target, const std::optional<std::uint8_t>* sig, std::size_t length)
// https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm
// https://www.youtube.com/watch?v=AuZUeshhy-s
handle scan_pattern(const std::optional<std::uint8_t>* sig, std::size_t length, handle begin, std::size_t module_size)
{
for (std::size_t i = 0; i < length; ++i)
std::size_t maxShift = length;
std::size_t max_idx = length - 1;
//Get wildcard index, and store max shiftable byte count
std::size_t wild_card_idx{ static_cast<size_t>(-1) };
for (int i{ static_cast<int>(max_idx - 1) }; i >= 0; --i)
{
if (sig[i] && *sig[i] != target[i])
return false;
if (!sig[i])
{
maxShift = max_idx - i;
wild_card_idx = i;
break;
}
}
return true;
};
//Store max shiftable bytes for non wildcards.
std::size_t shift_table[UINT8_MAX + 1]{};
for (std::size_t i{}; i <= UINT8_MAX; ++i)
{
shift_table[i] = maxShift;
}
//Fill shift table with sig bytes
for (std::size_t i{ wild_card_idx + 1 }; i != max_idx; ++i)
{
shift_table[*sig[i]] = max_idx - i;
}
//Loop data
for (std::size_t current_idx{}; current_idx != module_size - length;)
{
for (std::size_t sig_idx{ max_idx }; sig_idx >= 0; --sig_idx)
{
if (sig[sig_idx] && *begin.add(current_idx + sig_idx).as<uint8_t*>() != *sig[sig_idx])
{
current_idx += shift_table[*begin.add(current_idx + max_idx).as<uint8_t*>()];
break;
}
else if (sig_idx == NULL)
{
return begin.add(sig_idx);
}
}
}
return nullptr;
}
handle range::scan(pattern const &sig)
{
auto data = sig.m_bytes.data();
auto length = sig.m_bytes.size();
for (std::uintptr_t i = 0; i < m_size - length; ++i)
if (auto result = scan_pattern(data, length, m_base, m_size); result)
{
if (pattern_matches(m_base.add(i).as<std::uint8_t*>(), data, length))
{
return m_base.add(i);
}
return result;
}
return nullptr;
}
bool pattern_matches(std::uint8_t* target, const std::optional<std::uint8_t>* sig, std::size_t length)
{
for (std::size_t i{}; i != length; ++i)
{
if (sig[i] && *sig[i] != target[i])
{
return false;
}
}
return true;
}
std::vector<handle> range::scan_all(pattern const &sig)
{
std::vector<handle> result;
std::vector<handle> result{};
auto data = sig.m_bytes.data();
auto length = sig.m_bytes.size();
for (std::uintptr_t i = 0; i < m_size - length; ++i)
const auto search_end = m_size - length;
for (std::uintptr_t i{}; i != search_end; ++i)
{
if (pattern_matches(m_base.add(i).as<std::uint8_t*>(), data, length))
{

View File

@ -30,8 +30,6 @@ namespace big
main_batch.add("PF", "48 8B 05 ? ? ? ? 48 8B 48 08 48 85 C9 74 52 8B 81", [this](memory::handle ptr)
{
m_ped_factory = ptr.add(3).rip().as<CPedFactory**>();
LOG(G3LOG_DEBUG) << "CPedFactory => [" << HEX_TO_UPPER(m_ped_factory) << "]";
});
// Network Player Manager
@ -70,8 +68,6 @@ namespace big
main_batch.add("SG", "48 8D 15 ? ? ? ? 4C 8B C0 E8 ? ? ? ? 48 85 FF 48 89 1D", [this](memory::handle ptr)
{
m_script_globals = ptr.add(3).rip().as<std::int64_t**>();
LOG(G3LOG_DEBUG) << "ScriptGlobals => [" << HEX_TO_UPPER(m_script_globals) << "]";
});
// Game Script Handle Manager
@ -217,8 +213,6 @@ namespace big
main_batch.add("RI", "0F B7 44 24 ? 66 89 44 4E", [this](memory::handle ptr)
{
m_replay_interface = ptr.add(0x1F).rip().as<rage::CReplayInterface**>();
LOG(G3LOG_DEBUG) << "rage::CReplayInterface => [" << HEX_TO_UPPER(m_replay_interface) << "]";
});
// Pointer to Handle
@ -261,8 +255,6 @@ namespace big
main_batch.add("FR", "3B 0D ? ? ? ? 73 17", [this](memory::handle ptr)
{
m_friend_registry = ptr.add(2).rip().as<FriendRegistry*>();
LOG(G3LOG_DEBUG) << "FriendRegistry => [" << HEX_TO_UPPER(m_friend_registry) << "]";
});
// GET_SCREEN_COORDS_FROM_WORLD_COORDS
@ -326,8 +318,6 @@ namespace big
main_batch.add("MHT", "4C 03 05 ? ? ? ? EB 03", [this](memory::handle ptr)
{
m_model_table = ptr.add(3).rip().as<HashTable<CBaseModelInfo*>*>();
LOG(G3LOG_DEBUG) << "HashTable => [" << HEX_TO_UPPER(m_model_table) << "]";
});
// Get Label Text