mirror of
https://github.com/orange-cpp/omath.git
synced 2026-02-13 07:03:25 +00:00
Add Roboto Condensed font and documentation for new features
Co-authored-by: orange-cpp <59374393+orange-cpp@users.noreply.github.com>
This commit is contained in:
93
docs/collision/collider_interface.md
Normal file
93
docs/collision/collider_interface.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# `omath::collision::ColliderInterface` — Abstract collider base class
|
||||
|
||||
> Header: `omath/collision/collider_interface.hpp`
|
||||
> Namespace: `omath::collision`
|
||||
> Depends on: `omath/linear_algebra/vector3.hpp`
|
||||
|
||||
`ColliderInterface` is the abstract base class for all colliders used by the GJK and EPA algorithms. Any shape that can report its furthest vertex along a given direction can implement this interface and be used for collision detection.
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
```cpp
|
||||
namespace omath::collision {
|
||||
|
||||
template<class VecType = Vector3<float>>
|
||||
class ColliderInterface {
|
||||
public:
|
||||
using VectorType = VecType;
|
||||
|
||||
virtual ~ColliderInterface() = default;
|
||||
|
||||
// Return the world-space position of the vertex furthest along `direction`.
|
||||
[[nodiscard]]
|
||||
virtual VectorType find_abs_furthest_vertex_position(
|
||||
const VectorType& direction) const = 0;
|
||||
|
||||
// Get the collider's origin (center / position).
|
||||
[[nodiscard]]
|
||||
virtual const VectorType& get_origin() const = 0;
|
||||
|
||||
// Reposition the collider.
|
||||
virtual void set_origin(const VectorType& new_origin) = 0;
|
||||
};
|
||||
|
||||
} // namespace omath::collision
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementing a custom collider
|
||||
|
||||
To create a new collider shape, derive from `ColliderInterface` and implement the three pure-virtual methods:
|
||||
|
||||
```cpp
|
||||
#include "omath/collision/collider_interface.hpp"
|
||||
|
||||
class SphereCollider final
|
||||
: public omath::collision::ColliderInterface<omath::Vector3<float>>
|
||||
{
|
||||
public:
|
||||
SphereCollider(omath::Vector3<float> center, float radius)
|
||||
: m_center(center), m_radius(radius) {}
|
||||
|
||||
[[nodiscard]]
|
||||
omath::Vector3<float> find_abs_furthest_vertex_position(
|
||||
const omath::Vector3<float>& direction) const override
|
||||
{
|
||||
return m_center + direction.normalized() * m_radius;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
const omath::Vector3<float>& get_origin() const override
|
||||
{ return m_center; }
|
||||
|
||||
void set_origin(const omath::Vector3<float>& new_origin) override
|
||||
{ m_center = new_origin; }
|
||||
|
||||
private:
|
||||
omath::Vector3<float> m_center;
|
||||
float m_radius;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
* **Template parameter**: The default vector type is `Vector3<float>`, but any vector type with a `dot()` method can be used.
|
||||
* **GJK/EPA compatibility**: Both `GjkAlgorithm` and `EpaAlgorithm` accept any type satisfying the `ColliderInterface` contract through their template parameters.
|
||||
|
||||
---
|
||||
|
||||
## See also
|
||||
|
||||
* [GJK Algorithm](gjk_algorithm.md) — collision detection using GJK.
|
||||
* [EPA Algorithm](epa_algorithm.md) — penetration depth via EPA.
|
||||
* [Mesh Collider](mesh_collider.md) — concrete collider wrapping a `Mesh`.
|
||||
* [Simplex](simplex.md) — simplex data structure used by GJK/EPA.
|
||||
|
||||
---
|
||||
|
||||
*Last updated: Feb 2026*
|
||||
115
docs/containers/encrypted_variable.md
Normal file
115
docs/containers/encrypted_variable.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# `omath::EncryptedVariable` — Compile-time XOR-encrypted variable
|
||||
|
||||
> Header: `omath/containers/encrypted_variable.hpp`
|
||||
> Namespace: `omath`
|
||||
> Depends on: `<array>`, `<cstddef>`, `<cstdint>`, `<span>`
|
||||
|
||||
`EncryptedVariable` keeps a value XOR-encrypted in memory at rest, using a **compile-time generated random key**. It is designed to hinder static analysis and memory scanners from reading sensitive values (e.g., game constants, keys, thresholds) directly from process memory.
|
||||
|
||||
---
|
||||
|
||||
## Key concepts
|
||||
|
||||
* **Compile-time key generation** — a unique random byte array is produced at compile time via SplitMix64 + FNV-1a seeded from `__FILE__`, `__DATE__`, and `__TIME__`. Each `OMATH_DEF_CRYPT_VAR` expansion receives a distinct key.
|
||||
* **XOR cipher** — `encrypt()` / `decrypt()` toggle the encrypted state by XOR-ing the raw bytes of the stored value with the key.
|
||||
* **VarAnchor (RAII guard)** — `drop_anchor()` returns a `VarAnchor` that decrypts on construction and re-encrypts on destruction, ensuring the plaintext window is as short as possible.
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
```cpp
|
||||
namespace omath {
|
||||
|
||||
template<class T, std::size_t key_size, std::array<std::uint8_t, key_size> key>
|
||||
class EncryptedVariable final {
|
||||
public:
|
||||
using value_type = std::remove_cvref_t<T>;
|
||||
|
||||
constexpr explicit EncryptedVariable(const value_type& data);
|
||||
|
||||
[[nodiscard]] constexpr bool is_encrypted() const;
|
||||
|
||||
constexpr void decrypt();
|
||||
constexpr void encrypt();
|
||||
|
||||
[[nodiscard]] constexpr value_type& value();
|
||||
[[nodiscard]] constexpr const value_type& value() const;
|
||||
|
||||
[[nodiscard]] constexpr auto drop_anchor(); // returns VarAnchor
|
||||
|
||||
constexpr ~EncryptedVariable(); // decrypts on destruction
|
||||
};
|
||||
|
||||
template<class EncryptedVarType>
|
||||
class VarAnchor final {
|
||||
public:
|
||||
constexpr VarAnchor(EncryptedVarType& var); // decrypts
|
||||
constexpr ~VarAnchor(); // re-encrypts
|
||||
};
|
||||
|
||||
} // namespace omath
|
||||
```
|
||||
|
||||
### Helper macros
|
||||
|
||||
```cpp
|
||||
// Generate a compile-time random byte array of length N
|
||||
#define OMATH_CT_RAND_ARRAY_BYTE(N) /* ... */
|
||||
|
||||
// Declare a type alias for EncryptedVariable<TYPE> with KEY_SIZE random bytes
|
||||
#define OMATH_DEF_CRYPT_VAR(TYPE, KEY_SIZE) /* ... */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage examples
|
||||
|
||||
### Basic encrypt / decrypt
|
||||
|
||||
```cpp
|
||||
#include "omath/containers/encrypted_variable.hpp"
|
||||
|
||||
// Define an encrypted float with a 16-byte key
|
||||
using EncFloat = OMATH_DEF_CRYPT_VAR(float, 16);
|
||||
|
||||
EncFloat secret(3.14f); // encrypted immediately
|
||||
// secret.value() is XOR-scrambled in memory
|
||||
|
||||
secret.decrypt();
|
||||
float v = secret.value(); // v == 3.14f
|
||||
secret.encrypt(); // scrambled again
|
||||
```
|
||||
|
||||
### RAII guard with `drop_anchor()`
|
||||
|
||||
```cpp
|
||||
EncFloat secret(42.0f);
|
||||
|
||||
{
|
||||
auto anchor = secret.drop_anchor(); // decrypts
|
||||
float val = secret.value(); // safe to read
|
||||
// ... use val ...
|
||||
} // anchor destroyed → re-encrypts automatically
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes & edge cases
|
||||
|
||||
* **Force-inline**: When `OMATH_ENABLE_FORCE_INLINE` is defined, encrypt/decrypt operations use compiler-specific force-inline attributes to reduce the call-site footprint visible in disassembly.
|
||||
* **Not cryptographically secure**: XOR with a static key is an obfuscation technique, not encryption. It raises the bar for casual memory scanning but does not resist a determined attacker who can read the binary.
|
||||
* **Destructor decrypts**: The destructor calls `decrypt()` so the value is in plaintext at the end of its lifetime (e.g., for logging or cleanup).
|
||||
* **Thread safety**: No internal synchronization. Protect concurrent access with external locks.
|
||||
* **`constexpr` support**: All operations are `constexpr`-friendly (C++20).
|
||||
|
||||
---
|
||||
|
||||
## See also
|
||||
|
||||
* [Pattern Scan](../utility/pattern_scan.md) — scan memory for byte patterns.
|
||||
* [Getting Started](../getting_started.md) — quick start with OMath.
|
||||
|
||||
---
|
||||
|
||||
*Last updated: Feb 2026*
|
||||
172
docs/styles/fonts.css
Normal file
172
docs/styles/fonts.css
Normal file
@@ -0,0 +1,172 @@
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19-7DRs5.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19a7DRs5.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-1967DRs5.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19G7DRs5.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-1927DRs5.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19y7DRs5.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19K7DQ.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19-7DRs5.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19a7DRs5.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-1967DRs5.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19G7DRs5.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-1927DRs5.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19y7DRs5.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19K7DQ.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* cyrillic-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19-7DRs5.woff2) format('woff2');
|
||||
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||
}
|
||||
/* cyrillic */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19a7DRs5.woff2) format('woff2');
|
||||
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||
}
|
||||
/* greek-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-1967DRs5.woff2) format('woff2');
|
||||
unicode-range: U+1F00-1FFF;
|
||||
}
|
||||
/* greek */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19G7DRs5.woff2) format('woff2');
|
||||
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
|
||||
}
|
||||
/* vietnamese */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-1927DRs5.woff2) format('woff2');
|
||||
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19y7DRs5.woff2) format('woff2');
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Roboto Condensed';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(https://fonts.gstatic.com/s/robotocondensed/v31/ieVl2ZhZI2eCN5jzbjEETS9weq8-19K7DQ.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
}
|
||||
142
docs/utility/elf_pattern_scan.md
Normal file
142
docs/utility/elf_pattern_scan.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# `omath::ElfPatternScanner` — Scan ELF binaries for byte patterns
|
||||
|
||||
> Header: `omath/utility/elf_pattern_scan.hpp`
|
||||
> Namespace: `omath`
|
||||
> Platform: **Linux / ELF (Executable and Linkable Format) binaries**
|
||||
> Depends on: `<cstdint>`, `<filesystem>`, `<optional>`, `<string_view>`, `omath/utility/section_scan_result.hpp`
|
||||
> Companion: works well with `omath::PatternScanner` (same pattern grammar)
|
||||
|
||||
`ElfPatternScanner` searches **ELF** binaries for a hex pattern (with wildcards). You can scan:
|
||||
|
||||
* a **loaded module** in the current process, or
|
||||
* an **ELF file on disk** (by section name; defaults to **`.text`**).
|
||||
|
||||
---
|
||||
|
||||
## Pattern string grammar (same as `PatternScanner`)
|
||||
|
||||
* **Hex byte**: two hex digits → one byte (`90`, `4F`, `00`, `ff`).
|
||||
* **Wildcard byte**: `?` or `??` matches **any byte**.
|
||||
* **Whitespace**: ignored (use to group tokens).
|
||||
|
||||
✔️ `"48 8B ?? ?? 89"`, `"55 48 89 E5"`, `"??"`
|
||||
❌ odd digit counts, non-hex characters (besides `?` and whitespace)
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
```cpp
|
||||
namespace omath {
|
||||
|
||||
class ElfPatternScanner final {
|
||||
public:
|
||||
// Scan a module already loaded in *this* process.
|
||||
// module_base_address: base address of the loaded ELF (e.g., from dlopen / /proc/self/maps)
|
||||
// Returns absolute address (process VA) of the first match, or nullopt.
|
||||
static std::optional<std::uintptr_t>
|
||||
scan_for_pattern_in_loaded_module(
|
||||
const void* module_base_address,
|
||||
const std::string_view& pattern,
|
||||
const std::string_view& target_section_name = ".text");
|
||||
|
||||
// Scan an ELF file on disk, by section name (default ".text").
|
||||
// Returns section bases (virtual + raw) and match offset within the section, or nullopt.
|
||||
static std::optional<SectionScanResult>
|
||||
scan_for_pattern_in_file(
|
||||
const std::filesystem::path& path_to_file,
|
||||
const std::string_view& pattern,
|
||||
const std::string_view& target_section_name = ".text");
|
||||
};
|
||||
|
||||
} // namespace omath
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Return values
|
||||
|
||||
* **Loaded module**: `std::optional<std::uintptr_t>`
|
||||
|
||||
* `value()` = **process virtual address** of the first match.
|
||||
* `nullopt` = no match or parse/ELF error.
|
||||
|
||||
* **File scan**: `std::optional<SectionScanResult>`
|
||||
|
||||
* `virtual_base_addr` = virtual address base of the scanned section.
|
||||
* `raw_base_addr` = file offset of section start.
|
||||
* `target_offset` = offset from section base to the first matched byte.
|
||||
* To get addresses:
|
||||
|
||||
* **Virtual address** of hit = `virtual_base_addr + target_offset`
|
||||
* **Raw file offset** of hit = `raw_base_addr + target_offset`
|
||||
|
||||
---
|
||||
|
||||
## Usage examples
|
||||
|
||||
### Scan a loaded module (current process)
|
||||
|
||||
```cpp
|
||||
#include <dlfcn.h>
|
||||
#include "omath/utility/elf_pattern_scan.hpp"
|
||||
|
||||
using omath::ElfPatternScanner;
|
||||
|
||||
void* handle = dlopen("libexample.so", RTLD_LAZY);
|
||||
if (handle) {
|
||||
auto addr = ElfPatternScanner::scan_for_pattern_in_loaded_module(
|
||||
handle, "55 48 89 E5 ?? ?? 48"
|
||||
);
|
||||
if (addr) {
|
||||
std::uintptr_t hit_va = *addr;
|
||||
// ...
|
||||
}
|
||||
dlclose(handle);
|
||||
}
|
||||
```
|
||||
|
||||
### Scan an ELF file on disk
|
||||
|
||||
```cpp
|
||||
#include "omath/utility/elf_pattern_scan.hpp"
|
||||
using omath::ElfPatternScanner;
|
||||
|
||||
auto res = ElfPatternScanner::scan_for_pattern_in_file(
|
||||
"/usr/lib/libexample.so", "55 48 89 E5"
|
||||
);
|
||||
if (res) {
|
||||
auto va_hit = res->virtual_base_addr + res->target_offset;
|
||||
auto raw_hit = res->raw_base_addr + res->target_offset;
|
||||
}
|
||||
```
|
||||
|
||||
### Scan another section (e.g., ".rodata")
|
||||
|
||||
```cpp
|
||||
auto res = ElfPatternScanner::scan_for_pattern_in_file(
|
||||
"myapp", "48 8D 0D ?? ?? ?? ??", ".rodata"
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes & edge cases
|
||||
|
||||
* **ELF only**: these functions assume a valid ELF layout. Non-ELF files or corrupted headers yield `nullopt`.
|
||||
* **Section name**: defaults to **`.text`**; pass a different name to target other sections.
|
||||
* **Performance**: Pattern matching is **O(N × M)** (sliding window with wildcards). For large binaries, prefer scanning only necessary sections.
|
||||
* **Architecture**: works for 32-bit and 64-bit ELF binaries.
|
||||
|
||||
---
|
||||
|
||||
## See also
|
||||
|
||||
* [`omath::PatternScanner`](pattern_scan.md) — raw buffer/iterator scanning with the same pattern grammar.
|
||||
* [`omath::PePatternScanner`](pe_pattern_scan.md) — PE (Windows) binary scanner.
|
||||
* [`omath::MachOPatternScanner`](macho_pattern_scan.md) — Mach-O (macOS) binary scanner.
|
||||
* [`omath::SectionScanResult`](section_scan_result.md) — return type for file-based scans.
|
||||
|
||||
---
|
||||
|
||||
*Last updated: Feb 2026*
|
||||
142
docs/utility/macho_pattern_scan.md
Normal file
142
docs/utility/macho_pattern_scan.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# `omath::MachOPatternScanner` — Scan Mach-O binaries for byte patterns
|
||||
|
||||
> Header: `omath/utility/macho_pattern_scan.hpp`
|
||||
> Namespace: `omath`
|
||||
> Platform: **macOS / Mach-O binaries**
|
||||
> Depends on: `<cstdint>`, `<filesystem>`, `<optional>`, `<string_view>`, `omath/utility/section_scan_result.hpp`
|
||||
> Companion: works well with `omath::PatternScanner` (same pattern grammar)
|
||||
|
||||
`MachOPatternScanner` searches **Mach-O** binaries for a hex pattern (with wildcards). You can scan:
|
||||
|
||||
* a **loaded module** in the current process, or
|
||||
* a **Mach-O file on disk** (by section name; defaults to **`__text`**).
|
||||
|
||||
---
|
||||
|
||||
## Pattern string grammar (same as `PatternScanner`)
|
||||
|
||||
* **Hex byte**: two hex digits → one byte (`90`, `4F`, `00`, `ff`).
|
||||
* **Wildcard byte**: `?` or `??` matches **any byte**.
|
||||
* **Whitespace**: ignored (use to group tokens).
|
||||
|
||||
✔️ `"48 8B ?? ?? 89"`, `"55 48 89 E5"`, `"??"`
|
||||
❌ odd digit counts, non-hex characters (besides `?` and whitespace)
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
```cpp
|
||||
namespace omath {
|
||||
|
||||
class MachOPatternScanner final {
|
||||
public:
|
||||
// Scan a module already loaded in *this* process.
|
||||
// module_base_address: base address of the loaded Mach-O image
|
||||
// Returns absolute address (process VA) of the first match, or nullopt.
|
||||
static std::optional<std::uintptr_t>
|
||||
scan_for_pattern_in_loaded_module(
|
||||
const void* module_base_address,
|
||||
const std::string_view& pattern,
|
||||
const std::string_view& target_section_name = "__text");
|
||||
|
||||
// Scan a Mach-O file on disk, by section name (default "__text").
|
||||
// Returns section bases (virtual + raw) and match offset within the section, or nullopt.
|
||||
static std::optional<SectionScanResult>
|
||||
scan_for_pattern_in_file(
|
||||
const std::filesystem::path& path_to_file,
|
||||
const std::string_view& pattern,
|
||||
const std::string_view& target_section_name = "__text");
|
||||
};
|
||||
|
||||
} // namespace omath
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Return values
|
||||
|
||||
* **Loaded module**: `std::optional<std::uintptr_t>`
|
||||
|
||||
* `value()` = **process virtual address** of the first match.
|
||||
* `nullopt` = no match or parse/Mach-O error.
|
||||
|
||||
* **File scan**: `std::optional<SectionScanResult>`
|
||||
|
||||
* `virtual_base_addr` = virtual address base of the scanned section.
|
||||
* `raw_base_addr` = file offset of section start.
|
||||
* `target_offset` = offset from section base to the first matched byte.
|
||||
* To get addresses:
|
||||
|
||||
* **Virtual address** of hit = `virtual_base_addr + target_offset`
|
||||
* **Raw file offset** of hit = `raw_base_addr + target_offset`
|
||||
|
||||
---
|
||||
|
||||
## Usage examples
|
||||
|
||||
### Scan a loaded module (current process)
|
||||
|
||||
```cpp
|
||||
#include <dlfcn.h>
|
||||
#include "omath/utility/macho_pattern_scan.hpp"
|
||||
|
||||
using omath::MachOPatternScanner;
|
||||
|
||||
void* handle = dlopen("libexample.dylib", RTLD_LAZY);
|
||||
if (handle) {
|
||||
auto addr = MachOPatternScanner::scan_for_pattern_in_loaded_module(
|
||||
handle, "55 48 89 E5 ?? ?? 48"
|
||||
);
|
||||
if (addr) {
|
||||
std::uintptr_t hit_va = *addr;
|
||||
// ...
|
||||
}
|
||||
dlclose(handle);
|
||||
}
|
||||
```
|
||||
|
||||
### Scan a Mach-O file on disk
|
||||
|
||||
```cpp
|
||||
#include "omath/utility/macho_pattern_scan.hpp"
|
||||
using omath::MachOPatternScanner;
|
||||
|
||||
auto res = MachOPatternScanner::scan_for_pattern_in_file(
|
||||
"/usr/local/lib/libexample.dylib", "55 48 89 E5"
|
||||
);
|
||||
if (res) {
|
||||
auto va_hit = res->virtual_base_addr + res->target_offset;
|
||||
auto raw_hit = res->raw_base_addr + res->target_offset;
|
||||
}
|
||||
```
|
||||
|
||||
### Scan another section (e.g., "__cstring")
|
||||
|
||||
```cpp
|
||||
auto res = MachOPatternScanner::scan_for_pattern_in_file(
|
||||
"myapp", "48 8D 0D ?? ?? ?? ??", "__cstring"
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes & edge cases
|
||||
|
||||
* **Mach-O only**: these functions assume a valid Mach-O layout. Non-Mach-O files or corrupted headers yield `nullopt`.
|
||||
* **Section name**: defaults to **`__text`** (note the double underscore, per Mach-O convention); pass a different name to target other sections.
|
||||
* **Performance**: Pattern matching is **O(N × M)** (sliding window with wildcards). For large binaries, prefer scanning only necessary sections.
|
||||
* **Architecture**: works for 64-bit Mach-O binaries (x86_64 and arm64).
|
||||
|
||||
---
|
||||
|
||||
## See also
|
||||
|
||||
* [`omath::PatternScanner`](pattern_scan.md) — raw buffer/iterator scanning with the same pattern grammar.
|
||||
* [`omath::PePatternScanner`](pe_pattern_scan.md) — PE (Windows) binary scanner.
|
||||
* [`omath::ElfPatternScanner`](elf_pattern_scan.md) — ELF (Linux) binary scanner.
|
||||
* [`omath::SectionScanResult`](section_scan_result.md) — return type for file-based scans.
|
||||
|
||||
---
|
||||
|
||||
*Last updated: Feb 2026*
|
||||
58
docs/utility/section_scan_result.md
Normal file
58
docs/utility/section_scan_result.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# `omath::SectionScanResult` — File-based pattern scan result
|
||||
|
||||
> Header: `omath/utility/section_scan_result.hpp`
|
||||
> Namespace: `omath`
|
||||
> Depends on: `<cstddef>`, `<cstdint>`
|
||||
|
||||
`SectionScanResult` is the return type for file-based pattern scans across all binary formats (PE, ELF, Mach-O). It carries the section's virtual and raw base addresses together with the offset to the matched pattern.
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
```cpp
|
||||
namespace omath {
|
||||
|
||||
struct SectionScanResult final {
|
||||
std::uintptr_t virtual_base_addr; // virtual address base of the scanned section
|
||||
std::uintptr_t raw_base_addr; // file offset of the section start
|
||||
std::ptrdiff_t target_offset; // offset from section base to the first matched byte
|
||||
};
|
||||
|
||||
} // namespace omath
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Computing addresses from a result
|
||||
|
||||
```cpp
|
||||
omath::SectionScanResult res = /* ... */;
|
||||
|
||||
// Virtual address of the match (as if the binary were loaded at its preferred base)
|
||||
auto va_hit = res.virtual_base_addr + res.target_offset;
|
||||
|
||||
// Raw file offset of the match
|
||||
auto raw_hit = res.raw_base_addr + res.target_offset;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
* `virtual_base_addr` is computed from the section header (RVA for PE, `sh_addr` for ELF, `addr` for Mach-O).
|
||||
* `raw_base_addr` is the file offset where the section data begins on disk.
|
||||
* `target_offset` is always relative to the section base — add it to either address to locate the match.
|
||||
|
||||
---
|
||||
|
||||
## See also
|
||||
|
||||
* [`omath::PePatternScanner`](pe_pattern_scan.md) — PE (Windows) binary scanner.
|
||||
* [`omath::ElfPatternScanner`](elf_pattern_scan.md) — ELF (Linux) binary scanner.
|
||||
* [`omath::MachOPatternScanner`](macho_pattern_scan.md) — Mach-O (macOS) binary scanner.
|
||||
* [`omath::PatternScanner`](pattern_scan.md) — raw buffer/iterator scanning.
|
||||
|
||||
---
|
||||
|
||||
*Last updated: Feb 2026*
|
||||
Reference in New Issue
Block a user