diff --git a/volatility/commands.py b/volatility/commands.py index b3cbc038b..52f4a063e 100644 --- a/volatility/commands.py +++ b/volatility/commands.py @@ -31,6 +31,7 @@ from volatility.renderers.dot import DotRenderer from volatility.renderers.html import HTMLRenderer, JSONRenderer from volatility.renderers.sqlite import SqliteRenderer +from volatility.renderers.elastic import ElasticRenderer from volatility.renderers.text import TextRenderer, FormatCellRenderer, GrepTextRenderer from volatility.renderers.xlsx import XLSXRenderer @@ -67,6 +68,14 @@ def register_options(config): cache_invalidator = False, help = "Write output in this file") + config.add_option("ELASTIC-URL", default = 'http://localhost:9200', + cache_invalidator = False, + help = "Elasticsearch URL for the Elastic renderer") + + config.add_option("INDEX", default = 'volatility', + cache_invalidator = False, + help = "Elasticsearch index name for the Elastic renderer") + config.add_option("VERBOSE", default = 0, action = 'count', cache_invalidator = False, short_option = 'v', help = 'Verbose information') @@ -305,6 +314,14 @@ def render_sqlite(self, outfd, data): except TypeError, why: debug.error(why) + def render_elastic(self, outfd, data): + try: + self._render(outfd, ElasticRenderer(self.__class__.__name__, self._config), data) + except NotImplementedError, why: + debug.error(why) + except TypeError, why: + debug.error(why) + def render_dot(self, outfd, data): try: self._render(outfd, DotRenderer(self.text_cell_renderers, self._config), data) diff --git a/volatility/renderers/elastic.py b/volatility/renderers/elastic.py new file mode 100644 index 000000000..cb115567a --- /dev/null +++ b/volatility/renderers/elastic.py @@ -0,0 +1,70 @@ +# Volatility +# Copyright (C) 2008-2015 Volatility Foundation +# +# This file is part of Volatility. +# +# Volatility is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Volatility is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Volatility. If not, see . +# + +from datetime import datetime +try: + from elasticsearch import Elasticsearch + from elasticsearch import helpers +except ImportError: + Elasticsearch = None +from volatility.renderers.basic import Renderer, Bytes +from volatility import debug +import uuid + + +class ElasticRenderer(Renderer): + + def __init__(self, plugin_name, config): + if not Elasticsearch: + debug.error("You must install the Elasticsearch python client" \ + ":\n\thttps://pypi.org/project/elasticsearch/") + self._plugin_name = plugin_name + self._config = config + self._es = None + self._type = 'volatility' + self._accumulator = [] + + def render(self, outfd, grid): + self._es = Elasticsearch([self._config.ELASTIC_URL]) + + def _add_multiple_row(node, accumulator): + row = node.values._asdict() + if 'start' in row and row['start'][:-5] != '': + row['datetime'] = datetime.strptime(row['start'][:-5],"%Y-%m-%d %H:%M:%S %Z") + else: + row['datetime'] = datetime.now() + + row['plugin'] = self._plugin_name + accumulator.append({ + '_index': self._config.INDEX, + '_type': self._type, + '_id': uuid.uuid4().hex, + '_source': row + }) + if len(accumulator) > 500: + helpers.bulk(self._es, accumulator) + accumulator = [] + self._accumulator = accumulator + return accumulator + + grid.populate(_add_multiple_row, self._accumulator) + + #Insert last nodes + if len(self._accumulator) > 0: + helpers.bulk(self._es, self._accumulator)