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,88 @@
|
||||
---
|
||||
name: identifying-cryptographic-routines-in-binaries
|
||||
description: 'Identifies cryptographic algorithms embedded in a binary by scanning for well-known
|
||||
constants and tables (AES S-box, SHA-256/MD5 init constants, ChaCha sigma, CRC32 table, base64
|
||||
alphabet). Activates for requests to identify crypto in a binary, find AES/SHA constants, or
|
||||
detect which encryption algorithm a sample uses.'
|
||||
domain: cybersecurity
|
||||
subdomain: reverse-engineering
|
||||
tags:
|
||||
- reverse-engineering
|
||||
- cryptography
|
||||
- constants
|
||||
- aes
|
||||
- detection
|
||||
version: 1.0.0
|
||||
author: analyst-ai-pack
|
||||
license: Apache-2.0
|
||||
mitre_attack:
|
||||
- T1027
|
||||
- T1486
|
||||
- T1573
|
||||
d3fend:
|
||||
- D3-SDA
|
||||
- D3-DA
|
||||
references:
|
||||
- 'FIPS 197 (AES) S-box — https://csrc.nist.gov/publications/detail/fips/197/final'
|
||||
- 'FIPS 180-4 (SHA-2) constants — https://csrc.nist.gov/publications/detail/fips/180/4/final'
|
||||
---
|
||||
|
||||
# Identifying Cryptographic Routines in Binaries
|
||||
|
||||
## When to Use
|
||||
|
||||
- You need to determine which cryptographic algorithms a sample implements (ransomware crypto, C2
|
||||
encryption, config protection) by locating constant tables.
|
||||
- You want fast triage before manually reversing the crypto routine.
|
||||
|
||||
**Do not use** constant detection as proof of a specific mode/usage — it identifies the primitive,
|
||||
not how it is applied. This skill reads the binary statically and executes nothing.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- The binary (read inertly).
|
||||
|
||||
## Safety & Handling
|
||||
|
||||
- Read bytes statically; treat the sample as malicious data.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Scan for crypto constants
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py scan sample.bin
|
||||
```
|
||||
|
||||
Searches for AES S-box/Te tables, SHA-256 H/K init constants, MD5 init, ChaCha/Salsa `expand
|
||||
32-byte k` sigma, the CRC32 polynomial table, and the base64 alphabet, reporting offsets.
|
||||
|
||||
### Step 2: Corroborate with imports/strings
|
||||
|
||||
Pair constant hits with crypto API imports (`CryptDecrypt`, `BCryptEncrypt`, `EVP_*`) or library
|
||||
strings to confirm.
|
||||
|
||||
### Step 3: Locate the routine
|
||||
|
||||
Use the constant offset to find the referencing function for deeper reversing/key extraction.
|
||||
|
||||
### Step 4: Document
|
||||
|
||||
Record which primitives are present and where, mapping to behavior (e.g., AES → file encryption).
|
||||
|
||||
## Validation
|
||||
|
||||
- Each hit references a real, named constant table (not a coincidental byte run).
|
||||
- Detected primitives are corroborated by imports/strings where possible.
|
||||
- Offsets point into the binary and can be navigated in a disassembler.
|
||||
|
||||
## Pitfalls
|
||||
|
||||
- Statically linked crypto libraries adding constants unused by the malware's logic.
|
||||
- Custom/modified S-boxes evading exact-match detection.
|
||||
- Assuming AES presence implies ransomware — corroborate with behavior.
|
||||
|
||||
## References
|
||||
|
||||
- See [`references/api-reference.md`](references/api-reference.md) for the scanner.
|
||||
- FIPS 197 and FIPS 180-4 constant references (linked in frontmatter).
|
||||
@@ -0,0 +1,40 @@
|
||||
# API Reference: Identifying Cryptographic Routines in Binaries
|
||||
|
||||
## Overview
|
||||
|
||||
`scripts/analyst.py` scans a binary for well-known cryptographic constants — the AES S-box, SHA-256
|
||||
init values (LE/BE), MD5 init constants, ChaCha/Salsa sigma strings, a CRC32 table prefix, and the
|
||||
base64 alphabet — and reports their offsets. It reads bytes statically and executes nothing.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| (stdlib) | — | struct, JSON, file IO |
|
||||
|
||||
## Core functions
|
||||
|
||||
### `scan(path)`
|
||||
Returns the set of detected algorithms and per-constant offset hits.
|
||||
|
||||
### `_crc32_table_prefix()`
|
||||
Computes the first entries of the standard `0xEDB88320` CRC32 table to anchor detection.
|
||||
|
||||
## Constants anchored
|
||||
|
||||
- AES S-box first 16 bytes (`0x63 0x7c 0x77 0x7b ...`).
|
||||
- SHA-256 H0..H7; MD5 A/B/C/D init words.
|
||||
- `expand 32-byte k` (ChaCha) / `expand 16-byte k` (Salsa20).
|
||||
- Base64 standard alphabet.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python scripts/analyst.py scan sample.bin
|
||||
```
|
||||
|
||||
## Sources
|
||||
|
||||
- FIPS 197 (AES, S-box) — https://csrc.nist.gov/publications/detail/fips/197/final
|
||||
- FIPS 180-4 (SHA-2 init/round constants) — https://csrc.nist.gov/publications/detail/fips/180/4/final
|
||||
- RFC 8439 (ChaCha20 `expand 32-byte k` constant); CRC-32 polynomial 0xEDB88320 (ITU-T V.42)
|
||||
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Scan a binary for well-known cryptographic constants and tables.
|
||||
|
||||
Detects the AES S-box, SHA-256 init/round constants, MD5 init constants, ChaCha/Salsa sigma,
|
||||
the CRC32 (0xEDB88320) table, and the base64 alphabet by exact byte-pattern match, reporting
|
||||
offsets. Reads bytes statically; executes nothing.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import struct
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# AES S-box (FIPS 197) first 16 bytes are a stable, distinctive anchor.
|
||||
AES_SBOX_PREFIX = bytes([
|
||||
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
|
||||
0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
||||
])
|
||||
|
||||
# SHA-256 initial hash values H0..H7 (FIPS 180-4), little- and big-endian.
|
||||
SHA256_H = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]
|
||||
|
||||
# MD5 init constants A,B,C,D.
|
||||
MD5_INIT = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]
|
||||
|
||||
CHACHA_SIGMA = b"expand 32-byte k"
|
||||
SALSA_SIGMA16 = b"expand 16-byte k"
|
||||
BASE64_ALPHABET = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
|
||||
|
||||
def _ints_le(values: list[int]) -> bytes:
|
||||
return b"".join(struct.pack("<I", v) for v in values)
|
||||
|
||||
|
||||
def _ints_be(values: list[int]) -> bytes:
|
||||
return b"".join(struct.pack(">I", v) for v in values)
|
||||
|
||||
|
||||
def _crc32_table_prefix() -> bytes:
|
||||
poly = 0xEDB88320
|
||||
table = []
|
||||
for n in range(8): # first 8 entries are enough to anchor
|
||||
c = n
|
||||
for _ in range(8):
|
||||
c = (poly ^ (c >> 1)) if (c & 1) else (c >> 1)
|
||||
table.append(c)
|
||||
return _ints_le(table)
|
||||
|
||||
|
||||
def _find_all(data: bytes, needle: bytes, label: str, limit: int = 10) -> list[dict]:
|
||||
hits = []
|
||||
start = 0
|
||||
while len(hits) < limit:
|
||||
idx = data.find(needle, start)
|
||||
if idx == -1:
|
||||
break
|
||||
hits.append({"algorithm": label, "offset": idx})
|
||||
start = idx + 1
|
||||
return hits
|
||||
|
||||
|
||||
def scan(path: str) -> dict:
|
||||
data = Path(path).read_bytes()
|
||||
findings: list[dict] = []
|
||||
findings += _find_all(data, AES_SBOX_PREFIX, "AES S-box")
|
||||
findings += _find_all(data, _ints_le(SHA256_H), "SHA-256 init (LE)")
|
||||
findings += _find_all(data, _ints_be(SHA256_H), "SHA-256 init (BE)")
|
||||
findings += _find_all(data, _ints_le(MD5_INIT), "MD5 init (LE)")
|
||||
findings += _find_all(data, _crc32_table_prefix(), "CRC32 table")
|
||||
findings += _find_all(data, CHACHA_SIGMA, "ChaCha sigma")
|
||||
findings += _find_all(data, SALSA_SIGMA16, "Salsa20 sigma (16)")
|
||||
findings += _find_all(data, BASE64_ALPHABET, "Base64 alphabet")
|
||||
algos = sorted({f["algorithm"].split(" (")[0] for f in findings})
|
||||
return {"algorithms_detected": algos, "constant_hits": findings}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Identify crypto constants in a binary")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("scan")
|
||||
p.add_argument("file")
|
||||
args = parser.parse_args(argv)
|
||||
print(json.dumps(scan(args.file), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user