
LLVM पासों के माध्यम से C/C++ को ओबफस्केट करता है: स्ट्रिंग एन्क्रिप्शन, कंट्रोल-फ्लो फ्लैटनिंग, MBA रीराइटिंग और एंटी-विश्लेषण, ताकि रिवर्स इंजीनियरिंग को विफल किया जा सके।
██╗ ███████╗███████╗████████╗
██║ ██╔════╝██╔════╝╚══██╔══╝
██║ █████╗ █████╗ ██║
██║ ██╔══╝ ██╔══╝ ██║
███████╗███████╗███████╗ ██║
╚══════╝╚══════╝╚══════╝ ╚═╝
██████╗ ██████╗ ███████╗██╗ ██╗███████╗ ██████╗ █████╗ ████████╗ ██████╗ ██████╗
██╔═══██╗██╔══██╗██╔════╝██║ ██║██╔════╝██╔════╝██╔══██╗╚══██╔══╝██╔═══██╗██╔══██╗
██║ ██║██████╔╝█████╗ ██║ ██║███████╗██║ ███████║ ██║ ██║ ██║██████╔╝
██║ ██║██╔══██╗██╔══╝ ██║ ██║╚════██║██║ ██╔══██║ ██║ ██║ ██║██╔══██╗
╚██████╔╝██████╔╝██║ ╚██████╔╝███████║╚██████╗██║ ██║ ██║ ╚██████╔╝██║ ██║
╚═════╝ ╚═════╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝
A simple obfuscator for C/C++ x64 and x86 code
यह एक LLVM फोर्क के रूप में बनाया गया है, इसलिए अस्पष्टीकरण (obfuscate) करने के लिए आपको वास्तविक स्रोत की आवश्यकता होगी। यह कोई मनमाना executable obfuscator नहीं है। यह कंपाइलर का एक संशोधित संस्करण है।
यदि आप परिणाम देखना चाहते हैं, तो मैंने इसके साथ 2 crackme बनाए और obfuscate किए हैं। आप उन्हें releases में precompiled clang binary के साथ प्राप्त कर सकते हैं।
यह सब -O3 flag के साथ compile किया गया था और सभी screenshots IDA Pro 9.4 से हैं। मैंने इसे Binary Ninja और Ghidra के साथ भी परखा, परिणाम या तो समान थे या बदतर।
compile time पर strings को encrypt करता है और encrypted string के हर उपयोग पर एक decrypt function डालता है। यह binary में किसी भी string को खोजने की क्षमता को पूरी तरह से अक्षम कर देता है। और हर string की अपनी अद्वितीय key decrypt function में hardcoded होती है जो उन्हें dump और decrypt करना बहुत कठिन बनाती है। ये decrypt functions भी inlined होंगी। (नीचे केवल प्रदर्शन के लिए inlined नहीं हैं)
![]() pass से पहले |
![]() बाद में |
अंकगणितीय संक्रियाओं को उनके MBA समकक्षों से बदल देता है। मूल संक्रिया ने क्या किया था, यह देखना मूल रूप से असंभव है जब तक कि आप इसे पहले MBA deobfuscator से न गुज़ारें। बेशक यह obfuscation कुछ हद तक कमज़ोर है क्योंकि MBA किताब का सबसे पुराना तरीका है, इसलिए इससे निपटने के लिए कई tools हैं, उदाहरण के लिए CoBRA, यह इसे सफलतापूर्वक मूल expression में deobfuscate कर देगा:
./cobra-cli --mba "((x^y) - (((x^y)&0xFF)&0xA) + 10) * ((x^y) - ((x^y)|0xA) + 10) + ((x^y) - (((x^y)&0xFF)|0xFFFFFFF5) - 11) * (~(x^y) - (~(x^y)|0xA) + 10)" --bitwidth 32
10 * (x ^ y)
इसीलिए मैंने AAMBA पास बनाया है।
![]() पास से पहले |
![]() बाद में |
बाइनरी ऑपरेशन्स के ऑपरेंड्स को ADC(X, 255) - 255 - CF और SBB(X, 255) + 255 + CF से बदल देता है। बेशक यह हमेशा X ही देता है, लेकिन यह एक्सप्रेशन को कैरी फ्लैग पर निर्भर बना देता है। जब तक डीकंपाइलर CF की स्थिति को ट्रैक नहीं करता (जो कभी-कभी असंभव होता है) तब तक यह बहुत भ्रमित हो जाएगा और इन एक्सप्रेशन्स को फोल्ड नहीं कर पाएगा। यह पिछले MBA पास के साथ बहुत अच्छी तरह मेल खाता है, जो अंकगणित को और भी अधिक ऑब्फस्केट कर देता है। जैसा कि आप नीचे देख सकते हैं, डीकंपाइलर ने कुछ अतिरिक्त वेरिएबल्स बनाए हैं और बहुत सारे __PAIR64__ और __CFADD__ कॉल्स का उपयोग करता है, इसलिए इसे CoBRA जैसे टूल्स में पेस्ट करना बहुत कठिन हो जाता है (फिर भी असंभव नहीं), IDA का gooMBA प्लगइन भी इसे सरल बनाने में कोई मदद नहीं करता। साथ ही, IDA का डीकंपाइलर कुछ हद तक कैरी फ्लैग को ट्रैक करता है, लेकिन इसे कंट्रोल फ्लो ऑब्फस्केशन के साथ मिलाने से निष्पादन के बिना ट्रैकिंग मूल रूप से असंभव हो जाती है क्योंकि आपको पता नहीं होता कि पिछला ऑपरेशन क्या था, हो सकता है उसने CF को 1 पर सेट किया हो, हो सकता है नहीं, इसलिए बाद के पास इस पर और भी अधिक जोड़ देंगे।
![]() पास से पहले |
![]() बाद में |
किसी फ़ंक्शन के अंदर के सभी ब्लॉक्स को इकट्ठा करता है और उनसे एक विशाल स्टेट मशीन बना देता है, यह फ़ंक्शन की शुरुआत में एक जंप टेबल बनाता है और उसमें सभी ब्लॉक पॉइंटर्स रख देता है। फिर प्रत्येक ब्लॉक के अंत में सामान्य जंप के बजाय सब कुछ डिस्पैचर के माध्यम से रूट किया जाता है जो इनडायरेक्ट जंप्स का उपयोग करता है, इन्हें बिना किसी निष्पादन के स्टैटिक रूप से रिज़ॉल्व करना लगभग असंभव है। यह रजिस्टर्स को स्टैक पर भी डिमोट कर देता है, इसलिए यदि आप किसी ब्लॉक को आधे में विभाजित करते हैं, तो पिछले ब्लॉक के सभी वेरिएबल्स स्टैक पर होंगे, जिसका अर्थ है कि प्रत्येक एक्सप्रेशन में बहुत सारे वेरिएबल्स होंगे। यदि आप किसी एक ब्लॉक के ऑपरेशन्स को CoBRA के माध्यम से चलाते हैं तो यह इसे ज़्यादा डीऑब्फस्केट नहीं कर पाएगा क्योंकि बहुत सारे अज्ञात वेरिएबल्स होंगे।
अमान्य असेंबली वाले बहुत सारे फर्जी ब्लॉक्स बनाता है। यह डिसअसेंबलर्स को बहुत भ्रमित कर देता है क्योंकि यदि डिसअसेंबलर को कोई तकनीकी रूप से अमान्य बाइट मिलती है जो कभी निष्पादित नहीं होती, तो भी वह उसे समझने की कोशिश करेगा। इसलिए यदि बाइट अधूरी है, तो यह उसके बाद जो भी बाइट्स हों उनसे एक इंस्ट्रक्शन बना देगा, अनिवार्य रूप से उन्हें खपत कर लेगा। इससे एक डीसिंक पैदा होता है जो उसके बाद के प्रत्येक इंस्ट्रक्शन को नष्ट कर देता है। Windows बाइनरीज़ पर IDA कुछ हद तक इससे उबर पाएगा, दुर्लभ मामलों में यह एक ग्राफ़ बनाने और जो हो सके उसे डीकंपाइल करने में सक्षम होगा (हालांकि यह टूटा हुआ और अधूरा होगा), जबकि Linux बाइनरीज़ पर यह ग्राफ़ व्यू को पूरी तरह तोड़ देता है और डीकंपाइलेशन को अक्षम कर देता है। इसके अतिरिक्त यह RDTSC टाइमर और कुछ अन्य एंटी डीबग चेक्स डालता है, यदि यह किसी डीबगर का पता लगाता है तो आप क्रैश हो जाते हैं।
![]() Windows |
![]() Linux |