Writing Modules

FAME relies on modules to add functionality. Modules are actually Python classes that inherit from the fame.core.module.Module class.

Several kind of modules can be created:

  • PreloadingModule: these modules are used to preload the sample file prior to executing any processing module. A PreloadingModule should take the provided hash from FAME and download the corresponding sample from any source (e.g. VirusTotal).

  • ProcessingModule: this is where FAME’s magic is. A ProcessingModule should define some automated analysis that can be performed on some types of files / analysis information.

  • IsolatedProcessingModule: this is a special ProcessingModule that will be executed inside a VM, because it contains risks of infection.

  • ReportingModule: this kind of module enables reporting options, such as sending analysis results by email, or post a Slack notification when the analysis is finished.

  • ThreatIntelligenceModule: this kind of modules acts on IOCs. a ThreatIntelligenceModule has two roles:

    • Enrich the analysis, by adding Threat Intelligence information on IOCs when they are added to the analysis.

    • Enrich the Threat Intelligence Platform with IOCs extracted by FAME.

  • AntivirusModule: modules that act on files, and send them to antivirus vendors.

  • VirtualizationModule: modules that determine how-to orchestrate Virtual Machines when using IsolatedProcessingModule.

In order to create a module, create a Python file, and place it in the fame/modules/preloading, fame/modules/processing, fame/modules/reporting or fame/modules/threat_intelligence directory.

A valid module is simply a Python class in the fame/modules directory that inherits from the fame.core.module.Module class.

The fame/modules directory contains a subdirectory for each module repository that has been added to your FAME instance. You should create your module inside one of these repositories, where you will be able to commit your changes (create an empty repository if needed).

The best practice is to do the following:

Writing a Processing module

Processing modules are where the malware analysis is made. They will be chained together in order to produce a complete analysis.

A processing module can use the following inputs:

  • The main file to analyze

  • Files produced by other modules

  • The analysis itself, with all its elements

It can produce the following outputs:

  • Probable Name: this is the malware family. A module should only set the probable name if it has a very high confidence in its diagnostic.

  • Extractions: this is text information that should be the most useful for the analyst. A typical example would be malware’s configuration.

  • Tags: a tag is a computer-friendly piece of information that describes the analysis. Can be seen as a form of signature name.

  • Generated Files: files that were produced by the analysis, such as memory dumps.

  • Support Files: files that can be downloaded by the analyst, such as a sandbox analysis report.

  • Extracted Files: files that deserve an analysis of their own.

  • IOCs: indicators of compromise that could be used to detect this malware.

  • Detailed Results: any kind of information that would be useful to the analyst.

Defining such a module is simple. Just create a Python class that inherits from fame.core.module.ProcessingModule, and defines either fame.core.module.ProcessingModule.each() (recommended), fame.core.module.ProcessingModule.each_with_type() or fame.core.module.ProcessingModule.run().

These methods should return a boolean indicating if the module successfully did its job. If the return value is True, three things will happen:

  • A tag with the module’s name will automatically be added to the analysis.

  • All tags produced by the module (stored in self.tags will be added to the analysis tags).

  • The module detailed results (stored in self.results will also be added to the analysis).

For example, the module office_macros has one goal: extract VBA macros from office files. If it did not extract any macros, because the file did not contain any, the module should return False.

Here is the minimal code required for a (not very useful) module:

from fame.core.module import ProcessingModule


class Dummy(ProcessingModule):
    # You have to give your module a name, and this name should be unique.
    name = "dummy"

    # (optional) Describe what your module will do. This will be displayed to users.
    description = "Does nothing."

    # This method will be called, with the object to analyze in target
    def each(self, target):
        return True

Scope

In most cases, FAME will automatically decide when and where a processing module should be executed. These decisions are based on module attributes that you can define.

The most important attribute is fame.core.module.ProcessingModule.acts_on which defines the type of files this module can analyze. As an example, here is the definition of the office_macros module:

class OfficeMacros(ProcessingModule):
    name = "office_macros"
    description = "Extract and analyze Office macros."
    acts_on = ["word", "html", "excel", "powerpoint"]

Not specifying this attribute means that the module can analyze any type of objects.

You can also define conditions that will trigger a module’s execution with fame.core.module.ProcessingModule.triggered_by. Creating a module that would execute only on office documents that have macros could be done like this:

class SomethingOnMacros(ProcessingModule):
    name = "something_on_macros"
    description = "Do something on Office documents with macros"
    acts_on = ["word", "html", "excel", "powerpoint"]
    triggered_by = "office_macros"

This module will only be executed on analyses where the office_macros tag was added. The default value of None means that the module is always executed.

Finally, you can define on which workers this module will be executed, if you have several, by defining fame.core.module.ProcessingModule.queue:

class Dummy(ProcessingModule):
    name = "dummy"
    queue = "some_queue"

The default queue is named unix.

Adding tags and results

Adding tags contributes to the chaining of modules performed by FAME. A tag will be automatically added for every executed module that returned True, but modules also have the possibility to add their own tags with fame.core.module.ProcessingModule.add_tag().

While tags are needed by FAME in order to determine which modules to execute, detailed results are useful for the analysts. In order to add results, just put a JSON-serializable object in self.results.

As an example, we will imagine we were able to extract a list of signatures from a sandbox analysis:

class SandboxSignatures(ProcessingModule):
    name = "sandbox_signatures"

    def each(self, target):
        # Do stuff with target, let's say we were able to get this set of signatures:
        signatures = {
            'http_requests': 'Sample connects to the Internet',
            'high_entropy': 'High entropy, might contain encrypted or compressed data'
        }

        # Add these to tags and detailed results
        self.results = {
            'signatures': []
        }
        for signature in signature:
            self.add_tag(signature)
            self.results['signatures'].append(signatures[signature])

        return True

Adding observables

It is important that all observables detected by your modules are declared so that they are listed on the analysis and checked against your Threat Intelligence modules.

You can declare an observable to the analysis with the fame.core.module.ProcessingModule.add_ioc() method. You can also add tags to each observable if you have more information about what this is:

# Add an observable without tags
self.add_ioc("http://example.com/url")

# Add an observable with tags
self.add_ioc("http://c2.example.com/gate.php", ['c2', 'pony'])

Generated files

In some cases, modules can generate files that might be useful to other modules. These files should be added to the analysis by calling fame.core.module.ProcessingModule.register_files().

Here is an example of a module that is able to generate a memory dump:

class MemDump(ProcessingModule):
    name = "memdump"

    acts_on = "executable"
    generates = "memory_dump"

    def each(self, target):
        # Do stuff to get a memory dump:
        filepath = get_memdump_somehow(target)

        self.register_files('memory_dump', filepath)

        return True

Note that when your module is able to generate files, you should define the fame.core.module.ProcessingModule.generates attribute in order to specify which type of files can be generated by this module.

Support Files

Support files are files that are added to an analysis so that they can be downloaded by the analyst. This could be a sandbox report, extracted source code, etc.

In order to add a support file, use the fame.core.module.ProcessingModule.add_support_file() method:

self.add_support_file('NAME', filepath)

This will then display a link on the web interface that canbe used to download the file:

_images/modules-support-file.png

Extracted Files

Extracted files are files that deserve their own analysis. A good example is the zip module. It will unzip a file and launch a new analysis on each extracted file.

A module can add an extracted file by calling the fame.core.module.ProcessingModule.add_extracted_file() method:

self.add_extracted_file(filepath)

Links to the new analyses will be displayed like this:

_images/modules-extracted-files.png

Making results look great

By default, FAME will try to render the detailed results of every processing module in a readable way.

However, there are lots of cases where this won’t simply be enough. In these cases, you should create a specific template for your module by creating a file named details.html in your module’s directory.

This file can contain any valid Jinja2 templating code, and can access the detailed results by using the results variable.

In order for your results to fit nicely in the analysis page, you should use the following template:

<div class="col-md-12">
    <div class="card">
        <div class="header">
            <h4 class="title">{{name}}</h4>
            <p class="category">Detailed Results</p>
        </div>
        <div class="content">
            YOUR_SPECIFIC CODE HERE
        </div>
    </div>
</div>

If you need to add links in the bottom of the panel, you can use the following template (to put at the end of the #content div):

<div class="footer">
    <hr>
    <div class="stats">
        <ul>
            <li><a href="#">FIRST_LINK</a></li>
            <li><a href="#">SECOND_LINK</a></li>
            <li><a href="#">...</a></li>
        </ul>
    </div>
</div>

When your module might have support files, you can use the {{support_files(name)}} macro in order to display the download links:

<div class="footer">
    <hr />
    <div class="stats">
        <ul>
            <li><a href="#">FIRST_LINK</a></li>
            <li><a href="#">SECOND_LINK</a></li>
            {{support_files(name)}}
        </ul>
    </div>
</div>

As an example, here is the default template used:

<div class="col-md-12">
    <div class="card">
        <div class="header">
            <h4 class="title">{{name}}</h4>
            <p class="category">Detailed Results</p>
        </div>
        <div class="content">
            {% if results is mapping %}
                {% for key in results %}
                    <h5>{{key}}</h5>
                    {% if results[key] is string %}
                        <pre><code>{{results[key]}}</code></pre>
                    {% else %}
                        <pre><code>{{results[key]|to_json}}</code></pre>
                    {% endif %}
                {% endfor %}
            {% elif results is string %}
                <pre><code>{{ results }}</code></pre>
            {% else %}
                <pre><code>{{ results|to_json }}</code></pre>
            {% endif %}

            {% if 'support_files' in analysis %}
                {% if name in analysis.support_files %}
                    <div class="footer">
                        <hr />
                        <div class="stats">
                            <ul>
                                {{support_files(name)}}
                            </ul>
                        </div>
                    </div>
                {% endif %}
            {% endif %}
        </div>
    </div>
</div>

Isolated Processing Modules

When executing your module comes with risks of infection, it should not have full access to your FAME instance / your operating system. For this reason, you have the possibility to create an IsolatedProcessingModule. It is similar to a ProcessingModule except that it will be executed inside a Virtual Machine and will not have full access to your FAME instance.

Writing an IsolatedProcessingModule is similar to writing a ProcessingModule, with the following exceptions:

  • An IsolatedProcessingModule does not have full access to FAME’s code base. The only imports that will work are the following:

    from fame.core.module import IsolatedProcessingModule
    from fame.common.exceptions import ModuleInitializationError, ModuleExecutionError
    
  • An IsolatedProcessingModule cannot have installation scripts (they will not be executed). You should make sure to give specific installation instruction in the module’s README and verify requirements in the module’s initialize method.

Testing Processing Modules

When it comes to testing your processing modules during development, you have two options:

  • Use a full FAME instance and test your module by launching new analyses using the web interface. You will need a running worker to execute your module. Note that the workers will not automatically reload modified code, so you should make sure to click on the Reload button on Configuration options and module management.

  • The simpler option is to use the single_module.py utility. This way, you don’t need a webserver, a worker or even a MongoDB instance.

  • An IsolatedProcessingModule can also be tested with the single_module.py utility. By default, it will execute inside a Virtual Machine (as it should). If you want to test your module without this overhead (if you are already inside the VM for example), you can use the -l, --local switch.

Writing a Preloading module

Preloading modules are used to download a sample file automatically to make it available to FAME.

To define a new Preloading Module just create a Python class that inherits from fame.core.module.PreloadingModule and implements fame.core.module.PreloadingModule.preload().

If the module was able to successfuly find a sample associated with submitted hash, it should call the add_preloaded_file method. If this method is not called, the next preloading module will be scheduled.

For example, the module virustotal_download takes a hash and download the sample from VirusTotal.

Here is the minimal code required for a preloading module:

from fame.core.module import PreloadingModule


class Dummy(PreloadingModule):
    # You have to give your module a name, and this name should be unique.
    name = "dummy"

    # (optional) Describe what your module will do. This will be displayed to users.
    description = "Does nothing."

    # This method will be called, with the hash of the sample in target
    def preload(self, target):
        return False

Scope

It may happen that an analyst only has a hash available for analysis. In this case, FAME can download the sample from configured sample sources and trigger an analysis of the sample by its own.

Adding the preloading result

Once the module successfully preloaded the sample for FAME, it must add the file to the analysis. Based on what type the file is, FAME then schedules suitable processing modules (if magic mode is enabled).

You can add a preloaded file by calling fame.core.module.PreloadingModule.add_preloaded_file(). The function expects either a path to a file or a file-like object with the available data (file path has precedence if both are provided).

Common module features

The following paragraphs define functionalities that are available for all kinds of modules (not just Processing modules).

Configuration

Your modules will often need additional parameters in order to be able to execute properly. You can specify these configuration options by using the fame.core.module.Module.config attribute.

As an example, consider a module that will submit a file to a sandbox. It will need to know the URL where it can submit files:

from fame.core.module import ProcessingModule


class SandboxModule(ProcessingModule):
    name = "sandbox"

    config = [
        {
            'name': 'url',
            'type': 'str',
            'default': 'http://localhost:1234/submit',
            'description': 'URL of the sandbox submission endpoint.'
        },
    ]

config is a list of dictionaries (one for each configuration option), with the following keys:

  • name: name of this setting. Will determine how to access this setting in the module.

  • description: a description of this setting, to help the user / administrator.

  • type: can be str, integer, bool or text. The difference between text and str is only the representation of the setting in forms (input for str, textarea for text).

  • default (optional): default value.

  • option (optional): should this setting be available on a per-analysis basis ? Default value is False.

The submission URL will always be the same, which explains why option remains False. Adding an option to allow internet access on a per-analysis basis would look like this:

config = [
    {
        'name': 'url',
        'type': 'str',
        'default': 'http://localhost:1234/submit',
        'description': 'URL of the sandbox submission endpoint.'
    },
    {
        'name': 'allow_internet_access',
        'type': 'bool',
        'default': True,
        'description': 'This allows full Internet access to the sandbox.',
        'option': True
    }
]

These two settings are then available in your code by their respective names:

# Access configured submission URL
self.url

# See if internet access is allowed
self.allow_internet_access

Handling dependencies

When your module has some dependencies that are not already FAME dependencies, you should make sure that it is possible to import your module without the dependencies. Instead, check if the dependencies are available in the fame.core.module.Module.initialize() method:

try:
    import ijson
    HAVE_IJSON = True
except ImportError:
    HAVE_IJSON = False

from fame.common.exceptions import ModuleInitializationError
from fame.core.module import ProcessingModule

class SomeModule(ProcessingModule):
    name = "some_module"

    def initialize(self):
        if not HAVE_IJSON:
            raise ModuleInitializationError(self, "Missing dependency: ijson")

You should also provide scripts that will ensure that your dependencies are available. You have the following options (not mutually exclusives):

  • Add a requirements.txt to your module’s directory when all is required is to install some python packages.

  • Add a install.py file that will ensure dependencies are installed. If present, this file will be executed.

  • Add a install.sh file that will ensure dependencies are installed. If present, this file will be executed on UNIX systems.

  • Add a install.cmd file that will ensure dependencies are installed. If present, this file will be executed on Windows systems.

Note

The install.py, install.sh and install.cmd should exit with a non-zero return code if dependencies are not correctly installed.

These scripts will be launched at each worker restart, so they should ensure dependencies are installed rather than always installing them.

When creating installation scripts, you can use from fame.common.constants import VENDOR_ROOT in order to put files inside the vendor subdirectory.

Note

You can also provide additional information and installation information that will be displayed to the user by creating a README.md file.

Warning

Installation scripts are not available for IsolatedProcessingModule and VirtualizationModule.

Abstract Modules

Sometimes, it makes sense to create abstract modules in order to provide common functionalities to several modules.

In order to create an abstract module, just define a module without a fame.core.module.Module.name.

When you need configuration options in your abstract module, you should use fame.core.module.Module.named_configs rather than fame.core.module.Module.config. This way, the configuration will be shared between all modules instead of being duplicated:

class MalwareConfig(ProcessingModule):
    named_configs = {
        'malware_config': {
            'description': 'Needed in order to be able to track malware targets',
            'config': [
                {
                    'name': 'monitor',
                    'type': 'text',
                    'description': 'List of patterns (strings) to look for in malware configurations. There should be one pattern per line.'
                }
            ]
        }
    }

named_configs is a dict with the keys being the name of the configuration group.

This setting can then be accessed like this:

# self.name_of_the_configuration_group.name_of_the_setting
self.malware_config.monitor

API Reference

This page documents how to create the different kinds of modules, by detailing their respective API, starting with the Module API, which is common to every kind of module.

Common module API

class fame.core.module.Module(with_config=True)[source]

Base class for every kind of modules used by FAME.

Define functions that can be used by every kind of modules.

name

A string that defines the name of the module. This is what will be displayed in the interface. A module without a name is an abstract module.

description

A string describing what this module does. It will be displayed to the user in the interface.

config

Module configuration options. This attribute should be a list of dicts with the following form:

{
    'name': 'NAME',
    'type': 'TYPE', # 'str', 'integer', 'bool' or 'text'
    'default': 'DEFAULT',
    'description': 'The description.'
},

The following fields are available:

  • name: name of this setting. Will determine how to access this setting in the module.

  • description: a description of this setting, to help the user / administrator.

  • type: can be str, integer, bool or text. The difference between text and str is only the representation of the setting in forms (input for str, textarea for text).

  • default (optional): default value.

  • option (optional): should this setting be available on a per-analysis basis ? Default value is False.

named_configs

Configuration shared between modules. Typically used in abstract modules. This is a dict, with each key being the name of a configuration group, and the value being a dict with two fields:

Example:

named_configs = {
    'configuration_group': {
        'description': 'This is a shared configuration',
        'config': [
            {
                'name': 'NAME',
                'type': 'TYPE',
                'description': 'Description of this setting'
            }
        ]
    }
}
initialize()[source]

To implement in module to perform initialization operations.

All dependency verifications should be done by defining this method in modules.

Raises:

ModuleInitializationError – Should be raised for any initialization error, with a message.

log(level, message)[source]

Add a log message to the analysis

Parameters:
  • level – string to define the log level (debug, info, warning or error).

  • message – free text message containing the log information.

needs_variable(variables)[source]

Indicate that the module needs a specific attribute to work properly.

This function is only useful in abstract modules, in order to make sure that modules that inherit from this class correctly defines needed class attributes.

Parameters:

variables – a string or an array of strings containing the name of needed class attributes.

Raises:

ModuleInitializationError – One of the needed attributes is not correctly defined.

Preloading Module

class fame.core.module.PreloadingModule(with_config=True)[source]

Base class for preloading modules

PreloadingModules can be used to download the sample binary from e.g. VirusTotal before queueing any processing modules. Hence, PreloadingModules only work on hashes. A successful execution of a PreloadingModule updates the Analysis object with the new data and queues the remaining modules as if the sample itself was uploaded the FAME.

queue

A string defining on which queue the tasks will be added. This defines on which worker this module will execute. The default value is unix.

priority

An integer defining the module’s priority when preloading. The smallest values are used first (defaults to 100).

preload(target)[source]

To implement.

Parameters:

target (string) – the hash that is to be analyzed

Returns:

A boolean indicating whether or not a file could be downloaded for the given hash

skip_review(skip=True)[source]

Skip the review of an analysis.

Parameters:

skip (optional) – True if the analysis review should be skipped, False otherwise.

Processing Module

class fame.core.module.ProcessingModule(with_config=True)[source]

Base class for processing modules

This class provides several methods that can update the analysis with interesting findings.

acts_on

A string or a list of strings containing FAME files type that can be analyzed by this module. Default value of None means all file types.

generates

A string or a list of strings containing FAME files type that can be generated by this module. This will be used for module chaining in the case of Targeted analysis. Default value of None means no files are generated.

triggered_by

A string or a list of strings containing fnmatch patterns that will match analysis tags in order to determine if this module should run. Default value of None creates a generic module, that will always execute on the types of files it acts on.

queue

A string defining on which queue the tasks will be added. This defines on which worker this module will execute. The default value is unix.

permissions

A dictionnary listing permissions used by this module. Each key is the name of a permission, and the value is a description:

permissions = {
    'permission_name': "Description of the permission."
}

The default value is {}, which means the module does not use any permission.

add_extracted_file(location, filename='', automatic_analysis=True)[source]

Create a new file that deserves its own analysis.

Parameters:
  • location (string) – full path.

  • filename (string) – file name.

add_extraction(label, extraction)[source]

Add an extraction to the analysis.

Parameters:
  • label (string) – name of this extraction.

  • extraction (string) – extraction content.

add_ioc(value, tags=[])[source]

Add IOCs to the analysis.

Parameters:
  • value – string or list of strings containing the IOC’s value.

  • tags (optional) – string or list of strings containing tags that describe these IOCs.

add_probable_name(probable_name)[source]

Add a probable name to the analysis.

Parameters:

probable_name (string) – probable name of the malware.

add_support_file(name, location)[source]

Add a support file to this analysis. A support file is a file that will be stored permanently and you will be able for analysts to download.

Parameters:
  • name (string) – name of this support file.

  • location (string) – full path. The name of the file will be kept. You should ensure that you use filenames that cannot generate collisions. For example, you could use the module name as a prefix.

add_tag(tag)[source]

Add a tag to the analysis.

All tags added using this method will only be added to the analysis if the module’s execution returns True.

Parameters:

tag (string) – tag to add to the analysis. Tags added to the analysis will have the following format: module_name[tag].

change_type(location, new_type)[source]

Change the type of a file and launch a new analysis.

Parameters:
  • location (string) – path of the file to change.

  • new_type (string) – new FAME file type.

each(target)[source]

To implement. Perform the actual analysis.

This method will automatically be called once for every file in the analysis matching the acts_on attribute.

Should return True if the module succeeded, and add any useful information to self.results (format of this instance variable is free as long as it can be encoded with BSON).

Parameters:

target (string) – full path of the file to analyze. URL string when the object to analyze is a URL.

Returns:

boolean indicating if module was successful.

Raises:

ModuleExecutionError – if any error occurs during the analysis.

each_with_type(target, file_type)[source]

To implement. Perform the actual analysis.

This method is similar to each(), but has an additional argument, which is the type of the target. When creating a module, you can chose between implementing each() or each_with_type().

Parameters:
  • target (string) – full path of the file to analyze. URL string when the object to analyze is a URL.

  • file_type (string) – FAME type of the target.

Returns:

boolean indicating if module was successful.

Raises:

ModuleExecutionError – if any error occurs during the analysis.

register_files(file_type, locations)[source]

Add a generated file to the analysis.

Parameters:
  • file_type (string) – FAME file type of the generated file.

  • location (string) – full path of the file, or array of full path.

run()[source]

To implement, when fame.core.module.ProcessingModule.each() cannot be used.

This method will be called and should perform the actual analysis. It should have the same output than fame.core.module.ProcessingModule.each().

By default, it will call fame.core.module.ProcessingModule.each() on every elligible file in the analysis.

You should only define this method when the module does not work on files, but on the analysis itself. The analysis can be accessed using self._analysis.

Returns:

boolean indicating if module was successful.

Raises:

ModuleExecutionError – if any error occurs during the analysis.

skip_review(skip=True)[source]

Skip the review of an analysis.

Parameters:

skip (optional) – True if the analysis review should be skipped, False otherwise.

Special Processing Modules

Some processing modules define an interface of their own that make it easier to develop certain kinds of modules:

  • fame.modules.community.processing.vol.Volatility to develop plugins that rely on the memory analysis framework Volatility.

  • fame.modules.community.processing.malware_config.malware_config.MalwareConfig to develop plugins that extract useful information from malware configurations.

  • fame.modules.community.processing.apk.apk_plugins.APKPlugin to develop static APK analysis plugins relying on Androguard. This kind of plugins are a little different, since they do not inherit from fame.core.module.ProcessingModule.

Isolated Processing Module

class fame.core.module.IsolatedProcessingModule(with_config=True)[source]

Base class for isolated processing modules.

All processing modules that needs to be executed in an isolated environment (a VM) should inherit from this class.

Instead of executing the module directly, the worker will orchestrate a specifically configured Virtual Machine and execute the module inside of it.

should_restore

A boolean that can be set by the module to indicate that the VM should be restored to a clean state. It is set to False by default.

each_with_type(target, target_type)[source]

To implement. Perform the actual analysis.

This method is similar to each(), but has an additional argument, which is the type of the target. When creating a module, you can chose between implementing each() or each_with_type().

Parameters:
  • target (string) – full path of the file to analyze. URL string when the object to analyze is a URL.

  • file_type (string) – FAME type of the target.

Returns:

boolean indicating if module was successful.

Raises:

ModuleExecutionError – if any error occurs during the analysis.

initialize()[source]

To implement in module to perform initialization operations.

All dependency verifications should be done by defining this method in modules.

Raises:

ModuleInitializationError – Should be raised for any initialization error, with a message.

run()[source]

To implement, when fame.core.module.ProcessingModule.each() cannot be used.

This method will be called and should perform the actual analysis. It should have the same output than fame.core.module.ProcessingModule.each().

By default, it will call fame.core.module.ProcessingModule.each() on every elligible file in the analysis.

You should only define this method when the module does not work on files, but on the analysis itself. The analysis can be accessed using self._analysis.

Returns:

boolean indicating if module was successful.

Raises:

ModuleExecutionError – if any error occurs during the analysis.

Reporting Modules

Reporting Modules are meant to provide hooks in the analysis process for reporting needs. At this point, only one hook is available, when the analysis is finished.

class fame.core.module.ReportingModule(with_config=True)[source]

Base class for reporting modules

done(analysis)[source]

To implement. Called when an analysis is finished.

Parameters:

analysis – the finished analysis.

initialize()[source]

To implement in module to perform initialization operations.

All dependency verifications should be done by defining this method in modules.

Raises:

ModuleInitializationError – Should be raised for any initialization error, with a message.

Threat Intelligence Modules

Threat Intelligence Modules have two roles:

  • Enrich the analysis with Threat Intelligence data

  • Enrich the Threat Intelligence Platform with IOCs extracted by FAME

class fame.core.module.ThreatIntelligenceModule(with_config=True)[source]

Base class for Threat Intelligence Modules

has_submit_implementation()[source]

This method checks if one of the submission methods has been overwritten from his origin class.

This way, you can know if the ThreatIntelligence module has submission capability.

ioc_lookup(ioc)[source]

To implement. Perform an IOC lookup to enrich analysis.

Parameters:

ioc (string) – the IOC value to look for.

Returns:

A tuple (tags, indicators).

tags is a list of tags (strings) that describe this IOC.

indicators is a list of dicts with matching indicators, with the following keys:

  • name: name of this indicator.

  • description: additional description.

ioc_submission(analysis, ioc, tags)[source]

To implement. Perform a single IOC submission.

This method should send one IOC selected by the analyst and send it to the Threat Intelligence Platform to enrich it.

It should only be defined when you don’t know how to do bulk submission. Otherwise, define fame.core.module.ThreatIntelligenceModule.iocs_submission() instead.

Parameters:
  • analysis – the analysis that generated the IOC.

  • ioc (string) – the IOC’s value.

  • tags (string) – a list of tags separated by ,.

iocs_submission(analysis, iocs)[source]

To implement. Perform a bulk IOC submission.

This method should send all IOCs selected by the analyst and send them to the Threat Intelligence Platform to enrich it.

By default, this method calls fame.core.module.ThreatIntelligenceModule.ioc_submission() on every single IOC. This means that you should only define this method when you know how to do bulk submission. Otherwise, define fame.core.module.ThreatIntelligenceModule.ioc_submission() instead.

Parameters:
  • analysis – the analysis that generated the IOCs.

  • iocs (list) – a list of dicts with two keys: value (string containing the IOC’s value) and tags (string containing a list of tags delimited with ,).

Antivirus Modules

Antivirus Modules are used to submit analyzed files to antivirus vendors so that they can be included to their signatures.

class fame.core.module.AntivirusModule(with_config=True)[source]

Base class for Antivirus submission modules

submit(file)[source]

To implement. Submit the file to an Antivirus vendor

Parameters:

file (string) – full path of the file to submit.

Virtualization Module

class fame.core.module.VirtualizationModule(with_config=True)[source]

Base class for Virtualization modules, used by IsolatedProcessingModules

initialize(vm, base_url, snapshot=None)[source]

To implement if you have to check for requirements.

If you define your own implementation, you should make sure to call the base one:

VirtualizationModule.initialize(self, vm, base_url, snapshot)
Parameters:
  • vm (string) – label associated with the VM to use.

  • base_url (string) – base URL for the web service.

  • snapshot (string) – name of the snapshot to use when restoring the VM.

Raises:

ModuleInitializationError – One of requirements was not met.

is_running()[source]

To implement.

Must return True if the VM self.vm_label is in a running state.

Raises:

ModuleExecutionError – Could not execute correctly.

restore_snapshot()[source]

To implement.

Restore the snapshot in self.snapshot. When None, should restore the current snapshot.

Raises:

ModuleExecutionError – Could not execute correctly.

start()[source]

To implement.

Start the VM self.vm_label.

Raises:

ModuleExecutionError – Could not execute correctly.

stop()[source]

To implement.

Stop the VM self.vm_label.

Raises:

ModuleExecutionError – Could not execute correctly.