def _initialize_disassembler(self): """ Initialize the IDA Pro disassembler. Determine which version of IDA to use based on the project's architecture (32 or 64 bit). Sets the ``_ida_path`` attribute or raises an exception if IDA Pro cannot be found. """ ida_dir = self.config.get('ida', {}).get('dir') if not ida_dir: raise CommandError('No path to IDA has been given in s2e.yaml. ' 'Please add the following to your s2e.yaml ' 'config to use this disassembler backend:\n\n' 'ida:\n' '\tdir: /path/to/ida') project_arch = self._project_desc['target_arch'] if project_arch == 'i386': ida_path = os.path.join(ida_dir, 'idal') elif project_arch == 'x86_64': ida_path = os.path.join(ida_dir, 'idal64') else: raise CommandError('Invalid project architecture \'%s\' - unable ' 'to determine the version of IDA Pro to use' % project_arch) if not os.path.isfile(ida_path): raise CommandError('IDA Pro not found at %s' % ida_path) else: self._ida_path = ida_path
def _handle_inf(target_path, **options): logger.info( 'Detected Windows INF file, attempting to create a driver project...') driver = Driver(target_path) driver.analyze() driver_files = driver.get_files() if not driver_files: raise CommandError('Driver has no files') base_dir = os.path.dirname(target_path) logger.info(' Driver files:') file_paths = [] for f in driver_files: full_path = os.path.join(base_dir, f) if not os.path.exists(full_path): if full_path.endswith('.cat'): logger.warn('Catalog file %s is missing', full_path) continue else: raise CommandError('%s does not exist' % full_path) logger.info(' %s', full_path) file_paths.append(full_path) _gen_win_driver_project(target_path, file_paths, **options)
def handle(self, *args, **options): # Exit if the makefile doesn't exist makefile = self.env_path('source', 's2e', 'Makefile') if not os.path.isfile(makefile): raise CommandError('No makefile found in %s' % os.path.dirname(makefile)) # If the build directory doesn't exist, create it build_dir = self.env_path('build', 's2e') if not os.path.isdir(build_dir): os.mkdir(build_dir) try: # Set up some environment variables env_vars = os.environ.copy() env_vars['S2EPREFIX'] = self.install_path() # Run make make = sh.Command('make').bake(directory=build_dir, file=makefile, _out=sys.stdout, _err=sys.stderr, _env=env_vars, _fg=True) if options['debug']: logger.info('Building S2E (debug) in %s', build_dir) make('all-debug') else: logger.info('Building S2E (release) in %s', build_dir) make('install') except ErrorReturnCode as e: raise CommandError(e) return 'S2E built'
def _get_ida_path(ida_dir, arch): """ Returns the path to the required IDA Pro executable. IDA Pro 7 renamed ``idal`` (on Linux) to ``idat`` on (on all platforms). See https://www.hex-rays.com/products/ida/7.0/docs/api70_porting_guide.shtml for details. If we are analysing a 64-bit binary, then we must also use the 64-bit version of IDA Pro. Args: ida_dir: The path to the IDA Pro directory (as specified in the S2E environment's config file). arch: The target binary's architecture (as specified in the project description file). Returns: The path to the IDA Pro executable to use. """ for ida_bin in ('idat', 'idal'): ida_path = os.path.join(ida_dir, ida_bin) if arch == 'x86_64': ida_path = f'{ida_path}64' elif arch != 'i386': raise CommandError(f'Invalid project architecture {arch} - unable to ' 'determine the IDA Pro version') if os.path.isfile(ida_path): return ida_path raise CommandError(f'IDA Pro not found at {ida_dir}')
def _handle_empty_project(**options): if not options['no_target']: raise CommandError( 'No target binary specified. Use the --no-target option to create an empty project.' ) if not options['image']: raise CommandError( 'An empty project requires a VM image. Use the -i option to specify the image.' ) if not options['name']: raise CommandError( 'Project name missing. Use the -n option to specify one.') if options['type'] not in _get_configs(): raise CommandError( 'The project type is invalid. Please use %s for the --type option.' % _get_configs()) options['target'] = None options['target_files'] = [] options['target_arch'] = None # The module list is a list of tuples where the first element is # the module name and the second element is True if the module is # a kernel module options['modules'] = [] options['processes'] = [] call_command(Project(PROJECT_CONFIGS[options['type']]), **options)
def _check_iso(templates, app_templates, iso_dir, image_names): for image_name in image_names: base_image, app_name = _get_base_image_and_app(image_name) descriptors = [templates[base_image]] if app_name: descriptors.append(app_templates[app_name]) for desc in descriptors: iso = desc.get('iso', {}) if iso.get('url', ''): continue name = iso.get('name', '') if not name: continue if not iso_dir: raise CommandError( 'Please use the --iso-dir option to specify the path ' f'to a folder that contains {name}' ) path = os.path.join(iso_dir, name) if not os.path.exists(path): raise CommandError(f'The image {image_name} requires {path}, which could not be found')
def _gen_win_driver_project(target_path, file_paths, **options): first_sys_file = None for f in file_paths: if f.endswith('.sys'): first_sys_file = f # TODO: prompt the user to select the right driver if not first_sys_file: raise CommandError( 'Could not find any *.sys file in the INF file. ' 'Make sure the INF file is valid and belongs to a Windows driver.') # Pick the architecture of the first sys file arch, _ = get_arch(first_sys_file) if arch is None: raise CommandError('Could not determine architecture for %s' % first_sys_file) options['target'] = target_path options['target_arch'] = arch # All the files to download into the guest. options['target_files'] = list(set([target_path] + file_paths)) # TODO: support multiple kernel drivers options['modules'] = [(os.path.basename(first_sys_file), True)] options['processes'] = [] call_command(Project(WindowsDriverProjectConfiguration), **options)
def handle(self, *args, **options): # Initialize the backend disassembler self._initialize_disassembler() # Get the basic block information bbs = self._get_basic_blocks() if not bbs: raise CommandError('No basic block information found') # Get translation block coverage information target_path = self._project_desc['target_path'] tbs = self._get_tb_coverage(os.path.basename(target_path)) if not tbs: raise CommandError( 'No translation block coverage information found') # Calculate the basic block coverage information bb_coverage = _basic_block_coverage(bbs, tbs) # Calculate some statistics total_bbs = len(bbs) covered_bbs = len(bb_coverage) # Write the basic block information to a JSON file bb_coverage_file = self._save_basic_block_coverage( bb_coverage, total_bbs) return self.RESULTS.format(bb_file=bb_coverage_file, num_bbs=total_bbs, num_covered_bbs=covered_bbs, percent=covered_bbs / total_bbs)
def handle(self, *args, **options): # Exit if the makefile doesn't exist makefile = self.env_path('source', 'Makefile') if not os.path.isfile(makefile): raise CommandError(f'No makefile found in {os.path.dirname(makefile)}') # If the build directory doesn't exist, create it build_dir = self.env_path('build') if not os.path.isdir(build_dir): os.mkdir(build_dir) # Set up some environment variables env_vars = os.environ.copy() env_vars['S2E_PREFIX'] = self.install_path() components = options['components'] self._make = sh.Command('make').bake(directory=build_dir, file=makefile, _env=env_vars) # If the user has specified any components to rebuild, do this before # the build if components: self._rebuild_components(components) try: # Run make if options['debug']: logger.info('Building S2E (debug) in %s', build_dir) self._make('all-debug', _out=sys.stdout, _err=sys.stderr) else: logger.info('Building S2E (release) in %s', build_dir) self._make('install', _out=sys.stdout, _err=sys.stderr) except ErrorReturnCode as e: raise CommandError(e) from e logger.success('S2E built')
def _handle_empty_project(proj_class, *args, **options): if not options['no_target']: raise CommandError('No target binary specified. Use the -m option to ' 'create an empty project') if not options['image']: raise CommandError('An empty project requires a VM image. Use the -i ' 'option to specify the image') if not options['name']: raise CommandError('An empty project requires a name. Use the -n ' 'option to specify one') # If the project class wasn't explicitly overridden programmatically, get # one of the default project classes from the command line if not proj_class: project_types = PROJECT_TYPES.keys() if options['type'] not in project_types: raise CommandError('An empty project requires a type. Use the -t ' 'option and specify one from %s' % project_types) proj_class = PROJECT_TYPES[options['type']] target = Target.empty(proj_class) options['target'] = target return call_command(target.initialize_project(), *args, **options)
def _update_s2e_sources(self): """ Update all of the S2E repositories with repo. """ repo = sh.Command(self.install_path('bin', 'repo')) # cd into the S2E source directory orig_dir = os.getcwd() repo_dir = self.source_path('.repo') if not os.path.exists(repo_dir): raise CommandError( f'{repo_dir} does not exist. Your environment is not supported by this version of s2e-env.\n' 'Please create a new environment.' ) os.chdir(self.source_path()) try: logger.info('Updating S2E') repo.sync(_out=sys.stdout, _err=sys.stderr) except ErrorReturnCode as e: raise CommandError(e) from e finally: # Change back to the original directory os.chdir(orig_dir) # Success! logger.info('Updated S2E')
def _handle_with_file(**options): # Need an absolute path for the target in order to simplify # symlink creation. target_path = options['target'] target_path = os.path.realpath(target_path) # Check that the target actually exists if not os.path.isfile(target_path): raise CommandError('Target %s does not exist' % target_path) arch, proj_config_class = get_arch(target_path) if arch: options['target'] = target_path options['target_files'] = [target_path] options['target_arch'] = arch # The module list is a list of tuples where the first element is # the module name and the second element is True if the module is # a kernel module options['modules'] = [(os.path.basename(target_path), False)] options['processes'] = [] if not isinstance(proj_config_class, WindowsDLLProjectConfiguration): options['processes'].append(os.path.basename(target_path)) call_command(Project(proj_config_class), **options) elif target_path.endswith('.inf'): _handle_inf(target_path, **options) else: raise CommandError('%s is not a valid target for S2E analysis' % target_path)
def _initialize_disassembler(self): """ Initialize the IDA Pro disassembler. Determine which version of IDA to use based on the project's architecture (32 or 64 bit). Sets the ``_ida_path`` attribute or raises an exception if IDA Pro cannot be found. """ ida_dir = self.config['ida']['dir'] if not ida_dir: raise CommandError('No path to IDA has been given in s2e.yaml. ' 'IDA is required to generate a basic block ' 'coverage report') project_arch = self._project_desc['target_arch'] if project_arch == 'i386': ida_path = os.path.join(ida_dir, 'idal') elif project_arch == 'x86_64': ida_path = os.path.join(ida_dir, 'idal64') else: raise CommandError('Invalid project architecture \'%s\' - unable ' 'to determine the version of IDA Pro to use' % project_arch) if not os.path.isfile(ida_path): raise CommandError('IDA Pro not found at %s' % ida_path) else: self._ida_path = ida_path
def handle(self, *args, **options): # Initialize the backend disassembler self._initialize_disassembler() target_path = self._project_desc['target_path'] target_dir = os.path.dirname(target_path) modules = self._project_desc['modules'] # Get translation block coverage information for each module for module, _ in modules: module_path = os.path.join(target_dir, module) # Check if a cached version of the disassembly information exists. # If it does, then we don't have to disassemble the binary (which # may take a long time for large binaries) disas_info = self._get_cached_disassembly_info(module) # If no cached .disas file exists, generate a new one using the # given disassembler and cache the results if not disas_info: disas_info = self._get_disassembly_info(module_path) if not disas_info: raise CommandError('No disassembly information found') self._save_disassembly_info(module, disas_info) # Calculate basic block coverage information (based on the # translation block coverage recorded by S2E) bbs = disas_info.get('bbs', []) bb_coverage = self._get_basic_block_coverage(module, bbs) if not bb_coverage: raise CommandError('No basic block coverage information found') # Calculate some statistics (across all states) total_bbs = len(bbs) num_covered_bbs = len(set(itertools.chain(*bb_coverage.values()))) # Write the basic block coverage information to disk. # # If we are using drcov format, each state's basic block coverage # is written to a separate drcov file. # # Otherwise combine all the basic block coverage information # (across all states) into a single JSON file. if options['drcov']: bb_coverage_loc = self._save_drcov(module_path, disas_info['base_addr'], disas_info['end_addr'], bb_coverage) else: bb_coverage_loc = self._save_basic_block_coverage(module, bb_coverage, total_bbs, num_covered_bbs) logger.success(self.RESULTS.format(bb_loc=bb_coverage_loc, num_bbs=total_bbs, num_covered_bbs=num_covered_bbs, percent=num_covered_bbs / total_bbs))
def handle(self, *args, **options): env_path = os.path.realpath(options['dir']) # First check if we are already in the environment directory if os.path.realpath(env_path) == os.path.realpath(os.getcwd()): raise CommandError('You cannot create an S2E environment in the ' 'current working directory. Please `cd ..`') # Then check if something already exists at the environment directory if os.path.isdir(env_path) and not os.listdir(env_path) == []: if options['force']: logger.info('%s already exists - removing', env_path) shutil.rmtree(env_path) else: raise CommandError('Something already exists at \'%s\'. ' 'Please select a different location or use ' 'the ``force`` option to erase this ' 'environment.\n\nDid you mean to rebuild or ' 'update your existing environment? Try ' '``s2e build`` or ``s2e update`` instead' % env_path) try: # Create environment if it doesn't exist logger.info('Creating environment in %s', env_path) if not os.path.isdir(env_path): os.mkdir(env_path) # Create the directories within the environment for dir_ in CONSTANTS['dirs']: os.mkdir(os.path.join(env_path, dir_)) # Create the YAML config for the environment _create_config(env_path) prefix = options['use_existing_install'] if prefix is not None: _install_binary_dist(env_path, prefix) return 'Environment created in %s.' % env_path else: # Install S2E's dependencies via apt-get if not options['skip_dependencies']: _install_dependencies() # Get the source repositories _get_s2e_sources(env_path) _get_img_sources(env_path) return ('Environment created in %s. You may wish to modify ' 'your environment\'s s2e.yaml config file. Run ``s2e ' 'build`` to build S2E' % env_path) except: # Cleanup on failure if os.path.isdir(env_path): shutil.rmtree(env_path) raise
def handle(self, *args, **options): # Check the archive archive = options['archive'][0] if not os.path.isfile(archive): raise CommandError('%s is not a valid project archive' % archive) # Get the name of the project that we are importing project_name = options.get('project_name') if not project_name: project_name = _get_project_name(archive) logger.info('Importing project \'%s\' from %s', project_name, archive) # Check if a project with that name already exists project_path = self.projects_path(project_name) if os.path.isdir(project_path): if options['force']: logger.info('\'%s\' already exists - removing', project_name) shutil.rmtree(self.projects_path(project_name)) else: raise CommandError('\'%s\' already exists. Either remove this ' 'project or use the force option' % project_name) _decompress_archive(archive, project_path) # Rewrite all of the exported files to fix their S2E environment paths logger.info('Rewriting project files') rewrite_files(project_path, CONSTANTS['import_export']['project_files'], S2E_ENV_PLACEHOLDER, self.env_path()) with open(os.path.join(project_path, 'project.json'), 'r') as f: proj_desc = json.load(f) if 'image' not in proj_desc: logger.error('No image description found in project.json. Unable ' 'to determine the guest tools to symlink') return override_image = options.get('image', None) if override_image: dn = os.path.dirname(proj_desc['image']) old_image = os.path.basename(proj_desc['image']) proj_desc['image'] = os.path.join(dn, override_image) rewrite_files(project_path, CONSTANTS['import_export']['project_files'], old_image, override_image) image = get_image_descriptor(proj_desc['image']) # Create a symlink to the guest tools directory self._symlink_guest_tools(project_path, image) # Create a symlink to guestfs (if it exists) if proj_desc.get('has_guestfs'): self._symlink_guestfs(project_path, image) logger.success('Project successfully imported from %s to %s', archive, project_path)
def _get_basic_blocks(self): """ Extract basic block information from the target binary using S2E's IDA Pro script. This extraction is done within a temporary directory so that we don't pollute the file system with temporary idbs and other such things. """ logger.info('Generating basic block information from IDA Pro') try: with TemporaryDirectory() as temp_dir: target_path = self._project_desc['target_path'] # Copy the binary to the temporary directory. Because projects # are created with a symlink to the target program, then IDA # Pro will generate the idb and bblist files in the symlinked # target's directory. Which is not what we want target_name = os.path.basename(target_path) temp_target_path = os.path.join(temp_dir, target_name) shutil.copyfile(target_path, temp_target_path) # Run the IDA Pro extractBasicBlocks script env_vars = os.environ.copy() env_vars['TVHEADLESS'] = '1' # This is required if s2e-env runs inside screen env_vars['TERM'] = 'xterm' ida = sh.Command(self._ida_path) ida('-A', '-B', '-S%s' % self.install_path('bin', 'extractBasicBlocks.py'), temp_target_path, _out=os.devnull, _tty_out=False, _cwd=temp_dir, _env=env_vars) # Check that the basic block list file was correctly generated bblist_file = os.path.join(temp_dir, '%s.bblist' % target_name) if not os.path.isfile(bblist_file): raise CommandError('Failed to generate bblist file for ' '%s' % target_name) # Parse the basic block list file # # to_basic_block takes a 3-tuple read from the bblist file and # converts it to a BasicBlock to_basic_block = lambda tup: BasicBlock( int(tup[0], 16), int(tup[1], 16), tup[2]) with open(bblist_file, 'r') as f: return [to_basic_block(l.rstrip().split(' ')) for l in f] except ErrorReturnCode as e: raise CommandError(e)
def _download_image(self, image, dest_dir): image_desc = self._templates.get(image) if not image_desc: raise CommandError('%s is not a valid image name' % image) url = self._templates[image].get('url') if not url: raise CommandError('The image %s has not downloadable archive' % image) dest_file = os.path.join(dest_dir, '%s.tar.xz' % image) _download(url, dest_file) _decompress(dest_file)
def _resolve_target_path(test_root, target_name, guestfs_dirs): if target_name.startswith('$(GUEST_FS)'): for directory in guestfs_dirs: new_target = target_name.replace('$(GUEST_FS)', directory) if os.path.exists(new_target): return new_target raise CommandError( f'Could not resolve {target_name} in {guestfs_dirs}') ret = os.path.join(test_root, target_name) if not os.path.exists(ret): raise CommandError(f'{ret} does not exist') return ret
def _handle_inf(target_path, **options): logger.info( 'Detected Windows INF file, attempting to create device driver project...' ) driver = Driver(target_path) driver.analyze() driver_files = driver.get_files() if not driver_files: raise CommandError('Driver has no files') base_dir = os.path.dirname(target_path) logger.info(' Driver files:') file_paths = [] first_sys_file = None for f in driver_files: full_path = os.path.join(base_dir, f) if not os.path.exists(full_path): if full_path.endswith('.cat'): logger.warn('Catalog file %s is missing', full_path) continue else: raise CommandError('%s does not exist' % full_path) logger.info(' %s', full_path) file_paths.append(full_path) if full_path.endswith('.sys'): first_sys_file = full_path # Pick the architecture of the first sys file # TODO: prompt the user to select the right driver if not first_sys_file: raise CommandError('Could not find any *.sys file') arch, _ = get_arch(first_sys_file) if arch is None: raise CommandError('Could not determine architecture for %s' % first_sys_file) options['target'] = target_path options['target_arch'] = arch # All the files to download into the guest. options['target_files'] = [target_path] + file_paths # TODO: support multiple kernel drivers options['modules'] = [(os.path.basename(first_sys_file), True)] options['processes'] = [] call_command(Project(WindowsDriverProjectConfiguration), **options)
def _get_disassembly_info(self, module_path): """ Extract disassembly information from the given module using S2E's IDA Pro script. This extraction is done within a temporary directory so that we don't pollute the file system with temporary idbs and other such things. """ logger.info('Generating disassembly information from IDA Pro for %s', module_path) try: with TemporaryDirectory() as temp_dir: # Copy the binary to the temporary directory. Because projects # are created with a symlink to the given module, IDA Pro # will generate the idb and disas files in the symlinked # module's directory. This is not what we want module_name = os.path.basename(module_path) temp_module_path = os.path.join(temp_dir, module_name) shutil.copyfile(module_path, temp_module_path) # Run the IDA Pro extractBasicBlocks script env_vars = os.environ.copy() env_vars['TVHEADLESS'] = '1' # This is required if s2e-env runs inside screen env_vars['TERM'] = 'xterm' ida = sh.Command(self._ida_path) ida('-A', '-B', '-S%s' % self.install_path('bin', 'extractBasicBlocks.py'), temp_module_path, _out=os.devnull, _tty_out=False, _cwd=temp_dir, _env=env_vars) # Check that the basic block list file was correctly generated disas_file = os.path.join(temp_dir, '%s.disas' % module_name) if not os.path.isfile(disas_file): raise CommandError('Failed to generate disas file for ' '%s' % module_name) logger.info('Disassembly successful') # Parse the basic block list file with open(disas_file, 'r') as f: return json.load(f, cls=BasicBlockDecoder) except ErrorReturnCode as e: raise CommandError(e) from e
def _validate_and_create_project(self, options): self._target_path = options['target'] if not self._target_path: logger.warn( 'Creating a project without a target file, you will have to manually edit bootstrap.sh' ) project_name = options['name'] if self._target_path: # Check that the analysis target is valid if not os.path.isfile(self._target_path): raise CommandError( 'Cannot analyze %s because it does not seem to ' 'exist' % self._target_path) # The default project name is the target program to be analyzed # (without any file extension) if not project_name: project_name, _ = \ os.path.splitext(os.path.basename(self._target_path)) self._project_dir = self.env_path('projects', project_name) # Load the image JSON description. If it is not given, guess the image image = options['image'] img_build_dir = self.source_path(CONSTANTS['repos']['images']['build']) templates = get_image_templates(img_build_dir) if not image: image = self._guess_image(templates, options['target_arch']) self._img_json = self._get_or_download_image(templates, image, options['download_image']) if self._target_path: # Check architecture consistency if not is_valid_arch(options['target_arch'], self._img_json['os']): raise CommandError( 'Binary is x86_64 while VM image is %s. Please ' 'choose another image' % self._img_json['os']['arch']) # Check if the project dir already exists # Do this after all checks have completed self._check_project_dir(options['force']) # Create the project directory os.mkdir(self._project_dir)
def _install_dependencies(): """ Install S2E's dependencies. Only apt-get is supported for now. """ logger.info('Installing S2E dependencies') ubuntu_ver = _get_ubuntu_version() if not ubuntu_ver: return install_packages = CONSTANTS['dependencies']['common'] + \ CONSTANTS['dependencies']['ubuntu_%d' % ubuntu_ver] + \ CONSTANTS['dependencies']['ida'] try: # Enable 32-bit libraries dpkg_add_arch = sudo.bake('dpkg', add_architecture=True, _fg=True) dpkg_add_arch('i386') # Perform apt-get install apt_get = sudo.bake('apt-get', _fg=True) apt_get.update() apt_get.install(install_packages) except ErrorReturnCode as e: raise CommandError(e)
def handle(self, *args, **options): # Parse the ExecutionTracer.dat file(s) and generate an execution tree # for the given path IDs results_dir = self.project_path('s2e-last') execution_tree = parse_execution_tree(results_dir, path_ids=options['path_ids']) if not execution_tree: raise CommandError('The execution trace is empty') # Convert the tree into a JSON representation state = AnalyzerState() execution_tree_json = _make_json_trace(execution_tree, state) # Write the execution tree to a JSON file json_trace_file = self.project_path('s2e-last', 'execution_trace.json') with open(json_trace_file, 'w', encoding='utf-8') as f: if options['pretty_print']: json.dump(execution_tree_json, f, indent=4, sort_keys=True, cls=TraceEncoder) else: json.dump(execution_tree_json, f, cls=TraceEncoder) logger.success('Execution trace saved to %s', json_trace_file)
def _finalize_config(self, config): config['project_type'] = 'cgc' args = config.get('target_args', []) if args: raise CommandError('Command line arguments for Decree binaries ' 'not supported') use_seeds = config.get('use_seeds', False) if not use_seeds: logger.warning('CGC requires seeds, forcing seed option on') config['use_seeds'] = True use_recipes = config.get('use_recipes', False) if not use_recipes: logger.warning('CGC requires recipes, forcing recipe option on') config['use_recipes'] = True enable_pov_generation = config.get('enable_pov_generation', False) if not enable_pov_generation: logger.warning( 'CGC required PoV generation, forcing POV generation option on' ) config['enable_pov_generation'] = True # CGC binaries do not have input files config['warn_input_file'] = False config['warn_seeds'] = False # CGC has its own test case generation system config['use_test_case_generator'] = False
def validate_arguments(options): """ Check that arguments are consistent. """ tools = options.get('tools', []) for tool in tools: if tool not in SUPPORTED_TOOLS: raise CommandError(f'The specified tool "{tool}" is not supported') options['enable_pov_generation'] = 'pov' in tools options['enable_cfi'] = 'cfi' in tools options['enable_tickler'] = 'tickler' in tools has_errors = False if options.get('single_path'): if options.get('use_seeds'): logger.error('Cannot use seeds in single-path mode') has_errors = True if options.get('enable_pov_generation'): logger.error('Cannot use POV generation in single-path mode') has_errors = True if '@@' in options.get('target_args', []): logger.error('Cannot use symbolic input in single-path mode') has_errors = True if has_errors: return False return True
def _translate_target_path_to_guestfs(self, target_path, guestfs_paths): """ This function converts a target path that is located in a guestfs folder to an absolute guest path. :param target_path: The target path to convert :param guestfs_paths: The list if guestfs paths to inspect. :return: """ if not target_path: return None real_target_path = os.path.realpath(target_path) for guestfs_path in guestfs_paths: # target and guestfs may be symlinks, need to compare actual path real_guestfs_path = os.path.realpath(guestfs_path) paths = [real_target_path, real_guestfs_path] common = os.path.commonpath(paths) if real_guestfs_path in common: return f'/{os.path.relpath(real_target_path, real_guestfs_path)}' # Check if that target_path is in the correct guestfs if real_target_path.startswith(os.path.realpath(self.image_path())): logger.error('%s seems to be located in a guestfs directory.', target_path) logger.error('However, the selected image uses the following directories:') for path in guestfs_paths: logger.error(' * %s', path) raise CommandError('Please check that you selected the proper guest image (-i option) ' 'and/or the binary in the right guestfs directory.') return None
def _install_dependencies(interactive): """ Install S2E's dependencies. Only apt-get is supported for now. """ logger.info('Installing S2E dependencies') ubuntu_ver = _get_ubuntu_version() if not ubuntu_ver: return install_packages = CONSTANTS['dependencies']['common'] + \ CONSTANTS['dependencies']['ida'] try: # Enable 32-bit libraries dpkg_add_arch = sudo.bake('dpkg', add_architecture=True, _fg=True) dpkg_add_arch('i386') # Perform apt-get install install_opts = ['--no-install-recommends'] + install_packages env = {} if not interactive: logger.info('Running install in non-interactive mode') env['DEBIAN_FRONTEND'] = 'noninteractive' install_opts = ['-y'] + install_opts apt_get = sudo.bake('apt-get', _fg=True, _env=env) apt_get.update() apt_get.install(install_opts) except ErrorReturnCode as e: raise CommandError(e)
def _link_existing_install(env_path, existing_install): """ Reuse an existing S2E installation at ```existing_install``` in the new environment at ```env_path```. """ # Check that the expected S2E installation directories exist for dir_ in ('bin', os.path.join('share', 'libs2e')): if not os.path.isdir(os.path.join(existing_install, dir_)): raise CommandError('Invalid S2E installation - ``%s`` does not ' 'exist. Are you sure that this directory ' 'contains a valid S2E installation?' % dir_) logger.info('Using existing S2E installation at %s', existing_install) # Clear out anything that may exist in the new environment's install dir new_install = os.path.join(env_path, 'install') shutil.rmtree(new_install, True) # We must use an absolute path for symlinks os.symlink(os.path.abspath(existing_install), new_install) # We still need to clone guest-images repo, because it contains info about # the location of images guest_images_repo = CONSTANTS['repos']['images']['build'] repos.git_clone_to_source(env_path, guest_images_repo)
def _get_base_image_and_app(image_name): x = image_name.split('/') if len(x) == 1: return x[0], None if len(x) == 2: return x raise CommandError(f'Invalid image name {image_name}')