reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
Apache License 2.0 — see the repository root LICENSE file for the full text.
|
||||
|
||||
Copyright the AnalystAIPack contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
name: writing-yara-rules-from-reversed-code
|
||||
description: 'Turns reverse-engineering findings into durable YARA detections: selecting stable
|
||||
code constructs and constants over volatile strings, extracting opcode/byte patterns with
|
||||
wildcards, and validating rules for low false positives. Activates for requests to write a
|
||||
YARA rule from reversed code, create a detection signature, or convert RE findings into
|
||||
hunting rules.'
|
||||
domain: cybersecurity
|
||||
subdomain: reverse-engineering
|
||||
tags:
|
||||
- reverse-engineering
|
||||
- yara
|
||||
- detection
|
||||
- signatures
|
||||
- threat-hunting
|
||||
version: 1.0.0
|
||||
author: analyst-ai-pack
|
||||
license: Apache-2.0
|
||||
mitre_attack:
|
||||
- T1027.001
|
||||
- T1587.001
|
||||
d3fend:
|
||||
- D3-FCR
|
||||
- D3-FA
|
||||
references:
|
||||
- 'YARA documentation — https://yara.readthedocs.io/'
|
||||
- 'YARA writing-better-rules guidance — https://github.com/Neo23x0/YARA-Style-Guide'
|
||||
---
|
||||
|
||||
# Writing YARA Rules from Reversed Code
|
||||
|
||||
## When to Use
|
||||
|
||||
- You finished reversing a sample/family and want a detection that survives recompilation and
|
||||
cosmetic changes.
|
||||
- You need byte-pattern signatures from decryptors, API-hash constants, or unique algorithms
|
||||
rather than fragile strings.
|
||||
- You are converting RE notes into hunting/scanning rules for a corpus.
|
||||
|
||||
**Do not use** volatile artifacts (file paths, mutex names that change per build, packer stubs
|
||||
shared across unrelated families) as your primary anchor — they cause drift and false hits.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- The `yara` engine (and ideally `yara-python`) for testing.
|
||||
- A small corpus: target samples (true positives) and clean/unrelated files (false-positive
|
||||
control).
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Choose stable anchors
|
||||
|
||||
Prefer, in order: a unique algorithm's opcode sequence (decryptor, hashing loop), embedded
|
||||
magic constants (API hashes, XOR keys, S-box), then distinctive strings only if intrinsic.
|
||||
|
||||
### Step 2: Extract byte patterns with wildcards
|
||||
|
||||
Pull the relevant opcodes and wildcard volatile operands (addresses, immediates) so the rule
|
||||
survives relocation/recompilation:
|
||||
|
||||
```text
|
||||
$dec = { 8A 04 ?? 34 ?? 88 04 ?? 41 3B ?? 7? ?? } ; xor-decrypt loop, regs/disp wildcarded
|
||||
```
|
||||
|
||||
### Step 3: Assemble the rule
|
||||
|
||||
Combine 2–3 independent anchors with a `condition` requiring enough of them, plus a cheap
|
||||
prefilter (file size, PE magic) to keep scanning fast:
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py scaffold --name family_xyz --hash 0xABCDEF12
|
||||
```
|
||||
|
||||
### Step 4: Validate against the corpus
|
||||
|
||||
Run the rule across true positives and the clean control set; require all TPs match and zero
|
||||
FPs on the control.
|
||||
|
||||
```bash
|
||||
yara -r rules/family_xyz.yar ./corpus
|
||||
```
|
||||
|
||||
### Step 5: Tune and document
|
||||
|
||||
Adjust thresholds, add `meta` (author, date, reference, hash), and record which construct each
|
||||
string anchors so future analysts can maintain it.
|
||||
|
||||
## Validation
|
||||
|
||||
- Rule matches all intended samples and produces zero hits on the clean control set.
|
||||
- Anchors map to intrinsic code/constants, not build-specific noise.
|
||||
- `meta` documents source samples and the reasoning for each pattern.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Wildcarding too little (brittle) or too much (false positives); wildcard only volatile bytes.
|
||||
- Anchoring on shared library/packer code present in unrelated binaries.
|
||||
- Skipping the clean-corpus test, shipping a noisy rule.
|
||||
|
||||
## References
|
||||
|
||||
- See [`references/api-reference.md`](references/api-reference.md) for the rule scaffolder.
|
||||
- YARA documentation and style guide (linked in frontmatter).
|
||||
@@ -0,0 +1,40 @@
|
||||
# API Reference: Writing YARA Rules from Reversed Code
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/analyst.py` scaffolds a structured YARA rule with `meta`, placeholder byte-pattern
|
||||
strings, an optional API-hash constant (emitted little-endian), and a multi-anchor condition
|
||||
with a PE prefilter. It emits text only and scans nothing.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| (stdlib) | — | text generation |
|
||||
|
||||
## External tools
|
||||
|
||||
| Tool | Purpose |
|
||||
|------|---------|
|
||||
| yara | Compile and test rules against the corpus |
|
||||
| yara-python | Programmatic testing/automation |
|
||||
|
||||
## Core functions
|
||||
|
||||
### `scaffold(name, api_hash, author)`
|
||||
Returns a YARA rule skeleton; if `api_hash` is given, adds a little-endian byte-pattern string
|
||||
and includes it in the condition.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py scaffold --name family_xyz --hash 0xABCDEF12
|
||||
yara -r rules/family_xyz.yar ./corpus
|
||||
```
|
||||
|
||||
## Sources
|
||||
|
||||
- YARA documentation — https://yara.readthedocs.io/
|
||||
- YARA style guide (writing maintainable, low-FP rules) —
|
||||
https://github.com/Neo23x0/YARA-Style-Guide
|
||||
- Preference for code/constant anchors over volatile strings, established RE detection practice
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Scaffold a YARA rule from reverse-engineering findings.
|
||||
|
||||
Generates a well-structured rule skeleton with ``meta``, placeholder byte-pattern strings, an
|
||||
optional API-hash constant, and a sensible multi-anchor condition with a PE prefilter. The
|
||||
analyst fills in the real opcode bytes. This only emits text; it does not scan anything.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import datetime as _dt
|
||||
import sys
|
||||
|
||||
|
||||
def scaffold(name: str, api_hash: str | None, author: str) -> str:
|
||||
today = _dt.date.today().isoformat()
|
||||
hash_line = ""
|
||||
hash_cond = ""
|
||||
if api_hash:
|
||||
val = int(api_hash, 0)
|
||||
hash_line = f" $h_api = {{ {val & 0xFF:02X} {(val >> 8) & 0xFF:02X} " \
|
||||
f"{(val >> 16) & 0xFF:02X} {(val >> 24) & 0xFF:02X} }} // API hash LE\n"
|
||||
hash_cond = " and $h_api"
|
||||
return f"""rule {name}
|
||||
{{
|
||||
meta:
|
||||
author = "{author}"
|
||||
date = "{today}"
|
||||
description = "Detects {name} based on reversed code constructs"
|
||||
reference = "<analysis report / sample hash>"
|
||||
sample_sha256 = "<fill in>"
|
||||
|
||||
strings:
|
||||
// Replace with the real decryptor/algorithm opcodes; wildcard volatile operands.
|
||||
$code1 = {{ 8A 04 ?? 34 ?? 88 04 ?? 41 3B ?? }}
|
||||
$code2 = {{ 0F B6 ?? 33 ?? C1 ?? ?? }}
|
||||
{hash_line}
|
||||
condition:
|
||||
// Cheap prefilter then require multiple independent anchors.
|
||||
uint16(0) == 0x5A4D and filesize < 5MB and
|
||||
(2 of ($code*)){hash_cond}
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Scaffold a YARA rule")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("scaffold")
|
||||
p.add_argument("--name", required=True)
|
||||
p.add_argument("--hash", dest="api_hash", default=None,
|
||||
help="optional API-hash constant, e.g. 0xABCDEF12")
|
||||
p.add_argument("--author", default="analyst-ai-pack")
|
||||
args = parser.parse_args(argv)
|
||||
print(scaffold(args.name, args.api_hash, args.author))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user