
स्क्रिप्टेबल बाइनरी एमुलेशन फ्रेमवर्क जो IDA Pro/Radare2 को Unicorn इंजन के साथ एकीकृत करता है, जो x86, ARM, और ARM64 आर्किटेक्चर पर स्वचालित मैलवेयर विश्लेषण, स्ट्रिंग डिक्रिप्शन और कोड पथ अन्वेषण के लिए उपयोग होता है।
flare-emu एक समर्थित बाइनरी विश्लेषण ढांचे, जैसे IDA Pro या Radare2, को Unicorn के एमुलेशन ढांचे के साथ जोड़ता है ताकि उपयोगकर्ता को एमुलेशन कार्यों की स्क्रिप्टिंग के लिए एक उपयोग में आसान और लचीला इंटरफ़ेस प्रदान किया जा सके। इसे आपके समर्थित आर्किटेक्चर के लिए एक लचीला और मजबूत एमुलेटर स्थापित करने के सभी प्रबंधन को संभालने के लिए डिज़ाइन किया गया है ताकि आप अपनी कोड विश्लेषण समस्याओं को हल करने पर ध्यान केंद्रित कर सकें। वर्तमान में, flare-emu x86, x86_64, ARM, और ARM64 आर्किटेक्चर का समर्थन करता है।
यह वर्तमान में आपकी एमुलेशन आवश्यकताओं को पूरा करने के लिए पाँच अलग-अलग इंटरफ़ेस प्रदान करता है, साथ ही संबंधित सहायक और उपयोगिता कार्यों की एक श्रृंखला भी प्रदान करता है।
emulateRange – यह API उपयोगकर्ता-निर्दिष्ट संदर्भ के भीतर निर्देशों की एक श्रेणी, या एक फ़ंक्शन का अनुकरण करने के लिए उपयोग किया जाता है। यह व्यक्तिगत निर्देशों और जब "call" निर्देशों का सामना किया जाता है, दोनों के लिए उपयोगकर्ता-परिभाषित हुक के विकल्प प्रदान करता है। उपयोगकर्ता तय कर सकता है कि एमुलेटर फ़ंक्शन कॉल को छोड़ देगा या उनमें प्रवेश करेगा। यह इंटरफ़ेस उपयोगकर्ता को दिए गए रजिस्टरों और स्टैक तर्कों के लिए मान निर्दिष्ट करने का एक आसान तरीका प्रदान करता है। यदि एक बाइटस्ट्रिंग निर्दिष्ट की जाती है, तो इसे एमुलेटर की मेमोरी में लिखा जाता है और पॉइंटर को रजिस्टर या स्टैक वेरिएबल में लिखा जाता है। एमुलेशन के बाद, उपयोगकर्ता एमुलेटेड मेमोरी या रजिस्टरों से डेटा पढ़ने के लिए flare-emu के उपयोगिता कार्यों का उपयोग कर सकता है, या सीधे जांच के लिए वापस आए Unicorn एमुलेशन ऑब्जेक्ट का उपयोग कर सकता है। emulateRange के लिए एक छोटा रैपर फ़ंक्शन, जिसे emulateSelection नाम दिया गया है, का उपयोग IDA Pro में वर्तमान में हाइलाइट किए गए निर्देशों की श्रेणी का अनुकरण करने के लिए किया जा सकता है।
iterate - यह API किसी फ़ंक्शन के भीतर विशिष्ट शाखाओं पर एमुलेशन को बलपूर्वक ले जाने के लिए उपयोग किया जाता है ताकि किसी दिए गए लक्ष्य तक पहुंचा जा सके। उपयोगकर्ता लक्ष्य पतों की एक सूची, या एक फ़ंक्शन का पता निर्दिष्ट कर सकता है जिससे फ़ंक्शन के क्रॉस-रेफरेंस की सूची को लक्ष्य के रूप में उपयोग किया जाता है, साथ ही जब कोई लक्ष्य प्राप्त होता है तो एक कॉलबैक भी प्रदान किया जाता है। लक्ष्यों तक पहुंचा जाएगा, भले ही एमुलेशन के दौरान ऐसी स्थितियां हों जिनके कारण भिन्न शाखाएं ली गई हों। emulateRange API की तरह, व्यक्तिगत निर्देशों और जब "call" निर्देशों का सामना किया जाता है, दोनों के लिए उपयोगकर्ता-परिभाषित हुक के विकल्प प्रदान किए जाते हैं। iterate API का एक उदाहरण उपयोग हमारे argtracker टूल के समान कुछ प्राप्त करना है।
iterateAllPaths - यह API काफी हद तक iterate के समान है, सिवाय इसके कि एक लक्ष्य पता या पते प्रदान करने के बजाय, आप एक लक्ष्य फ़ंक्शन प्रदान करते हैं जिसके माध्यम से यह सभी पथ खोजने और अनुकरण करने का प्रयास करेगा। यह तब उपयोगी होता है जब आप कोड विश्लेषण कर रहे हों जो किसी फ़ंक्शन के हर बेसिक ब्लॉक तक पहुंचना चाहता हो।
emulateBytes – यह API बाहरी शेलकोड के एक ब्लॉब को सरलता से अनुकरण करने का एक तरीका प्रदान करता है। प्रदान किए गए बाइट्स IDB में नहीं जोड़े जाते हैं और जैसे हैं वैसे ही अनुकरण किए जाते हैं। यह एमुलेशन वातावरण तैयार करने के लिए उपयोगी हो सकता है। उदाहरण के लिए, flare-emu स्वयं इस API का उपयोग ARM64 CPU के लिए एक मॉडल विशिष्ट रजिस्टर (MSR) में हेरफेर करने के लिए करता है जो Unicorn द्वारा उजागर नहीं किया गया है ताकि वेक्टर फ्लोटिंग पॉइंट (VFP) निर्देशों और रजिस्टर एक्सेस को सक्षम किया जा सके। Unicorn एमुलेशन ऑब्जेक्ट उपयोगकर्ता द्वारा आगे की जांच के लिए वापस किया जाता है।
emulateFrom - यह API उन मामलों में उपयोगी है जहां फ़ंक्शन सीमाएं स्पष्ट रूप से परिभाषित नहीं होती हैं, जैसा कि अक्सर अस्पष्ट बायनरी या शेलकोड के साथ होता है। आप एक प्रारंभिक पता प्रदान करते हैं, और यह तब तक अनुकरण करेगा जब तक अनुकरण करने के लिए कुछ नहीं बचता या आप अपने किसी हुक में अनुकरण रोक देते हैं। IDA Pro के साथ, इसे strict पैरामीटर को False पर सेट करके बुलाया जा सकता है ताकि गतिशील कोड खोज सक्षम हो सके; flare-emu IDA Pro को एमुलेशन के दौरान सामने आने वाले निर्देशों को बनाने के लिए कहेगा।
flare-emu को IDA Pro के लिए स्थापित करने के लिए, बस flare_emu.py, flare_emu_ida.py, और flare_emu_hooks.py को अपने IDA Pro के python निर्देशिका में डालें और इसे अपने IDAPython स्क्रिप्ट में एक मॉड्यूल के रूप में आयात करें।
flare-emu को Rizin के लिए स्थापित करने के लिए, बस सुनिश्चित करें कि flare_emu.py, flare_emu_rizin.py, और flare_emu_hooks.py मॉड्यूल आयात करने के लिए Python के खोज पथ में हैं। जब Rizin का उपयोग flare-emu के लिए बाइनरी विश्लेषण घटक के रूप में किया जाता है, तो rzpipe आवश्यक है।
flare-emu को Radare2 के लिए स्थापित करने के लिए, बस सुनिश्चित करें कि flare_emu.py, flare_emu_radare.py, और flare_emu_hooks.py मॉड्यूल आयात करने के लिए Python के खोज पथ में हैं। जब Radare2 का उपयोग flare-emu के लिए बाइनरी विश्लेषण घटक के रूप में किया जाता है, तो r2pipe आवश्यक है।
किसी भी स्थिति में, flare-emu Unicorn और इसके Python बाइंडिंग पर निर्भर करता है।
महत्वपूर्ण नोट
flare-emu को नए IDA Pro 7x API का उपयोग करके लिखा गया था, यह IDA Pro के पिछले संस्करणों के साथ पिछड़ा संगत नहीं है।
जबकि flare-emu का उपयोग कई अलग-अलग कोड विश्लेषण समस्याओं को हल करने के लिए किया जा सकता है, इसका एक सामान्य उपयोग मैलवेयर बायनरी में स्ट्रिंग्स को डिक्रिप्ट करने में सहायता करना है। FLOSS एक बेहतरीन टूल है जो अक्सर आपके लिए स्वचालित रूप से ऐसा कर सकता है, स्ट्रिंग डिक्रिप्ट करने वाले फ़ंक्शन(ओं) की पहचान करने का प्रयास करके और हर क्रॉस-रेफरेंस पर पास की गई स्ट्रिंग्स को डिक्रिप्ट करने के लिए एमुलेशन का उपयोग करके। हालांकि, FLOSS के लिए हमेशा इन फ़ंक्शनों की पहचान करना और अपने सामान्य दृष्टिकोणों का उपयोग करके उन्हें ठीक से अनुकरण करना संभव नहीं है। कभी-कभी आपको थोड़ा और काम करना पड़ता है, और यह वह जगह है जहां flare-emu आपका बहुत समय बचा सकता है जब आप इसके साथ सहज हो जाते हैं। आइए एक सामान्य परिदृश्य पर चलते हैं जो एक मैलवेयर विश्लेषक एन्क्रिप्टेड स्ट्रिंग्स से निपटने के दौरान सामना करता है।
आपने एक x86_64 बायनरी में सभी स्ट्रिंग्स को डिक्रिप्ट करने वाले फ़ंक्शन की पहचान कर ली है। यह फ़ंक्शन हर जगह कॉल किया जाता है और कई अलग-अलग स्ट्रिंग्स को डिक्रिप्ट करता है। IDA Pro में, आप इस फ़ंक्शन का नाम decryptString रखते हैं। यहाँ आपकी flare-emu स्क्रिप्ट है जो इन सभी स्ट्रिंग्स को डिक्रिप्ट करेगी और प्रत्येक फ़ंक्शन कॉल पर डिक्रिप्ट किए गए स्ट्रिंग्स के साथ टिप्पणियाँ रखेगी, साथ ही प्रत्येक डिक्रिप्टेड स्ट्रिंग और उस पते को लॉग करेगी जिस पर इसे डिक्रिप्ट किया गया है।```
from future import print_function
import flare_emu
def decrypt(argv): myEH = flare_emu.EmuHelper() myEH.emulateRange(myEH.analysisHelper.getNameAddr("decryptString"), registers = {"arg1":argv[0], "arg2":argv[1], "arg3":argv[2], "arg4":argv[3]}) return myEH.getEmuString(argv[0])
def iterateCallback(eh, address, argv, userData): s = decrypt(argv) print("%s: %s" % (eh.hexString(address), s)) eh.analysisHelper.setComment(address, s, False)
if name == 'main':
eh = flare_emu.EmuHelper()
eh.iterate(eh.analysisHelper.getNameAddr("decryptString"), iterateCallback)
`__main__` में, हम `flare-emu` से `EmuHelper` वर्ग का एक उदाहरण बनाकर शुरू करते हैं। यह वह वर्ग है जिसका उपयोग हम `flare-emu` के साथ सब कुछ करने के लिए करते हैं। इसके बाद, हम `iterate` API का उपयोग करते हैं, जिसमें हम अपने `decryptString` फ़ंक्शन का पता और अपने कॉलबैक फ़ंक्शन का नाम देते हैं जिसे `EmuHelper` प्रत्येक क्रॉस-रेफरेंस के लिए कॉल करेगा जिसका अनुकरण किया जाता है।
`iterateCallback` फ़ंक्शन `EmuHelper` इंस्टेंस प्राप्त करता है, जिसे यहाँ `eh` नाम दिया गया है, साथ ही क्रॉस-रेफरेंस का पता, इस विशेष कॉल को दिए गए तर्क, और यहाँ एक विशेष शब्दकोश जिसे `userData` नाम दिया गया है। `userData` का उपयोग इस सरल उदाहरण में नहीं किया गया है, लेकिन इसे अपने एमुलेटर के लिए एक स्थायी संदर्भ के रूप में सोचें जहाँ आप अपना कस्टम डेटा संग्रहीत कर सकते हैं। हालाँकि, सावधान रहें, क्योंकि `flare-emu` स्वयं भी अपने कार्यों को करने के लिए आवश्यक महत्वपूर्ण जानकारी संग्रहीत करने के लिए इस शब्दकोश का उपयोग करता है। ऐसा ही एक डेटा `EmuHelper` इंस्टेंस ही है, जो "EmuHelper" कुंजी में संग्रहीत है। यदि आप रुचि रखते हैं, तो इस शब्दकोश के बारे में अधिक जानने के लिए स्रोत कोड खोजें। यह कॉलबैक फ़ंक्शन केवल `decrypt` फ़ंक्शन को कॉल करता है, डिक्रिप्टेड स्ट्रिंग को प्रिंट करता है और `decryptString` के उस कॉल के पते पर इसके लिए एक टिप्पणी बनाता है।
`decrypt` `EmuHelper` का दूसरा उदाहरण बनाता है जिसका उपयोग `decryptString` फ़ंक्शन का अनुकरण करने के लिए किया जाता है, जो हमारे लिए स्ट्रिंग को डिक्रिप्ट करेगा। इस `decryptString` फ़ंक्शन का प्रोटोटाइप इस प्रकार है: `char * decryptString(char *text, int textLength, char *key, int keyLength)`। यह स्ट्रिंग को जगह में डिक्रिप्ट करता है। हमारा `decrypt` फ़ंक्शन `iterateCallback` फ़ंक्शन द्वारा प्राप्त तर्कों को `EmuHelper` के `emulateRange` API के हमारे कॉल में पास करता है। चूँकि यह एक `x86_64` बाइनरी है, कॉलिंग कन्वेंशन तर्कों को पास करने के लिए स्टैक के बजाय रजिस्टरों का उपयोग करता है। `flare-emu` IDA Pro द्वारा निर्धारित बाइनरी के आर्किटेक्चर और फ़ाइल प्रारूप के आधार पर स्वचालित रूप से निर्धारित करता है कि कौन से रजिस्टर किस तर्क का प्रतिनिधित्व करते हैं, जिससे आप कम से कम कुछ हद तक आर्किटेक्चर-अज्ञेयवादी कोड लिख सकते हैं। यदि यह 32-बिट `x86` होता, तो आप तर्कों को पास करने के लिए इसके बजाय `stack` तर्क का उपयोग करते, इस प्रकार: `myEH.emulateRange(myEH.analysisHelper.getNameAddr("decryptString"), stack = [0, argv[0], argv[1], argv[2], argv[3]])`। `x86` में पहला स्टैक मान रिटर्न एड्रेस होता है, इसलिए हम यहाँ प्लेसहोल्डर मान के रूप में `0` का उपयोग करते हैं। अनुकरण पूरा होने के बाद, हम फ़ंक्शन को दिए गए पहले तर्क द्वारा इंगित मेमोरी स्थान में संग्रहीत नल-टर्मिनेटेड स्ट्रिंग को पुनः प्राप्त करने के लिए `getEmuString` API को कॉल करते हैं।
### flare-emu और idalib
* IDA Pro स्थापित करें
* Hex-Rays उपयोगकर्ता गाइड के अनुसार idalib स्थापित करें
* (वर्चुअल वातावरण सक्रिय करें)
* pip install /path/to/IDA/installation/idalib/python
* python /path/to/IDA/installation/idalib/python/py-activate-idalib.py [-d /path/to/active/IDA/installation]
* idapro आयात करें और अपनी स्क्रिप्ट लिखें
* एक उदाहरण के लिए tests/test_flare_emu_idalib.py देखें
### Rizin के साथ आसान स्ट्रिंग डिक्रिप्शन परिदृश्य
उसी उदाहरण का उपयोग करते हुए, Rizin के साथ काम करने पर बहुत अधिक परिवर्तन नहीं होता है, IDA Pro के विपरीत। एक अंतर यह है कि `flare-emu` वर्तमान में Rizin के साथ काम करते समय एक कमांड-लाइन स्क्रिप्ट या Python शेल के भीतर चलाने के लिए डिज़ाइन किया गया है। Python शेल तदर्थ समस्या समाधान के लिए बहुत अच्छा है जबकि कमांड-लाइन स्क्रिप्ट बैच प्रोसेसिंग के लिए बहुत अच्छी है। ऊपर स्क्रिप्ट का Rizin संस्करण इस तरह दिखता है (आप इसे rizin के अंदर चलाने के लिए नमूना पथ को छोड़ भी सकते हैं):```
from __future__ import print_function
import sys
import flare_emu
def decrypt(argv, eh):
myEH = flare_emu.EmuHelper(samplePath=sys.argv[1], emuHelper=eh, isRizin=True)
myEH.emulateRange(
myEH.analysisHelper.getNameAddr("decryptString"),
registers={
"arg1": argv[0],
"arg2": argv[1],
"arg3": argv[2],
"arg4": argv[3],
},
)
return myEH.getEmuString(argv[0])
def iterateCallback(eh, address, argv, userData):
s = decrypt(argv, eh)
print("%s: %s" % (eh.hexString(address), s))
eh.analysisHelper.setComment(address, s, False)
if __name__ == "__main__":
eh = flare_emu.EmuHelper(samplePath=sys.argv[1], isRizin=True)
rz = eh.analysisHelper.r
eh.analysisHelper.setName(0x100000D60, "decryptString")
eh.iterate(eh.analysisHelper.getNameAddr("decryptString"), iterateCallback)
ऊपर दिए गए उसी उदाहरण का उपयोग करते हुए, IDA Pro के बजाय Radare2 के साथ काम करने पर बहुत कुछ नहीं बदलता है। एक अंतर यह है कि flare-emu वर्तमान में Radare2 के साथ काम करते समय एक कमांड-लाइन स्क्रिप्ट या Python शेल के भीतर चलाने के लिए डिज़ाइन किया गया है। Python शेल तदर्थ समस्या समाधान के लिए उत्तम है जबकि कमांड-लाइन स्क्रिप्ट बैच प्रोसेसिंग के लिए उत्तम है। ऊपर दिए गए स्क्रिप्ट का Radare2 संस्करण इस प्रकार है:```
from future import print_function
import flare_emu
def decrypt(argv, eh): myEH = flare_emu.EmuHelper(samplePath=sys.argv[1], emuHelper=eh) myEH.emulateRange(myEH.analysisHelper.getNameAddr("decryptString"), registers = {"arg1":argv[0], "arg2":argv[1], "arg3":argv[2], "arg4":argv[3]}) return myEH.getEmuString(argv[0])
def iterateCallback(eh, address, argv, userData): s = decrypt(argv, eh) print("%s: %s" % (eh.hexString(address), s)) eh.analysisHelper.setComment(address, s, False)
if name == 'main':
eh = flare_emu.EmuHelper(samplePath=sys.argv[1])
eh.analysisHelper.setName(, "decryptString")
eh.iterate(eh.analysisHelper.getNameAddr("decryptString"), iterateCallback)
इस स्क्रिप्ट में दो अंतर हैं। पहला, `EmuHelper` कंस्ट्रक्टर यहाँ एक पैरामीटर लेता है: `samplePath=sys.argv[1]`। जब `samplePath` पैरामीटर प्रदान किया जाता है, तो `flare-emu` अपने बाइनरी विश्लेषण इंजन के रूप में Radare2 का उपयोग `r2pipe` के साथ करेगा। आप यह भी देख सकते हैं कि `decrypt` फंक्शन में बनाए गए दूसरे `EmuHelper` इंस्टेंस को एक दूसरा पैरामीटर दिया गया है। `emuHelper` पैरामीटर एक मौजूदा `EmuHelper` ऑब्जेक्ट लेता है और नया ऑब्जेक्ट बनाते समय उसकी मेमोरी को क्लोन करता है। साथ ही, यदि आप Radare2 का उपयोग कर रहे हैं, तो नया इंस्टेंस नया सत्र बनाने के बजाय मौजूदा Radare2 सत्र का पुन: उपयोग करता है, जिससे अतिरिक्त ओवरहेड नहीं बढ़ता। दूसरा, `flare-emu` `r2pipe.open` का उपयोग करके Radare2 का एक नया इंस्टेंस बनाता है, इसलिए संभवतः इसमें हमारी रुचि के फंक्शन के लिए `decryptString` नाम नहीं होगा। आप या तो `EmuHelper` के `analysisHelper` ऑब्जेक्ट का उपयोग करके स्वयं नाम सेट कर सकते हैं जैसे: `eh.analysisHelper.setName(<some address>, "decryptString")`, या आप `iterate` और `emulateRange` कॉल के लिए सीधे पता इनपुट कर सकते हैं।
## [एमुलेशन फंक्शन्स](#emulationfuncs)
`emulateRange(startAddr, endAddr=None, registers=None, stack=None, instructionHook=None, callHook=None, memAccessHook=None, hookData=None, skipCalls=True, hookApis=True, strict=True, count=0)` - `startAddress` से शुरू होकर `endAddress` (जिसमें `endAddress` पर निर्देश शामिल नहीं है) पर समाप्त होने वाले निर्देशों की श्रेणी का अनुकरण करता है। यदि endAddress `None` है, तो अनुकरण तब रुक जाता है जब उसी फंक्शन के भीतर "रिटर्न" प्रकार का निर्देश आता है जहाँ से अनुकरण शुरू हुआ था।
* `registers` एक शब्दकोश है जिसमें कुंजियाँ रजिस्टर नाम और मान रजिस्टर मान होते हैं। `flare-emu` द्वारा कुछ विशेष रजिस्टर नाम बनाए गए हैं और उनका उपयोग यहाँ किया जा सकता है, जैसे `arg1`, `arg2`, आदि, `ret`, और `pc`।
* `stack` उन मानों की एक सरणी है जिन्हें रिवर्स ऑर्डर में स्टैक पर धकेला जाना है, जैसे `x86` में एक फंक्शन के तर्क होते हैं। `x86` में, याद रखें कि इस सरणी में पहला मान फंक्शन कॉल के लिए रिटर्न एड्रेस के रूप में उपयोग होता है, न कि फंक्शन के पहले तर्क के रूप में। `flare-emu` `registers` और `stack` तर्कों में निर्दिष्ट मानों के अनुसार अनुकरणित थ्रेड के संदर्भ और मेमोरी को प्रारंभ करेगा। यदि इनमें से किसी भी मान के लिए एक स्ट्रिंग निर्दिष्ट की जाती है, तो इसे मेमोरी में एक स्थान पर लिखा जाएगा और उस मेमोरी का एक पॉइंटर निर्दिष्ट रजिस्टर या स्टैक स्थान पर लिखा जाएगा।
* `instructionHook` एक ऐसा फंक्शन हो सकता है जिसे आप प्रत्येक निर्देश के अनुकरण से पहले कॉल करने के लिए परिभाषित करते हैं। इसका निम्नलिखित प्रोटोटाइप है: `instructionHook(unicornObject, address, instructionSize, userData)`।
* `callHook` एक ऐसा फंक्शन हो सकता है जिसे आप परिभाषित करते हैं जो अनुकरण के दौरान "कॉल" प्रकार के निर्देश का सामना होने पर कॉल किया जाता है। इसका निम्नलिखित प्रोटोटाइप है: `callHook(address, arguments, functionName, userData)`।
* `hookData` एक शब्दकोश है जिसमें आपके हुक फंक्शनों के लिए उपलब्ध कराया जाने वाला उपयोगकर्ता-परिभाषित डेटा होता है। यह पूरे अनुकरण के दौरान डेटा को बनाए रखने का एक साधन है। `flare-emu` भी इस शब्दकोश का उपयोग अपने उद्देश्यों के लिए करता है, इसलिए सावधानी बरतनी चाहिए कि पहले से परिभाषित कुंजी को परिभाषित न करें। यूनिकॉर्न में इसके नामकरण के कारण इस वेरिएबल को अक्सर उपयोगकर्ता-परिभाषित हुक फंक्शनों में `userData` नाम दिया जाता है।
* `skipCalls` एमुलेटर को "कॉल" प्रकार के निर्देशों को छोड़ने और तदनुसार स्टैक को समायोजित करने का कारण बनेगा, डिफ़ॉल्ट रूप से `True`।
* `hookApis` `flare-emu` को अनुकरण के दौरान आने वाले कुछ सामान्य रनटाइम और ऑपरेटिंग सिस्टम लाइब्रेरी फंक्शनों का एक सरल कार्यान्वयन करने का कारण बनता है। यह आपको `memcpy`, `strcat`, `malloc`, आदि जैसे फंक्शनों के कॉल के बारे में चिंतित होने से मुक्त करता है, और डिफ़ॉल्ट रूप से `True` है।
* `memAccessHook` एक ऐसा फंक्शन हो सकता है जिसे आप परिभाषित करते हैं जो पढ़ने या लिखने के लिए मेमोरी एक्सेस करने पर कॉल किया जाता है। इसका निम्नलिखित प्रोटोटाइप है: `memAccessHook(unicornObject, accessType, memAccessAddress, memAccessSize, memValue, userData)`।
* `strict`, जब `True` (डिफ़ॉल्ट) पर सेट होता है, तो ब्रांच डेस्टिनेशन की जाँच करता है ताकि यह सुनिश्चित हो सके कि डिस्सेम्बलर निर्देशों की अपेक्षा करता है। अन्यथा यह ब्रांच निर्देश को छोड़ देता है। यदि IDA Pro का उपयोग करते समय `False` पर सेट किया जाता है, तो `flare-emu` IDA Pro में निर्देश बनाएगा क्योंकि यह उनका अनुकरण करता है **(सावधानी के साथ अक्षम करें)**।
* `count` अनुकरण करने के लिए निर्देशों की अधिकतम संख्या है, डिफ़ॉल्ट `0` है जिसका अर्थ है कोई सीमा नहीं।
`iterate(target, targetCallback, preEmuCallback=None, callHook=None, instructionHook=None, hookData=None, resetEmuMem=False, hookApis=True, memAccessHook=None)` - `target` द्वारा निर्दिष्ट प्रत्येक लक्ष्य के लिए, कंटेनिंग फंक्शन की शुरुआत से लक्ष्य पते तक एक अलग अनुकरण किया जाता है। प्रत्येक लक्ष्य तक पहुँचने के लिए आवश्यक शाखाओं पर अनुकरण को बलपूर्वक ले जाया जाएगा। `target` एक फंक्शन का पता हो सकता है, जिस स्थिति में लक्ष्य सूची निर्दिष्ट फंक्शन के सभी क्रॉस-रेफरेंस से आबाद होती है। या, `target` एक स्पष्ट लक्ष्य सूची हो सकती है।
* `targetCallback` एक ऐसा फंक्शन है जिसे आप बनाते हैं जिसे `flare-emu` द्वारा अनुकरण के दौरान पहुँचे गए प्रत्येक लक्ष्य के लिए कॉल किया जाएगा। इसका निम्नलिखित प्रोटोटाइप है: `targetHook(emuHelper, address, arguments, userData)`।
* `preEmuCallback` एक ऐसा फंक्शन है जिसे आप बनाते हैं जो प्रत्येक लक्ष्य के लिए अनुकरण शुरू होने से पहले कॉल किया जाएगा। यदि आवश्यक हो तो आप यहाँ कुछ सेटअप कोड लागू कर सकते हैं।
* `resetEmuMem` `flare-emu` को प्रत्येक लक्ष्य का अनुकरण शुरू होने से पहले अनुकरण मेमोरी को रीसेट करने का कारण बनेगा, डिफ़ॉल्ट `False` है।
`iterateAllPaths(target, targetCallback, preEmuCallback=None, callHook=None, instructionHook=None, hookData=None, resetEmuMem=False, hookApis=True, memAccessHook=None, maxPaths=MAXCODEPATHS, maxNodes=MAXNODESEARCH)` - पते `target` वाले फंक्शन के लिए, इसके माध्यम से खोजे गए प्रत्येक पथ के लिए एक अलग अनुकरण किया जाता है, अधिकतम `maxPaths` तक।
* `maxPaths` - फंक्शन के माध्यम से पथों की अधिकतम संख्या जो खोजी और अनुकरण की जाएगी। कुछ अधिक जटिल फंक्शन ग्राफ खोज फंक्शन को बहुत लंबा समय लेने या कभी खत्म न होने का कारण बन सकते हैं; उचित समय में अपनी आवश्यकताओं को पूरा करने के लिए इस पैरामीटर को ट्वीक करें।
* `maxNodes` - लक्ष्य फंक्शन के माध्यम से पथ खोजते समय खोजे जाने वाले मूल ब्लॉकों की अधिकतम संख्या। यह अनुचित खोज समय और हैंग को रोकने के लिए एक सुरक्षा उपाय है और संभवतः इसे बदलने की आवश्यकता नहीं है।
`emulateBytes(bytes, registers=None, stack=None, baseAddress=0x400000, instructionHook=None, hookData=None)` - `bytes` में निहित कोड को यदि संभव हो तो `baseAddress` पर अनुकरण मेमोरी में लिखता है और निर्देशों को `bytes` की शुरुआत से अंत तक अनुकरण करता है।
`emulateFrom(startAddr, registers=None, stack=None, instructionHook=None, callHook=None, memAccessHook=None, hookData=None, skipCalls=True, hookApis=True, strict=True, count=0)` - यह API उन मामलों में उपयोगी है जहाँ फंक्शन सीमाएँ स्पष्ट रूप से परिभाषित नहीं हैं जैसा कि अक्सर अस्पष्ट बाइनरी या शेलकोड के मामले में होता है। आप `startAddr` के रूप में एक प्रारंभिक पता प्रदान करते हैं, और यह तब तक अनुकरण करेगा जब तक अनुकरण करने के लिए कुछ बचा नहीं है या आप अपने किसी हुक में अनुकरण रोक नहीं देते। इसे गतिशील कोड खोज को सक्षम करने के लिए `strict` पैरामीटर `False` पर सेट करके कॉल किया जा सकता है; `flare-emu` IDA Pro को अनुकरण के दौरान सामने आने वाले निर्देश बनाने के लिए कहेगा।
## [उपयोगिता फंक्शन्स](#utility)
`EmuHelper` वर्ग द्वारा प्रदान किए गए कुछ उपयोगी उपयोगिता फंक्शनों की निम्नलिखित अपूर्ण सूची है।
* `hexString(value)` - मान के लिए एक हेक्साडेसिमल फ़ॉर्मेटेड स्ट्रिंग लौटाता है। लॉगिंग और प्रिंट स्टेटमेंट के लिए उपयोगी।
* `skipInstruction(userData, useAnalysisHelper=False)` - वर्तमान निर्देश को छोड़ने के लिए अनुकरण हुक से इसे कॉल करें, प्रोग्राम काउंटर को अगले निर्देश पर ले जाएँ। `useAnalysisHelper` विकल्प उन मामलों को संभालने के लिए जोड़ा गया था जहाँ बाइनरी विश्लेषण फ्रेमवर्क एक ही छद्म निर्देश में कई निर्देशों को मोड़ता है और आप उन सभी को छोड़ना चाहेंगे। एकल निर्देश हुक से कई निर्देशों को छोड़ने के लिए इस फंक्शन को कई बार कॉल नहीं किया जा सकता है। कई निर्देशों को छोड़ने के लिए, यदि आप ARM कोड का अनुकरण कर रहे हैं तो सीधे प्रोग्राम काउंटर पर लिखने की अनुशंसा नहीं की जाती है क्योंकि इससे थंब मोड में समस्या हो सकती है। इसके बजाय, `EmuHelper` के `changeProgramCounter` API (नीचे वर्णित) का प्रयास करें।
* `changeProgramCounter(userData, newAddress)` - प्रोग्राम काउंटर रजिस्टर का मान बदलने के लिए अनुकरण हुक से इसे कॉल करें। यह API ARM आर्किटेक्चर के लिए थंब मोड ट्रैकिंग का ध्यान रखता है।
* `getRegVal(registerName)` - निर्दिष्ट रजिस्टर का मान प्राप्त करता है, सब-रजिस्टर एड्रेसिंग के प्रति संवेदनशील होता है। उदाहरण के लिए, "ax" `x86` में EAX/RAX रजिस्टर के निचले 16 बिट्स लौटाएगा।
* `stopEmulation(userData)` - अनुकरण रोकने के लिए अनुकरण हुक से इसे कॉल करें। `emu_stop` Unicorn API कॉल करने के बजाय इसका उपयोग करें ताकि `EmuHelper` ऑब्जेक्ट `iterate` सुविधा से संबंधित बुककीपिंग कर सके।
* `getEmuString(address)` - अनुकरणित मेमोरी में एक पते पर स्थित वर्णों की स्ट्रिंग लौटाता है, एक शून्य टर्मिनेटर तक। वर्ण आवश्यक रूप से प्रिंट करने योग्य नहीं हैं।
* `getEmuWideString(address)` - अनुकरणित मेमोरी में एक पते पर स्थित "वाइड कैरेक्टर" की स्ट्रिंग लौटाता है, एक शून्य टर्मिनेटर तक। "वाइड कैरेक्टर" का अर्थ यहाँ ढीले ढंग से किसी भी बाइट श्रृंखला से है जिसमें हर दूसरे बाइट पर एक शून्य बाइट होता है, जैसा कि UTF-16 LE में एन्कोडेड ASCII स्ट्रिंग के मामले में होगा। वर्ण आवश्यक रूप से प्रिंट करने योग्य नहीं हैं।
* `getEmuBytes(address, length)` - अनुकरणित मेमोरी में एक पते पर स्थित बाइट्स की एक स्ट्रिंग लौटाता है।
* `getEmuPtr(address)` - दिए गए पते पर स्थित पॉइंटर मान लौटाता है।
* `writeEmuPtr(address, value)` - दिए गए पते पर अनुकरणित मेमोरी में पॉइंटर मान लिखता है।
* `loadBytes(bytes, address=None)` - एमुलेटर में मेमोरी आवंटित करता है और उसमें बाइट्स लिखता है।
* `isValidEmuPtr(address)` - `True` लौटाता है यदि प्रदान किया गया पता मान्य अनुकरणित मेमोरी की ओर इंगित करता है।
* `getEmuMemRegion(address)` - एक टपल लौटाता है जिसमें प्रदान किए गए पते वाले मेमोरी क्षेत्र का प्रारंभ और अंत पता होता है, या `None` यदि पता मान्य नहीं है।
* `getArgv()` - "कॉल" प्रकार के निर्देश पर अनुकरण हुक से इसे कॉल करें ताकि फंक्शन के तर्कों की एक सरणी प्राप्त हो सके।
* `addApiHook(apiName, hook)` - `EmuHelper` के इस इंस्टेंस के लिए एक नया API हुक जोड़ता है। जब भी अनुकरण के दौरान `apiName` के लिए एक कॉल निर्देश का सामना होता है, `EmuHelper` `hook` द्वारा निर्दिष्ट फंक्शन को कॉल करेगा। यदि `hook` एक स्ट्रिंग है, तो यह `EmuHelper` द्वारा पहले से हुक किए गए API का नाम होने की उम्मीद है, जिस स्थिति में यह अपने मौजूदा हुक फंक्शन को कॉल करेगा। यदि `hook` एक फंक्शन है, तो यह उस फंक्शन को कॉल करेगा।
* `allocEmuMem(size, addr=None)` - `size` बाइट्स रखने के लिए पर्याप्त एमुलेटर मेमोरी आवंटित करता है। यह अनुरोधित `address` का सम्मान करने का प्रयास करता है, लेकिन यदि यह किसी मौजूदा मेमोरी क्षेत्र के साथ ओवरलैप होता है, तो यह एक अप्रयुक्त मेमोरी क्षेत्र में आवंटित करेगा और नया पता लौटाएगा। यदि पता पेज-एलाइन नहीं है, तो यह एक ऐसा पता लौटाएगा जो नए क्षेत्र के भीतर समान पेज-एलाइन ऑफसेट बनाए रखता है। उदाहरण के लिए, पता `0x1234` का अनुरोध करने पर जब `0x1000` पहले से आवंटित है, तो यह `0x2000` पर आवंटित हो सकता है और इसके बजाय `0x2234` लौटा सकता है।
# [और जानें](#learn)
**flare-emu** के बारे में अधिक जानने के लिए, कृपया https://www.fireeye.com/blog/threat-research/2018/12/automating-objective-c-code-analysis-with-emulation.html पर हमारा परिचयात्मक ब्लॉग पढ़ें।