Project Name | Stars | Downloads | Repos Using This | Packages Using This | Most Recent Commit | Total Releases | Latest Release | Open Issues | License | Language |
---|---|---|---|---|---|---|---|---|---|---|
Core | 2,494 | 15 | 2 | 3 years ago | 42 | April 04, 2017 | 180 | other | JavaScript | |
Cloud9 Core - Part of the Cloud9 SDK for Plugin Development https://c9.github.io/core/ https://c9.io | ||||||||||
Trip.js | 785 | 4 | 3 | 5 years ago | 8 | April 23, 2019 | 30 | mit | JavaScript | |
🚀 Trip.js is a plugin that can help you customize a tutorial trip easily with more flexibilities. | ||||||||||
Clang Tutor | 514 | 5 months ago | 8 | unlicense | C++ | |||||
A collection of out-of-tree Clang plugins for teaching and learning | ||||||||||
Artsy Webpack Tour | 456 | 5 years ago | 5 | |||||||
Annotations on webpack source code in a pseudo-guided fashion. | ||||||||||
Wordpress Plugin Boilerplate Tutorial | 338 | 2 months ago | 4 | gpl-3.0 | JavaScript | |||||
Tutorials and Examples for WordPress Plugin Boilerplate, a foundation for WordPress Plugin Development. | ||||||||||
Vitacheat | 301 | 6 months ago | 53 | |||||||
FinalCheat / VitaCheat Database | ||||||||||
Awesome Remnote | 290 | 2 months ago | 4 | CSS | ||||||
A curated list of RemNote plugins, themes, tutorials and other resources. | ||||||||||
Hapijs.com | 186 | 4 years ago | other | JavaScript | ||||||
The hapijs.com website | ||||||||||
Wp_oxygen | 186 | 6 months ago | 2 | JavaScript | ||||||
🎓 Free tutorials for the WordPress plugin Oxygen Builder. Includes both WordPress and WooCommerce. | ||||||||||
Eslint Plugin Tutorial | 184 | a month ago | 12 | mit | TypeScript | |||||
A tutorial/template repository to explain how to create your eslint plugins |
Developing 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 book, /r/memoryforensics, Volatility Labs blog, etc.) but very few - almost none - where you can learn how to develop a plugin. Also, this tutorial inspired me to make a more complex one, and here we are!
I'll be working on a x64 Ubuntu 16.04 machine.
In this guide you will learn the following:
Be sure to have Oracle's VirtualBox 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. Yes, for real.
The latest Volatility stable version cat be found at The Volatility Foundation GitHub. You can just clone the repository via:
$ git clone https://github.com/volatilityfoundation/volatility.git
Now 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!
You 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, but, long story short:
$ vboxmanage debugvm <VM-NAME> dumpvmcore --filename vm.elf
Well, 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.
Now 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:
$ python vol.py -f <vm-dump> --profile=<vm-profile> pslist
Let's elaborate.
$ python vol.py --info
. I recommend the latter.Since I extracted a Windows 7 SP1 x64 memory dump, I would run:
$ python vol.py -f /home/user/vmcore.elf --profile=Win7SP1x64 pslist
Volatility Foundation Volatility Framework 2.6
Offset(V) Name PID PPID Thds Hnds Sess Wow64 Start Exit
------------------ -------------------- ------ ------ ------ -------- ------ ------ ------------------------------ ------------------------------
0xffffc480b4258040 System 4 0 107 0 ------ 0 2017-07-13 18:18:39 UTC+0000
0xffffc480b45a6040 smss.exe 296 4 4 0 ------ 0 2017-07-13 18:18:39 UTC+0000
0xffffc480b5b1e7c0 csrss.exe 384 368 11 0 0 0 2017-07-13 18:18:42 UTC+0000
0xffffc480b62d3080 smss.exe 448 296 0 -------- 1 0 2017-07-13 18:18:42 UTC+0000 2017-07-13 18:18:42 UTC+0000
0xffffc480b62d6080 wininit.exe 456 368 4 0 0 0 2017-07-13 18:18:42 UTC+0000
0xffffc480b62d9580 csrss.exe 464 448 12 0 1 0 2017-07-13 18:18:42 UTC+0000
0xffffc480b630d080 winlogon.exe 516 448 5 0 1 0 2017-07-13 18:18:42 UTC+0000
...
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.
You 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
.
Now 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:
cd ~
mkdir myPlugin
cd myPlugin
Now 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
.
vim myplugin.py
Ok, I was kidding, I'm not using vim for this. Any text editor will do: gedit, ATOM, VS Code, Sublime Text, etc.
We'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:
import volatility.plugins.common as common
class MyPlugin(common.AbstractWindowsCommand):
def render_text(self, outfd, data):
print "Hello world!"
Let me elaborate:
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.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.Now we can run it with:
$ python vol.py --plugins=<myplugin-dir> -f <vm-dump> --profile=<vm-profile> myplugin
Note that the --plugins
argument must be passed right after vol.py
, doing it elsewhere wont' work. Also, the path provided must be absolute:
python vol.py --plugins=/home/user/volpydev/myplugin -f <vmcore> myplugin
python vol.py -f <vmcore> --plugins=/home/user/volpydev/myplugin myplugin
python vol.py --plugins=volpydev/myplugin -f <vmcore> myplugin
Now we're going to make something with that memory dump and output something more useful than Hello world!
. Take this code:
import volatility.plugins.common as common
import volatility.utils as utils
import volatility.win32 as win32
class MyPlugin(common.AbstractWindowsCommand):
''' My plugin '''
def calculate(self):
addr_space = utils.load_as(self._config)
tasks = win32.tasks.pslist(addr_space)
return tasks
def render_text(self, outfd, data):
for task in data:
outfd.write("{!s}\n".format(str(task)))
Important things here:
''' My plugin'''
: This docstring will be written when calling your plugin with -h
(or --help
)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).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.Now you can run it and see what it prints:
$ python vol.py --plugins=<myplugin-dir> -f <vmcore> --profile=<vmcore-profile> myplugin
Volatility Foundation Volatility Framework 2.6
2223228800
2238952424
2246946864
2238545192
2223723360
2238307624
2247151664
2247164976
...
This looks good, but we can do better! Let's print something that we can understand, take this render_text
function:
def render_text(self, outfd, data):
for task in data:
outfd.write("{0} {1}\n".format(task.UniqueProcessId, task.ImageFileName))
This will print every process's PID next to it's name. Go check it!
$ python vol.py --plugins=<myplugin-dir> -f <vmcore> --profile=<vmcore-profile> myplugin
Volatility Foundation Volatility Framework 2.6
4 System
276 smss.exe
352 csrss.exe
388 wininit.exe
396 csrss.exe
436 winlogon.exe
480 services.exe
488 lsass.exe
496 lsm.exe
604 svchost.exe
...
Now 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!
Here 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 - and do it as you like. Defining render_x()
actually overrides the default printing function. We've already done that, remember?
As 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:
def generator(self, data):
for task in data:
yield (0, [
int(task.UniqueProcessId),
str(task.ImageFileName)
])
def unified_output(self, data):
tree = [
("PID", int),
("Name", str)
]
return TreeGrid(tree, self.generator(data))
As 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:
import volatility.plugins.common as common
import volatility.utils as utils
import volatility.win32 as win32
from volatility.renderers import TreeGrid
class MyPlugin(common.AbstractWindowsCommand):
''' My plugin '''
def calculate(self):
addr_space = utils.load_as(self._config)
tasks = win32.tasks.pslist(addr_space)
return tasks
def generator(self, data):
for task in data:
yield (0, [
int(task.UniqueProcessId),
str(task.ImageFileName)
])
def unified_output(self, data):
tree = [
("PID", int),
("Name", str)
]
return TreeGrid(tree, self.generator(data))
Now 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):
$ python vol.py --plugins=<myplugin-dir> -f <vmcore> --profile=<vmcore-profile> myplugin --output=json
Volatility Foundation Volatility Framework 2.6
{
"rows":[
[
4,
"System"
],
[
276,
"smss.exe"
],
...
],
"columns":[
"PID",
"Name"
]
}
Now 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!
A 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:
def __init__(self, config, *args, **kwargs):
common.AbstractWindowsCommand.__init__(self, config, *args, **kwargs)
self._config.add_option('PREFIX', short_option = 'P', default = None, help = 'Prefix all names.',
action = 'store')
def calculate(self):
addr_space = utils.load_as(self._config)
tasks = win32.tasks.pslist(addr_space)
prefix = self._config.PREFIX
if prefix:
for task in tasks:
task.ImageFileName = str(prefix) + task.ImageFileName
return tasks
This how we define a new parameter:
'PREFIX'
: This is the parameter name, this way we'll be able to give the prefix via --prefix <prefix>
.short_option = 'P'
: Why write --prefix <prefix>
when we can do it shorter: -P <prefix>
.default = None
: If no PREFIX parameter given, self._config.PREFIX will be whatever we put here. In this case: None
help = 'Prefix all names'
: This is what will be written about this parameter when running the plugin with -h
- or --help
-.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.Please, 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:
def build_conf(self):
# Create conf obj
procdump_conf = conf.ConfObject()
# Define conf
procdump_conf.readonly = {}
procdump_conf.PROFILE = self._config.PROFILE
procdump_conf.LOCATION = self._config.LOCATION
procdump_conf.DUMP_DIR = tempfile.mkdtemp()
return procdump_conf
def calculate(self):
...
procdump_conf = self.build_dump_conf()
# Run ProcDump
p = procdump.ProcDump(procdump_conf)
p.execute()
# It's done, get rid of it
del p
...
You 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.
A nice way to learn and see what others can come up with wile developing a plugin is navigation through the Community Plugins GitHub.