
PerimeterX के बाइटकोड VM के लिए रिवर्स इंजीनियरिंग टूलकिट, जिसमें CFG-आधारित डिस्सेम्बलर, 5-लेयर डिक्रिप्शन पाइपलाइन, ओपकोड टेबल पुनर्निर्माण, और बॉट डिटेक्शन फिंगरप्रिंटिंग पर सुरक्षा अनुसंधान के लिए स्टैक एमुलेशन क्लीनर शामिल है।
यह रिपॉजिटरी PerimeterX के auditor.js के रिवर्स इंजीनियरिंग का दस्तावेजीकरण करती है, जो एक बाइटकोड वर्चुअल मशीन है जो PX की बॉट डिटेक्शन पाइपलाइन में द्वितीयक फिंगरप्रिंटिंग परत के रूप में उपयोग होती है। यह विश्लेषण निम्नलिखित को कवर करता है:
नोट: यह रिपॉजिटरी केवल एक (स्थिर) VM संस्करण को कवर करती है और सुरक्षा अनुसंधान और विश्लेषण उद्देश्यों के लिए है। इसमें डायनामिक सॉल्वर या प्रोडक्शन सॉल्वर इम्प्लीमेंटेशन शामिल नहीं हैं।
गुरुवार, 2 अप्रैल 2026 को, PerimeterX ने अपनी बॉट डिटेक्शन पाइपलाइन के भाग के रूप में एक नया बाइटकोड VM तैनात किया।
auditor.js एक सामान्य PX सेंसर स्क्रिप्ट जैसा नहीं दिखता। सामान्य अस्पष्ट प्रॉपर्टी लुकअप और कलेक्टर फ़ंक्शन के बजाय:
_fg0 से _fg7 तक), एन्क्रिप्टेड VM प्रोग्राम चरों में विभाजित_dp) जिसमें प्रति-साइट कुंजी (_pk) प्रोग्राम JSON को अनपैक करती है_0x8df7) जो डिक्रिप्शन कुंजी प्राप्त करने के लिए VM के अपने स्रोत को हैश करता है, ताकि कोई भी संशोधन बाइटकोड डिक्रिप्शन को चुपचाप तोड़ देVM प्रोग्राम 8 चरों में विभाजित है, फिर _dp() के माध्यम से प्रति-साइट XOR सिफर का उपयोग करके डिक्रिप्ट किया जाता है, जो _pk द्वारा कुंजीबद्ध है:
var _pk = 893686289;
function _dp(_b) {
var _r = atob(_b), _o = new Array(_r.length);
for (var _i = 0; _i < _r.length; _i++) {
_o[_i] = String.fromCharCode(
_r.charCodeAt(_i) ^ (((_pk >>> (8 * (_i % 4))) ^ Math.imul(_i + 1, 0x6B8B4567)) & 0xFF)
);
}
return _o.join("");
}
परिणाम अस्पष्ट दो-अक्षर की-नामों वाला एक JSON ऑब्जेक्ट है (जैसे, बीज के लिए "uo", नॉन्स के लिए "dk")। एक मैपिंग तालिका उन्हें मानक नामों में परिवर्तित करती है।
node extractor.js
# -> program.json
| फील्ड | विवरण |
|---|---|
s | बीज (12755), सभी क्रिप्टोग्राफिक संचालन को संचालित करता है |
n | नॉन्स (1603730985), प्रति-प्रोग्राम यादृच्छिकीकरण |
g | जनरेटर फ्लैग, इंटीग्रिटी हैश डिक्रिप्शन परत को सक्षम करता है |
x | एन्क्रिप्टेड फ्लैग, कॉन्स्टेंट्स XOR-एन्क्रिप्टेड हैं |
c | कॉन्स्टेंट्स पूल, 1230 प्रविष्टियाँ |
f | फ़ंक्शन, 112 प्रविष्टियाँ एन्क्रिप्टेड बाइटकोड के साथ |
e | एंट्री पॉइंट, फ़ंक्शन इंडेक्स 0 |
सभी 1095 स्ट्रिंग कॉन्स्टेंट्स दो परतों के साथ एन्क्रिप्टेड हैं:
परत 1: 4008000571 द्वारा कुंजीबद्ध स्थिर मरमर XOR, स्थिति-निर्भर।
परत 2: PRNG स्ट्रीम XOR एक glibc LCG का उपयोग करके, प्रोग्राम बीज को नथ के गुणक हैश के माध्यम से प्रत्येक कॉन्स्टेंट के इंडेक्स के साथ मिलाकर बीजित किया जाता है।
डिक्रिप्शन से पहले, बीज को पर्यावरण फिंगरप्रिंट (_0xaf48) के साथ XOR किया जाता है, एक 8-बिट बिटमास्क जो ब्राउज़र API की जांच करके गणना की जाती है:
| बिट | परीक्षण | Chrome |
|---|---|---|
| 0 | typeof window.matchMedia === "function" | 1 |
| 1 | document.elementFromPoint मौजूद है | 1 |
| 2 | typeof window.requestAnimationFrame === "function" | 1 |
| 3 | typeof window.getComputedStyle === "function" | 1 |
| 4 | CSS.supports मौजूद है | 1 |
| 5 | navigator.sendBeacon मौजूद है | 1 |
| 6 | document.execCommand मौजूद है | 1 |
| 7 | process.versions.node मौजूद है (Node.js) | 0 |
Chrome के लिए: _0xaf48 = 0b01111111 = 127, जो प्रभावी बीज = 12755 ^ 127 = 12716 देता है।
इसका अर्थ है कि एक ही प्रोग्राम विभिन्न वातावरणों में अलग-अलग डिक्रिप्शन परिणाम उत्पन्न करता है। इसे Node.js बनाम Chrome बनाम Firefox में चलाने पर अलग-अलग बीज प्राप्त होते हैं।
node decrypt_constants.js
# -> program_decrypted.json, constants_table.txt
डिक्रिप्ट की गई स्ट्रिंग्स हमें बताती हैं कि VM वास्तव में क्या फिंगरप्रिंट करता है:
ब्राउज़र फिंगरप्रिंटिंग: screenWidth, screenHeight, innerWidth, innerHeight, devicePixelRatio, colorDepth, platform, userAgent, language, timezone, timezoneOffset, forcedColors, highContrast
प्रदर्शन समय: navigationStart, domComplete, domLoading, fetchStart, requestStart, responseEnd, secureConnectionStart, serverTiming
RSA क्रिप्टोग्राफी: BigInt, modPow, AQAB (65537 बेस64 में), modulusLength, shiftLeft, shiftRight, getRandomValues
DOM/SVG जांच: http://www.w3.org/2000/svg, createElementNS, getBoundingClientRect, getTotalLength, getBBox
PX फील्ड नाम: mtr, tst, mst, enc, sbx, fstec, pdc, prb, wvi, wva, pti, dis, los, cv, sc, jd, ads, enve, init
एंडपॉइंट संदर्भ: https://fst-ec.perimeterx.net/?id=
एंटी-डीबगर: _CMP_RCX_07;_JNZ_0x0A_EB_CC, CC|CD-04|BREAKPOINT-005
पूर्ण JavaScript भाषा को कवर करने वाले 107 आधार ऑपकोड, साथ ही गतिशील रूप से उत्पन्न शोर:
40 हनी ऑपकोड गणितीय रूप से समकक्ष लेकिन वाक्य रचनात्मक रूप से भिन्न अभिव्यक्तियों का उपयोग करके अंकगणित/तुलना संचालन के वैकल्पिक कार्यान्वयन हैं। ADD (a^b) + 2*(a&b) या -((-a)-b) या a-(-b) के रूप में प्रकट हो सकता है। प्रत्येक आधार ऑपकोड में बीज से नियतात्मक रूप से उत्पन्न 3 प्रकार तक हो सकते हैं। एक साधारण ADD निर्देश एक ही प्रोग्राम के भीतर 4 अलग-अलग बाइटकोड मानों के रूप में प्रकट हो सकता है, जो पैटर्न-मिलान दृष्टिकोण को तोड़ता है।
24 पैडिंग ऑपकोड क्रमचय में आवंटित होते हैं लेकिन उनके कोई हैंडलर नहीं होते और कभी उत्सर्जित नहीं होते। वे ऑपकोड स्थान का विस्तार करने और शफल को उलटना कठिन बनाने के लिए मौजूद हैं।
16 सुपरइंस्ट्रक्शन समूह सबसे महत्वपूर्ण एंटी-एनालिसिस विशेषता हैं। जब डिस्पैच लूप एक ऑपकोड को सुपरइंस्ट्रक्शन लीडर के रूप में हल करता है, तो हैंडलर बाइटकोड स्ट्रीम से एक अतिरिक्त बाइट पढ़ता है और एक उप-हैंडलर को डिस्पैच करता है। उप-हैंडलर पूरी तरह से अलग ऑपरेशन हो सकता है:
| लीडर के रूप में हल होता है | उप-बाइट | वास्तव में निष्पादित होता है |
|---|---|---|
FOR_IN_NEXT | 74 | FOR_IN_NEXT |
FOR_IN_NEXT | 100 | MAKE_CLOSURE |
ASSIGN_OP_VAR | 165 | ASSIGN_OP_VAR |
ASSIGN_OP_VAR | 37 | JMP |
GET_VAR_PROP_C | 143 | SET_VAR_POP |
GET_VAR_PROP_C | 23 | JMP_NULLISH |
ऑपकोड तालिका प्रभावी बीज द्वारा बीजित फिशर-येट्स के माध्यम से शफल की जाती है, इसलिए बाइटकोड मान प्रति बिल्ड भिन्न होते हैं।
node build_opcodes.js
# -> opcode_table.json, opcode_table.txt
टूलकिट का मूल। cfg.js PC=0 से सभी निष्पादन पथों का अनुसरण करके, सही एन्क्रिप्शन संदर्भ के साथ प्रत्येक निर्देश को डिकोड करके एक नियंत्रण प्रवाह ग्राफ बनाता है।
PX ब्लॉक सीमाओं पर ओवरलैपिंग निर्देशों का उपयोग करता है। एक ही बाइट एक निष्पादन पथ पर ऑपरेंड के रूप में और दूसरे पर ऑपकोड के रूप में डीकोड होते हैं, जो ब्लॉक एन्क्रिप्शन संदर्भ पर निर्भर करता है। एक रैखिक स्कैन प्रत्येक बाइट स्थिति को एक बार डीकोड करता है और वैकल्पिक पथ को छोड़ देता है। CFG फॉल-थ्रू और जंप दोनों किनारों का अनुसरण करता है, प्रत्येक पथ को स्वतंत्र रूप से डीकोड करता है।