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