Skip to content

Commit 5f93aa9

Browse files
committed
Improve signature scanning, closes #75
1 parent b0c0cf8 commit 5f93aa9

File tree

4 files changed

+64
-36
lines changed

4 files changed

+64
-36
lines changed

lib/include/maniac/osu/signatures.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ namespace signatures {
99
constexpr auto STATE_SIG_OFFSET = 1;
1010
constexpr auto STATE_SIG = "A1 ? ? ? ? A3 ? ? ? ? A1 ? ? ? ? A3 ? ? ? ? 83 3D ? ? ? ? 00 0F 84 ? ? ? ? B9 ? ? ? ? E8\0";
1111

12-
constexpr auto PLAYER_SIG_OFFSET = 7;
13-
constexpr auto PLAYER_SIG = "FF 50 0C 8B D8 8B 15\0";
12+
constexpr auto PLAYER_SIG_OFFSET = 3;
13+
constexpr auto PLAYER_SIG = "33 D2 A1 ? ? ? ? 85 C0\0";
1414

1515
constexpr auto RULESET_SIG_OFFSET = 4;
1616
constexpr auto RULESET_SIG = "73 7A 8B 0D ? ? ? ? 85 C9 74 1F\0";

lib/include/maniac/process.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ inline size_t Process::read_memory(uintptr_t address, T *out, size_t count) {
6969
if (!ReadProcessMemory(handle, reinterpret_cast<LPCVOID>(address),
7070
reinterpret_cast<LPVOID>(out), count * sizeof(T),
7171
reinterpret_cast<SIZE_T *>(&read))) {
72+
debug("failed reading memory at %x (%d)", address, GetLastError());
73+
7274
return 0;
7375
}
7476

@@ -113,7 +115,7 @@ T Process::read_memory_safe(const char *name, Any addr) {
113115
throw std::runtime_error(msg);
114116
}
115117

116-
debug_short("%s: %#x", name, (unsigned int)address);
118+
debug("%s: %#x", name, (unsigned int)address);
117119

118120
return out;
119121
}

lib/osu.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ Osu::Osu() : Process("osu!.exe") {
77

88
// TODO: Run this in parallel.
99

10-
time_address = read_memory<uintptr_t>(find_pattern(TIME_SIG) + TIME_SIG_OFFSET);
11-
debug("found time address: %#x", time_address);
10+
time_address = read_memory<uintptr_t>(find_pattern(TIME_SIG) + TIME_SIG_OFFSET);
11+
debug("found time address: %#x", time_address);
1212

13-
player_pointer = read_memory<uintptr_t>(find_pattern(PLAYER_SIG) + PLAYER_SIG_OFFSET);
14-
debug("found player pointer: %#x", player_pointer);
13+
player_pointer = read_memory<uintptr_t>(find_pattern(PLAYER_SIG) + PLAYER_SIG_OFFSET);
14+
debug("found player pointer: %#x", player_pointer);
1515
}
1616

1717
Osu::~Osu() = default;

lib/process.cpp

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ HANDLE Process::get_handle(const std::string &name) {
4242

4343
debug("%s '%s' %s %lu", "found process", name.c_str(), "with id", process_id);
4444

45-
HANDLE handle = OpenProcess(PROCESS_VM_READ, FALSE, process_id);
45+
HANDLE handle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, process_id);
4646
if (!handle || handle == INVALID_HANDLE_VALUE) {
4747
throw std::runtime_error("failed opening handle to process");
4848
}
@@ -53,53 +53,79 @@ HANDLE Process::get_handle(const std::string &name) {
5353
}
5454

5555
uintptr_t Process::find_pattern(const char *pattern) {
56-
auto pattern_bytes = std::vector<int>{ };
57-
58-
for (auto cur = pattern; *cur; cur++) {
59-
if (*cur == '?') {
60-
// If the current byte is a wildcard push a dummy byte
61-
pattern_bytes.push_back(-1);
62-
} else if (*cur == ' ') {
63-
continue;
64-
} else {
65-
// This is somewhat hacky: strtol parses *as many characters as
66-
// possible* and sets cur to the first character it couldn't parse.
67-
// This is the reason why patterns *must* follow the structure
68-
// "AB CD EF ? ? FF"; "ABCDEF??FF" would cause it to fail later (!).
69-
pattern_bytes.push_back(strtol(cur, const_cast<char **>(&cur), 16));
70-
}
71-
}
56+
const auto pattern_to_bytes = [](const char *pattern) {
57+
auto pattern_bytes = std::vector<int>{ };
7258

73-
auto pattern_size = pattern_bytes.size();
59+
for (auto cur = pattern; *cur; cur++) {
60+
if (*cur == '?') {
61+
// If the current byte is a wildcard push a dummy byte
62+
pattern_bytes.push_back(-1);
63+
} else if (*cur == ' ') {
64+
continue;
65+
} else {
66+
// This is somewhat hacky: strtol parses *as many characters as
67+
// possible* and sets cur to the first character it couldn't parse.
68+
// This is the reason why patterns *must* follow the structure
69+
// "AB CD EF ? ? FF"; "ABCDEF??FF" would cause it to fail later (!).
70+
pattern_bytes.push_back(strtol(cur, const_cast<char **>(&cur), 16));
71+
}
72+
}
7473

75-
const size_t chunk_size = 4096;
76-
std::byte chunk_bytes[chunk_size];
74+
return pattern_bytes;
75+
};
7776

78-
for (uintptr_t i = 1; i < INT_MAX; i += chunk_size - pattern_size) {
79-
if (!read_memory<std::byte>(i, chunk_bytes, chunk_size)) {
80-
continue;
81-
}
77+
const auto pattern_bytes = pattern_to_bytes(pattern);
78+
const auto pattern_size = pattern_bytes.size();
79+
80+
uintptr_t cur_address = 0;
81+
// this is an osu-specific optimization
82+
uintptr_t max_address = 0x7fffffff;
83+
84+
while (cur_address < max_address) {
85+
MEMORY_BASIC_INFORMATION info;
86+
87+
if (!VirtualQueryEx(handle, reinterpret_cast<void *>(cur_address), &info, sizeof(info))) {
88+
debug("couldn't query at %x", cur_address);
89+
90+
return 0;
91+
}
8292

83-
for (size_t j = 0; j < chunk_size; j++) {
93+
bool invalidType = (info.Type != MEM_IMAGE && info.Type != MEM_PRIVATE);
94+
bool invalidProtection = (info.Protect != PAGE_WRITECOPY && info.Protect != PAGE_EXECUTE_WRITECOPY
95+
&& info.Protect != PAGE_EXECUTE_READWRITE && info.Protect != PAGE_READWRITE);
96+
97+
if (info.RegionSize == 0 || info.State != MEM_COMMIT || invalidType || invalidProtection) {
98+
cur_address = reinterpret_cast<uintptr_t>(info.BaseAddress) + info.RegionSize;
99+
100+
continue;
101+
}
102+
103+
cur_address = reinterpret_cast<uintptr_t>(info.BaseAddress);
104+
105+
auto buffer = std::vector<std::byte>(info.RegionSize);
106+
read_memory<std::byte>(cur_address, buffer.data(), buffer.size());
107+
108+
for (size_t j = 0; j < buffer.size(); j++) {
84109
bool hit = true;
85110

86111
for (size_t k = 0; k < pattern_size; k++) {
87112
if (pattern_bytes[k] == -1) {
88113
continue;
89114
}
90115

91-
if (chunk_bytes[j + k] !=
92-
static_cast<std::byte>(pattern_bytes[k])) {
116+
if (buffer.at(j + k) != static_cast<std::byte>(pattern_bytes[k])) {
93117
hit = false;
94118
break;
95119
}
96120
}
97121

98122
if (hit) {
99-
return i + j;
123+
return cur_address + j;
100124
}
101125
}
102-
}
126+
127+
cur_address += info.RegionSize;
128+
}
103129

104130
return 0;
105131
}

0 commit comments

Comments
 (0)