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

Last change on this file since 210 was 210, checked in by tim, 14 years ago

beginnings of pyregfi documentation build script

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