Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ Include the header with the same name, e.g. `<PatternScanner/XORPattern.hpp>`.
- `4?` / `?8`: any low / high nibble
- arm64: `?` for a register (`X?`), immediate (`#?`), shift (`LSL #?`), condition (`B.?`) or a whole instruction

**Capture marker**: `X` marks the operand you care about; `X1`/`X2`/`X4`/`X8` give its size.
**Capture marker**: `X` marks the operand you care about. `X<n>` is how many bytes from it to the end of the instruction, which `/d` targets are relative to (`E8 X4 ? ? ? ?`: 4).

**Flags** (at the end of the pattern)
- `/d`: dereference: return the address the operand points to (e.g. a call or RIP-relative target)
- `/r`: read the value at the operand
- `/d1`, `/r2`, `/r8`...: the operand's size in bytes (1, 2, 4 or 8 on x64; default 4)
- `/a`: only match at aligned addresses (4 bytes on x86/arm64, 8 on x64)

## Examples
Expand All @@ -65,7 +66,7 @@ constexpr auto a = "AB CC 11 22 33 44 AB 6D X4 EF BE AD DE /r"_ctpattern;
constexpr auto b = "FE ED FA CE E8 X4 ? ? ? ? EF BE AD DE /da"_xorpattern;

// Follow a short jump (1-byte operand), pattern built at runtime
auto c = "BA BE CA FE 72 X1 ? 11 22 /d"_rtpattern;
auto c = "BA BE CA FE 72 X1 ? 11 22 /d1"_rtpattern;

// arm64: resolve ADRP + ADD to the full address
constexpr auto d =
Expand Down
11 changes: 8 additions & 3 deletions docs/arm64.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Writing patterns

One instruction per line; case, indentation and spacing don't matter, so lines pasted from IDA (`LDR W8, [X8,#0x18]`) or lldb (`ldr w8, [x8, #0x18]`) both work. lldb prints ADRP operands as page counts and branch targets as absolute addresses, so wildcard those (`#?`).
One instruction per line; case, indentation, spacing and `;` comments don't matter, so lines pasted from IDA (`LDR W8, [X8,#0x18]`) or lldb (`ldr w8, [x8, #0x18]`) both work. lldb prints ADRP operands as page counts and branch targets as absolute addresses, and IDA prints symbols (`BL _printf`, `#off_1000@PAGE`), so wildcard those (`#?`).

## Wildcards

Expand All @@ -15,7 +15,10 @@ One instruction per line; case, indentation and spacing don't matter, so lines p
- In loads and stores a bare `?` register is W or X; write `B?`/`H?`/`S?`/`D?`/`Q?` for floating-point/SIMD ones.
- Some wildcards can't be expressed with a mask and match more than the alias: `LSL Rd, Rn, #?` matches any `UBFM` (also `LSR`, `UBFX`...), and aliases that repeat a register (`CINC X?, X?, cond`, `ROR X?, X?, #n`) can't require the two to be the same.
- Others match less, as the encoding they name: `LDR Rt, [Rn,#?]` is the scaled form (write `LDUR` for negative or unaligned offsets, as IDA and lldb print them), and `FMOV Dd, #?` the 8-bit immediate form, not `#0.0` (`FMOV` from the zero register).
- An operand that can't be encoded (out of range, the wrong register kind, an unknown condition) makes the line a wildcard for those 4 bytes, matching any instruction (the IDA plugin asks before searching with one). It never makes a pattern that can't match. Write `?` for a deliberate one.

## Errors

A bad operand is an error: a compile error for `ArmPattern`/`ArmXORPattern`, a `std::logic_error` naming the line for `RuntimeArmPattern` (a trap without exceptions). That's a typo, a register that doesn't exist (`X31`) or doesn't fit (`SP` where the encoding reads the zero register, `W` mixed with `X`, `D0` in an integer instruction), a value out of range or misaligned for its field, a symbol instead of a number, an unknown shift, extend or condition, and a missing or extra operand.

## Supported instructions

Expand All @@ -32,7 +35,7 @@ One instruction per line; case, indentation and spacing don't matter, so lines p
- Atomics: `CAS`, `SWP`, `LD<op>`, `ST<op>` with `<op>` `ADD`, `CLR`, `EOR`, `SET`, `SMAX`, `SMIN`, `UMAX`, `UMIN`, each with the `A`/`L`/`AL` and `B`/`H` suffixes
- Floating point: `FMOV` (register, to/from W/X, and immediates like `#1.0`)

Any other instruction becomes a wildcard for those 4 bytes.
Any other mnemonic becomes a wildcard for those 4 bytes, matching any instruction (the IDA plugin asks before searching with one).

## Aliases

Expand All @@ -41,6 +44,7 @@ Encoded the way assemblers pick them:
- `CMP`/`CMN`/`TST`: `SUBS`/`ADDS`/`ANDS` to the zero register; `MVN`/`NEG(S)`/`NGC(S)`: `ORN`/`SUB(S)`/`SBC(S)` from it.
- `LDR`/`STR` offsets the scaled form can't hold (negative, unaligned): `LDUR`/`STUR`.
- `ADD`/`SUB`/`CMP` immediates that are negative or multiples of 0x1000: the opposite operation or `LSL #12`.
- `BIC`/`BICS`/`ORN`/`EON` immediates: `AND`/`ANDS`/`ORR`/`EOR` with the inverted value.
- Register `ADD`/`SUB` with `SP`: the extended (`UXTX`) form.
- `CSET`/`CSETM`/`CINC`/`CINV`/`CNEG`: `CSINC`/`CSINV`/`CSNEG` with the inverted condition.
- `LSL`/`LSR`/`ASR` by an immediate, `UBFX`/`UBFIZ`/`BFI`/`SXTB`...: `UBFM`/`SBFM`/`BFM`; `ROR` by an immediate: `EXTR`.
Expand All @@ -53,5 +57,6 @@ At the `X` instruction:
- `B`/`BL`, `B.cond`, `CBZ`/`CBNZ`, `TBZ`/`TBNZ`, `LDR` (literal) and `ADR` give their target.
- `ADRP` gives the page plus the `ADD`/`SUB` and load/store offset (including floating-point loads like `LDR D0`) that follow it through the same register (skipping NOPs and instructions that leave the register alone).
- `MOVZ`/`MOVN`, `ADD`/`SUB` immediates and load/store offsets give the value.
- Any other instruction: `find()` returns `nullptr`, as for no match.

`/r` is not used on arm64.
4 changes: 4 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,7 @@ Each `_xorpattern` and `_armxorpattern` gets its own random keys from where it's
## SIMD

Scanning uses SSE2 (x86/x64) or NEON (arm64) when available. Define `PATTERNS_NO_SIMD` before including to use a portable 64-bit integer scan instead (e.g. kernel code).

## Without exceptions

Pattern errors (bad hex, `/d` with `/r`, a bad arm64 operand...) throw `std::logic_error`, a compile error for compile-time patterns. Built without exceptions (`-fno-exceptions`, kernel code) they are still compile errors there, and trap at runtime (`RuntimePattern` with bad text).
Loading
Loading