Improved pattern scanner (#470)
This commit is contained in:
parent
f75ee5cbf8
commit
dc60780e27
@ -95,6 +95,10 @@ namespace big
|
||||
FreeConsole();
|
||||
}
|
||||
|
||||
void file_out(std::stringstream& str)
|
||||
{
|
||||
m_file_out << str.str() << '\n';
|
||||
}
|
||||
private:
|
||||
void create_backup()
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -3,59 +3,66 @@
|
||||
|
||||
namespace memory
|
||||
{
|
||||
pattern::pattern(std::string_view ida_sig)
|
||||
std::optional<std::uint8_t> to_hex(char const c)
|
||||
{
|
||||
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))
|
||||
switch (c)
|
||||
{
|
||||
case '0':
|
||||
return static_cast<std::uint8_t>(0);
|
||||
return static_cast<std::uint8_t>(0x0);
|
||||
case '1':
|
||||
return static_cast<std::uint8_t>(1);
|
||||
return static_cast<std::uint8_t>(0x1);
|
||||
case '2':
|
||||
return static_cast<std::uint8_t>(2);
|
||||
return static_cast<std::uint8_t>(0x2);
|
||||
case '3':
|
||||
return static_cast<std::uint8_t>(3);
|
||||
return static_cast<std::uint8_t>(0x3);
|
||||
case '4':
|
||||
return static_cast<std::uint8_t>(4);
|
||||
return static_cast<std::uint8_t>(0x4);
|
||||
case '5':
|
||||
return static_cast<std::uint8_t>(5);
|
||||
return static_cast<std::uint8_t>(0x5);
|
||||
case '6':
|
||||
return static_cast<std::uint8_t>(6);
|
||||
return static_cast<std::uint8_t>(0x6);
|
||||
case '7':
|
||||
return static_cast<std::uint8_t>(7);
|
||||
return static_cast<std::uint8_t>(0x7);
|
||||
case '8':
|
||||
return static_cast<std::uint8_t>(8);
|
||||
return static_cast<std::uint8_t>(0x8);
|
||||
case '9':
|
||||
return static_cast<std::uint8_t>(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>(10);
|
||||
return static_cast<std::uint8_t>(0xA);
|
||||
case 'B':
|
||||
return static_cast<std::uint8_t>(11);
|
||||
return static_cast<std::uint8_t>(0xB);
|
||||
case 'C':
|
||||
return static_cast<std::uint8_t>(12);
|
||||
return static_cast<std::uint8_t>(0xC);
|
||||
case 'D':
|
||||
return static_cast<std::uint8_t>(13);
|
||||
return static_cast<std::uint8_t>(0xD);
|
||||
case 'E':
|
||||
return static_cast<std::uint8_t>(14);
|
||||
return static_cast<std::uint8_t>(0xE);
|
||||
case 'F':
|
||||
return static_cast<std::uint8_t>(15);
|
||||
return static_cast<std::uint8_t>(0xF);
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < ida_sig.size(); ++i)
|
||||
pattern::pattern(std::string_view ida_sig)
|
||||
{
|
||||
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]);
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user