Files
MemProcFS-plugins/files/plugins/pym_procstruct/pym_procstruct.py
2018-12-05 13:49:54 +01:00

203 lines
8.8 KiB
Python

# pym_procstruct.py
#
# Example python plugin module for the memory process file system. This module
# shows how it is possible to display files in the file system and implement
# Read and Write functionality for the files.
#
# The module displays binary and hexascii versions of EPROCESS and PEB in the
# 'py/procstruct' directory.
#
# https://github.com/ufrisk/
#
# (c) Ulf Frisk, 2018
# Author: Ulf Frisk, pcileech@frizk.net
#
from vmmpy import *
from vmmpyplugin import *
procstruct_eprocess_size_bin = 0x500
procstruct_eprocess_size_hex = 0
procstruct_peb_size_bin = 0x1000
procstruct_peb_size_hex = 0
procstruct_cache_proc_wow64 = {}
def ReadEPROCESS_Binary(pid, file_name, file_attr, bytes_length, bytes_offset):
#
# Read binary data from the EPROCESS file that the List function put into
# the VmmPyPlugin file list.
#
# Since this function was only put into the VmmPyPlugin file list for this
# specific file no extra checks have to be made on pid (which could be none
# if this plugin would have registered the file entry as a root file -
# which it did not). Neither does the file_name need to be checked since
# this is the only file that will call this read function.
#
# The plugin manager also checks that bytes_length and bytes_offset does
# not exceed the file size that is registered in the plugin manager - if
# it does they are automatically adjusted - so no need to check those for
# validity either.
#
# Start by retrieving the process information for the given pid.
procinfo = VmmPy_ProcessGetInformation(pid)
# The procinfo is a dict that amongst other entries contains 'va-eprocess'
# which is the virtual address for the eprocess struct in kernel memory.
va_eprocess = procinfo['va-eprocess']
# Read the amount of bytes from virtual memory with the specified offset.
# Since EPROCESS resides in kernel memory (which is mapped into process
# address space as supervisor only memory, but is filtered out by the
# VMM) it is necessary to read it from the SYSTEM process - i.e. pid 4.
memory_data = VmmPy_MemRead(4, va_eprocess + bytes_offset, bytes_length)
# The read memory data should be of the correct size and correct offset,
# and it's also already of the bytes data type - so just return it and
# finish with the read!
return memory_data
def ReadEPROCESS_Hexdump(pid, file_name, file_attr, bytes_length, bytes_offset):
# Read the binary data required for the EPROCESS struct by calling the
# function ReadEPROCESS_Binary which already does this very conveniently.
memory_data = ReadEPROCESS_Binary(pid, file_name, file_attr, bytes_length, bytes_offset)
# Translate the binary data into hexascii memory dump format by calling
# the VmmPy_UtilFillHexAscii function.
hexdump_string = VmmPy_UtilFillHexAscii(memory_data)
# Convert from string into bytes using ascii encoding.
hexdump_binary = bytes(hexdump_string, 'ascii')
# return the data that should be read as a bytes object.
return hexdump_binary[bytes_offset:bytes_length+bytes_offset]
def WriteEPROCESS_Binary(pid, file_name, file_attr, bytes_data, bytes_offset):
# Write binary data to the EPROCESS struct in kernel memory.
#
# Since the List function only registered this function with one file which
# is in a process directory we do not need to check pid, file_name and
# bytes_offset since thise are all verified by the plugin manager.
#
# Start by retrieving the process information for the given pid.
procinfo = VmmPy_ProcessGetInformation(pid)
# The procinfo is a dict that amongst other entries contains 'va-eprocess'
# which is the virtual address for the eprocess struct in kernel memory.
va_eprocess = procinfo['va-eprocess']
# Now all data which is required to make a write exists! Perform the write!
VmmPy_MemWrite(4, va_eprocess+bytes_offset, bytes_data)
return VMMPY_STATUS_SUCCESS
def ReadPEB_Binary(pid, file_name, file_attr, bytes_length, bytes_offset):
#
# Read binary data from the PEB page. This is a compact version of the
# Read function. Please see ReadEPROCESS_Binary for a detailed description
#
procinfo = VmmPy_ProcessGetInformation(pid)
if '32' in file_name:
va_peb = procinfo['va-peb32']
else:
va_peb = procinfo['va-peb']
return VmmPy_MemRead(pid, va_peb + bytes_offset, bytes_length)
def ReadPEB_Hexdump(pid, file_name, file_attr, bytes_length, bytes_offset):
#
# Read hexascii data from the PEB page. This is a compact version of the
# Read function. Please see ReadEPROCESS_Hexdump for a detailed description
#
memory_data = ReadPEB_Binary(pid, file_name, file_attr, bytes_length, bytes_offset)
hexdump_string = VmmPy_UtilFillHexAscii(memory_data)
return bytes(hexdump_string, 'ascii')[bytes_offset:bytes_length+bytes_offset]
def WritePEB_Binary(pid, file_name, file_attr, bytes_data, bytes_offset):
#
# Write binary data to the PEB page. This is a compact version of the
# Write function. Please see WritePEB_Binary for a detailed description
#
procinfo = VmmPy_ProcessGetInformation(pid)
if '32' in file_name:
va_peb = procinfo['va-peb32']
else:
va_peb = procinfo['va-peb']
VmmPy_MemWrite(pid, va_peb+bytes_offset, bytes_data)
return VMMPY_STATUS_SUCCESS
def IsProcessPeb6432(pid):
#
# Check if the pid is a regular process with a PEB at all or if it's a special
# process like 'System' without a PEB. Also check whether a 32-bit PEB exists
# or not (wow64 process).
# Also employ a small caching functionality.
#
if pid in procstruct_cache_proc_wow64:
return procstruct_cache_proc_wow64[pid]
procinfo = VmmPy_ProcessGetInformation(pid)
if(procinfo['state'] != 0):
result = False, False
else:
result = procinfo['va-peb'] > 0, ('va-peb32' in procinfo and procinfo['va-peb32'] > 0)
procstruct_cache_proc_wow64[pid] = result
return result
def List(pid, path):
#
# List function - this module employs a dynamic list function - which makes
# it responsible for providing directory listings of its contents in a
# highly optimized way. It is very important that the List function is as
# speedy as possible - to avoid locking up the file system.
#
# First check the directory to be listed. Only the module root directory is
# allowed. If it's not the module root directory return None.
if path != 'procstruct':
return None
# Populate the 'common' files which are always in the module directory
# below. Both binary and hexdump/hexascii versions are populated.
result = {
'eprocess.bin': {'size': procstruct_eprocess_size_bin, 'read': ReadEPROCESS_Binary, 'write': WriteEPROCESS_Binary},
'eprocess.txt': {'size': procstruct_eprocess_size_hex, 'read': ReadEPROCESS_Hexdump, 'write': None}
}
# Populate PEB (if it exists), it almost always do, but there may be
# some special processes like 'System', 'Registry' and so on that only
# exist in kernel space without a PEB...
fPeb64, fPeb32 = IsProcessPeb6432(pid)
if fPeb64:
result['peb.bin'] = {'size': procstruct_peb_size_bin, 'read': ReadPEB_Binary, 'write': WritePEB_Binary}
result['peb.txt'] = {'size': procstruct_peb_size_hex, 'read': ReadPEB_Hexdump, 'write': None}
# Optionally populate 32-bit PEBs into the directory listings if a 32-bit
# process is to be listed.
if fPeb32:
result['peb32.bin'] = {'size': procstruct_peb_size_bin, 'read': ReadPEB_Binary, 'write': WritePEB_Binary}
result['peb32.txt'] = {'size': procstruct_peb_size_hex, 'read': ReadPEB_Hexdump, 'write': None}
return result
def Close():
# Nothing to clean up here for this plugin -> do nothing!
pass
def Initialize(target_system, target_memorymodel):
# Check that the operating system is 32-bit or 64-bit Windows. If it's not
# then raise an exception to terminate loading of this module.
if target_system != VMMPY_SYSTEM_WINDOWS_X64 and target_system != VMMPY_SYSTEM_WINDOWS_X86:
raise RuntimeError("Only Windows is supported by the pym_procstruct module.")
# Calculate the size of the 'eprocess_size_hex' global variable. This is
# only done once - at module instantiation to speed up the list operation.
global procstruct_eprocess_size_hex
procstruct_eprocess_size_hex = len(VmmPy_UtilFillHexAscii(bytes(procstruct_eprocess_size_bin)))
# Calculate the size of the 'PEB page'
global procstruct_peb_size_hex
procstruct_peb_size_hex = len(VmmPy_UtilFillHexAscii(bytes(procstruct_peb_size_bin)))
# Register a directory with the VmmPyPlugin plugin manager. The directory
# is a non-root (i.e. a process) directory and have a custom List function.
VmmPyPlugin_FileRegisterDirectory(False, 'procstruct', List)