"""Static Analysis Setup Windows. Setup script for the Windows vm for usage with MobSF for static analysis of Windows apps. """ import configparser import logging import os import platform import re import shutil import subprocess import sys from os.path import expanduser from six.moves import input as sinput try: import urllib.request as urlrequest except ImportError: import urllib as urlrequest logger = logging.getLogger(__name__) # pylint: disable=C0325,W0603 # Only static URL, let's hope this never changes.. CONFIG_URL = ( 'https://raw.githubusercontent.com/MobSF/' 'Mobile-Security-Framework-MobSF/master/' 'mobsf/install/windows/config.txt' ) # Static path to config file as a starting point CONFIG_PATH = expanduser('~') + '\\MobSF\\Config\\' CONFIG_FILE = 'config.txt' # Static path to autostart AUTOSTART = ( expanduser('~') + '\\AppData\\Roaming\\Microsoft\\' 'Windows\\Start Menu\\Programs\\Startup\\' ) # Global var so we don't have to pass it every time.. CONFIG = '' def windows_config_local(path): """Windows Configuration.""" # Configure here if you are on windows # Path to lock-file (so setup is only run once) path_to_lock_file = os.path.join(path, 'setup_done.txt') if (os.path.isfile(path_to_lock_file) is False and platform.system() == 'Windows' and 'CI' not in os.environ): logger.info('Running first time setup for windows.') # Setup is to-be-executed install_locally(path) def download_config(): """Download initial config file.""" # Create config path if not os.path.exists(CONFIG_PATH): os.makedirs(CONFIG_PATH) if os.path.exists(CONFIG_PATH + CONFIG_FILE): os.remove(CONFIG_PATH + CONFIG_FILE) # TODO(Give user time to modify config, but mayber after rewrite?) # Open File conf_file_local = open(CONFIG_PATH + CONFIG_FILE, 'wb') # Downloading File print('[*] Downloading config file..') conf_file = urlrequest.urlopen(CONFIG_URL) # pylint: disable-msg=E1101 # Save content print(('[*] Saving to File {}'.format(CONFIG_FILE))) # Write content to file conf_file_local.write(bytes(conf_file.read())) # Aaaand close conf_file_local.close() def read_config(): """Read the config file and write it to the global var.""" print('[*] Reading config file..') global CONFIG CONFIG = configparser.ConfigParser() CONFIG.read(CONFIG_PATH + CONFIG_FILE) def create_folders(): """Create MobSF dirs.""" print('[*] Creating other folders...') if not os.path.exists(CONFIG['MobSF']['downloads']): os.makedirs(CONFIG['MobSF']['downloads']) if not os.path.exists(CONFIG['MobSF']['tools']): os.makedirs(CONFIG['MobSF']['tools']) if not os.path.exists(CONFIG['MobSF']['samples']): os.makedirs(CONFIG['MobSF']['samples']) if not os.path.exists(CONFIG['MobSF']['key_path']): os.makedirs(CONFIG['MobSF']['key_path']) def check_dependencies(): """Check dependencies and install if necessary.""" print('[*] Checking dependencies...') missing_deps = [] try: import rsa # noqa F401 print('[+] rsa is installed.') except ImportError: # pylint: disable-msg=C0103 print('[!] rsa not installed!') missing_deps.append('rsa') if len(missing_deps) > 0: print('[!] Please install these missing dependencies:') print(missing_deps) sys.exit() else: print('[+] Everything good.') def tools_nuget(): """Download nuget.""" # Get config params nuget_url = CONFIG['nuget']['url'] mobsf_subdir_tools = CONFIG['MobSF']['tools'] nuget_file_path = CONFIG['nuget']['file'] # Open File nuget_file_local = open(os.path.join( mobsf_subdir_tools, nuget_file_path), 'wb') # Downloading File print('[*] Downloading nuget..') nuget_file = urlrequest.urlopen(nuget_url) # pylint: disable-msg=E1101 # Save content print(('[*] Saving to File {}'.format(nuget_file_path))) # Write content to file nuget_file_local.write(bytes(nuget_file.read())) # Aaaand close nuget_file_local.close() def tools_binskim(): """Download and extract binskim.""" # Get dirs, urls etc. binskim_nuget = CONFIG['binskim']['nuget'] mobsf_subdir_tools = CONFIG['MobSF']['tools'] nuget = mobsf_subdir_tools + CONFIG['nuget']['file'] print('[*] Downloading and installing Binskim...') # Execute nuget to get binkim output = subprocess.check_output( [ nuget, 'install', binskim_nuget, '-Pre', '-Version', '1.7.2', '-o', mobsf_subdir_tools]) # Some code to determine the version on the fly so we don't have to fix the # config file on every new release of binskim.. # Search for the version number folder = re.search( b'Microsoft\\.CodeAnalysis\\.BinSkim\\..{0,300}(\'|\") ', output) try: # Substring-Foo for removing b'X's in python3 if sys.version_info.major == 3: folder = str(folder.group(0)[:-2])[2:-1] else: folder = folder.group(0)[:-2] except AttributeError: print('[!] Unable to parse folder from binskim nuget installation.') sys.exit() # Search for the exes binaries = _find_exe(mobsf_subdir_tools + folder, []) if len(binaries) != 2: print('[!] Found more than 2 exes for binskim, panic!') sys.exit() # Determinde which one is for which arch if 'x86' in binaries[0]: CONFIG['binskim']['file_x86'] = binaries[0] CONFIG['binskim']['file_x64'] = binaries[1] else: CONFIG['binskim']['file_x86'] = binaries[1] CONFIG['binskim']['file_x64'] = binaries[0] # Write to config with open(os.path.join(CONFIG_PATH, CONFIG_FILE), 'w') as configfile: CONFIG.write(configfile) # pylint: disable-msg=E1101 def _find_exe(path, exe_list): """Return a list of all exes in path, recursive.""" for filename in os.listdir(path): if os.path.isfile(os.path.join(path, filename)): if '.exe' in filename: exe_list.append(path + '\\' + filename) else: exe_list = _find_exe(path + '\\' + filename, exe_list) return exe_list def tools_rpcclient(): """Download and install rpc-server for MobSF.""" rpc_url = CONFIG['rpc']['url'] mobsf_subdir_tools = CONFIG['MobSF']['tools'] rpc_file_path = CONFIG['rpc']['file'] # Open File rpc_local_file = open(mobsf_subdir_tools + rpc_file_path, 'wb') # Downloading File print('[*] Downloading rpc_server..') rpc_file = urlrequest.urlopen(rpc_url) # Save content print(('[*] Saving to File {}'.format(rpc_file_path))) # Write content to file rpc_local_file.write(bytes(rpc_file.read())) # Aaaand close rpc_local_file.close() def tools_binscope(): """Download and install Binscope for MobSF.""" mobsf_subdir_tools = CONFIG['MobSF']['tools'] binscope_path = mobsf_subdir_tools + 'BinScope' # Download the right version for os if platform.machine().endswith('64'): binscope_url = CONFIG['binscope']['url_x64'] binscope_installer_path = binscope_path + '\\BinScope_x64.msi' else: binscope_url = CONFIG['binscope']['url_x86'] binscope_installer_path = binscope_path + '\\BinScope_x86.msi' if not os.path.exists(binscope_path): os.makedirs(binscope_path) binscope_installer_file = open(binscope_installer_path, 'wb') # Downloading File print('[*] Downloading BinScope..') binscope_installer = urlrequest.urlopen(binscope_url) # Save content print(('[*] Saving to File {}'.format(binscope_installer_path))) # Write content to file binscope_installer_file.write(bytes(binscope_installer.read())) # Aaaand close binscope_installer_file.close() # Execute the installer print(('[*] Installing BinScope to {}'.format(binscope_path))) cmd = ('msiexec INSTALLLOCATION=' '"{}" /i "{}" /passive'.format( binscope_path, binscope_installer_path)) os.system(cmd) CONFIG['binscope']['file'] = binscope_path + '\\Binscope.exe' # Write to config with open(os.path.join(CONFIG_PATH, CONFIG_FILE), 'w') as configfile: CONFIG.write(configfile) # pylint: disable-msg=E1101 def generate_secret(): """Generate rsa keys for authentication.""" import rsa print('[*] Generating secret, please hang on.') # Generate keys, taken from # https://stuvel.eu/python-rsa-doc/usage.html#generating-keys (pubkey, privkey) = rsa.newkeys(2048) # Save private and pub key priv_key_file = open(CONFIG['MobSF']['priv_key'], 'w') priv_key_file.write(privkey.save_pkcs1().decode('utf-8')) priv_key_file.close() pub_key_file = open(CONFIG['MobSF']['pub_key'], 'w') pub_key_file.write(pubkey.save_pkcs1().decode('utf-8')) pub_key_file.close() config_path = os.path.join( expanduser('~'), '.MobSF', 'config.py') print(( '[!] Please move the private key file\n' '\t{}\n' '\tto MobSF to the path specified in {}\n' '\t(default: Mobile-Security-Framework-MobSF/' 'mobsf/MobSF/windows_vm_priv_key.asc)' .format(CONFIG['MobSF']['priv_key'], config_path) )) sinput('Please press any key when done..') def autostart(): """Create the autostart binary and run it.""" mobsf_subdir_tools = CONFIG['MobSF']['tools'] rpc_file = CONFIG['rpc']['file'] autostart_file = CONFIG['autostart']['file'] batch_file = AUTOSTART + autostart_file print('[*] Creating autostart binary...') # Open file autostart_file = open(batch_file, 'wb') # Define bat-text text = """ @echo off python "{}" %* pause""".format(mobsf_subdir_tools + rpc_file) autostart_file.write(bytes(text, 'utf8')) # Close handle autostart_file.close() print('[*] Done. Start the server.') # Execute. Beware the ' ' because of windows strange paths.. os.system('"{}"'.format(batch_file)) def _place_lockfile(mobsf_home): path = os.path.join(mobsf_home, 'setup_done.txt') open(path, 'a').close() def local_config(): """Move local config and save paths.""" # Set the CONFIG_PATH # Create path if it doesn't exist yet if not os.path.exists(CONFIG_PATH): os.makedirs(CONFIG_PATH) # Copy predefined config to MobSF folder shutil.copy( os.getcwd() + '\\mobsf\\install\\windows\\config.txt', os.path.join(CONFIG_PATH, CONFIG_FILE), ) def rewrite_local_config(mobsf_home): """For local installation some config-vars need to be rewritten.""" CONFIG['MobSF']['subdir_tools'] = ( mobsf_home + '\\mobsf\\StaticAnalyzer\\tools\\windows\\') CONFIG['MobSF']['dir'] = mobsf_home # Write to config with open(os.path.join(CONFIG_PATH, CONFIG_FILE), 'w') as configfile: CONFIG.write(configfile) # pylint: disable-msg=E1101 def rewrite_config(): """Rewrite the config to take the profile path as the base path.""" # Take user path as base path CONFIG['MobSF']['dir'] = expanduser('~') + CONFIG['MobSF']['dir'] # Rewrite config with new base path CONFIG['MobSF']['downloads'] = CONFIG['MobSF'][ 'dir'] + CONFIG['MobSF']['downloads'] CONFIG['MobSF']['tools'] = CONFIG['MobSF'][ 'dir'] + CONFIG['MobSF']['tools'] CONFIG['MobSF']['samples'] = CONFIG['MobSF'][ 'dir'] + CONFIG['MobSF']['samples'] CONFIG['MobSF']['key_path'] = CONFIG['MobSF'][ 'dir'] + CONFIG['MobSF']['key_path'] CONFIG['MobSF']['priv_key'] = CONFIG['MobSF'][ 'key_path'] + CONFIG['MobSF']['priv_key'] CONFIG['MobSF']['pub_key'] = CONFIG['MobSF'][ 'key_path'] + CONFIG['MobSF']['pub_key'] # Write to config with open(os.path.join(CONFIG_PATH, CONFIG_FILE), 'w') as configfile: CONFIG.write(configfile) # pylint: disable-msg=E1101 def install_locally(mobsf_home): """Install the MobSF-Utils on the same system as MobSF.""" local_config() read_config() rewrite_config() create_folders() tools_nuget() tools_binskim() tools_binscope() _place_lockfile(mobsf_home) def _install_remote(): """Install the MobSF-Utils on a Windows-VM for static analysis.""" download_config() read_config() rewrite_config() create_folders() check_dependencies() tools_nuget() tools_binskim() tools_binscope() tools_rpcclient() generate_secret() autostart() if __name__ == '__main__': # Gets directly run if setup.py is run on a remote machine _install_remote()