Skip to content

Commit

Permalink
fix memory scanning under proton/wine
Browse files Browse the repository at this point in the history
  • Loading branch information
Cloudef committed Sep 9, 2023
1 parent 9077edc commit 20b7f1a
Show file tree
Hide file tree
Showing 8 changed files with 40 additions and 173 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@
[submodule "third-party/MINT"]
path = third-party/MINT
url = https://github.com/Chuyu-Team/MINT.git
[submodule "third-party/mem"]
path = third-party/mem
url = https://github.com/0x1F9F1/mem.git
166 changes: 30 additions & 136 deletions include/modengine/util/memory_scanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,112 +10,36 @@

#include <windows.h>
#include <MINT.h>
#include <mem/pattern.h>

namespace modengine {

struct ScanPattern {
std::string pattern;
std::vector<short> wildcards;
ScanPattern() : pattern() {}
ScanPattern(const std::string &pattern) : pattern(pattern.c_str()) {}
mem::pattern pattern;
};

class MemoryScanner {
public:
struct MemoryScannerRegion {
private:
DWORD m_original_protection;
const MEMORY_BASIC_INFORMATION& m_info;

public:
MemoryScannerRegion(const MEMORY_BASIC_INFORMATION& info)
: m_info(info)
{
m_original_protection = m_info.AllocationProtect;

if (!VirtualProtect(m_info.BaseAddress, m_info.RegionSize, PAGE_EXECUTE_READWRITE, &m_original_protection)) {
throw std::runtime_error("Unable to change process memory protection flags");
}
}

~MemoryScannerRegion()
{
DWORD prot;
(void)VirtualProtect(m_info.BaseAddress, m_info.RegionSize, m_original_protection, &prot);
}

std::string_view view() const
{
return std::string_view { reinterpret_cast<char*>(m_info.BaseAddress), m_info.RegionSize };
}
};

MemoryScanner(HMODULE base)
{
const auto image = RtlImageNtHeader(base);
const auto start = reinterpret_cast<uintptr_t>(base);
const auto end = start + image->OptionalHeader.SizeOfImage;

HANDLE process = GetCurrentProcess();
MEMORY_BASIC_INFORMATION info;

for (uintptr_t next = 0; VirtualQueryEx(process, (void*)next, &info, sizeof(info)) == sizeof(info); next += info.RegionSize) {
if (info.State == MEM_COMMIT) {
info.BaseAddress = (void*)next;

if (next >= start && next + info.RegionSize <= end) {
m_memory_regions.push_back(info);
}
}
}
}

MemoryScanner()
: MemoryScanner((HMODULE)NtCurrentPeb()->ImageBaseAddress)
{
MemoryScanner(HMODULE base) {
uint64_t base64 = reinterpret_cast<uint64_t>(base);
auto const section = IMAGE_FIRST_SECTION(reinterpret_cast<PIMAGE_NT_HEADERS>(base64 + reinterpret_cast<PIMAGE_DOS_HEADER>(base64)->e_lfanew));
mem_start = reinterpret_cast<uint8_t*>(base64 + section->VirtualAddress);
mem_size = section->Misc.VirtualSize;
}

std::optional<uintptr_t> find(const std::string& pattern)
{
for (const auto region_info : m_memory_regions) {
const auto region = MemoryScannerRegion { region_info };
const auto region_view = region.view();
const auto iter = std::search(std::execution::par, region_view.begin(), region_view.end(), pattern.begin(), pattern.end());

if (iter != region_view.end()) {
return reinterpret_cast<uintptr_t>(&(*iter));
}
}

return std::nullopt;
}
MemoryScanner() : MemoryScanner(GetModuleHandleW(nullptr)) {}

std::optional<uintptr_t> find(const ScanPattern& pattern)
{
// Fairly naive scanner for now. A regex based scanner would probably be faster down the line
for (const auto region_info : m_memory_regions) {
const auto region = MemoryScannerRegion { region_info };
const auto region_view = region.view();
std::string_view pattern_view = pattern.pattern;
for (int i = 0; i < region_view.length() - pattern_view.length(); i++) {
bool found = true;
int wildcard_index = 0;
size_t wildcard_length = pattern.wildcards.size();
for (int j = 0; j < pattern_view.length(); j++) {
if (region_view[i + j] != pattern_view[j]) {
if (wildcard_index < wildcard_length && pattern.wildcards[wildcard_index] == j) {
wildcard_index++;
continue;
}
found = false;
break;
}
}
if (found) {
return reinterpret_cast<uintptr_t>(&(region_view[i]));
}
}
}

return std::nullopt;
mem::simd_scanner scanner(pattern.pattern);
std::optional<uintptr_t> result = std::nullopt;
scanner({ mem_start, mem_size }, [&](mem::pointer res) {
result = res.as<uintptr_t>();
return false;
});
return result;
}

bool replace_at(uintptr_t location, std::function<void(uintptr_t)> replace_callback)
Expand All @@ -132,56 +56,26 @@ class MemoryScanner {
return true;
}

bool replace(const std::string_view& pattern, std::function<void(uintptr_t)> replace_callback)
{
for (const auto region_info : m_memory_regions) {
const auto region = MemoryScannerRegion { region_info };
const auto region_view = region.view();
const auto iter = std::search(std::execution::par, region_view.begin(), region_view.end(), pattern.begin(), pattern.end());

if (iter != region_view.end()) {
replace_callback(reinterpret_cast<uintptr_t>(&(*iter)));

return true;
}
}

return false;
}

bool replace(const ScanPattern& pattern, std::function<void(uintptr_t)> replace_callback)
{
for (const auto region_info : m_memory_regions) {
const auto region = MemoryScannerRegion { region_info };
const auto region_view = region.view();
std::string_view pattern_view = pattern.pattern;

for (int i = 0; i < region_view.length() - pattern_view.length(); i++) {
bool found = true;
int wildcard_index = 0;
size_t wildcard_length = pattern.wildcards.size();
for (int j = 0; j < pattern_view.length(); j++) {
if (region_view[i + j] != pattern_view[j]) {
if (wildcard_index < wildcard_length && pattern.wildcards[wildcard_index] == j) {
wildcard_index++;
continue;
}
found = false;
break;
}
}
if (found) {
replace_callback(reinterpret_cast<uintptr_t>(&(region_view[i])));
return true;
}
}
mem::simd_scanner scanner(pattern.pattern);
std::optional<uintptr_t> result = std::nullopt;
scanner({ mem_start, mem_size }, [&](mem::pointer res) {
result = res.as<uintptr_t>();
return false;
});

if (result.has_value()) {
replace_callback(result.value());
return true;
}

return false;
}

private:
std::vector<MEMORY_BASIC_INFORMATION> m_memory_regions;
const uint8_t *mem_start;
size_t mem_size;
};

}
}
5 changes: 3 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ target_include_directories(modengine2 PRIVATE
"../third-party/imgui/backends"
"../third-party/MINT"
"../third-party/ImTerm/include"
"../third-party/sol2_ImGui_Bindings")
"../third-party/sol2_ImGui_Bindings"
"../third-party/mem/include")

target_link_libraries(modengine2 PRIVATE
${DETOURS_LIBRARY}
Expand All @@ -99,4 +100,4 @@ target_precompile_headers(modengine2 PUBLIC
"$<$<COMPILE_LANGUAGE:CXX>:<toml++/toml.h$<ANGLE-R>>")

target_precompile_headers(modengine2 PRIVATE
"$<$<COMPILE_LANGUAGE:CXX>:<imgui.h$<ANGLE-R>>")
"$<$<COMPILE_LANGUAGE:CXX>:<imgui.h$<ANGLE-R>>")
4 changes: 2 additions & 2 deletions src/modengine/ext/mod_loader/mod_loader_extension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ auto loose_params_aob_1 = util::hex_string("74 68 48 8B CF 48 89 5C 24 30 E8");
auto loose_params_aob_2 = util::hex_string("0F 85 C5 00 00 00 48 8D 4C 24 28");
//auto loose_params_aob_3 = util::hex_string("E8 C8 F7 F7 FF 90 E9 73 E3 1F 04");

auto virtual_to_archive_path_er_aob = util::hex_aob("e8 ?? ?? ?? ?? 48 83 7b 20 08 48 8d 4b 08 72 03 48 8b 09 4c 8b 4b 18 41 b8 05 00 00 00 4d 3b c8");
auto virtual_to_archive_path_ac6_aob = util::hex_aob("cf e8 ?? ?? ?? ?? 48 83 7b 20 08 48 8d 4b 08 72 03 48 8b 09 4c 8b 4b 18 41 b8 05 00 00 00 4d 3b c8");
auto virtual_to_archive_path_er_aob = ScanPattern("E8 ? ? ? ? 48 83 7B 20 08 48 8D 4B 08 72 03 48 8B 09 4C 8B 4B 18 41 B8 05 00 00 00 4D 3B C8");
auto virtual_to_archive_path_ac6_aob = ScanPattern("CF E8 ? ? ? ? 48 83 7B 20 08 48 8D 4B 08 72 03 48 8B 09 4C 8B 4B 18 41 B8 05 00 00 00 4D 3B C8");

static fs::path primary_mod_path(const Settings& settings)
{
Expand Down
2 changes: 1 addition & 1 deletion src/modengine/hook_set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ bool HookSet::hook_all()
}
auto addr = scanner.find(hook->pattern);
if (!addr) {
error("Could not find pattern");
error("Could not find pattern {}", hook->pattern.pattern.to_string());
continue;
}
*addr = (uintptr_t)(((char*)*addr) + hook->offset);
Expand Down
31 changes: 0 additions & 31 deletions src/modengine/util/hex_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,35 +37,4 @@ std::string hex_string(const char* input)
return output.str();
}

ScanPattern hex_aob(const char* input)
{
ScanPattern result;
std::stringstream input_stream { input };
std::stringstream output;

short index = 0;
while (!input_stream.eof()) {
char lo;
char hi;

input_stream >> lo;
input_stream >> hi;

if (input_stream.eof()) {
break;
}

if (lo == '?' && hi == '?') {
output << (char)0;
result.wildcards.push_back(index);
} else {
output << (char) (16 * (hex2bin(lo)) + (hex2bin(hi)));
}
index++;
}

result.pattern = output.str();
return result;
}

}
1 change: 0 additions & 1 deletion src/modengine/util/hex_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,5 @@
namespace modengine::util {

std::string hex_string(const char* input);
ScanPattern hex_aob(const char* input);

}
1 change: 1 addition & 0 deletions third-party/mem
Submodule mem added at 6acbf7

0 comments on commit 20b7f1a

Please sign in to comment.