Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
    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'
Ejemplo n.º 4
0
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}')
Ejemplo n.º 5
0
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)
Ejemplo n.º 6
0
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')
Ejemplo n.º 7
0
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)
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
Archivo: build.py Proyecto: S2E/s2e-env
    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')
Ejemplo n.º 10
0
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)
Ejemplo n.º 11
0
    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')
Ejemplo n.º 12
0
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)
Ejemplo n.º 13
0
    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
Ejemplo n.º 14
0
    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))
Ejemplo n.º 15
0
    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
Ejemplo n.º 16
0
    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)
Ejemplo n.º 17
0
    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)
Ejemplo n.º 18
0
    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)
Ejemplo n.º 19
0
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
Ejemplo n.º 20
0
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)
Ejemplo n.º 21
0
    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
Ejemplo n.º 22
0
    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)
Ejemplo n.º 23
0
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)
Ejemplo n.º 24
0
    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)
Ejemplo n.º 25
0
    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
Ejemplo n.º 26
0
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
Ejemplo n.º 27
0
    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
Ejemplo n.º 28
0
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)
Ejemplo n.º 29
0
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)
Ejemplo n.º 30
0
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}')