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
Line 
1#!/usr/bin/env python
2
3## @package pyregfi
4# Python interface to the regfi library.
5#
6
7import sys
8import time
9import weakref
10from pyregfi.structures import *
11
12import ctypes
13import ctypes.util
14from ctypes import c_char,c_char_p,c_int,c_uint16,c_uint32,c_bool,POINTER
15
16regfi = ctypes.CDLL(ctypes.util.find_library('regfi'), use_errno=True)
17
18
19regfi.regfi_alloc.argtypes = [c_int, REGFI_ENCODING]
20regfi.regfi_alloc.restype = POINTER(REGFI_FILE)
21
22regfi.regfi_alloc_cb.argtypes = [POINTER(REGFI_RAW_FILE), REGFI_ENCODING]
23regfi.regfi_alloc_cb.restype = POINTER(REGFI_FILE)
24
25regfi.regfi_free.argtypes = [POINTER(REGFI_FILE)]
26regfi.regfi_free.restype = None
27
28regfi.regfi_log_get_str.argtypes = []
29regfi.regfi_log_get_str.restype = c_char_p
30
31regfi.regfi_log_set_mask.argtypes = [c_uint16]
32regfi.regfi_log_set_mask.restype = c_bool
33
34regfi.regfi_get_rootkey.argtypes = [POINTER(REGFI_FILE)]
35regfi.regfi_get_rootkey.restype = POINTER(REGFI_NK)
36
37regfi.regfi_free_record.argtypes = [c_void_p]
38regfi.regfi_free_record.restype = None
39
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
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
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
71regfi.regfi_get_parentkey.argtypes = [POINTER(REGFI_FILE), POINTER(REGFI_NK)]
72regfi.regfi_get_parentkey.restype = POINTER(REGFI_NK)
73
74regfi.regfi_nt2unix_time.argtypes = [POINTER(REGFI_NTTIME)]
75regfi.regfi_nt2unix_time.restype = c_double
76
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
123regfi.regfi_init.argtypes = []
124regfi.regfi_init.restype = None
125regfi.regfi_init()
126
127
128## Retrieves messages produced by regfi during parsing and interpretation
129#
130def GetLogMessages():
131    msgs = regfi.regfi_log_get_str()
132    if msgs == None:
133        return ''
134    return msgs.decode('utf-8')
135
136
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
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
160def _charss2strlist(chars_pointer):
161    ret_val = []
162    i = 0
163    s = chars_pointer[i]
164    while s != None:
165        ret_val.append(s.decode('utf-8', 'replace'))
166        i += 1
167        s = chars_pointer[i]
168
169    return ret_val
170
171
172## Abstract class which Handles memory management and proxies attribute
173#  access to base structures 
174class _StructureWrapper(object):
175    _hive = None
176    _base = None
177
178    def __init__(self, hive, base):
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())
187        self._hive = hive
188        self._base = base
189
190    def __del__(self):
191        regfi.regfi_free_record(self._base)
192
193    def __getattr__(self, name):
194        return getattr(self._base.contents, name)
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
202class Key(_StructureWrapper):
203    pass
204
205class Value(_StructureWrapper):
206    pass
207
208## Registry value data
209class Data(_StructureWrapper):
210    pass
211
212## Registry security record/permissions
213class Security(_StructureWrapper):
214    pass
215
216
217class _GenericList(object):
218    _hive = None
219    _key = None
220    _length = None
221    _current = None
222
223    # implementation-specific functions for _SubkeyList and _ValueList
224    _fetch_num = None
225    _find_element = None
226    _get_element = None
227    _constructor = None
228
229    def __init__(self, key):
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
239        # XXX: check for NULL here, throw an exception if so.
240        self._key = weakref.proxy(key)
241        self._length = self._fetch_num(key._base)
242
243   
244    def __len__(self):
245        return self._length
246
247    def __getitem__(self, name):
248        index = c_uint32()
249        if isinstance(name, str):
250            name = name.encode('utf-8')
251
252        if name != None:
253            name = create_string_buffer(bytes(name))
254
255        if self._find_element(self._hive.file, self._key._base, name, byref(index)):
256            return self._constructor(self._hive, 
257                                     self._get_element(self._hive.file,
258                                                       self._key._base,
259                                                       index))
260        raise KeyError('')
261
262    def get(self, name, default):
263        try:
264            return self[name]
265        except KeyError:
266            return default
267   
268    def __iter__(self):
269        self._current = 0
270        return self
271   
272    def __next__(self):
273        if self._current >= self._length:
274            raise StopIteration('')
275
276        elem = self._get_element(self._hive.file, self._key._base,
277                                 c_uint32(self._current))
278        self._current += 1
279        return self._constructor(self._hive, elem)
280   
281    # For Python 2.x
282    next = __next__
283
284
285class _SubkeyList(_GenericList):
286    _fetch_num = regfi.regfi_fetch_num_subkeys
287    _find_element = regfi.regfi_find_subkey
288    _get_element = regfi.regfi_get_subkey
289
290
291class _ValueList(_GenericList):
292    _fetch_num = regfi.regfi_fetch_num_values
293    _find_element = regfi.regfi_find_value
294    _get_element = regfi.regfi_get_value
295
296
297## Registry key
298class Key(_StructureWrapper):
299    values = None
300    subkeys = None
301
302    def __init__(self, hive, base):
303        super(Key, self).__init__(hive, base)
304        self.values = _ValueList(self)
305        self.subkeys = _SubkeyList(self)
306
307    def __getattr__(self, name):
308        if name == "name":
309            ret_val = super(Key, self).__getattr__(name)
310
311            if ret_val == None:
312                ret_val = self.name_raw
313            else:
314                ret_val = ret_val.decode('utf-8', 'replace')
315               
316        elif name == "name_raw":
317            ret_val = super(Key, self).__getattr__(name)
318            length = super(Key, self).__getattr__('name_length')
319            ret_val = _buffer2bytearray(ret_val, length)
320       
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
327        return ret_val
328
329    def fetch_security(self):
330        return Security(self._hive,
331                        regfi.regfi_fetch_sk(self._hive.file, self._base))
332
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
347    def get_parent(self):
348        if self.is_root():
349            return None
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):
356        return (self._hive.root == self)
357
358
359## Registry value (metadata)
360#
361# These represent registry values (@ref REGFI_VK records) and provide
362# access to their associated data.
363#
364class Value(_StructureWrapper):
365    def fetch_data(self):
366        ret_val = None
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
371
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)
391
392        regfi.regfi_free_record(data_p)
393        return ret_val
394       
395    def fetch_raw_data(self):
396        ret_val = None
397
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
402
403        data_struct = data_p.contents
404        ret_val = _buffer2bytearray(data_struct.raw,
405                                    data_struct.size)
406        regfi.regfi_free_record(data_p)
407
408        return ret_val
409
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')
417
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
425# Avoids chicken/egg class definitions.
426# Also makes for convenient code reuse in these lists' parent classes.
427_SubkeyList._constructor = Key
428_ValueList._constructor = Value
429
430
431
432## Represents a single registry hive (file)
433#
434class Hive():
435    file = None
436    raw_file = None
437    _root = None
438
439    def __init__(self, fh):
440        # The fileno method may not exist, or it may throw an exception
441        # when called if the file isn't backed with a descriptor.
442        try:
443            if hasattr(fh, 'fileno'):
444                self.file = regfi.regfi_alloc(fh.fileno(), REGFI_ENCODING_UTF8)
445                return
446        except:
447            pass
448       
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)
453        self.file = regfi.regfi_alloc_cb(self.raw_file, REGFI_ENCODING_UTF8)
454
455    def __getattr__(self, name):
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
461        return getattr(self.file.contents, name)
462   
463    def __del__(self):
464        regfi.regfi_free(self.file)
465        if self.raw_file != None:
466            self.raw_file = None
467
468    def __iter__(self):
469        return HiveIterator(self)
470
471
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.
476    def subtree(self, path):
477        hi = HiveIterator(self)
478        hi.descend(path)
479        return hi
480
481
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.
494class HiveIterator():
495    hive = None
496    iter = None
497    iteration_root = None
498
499    def __init__(self, hive):
500        self.iter = regfi.regfi_iterator_new(hive.file, REGFI_ENCODING_UTF8)
501        if self.iter == None:
502            raise Exception("Could not create iterator.  Current log:\n"
503                            + GetLogMessages())
504        self._hive = hive
505       
506    def __getattr__(self, name):
507        return getattr(self.file.contents, name)
508
509    def __del__(self):   
510        regfi.regfi_iterator_free(self.iter)
511
512    def __iter__(self):
513        self.iteration_root = None
514        return self
515
516    def __next__(self):
517        if self.iteration_root == None:
518            self.iteration_root = self.current_key()
519        elif not regfi.regfi_iterator_down(self.iter):
520            up_ret = regfi.regfi_iterator_up(self.iter)
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('')
526                up_ret = regfi.regfi_iterator_up(self.iter)
527
528            if not up_ret:
529                raise StopIteration('')
530           
531            # XXX: Use non-generic exception
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)
537        return self.current_key()
538
539    # For Python 2.x
540    next = __next__
541
542    def down(self):
543        pass
544
545    def up(self):
546        pass
547
548    def descend(self, path):
549        cpath = _strlist2charss(path)
550
551        # XXX: Use non-generic exception
552        if not regfi.regfi_iterator_walk_path(self.iter, cpath):
553            raise Exception('Could not locate path.\n'+GetLogMessages())
554
555    def current_key(self):
556        return Key(self._hive, regfi.regfi_iterator_cur_key(self.iter))
557
558    #XXX Add subkey/value search accessor functions (?)
Note: See TracBrowser for help on using the repository browser.