reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Profile a cryptominer's configuration from static strings.
|
||||
|
||||
Extracts stratum pool endpoints, wallet addresses, algorithm identifiers, miner family strings,
|
||||
and resource-control/stealth settings. Reads bytes statically, defangs pool endpoints, and
|
||||
executes nothing.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ASCII_RE = re.compile(rb"[\x20-\x7e]{4,}")
|
||||
RE_STRATUM = re.compile(r"stratum\+(?:tcp|ssl)://[^\s\"'<>]+", re.I)
|
||||
RE_MONERO = re.compile(r"\b4[0-9AB][0-9A-Za-z]{93}\b")
|
||||
RE_BTC = re.compile(r"\b(?:bc1[0-9a-z]{20,60}|[13][a-km-zA-HJ-NP-Z1-9]{25,34})\b")
|
||||
RE_ETH = re.compile(r"\b0x[a-fA-F0-9]{40}\b")
|
||||
ALGOS = ["rx/0", "rx/wow", "cn/r", "cn/2", "cn-heavy", "argon2", "kawpow", "ethash", "randomx",
|
||||
"cryptonight"]
|
||||
MINER_IDS = ["xmrig", "xmr-stak", "minerd", "cpuminer", "nbminer", "phoenixminer", "t-rex",
|
||||
"lolminer", "nanominer"]
|
||||
RESOURCE_KEYS = ["max-cpu-usage", "cpu-priority", "donate-level", "background", "max-threads-hint",
|
||||
"huge-pages", "idle", "watchdog"]
|
||||
|
||||
|
||||
def _strings(data: bytes) -> str:
|
||||
return "\n".join(m.decode("latin-1", "ignore") for m in ASCII_RE.findall(data))
|
||||
|
||||
|
||||
def _defang(s: str) -> str:
|
||||
return s.replace("stratum", "stratum-x").replace("://", "[://]").replace(".", "[.]")
|
||||
|
||||
|
||||
def profile(path: str) -> dict:
|
||||
blob = _strings(Path(path).read_bytes())
|
||||
low = blob.lower()
|
||||
pools = sorted({_defang(p) for p in RE_STRATUM.findall(blob)})
|
||||
wallets = {
|
||||
"monero": sorted(set(RE_MONERO.findall(blob)))[:10],
|
||||
"bitcoin": sorted(set(RE_BTC.findall(blob)))[:10],
|
||||
"ethereum": sorted(set(RE_ETH.findall(blob)))[:10],
|
||||
}
|
||||
wallets = {k: v for k, v in wallets.items() if v}
|
||||
algos = sorted({a for a in ALGOS if a in low})
|
||||
miners = sorted({m for m in MINER_IDS if m in low})
|
||||
resource = sorted({r for r in RESOURCE_KEYS if r in low})
|
||||
return {
|
||||
"pool_endpoints": pools,
|
||||
"wallet_addresses": wallets,
|
||||
"algorithms": algos,
|
||||
"miner_family": miners,
|
||||
"resource_controls": resource,
|
||||
"is_likely_cryptominer": bool(pools or (miners and algos)),
|
||||
}
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Profile a cryptominer")
|
||||
sub = parser.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("profile")
|
||||
p.add_argument("file")
|
||||
args = parser.parse_args(argv)
|
||||
print(json.dumps(profile(args.file), indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user