VoltMod
C++23 framework for CS2 server plugins
Loading...
Searching...
No Matches
BytePattern.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <cstddef>
5#include <cstdint>
6#include <string>
7#include <vector>
8
9namespace VoltMod
10{
11
12/**
13 * @file BytePattern.hpp
14 * @brief Parsing and searching byte patterns, with no notion of a loaded module.
15 *
16 * The half of signature scanning that is plain buffer work, split from @ref SigScanner so it is
17 * unit-tested without a mapped module behind it. SigScanner supplies the memory to search and the
18 * frequencies to weigh; everything here is a pure function of its arguments.
19 */
20
21/** One byte of a parsed pattern. A wildcard matches whatever is there. */
23{
25 bool Wildcard = false;
26};
27
28/** How often each byte value occurs in the memory being searched. */
29using ByteHistogram = std::array<size_t, 256>;
30
31/**
32 * Parse a pattern like `48 8B ? ? 05` into bytes and wildcards.
33 *
34 * @return the bytes, or empty when a token is neither a hex byte nor `?`. That is reported here
35 * and leaves the caller dropping the signature, rather than searching for something the
36 * author did not write.
37 */
38std::vector<PatternByte> ParsePattern(const std::string& pattern);
39
40/** Add the byte values in [base, base + size) to @p counts. */
41void CountBytes(const uint8_t* base, size_t size, ByteHistogram& counts);
42
43/**
44 * Index of the pattern byte to search for, or `pattern.size()` when it is all wildcards.
45 *
46 * Which byte is searched for is what decides a scan's cost, because the rest of the pattern is
47 * only compared where that byte lands. The first byte - the obvious anchor - is close to the worst
48 * one for x86-64: nearly every signature opens with a REX prefix (0x48), which saturates the
49 * image. Measured over CS2's server.dll, anchoring on the rarest byte instead scans ~16x faster.
50 */
51size_t AnchorOf(const std::vector<PatternByte>& pattern, const ByteHistogram& frequencies);
52
53/**
54 * First match of @p pattern in [base, base + size), searched for by its @p anchor byte.
55 *
56 * @param anchor from @ref AnchorOf, against the frequencies of the memory being searched. Any
57 * index of a non-wildcard byte gives the same answer; only the speed differs.
58 * @return the match, or nullptr.
59 */
60const uint8_t* FindFirst(const uint8_t* base, size_t size, const std::vector<PatternByte>& pattern, size_t anchor);
61
62} // namespace VoltMod
std::vector< PatternByte > ParsePattern(const std::string &pattern)
const uint8_t * FindFirst(const uint8_t *base, size_t size, const std::vector< PatternByte > &pattern, size_t anchor)
static std::string ReadFile(const std::filesystem::path &path)
Definition Loader.cpp:56
size_t AnchorOf(const std::vector< PatternByte > &pattern, const ByteHistogram &frequencies)
std::array< size_t, 256 > ByteHistogram
void CountBytes(const uint8_t *base, size_t size, ByteHistogram &counts)