1#!/bin/python
2
3#    This file is part of python-registry.
4#
5#   Copyright 2011, 2012 Willi Ballenthin <william.ballenthin@mandiant.com>
6#                    while at Mandiant <http://www.mandiant.com>
7#
8#   Licensed under the Apache License, Version 2.0 (the "License");
9#   you may not use this file except in compliance with the License.
10#   You may obtain a copy of the License at
11#
12#       http://www.apache.org/licenses/LICENSE-2.0
13#
14#   Unless required by applicable law or agreed to in writing, software
15#   distributed under the License is distributed on an "AS IS" BASIS,
16#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17#   See the License for the specific language governing permissions and
18#   limitations under the License.
19from __future__ import print_function
20
21import sys
22import ntpath
23from enum import Enum
24
25from . import RegistryParse
26
27RegSZ = 0x0001
28RegExpandSZ = 0x0002
29RegBin = 0x0003
30RegDWord = 0x0004
31RegMultiSZ = 0x0007
32RegQWord = 0x000B
33RegNone = 0x0000
34RegBigEndian = 0x0005
35RegLink = 0x0006
36RegResourceList = 0x0008
37RegFullResourceDescriptor = 0x0009
38RegResourceRequirementsList = 0x000A
39RegFileTime = 0x0010
40
41DEVPROP_MASK_TYPE = 0x00000FFF
42
43class HiveType(Enum):
44    UNKNOWN = ""
45    NTUSER = "ntuser.dat"
46    SAM = "sam"
47    SECURITY = "security"
48    SOFTWARE = "software"
49    SYSTEM = "system"
50    USRCLASS = "usrclass.dat"
51    BCD = "bcd"
52    COMPONENTS = "components"
53    DEFAULT = "default"
54    SCHEMA = "schema.dat"
55    SETTINGS = "settings.dat"
56
57
58class RegistryKeyHasNoParentException(RegistryParse.RegistryStructureDoesNotExist):
59    """
60    """
61    def __init__(self, value):
62        """
63        Constructor.
64        Arguments:
65        - `value`: A string description.
66        """
67        super(RegistryKeyHasNoParentException, self).__init__(value)
68
69    def __str__(self):
70        return "Registry key has no parent key: %s" % (self._value)
71
72
73class RegistryKeyNotFoundException(RegistryParse.RegistryStructureDoesNotExist):
74    """
75    """
76    def __init__(self, value):
77        """
78
79        Arguments:
80        - `value`:
81        """
82        super(RegistryKeyNotFoundException, self).__init__(value)
83
84    def __str__(self):
85        return "Registry key not found: %s" % (self._value)
86
87class RegistryValueNotFoundException(RegistryParse.RegistryStructureDoesNotExist):
88    """
89    """
90    def __init__(self, value):
91        """
92
93        Arguments:
94        - `value`:
95        """
96        super(RegistryValueNotFoundException, self).__init__(value)
97
98    def __str__(self):
99        return "Registry value not found: %s" % (self._value)
100
101class RegistryValue(object):
102    """
103    This is a high level structure for working with the Windows Registry.
104    It represents the 3-tuple of (name, type, value) associated with
105      a registry value.
106    """
107    def __init__(self, vkrecord):
108        self._vkrecord = vkrecord
109
110    def __repr__(self):
111        return 'RegistryValue(name="{0}", value="{1}", type="{2}")'.format(self.name(), self.value(), self.value_type_str())
112
113    def name(self):
114        """
115        Get the name of the value as a string.
116        The name of the default value is returned as "(default)".
117        """
118        if self._vkrecord.has_name():
119            return self._vkrecord.name()
120        else:
121            return  "(default)"
122
123    def value_type(self):
124        """
125        Get the type of the value as an integer constant.
126
127        One of:
128         - RegSZ = 0x0001
129         - RegExpandSZ = 0x0002
130         - RegBin = 0x0003
131         - RegDWord = 0x0004
132         - RegMultiSZ = 0x0007
133         - RegQWord = 0x000B
134         - RegNone = 0x0000
135         - RegBigEndian = 0x0005
136         - RegLink = 0x0006
137         - RegResourceList = 0x0008
138         - RegFullResourceDescriptor = 0x0009
139         - RegResourceRequirementsList = 0x000A
140         - RegUint8 = 0x101
141         - RegInt16 = 0x102
142         - RegUint16 = 0x103
143         - RegInt32 = 0x104
144         - RegUint32 = 0x105
145         - RegInt64 = 0x106
146         - RegUint64 = 0x107
147         - RegFloat = 0x108
148         - RegDouble = 0x109
149         - RegUnicodeChar = 0x10A
150         - RegBoolean = 0x10B
151         - RegUnicodeString = 0x10C
152         - RegCompositeValue = 0x10D
153         - RegDateTimeOffset = 0x10E
154         - RegTimeSpan = 0x10F
155         - RegGUID = 0x110
156         - RegUnk111 = 0x111
157         - RegUnk112 = 0x112
158         - RegUnk113 = 0x113
159         - RegBytesArray = 0x114
160         - RegInt16Array = 0x115
161         - RegUint16Array = 0x116
162         - RegInt32Array = 0x117
163         - RegUInt32Array = 0x118
164         - RegInt64Array = 0x119
165         - RegUInt64Array = 0x11A
166         - RegFloatArray = 0x11B
167         - RegDoubleArray = 0x11C
168         - RegUnicodeCharArray = 0x11D
169         - RegBooleanArray = 0x11E
170         - RegUnicodeStringArray = 0x11F
171        """
172        return self._vkrecord.data_type()
173
174    def value_type_str(self):
175        """
176        Get the type of the value as a string.
177
178        One of:
179         - RegSZ
180         - RegExpandSZ
181         - RegBin
182         - RegDWord
183         - RegMultiSZ
184         - RegQWord
185         - RegNone
186         - RegBigEndian
187         - RegLink
188         - RegResourceList
189         - RegFullResourceDescriptor
190         - RegResourceRequirementsList
191         - RegUint8
192         - RegInt16
193         - RegUint16
194         - RegInt32
195         - RegUint32
196         - RegInt64
197         - RegUint64
198         - RegFloat
199         - RegDouble
200         - RegUnicodeChar
201         - RegBoolean
202         - RegUnicodeString
203         - RegCompositeValue
204         - RegDateTimeOffset
205         - RegTimeSpan
206         - RegGUID
207         - RegUnk111
208         - RegUnk112
209         - RegUnk113
210         - RegBytesArray
211         - RegInt16Array
212         - RegUint16Array
213         - RegInt32Array
214         - RegUInt32Array
215         - RegInt64Array
216         - RegUInt64Array
217         - RegFloatArray
218         - RegDoubleArray
219         - RegUnicodeCharArray
220         - RegBooleanArray
221         - RegUnicodeStringArray
222        """
223        return self._vkrecord.data_type_str()
224
225    def value(self, overrun=0):
226        return self._vkrecord.data(overrun)
227
228    def raw_data(self, overrun=0):
229        return self._vkrecord.raw_data(overrun)
230
231    def timestamp(self):
232        """
233        Get the last modified timestamp as a Python datetime. Only valid for
234        AppContainer settings.dat reg hive
235        """
236        return self._vkrecord.timestamp()
237
238
239class RegistryKey(object):
240    """
241    A high level structure for use in traversing the Windows Registry.
242    A RegistryKey is a node in a tree-like structure.
243    A RegistryKey may have a set of values associated with it,
244      as well as a last modified timestamp.
245    """
246    def __init__(self, nkrecord):
247        """
248
249        Arguments:
250        - `NKRecord`:
251        """
252        self._nkrecord = nkrecord
253
254    def __str__(self):
255        return "Registry Key %s with %d values and %d subkeys" % \
256            (self.path(), len(self.values()), len(self.subkeys()))
257
258    def __repr__(self):
259        return 'RegistryKey(name="{0}", path="{1}")'.format(self.name(), self.path())
260
261
262    def __getitem__(self, key):
263        return self.value(key)
264
265    def timestamp(self):
266        """
267        Get the last modified timestamp as a Python datetime.
268        """
269        return self._nkrecord.timestamp()
270
271    def name(self):
272        """
273        Get the name of the key as a string.
274
275        For example, "Windows" if the key path were
276        /{hive name}/SOFTWARE/Microsoft/Windows
277        See RegistryKey.path() to get the complete key name.
278        """
279        return self._nkrecord.name()
280
281    def path(self):
282        """
283        Get the full path of the RegistryKey as a string.
284        For example, "/{hive name}/SOFTWARE/Microsoft/Windows"
285        """
286        return self._nkrecord.path()
287
288    def parent(self):
289        """
290        Get the parent RegistryKey of this key, or raise
291        RegistryKeyHasNoParentException if it does not exist (for example,
292        the root key has no parent).
293        """
294        # there may be a memory inefficiency here, since we create
295        # a new RegistryKey from the NKRecord parent key, rather
296        # than using the parent of this instance, if it exists.
297        try:
298            return RegistryKey(self._nkrecord.parent_key())
299        except RegistryParse.ParseException:
300            raise RegistryKeyHasNoParentException(self.name())
301
302    def subkeys(self):
303        """
304        Return a list of all subkeys.
305        Each element in the list is a RegistryKey.
306        If the key has no subkeys, the empty list is returned.
307        """
308        if self._nkrecord.subkey_number() == 0:
309            return []
310
311        l = self._nkrecord.subkey_list()
312        return [RegistryKey(k) for k in l.keys()]
313
314    def subkey(self, name):
315        """
316        Return the subkey with a given name as a RegistryKey.
317        Raises RegistryKeyNotFoundException if the subkey with
318          the given name does not exist.
319        """
320        if self._nkrecord.subkey_number() == 0:
321            raise RegistryKeyNotFoundException(self.path() + "\\" + name)
322
323        for k in self._nkrecord.subkey_list().keys():
324            if k.name().lower() == name.lower():
325                return RegistryKey(k)
326        raise RegistryKeyNotFoundException(self.path() + "\\" + name)
327
328    def values(self):
329        """
330        Return a list containing the values associated with this RegistryKey.
331        Each element of the list will be a RegistryValue.
332        If there are no values associated with this RegistryKey, then the
333        empty list is returned.
334        """
335        try:
336            return [RegistryValue(v) for v in self._nkrecord.values_list().values()]
337        except RegistryParse.RegistryStructureDoesNotExist:
338            return []
339
340    def value(self, name):
341        """
342        Return the value with the given name as a RegistryValue.
343        Raises RegistryValueNotFoundExceptiono if the value with
344          the given name does not exist.
345        """
346        if name == "(default)":
347            name = ""
348        try:
349            for v in self._nkrecord.values_list().values():
350                if v.name().lower() == name.lower():
351                    return RegistryValue(v)
352        except RegistryParse.RegistryStructureDoesNotExist:
353            raise RegistryValueNotFoundException(self.path() + " : " + name)
354        raise RegistryValueNotFoundException(self.path() + " : " + name)
355
356    def find_key(self, path):
357        """
358        Perform a search for a RegistryKey with a specific path.
359        """
360        if len(path) == 0:
361            return self
362
363        (immediate, _, future) = path.partition("\\")
364        return self.subkey(immediate).find_key(future)
365
366    def values_number(self):
367    	"""
368    	Return the number of values associated with this key
369    	"""
370    	return self._nkrecord.values_number()
371
372    def subkeys_number(self):
373    	"""
374    	Return the number of subkeys associated with this key
375    	"""
376    	return self._nkrecord.subkey_number()
377
378
379class Registry(object):
380    """
381    A class for parsing and reading from a Windows Registry file.
382    """
383    def __init__(self, filelikeobject):
384        """
385        Constructor.
386        Arguments:
387        - `filelikeobject`: A file-like object with a .read() method.
388              If a Python string is passed, it is interpreted as a filename,
389              and the corresponding file is opened.
390        """
391        try:
392            self._buf = filelikeobject.read()
393        except AttributeError:
394            with open(filelikeobject, "rb") as f:
395                self._buf = f.read()
396        self._regf = RegistryParse.REGFBlock(self._buf, 0, False)
397
398    def __repr__(self):
399        return 'Registry(hive_name="{0}", hive_type="{1}")'.format(self.hive_name(), self.hive_type())
400
401    def hive_name(self):
402        """Returns the internal file name"""
403        return self._regf.hive_name()
404
405    def hive_type(self):
406        """Returns the hive type"""
407        temp = self.hive_name()
408        temp = temp.replace('\\??\\', '')
409        temp = ntpath.basename(temp)
410
411        if temp.lower() == HiveType.NTUSER.value:
412            return HiveType.NTUSER
413        elif temp.lower() == HiveType.SAM.value:
414            return HiveType.SAM
415        elif temp.lower() == HiveType.SECURITY.value:
416            return HiveType.SECURITY
417        elif temp.lower() == HiveType.SOFTWARE.value:
418            return HiveType.SOFTWARE
419        elif temp.lower() == HiveType.SYSTEM.value:
420            return HiveType.SYSTEM
421        elif temp.lower() == HiveType.USRCLASS.value:
422            return HiveType.USRCLASS
423        elif temp.lower() == HiveType.BCD.value:
424            return HiveType.BCD
425        elif temp.lower() == HiveType.COMPONENTS.value:
426            return HiveType.COMPONENTS
427        elif temp.lower() == HiveType.DEFAULT.value:
428            return HiveType.DEFAULT
429        elif temp.lower() == HiveType.SCHEMA.value:
430            return HiveType.SCHEMA
431        elif temp.lower() == HiveType.SETTINGS.value:
432            return HiveType.SETTINGS
433        else:
434            return HiveType.UNKNOWN
435
436    def root(self):
437        """
438        Return the first RegistryKey in the hive.
439        """
440        return RegistryKey(self._regf.first_key())
441
442    def open(self, path):
443        """
444        Return a RegistryKey by full path.
445        Subkeys are separated by the backslash character ('\').
446        A trailing backslash may or may not be present.
447        The hive name should not be included.
448        """
449        # is the first registry key always the root?
450        # are there any other keys at this
451        # level? is this the name of the hive?
452        return RegistryKey(self._regf.first_key()).find_key(path)
453
454def print_all(key):
455    if len(key.subkeys()) == 0:
456        print(key.path())
457    else:
458        for k in key.subkeys():
459            print_all(k)
460
461if __name__ == '__main__':
462    r = Registry(sys.argv[1])
463    print_all(r.root())
464