source: trunk/python/pyregfi/__init__.py @ 219

Last change on this file since 219 was 219, checked in by tim, 13 years ago

updated time conversion to return a double for more precision
added modified attribute to keys to obtain user friendly time value
added accessor for key classnames
converted data attributes to functions to make workload more explicit

File size: 17.2 KB
RevLine 
[204]1#!/usr/bin/env python
2
[210]3## @package pyregfi
4# Python interface to the regfi library.
5#
6
[204]7import sys
[219]8import time
[214]9import weakref
[204]10from pyregfi.structures import *
11
12import ctypes
13import ctypes.util
[207]14from ctypes import c_char,c_char_p,c_int,c_uint16,c_uint32,c_bool,POINTER
[204]15
16regfi = ctypes.CDLL(ctypes.util.find_library('regfi'), use_errno=True)
17
18
[213]19regfi.regfi_alloc.argtypes = [c_int, REGFI_ENCODING]
[204]20regfi.regfi_alloc.restype = POINTER(REGFI_FILE)
21
[213]22regfi.regfi_alloc_cb.argtypes = [POINTER(REGFI_RAW_FILE), REGFI_ENCODING]
[204]23regfi.regfi_alloc_cb.restype = POINTER(REGFI_FILE)
24
[205]25regfi.regfi_free.argtypes = [POINTER(REGFI_FILE)]
26regfi.regfi_free.restype = None
27
[204]28regfi.regfi_log_get_str.argtypes = []
29regfi.regfi_log_get_str.restype = c_char_p
30
[205]31regfi.regfi_log_set_mask.argtypes = [c_uint16]
32regfi.regfi_log_set_mask.restype = c_bool
33
[215]34regfi.regfi_get_rootkey.argtypes = [POINTER(REGFI_FILE)]
35regfi.regfi_get_rootkey.restype = POINTER(REGFI_NK)
36
[205]37regfi.regfi_free_record.argtypes = [c_void_p]
38regfi.regfi_free_record.restype = None
39
[207]40regfi.regfi_fetch_num_subkeys.argtypes = [POINTER(REGFI_NK)]
41regfi.regfi_fetch_num_subkeys.restype = c_uint32
42
43regfi.regfi_fetch_num_values.argtypes = [POINTER(REGFI_NK)]
44regfi.regfi_fetch_num_values.restype = c_uint32
45
[206]46regfi.regfi_fetch_classname.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)]
47regfi.regfi_fetch_classname.restype = POINTER(REGFI_CLASSNAME)
48
49regfi.regfi_fetch_sk.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)]
50regfi.regfi_fetch_sk.restype = POINTER(REGFI_SK)
51
52regfi.regfi_fetch_data.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_VK)]
53regfi.regfi_fetch_data.restype = POINTER(REGFI_DATA)
54
[207]55regfi.regfi_find_subkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
56                                    c_char_p, POINTER(c_uint32)]
57regfi.regfi_find_subkey.restype = c_bool
58
59regfi.regfi_find_value.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
60                                    c_char_p, POINTER(c_uint32)]
61regfi.regfi_find_value.restype = c_bool
62
63regfi.regfi_get_subkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
64                                   c_uint32]
65regfi.regfi_get_subkey.restype = POINTER(REGFI_NK)
66
67regfi.regfi_get_value.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK),
68                                   c_uint32]
69regfi.regfi_get_value.restype = POINTER(REGFI_VK)
70
[215]71regfi.regfi_get_parentkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)]
72regfi.regfi_get_parentkey.restype = POINTER(REGFI_NK)
73
[219]74regfi.regfi_nt2unix_time.argtypes = [POINTER(REGFI_NTTIME)]
75regfi.regfi_nt2unix_time.restype = c_double
76
[205]77regfi.regfi_iterator_new.argtypes = [POINTER(REGFI_FILE), REGFI_ENCODING]
78regfi.regfi_iterator_new.restype = POINTER(REGFI_ITERATOR)
79
80regfi.regfi_iterator_free.argtypes = [POINTER(REGFI_ITERATOR)]
81regfi.regfi_iterator_free.restype = None
82
83regfi.regfi_iterator_down.argtypes = [POINTER(REGFI_ITERATOR)]
84regfi.regfi_iterator_down.restype = c_bool
85
86regfi.regfi_iterator_up.argtypes = [POINTER(REGFI_ITERATOR)]
87regfi.regfi_iterator_up.restype = c_bool
88
89regfi.regfi_iterator_to_root.argtypes = [POINTER(REGFI_ITERATOR)]
90regfi.regfi_iterator_to_root.restype = c_bool
91
92regfi.regfi_iterator_walk_path.argtypes = [POINTER(REGFI_ITERATOR)]
93regfi.regfi_iterator_walk_path.restype = c_bool
94
95regfi.regfi_iterator_cur_key.argtypes = [POINTER(REGFI_ITERATOR)]
96regfi.regfi_iterator_cur_key.restype = POINTER(REGFI_NK)
97
98regfi.regfi_iterator_first_subkey.argtypes = [POINTER(REGFI_ITERATOR)]
99regfi.regfi_iterator_first_subkey.restype = c_bool
100
101regfi.regfi_iterator_cur_subkey.argtypes = [POINTER(REGFI_ITERATOR)]
102regfi.regfi_iterator_cur_subkey.restype = POINTER(REGFI_NK)
103
104regfi.regfi_iterator_next_subkey.argtypes = [POINTER(REGFI_ITERATOR)]
105regfi.regfi_iterator_next_subkey.restype = c_bool
106
107regfi.regfi_iterator_find_subkey.argtypes = [POINTER(REGFI_ITERATOR), c_char_p]
108regfi.regfi_iterator_find_subkey.restype = c_bool
109
110regfi.regfi_iterator_first_value.argtypes = [POINTER(REGFI_ITERATOR)]
111regfi.regfi_iterator_first_value.restype = c_bool
112
113regfi.regfi_iterator_cur_value.argtypes = [POINTER(REGFI_ITERATOR)]
114regfi.regfi_iterator_cur_value.restype = POINTER(REGFI_VK)
115
116regfi.regfi_iterator_next_value.argtypes = [POINTER(REGFI_ITERATOR)]
117regfi.regfi_iterator_next_value.restype = c_bool
118
119regfi.regfi_iterator_find_value.argtypes = [POINTER(REGFI_ITERATOR), c_char_p]
120regfi.regfi_iterator_find_value.restype = c_bool
121
122
[204]123regfi.regfi_init.argtypes = []
124regfi.regfi_init.restype = None
125regfi.regfi_init()
126
127
[210]128## Retrieves messages produced by regfi during parsing and interpretation
129#
[205]130def GetLogMessages():
[206]131    msgs = regfi.regfi_log_get_str()
132    if msgs == None:
133        return ''
[215]134    return msgs.decode('utf-8')
[205]135
136
[208]137def _buffer2bytearray(char_pointer, length):
138    if length == 0 or char_pointer == None:
139        return None
140   
141    ret_val = bytearray(length)
142    for i in range(0,length):
143        ret_val[i] = char_pointer[i][0]
144
145    return ret_val
146
147
[215]148def _strlist2charss(str_list):
149    ret_val = []
150    for s in str_list:
151        ret_val.append(s.encode('utf-8', 'replace'))
152
153    ret_val = (c_char_p*(len(str_list)+1))(*ret_val)
154    # Terminate the char** with a NULL pointer
155    ret_val[-1] = 0
156
157    return ret_val
158
159
[209]160def _charss2strlist(chars_pointer):
161    ret_val = []
162    i = 0
163    s = chars_pointer[i]
164    while s != None:
[213]165        ret_val.append(s.decode('utf-8', 'replace'))
[209]166        i += 1
167        s = chars_pointer[i]
[208]168
[209]169    return ret_val
[208]170
[210]171
172## Abstract class which Handles memory management and proxies attribute
173#  access to base structures 
[212]174class _StructureWrapper(object):
[214]175    _hive = None
176    _base = None
[206]177
[207]178    def __init__(self, hive, base):
[215]179        if not hive:
180            raise Exception("Could not create _StructureWrapper,"
181                            + " hive is NULL.  Current log:\n"
182                            + GetLogMessages())
183        if not base:
184            raise Exception("Could not create _StructureWrapper,"
185                            + " base is NULL.  Current log:\n"
186                            + GetLogMessages())
[214]187        self._hive = hive
188        self._base = base
[206]189
190    def __del__(self):
[214]191        regfi.regfi_free_record(self._base)
[206]192
193    def __getattr__(self, name):
[214]194        return getattr(self._base.contents, name)
[206]195
196    def __eq__(self, other):
197        return (type(self) == type(other)) and (self.offset == other.offset)
198
199    def __ne__(self, other):
200        return (not self.__eq__(other))
201
[208]202class Key(_StructureWrapper):
203    pass
204
[206]205class Value(_StructureWrapper):
206    pass
207
[210]208## Registry value data
[206]209class Data(_StructureWrapper):
210    pass
211
[210]212## Registry security record/permissions
[206]213class Security(_StructureWrapper):
214    pass
215
[207]216
[212]217class _GenericList(object):
[214]218    _hive = None
219    _key = None
220    _length = None
221    _current = None
[207]222
[214]223    # implementation-specific functions for _SubkeyList and _ValueList
224    _fetch_num = None
225    _find_element = None
226    _get_element = None
227    _constructor = None
[208]228
[207]229    def __init__(self, key):
[214]230        self._hive = key._hive
231
232        # Normally it's good to avoid cyclic references like this
233        # (key.list.key...) but in this case it makes ctypes memory
234        # management easier to reference the Key instead of the base
235        # structure.  We use a weak reference in order to allow for garbage
236        # collection, since value/subkey lists should not be usable if their
237        # parent Key is freed anyway.
238
[207]239        # XXX: check for NULL here, throw an exception if so.
[214]240        self._key = weakref.proxy(key)
241        self._length = self._fetch_num(key._base)
242
[207]243   
244    def __len__(self):
[214]245        return self._length
[207]246
247    def __getitem__(self, name):
248        index = c_uint32()
[208]249        if isinstance(name, str):
250            name = name.encode('utf-8')
251
[209]252        if name != None:
253            name = create_string_buffer(bytes(name))
254
[216]255        if self._find_element(self._hive.file, self._key._base, name, byref(index)):
[214]256            return self._constructor(self._hive, 
257                                     self._get_element(self._hive.file,
[216]258                                                       self._key._base,
[214]259                                                       index))
[207]260        raise KeyError('')
261
[209]262    def get(self, name, default):
263        try:
264            return self[name]
265        except KeyError:
266            return default
267   
[207]268    def __iter__(self):
[214]269        self._current = 0
[207]270        return self
271   
272    def __next__(self):
[214]273        if self._current >= self._length:
[207]274            raise StopIteration('')
275
[214]276        elem = self._get_element(self._hive.file, self._key._base,
[215]277                                 c_uint32(self._current))
[214]278        self._current += 1
279        return self._constructor(self._hive, elem)
[207]280   
[212]281    # For Python 2.x
[214]282    next = __next__
[207]283
[212]284
[208]285class _SubkeyList(_GenericList):
[214]286    _fetch_num = regfi.regfi_fetch_num_subkeys
287    _find_element = regfi.regfi_find_subkey
288    _get_element = regfi.regfi_get_subkey
[208]289
290
291class _ValueList(_GenericList):
[214]292    _fetch_num = regfi.regfi_fetch_num_values
293    _find_element = regfi.regfi_find_value
294    _get_element = regfi.regfi_get_value
[208]295
296
[215]297## Registry key
[207]298class Key(_StructureWrapper):
299    values = None
[208]300    subkeys = None
[207]301
302    def __init__(self, hive, base):
303        super(Key, self).__init__(hive, base)
304        self.values = _ValueList(self)
[208]305        self.subkeys = _SubkeyList(self)
[207]306
[208]307    def __getattr__(self, name):
308        if name == "name":
[219]309            ret_val = super(Key, self).__getattr__(name)
310
[209]311            if ret_val == None:
312                ret_val = self.name_raw
313            else:
[213]314                ret_val = ret_val.decode('utf-8', 'replace')
[209]315               
[208]316        elif name == "name_raw":
[219]317            ret_val = super(Key, self).__getattr__(name)
[208]318            length = super(Key, self).__getattr__('name_length')
319            ret_val = _buffer2bytearray(ret_val, length)
320       
[219]321        elif name == "modified":
322            ret_val = regfi.regfi_nt2unix_time(byref(self._base.contents.mtime))
323
324        else:
325            ret_val = super(Key, self).__getattr__(name)
326
[208]327        return ret_val
328
[207]329    def fetch_security(self):
[214]330        return Security(self._hive,
[215]331                        regfi.regfi_fetch_sk(self._hive.file, self._base))
[207]332
[219]333    def fetch_classname(self):
334        ret_val = None
335        cn_p = regfi.regfi_fetch_classname(self._hive.file, self._base)
336        if cn_p:
337            cn_struct = cn_p.contents
338            if cn_struct.interpreted:
339                ret_val = cn_struct.interpreted.decode('utf-8', 'replace')
340            else:
341                ret_val = _buffer2bytearray(cn_struct.raw,
342                                            cn_struct.size)
343            regfi.regfi_free_record(cn_p)
344
345        return ret_val
346
[215]347    def get_parent(self):
[218]348        if self.is_root():
349            return None
[215]350        parent_base = regfi.regfi_get_parentkey(self._hive.file, self._base)
351        if parent_base:
352            return Key(self._hive, parent_base)
353        return None
354
355    def is_root(self):
[218]356        return (self._hive.root == self)
[215]357
358
[210]359## Registry value (metadata)
360#
361# These represent registry values (@ref REGFI_VK records) and provide
362# access to their associated data.
363#
[208]364class Value(_StructureWrapper):
[219]365    def fetch_data(self):
[209]366        ret_val = None
[219]367        data_p = regfi.regfi_fetch_data(self._hive.file, self._base)
368        if not data_p:
369            return None
370        data_struct = data_p.contents
[208]371
[219]372        if data_struct.interpreted_size == 0:
373            ret_val = None
374        elif data_struct.type in (REG_SZ, REG_EXPAND_SZ, REG_LINK):
375            # Unicode strings
376            ret_val = data_struct.interpreted.string.decode('utf-8', 'replace')
377        elif data_struct.type in (REG_DWORD, REG_DWORD_BE):
378            # 32 bit integers
379            ret_val = data_struct.interpreted.dword
380        elif data_struct.type == REG_QWORD:
381            # 64 bit integers
382            ret_val = data_struct.interpreted.qword
383        elif data_struct.type == REG_MULTI_SZ:
384            ret_val = _charss2strlist(data_struct.interpreted.multiple_string)
385        elif data_struct.type in (REG_NONE, REG_RESOURCE_LIST,
386                                  REG_FULL_RESOURCE_DESCRIPTOR,
387                                  REG_RESOURCE_REQUIREMENTS_LIST,
388                                  REG_BINARY):
389            ret_val = _buffer2bytearray(data_struct.interpreted.none,
390                                        data_struct.interpreted_size)
[209]391
[219]392        regfi.regfi_free_record(data_p)
393        return ret_val
394       
395    def fetch_raw_data(self):
396        ret_val = None
[209]397
[219]398        # XXX: should we load the data without interpretation instead?
399        data_p = regfi.regfi_fetch_data(self._hive.file, self._base)
400        if not data_p:
401            return None
[209]402
[219]403        data_struct = data_p.contents
404        ret_val = _buffer2bytearray(data_struct.raw,
405                                    data_struct.size)
406        regfi.regfi_free_record(data_p)
[209]407
[208]408        return ret_val
409
[219]410    def __getattr__(self, name):
411        ret_val = super(Value, self).__getattr__(name)
412        if name == "name":
413            if ret_val == None:
414                ret_val = self.name_raw
415            else:
416                ret_val = ret_val.decode('utf-8', 'replace')
[208]417
[219]418        elif name == "name_raw":
419            length = super(Value, self).__getattr__('name_length')
420            ret_val = _buffer2bytearray(ret_val, length)
421
422        return ret_val
423
424
[208]425# Avoids chicken/egg class definitions.
426# Also makes for convenient code reuse in these lists' parent classes.
[214]427_SubkeyList._constructor = Key
428_ValueList._constructor = Value
[208]429
430
431
[210]432## Represents a single registry hive (file)
433#
434class Hive():
[204]435    file = None
436    raw_file = None
[218]437    _root = None
438
[204]439    def __init__(self, fh):
[209]440        # The fileno method may not exist, or it may throw an exception
[205]441        # when called if the file isn't backed with a descriptor.
442        try:
443            if hasattr(fh, 'fileno'):
[213]444                self.file = regfi.regfi_alloc(fh.fileno(), REGFI_ENCODING_UTF8)
[205]445                return
446        except:
447            pass
[204]448       
[205]449        self.raw_file = structures.REGFI_RAW_FILE()
450        self.raw_file.fh = fh
451        self.raw_file.seek = seek_cb_type(self.raw_file.cb_seek)
452        self.raw_file.read = read_cb_type(self.raw_file.cb_read)
[213]453        self.file = regfi.regfi_alloc_cb(self.raw_file, REGFI_ENCODING_UTF8)
[204]454
455    def __getattr__(self, name):
[218]456        if name == "root":
457            if self._root == None:
458                self._root = Key(self, regfi.regfi_get_rootkey(self.file))
459            return self._root
460
[204]461        return getattr(self.file.contents, name)
[205]462   
[210]463    def __del__(self):
[205]464        regfi.regfi_free(self.file)
465        if self.raw_file != None:
[213]466            self.raw_file = None
[204]467
[205]468    def __iter__(self):
469        return HiveIterator(self)
[204]470
[215]471
[210]472    ## Creates a @ref HiveIterator initialized at the specified path in
473    #  the hive.
474    #
475    # Raises an Exception if the path could not be found/traversed.
[206]476    def subtree(self, path):
477        hi = HiveIterator(self)
478        hi.descend(path)
479        return hi
[205]480
[206]481
[210]482## A special purpose iterator for registry hives
483#
484# Iterating over an object of this type causes all keys in a specific
485# hive subtree to be returned in a depth-first manner. These iterators
486# are typically created using the @ref Hive.subtree() function on a @ref Hive
487# object.
488#
489# HiveIterators can also be used to manually traverse up and down a
490# registry hive as they retain information about the current position in
491# the hive, along with which iteration state for subkeys and values for
492# every parent key.  See the @ref up and @ref down methods for more
493# information.
[205]494class HiveIterator():
495    hive = None
496    iter = None
[206]497    iteration_root = None
[205]498
499    def __init__(self, hive):
[213]500        self.iter = regfi.regfi_iterator_new(hive.file, REGFI_ENCODING_UTF8)
[205]501        if self.iter == None:
502            raise Exception("Could not create iterator.  Current log:\n"
503                            + GetLogMessages())
[214]504        self._hive = hive
[205]505       
506    def __getattr__(self, name):
507        return getattr(self.file.contents, name)
508
509    def __del__(self):   
[213]510        regfi.regfi_iterator_free(self.iter)
[205]511
512    def __iter__(self):
[206]513        self.iteration_root = None
[205]514        return self
515
516    def __next__(self):
[206]517        if self.iteration_root == None:
[213]518            self.iteration_root = self.current_key()
[205]519        elif not regfi.regfi_iterator_down(self.iter):
520            up_ret = regfi.regfi_iterator_up(self.iter)
[206]521            while (up_ret and
522                   not regfi.regfi_iterator_next_subkey(self.iter)):
523                if self.iteration_root == self.current_key():
524                    self.iteration_root = None
525                    raise StopIteration('')
[205]526                up_ret = regfi.regfi_iterator_up(self.iter)
527
528            if not up_ret:
529                raise StopIteration('')
530           
[210]531            # XXX: Use non-generic exception
[205]532            if not regfi.regfi_iterator_down(self.iter):
533                raise Exception('Error traversing iterator downward.'+
534                                ' Current log:\n'+ GetLogMessages())
535
536        regfi.regfi_iterator_first_subkey(self.iter)
[206]537        return self.current_key()
[205]538
[212]539    # For Python 2.x
[214]540    next = __next__
[212]541
[206]542    def down(self):
543        pass
544
545    def up(self):
546        pass
547
548    def descend(self, path):
[215]549        cpath = _strlist2charss(path)
[206]550
[210]551        # XXX: Use non-generic exception
[215]552        if not regfi.regfi_iterator_walk_path(self.iter, cpath):
[206]553            raise Exception('Could not locate path.\n'+GetLogMessages())
554
555    def current_key(self):
[214]556        return Key(self._hive, regfi.regfi_iterator_cur_key(self.iter))
[210]557
558    #XXX Add subkey/value search accessor functions (?)
Note: See TracBrowser for help on using the repository browser.