@anonympins/fingerprint 0.7.4 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/public/anonympins-bot-mitigation-pow.zip +0 -0
- package/src/js/dynamic-wasm.js +218 -218
- package/src/js/fingerprint.client.js +1784 -1738
- package/src/js/fingerprint.client.obfuscated.js +1 -1
- package/src/js/fingerprint.js +489 -99
- package/src/js/fingerprint.utils.js +252 -2
- package/src/js/pow.worker.js +2 -2
- package/src/js/tests/cross_parity.test.js +3 -3
- package/src/js/tests/dns-circuit-breaker.test.js +6 -6
- package/src/js/tests/dynamic-wasm.test.js +77 -77
- package/src/js/tests/fingerprint.builder.test.js +76 -78
- package/src/js/tests/fingerprint.client.init.test.js +78 -0
- package/src/js/tests/fingerprint.client.test.js +16 -17
- package/src/js/tests/fingerprint.test.js +37 -12
- package/src/js/tests/ja3AnomalyDetector.test.js +135 -136
- package/src/js/tests/library.test.js +94 -95
- package/src/js/tests/obfuscation.test.js +8 -8
- package/src/js/tests/quicFingerprint.test.js +47 -0
- package/src/js/tests/tcpFingerprint.test.js +4 -4
- package/src/js/tests/threatIntel.test.js +40 -1
- package/src/js/upow-model-task.js +155 -112
- package/src/php/Challenge/ChallengeUtils.php +392 -86
- package/src/php/Config/SecurityProfiles.php +79 -61
- package/src/php/DirectFingerprint.php +55 -8
- package/src/php/FingerprintBuilder.php +13 -13
- package/src/php/FingerprintClient.php +6 -6
- package/src/php/FingerprintEngine.php +2122 -1690
- package/src/php/Ja3AnomalyDetector.php +22 -22
- package/src/php/Optimization/FunctionRegistry.php +4 -4
- package/src/php/Optimization/Optimization.php +6 -6
- package/src/php/RequestContext.php +11 -11
- package/src/php/Store/InMemoryStore.php +96 -66
- package/src/php/Store/MongoDbStore.php +2 -2
- package/src/php/Store/RedisStore.php +1 -1
- package/src/php/Store/StoreManager.php +48 -35
- package/src/php/Tests/ChallengeUtilsTest.php +31 -0
- package/src/php/Tests/DnsCircuitBreakerTest.php +105 -105
- package/src/php/Tests/FingerprintClientTest.php +70 -70
- package/src/php/Tests/PowTest.php +37 -39
- package/src/php/Tests/QuicFingerprintTest.php +115 -53
- package/src/php/Tests/ThreatIntelTest.php +34 -0
- package/src/php/Utils/BigInt.php +202 -202
- package/src/php/Utils/BlockList.php +13 -13
- package/src/php/Utils/Logger.php +19 -1
- package/src/php/Utils/MaliciousPatterns.php +6 -6
- package/src/php/Utils/MetricsManager.php +209 -209
- package/src/php/Utils/RequestUtils.php +362 -1
- package/src/php/Utils/TLSClientHelloParser.php +385 -369
- package/src/php/WordPress/WpDbStore.php +7 -7
- package/src/php/WordPress/anonympins-bot-mitigation-pow.php +1121 -0
- package/src/php/WordPress/fingerprint-anti-bot.php +0 -559
- package/src/php/WordPress/languages/anonympins-bot-mitigation-pow-fr_FR.mo +0 -0
- package/src/php/WordPress/languages/anonympins-bot-mitigation-pow-fr_FR.po +232 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.mo +0 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.po +297 -261
- package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.mo +0 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.po +348 -267
- package/src/php/WordPress/package.php +80 -101
- package/src/php/WordPress/readme.txt +21 -5
- package/src/php/bin/auto-tune.php +1 -1
- package/public/fingerprint-anti-bot.zip +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,62 @@
|
|
|
1
|
+
## Version 0.8.0
|
|
2
|
+
|
|
3
|
+
- 🎟️ **Private Access Tokens (PAT / Privacy Pass) Support (RFC 9505, RFC 9577, RFC 9578)**:
|
|
4
|
+
- **Zero-Friction Cryptographic Bypass**: Integrated native validation for Privacy Pass tokens extracted from `Authorization: PrivateToken` and `Sec-Private-State-Token` headers. Clients presenting valid tokens obtain an immediate zero-friction bypass (`action: next`, `score: 0.0`) without executing CPU/Memory challenges or CAPTCHAs.
|
|
5
|
+
- **Multi-Algorithm Token Verification**: Supports Blind RSA (types `0x0001` and `0x0002` using RSA-PSS SHA-384 / SHA-256 and PKCS#1 v1.5 padding) as well as VOPRF / Ed25519 (type `0x0003`).
|
|
6
|
+
- **Anti-Replay Nonce Tracking**: Verified token nonces are indexed in the persistent datastore (`pat-nonce:{nonce}`) with configurable expiration (`pat.nonceTtl`, default 24h) to prevent token reuse.
|
|
7
|
+
|
|
8
|
+
- 🔒 **TLS 1.3 Encrypted Client Hello (ECH) & RFC 9460 Validation**:
|
|
9
|
+
- Added deep inspection for ECH presence and Outer SNI discrepancies (`x-ech-outer-sni`, `x-ech-present`, `x-ech-expected`).
|
|
10
|
+
- Flags client connections where modern browsers (Chrome $\ge 119$, Firefox $\ge 118$) fail to negotiate ECH when expected on HTTPS endpoints.
|
|
11
|
+
|
|
12
|
+
- 🌐 **HTTP/2 & QUIC / HTTP/3 Protocol Conformance (RFC 7540, RFC 9114, RFC 9218)**:
|
|
13
|
+
- **Hop-by-Hop Header Prohibition**: Enforced RFC 7540 and RFC 9114 constraints by heavily penalizing forbidden hop-by-hop headers (`Connection`, `Keep-Alive`, `Proxy-Connection`) on HTTP/2 and HTTP/3 multiplexed streams.
|
|
14
|
+
- **Extensible Prioritization Scheme (RFC 9218)**: Validated priority urgency bounds ($u \in [0, 7]$) and incremental flags (`i`), penalizing malformed or out-of-spec client priority frames.
|
|
15
|
+
- **HTTP/2 Stream Tree & Compression Analysis**: Added detection for stream self-dependency loops (`streamId === depStreamId`), non-browser priority frame distributions, and dynamic HPACK/QPACK table zero-entry anomalies under repeated requests.
|
|
16
|
+
|
|
17
|
+
- 📡 **Passive MTU & Transport Tunnel Detection (`mtuAnomalyScore`)**:
|
|
18
|
+
- Analyzes TCP SYN parameters, Path MTU, and Don't Fragment (`DF`) flags across IPv4 and IPv6 to detect WireGuard, OpenVPN, GRE encapsulation, and residential proxy backconnects.
|
|
19
|
+
- **Dynamic Weight Amplification**: When an MTU anomaly indicates network tunneling ($\text{mtuAnomalyScore} > 50$), the engine dynamically amplifies downstream weights (`tlsSpoofingScore`, `crossLayerInconsistencyScore`, `clientHintsInconsistencyScore`, `behaviorScore`, `requestPatternScore`).
|
|
20
|
+
|
|
21
|
+
- 🛠️ **WordPress Plugin: Sandbox Mode & Live Telemetry Feed**:
|
|
22
|
+
- **Dedicated Sandbox / Test Mode**: Configurable non-blocking Audit-Only mode, targeted IP whitelist filter, and diagnostic HTTP headers (`X-Fingerprint-Sandbox`, `X-Fingerprint-Score`).
|
|
23
|
+
- **Real-Time Observability**: Implemented a Server-Sent Events (SSE) streaming route (`/wp-json/fingerprint/v1/sandbox/sse`) and a REST polling endpoint (`/wp-json/fingerprint/v1/sandbox/telemetry`) to monitor challenged visitors and suspicion vector breakdowns live.
|
|
24
|
+
- **Client-Side Event Injection**: Injects `window.__FINGERPRINT_VECTOR__` and dispatches `fingerprint:suspicion` DOM CustomEvents when sandbox mode or administrative sessions are active.
|
|
25
|
+
|
|
26
|
+
- 🌍 **Code Standardization & Parity**:
|
|
27
|
+
- **Python Engine Enhancements**: Merged modular architecture improvements (`feature/python_classes`) for parity with Node.js and PHP.
|
|
28
|
+
- **Java Testing**: Added comprehensive test coverage for RFC 9578 Private Access Token extraction, parsing, and anti-replay verification (`PatTest.java`).
|
|
29
|
+
- **Documentation & Localization**: Standardized code comments into English across modules and updated French/German translations in compliance with WordPress guidelines.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Version 0.7.5
|
|
34
|
+
|
|
35
|
+
- 🛡️ **Differential Privacy on Federated Threat Intelligence**:
|
|
36
|
+
- **Laplacian Noise on Federation Timestamps ($\text{Laplace}(0, b)$)**: Added mathematical perturbation to exact event timestamps in federated ban exchanges (`share_threat_intel`) parameterized by sensitivity $\Delta t = 5000\text{ms}$ and privacy budget $\epsilon$ (`differentialPrivacy.epsilon`). Timestamps are bounded within a safe $\pm 60\text{s}$ window to satisfy anti-replay checks while completely preventing malicious or compromised federation nodes from reconstructing inter-domain browsing trajectories via millisecond timing correlation.
|
|
37
|
+
- **Randomized Response Decoy Injection (Chaffing)**: Implemented randomized response logic where nodes inject and broadcast synthetic ZKP public keys generated uniformly in $\mathbb{Z}_p$ with probability $p = 1 / (1 + e^{\epsilon})$ (or customizable `dummyRate`). Due to the negligible collision probability of 256-bit keys ($\approx 2^{-256}$), decoys never reach the collaborative consensus threshold ($K \ge 3$), guaranteeing mathematical plausible deniability without causing false positives.
|
|
38
|
+
- **Multi-Runtime Parity**: Fully implemented across JavaScript (`fingerprint.js`), PHP (`FingerprintEngine.php`), Python (`engine.py`), and Java (`FingerprintEngine.java`, `FingerprintProperties.java`).
|
|
39
|
+
|
|
40
|
+
- 🌐 **Network Layer Anomaly Detection**:
|
|
41
|
+
- **MTU Anomaly Scoring (`mtuAnomalyScore`)**: Introduced `mtuAnomalyScore` to the suspicion vector and configuration weights across all runtimes. Evaluates passive TCP SYN options (specifically MSS — Maximum Segment Size) to detect anomalous MTU values typical of VPN/GRE tunneling, WireGuard encapsulation, or residential proxy backconnect chains attempting to masquerade as standard domestic browser connections.
|
|
42
|
+
|
|
43
|
+
- ♿ **Accessibility (a11y) & Invisible Honeypot Hardening**:
|
|
44
|
+
- **Screen-Reader Compliant Honeypot Fields**: Hardened dynamic DOM honeypot generation within `FingerprintClient` with `aria-hidden="true"`, `tabindex="-1"`, and `autocomplete="off"` attributes combined with defensive offscreen CSS positioning (`position: absolute; left: -9999px; transform: scale(0); opacity: 0; pointer-events: none;`). Ensures assistive technologies (NVDA, JAWS, VoiceOver) and browser autofill routines ignore honeypot inputs for visually impaired users while maintaining a 100% trap catch rate against automated scrapers.
|
|
45
|
+
|
|
46
|
+
- 🤖 **Crawler Whitelist Updates & Multi-Language Parity**:
|
|
47
|
+
- **Meta / Facebook Whitelist Addition**: Added dedicated IP subnets and CIDR definitions for Meta and Facebook crawlers (`facebookexternalhit`, `31.13.64.0/18`, `66.220.144.0/20`, `69.63.176.0/20`, `157.240.0.0/16`) to guarantee smooth link preview generation and indexing without challenge interruptions.
|
|
48
|
+
- **Cross-Platform Whitelist Synchronization**: Standardized crawler allowlists and DNS verification workflows (Googlebot, Bingbot, Yandex, Facebook) across all supported backend engines (Node.js, PHP, Python, and Java).
|
|
49
|
+
|
|
50
|
+
- 🔧 **Core Engine Refinements**:
|
|
51
|
+
- **`fingerprint.js` Updates**: Synchronized client-side metric collectors, refined behavioral input tracking, and plugged federation broadcast hooks directly into the core security pipeline.
|
|
52
|
+
- **Java Configuration Enhancements**: Added missing getters/setters and mapped configuration fields (`quicAnomalyScore`, `virtualizationScore`, `differentialPrivacy`, `federatedPeers`) in `FingerprintProperties.java`.
|
|
53
|
+
- **PHP Store Lifecycle**: Resolved initialization order edge cases for store instances during request processing in `FingerprintEngine.php`.
|
|
54
|
+
|
|
55
|
+
- 📖 **Documentation**:
|
|
56
|
+
- **Configuration Options Reference (`full_options.md`)**: Documented all new settings, including `mtuAnomalyScore`, `differentialPrivacy` block (`enabled`, `epsilon`, `dummyRate`), and federated threat sharing parameters.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
1
60
|
## Version 0.7.4
|
|
2
61
|
|
|
3
62
|
- 🛡️ **Security & Rate Limiting**:
|
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anonympins/fingerprint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Advanced anti-bot library for Node.js using multi-layer fingerprinting (JA3, client-side, headers), behavioral analysis, and adaptive Proof-of-Work (PoW) challenges to mitigate scraping, scalping, and automated threats.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
Binary file
|
package/src/js/dynamic-wasm.js
CHANGED
|
@@ -1,219 +1,219 @@
|
|
|
1
|
-
import crypto from "node:crypto";
|
|
2
|
-
|
|
3
|
-
function encodeULEB128(val) {
|
|
4
|
-
const bytes = [];
|
|
5
|
-
let num = val >>> 0;
|
|
6
|
-
do {
|
|
7
|
-
let byte = num & 0x7f;
|
|
8
|
-
num >>>= 7;
|
|
9
|
-
if (num !== 0) byte |= 0x80;
|
|
10
|
-
bytes.push(byte);
|
|
11
|
-
} while (num !== 0);
|
|
12
|
-
return bytes;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function encodeSLEB128(val) {
|
|
16
|
-
const bytes = [];
|
|
17
|
-
let num = val | 0;
|
|
18
|
-
while (true) {
|
|
19
|
-
let byte = num & 0x7f;
|
|
20
|
-
num >>= 7;
|
|
21
|
-
if ((num === 0 && (byte & 0x40) === 0) || (num === -1 && (byte & 0x40) !== 0)) {
|
|
22
|
-
bytes.push(byte);
|
|
23
|
-
break;
|
|
24
|
-
}
|
|
25
|
-
bytes.push(byte | 0x80);
|
|
26
|
-
}
|
|
27
|
-
return bytes;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function getRandomNonTrivialOpcodes(tempLocal) {
|
|
31
|
-
const insts = [];
|
|
32
|
-
const ops = [
|
|
33
|
-
// local.get tempLocal, i32.const rand, rotl, local.set tempLocal
|
|
34
|
-
() => [0x20, tempLocal, 0x41, ...encodeSLEB128(crypto.randomInt(1, 32)), 0x77, 0x21, tempLocal],
|
|
35
|
-
// local.get tempLocal, i32.const rand, rotr, local.set tempLocal
|
|
36
|
-
() => [0x20, tempLocal, 0x41, ...encodeSLEB128(crypto.randomInt(1, 32)), 0x78, 0x21, tempLocal],
|
|
37
|
-
// local.get tempLocal, i32.const rand, xor, local.set tempLocal
|
|
38
|
-
() => [0x20, tempLocal, 0x41, ...encodeSLEB128(crypto.randomInt(0, 10000)), 0x73, 0x21, tempLocal],
|
|
39
|
-
// local.get tempLocal, popcnt, i32.const rand, mul, local.set tempLocal
|
|
40
|
-
() => [0x20, tempLocal, 0x69, 0x41, ...encodeSLEB128(crypto.randomInt(1, 1001)), 0x6c, 0x21, tempLocal],
|
|
41
|
-
// local.get tempLocal, clz, local.get tempLocal, ctz, add, local.set tempLocal
|
|
42
|
-
() => [0x20, tempLocal, 0x67, 0x20, tempLocal, 0x68, 0x6a, 0x21, tempLocal]
|
|
43
|
-
];
|
|
44
|
-
const count = crypto.randomInt(2, 5);
|
|
45
|
-
for (let i = 0; i < count; i++) {
|
|
46
|
-
const op = ops[crypto.randomInt(0, ops.length)];
|
|
47
|
-
insts.push(...op());
|
|
48
|
-
}
|
|
49
|
-
return insts;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function generatePolymorphicInstructions(stateLocal = 4, tempLocal = 5) {
|
|
53
|
-
const insts = [];
|
|
54
|
-
|
|
55
|
-
// Initialize tempLocal with a random value
|
|
56
|
-
const initVal = crypto.randomInt(-500, 500);
|
|
57
|
-
insts.push(0x41, ...encodeSLEB128(initVal), 0x21, tempLocal);
|
|
58
|
-
|
|
59
|
-
// Initialize stateLocal to 0
|
|
60
|
-
insts.push(0x41, ...encodeSLEB128(0), 0x21, stateLocal);
|
|
61
|
-
|
|
62
|
-
// Loop & Block for state machine
|
|
63
|
-
insts.push(0x03, 0x40); // loop
|
|
64
|
-
insts.push(0x02, 0x40); // block
|
|
65
|
-
|
|
66
|
-
for (let state = 0; state < 3; state++) {
|
|
67
|
-
insts.push(0x20, stateLocal, 0x41, ...encodeSLEB128(state), 0x46); // state == expected
|
|
68
|
-
insts.push(0x04, 0x40); // if
|
|
69
|
-
insts.push(...getRandomNonTrivialOpcodes(tempLocal));
|
|
70
|
-
insts.push(0x41, ...encodeSLEB128(state + 1), 0x21, stateLocal); // transit to state + 1
|
|
71
|
-
insts.push(0x0c, ...encodeULEB128(2)); // br 2 (targets loop start)
|
|
72
|
-
insts.push(0x0b); // end if
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Fallthrough / Default break (targets block depth 0, which exits the loop)
|
|
76
|
-
insts.push(0x0c, ...encodeULEB128(0));
|
|
77
|
-
|
|
78
|
-
// end block
|
|
79
|
-
insts.push(0x0b);
|
|
80
|
-
// end loop
|
|
81
|
-
insts.push(0x0b);
|
|
82
|
-
|
|
83
|
-
return insts;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export class DynamicWasmGenerator {
|
|
87
|
-
/**
|
|
88
|
-
* Generates a unique polymorphic WebAssembly module containing a custom hash function
|
|
89
|
-
* with randomized constants and control flow variables.
|
|
90
|
-
* @param {object} constants - Custom seed, multiplier and adder.
|
|
91
|
-
* @returns {Buffer} Valid WebAssembly binary buffer.
|
|
92
|
-
*/
|
|
93
|
-
static generate(constants) {
|
|
94
|
-
const { seed, multiplier, adder } = constants;
|
|
95
|
-
|
|
96
|
-
const preLoopPoly = generatePolymorphicInstructions(4, 5);
|
|
97
|
-
const midLoopPoly = generatePolymorphicInstructions(4, 5);
|
|
98
|
-
|
|
99
|
-
const inst = [
|
|
100
|
-
0x01, 0x06, 0x7f, // Locals: 1 entry of 6 locals of type i32
|
|
101
|
-
...preLoopPoly,
|
|
102
|
-
// h = seed
|
|
103
|
-
0x41, ...encodeSLEB128(seed),
|
|
104
|
-
0x21, 0x03,
|
|
105
|
-
// i = 0
|
|
106
|
-
0x41, 0x00,
|
|
107
|
-
0x21, 0x02,
|
|
108
|
-
|
|
109
|
-
// block
|
|
110
|
-
0x02, 0x40,
|
|
111
|
-
// loop
|
|
112
|
-
0x03, 0x40,
|
|
113
|
-
|
|
114
|
-
// if i >= len break
|
|
115
|
-
0x20, 0x02,
|
|
116
|
-
0x20, 0x01,
|
|
117
|
-
0x4f,
|
|
118
|
-
0x0d, 0x01,
|
|
119
|
-
|
|
120
|
-
// byte = load8_u(ptr + i)
|
|
121
|
-
0x20, 0x00,
|
|
122
|
-
0x20, 0x02,
|
|
123
|
-
0x6a,
|
|
124
|
-
0x2d, 0x00, 0x00,
|
|
125
|
-
|
|
126
|
-
// h = h ^ byte
|
|
127
|
-
0x20, 0x03,
|
|
128
|
-
0x73,
|
|
129
|
-
|
|
130
|
-
// h = h * multiplier
|
|
131
|
-
0x41, ...encodeSLEB128(multiplier),
|
|
132
|
-
0x6c,
|
|
133
|
-
|
|
134
|
-
// h = h + adder
|
|
135
|
-
0x41, ...encodeSLEB128(adder),
|
|
136
|
-
0x6a,
|
|
137
|
-
|
|
138
|
-
// local.set 3
|
|
139
|
-
0x21, 0x03,
|
|
140
|
-
...midLoopPoly,
|
|
141
|
-
|
|
142
|
-
// i = i + 1
|
|
143
|
-
0x20, 0x02,
|
|
144
|
-
0x41, 0x01,
|
|
145
|
-
0x6a,
|
|
146
|
-
0x21, 0x02,
|
|
147
|
-
|
|
148
|
-
// br 0
|
|
149
|
-
0x0c, 0x00,
|
|
150
|
-
|
|
151
|
-
0x0b, // end loop
|
|
152
|
-
0x0b, // end block
|
|
153
|
-
|
|
154
|
-
// return h
|
|
155
|
-
0x20, 0x03,
|
|
156
|
-
0x0b // end function
|
|
157
|
-
];
|
|
158
|
-
|
|
159
|
-
const funcBody = [
|
|
160
|
-
...encodeULEB128(inst.length),
|
|
161
|
-
...inst
|
|
162
|
-
];
|
|
163
|
-
|
|
164
|
-
const codeSectionPayload = [
|
|
165
|
-
...encodeULEB128(1),
|
|
166
|
-
...funcBody
|
|
167
|
-
];
|
|
168
|
-
|
|
169
|
-
const codeSection = [
|
|
170
|
-
0x0a,
|
|
171
|
-
...encodeULEB128(codeSectionPayload.length),
|
|
172
|
-
...codeSectionPayload
|
|
173
|
-
];
|
|
174
|
-
|
|
175
|
-
const typeSection = [
|
|
176
|
-
0x01, 0x07, 0x01, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f
|
|
177
|
-
];
|
|
178
|
-
|
|
179
|
-
const funcSection = [
|
|
180
|
-
0x03, 0x02, 0x01, 0x00
|
|
181
|
-
];
|
|
182
|
-
|
|
183
|
-
const memSection = [
|
|
184
|
-
0x05, 0x03, 0x01, 0x00, 0x01
|
|
185
|
-
];
|
|
186
|
-
|
|
187
|
-
const exportSection = [
|
|
188
|
-
0x07, 0x11, 0x02, 0x04, 0x68, 0x61, 0x73, 0x68, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00
|
|
189
|
-
];
|
|
190
|
-
|
|
191
|
-
const wasm = [
|
|
192
|
-
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
|
|
193
|
-
...typeSection,
|
|
194
|
-
...funcSection,
|
|
195
|
-
...memSection,
|
|
196
|
-
...exportSection,
|
|
197
|
-
...codeSection
|
|
198
|
-
];
|
|
199
|
-
|
|
200
|
-
return Buffer.from(wasm);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Pure JavaScript fallback equivalent of the custom polymorphic hash.
|
|
205
|
-
* @param {string} str Input string.
|
|
206
|
-
* @param {object} constants Parameters.
|
|
207
|
-
* @returns {number} 32-bit unsigned integer hash.
|
|
208
|
-
*/
|
|
209
|
-
static hashJs(str, constants) {
|
|
210
|
-
const { seed, multiplier, adder } = constants;
|
|
211
|
-
let h = seed | 0;
|
|
212
|
-
for (let i = 0; i < str.length; i++) {
|
|
213
|
-
const byte = str.charCodeAt(i) & 0xff;
|
|
214
|
-
h = h ^ byte;
|
|
215
|
-
h = Math.imul(h, multiplier) + adder;
|
|
216
|
-
}
|
|
217
|
-
return h >>> 0;
|
|
218
|
-
}
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
|
|
3
|
+
function encodeULEB128(val) {
|
|
4
|
+
const bytes = [];
|
|
5
|
+
let num = val >>> 0;
|
|
6
|
+
do {
|
|
7
|
+
let byte = num & 0x7f;
|
|
8
|
+
num >>>= 7;
|
|
9
|
+
if (num !== 0) byte |= 0x80;
|
|
10
|
+
bytes.push(byte);
|
|
11
|
+
} while (num !== 0);
|
|
12
|
+
return bytes;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function encodeSLEB128(val) {
|
|
16
|
+
const bytes = [];
|
|
17
|
+
let num = val | 0;
|
|
18
|
+
while (true) {
|
|
19
|
+
let byte = num & 0x7f;
|
|
20
|
+
num >>= 7;
|
|
21
|
+
if ((num === 0 && (byte & 0x40) === 0) || (num === -1 && (byte & 0x40) !== 0)) {
|
|
22
|
+
bytes.push(byte);
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
bytes.push(byte | 0x80);
|
|
26
|
+
}
|
|
27
|
+
return bytes;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getRandomNonTrivialOpcodes(tempLocal) {
|
|
31
|
+
const insts = [];
|
|
32
|
+
const ops = [
|
|
33
|
+
// local.get tempLocal, i32.const rand, rotl, local.set tempLocal
|
|
34
|
+
() => [0x20, tempLocal, 0x41, ...encodeSLEB128(crypto.randomInt(1, 32)), 0x77, 0x21, tempLocal],
|
|
35
|
+
// local.get tempLocal, i32.const rand, rotr, local.set tempLocal
|
|
36
|
+
() => [0x20, tempLocal, 0x41, ...encodeSLEB128(crypto.randomInt(1, 32)), 0x78, 0x21, tempLocal],
|
|
37
|
+
// local.get tempLocal, i32.const rand, xor, local.set tempLocal
|
|
38
|
+
() => [0x20, tempLocal, 0x41, ...encodeSLEB128(crypto.randomInt(0, 10000)), 0x73, 0x21, tempLocal],
|
|
39
|
+
// local.get tempLocal, popcnt, i32.const rand, mul, local.set tempLocal
|
|
40
|
+
() => [0x20, tempLocal, 0x69, 0x41, ...encodeSLEB128(crypto.randomInt(1, 1001)), 0x6c, 0x21, tempLocal],
|
|
41
|
+
// local.get tempLocal, clz, local.get tempLocal, ctz, add, local.set tempLocal
|
|
42
|
+
() => [0x20, tempLocal, 0x67, 0x20, tempLocal, 0x68, 0x6a, 0x21, tempLocal]
|
|
43
|
+
];
|
|
44
|
+
const count = crypto.randomInt(2, 5);
|
|
45
|
+
for (let i = 0; i < count; i++) {
|
|
46
|
+
const op = ops[crypto.randomInt(0, ops.length)];
|
|
47
|
+
insts.push(...op());
|
|
48
|
+
}
|
|
49
|
+
return insts;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function generatePolymorphicInstructions(stateLocal = 4, tempLocal = 5) {
|
|
53
|
+
const insts = [];
|
|
54
|
+
|
|
55
|
+
// Initialize tempLocal with a random value
|
|
56
|
+
const initVal = crypto.randomInt(-500, 500);
|
|
57
|
+
insts.push(0x41, ...encodeSLEB128(initVal), 0x21, tempLocal);
|
|
58
|
+
|
|
59
|
+
// Initialize stateLocal to 0
|
|
60
|
+
insts.push(0x41, ...encodeSLEB128(0), 0x21, stateLocal);
|
|
61
|
+
|
|
62
|
+
// Loop & Block for state machine
|
|
63
|
+
insts.push(0x03, 0x40); // loop
|
|
64
|
+
insts.push(0x02, 0x40); // block
|
|
65
|
+
|
|
66
|
+
for (let state = 0; state < 3; state++) {
|
|
67
|
+
insts.push(0x20, stateLocal, 0x41, ...encodeSLEB128(state), 0x46); // state == expected
|
|
68
|
+
insts.push(0x04, 0x40); // if
|
|
69
|
+
insts.push(...getRandomNonTrivialOpcodes(tempLocal));
|
|
70
|
+
insts.push(0x41, ...encodeSLEB128(state + 1), 0x21, stateLocal); // transit to state + 1
|
|
71
|
+
insts.push(0x0c, ...encodeULEB128(2)); // br 2 (targets loop start)
|
|
72
|
+
insts.push(0x0b); // end if
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Fallthrough / Default break (targets block depth 0, which exits the loop)
|
|
76
|
+
insts.push(0x0c, ...encodeULEB128(0));
|
|
77
|
+
|
|
78
|
+
// end block
|
|
79
|
+
insts.push(0x0b);
|
|
80
|
+
// end loop
|
|
81
|
+
insts.push(0x0b);
|
|
82
|
+
|
|
83
|
+
return insts;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export class DynamicWasmGenerator {
|
|
87
|
+
/**
|
|
88
|
+
* Generates a unique polymorphic WebAssembly module containing a custom hash function
|
|
89
|
+
* with randomized constants and control flow variables.
|
|
90
|
+
* @param {object} constants - Custom seed, multiplier and adder.
|
|
91
|
+
* @returns {Buffer} Valid WebAssembly binary buffer.
|
|
92
|
+
*/
|
|
93
|
+
static generate(constants) {
|
|
94
|
+
const { seed, multiplier, adder } = constants;
|
|
95
|
+
|
|
96
|
+
const preLoopPoly = generatePolymorphicInstructions(4, 5);
|
|
97
|
+
const midLoopPoly = generatePolymorphicInstructions(4, 5);
|
|
98
|
+
|
|
99
|
+
const inst = [
|
|
100
|
+
0x01, 0x06, 0x7f, // Locals: 1 entry of 6 locals of type i32
|
|
101
|
+
...preLoopPoly,
|
|
102
|
+
// h = seed
|
|
103
|
+
0x41, ...encodeSLEB128(seed),
|
|
104
|
+
0x21, 0x03,
|
|
105
|
+
// i = 0
|
|
106
|
+
0x41, 0x00,
|
|
107
|
+
0x21, 0x02,
|
|
108
|
+
|
|
109
|
+
// block
|
|
110
|
+
0x02, 0x40,
|
|
111
|
+
// loop
|
|
112
|
+
0x03, 0x40,
|
|
113
|
+
|
|
114
|
+
// if i >= len break
|
|
115
|
+
0x20, 0x02,
|
|
116
|
+
0x20, 0x01,
|
|
117
|
+
0x4f,
|
|
118
|
+
0x0d, 0x01,
|
|
119
|
+
|
|
120
|
+
// byte = load8_u(ptr + i)
|
|
121
|
+
0x20, 0x00,
|
|
122
|
+
0x20, 0x02,
|
|
123
|
+
0x6a,
|
|
124
|
+
0x2d, 0x00, 0x00,
|
|
125
|
+
|
|
126
|
+
// h = h ^ byte
|
|
127
|
+
0x20, 0x03,
|
|
128
|
+
0x73,
|
|
129
|
+
|
|
130
|
+
// h = h * multiplier
|
|
131
|
+
0x41, ...encodeSLEB128(multiplier),
|
|
132
|
+
0x6c,
|
|
133
|
+
|
|
134
|
+
// h = h + adder
|
|
135
|
+
0x41, ...encodeSLEB128(adder),
|
|
136
|
+
0x6a,
|
|
137
|
+
|
|
138
|
+
// local.set 3
|
|
139
|
+
0x21, 0x03,
|
|
140
|
+
...midLoopPoly,
|
|
141
|
+
|
|
142
|
+
// i = i + 1
|
|
143
|
+
0x20, 0x02,
|
|
144
|
+
0x41, 0x01,
|
|
145
|
+
0x6a,
|
|
146
|
+
0x21, 0x02,
|
|
147
|
+
|
|
148
|
+
// br 0
|
|
149
|
+
0x0c, 0x00,
|
|
150
|
+
|
|
151
|
+
0x0b, // end loop
|
|
152
|
+
0x0b, // end block
|
|
153
|
+
|
|
154
|
+
// return h
|
|
155
|
+
0x20, 0x03,
|
|
156
|
+
0x0b // end function
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
const funcBody = [
|
|
160
|
+
...encodeULEB128(inst.length),
|
|
161
|
+
...inst
|
|
162
|
+
];
|
|
163
|
+
|
|
164
|
+
const codeSectionPayload = [
|
|
165
|
+
...encodeULEB128(1),
|
|
166
|
+
...funcBody
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
const codeSection = [
|
|
170
|
+
0x0a,
|
|
171
|
+
...encodeULEB128(codeSectionPayload.length),
|
|
172
|
+
...codeSectionPayload
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
const typeSection = [
|
|
176
|
+
0x01, 0x07, 0x01, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
const funcSection = [
|
|
180
|
+
0x03, 0x02, 0x01, 0x00
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
const memSection = [
|
|
184
|
+
0x05, 0x03, 0x01, 0x00, 0x01
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
const exportSection = [
|
|
188
|
+
0x07, 0x11, 0x02, 0x04, 0x68, 0x61, 0x73, 0x68, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
const wasm = [
|
|
192
|
+
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
|
|
193
|
+
...typeSection,
|
|
194
|
+
...funcSection,
|
|
195
|
+
...memSection,
|
|
196
|
+
...exportSection,
|
|
197
|
+
...codeSection
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
return Buffer.from(wasm);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Pure JavaScript fallback equivalent of the custom polymorphic hash.
|
|
205
|
+
* @param {string} str Input string.
|
|
206
|
+
* @param {object} constants Parameters.
|
|
207
|
+
* @returns {number} 32-bit unsigned integer hash.
|
|
208
|
+
*/
|
|
209
|
+
static hashJs(str, constants) {
|
|
210
|
+
const { seed, multiplier, adder } = constants;
|
|
211
|
+
let h = seed | 0;
|
|
212
|
+
for (let i = 0; i < str.length; i++) {
|
|
213
|
+
const byte = str.charCodeAt(i) & 0xff;
|
|
214
|
+
h = h ^ byte;
|
|
215
|
+
h = Math.imul(h, multiplier) + adder;
|
|
216
|
+
}
|
|
217
|
+
return h >>> 0;
|
|
218
|
+
}
|
|
219
219
|
}
|