
إطار عمل قابل للبرمجة لمحاكاة الثنائيات يدمج IDA Pro/Radare2 مع محرك Unicorn للتحليل الآلي للبرامج الضارة، وفك تشفير السلاسل النصية، واستكشاف مسارات الكود عبر معماريات x86 و ARM و ARM64.
flare-emu يدمج إطار تحليل ثنائيات مدعوم، مثل IDA Pro أو Radare2، مع إطار المحاكاة Unicorn لتوفير واجهة سهلة الاستخدام ومرنة للمستخدم لبرمجة مهام المحاكاة. صُمم ليتولى جميع المهام المنزلية لإنشاء محاكي قوي ومرن للهياكل المدعومة، بحيث يمكنك التركيز على حل مشكلات تحليل الشيفرة. حاليًا، يدعم flare-emu هياكل x86 و x86_64 و ARM و ARM64.
يوفر حاليًا خمس واجهات مختلفة لخدمة احتياجات المحاكاة الخاصة بك، إلى جانب مجموعة من الدوال المساعدة والوظائف ذات الصلة.
emulateRange – تُستخدم هذه الواجهة لمحاكاة نطاق من التعليمات، أو دالة، ضمن سياق يحدده المستخدم. توفر خيارات لخطافات يحددها المستخدم لكل من التعليمات الفردية وعند مواجهة تعليمات "call". يمكن للمستخدم أن يقرر ما إذا كان المحاكي سيتجاوز استدعاءات الدوال أو يدخل إليها. تتيح هذه الواجهة طريقة سهلة للمستخدم لتحديد قيم لسجلات معينة ومعاملات المكدس. إذا تم تحديد سلسلة بايتات، فسيتم كتابتها إلى ذاكرة المحاكي ويتم كتابة المؤشر إلى السجل أو المتغير المكدسي. بعد المحاكاة، يمكن للمستخدم استخدام دوال الأداة المساعدة لـ flare-emu لقراءة البيانات من الذاكرة أو السجلات المحاكاة، أو استخدام كائن محاكاة Unicorn الذي يتم إرجاعه للاستعلام المباشر. هناك دالة غلاف صغيرة لـ emulateRange تُسمى emulateSelection، يمكن استخدامها لمحاكاة نطاق التعليمات المظللة حاليًا في IDA Pro.
iterate - تُستخدم هذه الواجهة لفرض المحاكاة على طول فروع محددة داخل دالة للوصول إلى هدف معين. يمكن للمستخدم تحديد قائمة بعناوين الأهداف، أو عنوان دالة يتم استخدام قائمة المراجع المتقاطعة لها كأهداف، إلى جانب دالة رد اتصال عند الوصول إلى الهدف. سيتم الوصول إلى الأهداف بغض النظر عن الظروف أثناء المحاكاة التي قد تكون تسببت في اتخاذ فروع مختلفة. كما هو الحال مع واجهة emulateRange، يتم توفير خيارات لخطافات يحددها المستخدم لكل من التعليمات الفردية وعند مواجهة تعليمات "call". مثال على استخدام واجهة iterate هو تحقيق شيء مشابه لما تفعله أداة argtracker الخاصة بنا.
iterateAllPaths - تشبه هذه الواجهة كثيرًا iterate، باستثناء أنه بدلاً من توفير عنوان هدف أو عناوين، فإنك تقدم دالة هدف سيحاول الوصول إلى جميع المسارات من خلالها ومحاكاتها. هذا مفيد عندما تقوم بتحليل شيفرة ترغب في الوصول إلى كل كتلة أساسية في دالة.
emulateBytes – توفر هذه الواجهة طريقة لمحاكاة كتلة من شيفرة شل خارجية ببساطة. لا تتم إضافة البايتات المقدمة إلى قاعدة بيانات IDA ويتم محاكاتها كما هي. يمكن أن يكون هذا مفيدًا لإعداد بيئة المحاكاة. على سبيل المثال، تستخدم flare-emu نفسها هذه الواجهة للتلاعب بسجل نموذجي خاص (MSR) لوحدة المعالجة المركزية ARM64 الذي لا يعرضه Unicorn لتمكين تعليمات النقطة العائمة المتجهة (VFP) والوصول إلى السجلات. يتم إرجاع كائن محاكاة Unicorn لمزيد من الاستعلام من قبل المستخدم.
emulateFrom - تكون هذه الواجهة مفيدة في الحالات التي لا تكون فيها حدود الدالة محددة بوضوح كما هو الحال غالبًا مع الثنائيات المبهمة أو شيفرة شل. توفر عنوان بدء، وسيقوم بالمحاكاة حتى لا يتبقى شيء للمحاكاة أو تقوم بإيقاف المحاكاة في أحد خطافاتك. مع IDA Pro، يمكن استدعاء هذا مع تعيين المعامل strict إلى False لتمكين اكتشاف الشيفرة الديناميكي؛ سيقوم flare-emu بجعل IDA Pro ينشئ تعليمات أثناء مواجهتها أثناء المحاكاة.
لتثبيت flare-emu لـ IDA Pro، ما عليك سوى وضع flare_emu.py و flare_emu_ida.py و flare_emu_hooks.py في دليل python الخاص بـ IDA Pro واستيرادها كوحدة في نصوص 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، وهي غير متوافقة مع الإصدارات السابقة من 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__`، نبدأ بإنشاء نسخة من الفئة `EmuHelper` من `flare-emu`. هذه هي الفئة التي نستخدمها لفعل كل شيء باستخدام `flare-emu`. بعد ذلك، نستخدم واجهة `iterate`، مع تمرير عنوان دالة `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` إلى استدعائنا لواجهة `emulateRange` الخاصة بـ `EmuHelper`. نظرًا لأن هذا ملف ثنائي `x86_64`، تستخدم اصطلاحات الاستدعاء السجلات لتمرير الوسائط وليس المكدس. تقوم `flare-emu` تلقائيًا بتحديد السجلات التي تمثل أي وسائط بناءً على بنية وتنسيق الملف الثنائي كما يحدده IDA Pro، مما يسمح لك بكتابة كود غير مرتبط بالبنية إلى حد ما. إذا كان هذا ملفًا ثنائيًا 32-bit `x86`، فستستخدم وسيط `stack` لتمرير الوسائط بدلاً من ذلك، كما يلي: `myEH.emulateRange(myEH.analysisHelper.getNameAddr("decryptString"), stack = [0, argv[0], argv[1], argv[2], argv[3]])`. القيمة الأولى في المكدس هي عنوان الإرجاع في `x86`، لذا نستخدم `0` كقيمة افتراضية هنا. عند اكتمال المحاكاة، نستدعي واجهة `getEmuString` لاسترجاع السلسلة المنتهية بقيمة خالية المخزنة في موقع الذاكرة المشار إليه بواسطة الوسيط الأول الممرر إلى الدالة.
### flare-emu and idalib
* install IDA Pro
* install idalib per the Hex-Rays user guide
* (activate virtual environment)
* 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]
* import idapro and write your script
* see tests/test_flare_emu_idalib.py for an example
### Easy String Decryption Scenario with Rizin
باستخدام نفس المثال أعلاه، لا تتغير الكثير من الأمور عند العمل مع Rizin بدلاً من IDA Pro. أحد الاختلافات هو أن `flare-emu` مصممة حاليًا للتشغيل كسكربت سطر أوامر أو داخل Python shell عند العمل مع Rizin. Python shell ممتاز لحل المشكلات المؤقتة بينما سكربت سطر الأوامر ممتاز للمعالجة الدفعية. تبدو نسخة 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)
باستخدام نفس المثال أعلاه، لا تتغير الأمور كثيرًا عند العمل مع Radare2 بدلاً من IDA Pro. أحد الاختلافات هو أن flare-emu مصمم حاليًا ليعمل كبرنامج نصي لسطر الأوامر أو داخل غلاف Python عند العمل مع Radare2. غلاف 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` كمحرك تحليل ثنائي خاص به. يمكنك أيضًا رؤية أنه يتم تمرير معامل ثانٍ إلى مثيل `EmuHelper` الثاني الذي تم إنشاؤه في دالة `decrypt`. يأخذ معامل `emuHelper` كائن `EmuHelper` موجودًا ويستنسخ ذاكرته عند إنشاء الكائن الجديد. أيضًا، إذا كنت تستخدم Radare2، فإن المثيل الجديد يعيد استخدام جلسة Radare2 الموجودة بدلاً من إنشاء جلسة جديدة مما قد يضيف مزيدًا من الحمل الزائد. ثانيًا، يقوم `flare-emu` بإنشاء مثيل جديد من Radare2 باستخدام `r2pipe.open`، لذا فمن المحتمل ألا يكون له اسم `decryptString` للدالة التي نهتم بها. يمكنك إما تعيين الاسم بنفسك باستخدام كائن `analysisHelper` الخاص بـ `EmuHelper` كما يلي: `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`، تتوقف المحاكاة عند مواجهة تعليمة من نوع "return" داخل نفس الدالة التي بدأت فيها المحاكاة.
* `registers` هو قاموس بمفاتيح تمثل أسماء المسجلات وقيم تمثل قيم المسجلات. يتم إنشاء بعض أسماء المسجلات الخاصة بواسطة `flare-emu` ويمكن استخدامها هنا، مثل `arg1`, `arg2`، إلخ، `ret`، و `pc`.
* `stack` هو مصفوفة من القيم التي سيتم دفعها على المكدس بترتيب عكسي، مثل الوسائط لدالة في `x86`. في `x86`، تذكر حساب القيمة الأولى في هذه المصفوفة التي تُستخدم كعنوان عودة لاستدعاء دالة وليس كوسيطة أولى للدالة. سيقوم `flare-emu` بتهيئة سياق وذاكرة الخيط المُحاكى وفقًا للقيم المحددة في وسيطات `registers` و `stack`. إذا تم تحديد سلسلة نصية لأي من هذه القيم، فسيتم كتابتها في موقع في الذاكرة وسيتم كتابة مؤشر إلى تلك الذاكرة في المسجل المحدد أو موقع المكدس بدلاً من ذلك.
* `instructionHook` يمكن أن تكون دالة تقوم بتعريفها ليتم استدعاؤها قبل محاكاة كل تعليمة. نموذجها الأولي هو: `instructionHook(unicornObject, address, instructionSize, userData)`.
* `callHook` يمكن أن تكون دالة تقوم بتعريفها ليتم استدعاؤها كلما تمت مواجهة تعليمة من نوع "call" أثناء المحاكاة. نموذجها الأولي هو: `callHook(address, arguments, functionName, userData)`.
* `hookData` هو قاموس يحتوي على بيانات معرفة من قبل المستخدم لتكون متاحة لدوال الربط الخاصة بك. إنها وسيلة للحفاظ على البيانات طوال المحاكاة. يستخدم `flare-emu` أيضًا هذا القاموس لأغراضه الخاصة، لذا يجب توخي الحذر لعدم تعريف مفتاح موجود بالفعل. غالبًا ما يُسمى هذا المتغير `userData` في دوال الربط المعرفة من قبل المستخدم بسبب تسميته في Unicorn.
* `skipCalls` ستؤدي إلى تخطي المحاكي لتعليمات من نوع "call" وضبط المكدس وفقًا لذلك، القيمة الافتراضية هي `True`.
* `hookApis` تجعل `flare-emu` يقوم بتنفيذ مبسط لبعض دوال مكتبة وقت التشغيل ونظام التشغيل الأكثر شيوعًا التي يواجهها أثناء المحاكاة. هذا يحررك من الاضطرار إلى الاهتمام باستدعاءات دوال مثل `memcpy` و `strcat` و `malloc`، إلخ، والقيمة الافتراضية هي `True`.
* `memAccessHook` يمكن أن تكون دالة تقوم بتعريفها ليتم استدعاؤها كلما تم الوصول إلى الذاكرة للقراءة أو الكتابة. نموذجها الأولي هو: `memAccessHook(unicornObject, accessType, memAccessAddress, memAccessSize, memValue, userData)`.
* `strict`، عند تعيينها على `True` (افتراضي)، تتحقق من وجهات التفرع للتأكد من أن مفكك الشيفرة يتوقع تعليمات. وإلا فإنها تتخطى تعليمة التفرع. إذا تم تعيينها على `False` عند استخدام IDA Pro، فسيقوم `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)` - هذه الواجهة البرمجية مفيدة في الحالات التي لا تكون فيها حدود الدالة محددة بوضوح كما هو الحال غالبًا مع الملفات الثنائية المشوشة أو شيفرة الشيل. توفر عنوان بداية كـ `startAddr`، وسوف تحاكي حتى لا يتبقى شيء للمحاكاة أو توقف المحاكاة في أحد ربطاتك. يمكن استدعاؤها مع تعيين معامل `strict` على `False` لتمكين الاكتشاف الديناميكي للشيفرة؛ سيجعل `flare-emu` IDA Pro يقوم بإنشاء تعليمات عند مواجهتها أثناء المحاكاة.
## [دوال مساعدة](#utility)
القائمة التالية غير مكتملة لبعض الدوال المساعدة المفيدة التي توفرها فئة `EmuHelper`.
* `hexString(value)` - تُرجع سلسلة نصية بتنسيق سداسي عشري للقيمة. مفيدة لجمل الطباعة والتسجيل.
* `skipInstruction(userData, useAnalysisHelper=False)` - استدعِ هذه الدالة من ربط محاكاة لتخطي التعليمات الحالية، ونقل عداد البرنامج إلى التعليمات التالية. تمت إضافة خيار `useAnalysisHelper` للتعامل مع الحالات التي يقوم فيها إطار التحليل الثنائي بطي عدة تعليمات في تعليمة زائفة واحدة وترغب في تخطيها جميعًا. لا يمكن استدعاء هذه الدالة عدة مرات من ربط تعليمة واحدة لتخطي تعليمات متعددة. لتخطي تعليمات متعددة، يُوصى بعدم الكتابة مباشرة على عداد البرنامج إذا كنت تحاكي كود ARM لأن ذلك قد يسبب مشاكل مع وضع الإبهام (thumb mode). بدلاً من ذلك، جرب واجهة `changeProgramCounter` الخاصة بـ `EmuHelper` (الموصوفة أدناه).
* `changeProgramCounter(userData, newAddress)` - استدعِ هذه الدالة من ربط محاكاة لتغيير قيمة مسجل عداد البرنامج. تعتني هذه الواجهة بتتبع وضع الإبهام لمعمارية ARM.
* `getRegVal(registerName)` - تسترجع قيمة المسجل المحدد، مع مراعاة العنونة الفرعية للمسجل. على سبيل المثال، "ax" سترجع أقل 16 بت من مسجل EAX/RAX في `x86`.
* `stopEmulation(userData)` - استدعِ هذه الدالة من ربط محاكاة لإيقاف المحاكاة. استخدمها بدلاً من استدعاء واجهة Unicorn `emu_stop` حتى يتمكن كائن `EmuHelper` من التعامل مع إمساك الدفاتر المتعلقة بميزة `iterate`.
* `getEmuString(address)` - تُرجع سلسلة الأحرف الموجودة في عنوان ما في الذاكرة المحاكاة، حتى فاصل null. الأحرف ليست بالضرورة قابلة للطباعة.
* `getEmuWideString(address)` - تُرجع سلسلة "الأحرف العريضة" الموجودة في عنوان ما في الذاكرة المحاكاة، حتى فاصل null. يُقصد بـ "الأحرف العريضة" بشكل فضفاض هنا الإشارة إلى أي سلسلة من البايتات تحتوي على بايت null كل بايتين، كما هو الحال بالنسبة لسلسلة ASCII مشفرة بـ UTF-16 LE. الأحرف ليست بالضرورة قابلة للطباعة.
* `getEmuBytes(address, length)` - تُرجع سلسلة من البايتات الموجودة في عنوان ما في الذاكرة المحاكاة.
* `getEmuPtr(address)` - تُرجع قيمة المؤشر الموجودة في العنوان المحدد.
* `writeEmuPtr(address, value)` - يكتب قيمة المؤشر في العنوان المحدد في الذاكرة المحاكاة.
* `loadBytes(bytes, address=None)` - يخصص ذاكرة في المحاكي ويكتب البايتات إليها.
* `isValidEmuPtr(address)` - تُرجع `True` إذا كان العنوان المقدم يشير إلى ذاكرة محاكاة صالحة.
* `getEmuMemRegion(address)` - تُرجع زوجًا يحتوي على عنوان البداية والنهاية لمنطقة الذاكرة التي تحتوي على العنوان المقدم، أو `None` إذا كان العنوان غير صالح.
* `getArgv()` - استدعِ هذه الدالة من ربط محاكاة عند تعليمة من نوع "call" لتلقي مصفوفة من وسائط الدالة.
* `addApiHook(apiName, hook)` - يضيف ربط API جديدًا لهذا المثيل من `EmuHelper`. عندما يتم مواجهة تعليمة استدعاء إلى `apiName` أثناء المحاكاة، سيقوم `EmuHelper` باستدعاء الدالة المحددة بواسطة `hook`. إذا كان `hook` عبارة عن سلسلة نصية، فمن المتوقع أن يكون اسم API مرتبط بالفعل بواسطة `EmuHelper`، وفي هذه الحالة سيقوم باستدعاء دالة الربط الموجودة لديه. إذا كان `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.