
مكتبة تنقيح ديناميكي خفيفة الوزن
Copyright 2020 Google LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
## ما هو TinyInst؟
TinyInst هي مكتبة أدوات ديناميكية خفيفة الوزن يمكن استخدامها لتجهيز وحدات مختارة فقط في العملية، مع ترك بقية العملية لتعمل بشكل أصلي. وهي مصممة لتكون سهلة الفهم، وسهلة التطوير عليها، وسهلة الاستخدام في التجارب الأمنية. إنها غير مصممة لتكون متوافقة مع جميع الأهداف (المزيد عن ذلك لاحقًا).
### كيف يمكن مقارنتها بـ [DynamoRIO](https://dynamorio.org/) و[PIN](https://software.intel.com/en-us/articles/pintool)؟
TinyInst ليس بديلاً لأُطر الأدوات المعقدة مثل DynamoRIO وPIN، بل هو خيار بديل للسيناريوهات التي يكفي فيها حل أكثر خفة. يفترض TinyInst أن الهدف حسن السلوك (بالمعنى الموضح أدناه)، وهذا ليس هو الحال بالنسبة للأُطر الأكثر تعقيدًا. وبالتالي، من المحتمل ألا تتمكن من تشغيل TinyInst بنجاح ضد البرمجيات الخبيثة كما [حدث مع DynamoRIO سابقًا](https://www.slideshare.net/MaximShudrak/fuzzing-malware-for-fun-profit-applying-coverageguided-fuzzing-to-find-bugs-in-modern-malware). من ناحية أخرى، إذا كان هدف ما لا يعمل مع الأُطر الأخرى بسبب وحدة لا تحتاج إلى تجهيز، وكانت الوحدة المجهزة حسن السلوك، فقد يعمل مع TinyInst. ولأنه مع TinyInst سيعمل معظم العملية بشكل أصلي، سيكون وقت بدء العملية أقصر، وقد يتفوق على الحلول الأخرى في الحالات التي يقضي فيها الهدف الكثير من الوقت في الوحدات التي لا تحتاج إلى أدوات قياس.
### كيف يمكن مقارنتها بـ [Mesos](https://github.com/gamozolabs/mesos) و[TrapFuzz](https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz)؟
TinyInst هو حل كامل لإعادة كتابة الثنائيات (binary rewriting)، لذا يمكن تغيير أي سلوك في الوحدة المستهدفة. وهذا يتيح له، على سبيل المثال، استخراج تغطية الحواف (edge coverage) بدلاً من الكتل الأساسية فقط. بالإضافة إلى ذلك، لا يعتمد TinyInst على برامج أخرى، مثل IDA Pro، لتحديد الكتل الأساسية.
### ما هي أنظمة التشغيل التي يدعمها TinyInst؟
يعمل TinyInst على Windows (x86 وx64)، وmacOS (x64 وARM64)، وLinux (x64 وARM64)، وAndroid (ARM64). يرجى الاطلاع على ملف README في المجلد المقابل لكل نظام تشغيل للحصول على ملاحظات وقيود إضافية.
### ما هي الأهداف المتوافقة مع TinyInst؟
يفترض TinyInst أن جميع الوحدات المجهزة حسن السلوك بمعنى أن
- لا يوجد كود يعدّل نفسه
- لا يتم الوصول إلى عنوان العودة على المكدس مباشرة بواسطة البرنامج
أو/و (حسب الإعدادات)
- لا يتم تخزين أي بيانات أبدًا قبل قمة المكدس (في عناوين أقل من تلك التي يشير إليها ESP/RSP). يمكن تخفيف هذا الشرط إلى "لا بيانات قبل (ESP/RSP - arbitrary_offset)" باستخدام العلم `-stack_offset`.
يتطلب TinyInst أيضًا تمكين DEP/NX للعملية المستهدفة. إذا لم يكن الأمر كذلك بالفعل، يمكنك استخدام العلم `-force_dep` لفرض تمكينه. ومع ذلك، في الحالة غير المحتملة التي يحتاج فيها الهدف فعليًا إلى إيقاف DEP ليعمل بشكل صحيح، فإن فرض تمكينه قد يسبب سلوكًا غير سليم.
### ما هو الحمل على الأداء؟
وفقًا للقياسات المبكرة على فك تشفير الصور، على هدف 64-بت حسن السلوك مع إعدادات TinyInst الافتراضية، كان الحمل على الأداء حوالي 15% بدون عميل (client) وحوالي 20% مع العميل النموذجي لجمع التغطية. لاحظ أن هذا لا يتضمن المهلة الزمنية الناتجة عن التجهيز الأولي للوحدات. راجع نصائح الأداء أدناه لمزيد من التفاصيل.
## بناء TinyInst
1. افتح طرفية (Terminal) وأعدّ بيئة البناء الخاصة بك (على سبيل المثال، على Windows، قم بتشغيل vcvars64.bat / vcvars32.bat)
2. انتقل إلى المجلد الذي يحتوي على الكود المصدري
3. شغّل الأوامر التالية (غيّر المولّد وفقًا لإصدار بيئة التطوير المتكاملة (IDE) والمنصة التي تريد البناء من أجلها):
#### Windows```
mkdir build
cd build
cmake -G "Visual Studio 16 2019" -A x64 ..
cmake --build . --config Release
mkdir build cd build cmake -G Xcode .. cmake --build . --config Release
#### Linux```
mkdir build
cd build
cmake ..
cmake --build . --config Release
mkdir build cd build cmake -DCMAKE_TOOLCHAIN_FILE=</path/to/android/ndk>build/cmake/android.toolchain.cmake -DANDROID_NDK=</path/to/android/ndk> -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM= .. cmake --build . --config Release
ملاحظة رقم 1: سيعمل إصدار 64-bit أيضًا على أهداف 32-bit في أنظمة تشغيل Windows و Linux
ملاحظة رقم 2: تواجه مشاكل في إنشاء إصدار 32-bit على نظام Windows 64-bit بسبب عدم إعداد البيئة بشكل صحيح ونقص المكتبات؟ افتح ملف .sln الذي تم إنشاؤه في Visual Studio وقم بالبناء من هناك بدلاً من تشغيل cmake --build. لاحظ أيضًا أن إصدار 64-bit سيعمل على أهداف 32-bit، لذا قد لا يكون إنشاء إصدار 32-bit ضروريًا.
## استخدام TinyInst
TinyInst مخصص في المقام الأول للاستخدام كمكتبة داخل برامج أخرى.
يُكتب عميل TinyInst كفئة فرعية من فئة TinyInst. يمكن للعميل بعد ذلك تجاوز طرق API التي يحتاجها. يتم تعريف طرق API أدناه.
بعد إنشاء العميل، يجب تهيئته بخيارات سطر الأوامر عن طريق استدعاء
`void init(int argc, char **argv);`
يتم تعريف خيارات سطر الأوامر أدناه، ويمكن للعميل أيضًا تعريف خيارات خاصة به. بعد ذلك، لتشغيل برنامج مُجهز (instrumented) والتحكم فيه، يمكن استخدام الدوال التالية.
`DebuggerStatus Run(int argc, char **argv, uint32_t timeout);`
`DebuggerStatus Attach(unsigned int pid, uint32_t timeout);`
تقوم هذه الدوال إما بتشغيل برنامج (باستخدام سطر الأوامر المحدد) أو بالارتباط ببرنامج قيد التشغيل بالفعل. إذا لم يتم تحديد طريقة هدف، فسيستمر الهدف في التشغيل حتى يخرج البرنامج، أو ينهار البرنامج، أو تنتهي المهلة (المحددة بالمللي ثانية). إذا تم تعريف طريقة هدف، فسيعيد TinyInst التحكم كلما تم الدخول إلى طريقة الهدف وكلما عادت طريقة الهدف، مما يسمح للمتصل بتنفيذ مهام إضافية.
عندما تعود `Run` و `Attach` بينما لا تزال عملية الهدف قيد الحياة، يمكن استخدام الدوال التالية إما لإنهاء العملية أو مواصلة التنفيذ.
`DebuggerStatus Kill();`
`DebuggerStatus Continue(uint32_t timeout);`
يأتي TinyInst مع ملف ثنائي مثال للتغطية (coverage)، يمكن استدعاؤه باستخدام
`<options> -- <target command line>`
مثال على Windows:
`litecov.exe -instrument_module notepad.exe -coverage_file coverage.txt -- notepad.exe`
## واجهة برمجة التطبيقات للتجهيز (Instrumentation API)
### استدعاءات أحداث المصحح
هذه الاستدعاءات مخصصة للمعلومات فقط، ويجب على العميل ألا يصدر أي كود مُجهز أثناءها. يجب على العملاء استدعاء نفس المعالج المعرف في الفئة الفائقة قبل معالجة هذه الأحداث بأنفسهم.
`OnProcessCreated`
يستدعى عند إنشاء عملية الهدف أو الارتباط بها.
`OnProcessExit`
يستدعى عند خروج عملية الهدف.
`OnProcessEntrypoint`
يستدعى عند الوصول إلى نقطة دخول العملية (الملف الثنائي الرئيسي).
`OnTargetMethodReached`
إذا تم تعريف طريقة الهدف، يستدعى عند الوصول إلى طريقة الهدف لأول مرة.
`OnModuleLoaded`
يستدعى عند تحميل وحدة. يستدعى لكل وحدة، وليس فقط للوحدات المُجهزة.
`OnModuleUnloaded`
يستدعى عند إلغاء تحميل وحدة. يستدعى لكل وحدة، وليس فقط للوحدات المُجهزة.
`OnException`
يستدعى عند مواجهة استثناء. يجب على العميل إما إرجاع true (إذا تم التعامل مع الاستثناء) أو نتيجة نفس الطريقة في الفئة الأصلية.
### استدعاءات التجهيز
خلال هذه الاستدعاءات، يمكن للعميل إضافة كود إلى الهدف عن طريق استدعاء `WriteCode()`. لاحظ أن العميل مسؤول عن حفظ واستعادة أي سياق (مثل السجلات والأعلام التي يتم إتلافها في الكود المُدرج).
`InstrumentBasicBlock`
يمكن استخدامه لإدراج كود سيتم تشغيله على كتلة أساسية معينة.
`InstrumentEdge`
يمكن استخدامه لإدراج كود سيتم تشغيله على حافة معينة. ملاحظة: لأسباب تتعلق بالأداء، يتم إصدار هذا الاستدعاء فقط على الحواف غير الحتمية (أي القفزات الشرطية) والقفزات/الاستدعاءات غير المباشرة (مثل `call rax`). بالنسبة للحواف التي تكون فيها الكتلة الأساسية التالية معروفة دائمًا بالنظر إلى الكتلة الأساسية السابقة (مثل `jmp offset` و `call offset`)، لن يتم إصدار أي استدعاء.
`InstrumentInstruction`
يمكن استخدامه لتعديل التعليمات أو إدراج كود قبلها. اعتمادًا على رمز الإرجاع، سيتم إصدار التعليمات الأصلية أو لن يتم إصدارها بعد الاستدعاء.
### استدعاءات أخرى
`OnModuleEntered`
يستدعى عند نقل تدفق التحكم إلى وحدة مُجهزة من وحدة أخرى.
`OnModuleInstrumented`
يستدعى عند تجهيز وحدة ما. يحدث هذا عمومًا عند الوصول إلى نقطة دخول العملية (إذا لم يتم تعريف طريقة الهدف) أو عند الوصول إلى طريقة الهدف (إذا تم تعريفها). يمكن للعميل تهيئة بياناته المتعلقة بالتجهيز هنا.
`OnModuleUninstrumented`
يستدعى عندما لا تكون بيانات التجهيز صالحة بعد الآن ويجب مسحها. لاحظ أن هذا ليس نفس إلغاء تحميل الوحدة، حيث أن التجهيز بشكل افتراضي يستمر عبر عمليات إلغاء تحميل/إعادة تحميل الوحدة. يمكن استخدام هذا الاستدعاء لمسح أي بيانات متعلقة بالتجهيز في العميل.
### واجهة الربط (Hook API)
بالإضافة إلى واجهة برمجة التطبيقات العامة الموثقة أعلاه، ينفذ TinyInst أيضًا واجهة برمجة تطبيقات للربط (hooking) تناسب بشكل أفضل فحص وتعديل سلوك الدوال الفردية. تم توثيق هذه الواجهة في [صفحة منفصلة](https://github.com/googleprojectzero/TinyInst/blob/master/hook.md).
## خيارات سطر الأوامر
### المتعلقة بالتجهيز
`-instrument_module [module name]` يحدد الوحدة التي سيتم تجهيزها، ويمكن تحديد خيارات `-instrument_module` متعددة لتجهيز وحدات متعددة.
`-instrument_transitive [module name]` مشابه لـ `-instrument_module` باستثناء أن الكود الذي يتم الدخول إليه من وحدات مُجهزة أخرى فقط هو الذي سيعمل مُجهزًا. يُستخدم بشكل أساسي كتحسين للاستدعاءات مثل module1->module2->module1 حيث ليس من المهم تجهيز وحدة module2 بالكامل، لكن حالات الدخول من module2 إلى module1 تسبب تباطؤًا.
`-indirect_instrumentation [none|local|global|auto]` يحدد التجهيز الذي سيتم استخدامه للقفزات/الاستدعاءات غير المباشرة
`-patch_return_addresses` - يستبدل عنوان الإرجاع بالقيمة الأصلية، مما يؤدي إلى تجهيز عمليات الإرجاع باستخدام أي طريقة `-indirect_instrumentation` تم تحديدها
`-generate_unwind` - يولد بيانات فك التراص (stack unwinding) للكود المُجهز (لمعالجة استثناءات C++ بشكل أسرع). لاحظ أنه قد لا يعمل بشكل صحيح على بعض إصدارات Windows الأقدم.
`-persist_instrumentation_data` (الافتراضي = true) لا يعيد تجهيز الوحدة عند إلغاء تحميلها/إعادة تحميلها. يعمل فقط إذا تم تحميل الوحدة على نفس العنوان الذي تم تحميلها عليه سابقًا.
`-instrument_cross_module_calls` (الافتراضي=true) إذا تم تحديد وحدات `-instrument_module` متعددة واستدعت إحداها أخرى، قم بالقفز إلى الكود المُجهز للوحدة الأخرى دون التسبب في استثناء (والذي قد يسبب تباطؤًا).
`-stack_offset` (الافتراضي=0) عند حفظ السياق على المكدس، اترك هذا العدد من البايتات أعلى المكدس (قبل مؤشر المكدس) دون تغيير.
`-patch_module_entries [off|data|code|all]` يحاول حل التباطؤ الناتج عن كثرة حالات الدخول إلى الوحدات عن طريق البحث عن مؤشرات إلى نقاط دخول تم اكتشافها سابقًا واستبدالها بنظيراتها المُجهزة. تتحكم قيمة العلامة في مكان البحث عن هذه المؤشرات. تحذير: قد يؤدي تمكين هذا إلى إدخال عدم استقرار محتمل في الهدف.
### المتعلقة بالتصحيح
`-trace_debug_events` - يطبع أحداث المصحح (الوحدات المحملة، الاستثناءات، إلخ.)
`-trace_basic_blocks` - يطبع الكتل الأساسية أثناء تنفيذها
`-trace_module_entries` - يطبع جميع حالات الدخول إلى الكود المُجهز
`-trace_syscalls` - [Linux/Android فقط] يتيح للعميل استقبال أحداث بداية/نهاية استدعاءات النظام عبر استدعاءات `OnSyscall()` / `OnSyscallEnd()`.
`-full_address_map` - يحافظ على خريطة على مستوى التعليمات للعناوين في الكود المُجهز إلى العناوين في الكود الأصلي. تستهلك ذاكرة كثيرة، لكنها مفيدة للتصحيح.
### طريقة الهدف والاستمرارية
يسمح TinyInst للمستخدم بتعريف طريقة هدف. إذا تم تعريف طريقة هدف، فلن يتم تجهيز أي كود (سيعمل كل شيء بشكل أصلي) حتى يتم الوصول إلى طريقة الهدف لأول مرة. بالإضافة إلى ذلك، سيكسر TinyInst التنفيذ عند دخول طريقة الهدف وخروجها.
`-target_module` - الوحدة التي تحتوي على طريقة الهدف
`-target_method` - اسم طريقة الهدف. يعمل هذا فقط إذا كانت طريقة الهدف مُصدَّرة أو كان لديك رموز (symbols) لوحدة الهدف.
`-target_offset` - يُستخدم عندما لا يمكن تحديد طريقة الهدف بالاسم. العنوان النسبي لطريقة الهدف من قاعدة الوحدة
`-loop` - إذا تم تحديد هذه العلامة، سيشغل TinyInst طريقة الهدف في حلقة لا نهائية (أو حتى يتم استدعاء Kill() أو تنتهي العملية لسبب آخر). سيتم حفظ وسائط الدالة واستعادتها بين التكرارات. يُستخدم هذا بشكل أساسي لفرض الاستمرارية لأغراض التسييب (fuzzing).
`-nargs` - عدد وسائط طريقة الهدف التي سيتم حفظها بين التكرارات. يُستخدم مع `-loop`
`-callcon [ms64|stdcall|fastcall|thiscall]` - اصطلاح الاستدعاء الذي تستخدمه طريقة الهدف. يُستخدم مع `-loop`
### أخرى
`-target_env key=value` - [حاليًا macOS و Linux/Android فقط] يحدد متغير بيئة إضافيًا لتمريره إلى عملية الهدف. يمكن تحديد خيارات `-target_env` متعددة لتمرير متغيرات بيئة متعددة.
`-force_dep` - [Windows فقط] يفرض تمكين DEP لعملية الهدف.
## وحدة التغطية
يأتي TinyInst مع وحدة تغطية (كمثال)، وهي `LiteCov`. يمكن لوحدة التغطية جمع تغطية الكتل الأساسية أو الحواف (يتم التحكم فيها باستخدام علامة `-covtype`). بالإضافة إلى ذلك، يمكن للوحدة استخراج تغطية "المقارنة" (حساب عدد البايتات المتطابقة في تعليمات cmp/sub) عن طريق تحديد علامة `-cmp_coverage`.
الميزة الخاصة لوحدة التغطية هي أن مخزن التغطية في عملية الهدف يتم تخصيصه مبدئيًا كقراءة فقط، مما يتسبب في استثناء عند مواجهة تغطية جديدة لأول مرة. وبالاقتران مع خيار تجاهل مجموعة فرعية معينة من التغطية، يتيح هذا الاستعلام بسرعة عما إذا كان تشغيل الهدف بمدخل معين قد نتج عنه تغطية جديدة أم لا.
## كيف يعمل TinyInst؟
تم بناء TinyInst فوق مصحح أخطاء مخصص. يراقب المصحح عملية الهدف بحثًا عن أحداث مثل تحميل الوحدات، والوصول إلى نقاط التوقف، وإطلاق الاستثناءات، إلخ. كما ينفذ المصحح نقاط التوقف والاستمرارية إذا تم تحديد طريقة الهدف.
عند تحميل وحدة سيتم تجهيزها، يتم "تجهيزها" مبدئيًا بالطريقة التالية
- يتم وضع علامة "غير قابلة للتنفيذ" على جميع المناطق القابلة للتنفيذ في الوحدة، مع الاحتفاظ بالأذونات الأخرى (القراءة/الكتابة) كما كانت في الأصل. يتسبب هذا في استثناء كلما وصل تدفق التحكم إلى وحدة مُجهزة، ويتم التقاطه ومعالجته بواسطة المصحح.
- يتم تخصيص منطقة ذاكرة قابلة للتنفيذ ضمن نطاق 2GB من نطاق عنوان الوحدة الأصلية. هذا هو المكان الذي سيتم فيه وضع الكود المُجهز/المعاد كتابته للوحدة. 2GB مهم لأنه يسمح باستبدال جميع التعليمات التي تستخدم العنونة بصيغة [rip+offset] بـ [rip+fixed_offset].
كلما تم الدخول إلى وحدة مُجهزة (سواء لأول مرة أو في أي وقت آخر)، يتم تجهيز الكتلة الأساسية التي تم الوصول إليها، بالإضافة إلى جميع الكتل الأساسية التي يمكن اكتشافها بشكل موثوق من خلال تتبع الفروع الشرطية بشكل متكرر وكذلك الاستدعاءات والقفزات المباشرة (مثل jmp offset و call offset).
هذا كافٍ لتشغيل الكود المُجهز لأن
- جميع القفزات/الاستدعاءات المباشرة ستهبط في الكود المُجهز في الموقع الصحيح
- جميع القفزات/الاستدعاءات غير المباشرة (مثل call rax) ستهبط في موقع الكود الأصلي الخاص بها، مما يتسبب في استثناء، يحله المصحح عن طريق استبدال مؤشر التعليمات بالموقع المقابل في الكود المُجهز.
ومع ذلك، على الرغم من أن هذا يعمل، لاحظ أنه سيؤدي إلى استثناء عند كل استدعاء/قفزة غير مباشرة يكون هدفها في وحدة مُجهزة. نظرًا لأن معالجة الاستثناءات بطيئة، فإن تجهيز الأهداف التي تتضمن قدرًا كبيرًا من الوصول غير المباشر (مثل الطرق الافتراضية في C++ ومؤشرات الدوال) سيكون بطيئًا بدون تجهيز إضافي.
### تجهيز الاستدعاءات والقفزات غير المباشرة
يمكن لـ TinyInst تجهيز الاستدعاءات والقفزات غير المباشرة لتجنب الاستثناءات على الأهداف غير المباشرة (التي شوهدت بالفعل). بدلاً من القفز إلى الهدف الأصلي، سيقفز الاستدعاء/القفزة المُجهز بدلاً من ذلك إلى رأس القائمة المرتبطة بالـ stubs. تحتوي كل stub على زوج من (original_target, translated_target). يتحقق مما إذا كان هدف القفزة/الاستدعاء يطابق original_target، وإذا كان الأمر كذلك، يتم توجيه تدفق التحكم إلى translated_target. بخلاف ذلك، يقفز إلى الـ stub التالية. إذا تم الوصول إلى نهاية القائمة، فهذا يعني أن هدف القفزة/الاستدعاء لم يُشاهد من قبل. سيؤدي هذا إلى نقطة توقف يلتقطها المصحح، والتي سيتم حلها عن طريق إنشاء stub أخرى وإدراجها في القائمة.
يمكن تنفيذ هذه الآلية بطريقتين
- قائمة لكل موقع استدعاء (محلية)
- جدول تجزئة عام تستخدمه جميع القفزات/الاستدعاءات غير المباشرة
يؤدي جدول التجزئة العام إلى أداء أفضل. بينما تسمح القائمة المحلية (لكل موقع استدعاء) بالحصول على حواف صحيحة (مع عنوان مصدر صحيح) عند الاستدعاءات/القفزات غير المباشرة.
لاحظ أنه على أنظمة Windows الحديثة، بسبب CFG، تحدث جميع القفزات/الاستدعاءات غير المباشرة من نفس الموقع، لذلك مع الملفات الثنائية المترجمة باستخدام CFG، من المستحيل (بدون نوع من المعالجة الخاصة) الحصول على حواف دقيقة على أي حال. هذا، إلى جانب فائدة الأداء، هو السبب في أن قائمة التجزئة العامة هي الطريقة الافتراضية للتعامل مع الاستدعاءات/القفزات غير المباشرة في TinyInst.
### تصحيح عنوان الإرجاع
بشكل افتراضي، عند حدوث استدعاء في الكود المُجهز، سيكون عنوان الإرجاع الذي يتم كتابته هو التعليمات التالية في *الكود المُجهز*. يعمل هذا بشكل صحيح في معظم الحالات، ومع ذلك سيسبب مشاكل إذا وصلت عملية الهدف إلى عناوين الإرجاع لأغراض أخرى غير الإرجاع. مثال بارز على ذلك هو فك التراص (stack unwinding) أثناء معالجة الاستثناءات على أنظمة التشغيل 64-bit. لذلك، لن تعمل الأهداف التي تحتاج إلى التقاط الاستثناءات بشكل صحيح مع TinyInst بشكل افتراضي.
يمكن حل هذا في معظم الحالات بإضافة علامة `-generate_unwind`، والتي تجعل TinyInst يولد ويسجل بيانات وصفية (metadata) لفك التراص / معالجة الاستثناءات لعملية الهدف. لاحظ أن `-generate_unwind` قد لا يعمل بشكل صحيح على بعض إصدارات Windows الأقدم بسبب الحاجة إلى UNWIND_INFO الإصدار 2.
لدى TinyInst أيضًا خيار (مُتاح من خلال علامة `-patch_return_addresses`) لإعادة كتابة عناوين الإرجاع إلى قيمها المقابلة في الكود غير المُجهز كلما حدث استدعاء. لاحظ مع ذلك أن هذا الخيار يقدم حملاً كبيرًا جدًا، حيث يتسبب في تبديل سياق عند كل إرجاع (حافة خلفية) من وحدة غير مُجهزة إلى وحدة مُجهزة.
## نصائح الأداء
يأتي أكبر حمل في TinyInst من استثناء يتم إطلاقه كلما تم الدخول إلى وحدة مُجهزة من وحدة غير مُجهزة. يمكنك رؤية هذه الاستثناءات وهي تُطلق باستخدام علامة `-trace_module_entries`. يجب استخدام تجهيز القفزات/الاستدعاءات غير المباشرة كلما أمكن ذلك، ويجب عدم استخدام تجهيز الإرجاع كلما أمكن ذلك. يعمل TinyInst بأفضل أداء على الوحدات (أو مجموعات الوحدات) المعقولة والمكتفية بذاتها. على سبيل المثال، إذا كان لديك وحدتان، A و B، حيث تستدعي A الوحدة B كثيرًا ولكن يتم تجهيز B فقط، فسيؤدي ذلك إلى الكثير من التباطؤ. يمكن تحقيق أداء أفضل عن طريق تجهيز كل من A و B.
## نصائح التصحيح
استخدم `-trace_basic_blocks` لرؤية الكتل الأساسية أثناء تنفيذها. سترى كلًا من العناوين في الكود المُجهز والعناوين المقابلة في الكود غير المُجهز.
استخدم استدعاء OnException() لفحص حالة البرنامج عند حدوث الانهيار.
## إخلاء مسؤولية
هذا ليس منتجًا رسميًا من Google.