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

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

fixed some pyregfi parameter bugs
fixed some iterator memory management problems
updated smoketest script to use ctypes pyregfi

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