
मैलवेयर ट्राइएज और बाइनरी विश्लेषण के लिए संयोजनीय कमांड-लाइन टूलकिट: दुर्भावनापूर्ण फ़ाइलों और शेलकोड से इंडिकेटर डिकोड, डिक्रिप्ट, कार्व और एक्सट्रेक्ट करें।
__ __ High Octane Triage Analysis __
|| _||______ __ __________ _____ ||
|| \||___ \__| ____/ ______/___ / ____\ ||
==||=====|| | __/ |/ \ /==| / __ \ __\===]|
'======|| | \ | | \_ _| \ ___/| | ||
||____ /__|___|__/ / | \____]| | ||
=========''====\/=========/ /==|__|=====|__|======'
\ /
\/
Binary Refinery™ Python स्क्रिप्ट्स का एक संग्रह है जो कंप्रेशन और एन्क्रिप्शन जैसे बाइनरी डेटा के ट्रांसफ़ॉर्मेशन को लागू करती हैं।
हम अक्सर इसे केवल refinery कहते हैं, जो संबंधित पैकेज का भी नाम है।
स्क्रिप्ट्स को विशेष रूप से stdin से इनपुट पढ़ने और stdout पर आउटपुट लिखने के लिए डिज़ाइन किया गया है।
मुख्य दर्शन यह है कि प्रत्येक स्क्रिप्ट एक इकाई होनी चाहिए, इस अर्थ में कि वह एक काम करती है,
और व्यक्तिगत इकाइयों को कमांड लाइन पर पाइपिंग ऑपरेटर | के साथ पाइपलाइनों में जोड़कर अधिक जटिल कार्य किए जा सकते हैं।
परियोजना का मुख्य फोकस मैलवेयर ट्राइएज है,
और यह कमांड लाइन पर CyberChef जैसा कुछ लागू करने का एक प्रयास है।
Python वर्चुअल एनवायरनमेंट बनाएँ। आपको Python 3.10 या उससे नया चाहिए। refinery को इस प्रकार इंस्टॉल करें:
python -m pip install -U pip
python -m pip install -U binary-refinery[extended]
यूनिट्स को -h के साथ चलाकर जानें कि वे कैसे काम करती हैं, docs में खोजें या उन्हें ढूँढने के लिए binref कमांड का उपयोग करें।
इसे क्रियाशील देखने के लिए एक हालिया वीडियो देखें।
लेकिन, इस readme के बाकी हिस्से को भी पढ़ें।
कोई निश्चित रिलीज़ शेड्यूल नहीं है, लेकिन रिलीज़ बहुत बार होती हैं और समय-समय पर अपडेट करने की अनुशंसा की जाती है। बगफिक्स GIT के बाहर प्रलेखित नहीं होते, लेकिन अन्य सभी परिवर्तन (यानी नई सुविधाएँ) changelog में प्रलेखित हैं। विशेष रूप से प्रभावशाली रिलीज़ों के अपडेट के लिए Mastodon पर मेरा अनुसरण करें।
किसी यूनिट को -h या --help स्विच के साथ चलाने पर जो सहायता पाठ प्रदर्शित होता है, वही उसका मुख्य दस्तावेज़ीकरण है।
स्वचालित रूप से निर्मित दस्तावेज़ीकरण में प्रत्येक यूनिट के लिए उस आउटपुट का शीर्ष-स्तरीय संकलन होता है,
साथ ही टूलकिट की तीन मूलभूत अवधारणाओं का विनिर्देशन भी:
framing, multibin तर्क, और meta चर।
हर यूनिट के विवरण और सहायता पाठ की फुल-टेक्स्ट खोज कमांड लाइन पर,
दिए गए binref कमांड के माध्यम से भी उपलब्ध है। यह स्वीकार करते हुए कि संदर्भ दस्तावेज़ीकरण कुछ शुष्क हो सकता है,
ट्यूटोरियल की एक श्रृंखला बनाने का निरंतर प्रयास चल रहा है; मैं अत्यधिक अनुशंसा करता हूँ कि आप उन्हें देखें।
इसके अलावा, मैं नीचे अतिरिक्त संसाधन (कुछ तृतीय-पक्ष द्वारा निर्मित सहित) एकत्र करता हूँ।
[!NOTE]
Refinery अभी भी अल्फा में है और इंटरफ़ेस कभी-कभी बदल सकता है, अर्थात यूनिट और पैरामीटर हटाए या नाम बदले जा सकते हैं। इसलिए, ऐसा हो सकता है कि पुराने वीडियो और ब्लॉग पोस्ट की विशिष्ट कमांड लाइनें अब काम न करें।
2021/08] OALabs ने मुझे एक फीचर वीडियो में टूलकिट का डेमो करने की कृपा की।
वीडियो में, मैं मूल रूप से पहले ट्यूटोरियल की सामग्री के माध्यम से काम करता हूँ।2021/11] Johannes Bader ने binary refinery के साथ malspam का विश्लेषण करने के बारे में एक अद्भुत ब्लॉग पोस्ट लिखा।2024/03] Malware Analysis For Hedgehogs ने refinery का उपयोग करके एक XWorm सैंपल को अनपैक करने के बारे में वीडियो बनाया।2024/11] the CyberYeti ने मुझे स्ट्रीम पर refinery प्रस्तुत करने के लिए आमंत्रित किया।2025/06] मैं the CyberYeti के साथ फिर से स्ट्रीम पर था, यह एक थोड़ा अधिक कच्चा है।
यहाँ दिखाई देने वाले सभी बग ठीक कर दिए गए हैं। 😉शोकेस में फिर से नीचे दिए गए उदाहरण अनुभाग के सैंपल और ट्यूटोरियल शामिल हैं।
Binary Refinery (c) 2019 Jesko Hüttenhain है, और इसे 3-Clause BSD License के अंतर्गत प्रकाशित किया गया है। इस रिपॉजिटरी में पूर्ण लाइसेंस पाठ की एक प्रति भी शामिल है। यदि आप इसके साथ कुछ ऐसा करना चाहते हैं जो इस लाइसेंस द्वारा कवर नहीं है, तो कृपया लेखक से संपर्क करने में संकोच न करें।
refinery को कम से कम Python 3.10 की आवश्यकता है। इसे अपने स्वयं के वर्चुअल एनवायरनमेंट में इंस्टॉल करने की अनुशंसा की जाती है: यह पैकेज बहुत सारे निर्भरताओं को खींच सकता है, और इसे आपके ग्लोबल Python में इंस्टॉल करना संस्करण संघर्षों के लिए कुछ हद तक प्रवण है। साथ ही, चूँकि टूलकिट बड़ी संख्या में नए कमांड पेश करता है, इस बात की अच्छी संभावना है कि इनमें से कुछ कुछ सिस्टमों पर टकराएँ, और उन्हें उनके अपने अलग वर्चुअल एनवायरनमेंट में रखना इसे रोकने का एक तरीका है।
यदि आप चाहते हैं कि सभी refinery कमांड आपके शेल में हर समय उपलब्ध रहें (यानी कस्टम वर्चुअल एनवायरनमेंट में स्विच किए बिना),
तो आपके पास इंस्टॉलेशन के लिए एक prefix चुनने का विकल्प भी है,
जिसे इंस्टॉल किए गए हर कमांड शिम के आगे रखा जाएगा।
उदाहरण के लिए, यदि आप अपने prefix के रूप में r. चुनते हैं, तो emit यूनिट r.emit कमांड के रूप में इंस्टॉल होगी।
एक अतिरिक्त लाभ यह है कि आप r. टाइप कर सकते हैं और उपलब्ध सभी refinery कमांड की सूची पाने के लिए Tab को दो बार दबा सकते हैं।
हालाँकि ध्यान दें कि दस्तावेज़ीकरण में कोई prefix नहीं माना गया है, और refinery का विकास लक्ष्य अधिकांश सिस्टमों पर न टकराना है।
लेखक prefix का उपयोग नहीं करता है और इस विकल्प को सुरक्षा कंबल के रूप में प्रदान करता है।
refinery को इंस्टॉल और अपडेट करने का सबसे सीधा तरीका pip के माध्यम से है। पहले सुनिश्चित करें कि आप नवीनतम संस्करण चला रहे हैं:
python -m pip install -U pip
फिर बस refinery पैकेज इंस्टॉल करें:
pip install -U binary-refinery
यदि आप सभी यूनिट्स के लिए एक prefix चुनना चाहते हैं, तो आप इसे पर्यावरण चर REFINERY_PREFIX के माध्यम से निर्दिष्ट कर सकते हैं।
उदाहरण के लिए, निम्न कमांड Linux पर r. prefix के साथ refinery को वर्तमान Python वातावरण में इंस्टॉल करेगी:
REFINERY_PREFIX=r. pip install -U binary-refinery
Windows पर, आपको निम्न कमांड चलानी होंगी:
set REFINERY_PREFIX=r.
pip install -U binary-refinery
विशेष prefix ! निर्दिष्ट करने का प्रभाव यह होगा कि कोई भी शेल कमांड बिल्कुल नहीं बनाई जाएगी,
और binary refinery केवल एक लाइब्रेरी के रूप में इंस्टॉल होगी।
यदि आप वर्तमान refinery HEAD इंस्टॉल करना चाहते हैं, तो आप उपरोक्त सभी चरणों को दोहरा सकते हैं, pip पैकेज के बजाय इस रिपॉजिटरी को निर्दिष्ट करते हुए।
उदाहरण के लिए, निम्नलिखित नवीनतम refinery कमिट को इंस्टॉल करेगा:
pip install -U git+git://github.com/binref/refinery.git
अंत में, यदि आप REMnux का उपयोग कर रहे हैं, तो आप उनके refinery docker container का उपयोग कर सकते हैं।
यदि आप अपने स्थानीय मैलवेयर विश्लेषण claude को binary refinery का उपयोग करना सिखाना चाहते हैं, तो Binary Refinery Skill देखें।
निम्नलिखित एक सारांश है कि विभिन्न शेल वातावरण वर्तमान में कितनी अच्छी तरह समर्थित हैं:
यदि आप एक अलग शेल का उपयोग कर रहे हैं और कुछ प्रतिक्रिया साझा करना चाहते हैं, तो कृपया मुझे बताएं!
कुछ अत्यधिक स्थितिजन्य यूनिट हैं जिनकी (कभी-कभी बड़ी) बाहरी निर्भरताएँ होती हैं।
उदाहरण के लिए, stego एक ऐसी यूनिट है जिसे इमेज पार्सिंग लाइब्रेरी Pillow की आवश्यकता होती है।
कुछ लाइब्रेरी डिफ़ॉल्ट रूप से इंस्टॉल नहीं की जाती हैं ताकि पहली बार उपयोगकर्ताओं के लिए refinery का इंस्टॉलेशन समय उचित स्तर पर रहे।
संबंधित यूनिट आपको बताएंगी कि उनकी निर्भरता गायब होने पर क्या करना है:
$ emit config.png | stego RG
(13:37:00) failure in stego: dependency Pillow is missing; run pip install Pillow
फिर आप इन लापता निर्भरताओं को मैन्युअल रूप से इंस्टॉल कर सकते हैं। यदि आप लापता निर्भरताओं से परेशान नहीं होना चाहते हैं और लंबे refinery इंस्टॉलेशन से कोई आपत्ति नहीं है, तो आप पैकेज को निम्नानुसार इंस्टॉल कर सकते हैं:
pip install -U binary-refinery[all]
जो आवश्यक निर्भरताओं के अतिरिक्त सभी निर्भरताएँ इंस्टॉल करेगा। अधिक सटीक रूप से, निम्नलिखित अतिरिक्त श्रेणियाँ उपलब्ध हैं:
| नाम | शामिल निर्भरताएँ |
|---|---|
default | उचित निर्भरताओं का अनुशंसित चयन, लेखक की पसंद |
extended | एक विस्तारित चयन, जिसमें केवल सबसे अस्पष्ट निर्भरताएँ शामिल नहीं हैं |
all | सभी refinery यूनिट्स के लिए सभी निर्भरताएँ |
इन्हें यहाँ आरोही क्रम में सूचीबद्ध किया गया है, यानी extended वह सब कुछ इंस्टॉल करेगा जो default इंस्टॉल करेगा।
वैकल्पिक रूप से, आप इस रिपॉजिटरी को क्लोन कर सकते हैं और refinery पैकेज को स्थानीय वर्चुअल एनवायरनमेंट में इंस्टॉल करने के लिए स्क्रिप्ट update.sh (Linux पर) या update.ps1 (Windows पर) का उपयोग कर सकते हैं। इस विधि के लिए इंस्टॉलेशन और अपडेट प्रक्रिया केवल स्क्रिप्ट चलाना है:
binary-refinery को अनइंस्टॉल करता है,binary-refinery[all] इंस्टॉल करता है।आप सभी दस्तावेज़ीकरण स्थानीय रूप से भी जनरेट कर सकते हैं।
ऐसा करने के लिए, run-pdoc3.py स्क्रिप्ट चलाएँ।
यह विफल होगा जब तक कि आप इसे ऐसे वातावरण से नहीं चलाते जहाँ binary refinery एक Python पैकेज के रूप में इंस्टॉल किया गया हो।
इसे चलाने के लिए, आपको run-pdoc3.py के पहले कमांड लाइन तर्क के रूप में एक वर्चुअल एनवायरनमेंट का पथ निर्दिष्ट करना होगा,
जिससे स्क्रिप्ट उस वातावरण के इंटरप्रेटर का उपयोग करके स्वयं को फिर से चलाएगी।
यदि आप सुनिश्चित हैं कि आप run-pdoc3.py चलाना चाहते हैं,
तो स्क्रिप्ट को वर्तमान डिफ़ॉल्ट Python इंटरप्रेटर के साथ चलाने के लिए एक कमांड लाइन स्विच है।
स्क्रिप्ट pdoc3 पैकेज इंस्टॉल करती है और इसका उपयोग refinery पैकेज के लिए HTML दस्तावेज़ीकरण जनरेट करने के लिए करती है।
दस्तावेज़ीकरण तब इस readme फ़ाइल के ठीक बगल में html उपनिर्देशिका में पाया जा सकता है।
ट्यूटोरियल Jupyter नोटबुक हैं जिन्हें आप आसानी से चला और निष्पादित कर सकते हैं यदि आपके वर्चुअल एनवायरनमेंट में Jupyter इंस्टॉल हो। यह बताना उचित है कि Visual Studio Code में Jupyter के लिए बहुत आरामदायक समर्थन है।
emit और dump यूनिट एक विशेष भूमिका निभाती हैं: पहली डेटा आउटपुट करने के लिए है जबकि दूसरी डेटा को क्लिपबोर्ड या डिस्क पर डंप करने के लिए है। एक उदाहरण के रूप में, निम्नलिखित पाइपलाइन पर विचार करें:
emit M7EwMzVzBkI3IwNTczM3cyMg2wQA | b64 | zl | hex
यहाँ, हम स्ट्रिंग M7EwMzVzBkI3IwNTczM3cyMg2wQA को emit करते हैं,
b64 का उपयोग करके इसे base64-डिकोड करते हैं,
zl का उपयोग करके परिणाम को zlib-डीकंप्रेस करते हैं,
और अंत में डीकंप्रेस किए गए डेटा को hex-डिकोड करते हैं।
प्रत्येक यूनिट डिफ़ॉल्ट रूप से एक निश्चित ट्रांसफ़ॉर्मेशन का "डिकोडिंग" ऑपरेशन करती है, लेकिन उनमें से कुछ रिवर्स ऑपरेशन भी लागू करती हैं।
यदि वे ऐसा करती हैं, तो यह हमेशा -R, या --reverse कमांड लाइन स्विच प्रदान करके किया जाता है।
आप उपरोक्त base64 स्ट्रिंग को निम्न कमांड का उपयोग करके उत्पन्न कर सकते हैं क्योंकि hex, zl, और b64 सभी रिवर्स ऑपरेशन प्रदान करते हैं:
emit "Hello World" | hex -R | zl -R | b64 -R
यदि एक फ़ाइल packed.bin में base64-एन्कोडेड पेलोड बफर है, तो निम्नलिखित पाइपलाइन उक्त पेलोड को payload.bin में निकालती है:
emit packed.bin | carve -l -t1 b64 | b64 | dump payload.bin
carve यूनिट का उपयोग इनपुट बफर से डेटा के ब्लॉक निकालने के लिए किया जा सकता है,
इस मामले में यह base64-एन्कोडेड डेटा की तलाश करता है, उन्हें लंबाई (-l) के आधार पर क्रमबद्ध करता है और इनमें से पहला (-t1) लौटाता है,
जो packed.bin से base64 जैसा सबसे बड़ा डेटा टुकड़ा निकालता है।
फिर डेटा को base64-डिकोड किया जाता है और payload.bin फ़ाइल में डंप किया जाता है।
pack यूनिट, एक टेक्स्ट बफर से सभी संख्यात्मक एक्सप्रेशन चुनकर उन्हें उनके बाइनरी प्रतिनिधित्व में बदल देगी। एक सरल उदाहरण यह पाइपलाइन है
emit "0xBA 0xAD 0xC0 0xFF 0xEE" | pack | hex -R
जो स्ट्रिंग BAADC0FFEE आउटपुट करेगी।
BLOB से base64-एन्कोडेड डेटा का सबसे बड़ा टुकड़ा निकालें और उसे डिकोड करें:
emit file.exe | carve -ds b64
बफर से एक ZIP फ़ाइल निकालें, उसमें से एक DLL चुनें, और उसके बारे में जानकारी प्रदर्शित करें:
emit file.bin | carve-zip | xtzip file.dll | pemeta
PE फ़ाइल अनुभागों को उनके संबंधित SHA-256 हैश के साथ सूचीबद्ध करें:
emit file.exe | vsect [| sha256 -t | pf {} {path} ]]
वर्तमान निर्देशिका में सभी फ़ाइलों को उनके संबंधित SHA-256 हैश के साथ पुनरावर्ती रूप से सूचीबद्ध करें:
ef "**" [| sha256 -t | pf {} {path} ]]
वर्तमान निर्देशिका के अंदर पुनरावर्ती रूप से गणना की गई सभी फ़ाइलों से संकेतक निकालें:
ef "**" [| xtp -n6 ipv4 socket url email | dedup ]]
नेटवर्क बाइट क्रम में हार्ड-कोडित IP पता 0xC0A80C2A को पठनीय प्रारूप में बदलें:
emit 0xC0A80C2A | pack -EB4 | pack -R [| sep . ]
एकल बाइट XOR ब्रूट फोर्स करें और हर पुनरावृत्ति में PE फ़ाइल पेलोड निकालने का प्रयास करें:
emit file.bin | rep 0x100 [| xor v:index | carve-pe -R | peek | dump {name} ]
एक RemCos C2 सर्वर निकालें:
emit c0019718c4d4538452affb97c70d16b7af3e4816d059010c277c4e579075c944 \
| perc SETTINGS [| put keylen cut::1 | rc4 cut::keylen | xtp socket ]
एक AgentTesla कॉन्फ़िगरेशन निकालें:
emit fb47a566911905d37bdb464a08ca66b9078f18f10411ce019e9d5ab747571b40 \
| dnfields [| aes x::32 --iv x::16 -T ]] \
| rex -M "((??email))\n(.*)\n(.*)\n:Zone" addr={1} pass={2} host={3}
एक दुर्भावनापूर्ण XLS मैक्रो ड्रॉपर से PowerShell पेलोड निकालें:
emit 81a1fca7a1fb97fe021a1f2cf0bf9011dd2e72a5864aad674f8fea4ef009417b [ \
| xlxtr 9.5:11.5 15.15 12.5:14.5 [ \
| scope -n 3 | chop -t 5 [| sorted -a | snip 2: | sep ] \
| pack 10 | alu --dec -sN B-S ]] \
| dump payload.cmd
और अगले चरण के लिए डोमेन प्राप्त करें:
emit payload.cmd | cmdarg | ps1 | xtp -f domain
अनपैक किए गए HawkEye सैंपलों का कॉन्फ़िगरेशन निकालें:
emit ee790d6f09c2292d457cbe92729937e06b3e21eb6b212bf2e32386ba7c2ff22c \
| put cfg perc[RCDATA]:c:: [\
| xtp guid | pbkdf2 48 rep[8]:h:00 | cca eat:cfg | aes -Q x::32 --iv x::16 ] \
| dnds
Warzone RAT:
emit 4537fab9de768a668ab4e72ae2cce3169b7af2dd36a1723ddab09c04d31d61a5 \
| vsect .bss | struct I{key:{}}{} [\
| rc4 eat:key | struct I{host:{}}{port:H} {host:u16}:{port} ]
शेलकोड लोडर से पेलोड निकालें और उसका c2 निकालें:
emit 58ba30052d249805caae0107a0e2a5a3cb85f3000ba5479fafb7767e2a5a78f3 \
| rex yara:50607080.* [| struct LL{s:L}{} | xor -B2 rand[s]:msvc | xtp url ]
एक भूले-बिसरे समय से दुर्भावनापूर्ण VBA मैक्रोज़ प्राप्त करें जब यही तरीका अपनाया जाता था:
emit ee103f8d64cd8fa884ff6a041db2f7aa403c502f54e26337c606044c2f205394 \
| vbamc
और फिर दुर्भावनापूर्ण डाउनलोडर पेलोड निकालें:
emit ee103f8d64cd8fa884ff6a041db2f7aa403c502f54e26337c606044c2f205394 \
| doctxt | repl drp:c: | carve -s b64 | rev | b64 | rev | ppjscript
एक दुर्भावनापूर्ण PDF दस्तावेज़ से पेलोड URL निकालें:
emit 066aec7b106f669e587b10b3e3c6745f11f1c116f7728002f30c072bd42d6253 \
| xt JS | csd string | csd string | url | xtp url [| urlfix ]]
एक समीकरण संपादक शोषण दस्तावेज़ से पेलोड URL निकालें:
emit e850f3849ea82980cf23844ad3caadf73856b2d5b0c4179847d82ce4016e80ee \
| officecrypt | xt oleObject | xt native | rex Y:E9[] | vstack -a=x32 -w=200 | xtp
मान लें कि data एक फ़ाइल है जिसे CBC मोड में 256-बिट AES से एन्क्रिप्ट किया गया था।
कुंजी को गुप्त पासफ़्रेज़ swordfish से PBKDF2 कुंजी व्युत्पत्ति रूटीन का उपयोग करके नमक s4lty के साथ व्युत्पन्न किया गया था।
IV को बफर के पहले 16 बाइट्स के रूप में प्रीफिक्स किया गया है।
इसे निम्नलिखित पाइपलाइन के साथ डिक्रिप्ट किया जा सकता है:
emit data | aes --mode cbc --iv cut::16 pbkdf2[32,s4lty]:swordfish
यहाँ, cut:0:16 और pbkdf2[32,s4lty]:swordfish दोनों multibin तर्क हैं जो एक विशेष हैंडलर का उपयोग करते हैं।
इस मामले में, cut:0:16 इनपुट डेटा से स्लाइस 0:16 (यानी पहले 16 बाइट्स) निकालता है - इस multibin हैंडलर के अनुप्रयोग के बाद,
इनपुट डेटा से पहले 16 बाइट्स हटा दिए जाते हैं और तर्क iv को इन ठीक 16 बाइट्स पर सेट किया जाता है।
अंतिम तर्क 32 बाइट की एन्क्रिप्शन कुंजी निर्दिष्ट करता है:
दूसरी ओर, हैंडलर pbkdf2[32,s4lty] refinery को निर्देश देता है कि वह pbkdf2 यूनिट का एक इंस्टेंस बनाए जैसे उसे कमांड लाइन पैरामीटर 32 और s4lty इस क्रम में दिए गए हों और बाइट स्ट्रिंग swordfish को इस यूनिट के साथ प्रोसेस करें।
एक सरल परीक्षण के रूप में, निम्नलिखित पाइपलाइन एक नमूना पाठ को एन्क्रिप्ट और डिक्रिप्ट करेगी:
emit "Once upon a time, at the foot of a great mountain ..." ^
| aes pbkdf2[32,s4lty]:swordfish --iv md5:X -R | ccp md5:X ^
| aes pbkdf2[32,s4lty]:swordfish --iv cut:0:16
| शेल | प्लेटफ़ॉर्म | स्थिति | टिप्पणी |
|---|
| Bash | Posix | 🔵 अच्छा | लेखक द्वारा कभी-कभी उपयोग किया जाता है। |
| CMD | Windows | 🔵 अच्छा | लेखक द्वारा व्यापक रूप से उपयोग किया जाता है। |
| PowerShell | Windows | 🟡 उचित | यह तब काम करता है जब PowerShell संस्करण कम से कम 7.4 हो। |
| Zsh | Posix | 🟠 मामूली समस्याएँ | एक चर्चा के बाद, एक समाधान मौजूद है। |
| Fish | Posix | 🟠 मामूली समस्याएँ | समस्या #55 और चर्चा #22 देखें। |