mirror of
https://github.com/ufrisk/MemProcFS-plugins.git
synced 2026-05-06 22:02:04 +08:00
Plugin Update
This commit is contained in:
17
README.md
17
README.md
@@ -4,23 +4,6 @@ This repository contains various non-core plugins for [MemProcFS - The Memory Pr
|
||||
|
||||
Plugins range from non-core plugins to plugins that have offensive capabilities - such as _pypykatz_. Please find a short description for each plugin below:
|
||||
|
||||
## pypykatz
|
||||
|
||||
#### Author:
|
||||
Tamas Jos ([@skelsec](https://twitter.com/SkelSec)) , info@skelsec.com , https://github.com/skelsec/
|
||||
|
||||
#### Overview:
|
||||
_pypykatz_ for MemProcFS exposes mimikatz functionality in the folder `/py/secrets/` in the file system root provided that the target is a supported Windows system. Functionality includes retrieval of hashes, passwords, kerberos tickets and various other credentials.
|
||||
<p align="center"><img src="https://raw.githubusercontent.com/wiki/ufrisk/MemProcFS-plugins/resources/p_pypykatz_1.png" height="375"/></p>
|
||||
|
||||
#### Installation instructions:
|
||||
1) Ensure MemProcFS supported version of 64-bit Python for Windows is on the system path (or specify in `-pythonpath` option when starting MemProcFS). NB! embedded Python will not work with _pypykatz_ since it requires access to Python pip installed packages.
|
||||
2) Install _pypykatz_ pip package, in correct python environment, by running `pip install dissect.cstruct pypykatz`.
|
||||
3) Copy the _pypykatz_ for _MemProcFS_ plugin by copying all files from [`/files/plugins/pym_pypykatz`](https://github.com/ufrisk/MemProcFS-plugins/tree/master/files/plugins/pym_pypykatz) to corresponding folder in MemProcFS - overwriting any existing files there.
|
||||
4) Start MemProcFS.
|
||||
|
||||
#### Last updated: 2021-03-21
|
||||
|
||||
## pypykatz regsecrets
|
||||
|
||||
#### Author:
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
from plugins.pym_pypykatz.pym_pypykatz import (
|
||||
Initialize,
|
||||
Notify,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"Initialize",
|
||||
"Notify",
|
||||
]
|
||||
@@ -1,296 +0,0 @@
|
||||
# pym_pypykatz.py
|
||||
#
|
||||
# Pypykatz plugin for MemProcFss
|
||||
#
|
||||
# https://github.com/skelsec/
|
||||
#
|
||||
# (c) Tamas Jos, 2019
|
||||
# Author: Tamas Jos (@skelsec), info@skelsec.com
|
||||
#
|
||||
|
||||
import memprocfs
|
||||
from vmmpyplugin import *
|
||||
import json
|
||||
import traceback
|
||||
import datetime
|
||||
|
||||
# globals needed for FS
|
||||
all_secrets = '' #all secrets extracted from lsass in json format
|
||||
luids = {} #secrets per-luid (logon session) in txt format
|
||||
domains = {}
|
||||
kerberos = {}
|
||||
|
||||
first_run = True
|
||||
|
||||
import_failed = None
|
||||
parsing_failed = None
|
||||
|
||||
import_error_text_template = """
|
||||
The imports for pypykatz plugin have failed at some point.
|
||||
Common causes:
|
||||
1. You dont have pypykatz installed
|
||||
2. Python runtime environment used by MemProcFs is not the same as you have installed pypykatz in.
|
||||
3. You are not using the correct python version
|
||||
|
||||
Error traceback:
|
||||
%s
|
||||
"""
|
||||
|
||||
parsing_error_template = """
|
||||
pypykatz plugin tried to parse the lsass.exe process in your memory dump but failed.
|
||||
This could be caused by multiple things:
|
||||
1. The pypykatz's parser code is potato
|
||||
2. MemProcFs could not fully parse the memory, usually this happens with incorrect memory dump files.
|
||||
Check for error strings like "Could not load segment data"
|
||||
|
||||
In case you are cretain the problem is caused by the parser,
|
||||
please submit an issue with the info below this line:
|
||||
%s
|
||||
|
||||
%s
|
||||
"""
|
||||
|
||||
import_error_text = None
|
||||
parsing_error_text = None
|
||||
|
||||
try:
|
||||
from pypykatz.pypykatz import pypykatz
|
||||
from pypykatz.commons.common import UniversalEncoder
|
||||
from plugins.pym_pypykatz.pypyreader import MemProcFsReader
|
||||
|
||||
#this needs to be the last line!
|
||||
import_failed = False
|
||||
|
||||
|
||||
except Exception as e:
|
||||
import_failed = True
|
||||
if VmmPyPlugin_fPrintV:
|
||||
traceback.print_exc()
|
||||
import_error_text = import_error_text_template % traceback.format_exc()
|
||||
pass
|
||||
|
||||
class KerberosInfo:
|
||||
def __init__(self, type, domain, user, data):
|
||||
self.type = type.name
|
||||
self.domain = domain
|
||||
self.user = user
|
||||
self.data = data
|
||||
|
||||
def get_filename_base(self):
|
||||
return '%s_%s_%s' % (self.type, self.domain, self.user)
|
||||
|
||||
def process_lsass():
|
||||
"""
|
||||
Processing lsass.exe with pypykatz, storing the results in the globals
|
||||
|
||||
"""
|
||||
global all_secrets
|
||||
global luids
|
||||
global domains
|
||||
global kerberos
|
||||
global parsing_error_text
|
||||
global parsing_failed
|
||||
|
||||
basic_info = ''
|
||||
try:
|
||||
memreader = MemProcFsReader()
|
||||
|
||||
basic_info = '===== BASIC INFO. SUBMIT THIS IF THERE IS AN ISSUE =====\r\n'
|
||||
basic_info += 'CPU arch: %s\r\n' % memreader.sysinfo.architecture.name
|
||||
basic_info += 'OS: %s\r\n' % memreader.sysinfo.operating_system
|
||||
basic_info += 'BuildNumber: %s\r\n' % memreader.sysinfo.buildnumber
|
||||
basic_info += 'MajorVersion: %s\r\n' % memreader.sysinfo.major_version
|
||||
basic_info += 'MSV timestamp: %s\r\n' % memreader.sysinfo.msv_dll_timestamp
|
||||
|
||||
|
||||
mimi = pypykatz(memreader, memreader.sysinfo)
|
||||
mimi.start()
|
||||
|
||||
all_secrets = json.dumps(mimi, cls = UniversalEncoder, indent=4, sort_keys=True)
|
||||
for luid in mimi.logon_sessions:
|
||||
luids[str(luid)] = str(mimi.logon_sessions[luid])
|
||||
for kc in mimi.logon_sessions[luid].kerberos_creds:
|
||||
for ticket in kc.tickets:
|
||||
if str(luid) not in kerberos:
|
||||
kerberos[str(luid)] = []
|
||||
|
||||
ki = KerberosInfo(ticket.type, ticket.DomainName, '.'.join(ticket.EClientName), ticket.to_asn1().dump())
|
||||
kerberos[str(luid)].append(ki)
|
||||
domain = mimi.logon_sessions[luid].domainname
|
||||
user = mimi.logon_sessions[luid].username
|
||||
|
||||
if domain == '':
|
||||
domain = 'local'
|
||||
|
||||
if user == '':
|
||||
user = 'empty'
|
||||
|
||||
if domain not in domains:
|
||||
domains[domain] = {}
|
||||
if user not in domains[domain]:
|
||||
domains[domain][user] = {}
|
||||
|
||||
domains[domain][user][str(luid)] = str(mimi.logon_sessions[luid])
|
||||
|
||||
parsing_failed = False
|
||||
|
||||
except Exception as e:
|
||||
parsing_failed = True
|
||||
if VmmPyPlugin_fPrintV:
|
||||
traceback.print_exc()
|
||||
parsing_error_text = parsing_error_template % (basic_info, traceback.format_exc())
|
||||
pass
|
||||
|
||||
|
||||
def ReadAllResults(pid, file_path, file_name, file_attr, bytes_length, bytes_offset):
|
||||
"""
|
||||
reads the all_results data as file on the virtual FS
|
||||
"""
|
||||
|
||||
return all_secrets[bytes_offset:bytes_offset+bytes_length].encode()
|
||||
|
||||
def ReadLuid(pid, file_path, file_name, file_attr, bytes_length, bytes_offset):
|
||||
"""
|
||||
reads the secrets for a specific luid data as file on the virtual FS
|
||||
"""
|
||||
try:
|
||||
|
||||
luid = file_name.rsplit('.', 1)[0]
|
||||
if luid.find('_') != -1:
|
||||
luid = luid.split('_')[1]
|
||||
return luids[luid].encode()[bytes_offset:bytes_offset+bytes_length]
|
||||
|
||||
except Exception as e:
|
||||
if VmmPyPlugin_fPrintV:
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
def ReadKerberos(pid, file_path, file_name, file_attr, bytes_length, bytes_offset):
|
||||
try:
|
||||
|
||||
t = file_name.rsplit('.', 1)[0]
|
||||
t, luid, pos = t.rsplit('_', 2)
|
||||
data = kerberos[luid][int(pos)].data
|
||||
|
||||
return data[bytes_offset:bytes_offset+bytes_length]
|
||||
|
||||
except Exception as e:
|
||||
if VmmPyPlugin_fPrintV:
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
def ReadErrors(pid, file_path, file_name, file_attr, bytes_length, bytes_offset):
|
||||
try:
|
||||
|
||||
if file_name == 'import_error.txt':
|
||||
return import_error_text.encode()[bytes_offset:bytes_offset+bytes_length]
|
||||
if file_name == 'parsing_error.txt':
|
||||
return parsing_error_text.encode()[bytes_offset:bytes_offset+bytes_length]
|
||||
|
||||
except Exception as e:
|
||||
if VmmPyPlugin_fPrintV:
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
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.
|
||||
global first_run
|
||||
try:
|
||||
|
||||
if path[:7] != 'secrets':
|
||||
return None
|
||||
|
||||
if first_run == True:
|
||||
process_lsass()
|
||||
first_run = False
|
||||
|
||||
if import_failed == True:
|
||||
print(import_failed)
|
||||
result = {
|
||||
'import_error.txt': {'size': len(import_error_text), 'read': ReadErrors, 'write': None},
|
||||
}
|
||||
return result
|
||||
|
||||
if parsing_failed == True:
|
||||
result = {
|
||||
'parsing_error.txt': {'size': len(parsing_error_text), 'read': ReadErrors, 'write': None},
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
if path == 'secrets':
|
||||
result = {
|
||||
'all_results.json': {'size': len(all_secrets), 'read': ReadAllResults, 'write': None},
|
||||
}
|
||||
result['by_luid'] = {'dirs' : True}
|
||||
result['by_domain'] = {'dirs' : True}
|
||||
result['kerberos'] = {'dirs' : True}
|
||||
return result
|
||||
|
||||
if path == 'secrets/by_luid':
|
||||
result = {}
|
||||
for luid in luids:
|
||||
result['%s.txt' % luid] = {'size': len(luids[luid]), 'read': ReadLuid, 'write': None}
|
||||
return result
|
||||
|
||||
if path == 'secrets/by_domain':
|
||||
result = {}
|
||||
for domain in domains:
|
||||
result[domain] = {'dirs' : True}
|
||||
return result
|
||||
|
||||
if path.find('secrets/by_domain/') == 0:
|
||||
result = {}
|
||||
domain = path.rsplit('/',1)[1]
|
||||
|
||||
if domain not in domains and domain.lower() == 'system32':
|
||||
#this is a special case for an unknown behaviour coming from windows itself.
|
||||
#for some reason it requests <path>/System32 and <path>/System32/System32 for reasons beyond science...
|
||||
return None
|
||||
|
||||
for user in domains[domain]:
|
||||
for luid in domains[domain][user]:
|
||||
result['%s_%s.txt' % (user, luid)] = {'size': len(luids[luid]), 'read': ReadLuid, 'write': None}
|
||||
return result
|
||||
|
||||
if path == 'secrets/kerberos':
|
||||
result = {}
|
||||
for luid in kerberos:
|
||||
result[luid] = {'dirs' : True}
|
||||
return result
|
||||
|
||||
if path.find('secrets/kerberos/') == 0:
|
||||
|
||||
luid = path.rsplit('/',1)[1]
|
||||
result = {}
|
||||
for i, ki in enumerate(kerberos[luid]):
|
||||
result['%s_%s_%s.kirbi' % (ki.get_filename_base(), luid, i)] = {'size': len(ki.data), 'read': ReadKerberos, 'write': None}
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
if VmmPyPlugin_fPrintV:
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
|
||||
def Notify(fEvent, bytesData):
|
||||
if fEvent == memprocfs.PLUGIN_EVENT_TOTALREFRESH and not import_failed and not parsing_failed:
|
||||
global first_run
|
||||
first_run = True
|
||||
|
||||
|
||||
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 != memprocfs.SYSTEM_WINDOWS_X64 and target_system != memprocfs.SYSTEM_WINDOWS_X86:
|
||||
raise RuntimeError("Only Windows is supported by the pym_pypykatz module.")
|
||||
VmmPyPlugin_FileRegisterDirectory(None, 'secrets', List)
|
||||
|
||||
@@ -1,283 +0,0 @@
|
||||
# pypyreader.py
|
||||
#
|
||||
# Pypykatz reader plugin for MemProcFss
|
||||
#
|
||||
# https://github.com/skelsec/
|
||||
#
|
||||
# (c) Tamas Jos, 2019
|
||||
# Author: Tamas Jos (@skelsec), info@skelsec.com
|
||||
#
|
||||
|
||||
from pypykatz.commons.common import KatzSystemArchitecture, KatzSystemInfo
|
||||
from .sysinfo_helpers import *
|
||||
|
||||
import memprocfs
|
||||
from vmmpyplugin import *
|
||||
import copy
|
||||
|
||||
class Module:
|
||||
def __init__(self):
|
||||
self.name = None
|
||||
self.baseaddress = None
|
||||
self.size = None
|
||||
self.endaddress = None
|
||||
self.pages = []
|
||||
|
||||
self.versioninfo = None
|
||||
self.checksum = None
|
||||
self.timestamp = None
|
||||
|
||||
def inrange(self, addr):
|
||||
return self.baseaddress <= addr < self.endaddress
|
||||
|
||||
def parse(data, timestamp = None):
|
||||
m = Module()
|
||||
m.name = data.name
|
||||
m.baseaddress = data.base
|
||||
m.size = data.image_size
|
||||
m.endaddress = m.baseaddress + m.size
|
||||
|
||||
m.timestamp = timestamp
|
||||
|
||||
return m
|
||||
|
||||
def __str__(self):
|
||||
return '%s %s %s %s %s' % (self.name, hex(self.baseaddress), hex(self.size), hex(self.endaddress), self.timestamp )
|
||||
|
||||
class Page:
|
||||
def __init__(self):
|
||||
self.BaseAddress = None
|
||||
self.AllocationBase = None
|
||||
self.AllocationProtect = None
|
||||
self.RegionSize = None
|
||||
self.EndAddress = None
|
||||
self.data = None
|
||||
|
||||
@staticmethod
|
||||
def parse(page_info, module):
|
||||
p = Page()
|
||||
p.BaseAddress = page_info['VirtualAddress'] + module.baseaddress
|
||||
p.AllocationBase = None #page_info['VirtualAddress'] ???? TODO
|
||||
p.AllocationProtect = None #page_info['VirtualAddress'] ???? TODO
|
||||
p.RegionSize = min(page_info['SizeOfRawData'], 100*1024*1024) # TODO: need this currently to stop infinite search
|
||||
p.EndAddress = p.BaseAddress + p.RegionSize
|
||||
return p
|
||||
|
||||
@staticmethod
|
||||
def parse_raw(page_info):
|
||||
p = Page()
|
||||
p.BaseAddress = page_info['va']
|
||||
p.AllocationBase = None #page_info['VirtualAddress'] ???? TODO
|
||||
p.AllocationProtect = None #page_info['VirtualAddress'] ???? TODO
|
||||
p.RegionSize = min(page_info['size'], 100*1024*1024) # TODO: need this currently to stop infinite search
|
||||
p.EndAddress = p.BaseAddress + p.RegionSize
|
||||
return p
|
||||
|
||||
def read_data(self, pid):
|
||||
self.data = vmm.process(pid).memory.read(self.BaseAddress, self.RegionSize)
|
||||
|
||||
def inrange(self, addr):
|
||||
return self.BaseAddress <= addr < self.EndAddress
|
||||
|
||||
def search(self, pattern, pid):
|
||||
if len(pattern) > self.RegionSize:
|
||||
return []
|
||||
data = vmm.process(pid).memory.read(self.BaseAddress, self.RegionSize)
|
||||
fl = []
|
||||
offset = 0
|
||||
while len(data) > len(pattern):
|
||||
marker = data.find(pattern)
|
||||
if marker == -1:
|
||||
return fl
|
||||
fl.append(marker + offset + self.BaseAddress)
|
||||
data = data[marker+1:]
|
||||
offset = marker + 1
|
||||
|
||||
return fl
|
||||
|
||||
def __str__(self):
|
||||
return '0x%08x 0x%08x %s 0x%08x' % (self.BaseAddress, self.AllocationBase, self.AllocationProtect, self.RegionSize)
|
||||
|
||||
|
||||
class MemProcFsReader:
|
||||
def __init__(self, process_name = 'lsass.exe', filename = None):
|
||||
self.filename = filename
|
||||
self.process_name = process_name
|
||||
self.sysinfo = None
|
||||
self.process = None
|
||||
self.current_position = None
|
||||
self.modules = []
|
||||
|
||||
|
||||
self.setup()
|
||||
|
||||
def get_sysinfo(self):
|
||||
self.sysinfo = KatzSystemInfo()
|
||||
|
||||
#print('[+] Getting BuildNumer')
|
||||
self.sysinfo.buildnumber = vmm.get_config(memprocfs.OPT_WIN_VERSION_BUILD)
|
||||
#print('[+] Found BuildNumber %s' % self.sysinfo.buildnumber)
|
||||
|
||||
#print('[+] Getting msv_dll_timestamp')
|
||||
self.sysinfo.msv_dll_timestamp = int(PEGetFileTime(self.process, self.process_name))
|
||||
#print('[+] Found msv_dll_timestamp %s' % self.sysinfo.msv_dll_timestamp)
|
||||
|
||||
#print('[+] Getting arch')
|
||||
val = vmm.get_config(memprocfs.OPT_CORE_SYSTEM)
|
||||
if val == memprocfs.SYSTEM_WINDOWS_X64:
|
||||
self.sysinfo.architecture = KatzSystemArchitecture.X64
|
||||
else:
|
||||
self.sysinfo.architecture = KatzSystemArchitecture.X86
|
||||
|
||||
#print('[+] Got arch %s' % self.sysinfo.architecture)
|
||||
|
||||
|
||||
def setup(self):
|
||||
|
||||
if self.filename:
|
||||
# if filename is specified we dont want to use the virtual FS, but then we need to init the vmmpy module
|
||||
VmmPy_Initialize(["-device", self.filename,'-vv'])
|
||||
|
||||
#print('[+] Searching LSASS')
|
||||
self.process = vmm.process(self.process_name)
|
||||
#print('[+] Found LSASS on PID %s' % self.process.pid)
|
||||
|
||||
self.get_sysinfo()
|
||||
|
||||
#print('[+] Getting modules info')
|
||||
for module in self.process.module_list():
|
||||
#print('moduleinfo: %s' % str(moduleinfo))
|
||||
m = Module.parse(module)
|
||||
try:
|
||||
for pageinfo in module.maps.sections():
|
||||
#print('pageinfo: %s' % str(pageinfo))
|
||||
m.pages.append(Page.parse(pageinfo, m))
|
||||
|
||||
self.modules.append(m)
|
||||
except:
|
||||
#module is paged out, hoping that it's not a module that is needed
|
||||
pass
|
||||
|
||||
#print('[+] Got modules info')
|
||||
|
||||
|
||||
def get_module_by_name(self, module_name):
|
||||
for mod in self.modules:
|
||||
if mod.name.lower().find(module_name.lower()) != -1:
|
||||
return mod
|
||||
return None
|
||||
|
||||
def find_in_module(self, module_name, pattern):
|
||||
mod = self.get_module_by_name(module_name)
|
||||
if mod is None:
|
||||
raise Exception('Could not find module! %s' % module_name)
|
||||
t = []
|
||||
for page in mod.pages:
|
||||
t += self.find(page.BaseAddress, page.EndAddress, pattern)
|
||||
return t
|
||||
|
||||
@staticmethod
|
||||
def find_all_pattern(data, pattern):
|
||||
substring_length = len(pattern)
|
||||
def recurse(locations_found, start):
|
||||
location = data.find(pattern, start)
|
||||
if location != -1:
|
||||
return recurse(locations_found + [location], location+substring_length)
|
||||
else:
|
||||
return locations_found
|
||||
|
||||
return recurse([], 0)
|
||||
|
||||
def find(self, start, end, pattern):
|
||||
"""
|
||||
Searches for all occurrences of a pattern in the current memory segment, returns all occurrences as a list
|
||||
"""
|
||||
data = self.process.memory.read(start, end - start)
|
||||
pos = []
|
||||
for p in MemProcFsReader.find_all_pattern(data, pattern):
|
||||
pos.append( p + start)
|
||||
return pos
|
||||
|
||||
def seek(self, offset, whence = 0):
|
||||
"""
|
||||
Changes the current address to an offset of offset.
|
||||
"""
|
||||
self.current_position += offset
|
||||
return
|
||||
|
||||
def move(self, address):
|
||||
"""
|
||||
Moves the buffer to a virtual address specified by address
|
||||
"""
|
||||
self.current_position = address
|
||||
return
|
||||
|
||||
def align(self, alignment = None):
|
||||
"""
|
||||
Repositions the current reader to match architecture alignment
|
||||
"""
|
||||
if alignment is None:
|
||||
if self.sysinfo.architecture == KatzSystemArchitecture.X64:
|
||||
alignment = 8
|
||||
else:
|
||||
alignment = 4
|
||||
offset = self.current_position % alignment
|
||||
if offset == 0:
|
||||
return
|
||||
offset_to_aligned = (alignment - offset) % alignment
|
||||
self.seek(offset_to_aligned, 1)
|
||||
return
|
||||
|
||||
def tell(self):
|
||||
"""
|
||||
Returns the current virtual address
|
||||
"""
|
||||
return self.current_position
|
||||
|
||||
def peek(self, length):
|
||||
t = self.current_position
|
||||
data = self.read(length)
|
||||
self.current_position = t
|
||||
|
||||
return data
|
||||
|
||||
def read(self, size = -1):
|
||||
data = self.process.memory.read(self.current_position, size)
|
||||
self.current_position += size
|
||||
return data
|
||||
|
||||
def read_int(self):
|
||||
"""
|
||||
Reads an integer. The size depends on the architecture.
|
||||
Reads a 4 byte small-endian singed int on 32 bit arch
|
||||
Reads an 8 byte small-endian singed int on 64 bit arch
|
||||
"""
|
||||
if self.sysinfo.architecture == KatzSystemArchitecture.X64:
|
||||
return int.from_bytes(self.read(8), byteorder = 'little', signed = True)
|
||||
else:
|
||||
return int.from_bytes(self.read(4), byteorder = 'little', signed = True)
|
||||
|
||||
def read_uint(self):
|
||||
"""
|
||||
Reads an integer. The size depends on the architecture.
|
||||
Reads a 4 byte small-endian unsinged int on 32 bit arch
|
||||
Reads an 8 byte small-endian unsinged int on 64 bit arch
|
||||
"""
|
||||
if self.sysinfo.architecture == KatzSystemArchitecture.X64:
|
||||
return int.from_bytes(self.read(8), byteorder = 'little', signed = False)
|
||||
else:
|
||||
return int.from_bytes(self.read(4), byteorder = 'little', signed = False)
|
||||
|
||||
def get_ptr(self, pos):
|
||||
self.move(pos)
|
||||
return self.read_uint()
|
||||
|
||||
def get_ptr_with_offset(self, pos):
|
||||
if self.sysinfo.architecture == KatzSystemArchitecture.X64:
|
||||
self.move(pos)
|
||||
ptr = int.from_bytes(self.read(4), byteorder = 'little', signed = True)
|
||||
return pos + 4 + ptr
|
||||
else:
|
||||
self.move(pos)
|
||||
return self.read_uint()
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
# sysinfo_helpers.py
|
||||
#
|
||||
# Helper functions to retrieve the file time and version information from a file.
|
||||
# NB! there are cleaner and better ways to do this, but this works ...
|
||||
#
|
||||
# https://github.com/ufrisk/
|
||||
#
|
||||
# (c) Ulf Frisk, 2019-2021
|
||||
# Author: Ulf Frisk, pcileech@frizk.net
|
||||
#
|
||||
|
||||
from io import BytesIO
|
||||
from dissect import cstruct
|
||||
import memprocfs
|
||||
|
||||
PE_STRUCT_DEFINITIONS = """
|
||||
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
|
||||
#define IMAGE_SIZEOF_SHORT_NAME 8
|
||||
typedef struct _IMAGE_DOS_HEADER
|
||||
{
|
||||
WORD e_magic;
|
||||
WORD e_cblp;
|
||||
WORD e_cp;
|
||||
WORD e_crlc;
|
||||
WORD e_cparhdr;
|
||||
WORD e_minalloc;
|
||||
WORD e_maxalloc;
|
||||
WORD e_ss;
|
||||
WORD e_sp;
|
||||
WORD e_csum;
|
||||
WORD e_ip;
|
||||
WORD e_cs;
|
||||
WORD e_lfarlc;
|
||||
WORD e_ovno;
|
||||
WORD e_res[4];
|
||||
WORD e_oemid;
|
||||
WORD e_oeminfo;
|
||||
WORD e_res2[10];
|
||||
LONG e_lfanew;
|
||||
} IMAGE_DOS_HEADER;
|
||||
typedef struct _IMAGE_FILE_HEADER {
|
||||
WORD Machine;
|
||||
WORD NumberOfSections;
|
||||
DWORD TimeDateStamp;
|
||||
DWORD PointerToSymbolTable;
|
||||
DWORD NumberOfSymbols;
|
||||
WORD SizeOfOptionalHeader;
|
||||
WORD Characteristics;
|
||||
} IMAGE_FILE_HEADER;
|
||||
"""
|
||||
|
||||
def PEGetFileTime(process, module):
|
||||
mz_va = process.module(module).base
|
||||
mz_bytes = process.memory.read(mz_va, 0x1000)
|
||||
mz_stream = BytesIO(mz_bytes)
|
||||
# Set up dissect.cstruct
|
||||
pestruct = cstruct.cstruct()
|
||||
pestruct.load(PE_STRUCT_DEFINITIONS)
|
||||
# Parse MZ header
|
||||
struct_mz = pestruct.IMAGE_DOS_HEADER(mz_stream)
|
||||
if struct_mz.e_magic != 0x5a4d:
|
||||
return 0
|
||||
mz_stream.seek(struct_mz.e_lfanew)
|
||||
signature = pestruct.uint32(mz_stream)
|
||||
if signature != 0x4550:
|
||||
return 0
|
||||
# Parse the PE file_header struct.
|
||||
struct_file_header = pestruct.IMAGE_FILE_HEADER(mz_stream)
|
||||
return struct_file_header.TimeDateStamp
|
||||
@@ -1 +0,0 @@
|
||||
1.3.0
|
||||
Reference in New Issue
Block a user