Hi!
I'm new to this list but I'd like to help on a few parts of the PE parsing apparati. I will be reviewing the source this week.
Is there any objection to a -v (verbose) flag which would output offsets to header references to the specified objects to assist
with manual image inspection?
Typically I use automated tools to an extent, but another feature I'd like to add is actual PE extraction if the executable was fully
loaded into memory (say the file self removes, or a rootkit lives only in RAM). This way we could plug things directly into objdump
or another disassembler of choice (IDA Pro here).
Thoughts? Suggestions?
Regards,
Jason Reynolds
Hiya guys,
I had a request from echo6 to allow Volatility to run against iSCSI
exported memory images (which basically show up in Linux as block
devices). The attached patch changes checks from "not isfile" to
"isdir", in order to include block files, regular files and symlinks.
The patch also includes a secondary method for determine filesize, based
on opening the file, seeking to the end, and then asking the file to
tell how far the cursor is. That works fine for block devices, whereas
getsize returns 0.
As you can see form the patch, there's a lot of plugins that reimplement
existing code and handle files directly. That also means that other
existing plugins will need converting if they're to work against block
devices...
Mike 5:)
diff --git a/trunk/Volatility/forensics/addrspace.py b/trunk/Volatility/forensics/addrspace.py
index d0c0dee..c1d7198 100755
--- a/trunk/Volatility/forensics/addrspace.py
+++ b/trunk/Volatility/forensics/addrspace.py
@@ -43,6 +43,12 @@ class FileAddressSpace:
self.name = fname
self.fhandle = open(fname, mode)
self.fsize = os.path.getsize(fname)
+ # getsize returns 0 for block devices
+ if self.fsize < 1:
+ f = open(fname, mode)
+ f.seek(0, os.SEEK_END)
+ self.fsize = f.tell()
+ f.close()
if fast == True:
self.fast_fhandle = open(fname, mode)
diff --git a/trunk/Volatility/forensics/win32/scan.py b/trunk/Volatility/forensics/win32/scan.py
index 3217983..771fe67 100644
--- a/trunk/Volatility/forensics/win32/scan.py
+++ b/trunk/Volatility/forensics/win32/scan.py
@@ -678,7 +678,6 @@ def connection_dump(address, cnt, object):
def conn_scan(addr_space, types, filename, beg, end, slow):
-
if slow == False:
connection_object = ScanObject(addr_space,types)
connection_object.set_fast_beg(beg)
diff --git a/trunk/Volatility/vmodules.py b/trunk/Volatility/vmodules.py
index cd1bf20..5aa0cd1 100644
--- a/trunk/Volatility/vmodules.py
+++ b/trunk/Volatility/vmodules.py
@@ -978,7 +978,7 @@ def psscan(cmdname, argv):
slow = opts.slow
- if (opts.filename is None) or (not os.path.isfile(opts.filename)) :
+ if (opts.filename is None) or (os.path.isdir(opts.filename)) :
op.error("File is required")
else:
filename = opts.filename
@@ -993,6 +993,12 @@ def psscan(cmdname, argv):
start = 0
filesize = os.path.getsize(filename)
+ # getsize returns 0 for block devices
+ if filesize < 1:
+ f = open(filename, 'rb')
+ f.seek(0, os.SEEK_END)
+ filesize = f.tell()
+ f.close()
if not opts.end is None:
try:
@@ -1050,7 +1056,7 @@ def thrdscan(cmdname, argv):
slow = opts.slow
- if (opts.filename is None) or (not os.path.isfile(opts.filename)) :
+ if (opts.filename is None) or (os.path.isdir(opts.filename)) :
op.error("File is required")
else:
filename = opts.filename
@@ -1065,6 +1071,12 @@ def thrdscan(cmdname, argv):
start = 0
filesize = os.path.getsize(filename)
+ # getsize returns 0 for block devices
+ if filesize < 1:
+ f = open(filename, 'rb')
+ f.seek(0, os.SEEK_END)
+ filesize = f.tell()
+ f.close()
if not opts.end is None:
try:
@@ -1123,7 +1135,7 @@ def sockscan(cmdname, argv):
slow = opts.slow
- if (opts.filename is None) or (not os.path.isfile(opts.filename)) :
+ if (opts.filename is None) or (os.path.isdir(opts.filename)) :
op.error("File is required")
else:
filename = opts.filename
@@ -1138,6 +1150,12 @@ def sockscan(cmdname, argv):
start = 0
filesize = os.path.getsize(filename)
+ # getsize returns 0 for block devices
+ if filesize < 1:
+ f = open(filename, 'rb')
+ f.seek(0, os.SEEK_END)
+ filesize = f.tell()
+ f.close()
if not opts.end is None:
try:
@@ -1194,7 +1212,7 @@ def connscan(cmdname, argv):
slow = opts.slow
- if (opts.filename is None) or (not os.path.isfile(opts.filename)) :
+ if (opts.filename is None) or (os.path.isdir(opts.filename)) :
op.error("File is required")
else:
filename = opts.filename
@@ -1209,6 +1227,12 @@ def connscan(cmdname, argv):
start = 0
filesize = os.path.getsize(filename)
+ # getsize returns 0 for block devices
+ if filesize < 1:
+ f = open(filename, 'rb')
+ f.seek(0, os.SEEK_END)
+ filesize = f.tell()
+ f.close()
if not opts.end is None:
try:
@@ -1257,7 +1281,7 @@ def mem_map(cmdname, argv):
opts, args = op.parse_args(argv)
- if (opts.filename is None) or (not os.path.isfile(opts.filename)) :
+ if (opts.filename is None) or (os.path.isdir(opts.filename)) :
op.error("File is required")
else:
filename = opts.filename
@@ -1354,7 +1378,7 @@ def modscan(cmdname, argv):
slow = opts.slow
- if (opts.filename is None) or (not os.path.isfile(opts.filename)) :
+ if (opts.filename is None) or (os.path.isdir(opts.filename)) :
op.error("File is required")
else:
filename = opts.filename
@@ -1368,6 +1392,12 @@ def modscan(cmdname, argv):
start = 0
filesize = os.path.getsize(filename)
+ # getsize returns 0 for block devices
+ if filesize < 1:
+ f = open(filename, 'rb')
+ f.seek(0, os.SEEK_END)
+ filesize = f.tell()
+ f.close()
if not opts.end is None:
try:
@@ -1423,7 +1453,7 @@ def dump_chk(cmdname, argv):
op = get_standard_parser(cmdname)
opts, args = op.parse_args(argv)
- if (opts.filename is None) or (not os.path.isfile(opts.filename)) :
+ if (opts.filename is None) or (os.path.isdir(opts.filename)) :
op.error("File is required")
else:
filename = opts.filename
@@ -1492,7 +1522,7 @@ def mem_dump(cmdname, argv):
opts, args = op.parse_args(argv)
- if (opts.filename is None) or (not os.path.isfile(opts.filename)) :
+ if (opts.filename is None) or (os.path.isdir(opts.filename)) :
op.error("File is required")
else:
filename = opts.filename
@@ -1601,7 +1631,7 @@ def hibinfo(cmdname, argv):
metavar="FILE", dest="dump")
opts, args = op.parse_args(argv)
- if (opts.filename is None) or (not os.path.isfile(opts.filename)) :
+ if (opts.filename is None) or (os.path.isdir(opts.filename)) :
op.error("File is required")
else:
filename = opts.filename
@@ -1923,7 +1953,7 @@ def connscan2(cmdname, argv):
op = get_standard_parser(cmdname)
opts, args = op.parse_args(argv)
- if (opts.filename is None) or (not os.path.isfile(opts.filename)) :
+ if (opts.filename is None) or (os.path.isdir(opts.filename)) :
op.error("File is required")
else:
filename = opts.filename
@@ -1968,7 +1998,7 @@ def sockscan2(cmdname, argv):
op = get_standard_parser(cmdname)
opts, args = op.parse_args(argv)
- if (opts.filename is None) or (not os.path.isfile(opts.filename)) :
+ if (opts.filename is None) or (os.path.isdir(opts.filename)) :
op.error("File is required")
else:
filename = opts.filename
@@ -2009,7 +2039,7 @@ def modscan2(cmdname, argv):
op = get_standard_parser(cmdname)
opts, args = op.parse_args(argv)
- if (opts.filename is None) or (not os.path.isfile(opts.filename)) :
+ if (opts.filename is None) or (os.path.isdir(opts.filename)) :
op.error("File is required")
else:
filename = opts.filename
@@ -2049,7 +2079,7 @@ def thrdscan2(cmdname, argv):
op = get_standard_parser(cmdname)
opts, args = op.parse_args(argv)
- if (opts.filename is None) or (not os.path.isfile(opts.filename)) :
+ if (opts.filename is None) or (os.path.isdir(opts.filename)) :
op.error("File is required")
else:
filename = opts.filename
@@ -2093,7 +2123,7 @@ def psscan2(cmdname, argv):
action='store_true',dest='dot_format', default=False)
opts, args = op.parse_args(argv)
- if (opts.filename is None) or (not os.path.isfile(opts.filename)) :
+ if (opts.filename is None) or (os.path.isdir(opts.filename)) :
op.error("File is required")
else:
filename = opts.filename
Hiya guys,
A slightly bigger and more complex one this time round. This is the
next module I've attempted to convert into a plugin. It seems a bit
fiddly, because you've got to jump around in memory looking for the
right spot (depending on the sys file that created it), so the error
checking on it could probably do with some improvements, but hopefully
the object model will automatically detect and mark as None any invalid
addresses we might ask for.
Again, any comments or criticisms would be gratefully received, I'm not
sure if I'm allowed to just invent type names like I've done for the
table pointers, and I'd also like to know whether the result of a
calculation can contain types, or whether I should be putting the data
into dicts/lists?
I'm also wondering what could be done about the plugin namespace,
because at the moment, there's no way of extending another plugin from a
different module (because relative addressing only works within a python
module, and we haven't been adding __init__.py files to the
memory_plugins directory). Is there a good way of inheriting from a
plugin, or should we just be putting the main functionality under
forensics.win32 and then make the plugins as empty as possible?
The *scan/scan2 functions look like they'll be trickier to convert, if
anyone with a good understanding of the NewObject model would like to
have a go at converting one of them, it'd help me a lot as a template to
get the others converted. If not, I'll have to wing it... 5;P
Mike 5:)
diff --git a/Volatility/forensics/object2.py b/Volatility/forensics/object2.py
index 29552d8..f4f9181 100644
--- a/Volatility/forensics/object2.py
+++ b/Volatility/forensics/object2.py
@@ -99,6 +99,9 @@ class NoneObject(object):
def __iter__(self):
return self
+ def __len__(self):
+ return 0
+
def next(self):
raise StopIteration()
@@ -588,8 +591,14 @@ class Profile:
system. We parse the abstract_types and join them with
native_types to make everything work together.
"""
- def __init__(self, native_types=x86_native_types, abstract_types=types,
- overlay=xpsp2overlays, strict=False):
+ def __init__(self, native_types=None, abstract_types=None,
+ overlay=None, strict=False):
+ if native_types is None:
+ native_types = x86_native_types
+ if abstract_types is None:
+ abstract_types = types
+ if overlay is None:
+ overlay = xpsp2overlays
self.types = {}
self.strict = strict
diff --git a/Volatility/forensics/win32/network.py b/Volatility/forensics/win32/network.py
index 5ee84d6..b4598dd 100644
--- a/Volatility/forensics/win32/network.py
+++ b/Volatility/forensics/win32/network.py
@@ -29,6 +29,8 @@
#pylint: disable-msg=C0111
import struct
+import forensics.win32 as win32
+import forensics.object2 as object2
from forensics.object import read_value, read_obj, get_obj_offset
from forensics.win32.datetime import read_time, windows_to_unix_time
from forensics.win32.modules import module_find_baseaddr, modules_list
@@ -60,10 +62,10 @@ module_versions = { \
'AddrObjTableSizeOffset' : [0x48664], \
},
'3394': {
- 'TCBTableOff': [0x49768], \
- 'SizeOff': [0x3F73C], \
- 'AddrObjTableOffset': [0x486E0], \
- 'AddrObjTableSizeOffset': [0x486E4], \
+ 'TCBTableOff': [0x49768], \
+ 'SizeOff': [0x3F73C], \
+ 'AddrObjTableOffset': [0x486E0], \
+ 'AddrObjTableSizeOffset': [0x486E4], \
},
'5625' : { \
'TCBTableOff' : [0x49ae8], \
@@ -80,6 +82,35 @@ module_versions = { \
}
+def determine_connections(addr_space, profile):
+ """Determines all connections for each module"""
+ all_modules = win32.modules.lsmod(addr_space, profile)
+
+ profile.add_types({'_TCPT_OBJECT_POINTER': [0x4,
+ {'Pointer': [0x0, ['pointer', ['_TCPT_OBJECT']]]}
+ ]})
+
+ connections = []
+
+ for m in all_modules:
+ if str(m.ModuleName).lower() == 'tcpip.sys':
+ for attempt in module_versions:
+ table_size = object2.NewObject("unsigned long", m.BaseAddress + module_versions[attempt]['SizeOff'][0], addr_space, profile=profile)
+ table_addr = object2.NewObject("unsigned long", m.BaseAddress + module_versions[attempt]['TCBTableOff'][0], addr_space, profile=profile)
+ if int(table_size) > 0:
+ table = object2.Array('Array', table_addr, addr_space, count=table_size, profile=profile,
+ target=object2.Curry(object2.NewObject, '_TCPT_OBJECT_POINTER'))
+ for entry in table:
+ if entry is None:
+ break
+ conn = entry.Pointer.dereference()
+ while conn.is_valid():
+ connections.append(conn)
+ conn = conn.Next
+ return connections
+
+ return object2.NoneObject("Unable to determine connections")
+
def tcb_connections(addr_space, types, symbol_table):
all_modules = modules_list(addr_space, types, symbol_table)
base_addr = module_find_baseaddr(addr_space, types, all_modules,"tcpip")
diff --git a/Volatility/memory_plugins/internal/connections.py b/Volatility/memory_plugins/internal/connections.py
new file mode 100644
index 0000000..91a1fff
--- /dev/null
+++ b/Volatility/memory_plugins/internal/connections.py
@@ -0,0 +1,40 @@
+'''
+Created on 25 Sep 2009
+
+@author: Mike Auty
+'''
+
+#pylint: disable-msg=C0111
+
+import forensics.commands
+import forensics.win32 as win32
+import forensics.object2 as object2
+import forensics.utils as utils
+
+class connections(forensics.commands.command):
+ """Print list of open connections"""
+
+ def __init__(self, args=None):
+ forensics.commands.command.__init__(self, args)
+ self.profile = None
+
+ def render_text(self, outfd, data):
+ if len(data):
+ outfd.write("%-25s %-25s %-6s\n" % ('Local Address', 'Remote Address', 'Pid'))
+
+ for conn in data:
+ local = "%s:%s" % (conn.LocalIpAddress, conn.LocalPort)
+ remote = "%s:%s" % (conn.RemoteIpAddress, conn.RemotePort)
+ outfd.write("%-25s %-25s %-6d\n" % (local, remote, conn.Pid))
+
+
+ def calculate(self):
+ result = {}
+ self.profile = object2.Profile()
+
+ addr_space = utils.load_as(self.opts)
+
+ # Get the Image Datetime
+ result = win32.network.determine_connections(addr_space, self.profile)
+
+ return result
\ No newline at end of file
diff --git a/Volatility/vmodules.py b/Volatility/vmodules.py
index ec642f8..46bef38 100644
--- a/Volatility/vmodules.py
+++ b/Volatility/vmodules.py
@@ -41,7 +41,6 @@ from forensics.win32.tasks import module_base, module_path, module_size, create_
from forensics.win32.tasks import process_imagename, process_ldrs, process_list, process_peb, process_pid, process_handle_table, process_create_time, process_handle_count
from forensics.win32.tasks import process_inherited_from, process_num_active_threads, process_vadroot
from forensics.win32.modules import modules_list
-from forensics.win32.network import connection_laddr, connection_lport, connection_raddr, connection_rport, connection_pid, tcb_connections
from forensics.win32.network import socket_create_time, socket_local_port, socket_pid, socket_protocol, open_sockets
from forensics.win32.handles import handle_entries, handle_process_id, handle_tables, handle_entry_object, is_object_file, object_data, file_name
from forensics.win32.modules import module_baseaddr, module_imagename, module_imagesize, module_modulename
@@ -336,39 +335,6 @@ def get_dlllist(cmdname, argv):
print
###################################
-# connections - List open connections
-###################################
-def get_connections(cmdname, argv):
- """
- Function prints a list of open connections
- """
- op = get_standard_parser(cmdname)
- opts, _args = op.parse_args(argv)
-
- (addr_space, symtab, types) = load_and_identify_image(op, opts)
-
- connections = tcb_connections(addr_space, types, symtab)
-
- if len(connections) > 0:
- print "%-25s %-25s %-6s" % ('Local Address', 'Remote Address', 'Pid')
-
- for connection in connections:
-
- if not addr_space.is_valid_address(connection):
- continue
-
- pid = connection_pid(addr_space, types, connection)
- lport = connection_lport(addr_space, types, connection)
- laddr = connection_laddr(addr_space, types, connection)
- rport = connection_rport(addr_space, types, connection)
- raddr = connection_raddr(addr_space, types, connection)
-
- local = "%s:%d" % (laddr, lport)
- remote = "%s:%d" % (raddr, rport)
-
- print "%-25s %-25s %-6d" % (local, remote, pid)
-
-###################################
# sockets - List open sockets
###################################
def get_sockets(cmdname, argv):
diff --git a/Volatility/volatility.py b/Volatility/volatility.py
index 4e04aeb..ebdf02e 100644
--- a/Volatility/volatility.py
+++ b/Volatility/volatility.py
@@ -56,10 +56,6 @@ modules = {
VolatoolsModule('files',
'Print list of open files for each process',
get_open_files),
- 'connections':
- VolatoolsModule('connections',
- 'Print list of open connections',
- get_connections),
'modules':
VolatoolsModule('modules',
'Print list of loaded modules',
Hiya guys,
Here's the first of a few patches. This one should improve the error
checking during the utils.load_as function call. Now if the base
address space can't be instantiated, it raises it's own form of
exception allowing the main program to catch it and report back
gracefully what went wrong.
Without this, just running volatility followed by a plugin name would
fail as the filename address space assumed a filename option would
always be present...
Mike 5:)
diff --git a/Volatility/forensics/utils.py b/Volatility/forensics/utils.py
index 69eccd2..0ff3151 100644
--- a/Volatility/forensics/utils.py
+++ b/Volatility/forensics/utils.py
@@ -19,7 +19,12 @@ def load_as(opts):
## selecting us means we are done:
if not found:
break
+
+ if base_as is None:
+ raise AddrSpaceError("No suitable address space maaping found")
return base_as
-
+class AddrSpaceError(Exception):
+ """Address Space Exception, so we can catch and deal with it in the main program"""
+ pass
\ No newline at end of file
diff --git a/Volatility/memory_objects/Windows/xp_sp2.py b/Volatility/memory_objects/Windows/xp_sp2.py
index 7cd97e0..2a8887e 100644
--- a/Volatility/memory_objects/Windows/xp_sp2.py
+++ b/Volatility/memory_objects/Windows/xp_sp2.py
@@ -25,7 +25,7 @@
#pylint: disable-msg=C0111
-from forensics.object2 import CType, NewObject, NativeType, Curry
+from forensics.object2 import CType, NewObject, NoneObject, NativeType, Curry
from vtypes import xpsp2types as types
from forensics.win32.datetime import windows_to_unix_time
import vmodules
diff --git a/Volatility/memory_plugins/address_spaces/standard.py b/Volatility/memory_plugins/address_spaces/standard.py
index 2712e09..1d4844e 100644
--- a/Volatility/memory_plugins/address_spaces/standard.py
+++ b/Volatility/memory_plugins/address_spaces/standard.py
@@ -23,6 +23,7 @@ class FileAddressSpace(addrspace.BaseAddressSpace):
def __init__(self, base, opts):
addrspace.BaseAddressSpace.__init__(self, base, opts)
assert(base == None)
+ assert(opts['filename'] is not None)
self.name = opts['filename']
self.fname = self.name
self.mode = opts.get('mode','rb')
diff --git a/Volatility/vmodules.py b/Volatility/vmodules.py
index 46737d1..ec642f8 100644
--- a/Volatility/vmodules.py
+++ b/Volatility/vmodules.py
@@ -37,7 +37,6 @@ from forensics.addrspace import FileAddressSpace
from forensics.win32.hiber_addrspace import WindowsHiberFileSpace32
from forensics.win32.crash_addrspace import WindowsCrashDumpSpace32
from forensics.object import read_unicode_string, read_obj
-from forensics.win32.datetime import local_time, windows_to_unix_time
from forensics.win32.tasks import module_base, module_path, module_size, create_addr_space, process_addr_space, process_command_line, process_dtb, process_find_pid
from forensics.win32.tasks import process_imagename, process_ldrs, process_list, process_peb, process_pid, process_handle_table, process_create_time, process_handle_count
from forensics.win32.tasks import process_inherited_from, process_num_active_threads, process_vadroot
diff --git a/Volatility/volatility.py b/Volatility/volatility.py
index a377749..4e04aeb 100644
--- a/Volatility/volatility.py
+++ b/Volatility/volatility.py
@@ -35,6 +35,7 @@
import sys
import os
import forensics.registry as MemoryRegistry
+import forensics.utils
from vmodules import *
@@ -201,12 +202,15 @@ def main(argv=sys.argv):
print "Error: Invalid module [%s]." % (argv[1])
usage(argv[0])
- if modules.has_key(argv[1]):
- modules[argv[1]].execute(argv[1], argv[2:])
- elif MemoryRegistry.PLUGIN_COMMANDS.commands.has_key(argv[1]):
- command = MemoryRegistry.PLUGIN_COMMANDS[argv[1]](argv[2:])
- command.execute()
-
+ try:
+ if modules.has_key(argv[1]):
+ modules[argv[1]].execute(argv[1], argv[2:])
+ elif MemoryRegistry.PLUGIN_COMMANDS.commands.has_key(argv[1]):
+ command = MemoryRegistry.PLUGIN_COMMANDS[argv[1]](argv[2:])
+ command.execute()
+ except forensics.utils.AddrSpaceError:
+ print "Error: No suitable address space found, please check your options."
+ usage(argv[0])
if __name__ == "__main__":
main()
Hi again,
This one's a very quick one, it just ensures that subtraction happens on
the raw value of the data (by overriding type.value rather than just
type.v). This allows two wintimestamps to be subtracted, without
passing them through windows_to_unix_time first (which maps a number of
small windows times down to 0).
I'm not certain of the difference between type.v and type.value, so if
there's some reason we shouldn't be overriding this, do please let me
know... 5;)
Without this, subtracting a TimeZoneBias (in the form of a wintimestamp)
from another wintimestamp will probably leave the original wintimestamp
the same (since the TimeZoneBias will register as unixtime 0)...
Mike 5:)
diff --git a/Volatility/memory_objects/Windows/xp_sp2.py b/Volatility/memory_objects/Windows/xp_sp2.py
index 2a8887e..4cc4956 100644
--- a/Volatility/memory_objects/Windows/xp_sp2.py
+++ b/Volatility/memory_objects/Windows/xp_sp2.py
@@ -26,7 +26,6 @@
#pylint: disable-msg=C0111
from forensics.object2 import CType, NewObject, NoneObject, NativeType, Curry
-from vtypes import xpsp2types as types
from forensics.win32.datetime import windows_to_unix_time
import vmodules
@@ -42,10 +41,10 @@ class _UNICODE_STRING(CType):
try:
length = self.Length.v()
if length > 1024:
- length=0
+ length = 0
data = self.vm.read(self.Buffer.v(), length)
return data.decode("utf16","ignore").encode("ascii",'backslashreplace')
- except Exception, e:
+ except Exception, _e:
return ''
def __str__(self):
@@ -119,20 +118,24 @@ class WinTimeStamp(NativeType):
parent=None, profile=None, name=None, **args):
## This allows us to have a WinTimeStamp object with a
## predetermined value
+ self.data = None
if value:
self.data = value
else:
NativeType.__init__(self, type, offset, vm, parent=parent, profile=profile,
name=name, format_string="q")
- def v(self):
- try:
+ def value(self):
+ """Override the value return, depending on whether we have a data field"""
+ if self.data is not None:
return self.data
- except:
- return windows_to_unix_time(NativeType.v(self))
+ return NativeType.value(self)
+
+ def v(self):
+ return windows_to_unix_time(self.value())
def __sub__(self, x):
- return WinTimeStamp(value = self.v() - x.v())
+ return WinTimeStamp(value = self.value() - x.value())
def __str__(self):
return vmodules.format_time(self.v())
@@ -142,7 +145,7 @@ LEVEL_MASK = 0xfffffff8
class _EPROCESS(CType):
""" An extensive _EPROCESS with bells and whistles """
- def _Peb(self,attr):
+ def _Peb(self, _attr):
""" Returns a _PEB object which is using the process address space.
The PEB structure is referencing back into the process address
@@ -198,7 +201,7 @@ class _EPROCESS(CType):
parent=self, profile=self.profile)
yield filevar
- except Exception, e:
+ except Exception, _e:
pass
def handles(self):
Hi Mike,
Thanks for the patch. I tried to update it in the spirit of the new
object framework.
Im sure you already know what im about to say but this is to clarify
for other members of the list.
When you add a vtype overlay like
+ '_KUSER_SHARED_DATA' : [ None, { \
+ 'SystemTime' : [ None, ['WinTimeStamp', {}]], \
+ 'TimeZoneBias' : [ None, ['WinTimeStamp', {}]], \
+ }],
You are saying that a WinTimeStamp object should be used to decode
these fields. This overrides the standard definition in vtypes.py:
'_KUSER_SHARED_DATA' : [ 0x338, { \
'SystemTime' : [ 0x14, ['_KSYSTEM_TIME']], \
'TimeZoneBias' : [ 0x20, ['_KSYSTEM_TIME']], \
'SuiteMask' : [ 0x2d0, ['unsigned long']], \
'NumberOfPhysicalPages' : [ 0x2e8, ['unsigned long']], \
so in your ident module when you do this:
k = object2.NewObject("_KUSER_SHARED_DATA",
win32.info.KUSER_SHARED_DATA, addr_space, profile=self.profile)
you will actually get an instance of WinTimeStamp back. Since adding
and subtracting time is quite common its worth adding a __sub__ method
to this class so you can just do:
return k.SystemTime - k.TimeZoneBias
Which is more readable and easier to understand - also note that this
should return a WinTimeStamp object - typically with the new command
reorg we try to ensure that we dont pass strings until the very end -
so as not to lose information. Note that expanding a WinTimeStamp into
a string will format it. You should try to use construct like
outfd.write(" Datetime: %s\n" % data['ImageDatetime'])
instead of
outfd.write(" Datetime: " + data['ImageDatetime'])
The latter breaks when data is not a string (which it should not usually be)
Finally try to return NoneObject( reason) from functions rather than
None - this allows you to do stuff like:
for task in win32.tasks.pslist(addr_space, self.profile):
if task.Peb.CSDVersion:
instead of
+ for task in win32.tasks.pslist(addr_space, self.profile):
+ if task.Peb is not None:
+ if task.Peb.CSDVersion is not None:
Which is more error prone because you can forget to check for None at
a certain level for a timebomb bug. Admittadly I fixed a bug where
pslist() didnt do this it was my fault - but thats what we aim for.
Note that NoneObjects can be deferenced as much as you like to produce
the same NoneObject (i.e. the original fault is propagated).
This is important since in memory forensics sometime you will fail to
dereference a point which you thought was there (e.g. if its paged
out) so at least this allows the code to proceed without raising and
prevents you from having to check every single dereference for
success.
Hope this help,
Michael.
On Mon, Sep 21, 2009 at 8:34 AM, Mike Auty <mike.auty(a)gmail.com> wrote:
> Michael Cohen wrote:
>> Hi Mike,
>
> Hiya Michael!
>
>> Ok I will push it to the repository. I must admit im not a fan of svn
>> so im still a bit rusty in using it. Is there any way to get you
>> access to the new object branch? (this question is directed at project
>> admins btw).
>
> Thanks very much for committing that. I know what you mean about svn, I
> tend to use git to track everything, and then it can just push back into
> svn when it needs too (and it'll remind you if you missed a file, just
> like hg!). 5;)
>
>> Ahh the linux tasks is really not even integrated at all yet. Its kind
>> of neglected since noone seems to be interested in linux atm :-(.
>
> I think Jon (echo6) might be interested in the Linux side of things.
> I've also got the old truecrypt plugin on look at on my todo list (way,
> way down on my todo list, but there at least), so we might be able to
> help bolster that. It might be more worthwhile persuing the Mac OS
> X/BSD side of things though, given the distribution of machines people
> might run up against (particularly if people start looking at iPhones on
> ARM)... 5:)
>
>> heh - see here is my svn ignorance showing again - svn doesnt
>> automatically tell you you missed adding a file as far as i can see
>> (like hg does) - sorry I forgot to add that file.
>
> No problem, thanks for adding it back in. 5:)
>
>> The scan2 stuff is faster because its a more optimised scanning
>> technique - not really related to the new object framework. Basically
>> I was going to convert all the basic modules to the new framework as
>> well as we can. It will require a re-engineering of the module rather
>> than just a search/replace though since the new framework is much more
>> concise and readable - case in point is the example I gave previously
>> of _CM_HIVE.
>
> Thanks, I had a go at starting to convert over some of the modules.
> I've done datetime and ident for now, just to get me going. Any chance
> you could take a look at them (part of the attached patch, since I
> touched other bits of the tree). I'm still not fully up to speed on
> everything, so any criticisms would be greatly appreciated! 5:)
>
>> It will be clearer quite quickly which ones we need and which are not
>> so needed - e.g. the pstree one is basically pslist with some extra
>> stuff. If you look at the memory_objects//Windows/xp_sp2.py you can
>> see that the new object classes are starting to implement their own
>> methods to do stuff they want - for example to iterate over an
>> _EPROCESS's handles you can just call:
>>
>> for h in process.handles():
>> xxx
>>
>> this basically obsoletes Volatility/forensics/win32/handles.py
>
> That's very cool! The next step I guess will be to start removing the
> older/unnecessary stuff to clear it all up. 5:)
>
>> Thats a great idea - although better than a single version string it
>> might be better to implement separate versions for each subsystem and
>> let the plugin decide if its ok to work with - e.g. new_object_version
>> 1.0 etc. Maybe just have a version in the command class?
>
> Yep, that seems reasonable. The command class can keep a requirements
> list of features, and the framework can check it satisfies them all.
> That will allow for newer versions and new features, and to gracefully
> deprecate then remove old versions.
>
>> It might work this way im not sure - but the intension is to produce
>> better code, so I would suggest a better replacement for each plugin
>> should be written in the spirit of the new object design.
>
> Absolutely, as I say, I've started with the easy ones, but I have every
> intention to moving getting rid of vmodules (and then clearing out as
> much of the win32.* as are no longer necessary/use the old object model.
>
>> Oh i wrote a page about it once -
>> http://www.pyflag.net/cgi-bin/moin.cgi/Volatility_Object_Framework
>>
>> This is just me playing around and not any "official" way to generate
>> vtypes. It should probably be added to the repo.
>
> Brilliant, that's just the kind of documentation I was looking for. It
> gives a good overview of what's going on. Thanks! 5:)
>
>> Developement typically occured ad hoc and people
>> needed to essentially patch the framework as well as provide plugins
>> which sort of defeats the whole point of plugins. One of the problems
>> has been the inability to add complex vtype handling via plugins - a
>> problem which is addressed in the new branch.
>
> The overlay idea looks really good, but I'm still a little unclear how
> to integrate it cleanly (ie, how to have it affect just a single module,
> in case one overlays changes that break another)...
>
>> ## Maintained for backward compatibility do not use in new code
>
> Yeah, I saw that, it was just still used all over the place, and unless
> it goes away, I think people will keep using it... 5:(
>
>> I was a bit reluctant to completely remove old object support - but I
>> think I might need to do that to make it clear what files are going to
>> be removed.
>
> Yeah, I think so too, but it should make everything a lot easier to
> follow (and much easier to document!). 5:)
>
> Mike 5:)
>
Hi guys,
Here's another pylint patch, much of it the same as the last one, but
this one applies to the new_object branch (it's probably a bit better
than the last one).
new_object requires utils.py[1] and linked_lists.py[2] to work, and the
patch requires their presence. Please be aware, I don't have a complete
library of images to test this stuff against, so whilst none of the
changes should break anything, if they do, please let me know and I'll
be happy to fix them up (particularly if you've got an image I can test
them against!). 5:)
I think the load_as function from utils.py (which makes use of the new
address_space voting scheme that scudette wrote) will make a huge
difference to the duplication going on in all the plugins, so my next
step is going to be converting vmodules.py into individual plugin files,
and then converting each one to use the new address_space system. Once
I get good at that, I can start taking on externally written plugins, if
it'll help? I think maintaining backwards compatibility will clutter an
already complicated layout, so I'd sooner offer my time to convert other
people's plugins, than try and maintain two mechanisms side-by-side and
hope people don't get confused about which one they should be using.
With the namespace cleanup happening anyway, this seems like an ideal
time to drop any deprecated code, unless people think otherwise?
After that, I'm going to have a go at converting them from Object to
NewObject calls. Does anyone know whether NewObject is a drop in
replacement for Object, or if there are specific changes required (such
as, does NewObject require Profiles, or will it still work with normal
types arrays)?
Also, I wondered what people's thoughts were (as a long term goal) on
integrating the Object2/NewObject code with the object code, so that all
the familiars like read_string and so on, would be done through the one
single object model?
So, once all the modules are in their own individual files, and the
imports are all explicit, it should become much easier to move things
around, and get volatility into it's own namespace. Once that's been
done, I'll probably do a sweep over the code changing all the direct
imports (from blah.mumble import foo) to slightly more explicit ones
(import blah.mumble) or similar. The reason I'd do this is because the
imports were all over the place (as evidenced by the fact that not one
internal plugin imported forensics.commands!), and the namespaces were
getting messed up (so most of the plugins were importing gmtime from
other volatility modules, rather than from time itself). The main
question becomes, would it be tedious/ugly to have such long names (like
"volatility.forensics.win32.tasks.process_handle(blah)") or are they not
called often enough to make coding cumbersome? What do people think?
That should cover the first three items on the todo list[3], and four
and five I've got existing code[4] that should get everything sorted
with the installation system, and allow volatility.exe binaries to be
created (there'll need to be some code modifications to allow plugins to
be dropped into directories external to the executable, but that's all
doable)...
Then it's just merging the linux stuff in (where does that come from?)
and lots and lots and lots of documentation!!! Anyone want to help out
with that? It'd really help to have some words about how scudette's
NewObject system works from anyone who understands it... 5:)
Mike 5:)
[1]
http://www.pyflag.net/pyflag/src/plugins/MemoryForensics/Volatility-1.3_Lin…
[2]
http://www.pyflag.net/pyflag/src/plugins/MemoryForensics/Volatility-1.3_Lin…
[3] http://code.google.com/p/volatility/wiki/ToDo14
[4]
http://phormat.svn.sourceforge.net/viewvc/phormat/trunk/phormat/setup.py?re…