From a57747e72a96e530cd51ba3ba2ce89ed19ff69f0 Mon Sep 17 00:00:00 2001 From: Ivan Maslov Date: Fri, 26 Jun 2020 18:16:44 +0300 Subject: [PATCH] # Add template to safe run (run crypted sources) : SafeSource --- Robot/SettingsRobotExample.py | 73 ------- Utils/SafeSource/DistrRun_x64.cmd | 4 - Utils/SafeSource/pyRobotName_Safe.py | 202 ++++++++++++++++++ Utils/SafeSource/pyRobotName_Safe_x64_Run.cmd | 4 + 4 files changed, 206 insertions(+), 77 deletions(-) delete mode 100644 Robot/SettingsRobotExample.py delete mode 100644 Utils/SafeSource/DistrRun_x64.cmd create mode 100644 Utils/SafeSource/pyRobotName_Safe.py create mode 100644 Utils/SafeSource/pyRobotName_Safe_x64_Run.cmd diff --git a/Robot/SettingsRobotExample.py b/Robot/SettingsRobotExample.py deleted file mode 100644 index 3c49a866..00000000 --- a/Robot/SettingsRobotExample.py +++ /dev/null @@ -1,73 +0,0 @@ -import logging -#Robot settings -from pyOpenRPA.Robot import OrchestratorConnector -import os -import logging -import datetime -#Definitions -lOrchestratorHost="localhost" -lOrchestratorPort=8081 -lOrchestratorProtocol="http" -lOrchestratorAuthToken="1992-04-03-0643-ru-b4ff-openrpa52zzz" -#Robot settings -def Settings(): - import os - mDict = { - "ProcessBitness": { - "Python32FullPath": None, #Set from user: "..\\Resources\\WPy32-3720\\python-3.7.2\\OpenRPARobotGUIx32.exe" - "Python64FullPath": None, #Set from user - "Python32ProcessName": "OpenRPAUIDesktopX32.exe", #Config set once - "Python64ProcessName": "OpenRPAUIDesktopX64.exe" #Config set once - }, - "Logger": logging.getLogger("Robot"), - "OrchestratorToRobotStorage": { - - }, - "OrchestratorToRobotResetStorage": { - "SafeTurnOff":False #Control from orchestrator to safety turn off robot - }, - "OrchestratorConnector": { - #Fill below - }, - "OrchestratorConnectorTerminateAll":OrchestratorConnector.IntervalTermimateAll #Call this function when program must be shutted down (to kill threads from OrchestratorConnector) - } - ###################### - #OrchestratorConnector - ###################### - mDict["OrchestratorConnector"]={ - "IntervalDataSendResetAsync": [ - { - "Interval": 2, - "RobotStorage": mGlobal["Storage"], - "RobotStorageKey": "R01_OrchestratorToRobot", - "RobotResetValue": {"Test": "Test"}, - "OrchestratorKeyList": ["Storage", "R01_OrchestratorToRobot"], - "OrchestratorProtocol": lOrchestratorProtocol, - "OrchestratorHost": lOrchestratorHost, - "OrchestratorPort": lOrchestratorPort, - "OrchestratorAuthToken": lOrchestratorAuthToken - } - ] - } - #Turn off many warnings from orchestrator connector logger - OrchestratorConnector.LoggerSetLevelError() - #Run OrchestratorConnector initialization - OrchestratorConnector.ConfigurationInit(mDict["OrchestratorConnector"]) - ######################### - #Создать файл логирования - # add filemode="w" to overwrite - if not os.path.exists("Reports"): - os.makedirs("Reports") - ########################## - #Подготовка логгера Robot - ######################### - mRobotLogger=mDict["Logger"] - mRobotLogger.setLevel(logging.INFO) - # create the logging file handler - mRobotLoggerFH = logging.FileHandler("Reports\ReportRobot_"+datetime.datetime.now().strftime("%Y_%m_%d")+".log") - mRobotLoggerFormatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') - mRobotLoggerFH.setFormatter(mRobotLoggerFormatter) - # add handler to logger object - mRobotLogger.addHandler(mRobotLoggerFH) - ############################################ - return mDict \ No newline at end of file diff --git a/Utils/SafeSource/DistrRun_x64.cmd b/Utils/SafeSource/DistrRun_x64.cmd deleted file mode 100644 index 0fc026df..00000000 --- a/Utils/SafeSource/DistrRun_x64.cmd +++ /dev/null @@ -1,4 +0,0 @@ -cd %~dp0..\..\Sources -copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\OpenRPA_SafeSource.exe -..\Resources\WPy64-3720\python-3.7.2.amd64\OpenRPA_SafeSource.exe -m pyOpenRPA.Tools.SafeSource "Run" -pause >nul \ No newline at end of file diff --git a/Utils/SafeSource/pyRobotName_Safe.py b/Utils/SafeSource/pyRobotName_Safe.py new file mode 100644 index 00000000..eec2fe32 --- /dev/null +++ b/Utils/SafeSource/pyRobotName_Safe.py @@ -0,0 +1,202 @@ +#coding=utf-8 + +import os +import sys +import imp +import base64 +from pyOpenRPA.Tools.SafeSource import Crypter # Crypto functions +import datetime #Datetime +import hashlib #Get hash of the word +import pyautogui +import os +import glob # list files +EXT = '.cry' +gExtensionName = "cry" + +# How to run +# sys.meta_path.append(Base64Importer(root_pkg_path)) + +# Init cryptographer +def CryptographerInit(inFolderPathList, inKey): + #sys.meta_path.append(Base64Importer(inFolderPath)) + # Flag add subfolder, which contains extension files + #path = 'c:\\projects\\hc2\\' + #folders = [] + # Recursive walk throught the tree + # r=root, d=directories, f = files + #import pdb + #pdb.set_trace() + for lItem in inFolderPathList: + sys.meta_path.append(Base64Importer(os.path.abspath(lItem), inKey=inKey)) + #for r, d, f in os.walk(lItem): + # for folder in d: + #folders.append(os.path.join(r, folder)) + #sys.meta_path.append(Base64Importer(os.path.join(r, folder), inKey=inKey)) + # pass + #for f in folders: + # print(f) +#=============================================================================== +class Base64Importer(object): + """Служит для поиска и импорта python-модулей, кодированных в base64 + Класс реализует Import Protocol (PEP 302) для возможности импортирования + модулей, зашифрованных в base64 из указанного пакета. + """ + #--------------------------------------------------------------------------- + def __init__(self, root_package_path, inKey): + self.mKeyStr = inKey + self.__modules_info = self.__collect_modules_info(root_package_path) + # Create list of cry files when run + #for lItem in root_package_path_list: + # lCryptedFileList = [f for f in glob.glob(os.path.join(lItem,f"**/*.{EXT}"), recursive=True)] + #--------------------------------------------------------------------------- + def find_module(self, fullname, path=None): + """Метод будет вызван при импорте модулей + Если модуль с именем fullname является base64 и находится в заданной + папке, данный метод вернёт экземпляр импортёра (finder), либо None, если + модуль не является base64. + """ + #print(f"find_module:: Fullname: {fullname}, path: {path}") + #print(f"modules info: {self.__modules_info}") + if fullname in self.__modules_info: + return self + return None + #--------------------------------------------------------------------------- + def load_module(self, fullname): + """Метод загружает base64 модуль + Если модуль с именем fullname является base64, то метод попытается его + загрузить. Возбуждает исключение ImportError в случае любой ошибки. + """ + #print(f"load_module:: Fullname: {fullname}") + 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.__file__ = self.__modules_info[fullname]['filename'] #Hotfix + #print(f"MODFILE::{mod.__file__}") + 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: + #__name__ = "pyRobotName" + #print(f"MODNAME:: {mod.__name__}") + #exec(src) in mod.__dict__ + exec(src,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: + src = Crypter.decrypt_file_bytes(key = self.mKeyStr, in_filename = filename).decode("utf-8") + #with open(filename, 'r') as ifile: + # src = base64.decodestring(ifile.read()) + except IOError: + src = '' + + return src + # for __main__ + def get_code(self, inModName): + return self.get_source(inModName) + #--------------------------------------------------------------------------- + def __collect_modules_info(self, root_package_path): + """Собирает информацию о модулях из указанного пакета + """ + modules = {} + """ + p = os.path.abspath(root_package_path) + dir_name = os.path.dirname(p) + os.sep + #dir_name = "" # Hotfix 2020 03 19 + #print(f"__collect_modules_info:: root_package_path: {root_package_path}") + 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]]) + fullname = os.path.splitext(f)[0] + modules[fullname] = { + 'filename': filename, + 'ispackage': False + } + """ + # # # # # # # # # # # + # New way of collection + lRootAbsPath = os.path.abspath(root_package_path) + #print(lRootAbsPath) + lNewPathIndex = len(lRootAbsPath)+len(os.sep) # Len of the root path + len sep + lCryptedFileList = [f for f in glob.glob(os.path.join(lRootAbsPath,f"**/*.{gExtensionName}"), recursive=True)] + #print(lCryptedFileList) + for lCryptedItemFullPath in lCryptedFileList: + # Get Module name + lModuleName = lCryptedItemFullPath[lNewPathIndex:-(1+len(gExtensionName))].replace(os.sep, '.') + # Check if file is not __init__.{EXT} - This is package + if f"__init__.{EXT}" in lCryptedItemFullPath: + # Add package + lModuleName = lModuleName.replace(f"{os.sep}__init__.{gExtensionName}","") + modules[lModuleName] = { + 'filename': lCryptedItemFullPath, + 'ispackage': True + } + else: + # Add item + modules[lModuleName] = { + 'filename': lCryptedItemFullPath, + 'ispackage': False + } + return modules +# Settings +gInUncryptedExtension = "py" # cry for filename.cry +gOutCryptedExtension = "cry" # cry for filename.cry +gFileMaskToDelete = "pyc" #Remove all .pyc files +print(f"{str(datetime.datetime.now())}: Run decryptography") +############# Step 5 - Ask and confirm the secret word +lKeyHashStr_1 = hashlib.sha256(pyautogui.password('Please enter the key to protect source code').encode("utf-8")).digest() +lKeyHashStr_2 = hashlib.sha256(pyautogui.password('Please repeat the key to protect source code').encode("utf-8")).digest() +if lKeyHashStr_1 == lKeyHashStr_2: + #sys.meta_path.append(Base64Importer("TestPackage",inKey = lKeyHashStr_1)) + CryptographerInit(sys.argv[1:], inKey = lKeyHashStr_1) + print(f"{str(datetime.datetime.now())}: Cryprography module has been successfully initialized") + if __name__ == "__main__": + import pyRobotName_Settings + pyRobotName_Settings.pyRobotName_Settings() +else: + raise Exception("User set different secret key 1 and key 2") +############ Step 6 - Final stage +#Init the functions +#import runpy +#runpy.run_module(**gSettings["run_module"]) +#runpy.run_path("pyRobotName_Settings", init_globals=None, run_name=None) +#src = Crypter.decrypt_file_bytes(key = lKeyHashStr_1, in_filename = "pyRobotName_Settings.cry").decode("utf-8") +#exec(src) in mod.__dict__ +#import pyRobotName_Settings +#pyRobotName_Settings. diff --git a/Utils/SafeSource/pyRobotName_Safe_x64_Run.cmd b/Utils/SafeSource/pyRobotName_Safe_x64_Run.cmd new file mode 100644 index 00000000..f43146f7 --- /dev/null +++ b/Utils/SafeSource/pyRobotName_Safe_x64_Run.cmd @@ -0,0 +1,4 @@ +cd %~dp0 +copy /Y ..\..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe ..\..\Resources\WPy64-3720\python-3.7.2.amd64\pyRobotName.exe +..\..\Resources\WPy64-3720\python-3.7.2.amd64\pyRobotName.exe "pyRobotName_Safe.py" "" "..\..\Sources" +pause >nul \ No newline at end of file