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); +}