Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rekall python3 #55

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ It will receive the events generated by KVM and display them.
- `python 3`
- `docopt`
- `libvirt`
- `ioctl-opt Python 3`
- `cffi Python3` (optional)
- `ioctl-opt`
- `cffi` (optional)
- `libvmi` (optional)
- `rekall` (optional)
- `rekall >= 1.7.1` (optional)

# Setup

Expand Down Expand Up @@ -136,15 +136,15 @@ informations, such as:

## Rekall

`Rekall` is used in `symbols.py` to extract the syscall table from
`Rekall` is used to extract the syscall table from
the memory dump.

Unfortunately, `Rekall` is not available as a Debian package.
For now you will have to install it system-wide with `pip`.

~~~
$ sudo pip3 install --upgrade setuptools pip wheel
$ sudo pip3 install rekall
$ sudo pip3 install rekall-agent rekall
~~~

## libvmi
Expand Down
112 changes: 91 additions & 21 deletions nitro/backends/windows/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
import re
import stat
import os
import subprocess
import shutil
import json

from io import StringIO
from collections import defaultdict
from rekall import session
from rekall import plugins
from tempfile import NamedTemporaryFile, TemporaryDirectory


import libvirt

from nitro.event import SyscallDirection
Expand All @@ -16,8 +18,6 @@
from nitro.backends.backend import Backend
from nitro.backends.windows.arguments import WindowsArgumentMap

GETSYMBOLS_SCRIPT = 'get_symbols.py'


class WindowsBackend(Backend):
__slots__ = (
Expand All @@ -38,15 +38,18 @@ def __init__(self, domain, libvmi, listener, syscall_filtering=True):
# create on syscall stack per vcpu
self.syscall_stack = tuple([] for _ in range(self.nb_vcpu))
self.sdt = None
self.load_symbols()
symbols = self.get_symbols()
self.load_symbols(symbols)
# save symbols
self.symbols = symbols

# get offsets
self.tasks_offset = self.libvmi.get_offset("win_tasks")
self.pdbase_offset = self.libvmi.get_offset("win_pdbase")

self.processes = {}

def load_symbols(self):
def get_symbols(self):
# we need to put the ram dump in our own directory
# because otherwise it will be created in /tmp
# and later owned by root
Expand All @@ -62,18 +65,34 @@ def load_symbols(self):
flags = libvirt.VIR_DUMP_MEMORY_ONLY
dumpformat = libvirt.VIR_DOMAIN_CORE_DUMP_FORMAT_RAW
self.domain.coreDumpWithFormat(ram_dump.name, dumpformat, flags)
# build symbols.py absolute path
script_dir = os.path.dirname(os.path.realpath(__file__))
symbols_script_path = os.path.join(script_dir,
GETSYMBOLS_SCRIPT)
# call rekall on ram dump
logging.info('Extracting symbols with Rekall')
python2 = shutil.which('python2')
symbols_process = [python2, symbols_script_path, ram_dump.name]
output = subprocess.check_output(symbols_process)
logging.info('Loading symbols')
# load output as json
symbols = json.loads(output.decode('utf-8'))
home = os.getenv('HOME')
# we need to make sure the directory exists otherwise rekall will complain
# when we specify it in the profile_path
local_cache_path = os.path.join(home, '.rekall_cache')
try:
os.makedirs(local_cache_path)
except OSError: # already exists
pass

s = session.Session(
filename=ram_dump.name,
autodetect=["rsds"],
logger=logging.getLogger(),
autodetect_build_local='none',
format='data',
profile_path=[
local_cache_path,
"http://profiles.rekall-forensic.com"
])

symbols = {}
output = StringIO()
s.RunPlugin("ssdt", output=output)
symbols['syscall_table'] = json.loads(output.getvalue())
symbols['offsets'] = self.get_offsets(s)
return symbols

def load_symbols(self, symbols):
# load ssdt entries
nt_ssdt = {'ServiceTable': {}, 'ArgumentTable': {}}
win32k_ssdt = {'ServiceTable': {}, 'ArgumentTable': {}}
Expand All @@ -92,8 +111,59 @@ def load_symbols(self):
# add entry to our current ssdt
cur_ssdt[entry] = full_name
logging.debug('Add SSDT entry [%s] -> %s', entry, full_name)
# save rekall symbols
self.symbols = symbols


def get_offsets(self, session):
offsets = defaultdict(dict)

offsets['KPROCESS'][
'DirectoryTableBase'] = session.profile.get_obj_offset('_KPROCESS',
'DirectoryTableBase')

offsets['EPROCESS'][
'ActiveProcessLinks'] = session.profile.get_obj_offset('_EPROCESS',
'ActiveProcessLinks')

offsets['EPROCESS']['ImageFileName'] = session.profile.get_obj_offset(
'_EPROCESS',
'ImageFileName')

offsets['EPROCESS']['UniqueProcessId'] = session.profile.get_obj_offset(
'_EPROCESS',
'UniqueProcessId')

offsets['EPROCESS']['InheritedFromUniqueProcessId'] = \
session.profile.get_obj_offset('_EPROCESS',
'InheritedFromUniqueProcessId')

offsets['EPROCESS']['Wow64Process'] = \
session.profile.get_obj_offset('_EPROCESS', 'Wow64Process')

offsets['EPROCESS']['CreateTime'] = \
session.profile.get_obj_offset('_EPROCESS', 'CreateTime')

offsets['EPROCESS']['SeAuditProcessCreationInfo'] = \
session.profile.get_obj_offset('_EPROCESS',
'SeAuditProcessCreationInfo')

offsets['SE_AUDIT_PROCESS_CREATION_INFO']['ImageFileName'] = \
session.profile.get_obj_offset('_SE_AUDIT_PROCESS_CREATION_INFO',
'ImageFileName')

offsets['OBJECT_NAME_INFORMATION']['Name'] = \
session.profile.get_obj_offset('_OBJECT_NAME_INFORMATION', 'Name')

offsets['EPROCESS']['Peb'] = session.profile.get_obj_offset('_EPROCESS',
'Peb')

offsets['PEB']['ProcessParameters'] = \
session.profile.get_obj_offset('_PEB', 'ProcessParameters')

offsets['RTL_USER_PROCESS_PARAMETERS']['CommandLine'] = \
session.profile.get_obj_offset('_RTL_USER_PROCESS_PARAMETERS',
'CommandLine')

return offsets

def process_event(self, event):
# invalidate libvmi cache
Expand Down
101 changes: 0 additions & 101 deletions nitro/backends/windows/get_symbols.py

This file was deleted.