
एक हल्का डायनेमिक इंस्ट्रुमेंटेशन लाइब्रेरी
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 मानता है कि लक्ष्य अच्छा व्यवहार करने वाला है (नीचे समझाए गए अर्थ में) जो अधिक जटिल फ्रेमवर्क के मामले में नहीं होता है। इस प्रकार, आप शायद [पहले DynamoRIO के साथ किए गए](https://www.slideshare.net/MaximShudrak/fuzzing-malware-for-fun-profit-applying-coverageguided-fuzzing-to-find-bugs-in-modern-malware) जैसे मैलवेयर के खिलाफ TinyInst को सफलतापूर्वक नहीं चला पाएंगे। दूसरी ओर, यदि कोई लक्ष्य अन्य फ्रेमवर्क के साथ काम नहीं करता है, क्योंकि उस मॉड्यूल को इंस्ट्रूमेंट करने की आवश्यकता नहीं है, और इंस्ट्रूमेंट किया गया मॉड्यूल अच्छा व्यवहार करने वाला है, तो यह TinyInst के साथ काम कर सकता है। चूंकि TinyInst के साथ, अधिकांश प्रक्रिया मूल रूप से चलेगी, इसलिए प्रक्रिया का स्टार्टअप समय कम होगा, और उन मामलों में यह अन्य समाधानों से बेहतर प्रदर्शन कर सकता है जहां लक्ष्य प्रक्रिया उन मॉड्यूल में बहुत समय बिताती है जहां इंस्ट्रूमेंटेशन की आवश्यकता नहीं होती है।
### यह [Mesos](https://github.com/gamozolabs/mesos) और [TrapFuzz](https://github.com/googleprojectzero/p0tools/tree/master/TrapFuzz) से कैसे तुलना करता है?
TinyInst एक पूर्ण बाइनरी रीराइटिंग समाधान है, इसलिए लक्ष्य मॉड्यूल में मनमाना व्यवहार बदला जा सकता है। यह उदाहरण के लिए, केवल बेसिक ब्लॉक के बजाय एज कवरेज निकालने में सक्षम बनाता है। इसके अतिरिक्त, TinyInst बेसिक ब्लॉकों की पहचान करने के लिए IDA Pro जैसे अन्य सॉफ़्टवेयर पर निर्भर नहीं करता है।
### TinyInst कौन सा ऑपरेटिंग सिस्टम समर्थन करता है?
TinyInst Windows (x86 और x64), macOS (x64 और ARM64), Linux (x64 और ARM64) और Android (ARM64) पर काम करता है। कृपया प्रत्येक ऑपरेटिंग सिस्टम के लिए संबंधित निर्देशिका में README देखें, अतिरिक्त नोट्स और सीमाओं के लिए।
### कौन से लक्ष्य TinyInst के साथ संगत हैं?
TinyInst मानता है कि सभी इंस्ट्रूमेंट किए गए मॉड्यूल अच्छे व्यवहार वाले हैं, इस अर्थ में कि
- कोई स्व-संशोधित कोड नहीं है
- स्टैक पर रिटर्न एड्रेस कभी भी प्रोग्राम द्वारा सीधे एक्सेस नहीं किया जाता है
OR/AND (सेटिंग्स के आधार पर)
- स्टैक के शीर्ष से पहले (ESP/RSP द्वारा इंगित किए गए पते से कम पते पर) कभी भी कोई डेटा संग्रहीत नहीं किया जाता है। इस शर्त को `-stack_offset` फ्लैग का उपयोग करके "(ESP/RSP - arbitrary_offset) से पहले कोई डेटा नहीं" में शिथिल किया जा सकता है।
TinyInst को लक्ष्य प्रक्रिया के लिए DEP/NX सक्षम होने की भी आवश्यकता होती है। यदि पहले से ऐसा नहीं है, तो आप इसे चालू करने के लिए `-force_dep` फ्लैग का उपयोग कर सकते हैं। हालाँकि, असंभावित स्थिति में कि लक्ष्य को ठीक से कार्य करने के लिए वास्तव में DEP बंद होने की आवश्यकता है, इसे चालू करने से यह गलत व्यवहार कर सकता है।
### प्रदर्शन ओवरहेड क्या है?
इमेज डिकोडिंग पर प्रारंभिक माप के अनुसार, डिफ़ॉल्ट TinyInst सेटिंग्स के साथ एक अच्छा व्यवहार करने वाले 64-बिट लक्ष्य पर, प्रदर्शन ओवरहेड बिना क्लाइंट के लगभग 15% और उदाहरण कवरेज-संग्रह करने वाले क्लाइंट के साथ लगभग 20% था। ध्यान दें कि इसमें शुरुआती रूप से इंस्ट्रूमेंट किए गए मॉड्यूल द्वारा पेश किया गया टाइमआउट शामिल नहीं है। अधिक विवरण के लिए नीचे दिए गए प्रदर्शन युक्तियाँ देखें।
## TinyInst बनाना
1. एक टर्मिनल खोलें और अपना बिल्ड वातावरण सेट करें (उदा. 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-बिट बिल्ड Windows और Linux ऑपरेटिंग सिस्टम पर 32-बिट टारगेट के विरुद्ध भी चलेगा
नोट #2: 64-बिट Windows पर 32-बिट बिल्ड बनाने में समस्याएँ आ रही हैं क्योंकि वातावरण ठीक से सेट नहीं है और लाइब्रेरीज़ गायब हैं? `cmake --build` चलाने के बजाय Visual Studio में जनरेट की गई .sln फ़ाइल खोलें और वहाँ से बिल्ड करें। यह भी ध्यान दें कि 64-बिट बिल्ड 32-बिट टारगेट पर काम करेगा, इसलिए 32-बिट बिल्ड बनाना आवश्यक नहीं हो सकता है।
## TinyInst का उपयोग
TinyInst मुख्य रूप से अन्य प्रोग्रामों के अंदर एक लाइब्रेरी के रूप में उपयोग किए जाने के लिए है।
TinyInst क्लाइंट TinyInst क्लास के सबक्लास के रूप में लिखा जाता है। क्लाइंट फिर अपनी आवश्यकता वाले API विधियों को ओवरराइड कर सकता है। API विधियाँ नीचे परिभाषित की गई हैं।
क्लाइंट बनाए जाने के बाद, उसे कमांड लाइन विकल्पों के साथ निम्न को कॉल करके प्रारंभ किया जाना चाहिए:
`void init(int argc, char **argv);`
कमांड लाइन विकल्प नीचे परिभाषित हैं और क्लाइंट अपने स्वयं के विकल्प भी परिभाषित कर सकता है। उसके बाद, एक इंस्ट्रूमेंटेड प्रोग्राम को चलाने और नियंत्रित करने के लिए निम्नलिखित फ़ंक्शन का उपयोग किया जा सकता है।
`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 के साथ एक उदाहरण कवरेज बाइनरी आती है, जिसे निम्न का उपयोग करके लागू किया जा सकता है:
`<options> -- <target command line>`
Windows पर उदाहरण:
`litecov.exe -instrument_module notepad.exe -coverage_file coverage.txt -- notepad.exe`
## इंस्ट्रूमेंटेशन API
### डीबगर इवेंट कॉलबैक
ये कॉलबैक केवल जानकारी के लिए हैं और क्लाइंट को इनके दौरान कोई इंस्ट्रूमेंटेड कोड उत्सर्जित नहीं करना चाहिए। इन घटनाओं को स्वयं संभालने से पहले क्लाइंट को सुपरक्लास में परिभाषित उसी हैंडलर को कॉल करना चाहिए।
`OnProcessCreated`
जब टारगेट प्रक्रिया बनाई जाती है या उससे अटैच किया जाता है तब कॉल किया जाता है।
`OnProcessExit`
जब टारगेट प्रक्रिया समाप्त होती है तब कॉल किया जाता है।
`OnProcessEntrypoint`
जब प्रक्रिया (मुख्य बाइनरी) का एंट्रीपॉइंट पहुँच जाता है तब कॉल किया जाता है।
`OnTargetMethodReached`
यदि टारगेट विधि परिभाषित है, तो पहली बार टारगेट विधि तक पहुँचने पर कॉल किया जाता है।
`OnModuleLoaded`
जब कोई मॉड्यूल लोड होता है तब कॉल किया जाता है। प्रत्येक मॉड्यूल के लिए कॉल किया जाता है, केवल इंस्ट्रूमेंटेड मॉड्यूल के लिए नहीं।
`OnModuleUnloaded`
जब कोई मॉड्यूल अनलोड होता है तब कॉल किया जाता है। प्रत्येक मॉड्यूल के लिए कॉल किया जाता है, केवल इंस्ट्रूमेंटेड मॉड्यूल के लिए नहीं।
`OnException`
जब कोई एक्सेप्शन सामने आता है तब कॉल किया जाता है। क्लाइंट को या तो true लौटाना चाहिए (यदि एक्सेप्शन हैंडल किया गया था) या पैरेंट क्लास पर उसी विधि का परिणाम लौटाना चाहिए।
### इंस्ट्रूमेंटेशन कॉलबैक
इन कॉलबैक के दौरान, क्लाइंट `WriteCode()` को कॉल करके टारगेट में कोड जोड़ सकता है। ध्यान दें कि किसी भी कॉन्टेक्स्ट (जैसे इन्सर्ट किए गए कोड में क्लोबर किए गए रजिस्टर और फ़्लैग) को सहेजने और पुनर्स्थापित करने की ज़िम्मेदारी क्लाइंट की है।
`InstrumentBasicBlock`
इसका उपयोग किसी विशेष बेसिक ब्लॉक पर चलने वाला कोड इन्सर्ट करने के लिए किया जा सकता है।
`InstrumentEdge`
इसका उपयोग किसी विशेष एज पर चलने वाला कोड इन्सर्ट करने के लिए किया जा सकता है। नोट: प्रदर्शन कारणों से, यह कॉलबैक केवल गैर-नियतात्मक एज (यानी कंडीशनल जंप) और इनडायरेक्ट जंप/कॉल (जैसे `call rax`) पर उत्सर्जित होता है। ऐसे एज के लिए जहाँ पिछले बेसिक ब्लॉक को देखते हुए अगला बेसिक ब्लॉक हमेशा ज्ञात होता है (जैसे `jmp offset`, `call offset`), कोई कॉलबैक उत्सर्जित नहीं किया जाएगा।
`InstrumentInstruction`
इसका उपयोग इंस्ट्रक्शन को संशोधित करने या उससे पहले कोड इन्सर्ट करने के लिए किया जा सकता है। रिटर्न कोड के आधार पर, कॉलबैक के बाद मूल इंस्ट्रक्शन या तो उत्सर्जित होगा या नहीं।
### अन्य कॉलबैक
`OnModuleEntered`
जब किसी अन्य मॉड्यूल से इंस्ट्रूमेंटेड मॉड्यूल में नियंत्रण प्रवाह स्थानांतरित होता है तब कॉल किया जाता है।
`OnModuleInstrumented`
जब कोई मॉड्यूल इंस्ट्रूमेंटेड होता है तब कॉल किया जाता है। यह आम तौर पर तब होता है जब प्रक्रिया एंट्रीपॉइंट पहुँच जाता है (यदि टारगेट विधि परिभाषित नहीं है) या जब टारगेट विधि पहुँच जाती है (यदि वह परिभाषित है)। क्लाइंट यहाँ अपने इंस्ट्रूमेंटेशन-संबंधित डेटा को प्रारंभ कर सकता है।
`OnModuleUninstrumented`
जब इंस्ट्रूमेंटेशन डेटा अब मान्य नहीं रहता और उसे साफ़ करने की आवश्यकता होती है तब कॉल किया जाता है। ध्यान दें कि यह मॉड्यूल के अनलोड होने के समान नहीं है, क्योंकि डिफ़ॉल्ट रूप से, इंस्ट्रूमेंटेशन मॉड्यूल अनलोड / रीलोड के दौरान बना रहता है। इस कॉलबैक का उपयोग क्लाइंट में किसी भी इंस्ट्रूमेंटेशन-संबंधित डेटा को साफ़ करने के लिए किया जा सकता है।
### हुक API
ऊपर प्रलेखित सामान्य-उद्देश्य API के अतिरिक्त, TinyInst एक हुकिंग API भी लागू करता है जो व्यक्तिगत फ़ंक्शनों के व्यवहार का निरीक्षण और संशोधन करने के लिए बेहतर उपयुक्त है। यह API [अलग पृष्ठ](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` - इंस्ट्रूमेंटेड कोड के लिए स्टैक अनवाइंडिंग डेटा उत्पन्न करता है (तेज़ 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()` कॉलबैक के माध्यम से syscall प्रारंभ/समाप्ति ईवेंट प्राप्त करने में सक्षम बनाता है।
`-full_address_map` - इंस्ट्रूमेंटेड कोड में पतों का मूल कोड में पतों के साथ एक इंस्ट्रक्शन-स्तरीय मानचित्र बनाए रखता है। मेमोरी-भारी, परंतु डीबगिंग के लिए उपयोगी।
### टारगेट विधि और पर्सिस्टेंस
TinyInst उपयोगकर्ता को एक टारगेट विधि परिभाषित करने की अनुमति देता है। यदि कोई टारगेट विधि परिभाषित है, तो जब तक टारगेट विधि पहली बार नहीं पहुँच जाती, कोई कोड इंस्ट्रूमेंट नहीं किया जाएगा (सब कुछ नेटिव रूप से चलेगा)। इसके अतिरिक्त, TinyInst टारगेट विधि के प्रवेश और निकास पर निष्पादन को तोड़ देगा।
`-target_module` - टारगेट विधि वाला मॉड्यूल
`-target_method` - टारगेट विधि का नाम। यह केवल तभी काम करता है यदि टारगेट विधि एक्सपोर्ट की गई है या आपके पास टारगेट मॉड्यूल के लिए सिंबल हैं।
`-target_offset` - तब उपयोग करें जब टारगेट विधि को नाम से निर्दिष्ट नहीं किया जा सकता। मॉड्यूल बेस से टारगेट विधि का सापेक्ष पता।
`-loop` - यदि यह फ़्लैग निर्दिष्ट है, तो TinyInst टारगेट विधि को एक अनंत लूप में चलाएगा (या जब तक `Kill()` को कॉल नहीं किया जाता या प्रक्रिया किसी अन्य कारण से समाप्त नहीं होती)। फ़ंक्शन तर्क पुनरावृत्तियों के बीच सहेजे और पुनर्स्थापित किए जाएँगे। यह मुख्य रूप से फ़ज़िंग के लिए पर्सिस्टेंस को मजबूर करने के लिए उपयोग किया जाता है।
`-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_coverage` फ़्लैग निर्दिष्ट करके "compare" कवरेज (cmp/sub इंस्ट्रक्शन में मेल खाने वाले बाइट्स की संख्या गिनना) निकाल सकता है।
कवरेज मॉड्यूल की विशेष विशेषता यह है कि टारगेट प्रक्रिया में कवरेज बफर को प्रारंभ में केवल-पठनीय के रूप में आवंटित किया जाता है, जिससे पहली बार नई कवरेज सामने आने पर एक एक्सेप्शन उत्पन्न होता है। कवरेज के एक निश्चित उपसमुच्चय को अनदेखा करने के विकल्प के साथ संयुक्त रूप से, यह शीघ्रता से पूछताछ करने में सक्षम बनाता है कि किसी दिए गए इनपुट के साथ टारगेट चलाने से नई कवरेज उत्पन्न हुई या नहीं।
## TinyInst कैसे काम करता है?
TinyInst एक कस्टम डीबगर के ऊपर बनाया गया है। डीबगर टारगेट प्रक्रिया में मॉड्यूल के लोड होने, ब्रेकपॉइंट हिट होने, एक्सेप्शन फायर होने आदि जैसी घटनाओं पर नज़र रखता है। यदि टारगेट विधि निर्दिष्ट है, तो डीबगर ब्रेकपॉइंट और पर्सिस्टेंस भी लागू करता है।
जब इंस्ट्रूमेंट किए जाने वाला मॉड्यूल लोड होता है, तो उसे प्रारंभ में निम्नलिखित तरीके से "इंस्ट्रूमेंट" किया जाता है:
- मॉड्यूल के सभी निष्पादन योग्य क्षेत्रों को गैर-निष्पादन योग्य के रूप में चिह्नित किया जाता है, जबकि अन्य अनुमतियाँ (पढ़ें/लिखें) मूल रूप से बनाए रखी जाती हैं। इससे जब भी नियंत्रण प्रवाह किसी इंस्ट्रूमेंटेड मॉड्यूल तक पहुँचता है, एक एक्सेप्शन उत्पन्न होता है, जिसे डीबगर द्वारा पकड़ा और हैंडल किया जाता है।
- मूल मॉड्यूल पता सीमा के 2GB के भीतर मेमोरी का एक निष्पादन योग्य क्षेत्र आवंटित किया जाता है। यह वह जगह है जहाँ मॉड्यूल का इंस्ट्रूमेंटेड/पुनर्लिखित कोड रखा जाएगा। 2GB महत्वपूर्ण है क्योंकि यह `[rip+offset]` के रूप में एड्रेसिंग का उपयोग करने वाले सभी इंस्ट्रक्शन को `[rip+fixed_offset]` से बदलने की अनुमति देता है।
जब भी किसी इंस्ट्रूमेंटेड मॉड्यूल में प्रवेश किया जाता है (चाहे पहली बार या किसी अन्य समय), जो बेसिक ब्लॉक हिट हुआ था, उसे कंडीशनल ब्रांच के साथ-साथ डायरेक्ट कॉल और जंप (जैसे `jmp offset`, `call offset`) को पुनरावर्ती रूप से अनुसरण करके विश्वसनीय रूप से खोजे जा सकने वाले सभी बेसिक ब्लॉक के साथ इंस्ट्रूमेंट किया जाता है।
यह इंस्ट्रूमेंटेड कोड चलाने के लिए पर्याप्त है क्योंकि:
- सभी डायरेक्ट जंप/कॉल सही स्थान पर इंस्ट्रूमेंटेड कोड में लैंड करेंगे।
- सभी इनडायरेक्ट जंप/कॉल (जैसे `call rax`) अपने मूल कोड स्थान पर लैंड करेंगे, जिससे एक एक्सेप्शन उत्पन्न होता है, जिसे डीबगर इंस्ट्रक्शन पॉइंटर को इंस्ट्रूमेंटेड कोड में संबंधित स्थान से बदलकर हल करता है।
हालाँकि, यह काम करते हुए भी, ध्यान दें कि यह हर उस इनडायरेक्ट कॉल/जंप पर एक एक्सेप्शन उत्पन्न करेगा जिसका टारगेट किसी इंस्ट्रूमेंटेड मॉड्यूल में है। चूँकि एक्सेप्शन हैंडलिंग धीमी है, बहुत अधिक इनडायरेक्शन वाले टारगेट (जैसे C++ में वर्चुअल मेथड, फ़ंक्शन पॉइंटर) बिना अतिरिक्त इंस्ट्रूमेंटेशन के धीमे होंगे।
### इनडायरेक्ट कॉल और जंप का इंस्ट्रूमेंटेशन
TinyInst इनडायरेक्ट कॉल और जंप को इंस्ट्रूमेंट कर सकता है ताकि (पहले से देखे गए) इनडायरेक्ट टारगेट पर एक्सेप्शन से बचा जा सके। एक इंस्ट्रूमेंटेड कॉल/जंप, मूल टारगेट पर जंप करने के बजाय, स्टब्स की लिंक्ड लिस्ट के शीर्ष पर जंप करेगा। प्रत्येक स्टब में (original_target, translated_target) की एक जोड़ी होती है। यह जाँचता है कि जंप/कॉल टारगेट original_target से मेल खाता है या नहीं, और यदि ऐसा है, तो नियंत्रण प्रवाह को translated_target की ओर निर्देशित किया जाता है। अन्यथा, यह अगले स्टब पर जंप करता है। यदि सूची के अंत तक पहुँच जाता है, तो इसका मतलब है कि जंप/कॉल टारगेट पहले नहीं देखा गया है। इससे एक ब्रेकपॉइंट उत्पन्न होगा जिसे डीबगर द्वारा पकड़ा जाता है, और एक और स्टब बनाकर उसे सूची में सम्मिलित करके हल किया जाएगा।
इस तंत्र को 2 तरीकों से लागू किया जा सकता है:
- प्रति-कॉलसाइट (लोकल) सूची
- सभी इनडायरेक्ट जंप/कॉल द्वारा उपयोग की जाने वाली वैश्विक हैशटेबल
वैश्विक हैशटेबल बेहतर प्रदर्शन देती है। लोकल (प्रति-कॉलसाइट सूची) इनडायरेक्ट कॉल/जंप पर सही एज (सही स्रोत पते के साथ) प्राप्त करने की अनुमति देती है।
ध्यान दें कि आधुनिक Windows पर, CFG के कारण, सभी इनडायरेक्ट जंप/कॉल एक ही स्थान से होते हैं, इसलिए CFG-संकलित बाइनरी के साथ, (किसी प्रकार की विशेष हैंडलिंग के बिना) सटीक एज प्राप्त करना वैसे भी असंभव है। यह, प्रदर्शन लाभ के साथ, वह कारण है कि TinyInst में इनडायरेक्ट कॉल/जंप को संभालने के लिए वैश्विक हैशलिस्ट डिफ़ॉल्ट तरीका है।
### रिटर्न एड्रेस पैचिंग
डिफ़ॉल्ट रूप से, जब इंस्ट्रूमेंटेड कोड में कोई कॉल होता है, तो लिखा जा रहा रिटर्न एड्रेस *इंस्ट्रूमेंटेड कोड* में अगला इंस्ट्रक्शन होगा। यह अधिकांश मामलों में सही ढंग से काम करता है, हालाँकि यदि टारगेट प्रक्रिया कभी रिटर्न के अलावा अन्य उद्देश्यों के लिए रिटर्न एड्रेस तक पहुँचती है तो यह समस्याएँ पैदा करेगा। इसका एक उल्लेखनीय उदाहरण 64-बिट ऑपरेटिंग सिस्टम पर एक्सेप्शन हैंडलिंग के दौरान स्टैक अनवाइंडिंग है। इसलिए, जिन टारगेट को एक्सेप्शन पकड़ने की आवश्यकता होती है, वे डिफ़ॉल्ट रूप से TinyInst के साथ सही ढंग से काम नहीं करेंगे।
इसे अधिकांश मामलों में `-generate_unwind` फ़्लैग जोड़कर हल किया जा सकता है, जो TinyInst को टारगेट प्रक्रिया के लिए स्टैक अनवाइंडिंग / एक्सेप्शन हैंडलिंग मेटाडेटा उत्पन्न करने और पंजीकृत करने का कारण बनता है। ध्यान दें कि UNWIND_INFO संस्करण 2 की आवश्यकता के कारण `-generate_unwind` कुछ पुराने Windows संस्करणों पर सही ढंग से काम नहीं कर सकता है।
TinyInst के पास एक विकल्प भी है (`-patch_return_addresses` फ़्लैग के माध्यम से उजागर) जब भी कोई कॉल होता है तो रिटर्न एड्रेस को गैर-इंस्ट्रूमेंटेड कोड में उनके संबंधित मानों में फिर से लिखने के लिए। हालाँकि ध्यान दें कि यह विकल्प काफी बड़ा ओवरहेड पेश करता है, क्योंकि यह एक गैर-इंस्ट्रूमेंटेड से इंस्ट्रूमेंटेड मॉड्यूल में हर रिटर्न (पिछड़े एज) पर एक कॉन्टेक्स्ट स्विच का कारण बनता है।
## प्रदर्शन युक्तियाँ
TinyInst में सबसे बड़ा ओवरहेड एक एक्सेप्शन के फेंके जाने से आता है जब भी किसी गैर-इंस्ट्रूमेंटेड मॉड्यूल से इंस्ट्रूमेंटेड मॉड्यूल में प्रवेश किया जाता है। आप `-trace_module_entries` फ़्लैग का उपयोग करके इन एक्सेप्शनों को ट्रिगर होते देख सकते हैं। जब भी संभव हो इनडायरेक्ट जंप/कॉल इंस्ट्रूमेंटेशन का उपयोग किया जाना चाहिए और जब भी संभव हो रिटर्न इंस्ट्रूमेंटेशन का उपयोग नहीं किया जाना चाहिए। TinyInst उन मॉड्यूल (या मॉड्यूल समूहों) पर सबसे अच्छा प्रदर्शन करता है जो उचित रूप से आत्म-निहित हैं। उदाहरण के लिए यदि आपके पास दो मॉड्यूल हैं, A और B, जहाँ A अक्सर B को कॉल करता है लेकिन केवल B इंस्ट्रूमेंटेड है, तो यह बहुत अधिक धीमापन पैदा करेगा। A और B दोनों को इंस्ट्रूमेंट करके बेहतर प्रदर्शन प्राप्त किया जा सकता है।
## डीबगिंग युक्तियाँ
बेसिक ब्लॉकों को निष्पादित होते समय देखने के लिए `-trace_basic_blocks` का उपयोग करें। आपको इंस्ट्रूमेंटेड कोड में पते और गैर-इंस्ट्रूमेंटेड कोड में संबंधित पते दोनों दिखाई देंगे।
क्रैश होने पर प्रोग्राम स्थिति की जाँच करने के लिए OnException() कॉलबैक का उपयोग करें।
## अस्वीकरण
यह एक आधिकारिक Google उत्पाद नहीं है।