source: releases/1.0.0/python/pyregfi/__init__.py@ 293

Last change on this file since 293 was 262, checked in by tim, 14 years ago

changed regfi_conv_charset to handle memory allocation
tweaked test cases
corrected some documentation

File size: 36.1 KB
Line 
1#!/usr/bin/env python
2
3# Copyright (C) 2010-2011 Timothy D. Morgan
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; version 3 of the License.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program; if not, write to the Free Software
16# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17#
18# $Id: $
19
20## @package pyregfi
21# Python interface to the regfi library.
22#
23
24## @mainpage API Documentation
25#
26# The pyregfi module provides a Python interface to the @ref regfi Windows
27# registry library.
28#
29# The library operates on registry hives, each of which is contained within a
30# single file. The quickest way to get started, is to use the @ref openHive()
31# function to obtain a Hive object. For example:
32# @code
33# >>> import pyregfi
34# >>> myHive = pyregfi.openHive('/mnt/win/c/WINDOWS/system32/config/system')
35# @endcode
36#
37# Using this Hive object, one can begin investigating what top-level keys
38# exist by starting with the root Key attribute:
39# @code
40# >>> for key in myHive.root.subkeys:
41# ... print(key.name)
42# ControlSet001
43# ControlSet003
44# LastKnownGoodRecovery
45# MountedDevices
46# Select
47# Setup
48# WPA
49# @endcode
50#
51# From there, accessing subkeys and values by name is a simple matter of:
52# @code
53# >>> myKey = myHive.root.subkeys['Select']
54# >>> myValue = myKey.values['Current']
55# @endcode
56#
57# The data associated with a Value can be obtained through the fetch_data()
58# method:
59# @code
60# >>> print(myValue.fetch_data())
61# 1
62# @endcode
63#
64# While useful for simple exercises, using the subkeys object for deeply nested
65# paths is not efficient and doesn't make for particularly attractive code.
66# Instead, a special-purpose HiveIterator class is provided for simplicity of
67# use and fast access to specific known paths:
68# @code
69# >>> myIter = pyregfi.HiveIterator(myHive)
70# >>> myIter.descend(['ControlSet001','Control','NetworkProvider','HwOrder'])
71# >>> myKey = myIter.current_key()
72# >>> print(myKey.values['ProviderOrder'].fetch_data())
73# RDPNP,LanmanWorkstation,WebClient
74# @endcode
75#
76# The first two lines above can be simplified in some "syntactic sugar" provided
77# by the Hive.subtree() method. Also, as one might expect, the HiveIterator
78# also acts as an iterator, producing keys in a depth-first order.
79# For instance, to traverse all keys under the ControlSet003\\Services key,
80# printing their names as we go, we could do:
81# @code
82# >>> for key in Hive.subtree(['ControlSet003','Services']):
83# >>> print(key.name)
84# Services
85# Abiosdsk
86# abp480n5
87# Parameters
88# PnpInterface
89# ACPI
90# [...]
91# @endcode
92#
93# Note that "Services" was printed first, since the subtree is traversed as a
94# "preordering depth-first" search starting with the HiveIterator's current_key().
95# As one might expect, traversals of subtrees stops when all elements in a
96# specific subtree (and none outside of it) have been traversed.
97#
98# For more information, peruse the various attributes and methods available on
99# the Hive, HiveIterator, Key, Value, and Security classes.
100#
101# @note @ref regfi is a read-only library by design and there
102# are no plans to implement write support.
103#
104# @note At present, pyregfi has been tested with Python versions 2.6 and 3.1
105#
106# @note Developers strive to make pyregfi thread-safe.
107#
108import sys
109import time
110import ctypes
111import ctypes.util
112import threading
113from pyregfi.structures import *
114
115
116## An enumeration of registry Value data types
117#
118# @note This is a static class, there is no need to instantiate it.
119# Just access its attributes directly as DATA_TYPES.SZ, etc
120class DATA_TYPES(object):
121 ## None / Unknown
122 NONE = 0
123 ## String
124 SZ = 1
125 ## String with %...% expansions
126 EXPAND_SZ = 2
127 ## Binary buffer
128 BINARY = 3
129 ## 32 bit integer (little endian)
130 DWORD = 4 # DWORD, little endian
131 ## 32 bit integer (little endian)
132 DWORD_LE = 4
133 ## 32 bit integer (big endian)
134 DWORD_BE = 5 # DWORD, big endian
135 ## Symbolic link
136 LINK = 6
137 ## List of strings
138 MULTI_SZ = 7
139 ## Unknown structure
140 RESOURCE_LIST = 8
141 ## Unknown structure
142 FULL_RESOURCE_DESCRIPTOR = 9
143 ## Unknown structure
144 RESOURCE_REQUIREMENTS_LIST = 10
145 ## 64 bit integer
146 QWORD = 11 # 64-bit little endian
147
148
149## An enumeration of log message types
150#
151# @note This is a static class, there is no need to instantiate it.
152# Just access its attributes directly as LOG_TYPES.INFO, etc
153class LOG_TYPES(object):
154 ## Informational messages, useful in debugging
155 INFO = 0x01
156 ## Non-critical problems in structure parsing or intepretation
157 WARN = 0x04
158 ## Major failures
159 ERROR = 0x10
160
161
162def _buffer2bytearray(char_pointer, length):
163 if length == 0 or char_pointer == None:
164 return None
165
166 ret_val = bytearray(length)
167 for i in range(0,length):
168 ret_val[i] = char_pointer[i][0]
169
170 return ret_val
171
172
173def _strlist2charss(str_list):
174 ret_val = []
175 for s in str_list:
176 ret_val.append(s.encode('utf-8', 'replace'))
177
178 ret_val = (ctypes.c_char_p*(len(str_list)+1))(*ret_val)
179 # Terminate the char** with a NULL pointer
180 ret_val[-1] = 0
181
182 return ret_val
183
184
185def _charss2strlist(chars_pointer):
186 ret_val = []
187 i = 0
188 s = chars_pointer[i]
189 while s:
190 ret_val.append(s.decode('utf-8', 'replace'))
191 i += 1
192 s = chars_pointer[i]
193
194 return ret_val
195
196
197
198## Returns the (py)regfi library version
199#
200# @return A string indicating the version
201def getVersion():
202 return regfi.regfi_version()
203
204
205## Retrieves messages produced by regfi during parsing and interpretation
206#
207# The regfi C library may generate log messages stored in a special thread-safe
208# global data structure. These messages should be retrieved periodically or
209# after each major operation by callers to determine if any errors or warnings
210# should be reported to the user. Failure to retrieve these could result in
211# excessive memory consumption.
212def getLogMessages():
213 msgs = regfi.regfi_log_get_str()
214 if not msgs:
215 return ''
216 return msgs.decode('utf-8')
217
218
219## Sets the types of log messages to record
220#
221# @param log_types A sequence of message types that regfi should generate.
222# Message types can be found in the LOG_TYPES enumeration.
223#
224# @return True on success, False on failure. Failures are rare, but could
225# indicate that global logging is not operating as expected.
226#
227# Example:
228# @code
229# setLogMask((LOG_TYPES.ERROR, LOG_TYPES.WARN, LOG_TYPES.INFO))
230# @endcode
231#
232# The message mask is a global (all hives, iterators), thread-specific value.
233# For more information, see @ref regfi_log_set_mask.
234#
235def setLogMask(log_types):
236 mask = 0
237 for m in log_types:
238 mask |= m
239 return regfi.regfi_log_set_mask(mask)
240
241
242## Opens a file as a registry hive
243#
244# @param path The file path of a hive, as one would provide to the
245# open() built-in
246#
247# @return A new Hive instance
248def openHive(path):
249 fh = open(path, 'rb')
250 return Hive(fh)
251
252
253## Abstract class for most objects returned by the library
254class _StructureWrapper(object):
255 _hive = None
256 _base = None
257
258 def __init__(self, hive, base):
259 if not hive:
260 raise Exception("Could not create _StructureWrapper,"
261 + " hive is NULL. Current log:\n"
262 + getLogMessages())
263 if not base:
264 raise Exception("Could not create _StructureWrapper,"
265 + " base is NULL. Current log:\n"
266 + getLogMessages())
267 self._hive = hive
268 self._base = base
269
270
271 # Memory management for most regfi structures is taken care of here
272 def __del__(self):
273 if self._base:
274 regfi.regfi_free_record(self._hive.file, self._base)
275
276
277 # Any attribute requests not explicitly defined in subclasses gets passed
278 # to the equivalent REGFI_* structure defined in structures.py
279 def __getattr__(self, name):
280 return getattr(self._base.contents, name)
281
282
283 ## Test for equality
284 #
285 # Records returned by pyregfi may be compared with one another. For example:
286 # @code
287 # >>> key2 = key1.subkeys['child']
288 # >>> key1 == key2
289 # False
290 # >>> key1 != key2
291 # True
292 # >>> key1 == key2.get_parent()
293 # True
294 # @endcode
295 def __eq__(self, other):
296 return (type(self) == type(other)) and (self.offset == other.offset)
297
298
299 def __ne__(self, other):
300 return (not self.__eq__(other))
301
302
303class Key():
304 pass
305
306
307class Value():
308 pass
309
310
311
312## Represents a registry SK record which contains a security descriptor
313#
314class Security(_StructureWrapper):
315 ## Number of registry Keys referencing this SK record
316 ref_count = 1
317
318 ## The absolute file offset of the SK record's cell in the Hive file
319 offset = 0xCAFEBABE
320
321 ## The @ref winsec.SecurityDescriptor for this SK record
322 descriptor = object()
323
324 def __init__(self, hive, base):
325 super(Security, self).__init__(hive, base)
326 # XXX: add checks for NULL pointers
327 self.descriptor = winsec.SecurityDescriptor(base.contents.sec_desc.contents)
328
329 ## Loads the "next" Security record in the hive
330 #
331 # @note
332 # SK records are included in a circular, doubly-linked list.
333 # To iterate over all SK records, be sure to check for the repetition of
334 # the SK record you started with to determine when all have been traversed.
335 def next_security(self):
336 return Security(self._hive,
337 regfi.regfi_next_sk(self._hive.file, self._base))
338
339 ## Loads the "previous" Security record in the hive
340 #
341 # @note
342 # SK records are included in a circular, doubly-linked list.
343 # To iterate over all SK records, be sure to check for the repetition of
344 # the SK record you started with to determine when all have been traversed.
345 def prev_security(self):
346 return Security(self._hive,
347 regfi.regfi_prev_sk(self._hive.file, self._base))
348
349
350## Abstract class for ValueList and SubkeyList
351class _GenericList(object):
352 # XXX: consider implementing keys(), values(), items() and other dictionary methods
353 _hive = None
354 _key_base = None
355 _length = None
356 _current = None
357
358 # implementation-specific functions for SubkeyList and ValueList
359 _fetch_num = None
360 _find_element = None
361 _get_element = None
362 _constructor = None
363
364 def __init__(self, key):
365 if not key:
366 raise Exception("Could not create _GenericList; key is NULL."
367 + "Current log:\n" + getLogMessages())
368
369 base = regfi.regfi_reference_record(key._hive.file, key._base)
370 if not base:
371 raise Exception("Could not create _GenericList; memory error."
372 + "Current log:\n" + getLogMessages())
373 self._key_base = cast(base, type(key._base))
374 self._length = self._fetch_num(self._key_base)
375 self._hive = key._hive
376
377
378 def __del__(self):
379 regfi.regfi_free_record(self._hive.file, self._key_base)
380
381
382 ## Length of list
383 def __len__(self):
384 return self._length
385
386
387 ## Retrieves a list element by name
388 #
389 # @param name The name of the subkey or value desired.
390 # This is case-insensitive.
391 #
392 # @note The registry format does not inherently prevent multiple
393 # subkeys or values from having the same name, having a key
394 # and a value with the same name, or having the same name in
395 # different cases that could both match.
396 # This interface simply returns the first match in the list.
397 # Lookups using this method could also fail due to incorrectly
398 # encoded strings stored as names.
399 # To identify any duplicates or elements with malformed names,
400 # use the iterator interface to check every list element.
401 #
402 # @return the first element whose name matches, or None if the element
403 # could not be found
404 def __getitem__(self, name):
405 # XXX: Consider interpreting integer names as offsets in the underlying list
406 index = ctypes.c_uint32()
407 if isinstance(name, str):
408 name = name.encode('utf-8')
409
410 if name != None:
411 name = create_string_buffer(bytes(name))
412
413 if self._find_element(self._hive.file, self._key_base,
414 name, byref(index)):
415 return self._constructor(self._hive,
416 self._get_element(self._hive.file,
417 self._key_base,
418 index))
419 raise KeyError('')
420
421
422 ## Fetches the requested element by name, or the default value if the lookup
423 # fails.
424 #
425 def get(self, name, default):
426 try:
427 return self[name]
428 except KeyError:
429 return default
430
431 def __iter__(self):
432 self._current = 0
433 return self
434
435 def __next__(self):
436 if self._current >= self._length:
437 raise StopIteration('')
438
439 elem = self._get_element(self._hive.file, self._key_base,
440 ctypes.c_uint32(self._current))
441 self._current += 1
442 return self._constructor(self._hive, elem)
443
444 # For Python 2.x
445 next = __next__
446
447
448## The list of subkeys associated with a Key
449#
450# This attribute is both iterable:
451# @code
452# for k in myKey.subkeys:
453# ...
454# @endcode
455# and accessible as a dictionary:
456# @code
457# mySubkey = myKey.subkeys["keyName"]
458# @endcode
459#
460# You may also request the len() of a subkeys list.
461# However keys(), values(), items() and similar methods are not currently
462# implemented.
463class SubkeyList(_GenericList):
464 _fetch_num = regfi.regfi_fetch_num_subkeys
465 _find_element = regfi.regfi_find_subkey
466 _get_element = regfi.regfi_get_subkey
467
468
469## The list of values associated with a Key
470#
471# This attribute is both iterable:
472# @code
473# for v in myKey.values:
474# ...
475# @endcode
476# and accessible as a dictionary:
477# @code
478# myValue = myKey.values["valueName"]
479# @endcode
480#
481# You may also request the len() of a values list.
482# However keys(), values(), items() and similar methods are not currently
483# implemented.
484class ValueList(_GenericList):
485 _fetch_num = regfi.regfi_fetch_num_values
486 _find_element = regfi.regfi_find_value
487 _get_element = regfi.regfi_get_value
488
489
490## Registry key
491# These represent registry keys (@ref REGFI_NK records) and provide
492# access to their subkeys, values, and other metadata.
493#
494# @note Key instances may provide access to more attributes than are
495# documented here. However, undocumented attributes may change over time
496# and are not officially supported. If you need access to an attribute
497# not shown here, see @ref pyregfi.structures.
498class Key(_StructureWrapper):
499 ## A @ref ValueList object representing the list of Values
500 # stored on this Key
501 values = None
502
503 ## A @ref SubkeyList object representing the list of subkeys
504 # stored on this Key
505 subkeys = None
506
507 ## The raw Key name as an uninterpreted bytearray
508 name_raw = (b"...")
509
510 ## The name of the Key as a (unicode) string
511 name = "..."
512
513 ## The absolute file offset of the Key record's cell in the Hive file
514 offset = 0xCAFEBABE
515
516 ## This Key's last modified time represented as the number of seconds
517 # since the UNIX epoch in UTC; similar to what time.time() returns
518 modified = 1300000000.123456
519
520 ## The NK record's flags field
521 flags = 0x10110001
522
523 def __init__(self, hive, base):
524 super(Key, self).__init__(hive, base)
525 self.values = ValueList(self)
526 self.subkeys = SubkeyList(self)
527
528 def __getattr__(self, name):
529 if name == "name":
530 ret_val = super(Key, self).__getattr__(name)
531
532 if not ret_val:
533 ret_val = self.name_raw
534 else:
535 ret_val = ret_val.decode('utf-8', 'replace')
536
537 elif name == "name_raw":
538 ret_val = super(Key, self).__getattr__(name)
539 length = super(Key, self).__getattr__('name_length')
540 ret_val = _buffer2bytearray(ret_val, length)
541
542 elif name == "modified":
543 ret_val = regfi.regfi_nt2unix_time(self._base.contents.mtime)
544
545 else:
546 ret_val = super(Key, self).__getattr__(name)
547
548 return ret_val
549
550
551 ## Retrieves the Security properties for this key
552 def fetch_security(self):
553 return Security(self._hive,
554 regfi.regfi_fetch_sk(self._hive.file, self._base))
555
556
557 ## Retrieves the class name for this key
558 #
559 # Class names are typically stored as UTF-16LE strings, so these are decoded
560 # into proper python (unicode) strings. However, if this fails, a bytearray
561 # is instead returned containing the raw buffer stored for the class name.
562 #
563 # @return The class name as a string or bytearray. None if a class name
564 # doesn't exist or an unrecoverable error occurred during retrieval.
565 def fetch_classname(self):
566 ret_val = None
567 cn_p = regfi.regfi_fetch_classname(self._hive.file, self._base)
568 if cn_p:
569 cn_struct = cn_p.contents
570 if cn_struct.interpreted:
571 ret_val = cn_struct.interpreted.decode('utf-8', 'replace')
572 else:
573 ret_val = _buffer2bytearray(cn_struct.raw,
574 cn_struct.size)
575 regfi.regfi_free_record(self._hive.file, cn_p)
576
577 return ret_val
578
579
580 ## Retrieves this key's parent key
581 #
582 # @return The parent's Key instance or None if current key is root
583 # (or an error occured)
584 def get_parent(self):
585 if self.is_root():
586 return None
587 parent_base = regfi.regfi_get_parentkey(self._hive.file, self._base)
588 if parent_base:
589 return Key(self._hive, parent_base)
590 return None
591
592
593 ## Checks to see if this Key is the root of its Hive
594 #
595 # @return True if it is, False otherwise
596 def is_root(self):
597 return (self._hive.root == self)
598
599
600## Registry value (metadata)
601#
602# These represent registry values (@ref REGFI_VK records) and provide
603# access to their associated data.
604#
605# @note Value instances may provide access to more attributes than are
606# documented here. However, undocumented attributes may change over time
607# and are not officially supported. If you need access to an attribute
608# not shown here, see @ref pyregfi.structures.
609class Value(_StructureWrapper):
610 ## The raw Value name as an uninterpreted bytearray
611 name_raw = (b"...")
612
613 ## The name of the Value as a (unicode) string
614 name = "..."
615
616 ## The absolute file offset of the Value record's cell in the Hive file
617 offset = 0xCAFEBABE
618
619 ## The length of data advertised in the VK record
620 data_size = 0xCAFEBABE
621
622 ## An integer which represents the data type for this Value's data
623 # Typically this value is one of 12 types defined in @ref DATA_TYPES,
624 # but in some cases (the SAM hive) it may be used for other purposes
625 type = DATA_TYPES.NONE
626
627 ## The VK record's flags field
628 flags = 0x10110001
629
630 ## Retrieves the Value's data according to advertised type
631 #
632 # Data is loaded from its cell(s) and then interpreted based on the data
633 # type recorded in the Value. It is not uncommon for data to be stored with
634 # the wrong type or even with invalid types. If you have difficulty
635 # obtaining desired data here, use @ref fetch_raw_data().
636 #
637 # @return The interpreted representation of the data as one of several
638 # possible Python types, as listed below. None if any failure
639 # occurred during extraction or conversion.
640 #
641 # @retval string for SZ, EXPAND_SZ, and LINK
642 # @retval int for DWORD, DWORD_BE, and QWORD
643 # @retval list(string) for MULTI_SZ
644 # @retval bytearray for NONE, BINARY, RESOURCE_LIST,
645 # FULL_RESOURCE_DESCRIPTOR, and RESOURCE_REQUIREMENTS_LIST
646 #
647 def fetch_data(self):
648 ret_val = None
649 data_p = regfi.regfi_fetch_data(self._hive.file, self._base)
650 if not data_p:
651 return None
652 data_struct = data_p.contents
653
654 if data_struct.interpreted_size == 0:
655 ret_val = None
656 elif data_struct.type in (DATA_TYPES.SZ, DATA_TYPES.EXPAND_SZ, DATA_TYPES.LINK):
657 # Unicode strings
658 ret_val = data_struct.interpreted.string.decode('utf-8', 'replace')
659 elif data_struct.type in (DATA_TYPES.DWORD, DATA_TYPES.DWORD_BE):
660 # 32 bit integers
661 ret_val = data_struct.interpreted.dword
662 elif data_struct.type == DATA_TYPES.QWORD:
663 # 64 bit integers
664 ret_val = data_struct.interpreted.qword
665 elif data_struct.type == DATA_TYPES.MULTI_SZ:
666 ret_val = _charss2strlist(data_struct.interpreted.multiple_string)
667 elif data_struct.type in (DATA_TYPES.NONE, DATA_TYPES.RESOURCE_LIST,
668 DATA_TYPES.FULL_RESOURCE_DESCRIPTOR,
669 DATA_TYPES.RESOURCE_REQUIREMENTS_LIST,
670 DATA_TYPES.BINARY):
671 ret_val = _buffer2bytearray(data_struct.interpreted.none,
672 data_struct.interpreted_size)
673
674 regfi.regfi_free_record(self._hive.file, data_p)
675 return ret_val
676
677
678 ## Retrieves raw representation of Value's data
679 #
680 # @return A bytearray containing the data
681 #
682 def fetch_raw_data(self):
683 ret_val = None
684 # XXX: should we load the data without interpretation instead?
685 data_p = regfi.regfi_fetch_data(self._hive.file, self._base)
686 if not data_p:
687 return None
688
689 data_struct = data_p.contents
690 ret_val = _buffer2bytearray(data_struct.raw,
691 data_struct.size)
692 regfi.regfi_free_record(self._hive.file, data_p)
693 return ret_val
694
695
696 def __getattr__(self, name):
697 ret_val = super(Value, self).__getattr__(name)
698 if name == "name":
699 if not ret_val:
700 ret_val = self.name_raw
701 else:
702 ret_val = ret_val.decode('utf-8', 'replace')
703
704 elif name == "name_raw":
705 length = super(Value, self).__getattr__('name_length')
706 ret_val = _buffer2bytearray(ret_val, length)
707
708 return ret_val
709
710
711# Avoids chicken/egg class definitions.
712# Also makes for convenient code reuse in these lists' parent classes.
713SubkeyList._constructor = Key
714ValueList._constructor = Value
715
716
717
718## Represents a single registry hive (file)
719class Hive():
720 file = None
721 raw_file = None
722 _fh = None
723 #_root = None
724
725
726 ## The root Key of this Hive
727 root = None
728
729 ## This Hives's last modified time represented as the number of seconds
730 # since the UNIX epoch in UTC; similar to what time.time() returns
731 modified = 1300000000.123456
732
733 ## First sequence number
734 sequence1 = 12345678
735
736 ## Second sequence number
737 sequence2 = 12345678
738
739 ## Major version
740 major_version = 1
741
742 ## Minor version
743 minor_version = 5
744
745 ## Constructor
746 #
747 # Initialize a new Hive based on a Python file object. To open a file by
748 # path, see @ref openHive.
749 #
750 # @param fh A Python file object. The constructor first looks for a valid
751 # fileno attribute on this object and uses it if possible.
752 # Otherwise, the seek and read methods are used for file
753 # access.
754 #
755 # @note Supplied file must be seekable. Do not perform any operation on
756 # the provided file object while a Hive is using it. Do not
757 # construct multiple Hive instances from the same file object.
758 # If a file must be accessed by separate code and pyregfi
759 # simultaneously, use a separate file descriptor. Hives are
760 # thread-safe, so multiple threads may use a single Hive object.
761 def __init__(self, fh):
762 # The fileno method may not exist, or it may throw an exception
763 # when called if the file isn't backed with a descriptor.
764 self._fh = fh
765 fn = None
766 try:
767 # XXX: Native calls to Windows filenos don't seem to work.
768 # Need to investigate why.
769 if not is_win32 and hasattr(fh, 'fileno'):
770 fn = fh.fileno()
771 except:
772 pass
773
774 if fn != None:
775 self.file = regfi.regfi_alloc(fn, REGFI_ENCODING_UTF8)
776 if not self.file:
777 # XXX: switch to non-generic exception
778 raise Exception("Could not open registry file. Current log:\n"
779 + getLogMessages())
780 else:
781 fh.seek(0)
782 self.raw_file = structures.REGFI_RAW_FILE()
783 self.raw_file.fh = fh
784 self.raw_file.seek = seek_cb_type(self.raw_file.cb_seek)
785 self.raw_file.read = read_cb_type(self.raw_file.cb_read)
786 self.file = regfi.regfi_alloc_cb(pointer(self.raw_file), REGFI_ENCODING_UTF8)
787 if not self.file:
788 # XXX: switch to non-generic exception
789 raise Exception("Could not open registry file. Current log:\n"
790 + getLogMessages())
791
792
793 def __getattr__(self, name):
794 if name == "root":
795 # XXX: This creates reference loops. Need to cache better inside regfi
796 #if self._root == None:
797 # self._root = Key(self, regfi.regfi_get_rootkey(self.file))
798 #return self._root
799 return Key(self, regfi.regfi_get_rootkey(self.file))
800
801 elif name == "modified":
802 return regfi.regfi_nt2unix_time(self._base.contents.mtime)
803
804 return getattr(self.file.contents, name)
805
806
807 def __del__(self):
808 if self.file:
809 regfi.regfi_free(self.file)
810
811 def __iter__(self):
812 return HiveIterator(self)
813
814
815 ## Creates a @ref HiveIterator initialized at the specified path in
816 # the hive.
817 #
818 # @param path A list of Key names which represent an absolute path within
819 # the Hive
820 #
821 # @return A @ref HiveIterator which is positioned at the specified path.
822 #
823 # @exception Exception If the path could not be found/traversed
824 def subtree(self, path):
825 hi = HiveIterator(self)
826 hi.descend(path)
827 return hi
828
829
830## A special purpose iterator for registry hives
831#
832# Iterating over an object of this type causes all keys in a specific
833# hive subtree to be returned in a depth-first manner. These iterators
834# are typically created using the @ref Hive.subtree() function on a @ref Hive
835# object.
836#
837# HiveIterators can also be used to manually traverse up and down a
838# registry hive as they retain information about the current position in
839# the hive, along with which iteration state for subkeys and values for
840# every parent key. See the @ref up and @ref down methods for more
841# information.
842class HiveIterator():
843 _hive = None
844 _iter = None
845 _iteration_root = None
846 _lock = None
847
848 def __init__(self, hive):
849 self._iter = regfi.regfi_iterator_new(hive.file)
850 if not self._iter:
851 raise Exception("Could not create iterator. Current log:\n"
852 + getLogMessages())
853 self._hive = hive
854 self._lock = threading.RLock()
855
856 def __getattr__(self, name):
857 self._lock.acquire()
858 ret_val = getattr(self._iter.contents, name)
859 self._lock.release()
860 return ret_val
861
862 def __del__(self):
863 self._lock.acquire()
864 regfi.regfi_iterator_free(self._iter)
865 self._lock.release()
866
867 def __iter__(self):
868 self._lock.acquire()
869 self._iteration_root = None
870 self._lock.release()
871 return self
872
873 def __next__(self):
874 self._lock.acquire()
875 if self._iteration_root == None:
876 self._iteration_root = self.current_key().offset
877 elif not regfi.regfi_iterator_down(self._iter):
878 up_ret = regfi.regfi_iterator_up(self._iter)
879 while (up_ret and
880 not regfi.regfi_iterator_next_subkey(self._iter)):
881 if self._iteration_root == self.current_key().offset:
882 self._iteration_root = None
883 self._lock.release()
884 raise StopIteration('')
885 up_ret = regfi.regfi_iterator_up(self._iter)
886
887 if not up_ret:
888 self._iteration_root = None
889 self._lock.release()
890 raise StopIteration('')
891
892 # XXX: Use non-generic exception
893 if not regfi.regfi_iterator_down(self._iter):
894 self._lock.release()
895 raise Exception('Error traversing iterator downward.'+
896 ' Current log:\n'+ getLogMessages())
897
898 regfi.regfi_iterator_first_subkey(self._iter)
899 ret_val = self.current_key()
900 self._lock.release()
901
902 return ret_val
903
904
905 # For Python 2.x
906 next = __next__
907
908 # XXX: Should add sanity checks on some of these traversal functions
909 # to throw exceptions if a traversal/retrieval *should* have worked
910 # but failed for some reason.
911
912 ## Descends the iterator to a subkey
913 #
914 # Descends the iterator one level to the current subkey, or a subkey
915 # specified by name.
916 #
917 # @param subkey_name If specified, locates specified subkey by name
918 # (via find_subkey()) and descends to it.
919 #
920 # @return True if successful, False otherwise
921 def down(self, subkey_name=None):
922 ret_val = None
923 if subkey_name == None:
924 self._lock.acquire()
925 ret_val = regfi.regfi_iterator_down(self._iter)
926 else:
927 if name != None:
928 name = name.encode('utf-8')
929 self._lock.acquire()
930 ret_val = (regfi.regfi_iterator_find_subkey(self._iter, name)
931 and regfi.regfi_iterator_down(self._iter))
932
933 self._lock.release()
934 return ret_val
935
936
937 ## Causes the iterator to ascend to the current Key's parent
938 #
939 # @return True if successful, False otherwise
940 #
941 # @note The state of current subkeys and values at this level in the tree
942 # is lost as a side effect. That is, if you go up() and then back
943 # down() again, current_subkey() and current_value() will return
944 # default selections.
945 def up(self):
946 self._lock.acquire()
947 ret_val = regfi.regfi_iterator_up(self._iter)
948 self._lock.release()
949 return ret_val
950
951
952 ## Selects first subkey of current key
953 #
954 # @return A Key instance for the first subkey.
955 # None on error or if the current key has no subkeys.
956 def first_subkey(self):
957 ret_val = None
958 self._lock.acquire()
959 if regfi.regfi_iterator_first_subkey(self._iter):
960 ret_val = self.current_subkey()
961 self._lock.release()
962 return ret_val
963
964
965 ## Selects first value of current Key
966 #
967 # @return A Value instance for the first value.
968 # None on error or if the current key has no values.
969 def first_value(self):
970 ret_val = None
971 self._lock.acquire()
972 if regfi.regfi_iterator_first_value(self._iter):
973 ret_val = self.current_value()
974 self._lock.release()
975 return ret_val
976
977
978 ## Selects the next subkey in the current Key's list
979 #
980 # @return A Key instance for the next subkey.
981 # None if there are no remaining subkeys or an error occurred.
982 def next_subkey(self):
983 ret_val = None
984 self._lock.acquire()
985 if regfi.regfi_iterator_next_subkey(self._iter):
986 ret_val = self.current_subkey()
987 self._lock.release()
988 return ret_val
989
990
991 ## Selects the next value in the current Key's list
992 #
993 # @return A Value instance for the next value.
994 # None if there are no remaining values or an error occurred.
995 def next_value(self):
996 ret_val = None
997 self._lock.acquire()
998 if regfi.regfi_iterator_next_value(self._iter):
999 ret_val = self.current_value()
1000 self._lock.release()
1001 return ret_val
1002
1003
1004 ## Selects the first subkey which has the specified name
1005 #
1006 # @return A Key instance for the selected key.
1007 # None if it could not be located or an error occurred.
1008 def find_subkey(self, name):
1009 if name != None:
1010 name = name.encode('utf-8')
1011 ret_val = None
1012 self._lock.acquire()
1013 if regfi.regfi_iterator_find_subkey(self._iter, name):
1014 ret_val = self.current_subkey()
1015 self._lock.release()
1016 return ret_val
1017
1018
1019 ## Selects the first value which has the specified name
1020 #
1021 # @return A Value instance for the selected value.
1022 # None if it could not be located or an error occurred.
1023 def find_value(self, name):
1024 if name != None:
1025 name = name.encode('utf-8')
1026 ret_val = None
1027 self._lock.acquire()
1028 if regfi.regfi_iterator_find_value(self._iter, name):
1029 ret_val = self.current_value()
1030 self._lock.release()
1031 return ret_val
1032
1033 ## Retrieves the currently selected subkey
1034 #
1035 # @return A Key instance of the current subkey
1036 def current_subkey(self):
1037 self._lock.acquire()
1038 ret_val = Key(self._hive, regfi.regfi_iterator_cur_subkey(self._iter))
1039 self._lock.release()
1040 return ret_val
1041
1042 ## Retrieves the currently selected value
1043 #
1044 # @return A Value instance of the current value
1045 def current_value(self):
1046 self._lock.acquire()
1047 ret_val = Value(self._hive, regfi.regfi_iterator_cur_value(self._iter))
1048 self._lock.release()
1049 return ret_val
1050
1051 ## Retrieves the current key
1052 #
1053 # @return A Key instance of the current position of the iterator
1054 def current_key(self):
1055 self._lock.acquire()
1056 ret_val = Key(self._hive, regfi.regfi_iterator_cur_key(self._iter))
1057 self._lock.release()
1058 return ret_val
1059
1060 ## Traverse downward multiple levels
1061 #
1062 # This is more efficient than calling down() multiple times
1063 #
1064 # @param path A list of Key names which represent the path to descend
1065 #
1066 # @exception Exception If path could not be located
1067 def descend(self, path):
1068 cpath = _strlist2charss(path)
1069
1070 self._lock.acquire()
1071 result = regfi.regfi_iterator_descend(self._iter, cpath)
1072 self._lock.release()
1073 if not result:
1074 # XXX: Use non-generic exception
1075 raise Exception('Could not locate path.\n'+getLogMessages())
1076
1077 ## Obtains a list of the current key's ancestry
1078 #
1079 # @return A list of all parent keys starting with the root Key and ending
1080 # with the current Key
1081 def ancestry(self):
1082 self._lock.acquire()
1083 result = regfi.regfi_iterator_ancestry(self._iter)
1084 self._lock.release()
1085
1086 ret_val = []
1087 i = 0
1088 k = result[i]
1089 while k:
1090 k = cast(regfi.regfi_reference_record(self._hive.file, k), POINTER(REGFI_NK))
1091 ret_val.append(Key(self._hive, k))
1092 i += 1
1093 k = result[i]
1094
1095 regfi.regfi_free_record(self._hive.file, result)
1096 return ret_val
1097
1098 ## Obtains the current path of the iterator
1099 #
1100 # @return A list of key names starting with the root up to and
1101 # including the current key
1102 #
1103 def current_path(self):
1104 ancestry = self.ancestry()
1105 return [str(a.name) for a in ancestry]
1106
1107
1108# Freeing symbols defined for the sake of documentation
1109del Value.name,Value.name_raw,Value.offset,Value.data_size,Value.type,Value.flags
1110del Key.name,Key.name_raw,Key.offset,Key.modified,Key.flags
1111del Hive.root,Hive.modified,Hive.sequence1,Hive.sequence2,Hive.major_version,Hive.minor_version
1112del Security.ref_count,Security.offset,Security.descriptor
Note: See TracBrowser for help on using the repository browser.