diff --git a/usr/local/bin/framework/__init__.py b/usr/local/bin/framework/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/usr/local/bin/framework/__pycache__/__init__.cpython-38.pyc b/usr/local/bin/framework/__pycache__/__init__.cpython-38.pyc
new file mode 100644
index 0000000..4e5c8f3
Binary files /dev/null and b/usr/local/bin/framework/__pycache__/__init__.cpython-38.pyc differ
diff --git a/usr/local/bin/framework/__pycache__/addrspace.cpython-38.pyc b/usr/local/bin/framework/__pycache__/addrspace.cpython-38.pyc
new file mode 100644
index 0000000..a18cd42
Binary files /dev/null and b/usr/local/bin/framework/__pycache__/addrspace.cpython-38.pyc differ
diff --git a/usr/local/bin/framework/__pycache__/newobj.cpython-38.pyc b/usr/local/bin/framework/__pycache__/newobj.cpython-38.pyc
new file mode 100644
index 0000000..f78a97e
Binary files /dev/null and b/usr/local/bin/framework/__pycache__/newobj.cpython-38.pyc differ
diff --git a/usr/local/bin/framework/__pycache__/object.cpython-38.pyc b/usr/local/bin/framework/__pycache__/object.cpython-38.pyc
new file mode 100644
index 0000000..3eac878
Binary files /dev/null and b/usr/local/bin/framework/__pycache__/object.cpython-38.pyc differ
diff --git a/usr/local/bin/framework/__pycache__/types.cpython-38.pyc b/usr/local/bin/framework/__pycache__/types.cpython-38.pyc
new file mode 100644
index 0000000..289955d
Binary files /dev/null and b/usr/local/bin/framework/__pycache__/types.cpython-38.pyc differ
diff --git a/usr/local/bin/framework/addrspace.py b/usr/local/bin/framework/addrspace.py
new file mode 100755
index 0000000..fe42d57
--- /dev/null
+++ b/usr/local/bin/framework/addrspace.py
@@ -0,0 +1,147 @@
+# Volatility
+# Copyright (C) 2007 Volatile Systems
+#
+# Original Source:
+# Copyright (C) 2004,2005,2006 4tphi Research
+# Author: {npetroni,awalters}@4tphi.net (Nick Petroni and AAron Walters)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+"""
+@author: AAron Walters
+@license: GNU General Public License 2.0 or later
+@contact: awalters@volatilesystems.com
+@organization: Volatile Systems
+
+Alias for all address spaces
+"""
+
+# pylint: disable=missing-docstring
+
+import os
+import struct
+
+
+class FileAddressSpace:
+ def __init__(self, fname, mode='rb', fast=False):
+ self.fname = fname
+ self.name = fname
+ self.fhandle = open(fname, mode)
+ self.fsize = os.path.getsize(fname)
+
+ if fast:
+ self.fast_fhandle = open(fname, mode)
+
+ def fread(self, len):
+ return self.fast_fhandle.read(len)
+
+ def read(self, addr, len):
+ self.fhandle.seek(addr)
+ return self.fhandle.read(len)
+
+ def read_long(self, addr):
+ string = self.read(addr, 4)
+ (longval,) = struct.unpack('L', string)
+ return longval
+
+ def get_address_range(self):
+ return [0, self.fsize - 1]
+
+ def get_available_addresses(self):
+ return [self.get_address_range()]
+
+ def is_valid_address(self, addr):
+ return addr < self.fsize - 1
+
+ def close(self):
+ self.fhandle.close()
+
+
+# Code below written by Brendan Dolan-Gavitt
+
+BLOCK_SIZE = 0x1000
+
+
+class HiveFileAddressSpace:
+ def __init__(self, fname):
+ self.fname = fname
+ self.base = FileAddressSpace(fname)
+
+ def vtop(self, vaddr):
+ return vaddr + BLOCK_SIZE + 4
+
+ def read(self, vaddr, length, zero=False):
+ first_block = BLOCK_SIZE - vaddr % BLOCK_SIZE
+ full_blocks = ((length + (vaddr % BLOCK_SIZE)) // BLOCK_SIZE) - 1
+ left_over = (length + vaddr) % BLOCK_SIZE
+
+ paddr = self.vtop(vaddr)
+ if paddr is None and zero:
+ if length < first_block:
+ return "\0" * length
+ else:
+ stuff_read = "\0" * first_block
+ elif paddr is None:
+ return None
+ else:
+ if length < first_block:
+ stuff_read = self.base.read(paddr, length)
+ if not stuff_read and zero:
+ return "\0" * length
+ else:
+ return stuff_read
+
+ stuff_read = self.base.read(paddr, first_block)
+ if not stuff_read and zero:
+ stuff_read = "\0" * first_block
+
+ new_vaddr = vaddr + first_block
+ for __ in range(0, full_blocks):
+ paddr = self.vtop(new_vaddr)
+ if paddr is None and zero:
+ stuff_read = stuff_read + "\0" * BLOCK_SIZE
+ elif paddr is None:
+ return None
+ else:
+ new_stuff = self.base.read(paddr, BLOCK_SIZE)
+ if not new_stuff and zero:
+ new_stuff = "\0" * BLOCK_SIZE
+ elif not new_stuff:
+ return None
+ else:
+ stuff_read = stuff_read + new_stuff
+ new_vaddr = new_vaddr + BLOCK_SIZE
+
+ if left_over > 0:
+ paddr = self.vtop(new_vaddr)
+ if paddr is None and zero:
+ stuff_read = stuff_read + "\0" * left_over
+ elif paddr is None:
+ return None
+ else:
+ stuff_read = stuff_read + self.base.read(paddr, left_over)
+ return stuff_read
+
+ def read_long_phys(self, addr):
+ string = self.base.read(addr, 4)
+ (longval,) = struct.unpack('L', string)
+ return longval
+
+ def is_valid_address(self, vaddr):
+ paddr = self.vtop(vaddr)
+ if not paddr:
+ return False
+ return self.base.is_valid_address(paddr)
diff --git a/usr/local/bin/framework/newobj.py b/usr/local/bin/framework/newobj.py
new file mode 100644
index 0000000..1a28972
--- /dev/null
+++ b/usr/local/bin/framework/newobj.py
@@ -0,0 +1,320 @@
+# This file is part of creddump.
+#
+# creddump is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# creddump is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with creddump. If not, see .
+
+"""
+@author: Brendan Dolan-Gavitt
+@license: GNU General Public License 2.0 or later
+@contact: bdolangavitt@wesleyan.edu
+"""
+
+# pylint: disable=missing-docstring,invalid-name,no-else-return,arguments-differ,unused-argument
+
+from operator import itemgetter
+from struct import unpack
+
+from framework.object import get_obj_offset, builtin_types, read_value, read_unicode_string, read_string, read_obj
+from framework.types import regtypes as types
+
+
+def get_ptr_type(structure, member):
+ """Return the type a pointer points to.
+
+ Arguments:
+ structure : the name of the structure from vtypes
+ member : a list of members
+
+ Example:
+ get_ptr_type('_EPROCESS', ['ActiveProcessLinks', 'Flink']) => ['_LIST_ENTRY']
+ """
+ if len(member) > 1:
+ _, tp = get_obj_offset(types, [structure, member[0]])
+ if tp == 'array':
+ return types[structure][1][member[0]][1][2][1]
+ else:
+ return get_ptr_type(tp, member[1:])
+ else:
+ return types[structure][1][member[0]][1][1]
+
+
+class Obj(object):
+ """Base class for all objects.
+
+ May return a subclass for certain data types to allow
+ for special handling.
+ """
+
+ def __new__(cls, name, address, space):
+ if name in globals():
+ # This is a bit of "magic"
+ # Could be replaced with a dict mapping type names to types
+ return globals()[name](name, address, space)
+ elif name in builtin_types:
+ return Primitive(name, address, space)
+ else:
+ obj = object.__new__(cls)
+ return obj
+
+ def __init__(self, name, address, space):
+ self.name = name
+ self.address = address
+ self.space = space
+
+ # Subclasses can add fields to this list if they want them
+ # to show up in values() or members(), even if they do not
+ # appear in the vtype definition
+ self.extra_members = []
+
+ def __getattribute__(self, attr):
+ try:
+ return object.__getattribute__(self, attr)
+ except AttributeError:
+ pass
+
+ if self.name in builtin_types:
+ raise AttributeError("Primitive types have no dynamic attributes")
+
+ try:
+ off, tp = get_obj_offset(types, [self.name, attr])
+ except:
+ raise AttributeError("'%s' has no attribute '%s'" % (self.name, attr))
+
+ if tp == 'array':
+ a_len = types[self.name][1][attr][1][1]
+ l = []
+ for i in range(a_len):
+ a_off, a_tp = get_obj_offset(types, [self.name, attr, i])
+ if a_tp == 'pointer':
+ ptp = get_ptr_type(self.name, [attr, i])
+ l.append(Pointer(a_tp, self.address + a_off, self.space, ptp))
+ else:
+ l.append(Obj(a_tp, self.address + a_off, self.space))
+ return l
+ elif tp == 'pointer':
+ # Can't just return a Obj here, since pointers need to also
+ # know what type they point to.
+ ptp = get_ptr_type(self.name, [attr])
+ return Pointer(tp, self.address + off, self.space, ptp)
+ else:
+ return Obj(tp, self.address + off, self.space)
+
+ def __truediv__(self, other):
+ if isinstance(other, (tuple, list)):
+ return Pointer(other[0], self.address, self.space, other[1])
+ elif isinstance(other, str):
+ return Obj(other, self.address, self.space)
+ else:
+ raise ValueError("Must provide a type name as string for casting")
+
+ def members(self):
+ """Return a list of this object's members, sorted by offset."""
+
+ # Could also just return the list
+ membs = [(k, v[0]) for k, v in list(types[self.name][1].items())]
+ membs.sort(key=itemgetter(1))
+ return list(map(itemgetter(0), membs)) + self.extra_members
+
+ def values(self):
+ """Return a dictionary of this object's members and their values"""
+
+ valdict = {}
+ for k in self.members():
+ valdict[k] = getattr(self, k)
+ return valdict
+
+ def bytes(self, length=-1):
+ """Get bytes starting at the address of this object.
+
+ Arguments:
+ length : the number of bytes to read. Default: size of
+ this object.
+ """
+
+ if length == -1:
+ length = self.size()
+ return self.space.read(self.address, length)
+
+ def size(self):
+ """Get the size of this object."""
+
+ if self.name in builtin_types:
+ return builtin_types[self.name][0]
+ else:
+ return types[self.name][0]
+
+ def __repr__(self):
+ return "<%s @%08x>" % (self.name, self.address)
+
+ def __eq__(self, other):
+ if not isinstance(other, Obj):
+ raise TypeError("Types are incomparable")
+ return self.address == other.address and self.name == other.name
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __hash__(self):
+ return hash(self.address) ^ hash(self.name)
+
+ def is_valid(self):
+ return self.space.is_valid_address(self.address)
+
+ def get_offset(self, member):
+ return get_obj_offset(types, [self.name] + member)
+
+
+class Primitive(Obj):
+ """Class to represent a primitive data type.
+
+ Attributes:
+ value : the python primitive value of this type
+ """
+
+ def __new__(cls, *args, **kwargs):
+ obj = object.__new__(cls)
+ return obj
+
+ def __init__(self, name, address, space):
+ super(Primitive, self).__init__(name, address, space)
+ length, fmt = builtin_types[name]
+ data = space.read(address, length)
+ if not data:
+ self.value = None
+ else:
+ self.value = unpack(fmt, data)[0]
+
+ def __repr__(self):
+ return repr(self.value)
+
+ def members(self):
+ return []
+
+
+class Pointer(Obj):
+ """Class to represent pointers.
+
+ value : the object pointed to
+
+ If an attribute is not found in this instance,
+ the attribute will be looked up in the referenced
+ object."""
+
+ def __new__(cls, *args, **kwargs):
+ obj = object.__new__(cls)
+ return obj
+
+ def __init__(self, name, address, space, ptr_type):
+ super(Pointer, self).__init__(name, address, space)
+ ptr_address = read_value(space, name, address)
+ if ptr_type[0] == 'pointer':
+ self.value = Pointer(ptr_type[0], ptr_address, self.space, ptr_type[1])
+ else:
+ self.value = Obj(ptr_type[0], ptr_address, self.space)
+
+ def __getattribute__(self, attr):
+ # It's still nice to be able to access things through pointers
+ # without having to explicitly dereference them, so if we don't
+ # find an attribute via our superclass, just dereference the pointer
+ # and return the attribute in the pointed-to type.
+ try:
+ return super(Pointer, self).__getattribute__(attr)
+ except AttributeError:
+ return getattr(self.value, attr)
+
+ def __repr__(self):
+ return "" % (self.value.name, self.value.address)
+
+ def members(self):
+ return self.value.members()
+
+
+class _UNICODE_STRING(Obj):
+ """Class representing a _UNICODE_STRING
+
+ Adds the following behavior:
+ * The Buffer attribute is presented as a Python string rather
+ than a pointer to an unsigned short.
+ * The __str__ method returns the value of the Buffer.
+ """
+
+ def __new__(cls, *args, **kwargs):
+ obj = object.__new__(cls)
+ return obj
+
+ def __str__(self):
+ return self.Buffer
+
+ # Custom Attributes
+ def getBuffer(self):
+ return read_unicode_string(self.space, types, [], self.address)
+
+ Buffer = property(fget=getBuffer)
+
+
+class _CM_KEY_NODE(Obj):
+ def __new__(cls, *args, **kwargs):
+ obj = object.__new__(cls)
+ return obj
+
+ def getName(self):
+ return read_string(self.space, types, ['_CM_KEY_NODE', 'Name'],
+ self.address, self.NameLength.value)
+
+ Name = property(fget=getName)
+
+
+class _CM_KEY_VALUE(Obj):
+ def __new__(cls, *args, **kwargs):
+ obj = object.__new__(cls)
+ return obj
+
+ def getName(self):
+ return read_string(self.space, types, ['_CM_KEY_VALUE', 'Name'],
+ self.address, self.NameLength.value)
+
+ Name = property(fget=getName)
+
+
+class _CHILD_LIST(Obj):
+ def __new__(cls, *args, **kwargs):
+ obj = object.__new__(cls)
+ return obj
+
+ def getList(self):
+ lst = []
+ list_address = read_obj(self.space, types,
+ ['_CHILD_LIST', 'List'], self.address)
+ for i in range(self.Count.value):
+ lst.append(Pointer("pointer", list_address + (i * 4), self.space,
+ ["_CM_KEY_VALUE"]))
+ return lst
+
+ List = property(fget=getList)
+
+
+class _CM_KEY_INDEX(Obj):
+ def __new__(cls, *args, **kwargs):
+ obj = object.__new__(cls)
+ return obj
+
+ def getList(self):
+ lst = []
+ for i in range(self.Count.value):
+ # we are ignoring the hash value here
+ off, __ = get_obj_offset(types, ['_CM_KEY_INDEX', 'List', i * 2])
+ lst.append(Pointer("pointer", self.address + off, self.space,
+ ["_CM_KEY_NODE"]))
+ return lst
+
+ List = property(fget=getList)
diff --git a/usr/local/bin/framework/object.py b/usr/local/bin/framework/object.py
new file mode 100644
index 0000000..d11243d
--- /dev/null
+++ b/usr/local/bin/framework/object.py
@@ -0,0 +1,171 @@
+# Volatools Basic
+# Copyright (C) 2007 Komoku, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+# pylint: disable=invalid-name,missing-docstring
+
+"""
+@author: AAron Walters and Nick Petroni
+@license: GNU General Public License 2.0 or later
+@contact: awalters@komoku.com, npetroni@komoku.com
+@organization: Komoku, Inc.
+"""
+
+import struct
+
+builtin_types = {
+ 'int': (4, 'i'),
+ 'long': (4, 'i'),
+ 'unsigned long': (4, 'I'),
+ 'unsigned int': (4, 'I'),
+ 'address': (4, 'I'),
+ 'char': (1, 'c'),
+ 'unsigned char': (1, 'B'),
+ 'unsigned short': (2, 'H'),
+ 'short': (2, 'h'),
+ 'long long': (8, 'q'),
+ 'unsigned long long': (8, 'Q'),
+ 'pointer': (4, 'I'),
+}
+
+
+def obj_size(types, objname):
+ if objname not in types:
+ raise Exception('Invalid type %s not in types' % (objname))
+
+ return types[objname][0]
+
+
+def builtin_size(builtin):
+ if builtin not in builtin_types:
+ raise Exception('Invalid built-in type %s' % (builtin))
+
+ return builtin_types[builtin][0]
+
+
+def read_value(addr_space, value_type, vaddr):
+ """
+ Read the low-level value for a built-in type.
+ """
+
+ if value_type not in builtin_types:
+ raise Exception('Invalid built-in type %s' % (value_type))
+
+ type_unpack_char = builtin_types[value_type][1]
+ type_size = builtin_types[value_type][0]
+
+ buf = addr_space.read(vaddr, type_size)
+ if buf is None:
+ return None
+ (val,) = struct.unpack(type_unpack_char, buf)
+
+ return val
+
+
+def read_unicode_string(addr_space, types, member_list, vaddr):
+ offset = 0
+ if len(member_list) > 1:
+ (offset, __) = get_obj_offset(types, member_list)
+
+ buf = read_obj(addr_space, types, ['_UNICODE_STRING', 'Buffer'], vaddr + offset)
+ length = read_obj(addr_space, types, ['_UNICODE_STRING', 'Length'], vaddr + offset)
+
+ if length == 0x0:
+ return ""
+
+ if buf is None or length is None:
+ return None
+
+ readBuf = read_string(addr_space, types, ['char'], buf, length)
+
+ if readBuf is None:
+ return None
+
+ try:
+ readBuf = readBuf.decode('UTF-16').encode('ascii')
+ except Exception: # pylint: disable=broad-except
+ return None
+
+ return readBuf
+
+
+def read_string(addr_space, types, member_list, vaddr, max_length=256):
+ offset = 0
+ if len(member_list) > 1:
+ (offset, __) = get_obj_offset(types, member_list)
+
+ val = addr_space.read(vaddr + offset, max_length)
+
+ return val
+
+
+def read_null_string(addr_space, types, member_list, vaddr, max_length=256):
+ string = read_string(addr_space, types, member_list, vaddr, max_length)
+
+ if string is None:
+ return None
+
+ return string.split('\0', 1)[0]
+
+
+def get_obj_offset(types, member_list):
+ """
+ Returns the (offset, type) pair for a given list
+ """
+ member_list.reverse()
+
+ current_type = member_list.pop()
+
+ offset = 0
+
+ while member_list:
+ if current_type == 'array':
+ current_type = member_dict[current_member][1][2][0]
+ if current_type in builtin_types:
+ current_type_size = builtin_size(current_type)
+ else:
+ current_type_size = obj_size(types, current_type)
+ index = member_list.pop()
+ offset += index * current_type_size
+ continue
+
+ elif current_type not in types:
+ raise Exception('Invalid type ' + current_type)
+
+ member_dict = types[current_type][1]
+
+ current_member = member_list.pop()
+ if current_member not in member_dict:
+ raise Exception('Invalid member %s in type %s' % (current_member, current_type))
+
+ offset += member_dict[current_member][0]
+
+ current_type = member_dict[current_member][1][0]
+
+ return (offset, current_type)
+
+
+def read_obj(addr_space, types, member_list, vaddr):
+ """
+ Read the low-level value for some complex type's member.
+ The type must have members.
+ """
+ if len(member_list) < 2:
+ raise Exception('Invalid type/member ' + str(member_list))
+
+ (offset, current_type) = get_obj_offset(types, member_list)
+ return read_value(addr_space, current_type, vaddr + offset)
diff --git a/usr/local/bin/framework/types.py b/usr/local/bin/framework/types.py
new file mode 100644
index 0000000..cbf8b4f
--- /dev/null
+++ b/usr/local/bin/framework/types.py
@@ -0,0 +1,65 @@
+# This file is part of creddump.
+#
+# creddump is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# creddump is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with creddump. If not, see .
+
+# pylint: disable=invalid-name
+
+"""
+@author: Brendan Dolan-Gavitt
+@license: GNU General Public License 2.0 or later
+@contact: bdolangavitt@wesleyan.edu
+"""
+
+regtypes = {
+ '_CM_KEY_VALUE': [0x18, {
+ 'Signature': [0x0, ['unsigned short']],
+ 'NameLength': [0x2, ['unsigned short']],
+ 'DataLength': [0x4, ['unsigned long']],
+ 'Data': [0x8, ['unsigned long']],
+ 'Type': [0xc, ['unsigned long']],
+ 'Flags': [0x10, ['unsigned short']],
+ 'Spare': [0x12, ['unsigned short']],
+ 'Name': [0x14, ['array', 1, ['unsigned short']]],
+ }],
+ '_CM_KEY_NODE': [0x50, {
+ 'Signature': [0x0, ['unsigned short']],
+ 'Flags': [0x2, ['unsigned short']],
+ 'LastWriteTime': [0x4, ['_LARGE_INTEGER']],
+ 'Spare': [0xc, ['unsigned long']],
+ 'Parent': [0x10, ['unsigned long']],
+ 'SubKeyCounts': [0x14, ['array', 2, ['unsigned long']]],
+ 'SubKeyLists': [0x1c, ['array', 2, ['unsigned long']]],
+ 'ValueList': [0x24, ['_CHILD_LIST']],
+ 'ChildHiveReference': [0x1c, ['_CM_KEY_REFERENCE']],
+ 'Security': [0x2c, ['unsigned long']],
+ 'Class': [0x30, ['unsigned long']],
+ 'MaxNameLen': [0x34, ['unsigned long']],
+ 'MaxClassLen': [0x38, ['unsigned long']],
+ 'MaxValueNameLen': [0x3c, ['unsigned long']],
+ 'MaxValueDataLen': [0x40, ['unsigned long']],
+ 'WorkVar': [0x44, ['unsigned long']],
+ 'NameLength': [0x48, ['unsigned short']],
+ 'ClassLength': [0x4a, ['unsigned short']],
+ 'Name': [0x4c, ['array', 1, ['unsigned short']]],
+ }],
+ '_CM_KEY_INDEX': [0x8, {
+ 'Signature': [0x0, ['unsigned short']],
+ 'Count': [0x2, ['unsigned short']],
+ 'List': [0x4, ['array', 1, ['unsigned long']]],
+ }],
+ '_CHILD_LIST': [0x8, {
+ 'Count': [0x0, ['unsigned long']],
+ 'List': [0x4, ['unsigned long']],
+ }],
+}
diff --git a/usr/local/bin/framework/win32/__init__.py b/usr/local/bin/framework/win32/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/usr/local/bin/framework/win32/__pycache__/__init__.cpython-38.pyc b/usr/local/bin/framework/win32/__pycache__/__init__.cpython-38.pyc
new file mode 100644
index 0000000..ce0d731
Binary files /dev/null and b/usr/local/bin/framework/win32/__pycache__/__init__.cpython-38.pyc differ
diff --git a/usr/local/bin/framework/win32/__pycache__/hashdump.cpython-38.pyc b/usr/local/bin/framework/win32/__pycache__/hashdump.cpython-38.pyc
new file mode 100644
index 0000000..a572862
Binary files /dev/null and b/usr/local/bin/framework/win32/__pycache__/hashdump.cpython-38.pyc differ
diff --git a/usr/local/bin/framework/win32/__pycache__/rawreg.cpython-38.pyc b/usr/local/bin/framework/win32/__pycache__/rawreg.cpython-38.pyc
new file mode 100644
index 0000000..00de15f
Binary files /dev/null and b/usr/local/bin/framework/win32/__pycache__/rawreg.cpython-38.pyc differ
diff --git a/usr/local/bin/framework/win32/domcachedump.py b/usr/local/bin/framework/win32/domcachedump.py
new file mode 100644
index 0000000..542ce41
--- /dev/null
+++ b/usr/local/bin/framework/win32/domcachedump.py
@@ -0,0 +1,135 @@
+# This file is part of creddump.
+#
+# creddump is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# creddump is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with creddump. If not, see .
+
+"""
+@author: Brendan Dolan-Gavitt
+@license: GNU General Public License 2.0 or later
+@contact: bdolangavitt@wesleyan.edu
+"""
+
+from framework.win32.rawreg import *
+from framework.addrspace import HiveFileAddressSpace
+from framework.win32.hashdump import get_bootkey
+from framework.win32.lsasecrets import get_secret_by_name,get_lsa_key
+from Crypto.Hash import HMAC
+from Crypto.Cipher import ARC4, AES
+from struct import unpack
+
+def get_nlkm(secaddr, lsakey, vista):
+ return get_secret_by_name(secaddr, 'NL$KM', lsakey, vista)
+
+def decrypt_hash(edata, nlkm, ch):
+ hmac_md5 = HMAC.new(nlkm,ch)
+ rc4key = hmac_md5.digest()
+
+ rc4 = ARC4.new(rc4key)
+ data = rc4.encrypt(edata)
+ return data
+
+def decrypt_hash_vista(edata, nlkm, ch):
+ """
+ Based on code from http://lab.mediaservice.net/code/cachedump.rb
+ """
+ aes = AES.new(nlkm[16:32], AES.MODE_CBC, ch)
+
+ out = bytearray()
+ for i in range(0, len(edata), 16):
+ buf = edata[i : i+16]
+ if len(buf) < 16:
+ buf += (16 - len(buf)) * b"\00"
+
+ out += aes.decrypt(buf)
+ return out
+
+def parse_cache_entry(cache_data):
+ (uname_len, domain_len) = unpack(".
+
+# pylint: disable=invalid-name,missing-docstring
+
+"""
+@author: Brendan Dolan-Gavitt
+@license: GNU General Public License 2.0 or later
+@contact: bdolangavitt@wesleyan.edu
+"""
+
+from struct import unpack, pack
+import binascii
+
+from Crypto.Hash import MD5
+from Crypto.Cipher import ARC4, DES, AES
+
+from framework.win32.rawreg import get_root, open_key, values, subkeys
+from framework.addrspace import HiveFileAddressSpace
+
+odd_parity = [
+ 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
+ 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
+ 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
+ 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
+ 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
+ 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
+ 97, 97, 98, 98, 100, 100, 103, 103, 104, 104, 107, 107, 109, 109, 110, 110,
+ 112, 112, 115, 115, 117, 117, 118, 118, 121, 121, 122, 122, 124, 124, 127, 127,
+ 128, 128, 131, 131, 133, 133, 134, 134, 137, 137, 138, 138, 140, 140, 143, 143,
+ 145, 145, 146, 146, 148, 148, 151, 151, 152, 152, 155, 155, 157, 157, 158, 158,
+ 161, 161, 162, 162, 164, 164, 167, 167, 168, 168, 171, 171, 173, 173, 174, 174,
+ 176, 176, 179, 179, 181, 181, 182, 182, 185, 185, 186, 186, 188, 188, 191, 191,
+ 193, 193, 194, 194, 196, 196, 199, 199, 200, 200, 203, 203, 205, 205, 206, 206,
+ 208, 208, 211, 211, 213, 213, 214, 214, 217, 217, 218, 218, 220, 220, 223, 223,
+ 224, 224, 227, 227, 229, 229, 230, 230, 233, 233, 234, 234, 236, 236, 239, 239,
+ 241, 241, 242, 242, 244, 244, 247, 247, 248, 248, 251, 251, 253, 253, 254, 254
+]
+
+# Permutation matrix for boot key
+p = [0x8, 0x5, 0x4, 0x2, 0xb, 0x9, 0xd, 0x3,
+ 0x0, 0x6, 0x1, 0xc, 0xe, 0xa, 0xf, 0x7]
+
+# Constants for SAM decrypt algorithm
+aqwerty = b"!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\0"
+anum = b"0123456789012345678901234567890123456789\0"
+antpassword = b"NTPASSWORD\0"
+almpassword = b"LMPASSWORD\0"
+
+empty_lm = binascii.unhexlify("aad3b435b51404eeaad3b435b51404ee")
+empty_nt = binascii.unhexlify("31d6cfe0d16ae931b73c59d7e0c089c0")
+
+
+def str_to_key(s):
+ key = bytearray()
+ key.append(s[0] >> 1)
+ key.append(((s[0] & 0x01) << 6) | ((s[1]) >> 2))
+ key.append(((s[1] & 0x03) << 5) | ((s[2]) >> 3))
+ key.append(((s[2] & 0x07) << 4) | ((s[3]) >> 4))
+ key.append(((s[3] & 0x0F) << 3) | ((s[4]) >> 5))
+ key.append(((s[4] & 0x1F) << 2) | ((s[5]) >> 6))
+ key.append(((s[5] & 0x3F) << 1) | ((s[6]) >> 7))
+ key.append(s[6] & 0x7F)
+ for i in range(8):
+ key[i] = (key[i] << 1)
+ key[i] = odd_parity[key[i]]
+ return key
+
+
+def sid_to_key(sid):
+ s1 = bytearray()
+ s1.append(sid & 0xFF)
+ s1.append((sid >> 8) & 0xFF)
+ s1.append((sid >> 16) & 0xFF)
+ s1.append((sid >> 24) & 0xFF)
+ s1.append(s1[0])
+ s1.append(s1[1])
+ s1.append(s1[2])
+ s2 = bytearray([s1[3], s1[0], s1[1], s1[2]])
+ s2.append(s2[0])
+ s2.append(s2[1])
+ s2.append(s2[2])
+
+ return str_to_key(s1), str_to_key(s2)
+
+
+def find_control_set(sysaddr):
+ root = get_root(sysaddr)
+ if not root:
+ return 1
+
+ csselect = open_key(root, ["Select"])
+ if not csselect:
+ return 1
+
+ for v in values(csselect):
+ if v.Name == b"Current":
+ return v.Data.value
+
+ return 1
+
+
+def get_bootkey(sysaddr):
+ cs = find_control_set(sysaddr)
+ lsa_base = ["ControlSet%03d" % cs, "Control", "Lsa"]
+ lsa_keys = ["JD", "Skew1", "GBG", "Data"]
+
+ root = get_root(sysaddr)
+ if not root:
+ return None
+
+ lsa = open_key(root, lsa_base)
+ if not lsa:
+ return None
+
+ bootkey = []
+
+ for lk in lsa_keys:
+ key = open_key(lsa, [lk])
+ class_data = sysaddr.read(key.Class.value, key.ClassLength.value)
+ hex_string = class_data.decode('utf-16-le')
+ hex_data = binascii.unhexlify(hex_string)
+ for h in hex_data:
+ bootkey.append(h)
+
+ bootkey_scrambled = []
+ for i in range(len(bootkey)):
+ bootkey_scrambled.append(bootkey[p[i]])
+
+ return bytes(bootkey_scrambled)
+
+
+def get_hbootkey(samaddr, bootkey):
+ sam_account_path = ["SAM", "Domains", "Account"]
+
+ root = get_root(samaddr)
+ if not root:
+ return None
+
+ sam_account_key = open_key(root, sam_account_path)
+ if not sam_account_key:
+ return None
+
+ F = None
+ for v in values(sam_account_key):
+ if v.Name == b'F':
+ F = samaddr.read(v.Data.value, v.DataLength.value)
+ if not F:
+ return None
+
+ revision = F[0x00]
+ if revision == 2:
+ md5 = MD5.new()
+ md5.update(F[0x70:0x80] + aqwerty + bootkey + anum)
+ rc4_key = md5.digest()
+
+ rc4 = ARC4.new(rc4_key)
+ hbootkey = rc4.encrypt(F[0x80:0xA0])
+
+ return hbootkey
+
+ if revision == 3:
+ iv = F[0x78:0x88]
+ encryptedHBootKey = F[0x88:0xA8]
+ cipher = AES.new(bootkey, AES.MODE_CBC, iv)
+ hbootkey = cipher.decrypt(encryptedHBootKey)
+
+ return hbootkey[:16]
+
+ print("Unknown revision: %d" % revision)
+ return None
+
+def get_user_keys(samaddr):
+ user_key_path = ["SAM", "Domains", "Account", "Users"]
+
+ root = get_root(samaddr)
+ if not root:
+ return []
+
+ user_key = open_key(root, user_key_path)
+ if not user_key:
+ return []
+
+ return [k for k in subkeys(user_key) if k.Name != b"Names"]
+
+
+def decrypt_single_hash(rid, hbootkey, enc_hash, lmntstr):
+ if enc_hash == "":
+ return ""
+ (des_k1, des_k2) = sid_to_key(rid)
+ d1 = DES.new(des_k1, DES.MODE_ECB)
+ d2 = DES.new(des_k2, DES.MODE_ECB)
+ md5 = MD5.new()
+ md5.update(hbootkey[:0x10] + pack(".
+
+# pylint: disable=missing-docstring
+
+"""
+@author: Brendan Dolan-Gavitt
+@license: GNU General Public License 2.0 or later
+@contact: bdolangavitt@wesleyan.edu
+"""
+
+from Crypto.Hash import MD5, SHA256
+from Crypto.Cipher import ARC4, DES, AES
+
+from framework.win32.rawreg import get_root, open_key, subkeys, unpack
+from framework.addrspace import HiveFileAddressSpace
+from framework.win32.hashdump import get_bootkey, str_to_key
+
+
+def get_lsa_key(secaddr, bootkey, vista):
+ root = get_root(secaddr)
+ if not root:
+ return None
+
+ if vista:
+ enc_reg_key = open_key(root, ["Policy", "PolEKList"])
+ else:
+ enc_reg_key = open_key(root, ["Policy", "PolSecretEncryptionKey"])
+
+ if not enc_reg_key:
+ return None
+
+ enc_reg_value = enc_reg_key.ValueList.List[0]
+ if not enc_reg_value:
+ return None
+
+ obf_lsa_key = secaddr.read(enc_reg_value.Data.value,
+ enc_reg_value.DataLength.value)
+ if not obf_lsa_key:
+ return None
+
+ if not vista:
+ md5 = MD5.new()
+ md5.update(bootkey)
+ for __ in range(1000):
+ md5.update(obf_lsa_key[60:76])
+ rc4key = md5.digest()
+ rc4 = ARC4.new(rc4key)
+ lsa_key = rc4.decrypt(obf_lsa_key[12:60])
+ lsa_key = lsa_key[0x10:0x20]
+ else:
+ lsa_key = decrypt_aes(obf_lsa_key, bootkey)
+ lsa_key = lsa_key[68:100]
+
+ return lsa_key
+
+
+def decrypt_secret(secret, key):
+ """Python implementation of SystemFunction005.
+
+ Decrypts a block of data with DES using given key.
+ Note that key can be longer than 7 bytes."""
+ decrypted_data = bytearray()
+ j = 0 # key index
+ for i in range(0, len(secret), 8):
+ enc_block = secret[i:i + 8]
+ block_key = key[j:j + 7]
+ des_key = str_to_key(block_key)
+
+ des = DES.new(des_key, DES.MODE_ECB)
+ decrypted_data += des.decrypt(enc_block)
+
+ j += 7
+ if len(key[j:j + 7]) < 7:
+ j = len(key[j:j + 7])
+
+ (dec_data_len,) = unpack(".
+
+"""
+@author: Brendan Dolan-Gavitt
+@license: GNU General Public License 2.0 or later
+@contact: bdolangavitt@wesleyan.edu
+"""
+
+from framework.newobj import Obj,Pointer
+from struct import unpack
+
+ROOT_INDEX = 0x20
+LH_SIG = unpack(" 0:
+ tam -= tam_xor
+ magic += magic_static
+print u'\tUsed Magic Xor: 0x{}.'.format(magic)
+
+i = 0
+while i < len(kcpasswd):
+ charkc = kcpasswd[i] + kcpasswd[i+1]
+ charkch = int(charkc, 16)
+ charm = magic[i] + magic[i+1]
+ charmh = int(charm, 16)
+ r = charkch ^ charmh
+ pwd += chr(r)
+ if r == 0:
+ print '\n\tThe password is: "{}".\n'.format(pwd.rstrip('\0'))
+ break
+ i += 2
diff --git a/usr/local/bin/print_plist_entry.py b/usr/local/bin/print_plist_entry.py
new file mode 100755
index 0000000..da97a16
--- /dev/null
+++ b/usr/local/bin/print_plist_entry.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python3
+#===============================================================================
+# DIRECTORY:
+# /usr/local/bin
+#
+# FILE:
+# print_plist_entry.py
+#
+# USAGE:
+# ./print_plist_entry.py
+# OR
+# python3 print_plist_entry.py
+#
+# OPTIONS:
+# none
+#
+# DESCRIPTION:
+# Prints key-value pair of key in plist
+#
+# REQUIREMENTS:
+# python3 and python-plistlib
+#
+# BUGS:
+# ---
+#
+# NOTES:
+# Tested on:
+# - Arch Linux
+#
+# AUTHOR:
+# Patrick Neumann, patrick@neumannsland.de
+#
+# COMPANY:
+# (privately)
+#
+# VERSION:
+# 0.9 (beta)
+#
+# LINK TO THE MOST CURRENT VERSIONS:
+# https://...
+#
+# CREATED:
+# 17.11.2020
+#
+# COPYRIGHT (C):
+# 2015-2020 - Patrick Neumann
+#
+# LICENSE:
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# WARRANTY:
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# TODO:
+# ---
+#
+# HISTORY:
+# 0.9 - Patrick Neumann - Initial (public) release
+#
+#===============================================================================
+
+import os.path
+import plistlib
+import sys
+
+# short help for new users
+def usage():
+ print( "\nUsage: python print_plist_entry.py \n" )
+ print( " (use the \"ALL\" to list all entries.)\n" )
+
+# check for first parameter
+try:
+ plist = sys.argv[1]
+except IndexError:
+ print( "\nError: No (file) given!" )
+ usage()
+ sys.exit( 1 )
+
+# check if first parameter is a file
+if not os.path.isfile( plist ):
+ print( "\nError: plist (file) does not exist!\n" )
+ sys.exit( 1 )
+
+# check for second parameter
+try:
+ key = sys.argv[2]
+except IndexError:
+ print( "\nError: No (string) given!" )
+ usage()
+ sys.exit( 1 )
+
+# plist has to be a XML file
+try:
+ with open( plist, 'rb' ) as fp:
+ content = plistlib.load( fp )
+except:
+ print( "\nError: plist is not a XML file!\n" )
+ sys.exit( 1 )
+
+try:
+ print( key + ":", content[ key ] )
+except:
+ print( "\nError: key not found!\n" )
+ sys.exit( 1 )
+
+sys.exit( 0 )
diff --git a/usr/local/bin/pwdump.py b/usr/local/bin/pwdump.py
new file mode 100755
index 0000000..462df85
--- /dev/null
+++ b/usr/local/bin/pwdump.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+
+# This file is part of creddump.
+#
+# creddump is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# creddump is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with creddump. If not, see .
+
+"""
+@author: Brendan Dolan-Gavitt
+@license: GNU General Public License 2.0 or later
+@contact: bdolangavitt@wesleyan.edu
+"""
+
+import sys
+from framework.win32.hashdump import dump_file_hashes
+
+if len(sys.argv) < 3:
+ print("usage: %s " % sys.argv[0])
+ sys.exit(1)
+
+dump_file_hashes(sys.argv[1], sys.argv[2])
diff --git a/usr/local/bin/sigfind b/usr/local/bin/sigfind
new file mode 100755
index 0000000..690d331
Binary files /dev/null and b/usr/local/bin/sigfind differ
diff --git a/usr/local/bin/sigfind.cpp b/usr/local/bin/sigfind.cpp
new file mode 100644
index 0000000..ab330db
--- /dev/null
+++ b/usr/local/bin/sigfind.cpp
@@ -0,0 +1,607 @@
+/*
+ * The Sleuth Kit
+ *
+ * Brian Carrier [carrier sleuthkit [dot] org]
+ * Copyright (c) 2004 Brian Carrier. All rights reserved
+ *
+ * sigfind
+ *
+ * This software is distributed under the Common Public License 1.0
+ */
+
+#include "tsk/tsk_tools_i.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static char *progname;
+
+int opterr = 1, /* if error message should be printed */
+optind = 1, /* index into parent argv vector */
+optopt, /* character checked for validity */
+optreset; /* reset getopt */
+char *optarg; /* argument associated with option */
+
+#define BADCH (int)'?'
+#define BADARG (int)':'
+#define EMSG ""
+
+
+void printSectorMd5(TSK_MD5_CTX *md){
+
+ unsigned char hash[16];
+
+ TSK_MD5_Final(hash, md);
+ printf("MD5: ");
+ for (int x = 0; x < 16; x++) {
+ printf("%x%x", (hash[x] >> 4) & 0xf, hash[x] & 0xf);
+ }
+ printf("\n");
+
+}
+
+
+uint8_t* changeEndian(uint8_t* sig, int sig_size){
+ uint8_t tmp;
+
+ if (sig_size == 2) {
+ tmp = sig[1];
+ sig[1] = sig[0];
+ sig[0] = tmp;
+ }
+ else if (sig_size == 3) {
+ tmp = sig[2];
+ sig[2] = sig[0];
+ sig[0] = tmp;
+ }
+ else if (sig_size == 4) {
+ tmp = sig[3];
+ sig[3] = sig[0];
+ sig[0] = tmp;
+
+ tmp = sig[2];
+ sig[2] = sig[1];
+ sig[1] = tmp;
+ }
+ return sig;
+}
+
+/*
+* getopt --
+* Parse argc/argv argument vector.
+*/
+int
+getopt(int nargc, char * const nargv[], const char *ostr)
+{
+ static char *place = EMSG; /* option letter processing */
+ const char *oli; /* option letter list index */
+
+ if (optreset || !*place) { /* update scanning pointer */
+ optreset = 0;
+ if (optind >= nargc || *(place = nargv[optind]) != '-') {
+ place = EMSG;
+ return (-1);
+ }
+ if (place[1] && *++place == '-') { /* found "--" */
+ ++optind;
+ place = EMSG;
+ return (-1);
+ }
+ } /* option letter okay? */
+ if ((optopt = (int)*place++) == (int)':' ||
+ !(oli = strchr(ostr, optopt))) {
+ /*
+ * if the user didn't specify '-' as an option,
+ * assume it means -1.
+ */
+ if (optopt == (int)'-')
+ return (-1);
+ if (!*place)
+ ++optind;
+ if (opterr && *ostr != ':')
+ (void)printf("illegal option -- %c\n", optopt);
+ return (BADCH);
+ }
+ if (*++oli != ':') { /* don't need argument */
+ optarg = NULL;
+ if (!*place)
+ ++optind;
+ }
+ else { /* need an argument */
+ if (*place) /* no white space */
+ optarg = place;
+ else if (nargc <= ++optind) { /* no arg */
+ place = EMSG;
+ if (*ostr == ':')
+ return (BADARG);
+ if (opterr)
+ (void)printf("option requires an argument -- %c\n", optopt);
+ return (BADCH);
+ }
+ else /* white space */
+ optarg = nargv[optind];
+ place = EMSG;
+ ++optind;
+ }
+ return (optopt); /* dump back option letter */
+}
+
+void
+usage()
+{
+ fprintf(stderr,
+ "%s [-b bsize] [-o offset] [-t template] [-lV] [hex_signature] file\n",
+ progname);
+ fprintf(stderr, "\t-b bsize: Give block size (default 512)\n");
+ fprintf(stderr,
+ "\t-o offset: Give offset into block where signature should exist (default 0)\n");
+ fprintf(stderr, "\t-l: Signature will be little endian in image\n");
+ fprintf(stderr, "\t-V: Version\n");
+ fprintf(stderr, "\t-r: rsize: Give the block size to read (default 512)\n");
+ fprintf(stderr,
+ "\t-t template: The name of a data structure template:\n");
+ fprintf(stderr,
+ "\t\tdospart, ext2, ext3, ext4, fat, hfs, hfs+, ufs1, ufs2\n");
+ fprintf(stderr,
+ "\t\t * New templates: gpt, fat32, fat12or16, exfat, ntfs, refs, hfsx, apfs\n\n");
+ fprintf(stderr,
+ "Modified version running on Windows, also included more advanced Windows FS templates\n");
+ fprintf(stderr,
+ "Modified by Rune Nordvik - 2015/12/17\n");
+ exit(1);
+}
+
+// @@@ Should have a big endian flag as well
+int
+main(int argc, char **argv)
+{
+ int ch;
+ uint8_t sig[4] = { 0, 0, 0, 0 };
+ uint8_t sig2[4] = { 0, 0, 0, 0 }; // Another signature
+ uint8_t block[4096];
+
+ char **err = NULL;
+ TSK_IMG_INFO *img_info;
+ TSK_OFF_T cur_offset;
+ int sig_offset = 0, rel_offset = 0;
+ int read_size=512, bs = 512;
+ int sig2_offset = 0, rel2_offset = 0; // Added offsets for sig 2
+ bool hasHits = false; // Since prec_hit is unsigned we can not use -1 for it
+ TSK_DADDR_T i, prev_hit; // Must be unsigned because of the large number of sectors
+ TSK_DADDR_T * sectors64bitInVolume = NULL;
+ int sig_size = 0;
+ int sig2_size = 0;
+ uint8_t lit_end = 0;
+ int sig_print = 0, sig2_print = 0;
+ TSK_MD5_CTX md;
+
+
+
+ progname = argv[0];
+
+ while ((ch = getopt(argc, argv, "b:r:lo:t:V")) > 0) {
+ switch (ch) {
+ case 'b':
+ bs = strtol(optarg, err, 10);
+ if ((bs == 0) || (errno == EINVAL)) {
+ fprintf(stderr, "Error converting block size: %s\n",
+ optarg);
+ exit(1);
+ }
+
+ if (bs % 512) {
+ fprintf(stderr, "Invalid block size\n");
+ exit(1);
+ }
+ break;
+ case 'l':
+ lit_end = 1;
+ break;
+
+ case 'o':
+
+ /* Get the sig_offset in the sector */
+ sig_offset = strtol(optarg, err, 10);
+ if ((sig_offset == 0) || (errno == EINVAL)) {
+ fprintf(stderr, "Error converting offset value: %s\n",
+ optarg);
+ exit(1);
+ }
+ break;
+
+ case 't':
+ if ((strcmp(optarg, "ext2") == 0) ||
+ (strcmp(optarg, "ext3") == 0) ||
+ (strcmp(optarg, "ext4") == 0)) {
+ lit_end = 1;
+ sig[0] = 0x53;
+ sig[1] = 0xef;
+ sig_size = 2;
+ sig_offset = 56;
+ bs = 512;
+ }
+ else if (strcmp(optarg, "apfs") == 0) {
+ lit_end = 1;
+ sig[0] = 0x4E; // N
+ sig[1] = 0x58; // X
+ sig[2] = 0x53; // S
+ sig[3] = 0x42; // B
+ sig_size = 4;
+ sig_offset = 32;
+ bs = 512;
+ }
+ else if ((strcmp(optarg, "dospart") == 0) ||
+ (strcmp(optarg, "fat") == 0)) {
+ lit_end = 1;
+ sig[0] = 0x55;
+ sig[1] = 0xaa;
+ sig_size = 2;
+ sig_offset = 510;
+ bs = 512;
+ }
+ else if (strcmp(optarg, "gpt") == 0) {
+ lit_end = 1;
+ sig[0] = 0x55;
+ sig[1] = 0xaa;
+ sig2[0] = 0x45; // E
+ sig2[1] = 0x46; // F
+ sig2[2] = 0x49; // I
+ sig2[3] = 0x20; //
+ sig_size = 2;
+ sig2_size = 4;
+ sig_offset = 510;
+ sig2_offset = 0;
+ bs = 512;
+
+ }
+ else if (strcmp(optarg, "ntfs") == 0) {
+ lit_end = 1;
+ sig[0] = 0x55;
+ sig[1] = 0xaa;
+ sig2[0] = 0x4e; // N
+ sig2[1] = 0x54; // T
+ sig2[2] = 0x46; // F
+ sig2[3] = 0x53; // S
+ sig_size = 2;
+ sig2_size = 4;
+ sig_offset = 510;
+ sig2_offset = 3;
+ bs = 512;
+
+ }
+ else if (strcmp(optarg, "fat32") == 0) {
+ lit_end = 1;
+ sig[0] = 0x55;
+ sig[1] = 0xaa;
+ sig2[0] = 0x46; // F
+ sig2[1] = 0x41; // A
+ sig2[2] = 0x54; // T
+ sig2[3] = 0x33; // 3
+ sig_size = 2;
+ sig2_size = 4;
+ sig_offset = 510;
+ sig2_offset = 82;
+ bs = 512;
+ }
+ else if (strcmp(optarg, "fat12or16") == 0) {
+ lit_end = 1;
+ sig[0] = 0x55;
+ sig[1] = 0xaa;
+ sig2[0] = 0x46; // F
+ sig2[1] = 0x41; // A
+ sig2[2] = 0x54; // T
+ sig2[3] = 0x31; // 1
+ sig_size = 2;
+ sig2_size = 4;
+ sig_offset = 510;
+ sig2_offset = 54;
+ bs = 512;
+ }
+ else if (strcmp(optarg, "exfat") == 0) {
+ lit_end = 1;
+ sig[0] = 0x55;
+ sig[1] = 0xaa;
+ sig2[0] = 0x45; // E
+ sig2[1] = 0x58; // X
+ sig2[2] = 0x46; // F
+ sig2[3] = 0x41; // A
+ sig_size = 2;
+ sig2_size = 4;
+ sig_offset = 510;
+ sig2_offset = 3;
+ bs = 512;
+ }
+ else if (strcmp(optarg, "refs") == 0) {
+ lit_end = 1;
+ sig[0] = 0x52; // R
+ sig[1] = 0x65; // e
+ sig[2] = 0x46; // F
+ sig[3] = 0x53; // S
+ sig2[0] = 0x46; // F(ile)
+ sig2[1] = 0x53; // S(ystem)
+ sig2[2] = 0x52; // R(ecognition)
+ sig2[3] = 0x53; // S(tructure)
+ sig_size = 4;
+ sig2_size = 4;
+ sig_offset = 3;
+ sig2_offset = 16;
+ bs = 512;
+ }
+ else if (strcmp(optarg, "ufs1") == 0) {
+ lit_end = 1;
+ sig[0] = 0x54;
+ sig[1] = 0x19;
+ sig[2] = 0x01;
+ sig[3] = 0x00;
+ sig_size = 4;
+ /* Located 1372 into SB */
+ sig_offset = 348;
+ bs = 512;
+ }
+ else if (strcmp(optarg, "ufs2") == 0) {
+ lit_end = 1;
+ sig[0] = 0x19;
+ sig[1] = 0x01;
+ sig[2] = 0x54;
+ sig[3] = 0x19;
+ sig_size = 4;
+ /* Located 1372 into SB */
+ sig_offset = 348;
+ bs = 512;
+ }
+ else if (strcmp(optarg, "hfs+") == 0) {
+ lit_end = 1;
+ sig[0] = 0x48;
+ sig[1] = 0x2b;
+ sig[2] = 0x00;
+ sig[3] = 0x04;
+ sig_size = 4;
+ /* Located 1024 into image */
+ sig_offset = 0;
+ bs = 512;
+ }
+ else if (strcmp(optarg, "hfsx") == 0) {
+ lit_end = 1;
+ sig[0] = 0x48; // H
+ sig[1] = 0x58; // X
+ sig[2] = 0x00;
+ sig[3] = 0x05; // 5 - Version for HFSX
+ sig_size = 4;
+ /* Located 1024 into image */
+ sig_offset = 0;
+ bs = 512;
+ }
+ else if (strcmp(optarg, "hfs") == 0) {
+ lit_end = 1;
+ sig[0] = 0x42;
+ sig[1] = 0x44;
+ sig_size = 2;
+ /* Located 1024 into image */
+ sig_offset = 0;
+ bs = 512;
+ }
+ else {
+ fprintf(stderr, "Invalid template\n");
+ exit(1);
+ }
+ break;
+
+ case 'V':
+ tsk_version_print(stdout);
+ exit(0);
+ case 'r':
+ // Size of block to read
+ read_size = strtol(optarg, err, 10);
+ if ((read_size == 0) || (errno == EINVAL)) {
+ fprintf(stderr, "Error converting block size: %s\n",
+ optarg);
+ exit(1);
+ }
+ break;
+ default:
+ usage();
+ }
+ }
+
+
+ /* If we didn't get a template then check the cmd line */
+ if (sig_size == 0) {
+ if (optind + 1 > argc) {
+ usage();
+ }
+ /* Get the hex value */
+ sig_size = 0;
+ for (i = 0; i < 9; i++) {
+ uint8_t tmp;
+ tmp = argv[optind][i];
+
+ if (tmp == 0) {
+ if (i % 2) {
+ fprintf(stderr,
+ "Invaild signature - full bytes only\n");
+ exit(1);
+ }
+ break;
+ }
+
+ /* Digit */
+ if ((tmp >= 0x30) && (tmp <= 0x39)) {
+ tmp -= 0x30;
+ }
+ /* lowercase a-f */
+ else if ((tmp >= 0x61) && (tmp <= 0x66)) {
+ tmp -= 0x57;
+ }
+ else if ((tmp >= 0x41) && (tmp <= 0x46)) {
+ tmp -= 0x37;
+ }
+ else {
+ fprintf(stderr, "Invalid signature value: %c\n", tmp);
+ exit(1);
+ }
+
+ /* big nibble */
+ if (0 == (i % 2)) {
+ sig[sig_size] = 16 * tmp;
+ }
+ else {
+ sig[sig_size] += tmp;
+ sig_size++;
+ }
+ }
+ optind++;
+
+ /* Check the signature length */
+ if (i == 9) {
+ fprintf(stderr,
+ "Error: Maximum supported signature size is 4 bytes\n");
+ exit(1);
+ }
+
+
+ /* Need to switch order */
+ if (lit_end) {
+ // call change endian order
+ uint8_t* pointerToSig = 0;
+ pointerToSig = changeEndian(sig, sig_size);
+ for (int i = 0; i < sig_size; i++)
+ sig[i] = pointerToSig[i];
+ if (0 != sig2[0]){
+ pointerToSig = changeEndian(sig2, sig2_size);
+ for (int i = 0; i < sig2_size; i++)
+ sig2[i] = pointerToSig[i];
+ }
+ }
+ }
+
+ if (sig_offset < 0) {
+ fprintf(stderr, "Error: negative signature offset\n");
+ exit(1);
+ }
+
+
+ /* Check that the signature and offset are not larger than a block */
+ if ((sig_offset + sig_size) > bs) {
+ fprintf(stderr,
+ "Error: The offset and signature sizes are greater than the block size\n");
+ exit(1);
+ }
+
+// read_size = 512;
+ /* If our signature crosses the 512 boundary, then read 1k at a time */
+ if ((sig_offset / 512) != ((sig_offset + sig_size - 1) / 512)) {
+ read_size = 4096;
+ }
+
+ /* Get the image */
+ if (optind + 1 != argc) {
+ usage();
+ }
+
+ if ((img_info =
+ tsk_img_open_utf8_sing(argv[optind],
+ TSK_IMG_TYPE_DETECT, 0)) == NULL) {
+ tsk_error_print(stderr);
+ exit(1);
+ }
+
+ /* Make a version that can be more easily printed */
+ for (i = 0; i < sig_size; i++) {
+ sig_print |= (sig[i] << ((sig_size - 1 - i) * 8));
+ }
+
+ for (i = 0; i < sig2_size; i++) {
+ sig2_print |= (sig2[i] << ((sig2_size - 1 - i) * 8));
+ }
+
+ if (0 != sig2[0]){
+ printf("Block size: %d\nOffset1: %d Signature1: %X\nOffset2: %d Signature2: %X\n\n", bs, sig_offset,
+ sig_print, sig2_offset, sig2_print);
+
+ }
+ else{
+ printf("Block size: %d\nOffset: %d Signature: %X\n\n", bs, sig_offset,
+ sig_print);
+ }
+ /* Loop through by blocks - we will read in block sized chunks
+ * so that we can be used on raw devices
+ */
+ cur_offset = (sig_offset / 512) * 512;
+ rel_offset = sig_offset % 512;
+ rel2_offset = sig2_offset % 512;
+ //prev_hit = -1;
+ for (i = 0;; i++) {
+ ssize_t retval;
+
+ /* Read the signature area */
+ retval = tsk_img_read(img_info, cur_offset,
+ (char *)block, read_size);
+ if (retval == 0) {
+ break;
+ }
+ else if (retval == -1) {
+ fprintf(stderr, "error reading bytes %lu\n",
+ (unsigned long) i);
+ exit(1);
+ }
+
+ /* Check the sig */
+ if ((block[rel_offset] == sig[0]) &&
+ ((sig_size < 2) || (block[rel_offset + 1] == sig[1])) &&
+ ((sig_size < 3) || (block[rel_offset + 2] == sig[2])) &&
+ ((sig_size < 4) || (block[rel_offset + 3] == sig[3]))) {
+ if( 0 == sig2[0]){ // Only one signature
+ if (! hasHits){
+ printf("Block: %lu (-)\t", (unsigned long)i);
+ hasHits = !hasHits;
+ }
+ else{
+ printf("Block: %lu (+%lu)\t", (unsigned long)i,
+ (unsigned long)(i - prev_hit));
+ } // inner if else
+
+ prev_hit = i; // only update if a hit
+ TSK_MD5_Init(&md);
+ TSK_MD5_Update(&md, block, bs);
+ printSectorMd5(&md); // Prints the md5 hash value of current sector
+
+
+ }else{ // We have a signature number two
+ if ((block[rel2_offset] == sig2[0]) &&
+ ((sig2_size < 2) || (block[rel2_offset + 1] == sig2[1])) &&
+ ((sig2_size < 3) || (block[rel2_offset + 2] == sig2[2])) &&
+ ((sig2_size < 4) || (block[rel2_offset + 3] == sig2[3]))) {
+ if (!hasHits){
+ printf("Block: %lu (-)\t", (unsigned long)i);
+ hasHits = !hasHits;
+ }
+ else{
+ printf("Block: %lu (+%lu)\t", (unsigned long)i,
+ (unsigned long)(i - prev_hit));
+ } // inner else if
+
+ prev_hit = i; // only update if a hit
+ TSK_MD5_Init(&md);
+ TSK_MD5_Update(&md, block, bs);
+ printSectorMd5(&md); // Prints the md5 hash value of current sector
+ // Perhaps count the sectors in volume
+ //sectors64bitInVolume = (TSK_DADDR_T*)&block[40];
+ //printf("Sectors in Volume: %lu\t", (unsigned long)*sectors64bitInVolume);
+ //sectors64bitInVolume = NULL; // Best practice
+ } // inner if
+
+
+ } // if else
+
+ }
+ cur_offset += bs;
+ }
+
+ tsk_img_close(img_info);
+ exit(0);
+}