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

Last change on this file since 213 was 213, checked in by tim, 14 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.