पायथन कार्यक्रम के स्रोत कोड की सुरक्षा के लिए एक तरीका है

यह सब कैसे शुरू हुआ


एक बार मुझे वैज्ञानिक गणना के लिए एक छोटी परियोजना के विकास में भाग लेना था, जिसे पायथन प्रोग्रामिंग भाषा में विकसित किया गया था। प्रारंभ में, पायथन को प्रयोग, विज़ुअलाइज़ेशन, रैपिड प्रोटोटाइप और एल्गोरिथम विकास के लिए एक सुविधाजनक और लचीली भाषा के रूप में चुना गया था, लेकिन बाद में इस परियोजना को विकसित करने के लिए मुख्य भाषा बन गई। यह ध्यान दिया जाना चाहिए कि परियोजना बड़ी नहीं थी, लेकिन काफी तकनीकी रूप से संतृप्त थी। आवश्यक कार्यक्षमता प्रदान करने के लिए, परियोजना ने व्यापक रूप से ग्राफ सिद्धांत एल्गोरिदम, गणितीय अनुकूलन, रैखिक बीजगणित और सांख्यिकी का उपयोग किया। सज्जाकार, मेटाक्लास और आत्मनिरीक्षण उपकरण भी इस्तेमाल किए गए थे। विकास की प्रक्रिया में, मुझे तीसरे पक्ष के गणितीय पैकेज और पुस्तकालयों का उपयोग करना पड़ा, उदाहरण के लिए, जैसे कि सुन्न और डरावना, साथ ही साथ कई अन्य।

समय के साथ, यह स्पष्ट हो गया कि संकलित भाषा में एक परियोजना को फिर से लिखना समय और संसाधनों में बहुत महंगा है। इस मामले में गति और स्मृति खपत महत्वपूर्ण संकेतक नहीं थे और काफी स्वीकार्य और पर्याप्त थे। इसलिए, यह सब कुछ छोड़ने का फैसला किया गया था, और पायथन में परियोजना का विकास और समर्थन जारी रखने के लिए। इसके अलावा, प्रलेखन में सबसे अधिक भाग के लिए पहले से ही स्फिंक्स का उपयोग करके लिखा गया है।

परियोजना एक पुस्तकालय थी जिसके कार्यों का उपयोग एक बड़े सॉफ्टवेयर पैकेज में विस्तार मॉड्यूल में से एक में किया गया था। सॉफ्टवेयर पैकेज सी ++ में लिखा गया था, एक वाणिज्यिक उत्पाद था, एक हार्डवेयर कुंजी के साथ सुरक्षा थी, और स्रोत कोड प्रदान किए बिना ग्राहकों को वितरित किया गया था।

यहां एक नई समस्या को तुरंत पहचान लिया गया: हमारे पायथन पुस्तकालय के स्रोत कोड की सुरक्षा कैसे करें? हो सकता है, अन्यथा, कोई भी ऐसा करना शुरू नहीं करता था, मैं निश्चित रूप से करूंगा, लेकिन कुछ जानकारियों को पुस्तकालय में लागू किया गया था, और परियोजना प्रबंधक नहीं चाहते थे कि ये घटनाक्रम प्रतियोगियों को मिलें। चूंकि मैं कलाकारों में से एक था, इसलिए मुझे इस समस्या में शामिल होना पड़ा। इसके बाद, मैं उस मुख्य विचार के बारे में बात करने की कोशिश करूंगा जो उसमें से निकला था, और हम अनावश्यक आँखों से पायथन स्रोत को छिपाने में कैसे कामयाब रहे।

लोग क्या पेशकश करते हैं?


जैसा कि ज्यादातर डेवलपर्स शायद जानते हैं, पायथन एक व्याख्यात्मक, गतिशील भाषा है जिसमें समृद्ध आत्मनिरीक्षण क्षमता है। * .Pyc और * .pyo मॉड्यूल (बाइटकोड) की बाइनरी फाइलें आसानी से विघटित हो जाती हैं, इसलिए आप उन्हें उनके शुद्ध रूप में वितरित नहीं कर सकते हैं (यदि हमने वास्तविक के लिए स्रोत नहीं दिखाने का फैसला किया है)।

कैसे, मुझे लगता है, मेरे स्थान पर किसी ने भी, सबसे पहले मैंने खोज करने का फैसला किया, लेकिन लोग ऐसे मामलों में क्या करते हैं? पहली खोजों से पता चला कि लोगों को यह पता नहीं है कि स्टैकओवरफ्लो पर और अन्य स्थानों पर इसके बारे में क्या पूछना है, उदाहरण के लिए, स्टैकओवरफ्लो पर एक सवाल है । खोज करने के बाद, मैं इस निष्कर्ष पर पहुंचा कि हर जगह वे कई विवादास्पद तरीके पेश करते हैं:



कई कारणों से, मैंने इन सभी तरीकों को अनुचित बताया। उदाहरण के लिए, पायथन कोड का आक्षेप। ठीक है, जब भाषा का वाक्य रचना इंडेंटेशन पर बनाया गया है, तो किस तरह का आक्षेप हो सकता है, और भाषा स्वयं "चालाक आत्मनिरीक्षण" से भरा हुआ है? सभी पायथन मॉड्यूल को बाइनरी एक्सटेंशन मॉड्यूल में अनुवाद करना भी संभव नहीं था, क्योंकि परियोजना, मुझे याद है, कई तृतीय-पक्ष पैकेजों का उपयोग करके तकनीकी रूप से काफी जटिल था, और इसमें बहु-स्तरीय पदानुक्रम में बड़ी संख्या में मॉड्यूल शामिल थे जो कि ओवरटेक करने के लिए थकाऊ थे। * .pyd, और फिर नीले रंग से रेंगने वाले कीड़े पकड़ते हैं। मैं opcodes के प्रतिस्थापन के साथ टिंकर नहीं करना चाहता था, क्योंकि मुझे पायथन दुभाषिया की अपनी विधानसभा को वितरित और बनाए रखना होगा, और यहां तक ​​कि इसके द्वारा उपयोग किए जाने वाले सभी तीसरे पक्ष के पुस्तकालयों के पायथन मॉड्यूल को संकलित करना होगा।

कुछ बिंदु पर, यह मुझे लगा कि पायथन स्रोत कोड की सुरक्षा के साथ यह विचार बेकार था, मुझे इसे पूरा करना था और प्रबंधन को यह विश्वास दिलाना था कि कुछ भी काम नहीं करेगा और कुछ उपयोगी होगा। हम * .pyc फ़ाइलें देते हैं और अच्छी तरह से, जो वहाँ समझ जाएगा? यह समझाने के लिए संभव नहीं था, कोई भी C ++ में पुस्तकालय को फिर से लिखना नहीं चाहता था, और परियोजना को सौंपने की आवश्यकता थी। अंत में, फिर भी कुछ करने में कामयाब रहे। यदि आप अभी भी रुचि रखते हैं, तो आप इसके बारे में अधिक पढ़ सकते हैं।

हमने क्या किया है?


डिजिटल मीडिया पर किसी भी जानकारी को अजनबियों से सबसे अच्छी तरह से क्या बचा सकता है? मुझे लगता है कि यह एन्क्रिप्शन है। इस मौलिक विचार के साथ, मैंने फैसला किया कि स्रोत कोड को एन्क्रिप्ट किया जाना चाहिए, अन्यथा यह नहीं होना चाहिए। एक बाहरी पर्यवेक्षक के लिए, जो अत्यधिक रुचि दिखाने लगे, यह सब समझ से बाहर की सामग्री के साथ अस्पष्ट फ़ाइलों का एक गुच्छा जैसा दिखना चाहिए। यह एक बहुत ही अप्रिय है, लेकिन चर नामों की जगह और खाली लाइनों को सम्मिलित करने से अधिक उन्नत है।

मेरे विचार की ट्रेन इस प्रकार थी:



मुख्य विचार, मुझे लगता है, स्पष्ट है - यह अधिक उन्नत आक्षेप है। यह कैसे करना है? Googling, मैं इस नतीजे पर पहुंचा कि ऐसा करना काफी यथार्थवादी है और काफी सरल भी। स्रोत एन्क्रिप्शन के साथ, सब कुछ स्पष्ट है, आप विभिन्न तरीकों से फ़ाइलों को एन्क्रिप्ट और / या बाधित कर सकते हैं, मुख्य बात यह है कि "दलिया" और "कुछ भी स्पष्ट नहीं है", और यह सब भी अपने मूल रूप में एक अज्ञात तरीके (आपत्ति के मामले में) पर वापस आना चाहिए। यहाँ उदाहरण के लिए, मैं "एन्क्रिप्शन" के लिए बेस 64 पायथन मॉड्यूल का उपयोग करूँगा। गैर-महत्वपूर्ण मामलों में, आप अद्भुत obfuscate पैकेज का उपयोग कर सकते हैं।

अजगर प्रोटोकॉल प्रोटोकॉल

हमें एन्क्रिप्टेड फ़ाइलों से मॉड्यूल आयात करने की क्षमता का एहसास कैसे होता है? सौभाग्य से, पायथन में एक आयात हुक प्रणाली है जो आयातक प्रोटोकॉल ( PEP 302 ) पर चलती है। इसलिए हम इस अवसर का उपयोग करेंगे। sys.meta_path डिक्शनरी का उपयोग आयात को बाधित करने के लिए किया जाता है, जिसमें आयातक प्रोटोकॉल को लागू करने वाले finder/loader ऑब्जेक्ट को संग्रहीत किया जाना चाहिए। इस शब्दकोश का मतदान हमेशा तब तक होता है जब तक कि sys.path में रास्तों की जाँच नहीं हो जाती।

आयात प्रोटोकॉल के कार्यान्वयन को कम करने के लिए, दो तरीकों को लागू किया जाना चाहिए: find_module और load_modulefind_module विधि एक विशिष्ट मॉड्यूल / पैकेज खोजने के लिए ज़िम्मेदार है (आखिरकार, हमें केवल हमारे मॉड्यूल के आयात को इंटरसेप्ट करने की आवश्यकता है, और बाकी को मानक तंत्र को दिया जाना चाहिए), और load_module विधि, क्रमशः, एक विशिष्ट मॉड्यूल को लोड करती है, यदि यह find_module विधि में "पाया" गया हो।

तो, यह बात पर लगता है। आप एक सरल उदाहरण दे सकते हैं। एक वर्ग का न्यूनतम उदाहरण जो आयातक प्रोटोकॉल को लागू करता है, हमारे उद्देश्यों के लिए उपयुक्त है। वह सामान्य पैकेज संरचना से बेस 64 "एन्क्रिप्टेड" मॉड्यूल आयात करेगा (इस मामले में, सादगी के लिए, हम बस फाइलों की सामग्री को "एन्क्रिप्टेड" करते हैं, लेकिन उनके नाम और पैकेज संरचनाओं को नहीं बदलते हैं)। हम मानते हैं कि हमारे मॉड्यूल के लिए फ़ाइल एक्सटेंशन को गर्व से ".b64" कहा जाएगा।

आयातक वर्ग
 #coding=utf-8 import os import sys import imp import base64 EXT = '.b64' #=============================================================================== class Base64Importer(object): """     python-,   base64   Import Protocol (PEP 302)    ,   base64   . """ #--------------------------------------------------------------------------- def __init__(self, root_package_path): self.__modules_info = self.__collect_modules_info(root_package_path) #--------------------------------------------------------------------------- def find_module(self, fullname, path=None): """          fullname  base64     ,      (finder),  None,     base64. """ if fullname in self.__modules_info: return self return None #--------------------------------------------------------------------------- def load_module(self, fullname): """  base64      fullname  base64,     .   ImportError    . """ if not fullname in self.__modules_info: raise ImportError(fullname) #   imp.acquire_lock() try: mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) mod.__file__ = "<{}>".format(self.__class__.__name__) mod.__loader__ = self if self.is_package(fullname): mod.__path__ = [] mod.__package__ = fullname else: mod.__package__ = fullname.rpartition('.')[0] src = self.get_source(fullname) try: exec src in mod.__dict__ except: del sys.modules[fullname] raise ImportError(fullname) finally: imp.release_lock() return mod #--------------------------------------------------------------------------- def is_package(self, fullname): """ True  fullname   """ return self.__modules_info[fullname]['ispackage'] #--------------------------------------------------------------------------- def get_source(self, fullname): """    fullname         base64 """ filename = self.__modules_info[fullname]['filename'] try: with file(filename, 'r') as ifile: src = base64.decodestring(ifile.read()) except IOError: src = '' return src #--------------------------------------------------------------------------- def __collect_modules_info(self, root_package_path): """       """ modules = {} p = os.path.abspath(root_package_path) dir_name = os.path.dirname(p) + os.sep for root, _, files in os.walk(p): #     filename = os.path.join(root, '__init__' + EXT) p_fullname = root.rpartition(dir_name)[2].replace(os.sep, '.') modules[p_fullname] = { 'filename': filename, 'ispackage': True } #       for f in files: if not f.endswith(EXT): continue filename = os.path.join(root, f) fullname = '.'.join([p_fullname, os.path.splitext(f)[0]]) modules[fullname] = { 'filename': filename, 'ispackage': False } return modules 




यह कैसे काम करता है? सबसे पहले, कक्षा का एक उदाहरण बनाते समय, हमारे पुस्तकालय के मॉड्यूल के बारे में जानकारी एकत्र की जाती है, जिसे हम "एन्क्रिप्ट" करते हैं। फिर, एक विशिष्ट मॉड्यूल लोड करते समय, वांछित "एन्क्रिप्टेड" फ़ाइल को "डिक्रिप्ट" किया जाता है और "डिक्रिप्ट" पाठ स्ट्रिंग से imp मॉड्यूल के उपकरण का उपयोग करके आयात किया जाता है। इस वर्ग का उपयोग कैसे करें? बहुत आसान है। शाब्दिक रूप से, एक पंक्ति में हमारे पुस्तकालय के "एन्क्रिप्टेड" स्रोतों को आयात करने की क्षमता शामिल है, लेकिन वास्तव में आयात हुक निर्धारित है:

 sys.meta_path.append(Base64Importer(root_pkg_path)) 


जहाँ root_pkg_path हमारी लाइब्रेरी के रूट पैकेज का पूर्ण या सापेक्ष पथ है। इसी समय, इस लाइन को हटाकर, हम सामान्य स्रोत कोड का उपयोग कर सकते हैं, यदि उपलब्ध हो। सब कुछ बिल्कुल पारदर्शी होता है, और सभी परिवर्तन एक ही स्थान पर होते हैं।

बस, इसी क्षण से हमारी लाइब्रेरी से मॉड्यूल का आयात अवरोधन और "डिक्रिप्शन" के साथ किया जाता है। हमारा हुक आयात करने के लिए किसी भी कॉल पर चिकोटी देगा, और अगर हमारे पुस्तकालय के मॉड्यूल आयात किए जाते हैं, तो हुक प्रक्रिया करेगा और उन्हें लोड करेगा, बाकी आयात मानक के रूप में संसाधित किए जाएंगे। जिसे हमें अधिक उन्नत स्थगन की आवश्यकता थी। प्रस्तुत आयातक और हुक इंस्टॉलेशन कोड पहले से ही .pyd फ़ाइल में डाले जा सकते हैं और उम्मीद करते हैं कि कोई भी इसे समझने की आशा में हमें यहां नहीं करेगा। एक वास्तविक परियोजना में, आप एक हार्डवेयर कुंजी का उपयोग करने सहित वास्तविक एन्क्रिप्शन का उपयोग कर सकते हैं, जिससे इस पद्धति की विश्वसनीयता बढ़नी चाहिए। इसके अलावा, फ़ाइल नाम और पैकेज संरचना बदलना अधिक भ्रम के लिए उपयोगी हो सकता है।

निष्कर्ष


अंत में, मैं कहना चाहता हूं कि मैं स्रोत कोड को छिपाने के लिए एक विरोधी हूं, जिसे सिर्फ लिया और छिपाया नहीं जा सकता। इस मामले में, मैंने इस मुद्दे के नैतिक पक्ष और पायथन स्रोतों को छिपाने की आवश्यकता / उपयोगिता पर चर्चा करने का साहस नहीं किया। फिर मैंने बस एक तरीका प्रस्तुत किया कि यह कैसे करना है और किसी प्रकार का परिणाम प्राप्त करना है। स्वाभाविक रूप से, यह रामबाण नहीं है। अजगर कोड वास्तव में पूरी तरह से और सभी से छिपाना असंभव है। मॉड्यूल का कोड हमेशा अंतर्निहित भाषा सुविधाओं के साथ आत्मनिरीक्षण द्वारा प्राप्त किया जा सकता है उन्हें लोड करने के बाद, उदाहरण के लिए, sys.modules चर से। लेकिन यह इतना स्पष्ट नहीं है, जैसे कि स्रोत शुरू में खुले थे।

यह संभव है कि यहां जो कुछ भी लिखा गया है और अंडे इसके लायक नहीं हैं - लंबे समय से ज्ञात सत्य, या पागल का प्रलाप। लेकिन अगर उपरोक्त किसी के लिए उपयोगी हो सकता है, तो मुझे खुशी होगी। मेरे लिए व्यक्तिगत रूप से, यह अनुभव उपयोगी था, यदि केवल इसलिए कि इससे मुझे मॉड्यूल और आयातक प्रोटोकॉल को लोड करने और आयात करने के लिए शक्तिशाली और लचीली प्रणाली को बेहतर ढंग से समझने की अनुमति मिली। उन चीजों के बारे में जो अक्सर पायथन में प्रोग्राम लिखने के लिए डेवलपर्स द्वारा आवश्यक नहीं होती हैं। कुछ नया सीखना हमेशा दिलचस्प होता है।

आपका ध्यान देने के लिए धन्यवाद।

UPD 08/14/2013 :
टेंग्रो के अनुरोध पर , उन्होंने एक न्यूनतम परियोजना बनाई जो वर्णित विधि को प्रदर्शित करती है, लेकिन वास्तविक एन्क्रिप्शन के बिना (केवल कुछ प्रतिवर्ती रूपांतरण एल्गोरिदम लागू किए गए थे)।
आप लिंक से जिप संग्रह डाउनलोड कर सकते हैं। विंडोज पर पायथन 2.7 x86 की आवश्यकता है। आपको "test_main.py" स्क्रिप्ट चलाने की आवश्यकता है।

UPD 2:
और अधिक दिलचस्प उदाहरण, जिसमें कुछ गणना की जाती है। यहां, एन्क्रिप्टेड मॉड्यूल से सभी आयात और फ़ंक्शन कॉल बाइनरी मॉड्यूल में छिपे हुए हैं। आप संग्रह को लिंक से डाउनलोड कर सकते हैं।

Source: https://habr.com/ru/post/In189772/


All Articles