{"id":13612360,"url":"https://github.com/iAbadia/Volatility-Plugin-Tutorial","last_synced_at":"2025-04-13T11:31:59.181Z","repository":{"id":89287213,"uuid":"102018081","full_name":"iAbadia/Volatility-Plugin-Tutorial","owner":"iAbadia","description":"Development guide for Volatility Plugins","archived":false,"fork":false,"pushed_at":"2017-09-06T10:57:33.000Z","size":7,"stargazers_count":23,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-07T20:42:03.642Z","etag":null,"topics":["guide","memory-forensics","plugin","python","tutorial","volatility"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iAbadia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-08-31T15:50:04.000Z","updated_at":"2023-09-08T17:29:21.000Z","dependencies_parsed_at":"2023-04-13T04:22:35.874Z","dependency_job_id":null,"html_url":"https://github.com/iAbadia/Volatility-Plugin-Tutorial","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iAbadia%2FVolatility-Plugin-Tutorial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iAbadia%2FVolatility-Plugin-Tutorial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iAbadia%2FVolatility-Plugin-Tutorial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iAbadia%2FVolatility-Plugin-Tutorial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iAbadia","download_url":"https://codeload.github.com/iAbadia/Volatility-Plugin-Tutorial/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248705736,"owners_count":21148583,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["guide","memory-forensics","plugin","python","tutorial","volatility"],"created_at":"2024-08-01T20:00:28.640Z","updated_at":"2025-04-13T11:31:58.905Z","avatar_url":"https://github.com/iAbadia.png","language":null,"readme":"# Volatility Plugin Tutorial\n\n## Introduction\nDeveloping a plugin for Volatility is way easier that it might appear. The biggest obstacle, in my opinion, is knowing how or where to start. There're plenty of sources where you can learn about Volatility ([The Art of Memory Forensics](https://www.memoryanalysis.net/amf) book, [/r/memoryforensics](https://www.reddit.com/r/memoryforensics/), [Volatility Labs](https://volatility-labs.blogspot.com) blog, etc.) but very few - almost none - where you can learn how to develop a plugin.\nAlso, [this tutorial](https://gist.github.com/bridgeythegeek/bf7284d4469b60b8b9b3c4bfd03d051e#file-myfirstvolatilitypluginwithunifiedoutput-md) inspired me to make a more complex one, and here we are!\n\nI'll be working on a x64 Ubuntu 16.04 machine.\n\n## Objectives\nIn this guide you will learn the following:\n - Download and run Volatility from source.\n - Get a memory dump from Oracle's VirtualBox VM.\n - Understand what exactly a Volatility plugin is.\n - Write a working Volatility plugin.\n\n## Before we begin\nBe sure to have [Oracle's VirtualBox](https://www.virtualbox.org/wiki/Downloads) installed. You won't need the Extension Pack but it's advisable to have it. Also, If you don't have a licensed Windows copy go get a [Microsoft Edge and IE test free copy](https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/). Yes, for real.\n\n## Guide\n\n### Download Volatility\nThe latest Volatility stable version cat be found at [The Volatility Foundation GitHub](https://github.com/volatilityfoundation). You can just clone the repository via:\n\n```\n$ git clone https://github.com/volatilityfoundation/volatility.git\n```\n\nNow `cd` to volatility's folder and execute a couple or two... wait! We don't have a memory dump to analyze yet. Let's fix that!\n\n### Get a memory dump\n\nYou can get a memory dump in multiple ways, we'll leverage virtualization and use VirtualBox to easily get a VM's memory dump. A more extensive version of this process can be found [here](https://andreafortuna.org/how-to-extract-a-ram-dump-from-a-running-virtualbox-machine-b803f9fd2a0), but, long story short:\n - Boot up a virtual machine in VirtualBox and note it's name.\n - `$ vboxmanage debugvm \u003cVM-NAME\u003e dumpvmcore --filename vm.elf`\n - That's it.\n\nWell, this is not a memory dump per se. It's a `.elf` that also contains other data from VirtualBox we don't need. Guess what? Volatility doesn't care. If you really want a raw memory dump go check out the more extensive version.\n\n### Analyze memory dump\nNow we get to look into the memory dump. We have all we need so `cd` to volatility's folder and execute `pslist` command like this:\n```\n$ python vol.py -f \u003cvm-dump\u003e --profile=\u003cvm-profile\u003e pslist\n```\nLet's elaborate. \n\n - **vm-dump**: This is the .elf - or .raw - file that contains the VM's memory dump. It's **imperative** that you provide this argument, otherwise, volatility won't run.\n  - **vm-profile**: The profile tells volatility from which machine the memory dump came from. If you don't provide this information, volatility will try to guess - not a good idea btw. A list of valid profiles can be found [here](https://github.com/volatilityfoundation/volatility/wiki/Volatility-Usage#selecting-a-profile) or you can run `$ python vol.py --info`. I recommend the latter.\n  - **pslist**: This is the command we invoke.\n\nSince I extracted a Windows 7 SP1 x64 memory dump, I would run:\n\n    $ python vol.py -f /home/user/vmcore.elf --profile=Win7SP1x64 pslist\n    Volatility Foundation Volatility Framework 2.6\n    Offset(V)          Name                    PID   PPID   Thds     Hnds   Sess  Wow64 Start                          Exit                          \n    ------------------ -------------------- ------ ------ ------ -------- ------ ------ ------------------------------ ------------------------------\n    0xffffc480b4258040 System                    4      0    107        0 ------      0 2017-07-13 18:18:39 UTC+0000                                 \n    0xffffc480b45a6040 smss.exe                296      4      4        0 ------      0 2017-07-13 18:18:39 UTC+0000                                 \n    0xffffc480b5b1e7c0 csrss.exe               384    368     11        0      0      0 2017-07-13 18:18:42 UTC+0000                                 \n    0xffffc480b62d3080 smss.exe                448    296      0 --------      1      0 2017-07-13 18:18:42 UTC+0000   2017-07-13 18:18:42 UTC+0000  \n    0xffffc480b62d6080 wininit.exe             456    368      4        0      0      0 2017-07-13 18:18:42 UTC+0000                                 \n    0xffffc480b62d9580 csrss.exe               464    448     12        0      1      0 2017-07-13 18:18:42 UTC+0000                                 \n    0xffffc480b630d080 winlogon.exe            516    448      5        0      1      0 2017-07-13 18:18:42 UTC+0000\n    ...\n\n`pslist` is such a trivial command, it simply lists all running processes. But don't be fooled, volatility has quite a set of useful commands which can be found [here](https://github.com/volatilityfoundation/volatility/wiki/Command-Reference).\n\n### What's a Volatility plugin?\nYou want to develop a plugin for volatility, but, do you know what a plugin is? The first thing to know here is: **EVERYTHING IS A PLUGIN**. Yes, there're plugins developed by regular people - you and me - and the ones developed by The Volatility Foundation, that are included in Volatility. The so-called volatility commands are nothing more than plugins, `pslist` is a plugin. The plugin you'll create will be invoked almost the same way we used `pslist`. \n\n### Create the plugin\nNow that we know what we're going to do, time to learn how to make it. We'll start by creating a folder for our plugin:\n\n```\ncd ~\nmkdir myPlugin\ncd myPlugin\n```\nNow we create a file for the plugin. Note that the name you give this file will be the name of the command you'll have to give volatility later to run your plugin. I'll call it `myplugin`.\n```\nvim myplugin.py\n```\n\nOk, I was kidding, I'm not using vim for this. Any text editor will do: gedit, [ATOM](https://atom.io/), [VS Code](https://code.visualstudio.com/), [Sublime Text](https://www.sublimetext.com/), etc.\n\n#### The basics\nWe're going to do this the right way: Write the minimum for the plugin to run and build up from it. This would be the simplest working plugin:\n\n    import volatility.plugins.common as common\n    class MyPlugin(common.AbstractWindowsCommand):\n        def render_text(self, outfd, data):\n            print \"Hello world!\"\n\nLet me elaborate:\n - `class MyPlugin(common.AbstractWindowsCommand)`: This is the class that Volatility instantiates when you run the plugin. It **must** inherit from one of the `common.Command` subclases, I've chosen `common.AbstractWindowsCommand` because my plugin will be Windows-oriented.\n - `def render_text(self, outfd, data)`: This is the called function for presenting the results in plain text format. **outfd** is the file descriptor to which Volatility will write, by default it's `stdout` but you can give it with `--output-file`. We didn't do any work so, after (not) doing all the things my plugin does, it's called and prints `Hello world!` to `stdout`. You can tell the plugin to present the results in plain text, dot, JSON or even a formated HTML document but we'll get to that later.\n\nNow we can run it with:\n\n```\n$ python vol.py --plugins=\u003cmyplugin-dir\u003e -f \u003cvm-dump\u003e --profile=\u003cvm-profile\u003e myplugin\n```\n\nNote that the `--plugins` argument **must** be passed right after `vol.py`, doing it elsewhere wont' work. Also, the path provided must be absolute:\n\n - This is good: `python vol.py --plugins=/home/user/volpydev/myplugin -f \u003cvmcore\u003e myplugin`\n - This is not:  `python vol.py -f \u003cvmcore\u003e --plugins=/home/user/volpydev/myplugin myplugin`\n - This isn't either: `python vol.py --plugins=volpydev/myplugin -f \u003cvmcore\u003e myplugin`\n\n#### The not-so-basics\nNow we're going to make something with that memory dump and output something more useful than `Hello world!`. Take this code:\n\n    import volatility.plugins.common as common\n    import volatility.utils as utils\n    import volatility.win32 as win32\n\n    class MyPlugin(common.AbstractWindowsCommand):\n        ''' My plugin '''\n\n        def calculate(self):\n            addr_space = utils.load_as(self._config)\n            tasks = win32.tasks.pslist(addr_space)\n            return tasks\n\n        def render_text(self, outfd, data):\n            for task in data:\n                outfd.write(\"{!s}\\n\".format(str(task)))\n\nImportant things here:\n\n - `''' My plugin'''`: This docstring will be written when calling your plugin with `-h` (or `--help`)\n - `def calculate(self)`: Here is where we do the real work, the returned data will be passed as `data` to `render_text`. What we are doing is getting a list with all the running processes in the given memory dump's address space and return it. `render_text` will iterate over that list and print whatever `str(task)` returns (It happens to be the virtual offset of the process' _EPROCESS internal structure, but that's not important right now).\n - `self._config`: This is the configuration object built for a single specific run of Volatility, it identifies the given memory dump's address space. It also contains other data such as passed arguments.\n\nNow you can run it and see what it prints:\n\n    $ python vol.py --plugins=\u003cmyplugin-dir\u003e -f \u003cvmcore\u003e --profile=\u003cvmcore-profile\u003e myplugin\n    Volatility Foundation Volatility Framework 2.6\n    2223228800\n    2238952424\n    2246946864\n    2238545192\n    2223723360\n    2238307624\n    2247151664\n    2247164976\n    ...\n\nThis looks good, but we can do better! Let's print something that we can understand, take this `render_text` function:\n\n    def render_text(self, outfd, data):\n        for task in data:\n            outfd.write(\"{0} {1}\\n\".format(task.UniqueProcessId, task.ImageFileName))\n\nThis will print every process's PID next to it's name. Go check it!\n\n    $ python vol.py --plugins=\u003cmyplugin-dir\u003e -f \u003cvmcore\u003e --profile=\u003cvmcore-profile\u003e myplugin\n    Volatility Foundation Volatility Framework 2.6\n    4 System\n    276 smss.exe\n    352 csrss.exe\n    388 wininit.exe\n    396 csrss.exe\n    436 winlogon.exe\n    480 services.exe\n    488 lsass.exe\n    496 lsm.exe\n    604 svchost.exe\n    ...\n\nNow we're talking! This is great but, what if you want the results in greptext for an easier processing? Or even an sqlite3 database? We'll need the **unified output** for that, let's find out how!\n\n#### Unified Output\nHere we learn how to print the right way. `render_text()` works for a quick fix but, if we want our plugin to offer the best we need `unifed_output()`. This will centralize all the presenting results stuff into one function - actually two. However, if you want to do something \"special\" when printing on any specific format you can always define `render_x()` - where x is any of the [available formats](https://github.com/volatilityfoundation/volatility/wiki/Unified-Output#standard-renderers) - and do it as you like. Defining `render_x()` actually overrides the default printing function. We've already done that, remember?\n\nAs I said, it's not just one function, but two: `unified_output()` and `generator()`. The latter will provide `unified_output()` a generator with the data and `unified_output()` will return a `TreeGrid` object with that data and it's structure. An example is worth a thousand words, so:\n\n    def generator(self, data):\n        for task in data:\n\t\t\tyield (0, [\n\t\t\t    int(task.UniqueProcessId),\n\t\t\t    str(task.ImageFileName)\n\t\t\t])\n\n    def unified_output(self, data):\n        tree = [\n            (\"PID\", int),\n            (\"Name\", str)\n            ]\n        return TreeGrid(tree, self.generator(data))\n\nAs you can see, `TreeGrid` takes a tuple array and the generator returned by `generator()`. This tuple contains the name and data type of each column. `generator()` will return a generator with the process's PID and name. Note that the types of the data given to `yield` in `generator()` and the types in the tuple list must coincide. Let's see the result:\n\n    import volatility.plugins.common as common\n    import volatility.utils as utils\n    import volatility.win32 as win32\n\n    from volatility.renderers import TreeGrid\n\n    class MyPlugin(common.AbstractWindowsCommand):\n        ''' My plugin '''\n\n        def calculate(self):\n            addr_space = utils.load_as(self._config)\n            tasks = win32.tasks.pslist(addr_space)\n            return tasks\n\n        def generator(self, data):\n            for task in data:\n                yield (0, [\n                    int(task.UniqueProcessId),\n                    str(task.ImageFileName)\n                ])\n\n        def unified_output(self, data):\n            tree = [\n                (\"PID\", int),\n                (\"Name\", str)\n                ]\n            return TreeGrid(tree, self.generator(data))\n\nNow execute it and check that the results... are exactly the same as before. But hey! now we can get the results in other formats too. Try json for example (It'll output a minified JSON, I've beautified it):\n\n    $ python vol.py --plugins=\u003cmyplugin-dir\u003e -f \u003cvmcore\u003e --profile=\u003cvmcore-profile\u003e myplugin --output=json\n    Volatility Foundation Volatility Framework 2.6\n    {\n    \"rows\":[\n        [\n            4,\n            \"System\"\n        ],\n        [\n            276,\n            \"smss.exe\"\n        ],\n    ...\n    ],\n    \"columns\":[\n        \"PID\",\n        \"Name\"\n    ]\n    }\n\n#### Other interesting things\nNow we're doing useful work and using unified output, what else can we do? I'll teach you a couple interesting things but the best way to learn is by doing!\n\n##### Parameters\nA nice way to give your plugin some life is by taking parameters. We're gonna use the `__init__()` function for this. For example, we want to give our plugin a prefix for all processes' names. Why? Why not! Here's how we do it:\n\n    def __init__(self, config, *args, **kwargs):\n        common.AbstractWindowsCommand.__init__(self, config, *args, **kwargs)\n        self._config.add_option('PREFIX', short_option = 'P', default = None, help = 'Prefix all names.', \n            action = 'store')\n\n    def calculate(self):\n        addr_space = utils.load_as(self._config)\n        tasks = win32.tasks.pslist(addr_space)\n\n        prefix = self._config.PREFIX\n        if prefix:\n            for task in tasks:\n                task.ImageFileName = str(prefix) + task.ImageFileName\n        return tasks\n\nThis how we define a new parameter:\n\n - `'PREFIX'`: This is the parameter name, this way we'll be able to give the prefix via `--prefix \u003cprefix\u003e`.\n - `short_option = 'P'`: Why write `--prefix \u003cprefix\u003e` when we can do it shorter: `-P \u003cprefix\u003e`.\n - `default = None`: If no PREFIX parameter given, self._config.PREFIX will be whatever we put here. In this case: `None`\n - `help = 'Prefix all names'`: This is what will be written about this parameter when running the plugin with `-h` - or `--help` -.\n - `action = 'store'`: This indicates that the parameter takes a value behind, the prefix in this case. Instead of `store` you can use `store_true` (giving the parameter will make it store True, or False) or `append` (multiple parameters will stack: -P hehe -P you ) too.\n\n\n##### Use other plugins from inside yours\nPlease, don't reinvent the wheel. Volatility comes with a lot of plugins that might do part of the work your plugin needs, you simply need to call that other plugin from yours. Let's say you need a the Portable Executable that's in a process' address space, `procdump` can do it for you:\n\n    def build_conf(self):\n        # Create conf obj\n        procdump_conf = conf.ConfObject()\n\n        # Define conf\n        procdump_conf.readonly = {}\n        procdump_conf.PROFILE = self._config.PROFILE\n        procdump_conf.LOCATION = self._config.LOCATION\n        procdump_conf.DUMP_DIR = tempfile.mkdtemp()\n\n        return procdump_conf\n    \n    def calculate(self):\n        ...\n        procdump_conf = self.build_dump_conf()\n        # Run ProcDump\n        p = procdump.ProcDump(procdump_conf)\n        p.execute()\n        # It's done, get rid of it\n        del p\n        ...\n\nYou need to build a `ConfObject`, just like the one that Volatility gives you in `self._config`. Give that configuration object to the plugin you want to run, execute it and remember to delete it afterwards - you no longer need it.\n\n## Other community plugins\n\nA nice way to learn and see what others can come up with wile developing a plugin is navigation through the [Community Plugins GitHub](https://github.com/volatilityfoundation/community).","funding_links":[],"categories":["Volatility plugins"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FiAbadia%2FVolatility-Plugin-Tutorial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FiAbadia%2FVolatility-Plugin-Tutorial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FiAbadia%2FVolatility-Plugin-Tutorial/lists"}