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

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

fixed python2 compatbility problems in pyregfi
added better install scripts for both python2 and python3

File size: 14.9 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]
18regfi.regfi_alloc.restype = POINTER(REGFI_FILE)
19
20regfi.regfi_alloc_cb.argtypes = [POINTER(REGFI_RAW_FILE)]
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'))
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
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
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 __len__(self):
208        return self.length
209
210    def __getitem__(self, name):
211        index = c_uint32()
212        if isinstance(name, str):
213            name = name.encode('utf-8')
214
215        if name != None:
216            name = create_string_buffer(bytes(name))
217
218        if self.find_element(self.hive.file, self.key.base, name, byref(index)):
219            return self.constructor(self.hive, self.get_element(self.hive.file,
220                                                                self.key.base,
221                                                                index))
222        raise KeyError('')
223
224    def get(self, name, default):
225        try:
226            return self[name]
227        except KeyError:
228            return default
229   
230    def __iter__(self):
231        self.current = 0
232        return self
233   
234    def __next__(self):
235        if self.current >= self.length:
236            raise StopIteration('')
237
238        elem = self.get_element(self.hive.file, self.key.base,
239                                c_uint32(self.current))
240        self.current += 1
241        return self.constructor(self.hive, elem)
242   
243    # For Python 2.x
244    def next(self):
245        return self.__next__()
246
247
248class _SubkeyList(_GenericList):
249    fetch_num = regfi.regfi_fetch_num_subkeys
250    find_element = regfi.regfi_find_subkey
251    get_element = regfi.regfi_get_subkey
252
253
254class _ValueList(_GenericList):
255    fetch_num = regfi.regfi_fetch_num_values
256    find_element = regfi.regfi_find_value
257    get_element = regfi.regfi_get_value
258
259
260class Key(_StructureWrapper):
261    values = None
262    subkeys = None
263
264    def __init__(self, hive, base):
265        super(Key, self).__init__(hive, base)
266        self.values = _ValueList(self)
267        self.subkeys = _SubkeyList(self)
268
269    def __getattr__(self, name):
270        ret_val = super(Key, self).__getattr__(name)
271       
272        if name == "name":
273            if ret_val == None:
274                ret_val = self.name_raw
275            else:
276                ret_val = ret_val.decode('utf-8')
277               
278        elif name == "name_raw":
279            length = super(Key, self).__getattr__('name_length')
280            ret_val = _buffer2bytearray(ret_val, length)
281       
282        return ret_val
283
284
285    def fetch_security(self):
286        return Security(self.hive,
287                        regfi.regfi_fetch_sk(self.hive.file, self.base))
288
289
290## Registry value (metadata)
291#
292# These represent registry values (@ref REGFI_VK records) and provide
293# access to their associated data.
294#
295class Value(_StructureWrapper):
296    def __getattr__(self, name):
297        ret_val = None
298        if name == "data":
299            data_p = regfi.regfi_fetch_data(self.hive.file, self.base)
300            try:
301                data_struct = data_p.contents
302            except Exception:
303                return None
304
305            if data_struct.interpreted_size == 0:
306                ret_val = None
307            elif data_struct.type in (REG_SZ, REG_EXPAND_SZ, REG_LINK):
308                # Unicode strings
309                ret_val = data_struct.interpreted.string.decode('utf-8')
310            elif data_struct.type in (REG_DWORD, REG_DWORD_BE):
311                # 32 bit integers
312                ret_val = data_struct.interpreted.dword
313            elif data_struct.type == REG_QWORD:
314                # 64 bit integers
315                ret_val = data_struct.interpreted.qword
316            elif data_struct.type == REG_MULTI_SZ:
317                ret_val = _charss2strlist(data_struct.interpreted.multiple_string)
318            elif data_struct.type in (REG_NONE, REG_RESOURCE_LIST,
319                                      REG_FULL_RESOURCE_DESCRIPTOR,
320                                      REG_RESOURCE_REQUIREMENTS_LIST,
321                                      REG_BINARY):
322                ret_val = _buffer2bytearray(data_struct.interpreted.none,
323                                            data_struct.interpreted_size)
324
325            regfi.regfi_free_record(data_p)
326           
327        elif name == "data_raw":
328            # XXX: should we load the data without interpretation instead?
329            data_p = regfi.regfi_fetch_data(self.hive.file, self.base)
330            try:
331                data_struct = data_p.contents
332            except Exception:
333                return None
334
335            ret_val = _buffer2bytearray(data_struct.raw,
336                                        data_struct.size)
337            regfi.regfi_free_record(data_p)           
338           
339        else:
340            ret_val = super(Value, self).__getattr__(name)
341            if name == "name":
342                if ret_val == None:
343                    ret_val = self.name_raw
344                else:
345                    ret_val = ret_val.decode('utf-8')
346
347            elif name == "name_raw":
348                length = super(Value, self).__getattr__('name_length')
349                ret_val = _buffer2bytearray(ret_val, length)
350
351        return ret_val
352
353
354# Avoids chicken/egg class definitions.
355# Also makes for convenient code reuse in these lists' parent classes.
356_SubkeyList.constructor = Key
357_ValueList.constructor = Value
358
359
360
361## Represents a single registry hive (file)
362#
363class Hive():
364    file = None
365    raw_file = None
366   
367    def __init__(self, fh):
368        # The fileno method may not exist, or it may throw an exception
369        # when called if the file isn't backed with a descriptor.
370        try:
371            if hasattr(fh, 'fileno'):
372                self.file = regfi.regfi_alloc(fh.fileno())
373                return
374        except:
375            pass
376       
377        self.raw_file = structures.REGFI_RAW_FILE()
378        self.raw_file.fh = fh
379        self.raw_file.seek = seek_cb_type(self.raw_file.cb_seek)
380        self.raw_file.read = read_cb_type(self.raw_file.cb_read)
381        self.file = regfi.regfi_alloc_cb(self.raw_file)
382
383    def __getattr__(self, name):
384        return getattr(self.file.contents, name)
385   
386    def __del__(self):
387        regfi.regfi_free(self.file)
388        if self.raw_file != None:
389            regfi.regfi_free(self.file)
390           
391
392    def __iter__(self):
393        return HiveIterator(self)
394
395    ## Creates a @ref HiveIterator initialized at the specified path in
396    #  the hive.
397    #
398    # Raises an Exception if the path could not be found/traversed.
399    def subtree(self, path):
400        hi = HiveIterator(self)
401        hi.descend(path)
402        return hi
403
404
405## A special purpose iterator for registry hives
406#
407# Iterating over an object of this type causes all keys in a specific
408# hive subtree to be returned in a depth-first manner. These iterators
409# are typically created using the @ref Hive.subtree() function on a @ref Hive
410# object.
411#
412# HiveIterators can also be used to manually traverse up and down a
413# registry hive as they retain information about the current position in
414# the hive, along with which iteration state for subkeys and values for
415# every parent key.  See the @ref up and @ref down methods for more
416# information.
417class HiveIterator():
418    hive = None
419    iter = None
420    iteration_root = None
421
422    def __init__(self, hive):
423        # REGFI_ENCODING_UTF8==1
424        self.iter = regfi.regfi_iterator_new(hive.file, 1)
425        if self.iter == None:
426            raise Exception("Could not create iterator.  Current log:\n"
427                            + GetLogMessages())
428        self.hive = hive
429       
430    def __getattr__(self, name):
431        return getattr(self.file.contents, name)
432
433    def __del__(self):   
434        regfi.regfi_iterator_free(self.iter)       
435
436    def __iter__(self):
437        self.iteration_root = None
438        return self
439
440    def __next__(self):
441        if self.iteration_root == None:
442            self.iteration_root = self.current_key()           
443        elif not regfi.regfi_iterator_down(self.iter):
444            up_ret = regfi.regfi_iterator_up(self.iter)
445            while (up_ret and
446                   not regfi.regfi_iterator_next_subkey(self.iter)):
447                if self.iteration_root == self.current_key():
448                    self.iteration_root = None
449                    raise StopIteration('')
450                up_ret = regfi.regfi_iterator_up(self.iter)
451
452            if not up_ret:
453                raise StopIteration('')
454           
455            # XXX: Use non-generic exception
456            if not regfi.regfi_iterator_down(self.iter):
457                raise Exception('Error traversing iterator downward.'+
458                                ' Current log:\n'+ GetLogMessages())
459
460        regfi.regfi_iterator_first_subkey(self.iter)
461        return self.current_key()
462
463    # For Python 2.x
464    def next(self):
465        return self.__next__()
466
467    def down(self):
468        pass
469
470    def up(self):
471        pass
472
473    def descend(self, path):
474        #set up generator
475        cpath = (bytes(p,'ascii') for p in path) 
476
477        # evaluate generator and create char* array
478        apath = (c_char_p*len(path))(*cpath)
479
480        # XXX: Use non-generic exception
481        if not regfi.regfi_iterator_walk_path(self.iter,apath):
482            raise Exception('Could not locate path.\n'+GetLogMessages())
483
484    def current_key(self):
485        return Key(self.hive, regfi.regfi_iterator_cur_key(self.iter))
486
487    #XXX Add subkey/value search accessor functions (?)
Note: See TracBrowser for help on using the repository browser.