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
Line 
1#!/usr/bin/env python
2
3## @package pyregfi
4# Python interface to the regfi library.
5#
6
7import sys
8from pyregfi.structures import *
9
10import ctypes
11import ctypes.util
12from ctypes import c_char,c_char_p,c_int,c_uint16,c_uint32,c_bool,POINTER
13
14regfi = ctypes.CDLL(ctypes.util.find_library('regfi'), use_errno=True)
15
16
17regfi.regfi_alloc.argtypes = [c_int, REGFI_ENCODING]
18regfi.regfi_alloc.restype = POINTER(REGFI_FILE)
19
20regfi.regfi_alloc_cb.argtypes = [POINTER(REGFI_RAW_FILE), REGFI_ENCODING]
21regfi.regfi_alloc_cb.restype = POINTER(REGFI_FILE)
22
23regfi.regfi_free.argtypes = [POINTER(REGFI_FILE)]
24regfi.regfi_free.restype = None
25
26regfi.regfi_log_get_str.argtypes = []
27regfi.regfi_log_get_str.restype = c_char_p
28
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
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
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
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
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
112regfi.regfi_init.argtypes = []
113regfi.regfi_init.restype = None
114regfi.regfi_init()
115
116
117## Retrieves messages produced by regfi during parsing and interpretation
118#
119def GetLogMessages():
120    msgs = regfi.regfi_log_get_str()
121    if msgs == None:
122        return ''
123    return msgs.decode('ascii')
124
125
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
137def _charss2strlist(chars_pointer):
138    ret_val = []
139    i = 0
140    s = chars_pointer[i]
141    while s != None:
142        ret_val.append(s.decode('utf-8', 'replace'))
143        i += 1
144        s = chars_pointer[i]
145
146    return ret_val
147
148
149## Abstract class which Handles memory management and proxies attribute
150#  access to base structures 
151class _StructureWrapper(object):
152    hive = None
153    base = None
154
155    def __init__(self, hive, base):
156        self.hive = hive
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)
162        hive = None
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
173## Registry key
174class Key(_StructureWrapper):
175    pass
176
177class Value(_StructureWrapper):
178    pass
179
180## Registry value data
181class Data(_StructureWrapper):
182    pass
183
184## Registry security record/permissions
185class Security(_StructureWrapper):
186    pass
187
188
189class _GenericList(object):
190    hive = None
191    key = None
192    length = None
193    current = None
194
195    # implementation-specific functions
196    fetch_num = None
197    find_element = None
198    get_element = None
199    constructor = None
200
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
205        self.length = self.fetch_num(key.base)
206   
207    def __del__(self):
208        self.key = None
209
210    def __len__(self):
211        return self.length
212
213    def __getitem__(self, name):
214        index = c_uint32()
215        if isinstance(name, str):
216            name = name.encode('utf-8')
217
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)):
222            return self.constructor(self.hive, self.get_element(self.hive.file,
223                                                                self.key.base,
224                                                                index))
225        raise KeyError('')
226
227    def get(self, name, default):
228        try:
229            return self[name]
230        except KeyError:
231            return default
232   
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
241        elem = self.get_element(self.hive.file, self.key.base,
242                                c_uint32(self.current))
243        self.current += 1
244        return self.constructor(self.hive, elem)
245   
246    # For Python 2.x
247    def next(self):
248        return self.__next__()
249
250
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
263class Key(_StructureWrapper):
264    values = None
265    subkeys = None
266
267    def __init__(self, hive, base):
268        super(Key, self).__init__(hive, base)
269        self.values = _ValueList(self)
270        self.subkeys = _SubkeyList(self)
271
272    def __getattr__(self, name):
273        ret_val = super(Key, self).__getattr__(name)
274       
275        if name == "name":
276            if ret_val == None:
277                ret_val = self.name_raw
278            else:
279                ret_val = ret_val.decode('utf-8', 'replace')
280               
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
288    def fetch_security(self):
289        return Security(self.hive,
290                        regfi.regfi_fetch_sk(self.hive.file, self.base))
291
292
293## Registry value (metadata)
294#
295# These represent registry values (@ref REGFI_VK records) and provide
296# access to their associated data.
297#
298class Value(_StructureWrapper):
299    def __getattr__(self, name):
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
307
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
312                ret_val = data_struct.interpreted.string.decode('utf-8', 'replace')
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:
348                    ret_val = ret_val.decode('utf-8', 'replace')
349
350            elif name == "name_raw":
351                length = super(Value, self).__getattr__('name_length')
352                ret_val = _buffer2bytearray(ret_val, length)
353
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
364## Represents a single registry hive (file)
365#
366class Hive():
367    file = None
368    raw_file = None
369   
370    def __init__(self, fh):
371        # The fileno method may not exist, or it may throw an exception
372        # when called if the file isn't backed with a descriptor.
373        try:
374            if hasattr(fh, 'fileno'):
375                self.file = regfi.regfi_alloc(fh.fileno(), REGFI_ENCODING_UTF8)
376                return
377        except:
378            pass
379       
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)
384        self.file = regfi.regfi_alloc_cb(self.raw_file, REGFI_ENCODING_UTF8)
385
386    def __getattr__(self, name):
387        return getattr(self.file.contents, name)
388   
389    def __del__(self):
390        regfi.regfi_free(self.file)
391        if self.raw_file != None:
392            self.raw_file = None
393
394    def __iter__(self):
395        return HiveIterator(self)
396
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.
401    def subtree(self, path):
402        hi = HiveIterator(self)
403        hi.descend(path)
404        return hi
405
406
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.
419class HiveIterator():
420    hive = None
421    iter = None
422    iteration_root = None
423
424    def __init__(self, hive):
425        self.iter = regfi.regfi_iterator_new(hive.file, REGFI_ENCODING_UTF8)
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):   
435        regfi.regfi_iterator_free(self.iter)
436
437    def __iter__(self):
438        self.iteration_root = None
439        return self
440
441    def __next__(self):
442        if self.iteration_root == None:
443            self.iteration_root = self.current_key()
444        elif not regfi.regfi_iterator_down(self.iter):
445            up_ret = regfi.regfi_iterator_up(self.iter)
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('')
451                up_ret = regfi.regfi_iterator_up(self.iter)
452
453            if not up_ret:
454                raise StopIteration('')
455           
456            # XXX: Use non-generic exception
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)
462        return self.current_key()
463
464    # For Python 2.x
465    def next(self):
466        return self.__next__()
467
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
481        # XXX: Use non-generic exception
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):
486        return Key(self.hive, regfi.regfi_iterator_cur_key(self.iter))
487
488    #XXX Add subkey/value search accessor functions (?)
Note: See TracBrowser for help on using the repository browser.