Skip to content

Commit

Permalink
Update.
Browse files Browse the repository at this point in the history
  • Loading branch information
msuhanov committed Dec 6, 2017
1 parent bcd9c94 commit 798ba91
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 14 deletions.
8 changes: 8 additions & 0 deletions ChangeLog
@@ -1,3 +1,11 @@
Version: 1.0.6

Registry, RegistrySqlite: handle an invalid reference to a parent key when parsing a truncated hive.
RegistrySqlite: rename the 'values_deleted' method (YarpDB) to 'values_unassociated', clarify the docstring.
Minor changes.

---

Version: 1.0.5

RegistryCarve: improve the tracking of compressed hive bins from primary files when dealing with registry fragments.
Expand Down
2 changes: 1 addition & 1 deletion ReadMe
Expand Up @@ -31,7 +31,7 @@ See the 'License' file.

5. Installation

# pip3 install https://github.com/msuhanov/yarp/archive/1.0.5.tar.gz
# pip3 install https://github.com/msuhanov/yarp/archive/1.0.6.tar.gz

---
(c) Maxim Suhanov
Binary file added hives_for_tests/DeletedDataHiveTruncated
Binary file not shown.
Binary file added hives_for_tests/InvalidParentFragment
Binary file not shown.
74 changes: 69 additions & 5 deletions test_cases.py
Expand Up @@ -62,6 +62,7 @@
hive_truncated_name = path.join(HIVES_DIR, 'TruncatedNameHive')
hive_healed = path.join(HIVES_DIR, 'HealedHive')
hive_deleted_data = path.join(HIVES_DIR, 'DeletedDataHive')
hive_deleted_data_truncated = path.join(HIVES_DIR, 'DeletedDataHiveTruncated')
hive_deleted_tree = path.join(HIVES_DIR, 'DeletedTreeHive')
hive_comp = path.join(HIVES_DIR, 'CompHive')
hive_remnants = path.join(HIVES_DIR, 'RemnantsHive')
Expand All @@ -85,6 +86,7 @@

fragment_sqlite = path.join(HIVES_DIR, 'SqliteFragment')
fragment_sqlite_db = path.join(HIVES_DIR, 'SqliteFragment.sqlite')
fragment_invalid_parent = path.join(HIVES_DIR, 'InvalidParentFragment')

truncated_hbin = path.join(HIVES_DIR, 'TruncatedHiveBin')

Expand Down Expand Up @@ -1451,7 +1453,7 @@ def test_sqlite():
assert c == 0

c = 0
for value in h.values_deleted():
for value in h.values_unassociated():
c += 1

assert value.is_deleted
Expand Down Expand Up @@ -1509,7 +1511,7 @@ def test_sqlite():
assert c == 1

c = 0
for value in h.values_deleted():
for value in h.values_unassociated():
c += 1

assert c == 0
Expand Down Expand Up @@ -1600,7 +1602,7 @@ def test_sqlite():
assert c == 2

c = 0
for value in h.values_deleted():
for value in h.values_unassociated():
c += 1

assert value.name == ''
Expand Down Expand Up @@ -1718,7 +1720,7 @@ def test_sqlite():
assert c == 2

c = 0
for value in h.values_deleted():
for value in h.values_unassociated():
c += 1

assert value.name == ''
Expand Down Expand Up @@ -1763,6 +1765,52 @@ def test_sqlite():

assert c == 2

with RegistrySqlite.YarpDB(fragment_invalid_parent, ':memory:') as h:
assert h.info().recovered == 0
assert h.info().truncated == 1
assert h.info().rebuilt == 1
assert h.root_key() is None

c = 0
for subkey in h.subkeys_unassociated():
c += 1

assert subkey.name == '{dedef10d-30ff-45b5-9d44-b3fa249ecd49}'
assert subkey.access_bits == 0
assert subkey.classname is None
assert not subkey.is_deleted

assert c == 1

with RegistrySqlite.YarpDB(hive_deleted_data, ':memory:') as h:
assert h.info().recovered == 0
assert h.info().truncated == 0
assert h.info().rebuilt == 0

c = 0
for value in h.values_unassociated():
c += 1

assert value.is_deleted
assert value.name == 'v' or value.name == 'v2'
assert value.type == 1

assert c == 2

with RegistrySqlite.YarpDB(hive_deleted_data_truncated, ':memory:') as h:
assert h.info().recovered == 0
assert h.info().truncated == 1
assert h.info().rebuilt == 0

c = 0
for value in h.values_unassociated():
c += 1

assert (value.is_deleted and (value.name == 'v' or value.name == 'v2')) or ((not value.is_deleted) and value.name == 'v1')
assert value.type == 1

assert c == 3

with RegistrySqlite.YarpDB(None, fragment_sqlite_db) as h:
assert h.info().recovered == 0
assert h.info().truncated == 1
Expand Down Expand Up @@ -1833,7 +1881,7 @@ def process_rowid(rowid):
for i in h.subkeys_unassociated():
process_rowid(i.rowid)

for i in h.values_deleted():
for i in h.values_unassociated():
pass

def test_translator():
Expand All @@ -1850,3 +1898,19 @@ def test_translator():
c += 1

assert c == 2

def test_invalid_parent_fragment():
with open(fragment_invalid_parent, 'rb') as f:
hive_obj = RegistryFile.FragmentTranslator(f)

hive = Registry.RegistryHiveTruncated(hive_obj)

c = 0
for key in hive.scan():
assert type(key) is Registry.RegistryKey
assert key.name() == '{dedef10d-30ff-45b5-9d44-b3fa249ecd49}'
assert key.path_partial() == '{dedef10d-30ff-45b5-9d44-b3fa249ecd49}'

c += 1

assert c == 1
8 changes: 4 additions & 4 deletions yarp/Registry.py
Expand Up @@ -323,7 +323,7 @@ class RegistryKey(object):
"""A set of data strings from different slack space locations to be used in the deleted data recovery."""

def __init__(self, primary_file, buf, layer, cell_relative_offset, tolerate_minor_errors = False, naive = False):
"""When working with deleted registry keys, set 'naive' to True, 'cell_relative_offset' and 'layer' to None.
"""When working with deleted registry keys or truncated hives, set 'naive' to True, 'cell_relative_offset' and 'layer' to None.
For a root key, set 'layer' to 0 (increment 'layer' by one when going to subkeys of a current key and decrement it by one when going to a parent key).
"""

Expand Down Expand Up @@ -749,7 +749,7 @@ class RegistryValue(object):
"""A KeyValue object."""

def __init__(self, primary_file, buf, naive = False):
"""When working with deleted registry values, set 'naive' to True."""
"""When working with deleted registry values or truncated hives, set 'naive' to True."""

self.registry_file = primary_file
if not naive:
Expand Down Expand Up @@ -950,7 +950,7 @@ def scan(self):

if len(cell_data) > 76: # A key node with at least one character in the name.
try:
key = RegistryKey(self.registry_file, cell_data, None, None, True, False)
key = RegistryKey(self.registry_file, cell_data, None, None, True, True)
key_name = key.name()
except (RegistryException, UnicodeDecodeError):
pass
Expand All @@ -960,7 +960,7 @@ def scan(self):

if len(cell_data) >= 20: # A key value with no name (at least).
try:
value = RegistryValue(self.registry_file, cell_data, False)
value = RegistryValue(self.registry_file, cell_data, True)
value_name = value.name()
except (RegistryException, UnicodeDecodeError):
pass
Expand Down
12 changes: 9 additions & 3 deletions yarp/RegistrySqlite.py
Expand Up @@ -317,6 +317,9 @@ def _db_add_key(self, key, is_deleted = 0):
parent_key_status = 1

parent_key_id = self._db_key_to_id(parent_key, parent_key_status)

if parent_key_id == key_id: # Invalid parent key.
parent_key_id = None
else:
# This is the root key.
parent_key_id = None
Expand Down Expand Up @@ -431,7 +434,7 @@ def subkeys(self, key_rowid):
yield Key(rowid = result[0], is_deleted = result[1], name = result[2], classname = result[3], last_written_timestamp = int(result[4]), access_bits = result[5], parent_key_id = result[6])

def subkeys_unassociated(self):
"""Get and yield unassociated subkeys."""
"""Get and yield unassociated (unlinked) subkeys."""

root_key = self.root_key()
if root_key is not None:
Expand Down Expand Up @@ -471,8 +474,11 @@ def values(self, key_rowid):
for result in results:
yield Value(rowid = result[0], is_deleted = result[1], name = result[2], type = result[3], data = result[4], parent_key_id = result[5])

def values_deleted(self):
"""Get and yield all deleted values."""
def values_unassociated(self):
"""Get and yield all unassociated values. For normal hives, this will yield all deleted values; for truncated hives, this will yield all values.
Note: unlike unassociated subkeys, unassociated values may be referenced by existing keys (here, the word 'unassociated' means that values were extracted without parsing keys).
This method will yield unlinked instances of unassociated values.
"""

self.db_cursor.execute('SELECT `rowid`, `is_deleted`, `name`, `type`, `data`, `parent_key_id` FROM `values` WHERE `parent_key_id` IS NULL ORDER BY UPPER(`name`) ASC')
results = self.db_cursor.fetchall()
Expand Down
2 changes: 1 addition & 1 deletion yarp/__init__.py
@@ -1,5 +1,5 @@
# yarp: yet another registry parser
# (c) Maxim Suhanov

__version__ = '1.0.5'
__version__ = '1.0.6'
__all__ = [ 'Registry', 'RegistryFile', 'RegistryRecords', 'RegistryRecover', 'RegistryCarve', 'RegistryHelpers' ]

0 comments on commit 798ba91

Please sign in to comment.