Menulis Plugin Volatility 3 Pertamamu
oleh Rifqi Ardia Ramadhan
Volatility adalah sebuah tools forensik yang terkenal akan kemampuannya untuk memproses hasil dump memory agar dapat mengambil suatu informasi terkait device asal memory dump tersebut diambil. Volatility 2 seringkali jadi pilihan utama ketika menganalisis memory, dikarenakan luasnya penggunaan tools versi ini. Namun demikian, Volatility 2 menuju senja – alias uzur – bersamaan dengan dihentikannya support untuk Python 2, sehingga mulai banyak yang melakukan migrasi ke Volatility 3 yang didasarkan pada Python 3. Dengan peningkatan performa dan standarisasi model plugin yang lebih efisien, membuat plugin untuk Volatility 3 tidak akan serumit versi sebelumnya. Di sini, kita akan mencoba untuk membuat plugin Volatility 3 sederhana dalam upaya memahami cara kerja plugin, dengan harapan dapat membuat plugin yang lebih kompleks dan bisa dimanfaatkan oleh seluruh pengguna Volatility 3.
Sebelum memulai, dibutuhkan keahlian dasar pemrograman Python 3 – tentu saja karena Volatility 3 dibuat di atas Python 3, jadi plugin juga berdasarkan pemrograman tersebut. Hal kedua yang dibutuhkan adalah sampel memory dari mesin Windows. Opsi IDE yang digunakan bisa menggunakan apapun, namun di sini kita akan menggunakan VS Code.
Dalam artikel ini kita akan membuat plugin Volatility 3 untuk sistem operasi versi Windows agar mempermudah anda untuk mengikuti proses dalam tutorial ini dan memiliki lingkungan yang sama (dikarenakan sistem operasi non-Windows perlu menyertakan versi sistem operasi yang unik dan mengambil sampel kernel debugging yang belum tentu ada di tiap versi kernel).
Kita akan membuat plugin sederhana yang membuat daftar proses dan PID yang ada di dalam memory dump.
Langkah pertama adalah membuat file
.py
di dalam foldervolatility3/plugins/windows
. Nama ini akan menjadi parameter yang dipanggil ketika menggunakan plugin, jadi usahakan sama dengan nama Class untuk memudahkan identifikasi saat pengembangan plugin. Kita coba buat filetesting.py
.

Kemudian di dalam file
.py
tersebut, buat sebuah class yang menurunkan dari interfacePluginInterface
. Pastinyaplugins
tidak di-recognize oleh Python, jadi kita perlu melakukan import terhadapplugins
divolatility3.framework.interfaces
. Kita namakan class tersebutTesting
, karena kita sedang mencoba-coba membuat plugin.
from volatility3.framework.interfaces import plugins
class Testing(plugins.PluginInterface):
_required_framework_version = (2, 0, 0)
Saat membuat class, framework version perlu disesuaikan dengan versi Volatility 3 saat ini. Untuk sekarang kita buat value
_required_framework_version
menjadi(2, 0, 0)
. Jika setelah kita selesai membuat plugin ini dan muncul error di framework version ketika proses testing, ubah value menjadi(1, 0, 0)
, tergantung dari value yang diminta ketika error muncul.Contoh error:
RuntimeError: Framework interface version 2 is incompatible with required version 1.
Pada dasarnya dalam mengembangkan plugin, hanya ada 3 hal yang perlu ditulis, yakni
Requirement. Requirement berisikan syarat library, dependency, dan input parameter agar bisa menjalankan plugin ini. Biasanya jika ada syarat dependency yang tak terpenuhi, Volatility tidak akan menuliskan plugin tersebut di daftar runnable plugin (ketika menjalankan
vol.py --help
). Hal ini juga diperlukan untuk mewadahi proses error handling. Jika terdapat parameter yang belum terpenuhi, maka Volatility akan return error.Run. Run merupakan fungsi pertama yang dijalankan saat plugin dijalankan. Dijalankan, setelah requirement terpenuhi, fungsi
run()
akan langsung dijalankan. Misalkan baris perintah berikut ini:
vol.py -f xxx windows.testing
Generator. Generator digunakan sebagai fungsi untuk membuat list data yang di-return setelah pemrosesan. Digunakan di dalam run untuk generate list hasil pemrosesan.
Kita mulai dari hal yang pertama, yakni Requirement.
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [
requirements.TranslationLayerRequirement(name = 'primary',
description = 'Memory layer for the kernel',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolTableRequirement(name = "nt_symbols",
description = "Windows kernel symbols"),
Yang dibutuhkan agar kode tersebut berjalan adalah dependency untuk requirement tersebut. Berikut merupakan contoh dependency yang dibutuhkan:
from volatility3.framework.configuration import requirements
from volatility3.framework import interfaces
from typing import List
Secara umum terdapat dua pendekatan dalam menulis requirement untuk plugin Windows. Pertama adalah menulis requirement module kernel
. Jika menggunakan metode ini, kita perlu mengisi parameter pada module lain yang diperlukan untuk dapat menggunakan module tersebut. Misalnya pada module pslist
diperlukan parameter context
, layer_name
, dan symbol_table
. Untuk dapat menggunakan module ini dengan pendekatan pertama, module dapat dipakai dengan:
...
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [
requirements.ModuleRequirement(name="kernel",
description="Windows kernel",
architectures=["Intel32", "Intel64"],
)
]
...
kernel = self.context.modules[self.config["kernel"]]
tasks = pslist.PsList.list_processes(self.context, kernel.layer_name, kernel.symbol_table_name)
Pendekatan kedua adalah dengan secara eksplisit menuliskan layer_name
dan symbol_table
sebagai context. Sehingga penulisan pendekatannya akan menjadi seperti berikut:
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [
requirements.TranslationLayerRequirement(name = 'primary',
description = 'Memory layer for the kernel',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolTableRequirement(name = "nt_symbols",
description = "Windows kernel symbols"),
]
...
tasks = pslist.PsList.list_processes(self.context, self.config['primary'],
self.config['nt_symbols'])
Untuk artikel ini kita akan memakai pendekatan pertama karena module kernel
sedikit lebih lengkap dan lebih mudah dipahami.
Selanjutnya adalah Run.
Pada dasarnya, kode yang ada di sini disesuaikan dengan kebutuhan dari pembuat plugin, dan juga logic program utama bisa diletakkan di sini. Dalam konteks artikel ini, karena kita akan mencoba untuk melakukan proses listing, kita membutuhkan fungsi list_processes
dari module pslist
.
Jika merunut dengan pendekatan Requirements pertama, maka bentuknya akan seperti ini:
from volatility3.plugins.windows import pslist
...
def run(self):
kernel = self.context.modules[self.config["kernel"]]
tasks = pslist.PsList.list_processes(self.context, kernel.layer_name, kernel.symbol_table_name)
Jika kita membutuhkan module lain, kita juga bisa include dengan meng-import module tersebut. Kita perlu membaca tiap file plugin untuk bisa tahu fungsi yang dapat kita manfaatkan.
Di bagian paling akhir, untuk melakukan render text, diperlukan module renderers
untuk melakukan printing rapi dari hasil analisis. Di sini kita akan menggunakan TreeGrid, yang merupakan default output dari plugin Volatility 3 pada umumnya.
from volatility3.framework import renderers
...
return renderers.TreeGrid([("PID", str), ("Image", str)], self._generator(tasks))
renderers.TreeGrid()
menerima dua parameter, parameter pertama adalah List yang berisikan Tuple (“nama kolom”, tipedata)
yang ukurannya disesuaikan dengan bentuk output, dan self._generator()
yang merupakan fungsi Generator yang akan dibahas selanjutnya.
Dan yang terakhir adalah Generator.
Generator adalah, sesuai namanya, generator untuk hasil analisis.
def _generator(self, data):
for task in data:
yield (0, [
int(task.UniqueProcessId),
task.ImageFileName.cast("string",
max_length = task.ImageFileName.vol.count,
errors = 'replace')
])
Fungsi _generator()
bisa diberikan parameter sesuai kebutuhan. Dalam konteks ini karena kita akan melakukan listing dari proses, kita memerlukan satu parameter, sebut saja data
, dan melihat dari fungsi sebelumnya, data adalah tasks
. Output dari pslist (`tasks`)
sebelumnya adalah Iterable
, sehingga untuk dapat melihat semua list proses dari tasks
, perlu dilakukan iterasi dengan menggunakan looping for task in data:
.
Output yang diperlukan dari _generator()
adalah List
dari (0, [output1, output2,…])
. Nilai ini bisa di-provide dengan return
maupun yield
. PID dapat diambil dari task
dengan UniqueProcessId
yang di-cast dalam int
, sedangkan nama executable dapat diambil dengan ImageFileName
yang di-cast ke string
, dan ditentukan max_length
nya.
Sehingga seluruh code untuk testing.py
akan berbentuk seperti ini:
from typing import List
from volatility3.framework import renderers, interfaces
from volatility3.framework.configuration import requirements
from volatility3.framework.interfaces import plugins
from volatility3.plugins.windows import pslist
class Testing(plugins.PluginInterface):
_required_framework_version = (2, 0, 0)
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [
requirements.ModuleRequirement(name="kernel",
description="Windows kernel",
architectures=["Intel32", "Intel64"],
)
]
def run(self):
kernel = self.context.modules[self.config["kernel"]]
tasks = pslist.PsList.list_processes(self.context,
kernel.layer_name,
kernel.symbol_table_name)
return renderers.TreeGrid([("PID", int), ("Image", str),], self._generator(tasks))
def _generator(self, data):
for task in data:
yield (0, [
int(task.UniqueProcessId),
task.ImageFileName.cast("string",
max_length = task.ImageFileName.vol.count,
errors = 'replace')
])
Untuk mencoba kode tersebut, maka kita jalankan dengan perintah berikut:
`python vol.py -f <sample memory> windows.testing`
kemudian akan muncul informasi sebagai berikut:
PID Image
4 System
296 smss.exe
380 csrss.exe
416 csrss.exe
424 wininit.exe
448 winlogon.exe
512 services.exe
520 lsass.exe
528 lsm.exe
...
Selamat! Kalian telah membuat plugin Volatility 3 dalam 30-an baris kode saja. Sekarang dengan pemahaman dasar ini, kita bisa mengeksplor lebih jauh fungsi-fungsi yang telah disediakan oleh Volatility 3 dan mengembangkan lebih jauh untuk keperluan memory analysis.
Referensi
Tentang Penulis
Rifqi Ardia Ramadhan adalah seorang cybersecurity engineer, saat ini bekerja di Blibli.com. Lulusan Informatika Institut Teknologi Sepuluh Nopember, di sana dia menggeluti dasar pemrograman, terutama Python. Selama 2 tahun bekerja sambil mempelajari bidang keamanan blue team, baik dari sisi end-user maupun enterprise. Di waktu luangnya dia mengikuti lomba Capture the Flag yang berfokus pada defensive security dan post-mortem machine analysis. Saat ini sedang berfokus meneliti tentang memory dump analysis dan pengembangan toolsnya di Python.
Last updated