Automating Volatility

When I use Volatility I'm always amazed of the amount of forensic information that is available just from memory. Volatility comes with a large amount of plugins that make it very easy to get that information out of a memory image without extensive knowledge on how memory actually is organized.

Sometimes though I find myself repeating the same steps over and over again, using the output from one plugin as the input for the next, so this calls for a little automation. I could use a script to parse the plugin results and pass them along, but the great thing about Volatility is that is written in Python and therefore very extensible and reusable. It's possible to derive from existing plugins and alter their behaviour, but you can also call other plugins and process their results in your own. This will give you greater flexibility and most of the time more information than a batch script, because there often is more available in memory of the plugin than is actually written to the screen.

As a little challenge for myself I wanted to write a plugin that finds hidden processes (that might have been hidden by a rootkit) and dump their executables to a file. Since the main goal of the new plugin is to dump exe's to the filesystem, I chose to use the procexedump plugin as my base. The procexedump plugin can dump the exe based on multiple pids or by a single offset. The downside is that pids may not be hidden processes. You can dump hidden processes by offset, but you can only supply a single offset at a time. The new plugin has to solve this problem.

To find the hidden processes you can use the excellent psxview plugin, but I chose to do the comparison by hand using the pslist and psscan modules. The pslist module list the processes from memory like the task manager does, so it will not be able to find processes hidden by rootkits. The psscan module scans the whole memory for process structures and is able to find both hidden and terminated processes. Comparing the results from both will show which processes where actually hidden or terminated.

When writing a plugin that does some basic automation - like my new dumphidden plugin - a couple functions are important to understand. The constructor __init__ is used in Volatility to define (or remove) the arguments that can be supplied from the command line, like this example from the strings plugin:

[sourcecode language="python" wraplines="true" collapse="false"]
def __init__(self, config, *args, **kwargs):
     taskmods.DllList.__init__(self, config, *args, **kwargs)
     config.remove_option('PID')
     config.add_option('STRING-FILE', short_option = 's', default = None,
                       help = 'File output in strings format (offset:string)',
                       action = 'store', type = 'str') [/sourcecode]

In the sample plugin that I’ve written below I don’t define any new arguments, I just pass the config to the constructor of the procexedump plugin.

The calculate function is the one where the “magic” happens, the actual retrieval of information from the memory dump. This is where in our simple re-use and extend scenario we call the other plugins.

The last notable function is render_text where to results of the calculate function are written to the screen or a file. The command base class - from which all Volatility plugins are derived - has some handy functions to create tables for easy output rendering that can be used in this function.

Putting it all together results in the following code for my sample plugin:

[sourcecode language="python" wraplines="true" collapse="false"]
import volatility.plugins.taskmods as taskmods
import volatility.plugins.filescan as filescan
import volatility.plugins.procdump as procdump

class DumpHidden(procdump.ProcExeDump):
    """Dumps hidden process executables"""
    skippedPids = []

    def __init__(self, config, *args, **kwargs):
        procdump.ProcExeDump.__init__(self, config, *args, **kwargs)

    def calculate(self):
        pslist = taskmods.PSList(self._config).calculate()
        psscan = filescan.PSScan(self._config).calculate()

        visibletasks = []
        hiddentasks  = []

        for task in pslist:
            visibletasks.append(task.UniqueProcessId.v())

        for task in psscan:
            if task.UniqueProcessId.v() not in visibletasks:
                self._config.OFFSET = task.obj_offset
                hiddentask = procdump.ProcExeDump.calculate(self)[0]
                hiddentasks.append(hiddentask)
                if hiddentask.__class__.__name__ == "NoneObject":
                    self.skippedPids.append(str(task.UniqueProcessId))

        return hiddentasks

    def render_text(self, outfd, data):
        procdump.ProcExeDump.render_text(self, outfd, data)
        outfd.write("\r\n\r\nSkipped Pids: " + ",".join(self.skippedPids) + "\r\n")
[/sourcecode]

In the calculate function on lines 13 and 14 the tasks are retrieved from the psscan and pslist plugins using their calculatefunctions. We store the pid of every process that we retrieved using pslist in a list to compare them to the results from psscan. If a result is not available in the pslist results we call the calculate function from the procexedump plugin. We point procexedump to the memory offset where the process structure can be found. By setting the offset and calling calculate for every hidden process we evade the “single offset” problem described above. The result from the calculatefunction is added to the list of tasks we want to process later on. When procexedump cannot find a process at the given offset, it returns a NoneObject type. I want to store the pids of these tasks so I can review them later, as procexedumpdoesn’t show the pids of these processes in it’s output.

In the render_text function we call the render_text function of procexedump, simply passing on the parameters of the function. To show the skipped pids at the end we write the list to the screen using the write function of outfd, which results in the following output of the new plugin:

This is just an example of how easy it is to automate and extend Volatility plugins by using some basic Python skills and a little knowledge on how plugins work.