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

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

fixed pyregfi circular class references that caused garbage collection problems
renamed several pyregfi properties/methods as "private"

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