
गूगल के Gemma 4 E4B स्थानीय AI मॉडल का उपयोग करके Ghidra के साथ एक विंडोज क्रैकमी को रिवर्स इंजीनियर करने पर चरण-दर-चरण ट्यूटोरियल, जिसमें स्थानीय अनुमान के लिए सेटअप और स्वचालित फ़ंक्शन/वेरिएबल नामकरण शामिल है।
मैं Google द्वारा जारी किए गए नए Gemma E4B ओपन वेट्स स्थानीय मॉडल के साथ खेल रहा था, और मेरे आश्चर्य की बात है कि मैं इसे स्थानीय ऑफ़लाइन रिवर्स इंजीनियरिंग परिदृश्यों में उपयोग करने में बहुत अधिक सफलता देख रहा था। मैं इस ट्यूटोरियल को लिखना चाहता था ताकि यह प्रसारित किया जा सके कि स्थानीय AI अब कई बुनियादी रिवर्सिंग कार्यों के लिए पर्याप्त अच्छा है, और यह कि चीजें शायद यहाँ से तेजी से सुधरेंगी।
एक नए बाइनरी को रिवर्स करने के सबसे कठिन हिस्सों में से एक शुरुआत में होता है, जब आपको यह पता नहीं होता कि महत्वपूर्ण फ़ंक्शन और वेरिएबल क्या हैं। ऐसी कई तरकीबें हैं जिनका उपयोग रिवर्सर्स शुरू करने के लिए करते हैं, जिनमें स्ट्रिंग संदर्भों को देखना, बाइनरी डिफिंग, या समान फ़ंक्शनों का मिलान शामिल है।
AI यहाँ मदद करने में बहुत अच्छा है, और मुझे व्यक्तिगत रूप से OpenAI API का उपयोग करके बाइनरी को चिह्नित करने या डीकंपाइलर आउटपुट को साफ करने में बड़ी सफलता मिली है। हालांकि, इन API का उपयोग करने के कई नुकसान हैं:
लागत - डीकंपिलेशन और डिसअसेंबली बहुत सारे टोकन उत्पन्न करते हैं। API प्रति टोकन $ शुल्क लेते हैं, इसलिए बड़े बाइनरी को विश्लेषण करने में काफी $$$ लग सकते हैं। यदि आप कई बाइनरी वाले एक बड़े लक्ष्य से निपट रहे हैं जो हर हफ्ते अपडेट होते हैं, तो ये लागतें जल्दी बढ़ सकती हैं, और शौकिया रिवर्स इंजीनियर्स के लिए निषेधात्मक हैं।
गोपनीयता - जब आप एक रिमोट API का उपयोग कर रहे होते हैं, तो AI के होस्ट को पता होता है कि आप क्या कर रहे हैं। यह कुछ पेशेवर परिदृश्यों में एक गैर-शुरुआत है।
नियंत्रण - जब आप एक रिमोट API पर निर्भर करते हैं, तो आपका इस बात पर कोई नियंत्रण नहीं होता कि आपको कौन से मॉडल परोसे जा रहे हैं, या मॉडलों की गुणवत्ता क्या है। यदि आप मिशन-महत्वपूर्ण चीजों के लिए उन पर निर्भर हैं, तो यह एक समस्या हो सकती है जब वे धीमे हो जाते हैं या डाउन हो जाते हैं जब आपको उनकी आवश्यकता होती है, या जब उनके आउटपुट की गुणवत्ता इस हद तक गिर जाती है कि वे बेकार हो जाते हैं।
अपने स्वयं के स्थानीय AI मॉडल चलाने से इनमें से कुछ समस्या बिंदुओं का समाधान होता है:
लागत - एक होस्टेड सेवा पर निर्भर रहने की तुलना में एक स्थानीय मॉडल चलाना बहुत सस्ता हो सकता है। जबकि आपका स्थानीय मॉडल शायद एक अच्छे प्रदाता से छोटा और धीमा है, यदि यह पर्याप्त अच्छा है और उचित समय में चलता है, तो अपने स्वयं के मॉडल को चलाकर पैसे बचाना समझदारी हो सकती है, खासकर यदि आप सरल कार्यों के लिए बड़ी मात्रा में डेटा प्रोसेस कर रहे हैं।
गोपनीयता - जब आप मॉडल को स्थानीय रूप से चलाते हैं, तो कोई नेटवर्क कॉल नहीं होती हैं, और आप अपनी गोपनीयता पर पूर्ण नियंत्रण में हैं। कोई नहीं देख सकता कि आप अपनी मशीन पर मॉडल का उपयोग किस लिए कर रहे हैं।
नियंत्रण - एक ओपन वेट्स मॉडल की खूबसूरती यह है कि कोई भी इसे आपसे छीन नहीं सकता। OpenAI या Anthropic एक दिन अपने SotA मॉडल को अनुपलब्ध कर सकते हैं, या तो मूल्य वृद्धि के माध्यम से या अपने API को स्पष्ट रूप से हटाकर। लेकिन एक ओपन वेट्स मॉडल के साथ, आप अपनी नियति के नियंत्रण में हैं, चाहे अच्छा हो या बुरा।
हालांकि, स्थानीय AI मॉडलों के अपने नुकसान भी हैं:
आकार - आपका मॉडल जितना बड़ा होगा, वह उतना ही बुद्धिमान होगा। हालांकि, अधिकांश बड़े मॉडल उपभोक्ता हार्डवेयर पर फिट नहीं हो सकते। इस वजह से, यदि आप एक स्थानीय मॉडल चला रहे हैं, तो संभावना है कि आप एक ऐसा मॉडल चला रहे हैं जो SotA (State-of-the-Art) मॉडल से 10x-100x छोटा है। आकार में यह कमी सीधे मॉडल की बुद्धिमत्ता में कमी लाती है, जिससे वे कई कार्यों के लिए अनुपयुक्त हो जाते हैं जिन्हें लोग हल्के में लेते हैं SotA मॉडल जैसे ChatGPT/Codex या Claude में।
गति - स्थानीय मॉडल संभवतः AI API का उपयोग करने की तुलना में आपकी मशीन पर धीमे चलेंगे। फिर से, यह उपभोक्ता हार्डवेयर की सीमाओं और कुछ उन तरकीबों के कारण है जो API प्रदाता कर सकते हैं जो आम तौर पर आपके लिए उपलब्ध नहीं हैं।
कॉन्फ़िगरेशन - स्थानीय मॉडल चलाना एक रिफर्बिश्ड लैपटॉप पर Linux चलाने की कोशिश करने जैसा है, बनाम Apple स्टोर में जाकर एक साफ-सुथरा नया Macbook Air खरीदना। Codex और Claude Code का अनुभव AI का Apple स्टोर अनुभव है। स्थानीय AI मॉडल का अनुभव वह आदमी है जो अपने गैराज में फेडोरा पहने एक जर्जर कंप्यूटर पर पीट-पीट कर उसे काम करने की कोशिश कर रहा है। कम से कम, आपको निम्नलिखित चीजों के बारे में चिंता करनी होगी:
यह एक आसान यात्रा नहीं है, और बहुत से लोग हार मान लेते हैं और मान लेते हैं कि स्थानीय AI मॉडल कार्य के लिए उपयुक्त नहीं हैं, क्योंकि उन्हें कभी भी हार्डवेयर/मॉडल/सेटिंग्स/प्रॉम्प्टिंग/हार्नेस का सही संयोजन नहीं मिला जो उनके कार्य के लिए काम करे। जबकि कई मामलों में वे सही हैं, मुझे उम्मीद है कि यह ट्यूटोरियल कम से कम इस बात पर प्रकाश डालेगा कि स्थानीय मॉडल कितनी दूर आ गए हैं, वे रिवर्स इंजीनियरिंग में कैसे मदद कर सकते हैं, और लोगों को स्थानीय AI को एक मौका देने के लिए प्रेरित करेगा।
(आर्काइव पासवर्ड crackmes.one है)। मैंने एक वैकल्पिक लिंक यहाँ रिपॉजिटरी में होस्ट किया है यदि मूल लिंक डाउन हो जाए।
Ghidra 12.04 का उपयोग डिसअसेंबली और डीकंपिलेशन के लिए किया जाता है। इसका उपयोग करने के लिए आपको OpenJDK 21 इंस्टॉल करना होगा: https://github.com/nationalsecurityagency/ghidra
इस ट्यूटोरियल पर काम करते हुए मैंने Claude Code के साथ vibe-code करके एक Ghidra प्लगइन बनाया ताकि AI के साथ फ़ंक्शन और वेरिएबल का नाम बदला जा सके। आप प्लगइन यहाँ से डाउनलोड कर सकते हैं: https://github.com/markoglasgow/Ghidra_FastAIRenamer_Plugin
इसे इंस्टॉल करने के लिए, बस ज़िप फ़ाइल ghidra_12.0.4_PUBLIC_20260427_FastAIRenamerPlugin.zip को ${GHIDRA_HOME}\Extensions\Ghidra में ले जाएं, फिर ${GHIDRA_HOME}\ghidraRun.bat चलाकर Ghidra चलाएं। प्लगइन को सक्रिय करने के लिए, प्रारंभिक Ghidra स्क्रीन पर शीर्ष मेनू से File -> Install Extensions चुनें, फिर प्लगइन ब्राउज़र में FastAIRenamerPlugin के आगे चेकबॉक्स पर टिक करें, फिर Ok पर क्लिक करें। Ghidra आपको खुद को पुनरारंभ करने का संकेत देगा, इसलिए तुरंत ऐसा करें।
प्लगइन को कॉन्फ़िगर करने के लिए, अगली बार जब Ghidra शुरू हो, शीर्ष मेनू पर Tools -> Run Tool -> CodeBrowser पर जाएं। Ghidra कहेगा "New Extensions detected. Would you like to configure them?". हाँ पर क्लिक करें, फिर FastAIRenamerPlugin के आगे चेकबॉक्स पर फिर से टिक करें, फिर Ok पर क्लिक करें। जब CodeBrowser खुले, शीर्ष मेनू पर Window -> Fast AI Renamer पर क्लिक करें, फिर Config बटन पर क्लिक करें। यहां आप अपने AI मॉडल को कॉन्फ़िगर करने में सक्षम होंगे। एक बार हो जाने पर प्लगइन विंडो और खाली CodeBrowser विंडो बंद कर दें।
नोट: यदि आपको प्लगइन लोड करने में कोई समस्या होती है, तो आपको Ghidra में डेवलपर मोड सक्षम करने की आवश्यकता हो सकती है (File -> Configure -> checkbox next to Developer)
नोट: आप CodeBrowser पर जाकर, File -> Configure -> Ghidra Core पर क्लिक करके, फिर नीले configure बटन पर क्लिक करके, "FastAIRenamer" से फ़िल्टर करके, और उसके नाम के आगे चेकबॉक्स चेक किया हुआ है यह सुनिश्चित करके हमेशा जांच सकते हैं कि प्लगइन लोड है या नहीं।
नोट: प्लगइन को अनइंस्टॉल करने के लिए, पहले CodeBrowser खोलें, File -> Configure -> Ghidra Core पर जाएं -> नीले configure बटन पर क्लिक करें -> "FastAIRenamer" से फ़िल्टर करें -> अनचेक करें -> ok। CodeBrowser बंद करें, फिर प्रारंभिक Ghidra विंडो में, File -> Install Extensions पर जाएं -> "FastAIRenamer" को अनचेक करें। अंत में Ghidra बंद करें और ${GHIDRA_HOME}\Extensions\Ghidra से ghidra_12.0.4_PUBLIC_20260427_FastAIRenamerPlugin.zip हटा दें। यह सुनिश्चित करने के लिए कि एक्सटेंशन हटा दिया गया है, अगली बार जब आप Ghidra चलाएं, प्रारंभिक विंडो पर Help -> Runtime Information -> Extension Points पर जाएं -> "FastAIRenamer" से फ़िल्टर करें और सुनिश्चित करें कि कुछ भी न दिखे। वाह।
अंत में, सुनिश्चित करें कि आपके पास Visual Studio या कोई अन्य C++ डेवलपमेंट एनवायरनमेंट सेट है ताकि जब समय आए तो आप crackme का समाधान vibe code कर सकें।
लोगों के स्थानीय AI सेटअप उनके पास उपलब्ध हार्डवेयर और $$$ के आधार पर बहुत भिन्न होंगे। मेरे लिए:
मैं व्यक्तिगत रूप से Nvidia GTX 3080 चला रहा हूं, जिसमें Cuda 13.2 इंस्टॉल है। आप अपने विंडोज टर्मिनल में nvcc --version चलाकर अपना Cuda संस्करण जांच सकते हैं।
मैं bartowski से https://huggingface.co/bartowski/google_gemma-4-E4B-it-GGUF/blob/main/google_gemma-4-E4B-it-Q8_0.gguf quants चला रहा हूं। यह मेरी ओर से अंधविश्वास हो सकता है, लेकिन विश्लेषणात्मक कार्यों के लिए मैं क्वांटाइजेशन की न्यूनतम मात्रा के साथ चलाने की कोशिश करता हूं, और यदि संभव हो तो इसे पूरी तरह से टालता हूं।
मैं अपने इन्फ्रेंस सर्वर के रूप में llama.cpp का उपयोग कर रहा हूं, जिसका यह विशिष्ट संस्करण है: llama-b8893-bin-win-cuda-13.1-x64.
मैं llama.cpp को निम्नलिखित CLI सेटिंग्स के साथ चला रहा हूं:```
..\llama-b8893-bin-win-cuda-13.1-x64\llama-server.exe ^
--port 8090 ^
--threads 12 ^
--n-gpu-layers 256 ^
--no-mmap ^
--model "google_gemma-4-E4B-it-Q8_0.gguf" ^
--ctx-size 32768 ^
--temp 1.0 ^
--top-k 64 ^
--top-p 0.95 ^
--offline
इससे `75 टोकन/सेकंड` मिलता है, जो स्थानीय AI के लिए काफी तेज़ है।
## कोई हार्डवेयर नहीं? कोई समस्या नहीं
यदि आपके पास हार्डवेयर उपलब्ध नहीं है या कॉन्फ़िगरेशन में समस्या हो रही है, फिर भी इस ट्यूटोरियल में साथ चलना चाहते हैं, तो आप OpenRouter लॉगिन प्राप्त कर सकते हैं और मुफ्त API में से एक का उपयोग कर सकते हैं:
https://openrouter.ai/models/?q=free
Google विशेष रूप से `Gemma 4 31B` और `Gemma 4 26B-A4B` अभी मुफ्त (केवल सीमित समय के लिए) ऑफर कर रहा है:
https://openrouter.ai/google/gemma-4-31b-it:free
https://openrouter.ai/google/gemma-4-26b-a4b-it:free
... बस ध्यान रखें कि इन मुफ्त API के साथ, वे धीमी, अविश्वसनीय हैं, और आप उन्हें जो भी डेटा भेजते हैं, संभवतः उनके आंतरिक विश्लेषण में लॉग किया जाएगा और उनके अगले प्रशिक्षण रन में उपयोग किया जाएगा। फिर भी, इस ट्यूटोरियल के उद्देश्यों के लिए, ये API आपको साथ चलने देंगे।
Ghidra प्लगइन को अपने OpenRouter लॉगिन का उपयोग करने के लिए कॉन्फ़िगर करने के लिए, प्लगइन कॉन्फ़िग खोलें और निम्नलिखित दर्ज करें:```
Base URL: https://openrouter.ai/api/
API Key: <your OpenRouter API Key>
Model Name: qwen/qwen3-235b-a22b-2507
यह उदाहरण qwen3 मॉडल के विरुद्ध प्लगइन चलाएगा यहाँ
ज़िप निकालें और crackmepls.exe चलाएँ, आपको एक मानक लॉगिन स्क्रीन दिखाई देगी। कोई भी रैंडम पासवर्ड टाइप करें, और आपको 'पहुँच अस्वीकृत' संदेश मिलेगा:```
User: marko
Pass: 123
Access denied
Ghidra को `ghidraRun.bat` चलाकर खोलें। टूलबार में `File -> New Project` चुनें, `Non-Shared Project` चयनित छोड़ें और `Next >>` पर क्लिक करें, फिर एक खाली प्रोजेक्ट डायरेक्टरी चुनें और प्रोजेक्ट को एक नाम दें। फिर `Finish` पर क्लिक करें।
इसके बाद, `File -> Import File` पर क्लिक करें, फिर इसे प्रोजेक्ट में जोड़ने के लिए `crackmepls.exe` चुनें। Ghidra फ़ाइल के बारे में कुछ विवरण दिखाएगा, जो आपको बताएगा कि यह `x86:LE:64:default:windows` के लिए एक `Portable Executable (PE)` फ़ाइल है। बिना कुछ बदले केवल `OK` पर क्लिक करें। थोड़ी देर बाद, यह फ़ाइल के बारे में कुछ और विवरण दिखाएगा, फिर से केवल `OK` पर क्लिक करें। अंत में, प्रोजेक्ट में `crackmepls.exe` पर डबल-क्लिक करके Code Browser खोलें, ताकि इसे डिस्सेम्बल करना शुरू किया जा सके।
शुरुआत में, Ghidra एक संदेश दिखाएगा जिसमें लिखा होगा `crackmepls.exe का विश्लेषण नहीं किया गया है। क्या आप अभी इसका विश्लेषण करना चाहेंगे?` `Yes` पर क्लिक करें और फिर अगली विंडो में `Analyze` बटन पर क्लिक करें, और फिर Ghidra द्वारा बाइनरी में सभी फंक्शन्स का पता लगाने, डिस्सेम्बल करने और डीकंपाइल करने तक प्रतीक्षा करें। विश्लेषण के दौरान PDB फ़ाइलें न मिलने के बारे में एक या दो त्रुटियाँ मिल सकती हैं, बस `Ok` पर क्लिक करें और उन्हें अनदेखा करें।
एक बार जब Ghidra फ़ाइल का विश्लेषण पूरा कर लेता है, तो आपको नीचे कुछ ऐसा देखना चाहिए:

यह आपका मानक MSVC एंट्रीपॉइंट है। `FUN_14000200c` पर डबल-क्लिक करें और नीचे स्क्रॉल करें।
आपको डीकंपाइलर विंडो में `__p___argv`, `__p___argc` के संदर्भ दिखने चाहिए, और फिर एक फंक्शन कॉल `FUN_140001290` जो उन्हें पैरामीटर के रूप में स्वीकार करता है। यह संभवतः क्रैकमी का `main()` फंक्शन है, इसलिए इस पर डबल-क्लिक करें।

एक बार `FUN_140001290` फंक्शन में आने के बाद, डीकंपाइलर विंडो में थोड़ा नीचे स्क्रॉल करें। आपको स्ट्रिंग्स `User:` और `Pass:` के स्ट्रिंग रेफरेंस दिखाई देंगे, साथ ही `basic_istream` (इनपुट स्ट्रीम) और `basic_ostream` (आउटपुट स्ट्रीम) के रेफरेंस भी दिखाई देंगे।

ये स्ट्रिंग्स उन प्रिंट और इनपुट स्टेटमेंट्स से मेल खाती हैं जो हमने पहली बार क्रैकमी चलाने पर देखे थे, इसलिए हम जानते हैं कि हम क्रैकमी के `main()` फंक्शन में हैं।
अब इस बिंदु पर, वह थकाऊ काम शुरू होता जहाँ हमें वास्तव में बैठकर हर वेरिएबल नाम और फंक्शन कॉल को कुछ सार्थक नाम देना होता, क्योंकि हम विश्लेषण कर रहे हैं कि बाइनरी क्या कर रही है ताकि हम क्रैकमी को हल कर सकें। रिवर्स इंजीनियर्स को पहले यह काम डिस्सेम्बली में हाथ से करना पड़ता था, लेकिन शुक्र है कि तकनीक और कर डॉलर ने हमें यह उपयोगी डीकंपाइलर दिया है जिसे AI के साथ काम करने के लिए स्क्रिप्ट किया जा सकता है।
इसलिए असली काम करने के बजाय, चलिए पीछे बैठते हैं, अपने दिमाग को बंद करते हैं, और अपने स्थानीय AI को हमारे लिए सारा काम करने देते हैं।
इस ट्यूटोरियल की सेटअप के दौरान, आपने फंक्शन और वेरिएबल रीनेमिंग में मदद के लिए एक Ghidra प्लगइन इंस्टॉल किया होगा। अब इसका उपयोग करने का समय है। Ghidra में, शीर्ष पर टूलबार में, `Window` पर क्लिक करें और फिर प्लगइन खोलने के लिए `Fast AI Renamer` पर क्लिक करें।

आपको प्लगइन UI दिखना चाहिए, जो कई बटन और एक टेक्स्टएरिया है। पहले "Config" बटन पर क्लिक करें, और फिर सुनिश्चित करें कि आपने अपने स्थानीय AI से ठीक से बात करने के लिए सब कुछ कॉन्फ़िगर किया है। यहाँ बताया गया है कि यह मेरे कंप्यूटर पर कैसा दिखता है:

आप "Save" पर क्लिक करके कॉन्फ़िगरेशन विंडो बंद कर सकते हैं, और फिर `Rename Variables` बटन पर क्लिक करें। एक ड्रैगन के साथ एक प्रगति विंडो पॉप अप होनी चाहिए, और आप अपने कंप्यूटर को जोर से चलने की आवाज़ सुन सकते हैं क्योंकि यह डीकंपाइलर विंडो में सभी वेरिएबल्स का नाम बदलने के लिए स्थानीय AI मॉडल चलाने का काम करता है:

AI के समाप्त होने के बाद (मेरी मशीन पर लगभग 10-20 सेकंड लगते हैं) आपको प्लगइन के टेक्स्टएरिया में एक विवरण दिखना चाहिए कि इसने किन वेरिएबल्स का नाम बदला है, और डीकंपाइलर विंडो में वेरिएबल्स स्वयं नाम बदलकर दिखने चाहिए:

सभी AI मॉडल, विशेष रूप से छोटे स्थानीय मॉडल, स्वाभाविक रूप से अविश्वसनीय होते हैं, इसलिए आपको इस अंतिम चरण में कुछ त्रुटियाँ दिख सकती हैं। हम इस पर बाद में फिर से बात करेंगे। इस बीच, आप हमेशा `Rename Variables` बटन पर फिर से क्लिक करके AI को फिर से चला सकते हैं, जब तक कि आपको त्रुटियों के बिना परिणाम न मिल जाएं जो आपको संतोषजनक लगें।
## क्रैकमी को हल करना
इस बिंदु पर, हमारे पास कुछ अच्छी तरह से चिह्नित डीकंपाइलर आउटपुट है, जहाँ सभी वेरिएबल नामों को कुछ सार्थक नाम दिए गए हैं। परंपरागत रूप से, एक रिवर्स इंजीनियर अब इस कोड को पढ़ सकता है और क्रैकमी के लिए एक समाधान तैयार करना शुरू कर सकता है। हालाँकि, मैंने सोचा कि यह अच्छा होगा कि स्थानीय AI को हमारे लिए क्रैकमी को हल करने का प्रयास कराया जाए। याद रखें, हम आज अपने दिमाग का उपयोग नहीं करेंगे।
पहले, मैंने AI को `Rename ALL Functions and Variables` बटन पर क्लिक करके पूरे बाइनरी को हमारे लिए चिह्नित करने के लिए कहा। यह इस क्रैकमी फंक्शन में उपयोग किए जा रहे सभी फंक्शन कॉल्स को नाम निर्दिष्ट करने का काम करता है, जो डीकंपाइलर आउटपुट को और भी साफ करता है। मेरी मशीन पर, इसमें लगभग 15 मिनट लगते हैं।
जब आप `Local AI Setup` के तहत सूचीबद्ध CLI पैराम्स के साथ `llama.cpp` का उपयोग कर रहे हैं, तो आप ब्राउज़र में [http://localhost:8090/](http://localhost:8090/) पर नेविगेट करके अपने स्थानीय मॉडल के लिए एक Chat UI खोल सकते हैं।
यहाँ, मैंने क्रैकमी से फंक्शन का डीकंपाइलर आउटपुट दर्ज किया (आप इसे प्लगइन UI में "Decompile Function" बटन पर क्लिक करके, या बस दाईं ओर डीकंपाइलर विंडो से कॉपी करके प्राप्त कर सकते हैं), और AI से हमारे लिए एक समाधान कोड करने के लिए कहा:
======================================================================
======================================================================
======================================================================
मैं चाहता हूँ कि आप एक क्रैकमी के लिए समाधान लिखने में मेरी मदद करें। नीचे एक क्रैकमी से एक फंक्शन की डीकंपाइलर लिस्टिंग है, जहाँ उपयोगकर्ता एक उपयोगकर्ता नाम और एक पासवर्ड टाइप करता है, और फिर पहुँच दी जाती है यदि उन्होंने सही पासवर्ड प्रदान किया है। पासवर्ड फंक्शन में गणना की जाती है।```
/* WARNING: Function: __security_check_cookie replaced with injection: security_check_cookie */
/* **Reasoning:**
The function takes user input (a username/input and a password) via standard input. It processes
the user input by calculating a complex, custom checksum/hash. It then compares the provided
password input against a target buffer (likely a stored hash or secret). Finally, it determines
and outputs whether "Access granted" or "Access denied," indicating the function serves as an
authentication routine. */
undefined8 authenticate_user(undefined8 param_1,undefined8 param_2,undefined8 max_len)
{
uint user_checksum;
int iVar1;
undefined8 ****temp_free_ptr;
ulonglong user_input_byte_index;
char *result_message;
undefined1 *status_buffer;
undefined8 ****data_buffer_ptr;
ulonglong pass_input_length_1;
bool access_granted;
undefined8 uStack_d0;
undefined1 status_buffer_small [8];
undefined1 status_buffer_large [32];
undefined8 ***allocated_ptrs [2];
size_t compare_length;
ulonglong ptr_metadata_size;
undefined8 ***user_input_buffer;
undefined8 uStack_78;
ulonglong user_input_length;
ulonglong user_input_length_param;
undefined8 ***pass_input_buffer;
undefined8 uStack_58;
size_t pass_input_length;
ulonglong pass_input_length_param;
ulonglong checksum_seed;
code *code_pointer;
undefined8 ***pass_input_buffer_ptr;
undefined8 ***user_input_buffer_ptr;
ulonglong user_input_length_1;
status_buffer = status_buffer_small;
checksum_seed = MAGIC_VALUE_1 ^ (ulonglong)status_buffer_small;
user_input_byte_index = 0;
uStack_78 = 0;
user_input_length = 0;
user_input_length_param = 0xf;
user_input_buffer = (undefined8 ****)0x0;
uStack_58 = 0;
pass_input_length = 0;
pass_input_length_param = 0xf;
pass_input_buffer = (undefined8 ****)0x0;
formatted_string_output((basic_ostream<char,struct_std::char_traits<char>_> *)cout_exref,"User: ");
extract_token_from_stream((basic_istream<char,struct_std::char_traits<char>_> *)cin_exref,
(longlong *)&user_input_buffer,max_len);
formatted_string_output((basic_ostream<char,struct_std::char_traits<char>_> *)cout_exref,"Pass: ");
extract_token_from_stream((basic_istream<char,struct_std::char_traits<char>_> *)cin_exref,
(longlong *)&pass_input_buffer,max_len);
user_input_length_1 = user_input_length_param;
user_input_buffer_ptr = user_input_buffer;
user_checksum = 0;
pass_input_length_1 = user_input_byte_index;
if (user_input_length != 0) {
do {
temp_free_ptr = &user_input_buffer;
if (0xf < user_input_length_param) {
temp_free_ptr = (undefined8 ****)user_input_buffer;
}
user_checksum =
((int)user_input_byte_index + 1) *
(int)*(char *)((longlong)temp_free_ptr + user_input_byte_index) +
(int)pass_input_length_1;
user_checksum = user_checksum * 8 ^ user_checksum;
user_input_byte_index = user_input_byte_index + 1;
pass_input_length_1 = (ulonglong)user_checksum;
} while (user_input_byte_index < user_input_length);
}
int_to_string_dynamic(allocated_ptrs,user_checksum * 0x539 ^ 0x5a5a);
pass_input_length_1 = pass_input_length_param;
pass_input_buffer_ptr = pass_input_buffer;
temp_free_ptr = &pass_input_buffer;
if (0xf < pass_input_length_param) {
temp_free_ptr = (undefined8 ****)pass_input_buffer;
}
data_buffer_ptr = allocated_ptrs;
if (0xf < ptr_metadata_size) {
data_buffer_ptr = (undefined8 ****)allocated_ptrs[0];
}
if (compare_length == pass_input_length) {
if (compare_length == 0) {
access_granted = true;
}
else {
iVar1 = memcmp(data_buffer_ptr,temp_free_ptr,compare_length);
access_granted = iVar1 == 0;
}
}
else {
access_granted = false;
}
if (0xf < ptr_metadata_size) {
temp_free_ptr = (undefined8 ****)allocated_ptrs[0];
status_buffer = status_buffer_small;
if (0xfff < ptr_metadata_size + 1) {
temp_free_ptr = (undefined8 ****)allocated_ptrs[0][-1];
data_buffer_ptr =
(undefined8 ****)((longlong)allocated_ptrs[0] + (-8 - (longlong)temp_free_ptr));
status_buffer = status_buffer_small;
if ((undefined8 ****)0x1f < data_buffer_ptr) {
code_pointer = (code *)swi(0x29);
(*code_pointer)(5);
temp_free_ptr = data_buffer_ptr;
status_buffer = status_buffer_large;
}
}
*(undefined8 *)(status_buffer + -8) = 0x140001424;
free(temp_free_ptr);
}
result_message = "Access granted\n";
if (!access_granted) {
result_message = "Access denied\n";
}
*(undefined8 *)(status_buffer + -8) = 0x140001443;
formatted_string_output((basic_ostream<char,struct_std::char_traits<char>_> *)cout_exref,result_message);
if (0xf < pass_input_length_1) {
temp_free_ptr = (undefined8 ****)pass_input_buffer_ptr;
if (0xfff < pass_input_length_1 + 1) {
temp_free_ptr = (undefined8 ****)pass_input_buffer_ptr[-1];
data_buffer_ptr =
(undefined8 ****)((longlong)pass_input_buffer_ptr + (-8 - (longlong)temp_free_ptr));
if ((undefined8 ****)0x1f < data_buffer_ptr) {
code_pointer = (code *)swi(0x29);
(*code_pointer)(5);
status_buffer = status_buffer + 8;
temp_free_ptr = data_buffer_ptr;
}
}
*(undefined8 *)(status_buffer + -8) = 0x14000147d;
free(temp_free_ptr);
}
if (0xf < user_input_length_1) {
temp_free_ptr = (undefined8 ****)user_input_buffer_ptr;
if (0xfff < user_input_length_1 + 1) {
temp_free_ptr = (undefined8 ****)user_input_buffer_ptr[-1];
data_buffer_ptr =
(undefined8 ****)((longlong)user_input_buffer_ptr + (-8 - (longlong)temp_free_ptr));
if ((undefined8 ****)0x1f < data_buffer_ptr) {
code_pointer = (code *)swi(0x29);
(*code_pointer)(5);
status_buffer = status_buffer + 8;
temp_free_ptr = data_buffer_ptr;
}
}
*(undefined8 *)(status_buffer + -8) = 0x1400014b7;
free(temp_free_ptr);
}
*(undefined8 *)(status_buffer + -8) = 0x1400014c9;
return 0;
}
कृपया डिकंपाइलर लिस्टिंग पढ़ें, और फिर दर्ज किए गए उपयोगकर्ता नाम के लिए सही पासवर्ड की गणना करने के लिए नीचे दिए गए मेरे C++ कोड को पूरा करें:``` #include #include #include #include
int main() { std::string username; std::cout << "User: "; std::cin >> username;
uint32_t password_value = 0;
std::cout << "Pass: " << password_value << "\n";
return 0;
}
======================================================================
======================================================================
======================================================================
## उत्पन्न समाधान
पहली बार जब इसने एक समाधान उत्पन्न किया, तो वह गलत था। मैंने इसे इस रिपॉजिटरी में [tut1.txt](https://github.com/markoglasgow/gemma_crackme_tutorial/blob/HEAD/tut1.txt) में शामिल किया है। पहली नज़र में समाधान _सही दिखता है_, लेकिन जब हम इसे आज़माते हैं तो यह विफल हो जाता है। AI मौलिक रूप से अविश्वसनीय हैं, और यह इसका एक आदर्श उदाहरण है।
कोई बात नहीं, हम बस एक नया चैट खोल सकते हैं, उपरोक्त प्रॉम्प्ट को कॉपी-पेस्ट कर सकते हैं, और AI से हमारे लिए एक नया समाधान उत्पन्न करने का प्रयास कर सकते हैं। मेरे मामले में, मुझे दूसरे प्रयास में एक कार्यशील समाधान मिल गया:```
#include <cstdio>
#include <cstdint>
#include <string>
#include <iostream>
#include <sstream>
#include <iomanip>
/**
* @brief Simulates the custom checksum calculation defined in the crackme function.
*
* @param username The input string (username).
* @return int The final calculated user_checksum.
*/
int calculate_username_checksum(const std::string& username) {
// user_checksum is treated as an 'int' (32-bit signed integer) during the core calculation.
int user_checksum = 0;
// pass_input_length_1 acts as the state variable and is a ulonglong (64-bit).
// It starts at 0, as pass_input_length_1 = user_input_byte_index (which is 0) initially.
uint64_t state_pass_length_1 = 0;
for (size_t i = 0; i < username.length(); ++i) {
// i = user_input_byte_index
// Step 1: Calculate the intermediate value T1.
// The decompiler implies this calculation happens using standard int math
// (truncating/wrapping if intermediate values exceed 32 bits).
// (int)user_input_byte_index + 1
int index_term = (int)(i + 1);
// (int)*(char *)((longlong)temp_free_ptr + user_input_byte_index)
// This is just the ASCII value of the character.
int char_term = (int)username[i];
// (int)pass_input_length_1
// The state is cast down to an int for this arithmetic operation.
int state_term = (int)state_pass_length_1;
// T1 = ((int)user_input_byte_index + 1) * (int)char + (int)state
int intermediate_t1 = index_term * char_term + state_term;
// Step 2: Apply the XOR/Multiplication hash function.
// user_checksum = user_checksum * 8 ^ user_checksum;
// Note: The original code structure suggests the update happens here,
// but since we are recalculating the hash step-by-step, we use the definition:
// user_checksum = T1 * 8 ^ T1;
// Since the original code overwrites user_checksum completely with the hash
// of the current character, we use the T1 derived above.
int current_checksum = intermediate_t1;
current_checksum = current_checksum * 8 ^ current_checksum;
// Update the final running checksum
user_checksum = current_checksum;
// Step 3: Update the state variable for the next iteration.
state_pass_length_1 = (uint64_t)user_checksum;
}
return user_checksum;
}
int main()
{
std::string username;
std::cout << "User: ";
std::cin >> username;
// 1. Calculate the final checksum using the username
int final_user_checksum = calculate_username_checksum(username);
// 2. Calculate the final hash/secret value
// FinalHashValue = user_checksum * 0x539 ^ 0x5a5a
// Note: The arithmetic here is done on the final 32-bit 'int' checksum.
uint32_t final_hash_value = (uint32_t)final_user_checksum * 0x539 ^ 0x5a5a;
// 3. Convert the final hash value into a string (as implied by int_to_string_dynamic)
// Since the password needs to match the memory contents (memcmp), it must be the string representation.
std::stringstream ss;
ss << final_hash_value;
std::string required_password = ss.str();
// We output the required password string.
std::cout << "Pass: " << required_password << "\n";
return 0;
}
मैंने उस चैट को शामिल किया जिसने समाधान उत्पन्न किया है, जैसा कि solution_chat.html इस रिपॉजिटरी में है। इसमें तर्क स्ट्रीम भी शामिल था, जो मुझे लगता है कि स्थानीय रूप से चलने वाले इतने छोटे मॉडल के लिए काफी अच्छा था।
खैर, हम विज़ुअल स्टूडियो के साथ समाधान को संकलित कर सकते हैं और फिर एक मान्य उपयोगकर्ता नाम/पासवर्ड कॉम्बो उत्पन्न करने के लिए इसे चला सकते हैं। फिर हम उपयोगकर्ता नाम/पासवर्ड को क्रैकमी में इनपुट करते हैं, और सत्यापित करते हैं कि हमने इसे हल कर लिया है:

gg
जैसा कि हमने इस ट्यूटोरियल में कई बार देखा है, AI स्वाभाविक रूप से अविश्वसनीय है। नीचे दिए गए दो स्क्रीनशॉट पर विचार करें:


पहला स्क्रीनशॉट ट्यूटोरियल में पहले का है, जबकि दूसरा स्क्रीनशॉट उसी चरण में लिया गया था, बस AI के नाम बदलने के एक अलग रीरन के साथ। डीकंपाइलर विंडो में ध्यान दें, ऊपर वाले स्क्रीनशॉट में पासवर्ड इनपुट बफर को &pass_input_buffer लेबल किया गया है, जबकि नीचे वाले स्क्रीनशॉट में उसी बफर को &username_buffer_1 लेबल किया गया है। नीचे वाले स्क्रीनशॉट में, AI हमसे झूठ बोल रहा है, और हम उन झूठों को "भ्रम (hallucinations)" कहते हैं।
सभी AI आपसे झूठ बोलेंगे और भ्रम (hallucinate) करेंगे। मॉडल जितना छोटा होगा, भ्रम उतनी ही बार होगा। मॉडल जितना बड़ा होगा, आप उतने ही भाग्यशाली होंगे और वे उतना कम भ्रम करेंगे। आप जितना अधिक भुगतान करेंगे, उतने ही भाग्यशाली होंगे। हालाँकि, कोई भी AI मॉडल भ्रम से अछूता नहीं है, और आपको उनके साथ काम करते समय इसे कभी नहीं भूलना चाहिए।
AI उन कार्यों के लिए सबसे उपयुक्त है जहाँ आप आउटपुट को सत्यापित कर सकते हैं, या आउटपुट इतना कम महत्वपूर्ण है कि पूर्ण सटीकता आवश्यक नहीं है। रिवर्स इंजीनियरिंग के मामले में, AI हमें किसी अज्ञात बाइनरी पर जल्दी से एक नज़र डालने में मदद कर सकता है और हमें बहुत सारे थकाऊ मैनुअल काम से बचा सकता है। लेकिन जैसा कि हमने खराब नाम बदलने के साथ देखा है, यह हमें गलत रास्ते पर भी ले जा सकता है। AI-जनित परिणामों के साथ हमेशा सावधानी बरतें, और यदि संभव हो तो उन्हें हमेशा सत्यापित करें।
AI का उपयोग करने का सबसे अच्छा तरीका इसके परिणामों को स्वचालित रूप से सत्यापित करना है। उदाहरण के लिए, यदि आप AI से कोड लिखवा रहे हैं, तो एक स्क्रिप्ट होना बहुत अच्छा होगा जो AI के आउटपुट को आपके द्वारा हाथ से लिखे गए कुछ परीक्षण सूट के विरुद्ध परीक्षण करे। हमारे क्रैकमी के लिए AI द्वारा समाधान उत्पन्न करने के मामले में, हम एक परीक्षण सूट लिख सकते थे जो प्रत्येक AI-जनित समाधान को स्वचालित रूप से क्रैकमी के विरुद्ध परीक्षण करता, और फिर एक ऐसे समाधान को स्वीकार करता जो हजारों विभिन्न उपयोगकर्ता नामों के लिए अच्छे पासवर्ड उत्पन्न करता। फिर हम AI को एक लूप में चला सकते थे, जब तक कि वह एक ऐसा समाधान न लिख दे जो हमारे परीक्षणों में पास हो जाए।
हमने इस क्रैकमी को अपेक्षाकृत छोटे मॉडल (Gemma 4 E4B) से हल किया। इस मॉडल में 8 बिलियन पैरामीटर हैं, जबकि SotA मॉडल जिनमें सैकड़ों बिलियन या खरबों पैरामीटर होते हैं से अलग है। एक छोटा मॉडल अच्छी तरह से तर्क नहीं कर पाएगा, अधिक बार भ्रम करेगा, और जैसे-जैसे इसकी संदर्भ विंडो भरती जाएगी, यह और अधिक बेकार होता जाएगा।
एक वास्तविक रिवर्स इंजीनियरिंग अभ्यास में, हमें ऐसे फ़ंक्शन को रिवर्स इंजीनियर करना पड़ सकता है जो हमारे छोटे मॉडल की संदर्भ विंडो से बड़े हों, या जिनमें ऐसा तर्क हो जो इसकी सीमित बुद्धि के लिए समझने में बहुत जटिल हो। हमें अपने मॉडलों का उपयोग करते समय उनकी सीमाओं से परिचित होने की आवश्यकता है, ताकि हम पहले से समझ सकें कि वे हमें कहाँ और कैसे विफल करेंगे।
जैसे-जैसे उपभोक्ता हार्डवेयर मॉडल आर्किटेक्चर के साथ-साथ आगे बढ़ता है, मेरा मानना है कि हम बड़े और बेहतर मॉडलों को स्थानीय रूप से चला पाएंगे, जो हमारे स्थानीय मॉडलों के लिए और भी अधिक उन्नत रिवर्स इंजीनियरिंग कार्यों को पहुंच में लाएंगे।
मेरे द्वारा लिखा गया "फास्ट AI रीनेमर" Ghidra प्लगइन वर्तमान में प्रत्येक फ़ंक्शन के लिए एक सारांश लिखता है जिसका वह विश्लेषण करता है, और इसमें उस प्रोजेक्ट के C और asm लिस्टिंग को बड़े पैमाने पर निर्यात करने की क्षमता है जिस पर वह काम कर रहा है। मैं एक ऐसा उपकरण लिखना चाहूंगा जो प्रत्येक फ़ंक्शन सारांश के लिए एक एम्बेडिंग की गणना करता है और फिर इसे वेक्टर डेटाबेस या SQLite में अनुक्रमित करता है, और फिर हम प्राकृतिक भाषा प्रश्नों के अनुसार फ़ंक्शन खोजने के लिए डेटाबेस से क्वेरी कर सकते हैं।
Ghidra प्लगइन वर्तमान में नाम बदलने के लिए मल्टी-टर्न बातचीत के बिना एक सरल वर्कफ़्लो का उपयोग करता है, और अपने कार्य को करने के लिए AI आउटपुट से CSV को पार्स करने पर निर्भर करता है। मैंने चीजों को इस तरह से लागू किया क्योंकि मुझे नहीं लगता था कि एक छोटा AI मॉडल एक एजेंटिक वर्कफ़्लो में निहित लंबी बातचीत और कई टूल कॉल को संभाल पाएगा। मैं गलत हो सकता हूँ, और AI को प्रॉम्प्ट करने के वैकल्पिक तरीकों का पता लगाया जाना चाहिए। फिर भी, छोटे स्थानीय मॉडलों के लिए, मुझे लगता है कि संदर्भ विंडो के आकार को जितना संभव हो उतना सीमित करना सबसे अच्छा है।
मुझे यकीन नहीं है कि Ghidra प्लगइन प्रॉम्प्ट इंजेक्शन के लिए कितना असुरक्षित है, और किसी भी सुरक्षा पेशेवर को मैलवेयर या शत्रुतापूर्ण बाइनरी का विश्लेषण करने के लिए इसका उपयोग करने से सावधान रहना चाहिए। सामान्य तौर पर, यह सभी AI-संचालित रिवर्स इंजीनियरिंग टूल पर लागू होता है, जिनमें लोकलहोस्ट बिना प्रमाणीकरण के MCP सर्वर हो सकते हैं, या संवेदनशील कार्यों के लिए उपलब्ध उपकरण हो सकते हैं जो कोड निष्पादन प्राप्त कर सकते हैं। सावधान रहें।
यह देखते हुए कि हम रिवर्स इंजीनियरिंग के लिए एक छोटे सामान्य-उद्देश्य वाले मॉडल का उपयोग कर रहे हैं, मुझे आश्चर्य है कि क्या बड़े और अधिक बुद्धिमान मॉडल से डिस्टिल्ड वार्तालापों पर फाइन-ट्यूनिंग करके इसके प्रदर्शन में कितना सुधार किया जा सकता है?
अंत में, Ghidra प्लगइन की C/asm निर्यात कार्यक्षमता Claude Code के साथ अच्छी तरह से काम करती है, और मैं पहली बार क्रैकमी के लिए एक सही समाधान लिखने के लिए Claude Code + Sonnet का उपयोग करने में सक्षम था, बस इसे क्रैकमी के चिह्नित डीकंपिलेशन को खिलाकर और इसे एक समाधान खोजने के लिए कहकर। मुझे आश्चर्य है कि क्या कोई ऐसी स्किल्स हैं जिन्हें हम Claude Code के लिए लिख सकते हैं जो इसे अपने डीकंपाइलर आउटपुट की टेक्स्ट फ़ाइलों के माध्यम से बाइनरी का विश्लेषण करने में बेहतर बना सकती हैं, और इसे विश्लेषण करने के लिए Ghidra के लिए MCP सर्वर से नहीं गुजरना पड़े। उदाहरण के लिए, हम Claude Code के लिए एक स्किल शुरू कर सकते हैं जो इसे बाइनरी की वर्चुअल मेमोरी में बाइट्स का निरीक्षण करने देगा, ताकि यदि यह डिस्सेम्बली/डीकंपाइलर लिस्टिंग से कुछ डेटा को संदर्भित करता हुआ देखता है, तो यह देख सके कि उस मेमोरी में क्या है, बिना Ghidra के माध्यम से जाए।
Kryptos को क्रैकमी बनाने के लिए, और Crackmes.one को इसे होस्ट करने के लिए धन्यवाद। मैं उनकी सराहना करता हूँ कि उन्होंने वहाँ से उठाया जहाँ crackmes.de छोड़ा था (शांति में रहे)।
Google को एक अद्भुत और उपयोगी स्थानीय मॉडल जारी करने के लिए धन्यवाद।
NSA को मेरे कर डॉलर का उपयोग करके एक शानदार रिवर्स इंजीनियरिंग टूल बनाने के लिए धन्यवाद, और Ryan Kurtz को Ghidra Github पर प्रदान किए गए सभी समर्थन के लिए धन्यवाद।