Coverage for /root/GitHubProjects/impacket/impacket/winregistry.py : 14%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Impacket - Collection of Python classes for working with network protocols.
2#
3# SECUREAUTH LABS. Copyright (C) 2018 SecureAuth Corporation. All rights reserved.
4#
5# This software is provided under a slightly modified version
6# of the Apache Software License. See the accompanying LICENSE file
7# for more information.
8#
9# Description:
10# A Windows Registry Library Parser
11#
12# Author:
13# Alberto Solino (@agsolino)
14#
15# Reference:
16# Data taken from https://bazaar.launchpad.net/~guadalinex-members/dumphive/trunk/view/head:/winreg.txt
17# http://sentinelchicken.com/data/TheWindowsNTRegistryFileFormat.pdf
18#
19# ToDo:
20# [ ] Parse li records, probable the same as the ri but couldn't find any to probe
21#
23from __future__ import division
24from __future__ import print_function
25import sys
26from struct import unpack
27import ntpath
28from six import b
30from impacket import LOG
31from impacket.structure import Structure, hexdump
34# Constants
36ROOT_KEY = 0x2c
37REG_NONE = 0x00
38REG_SZ = 0x01
39REG_EXPAND_SZ = 0x02
40REG_BINARY = 0x03
41REG_DWORD = 0x04
42REG_MULTISZ = 0x07
43REG_QWORD = 0x0b
45# Structs
46class REG_REGF(Structure):
47 structure = (
48 ('Magic','"regf'),
49 ('Unknown','<L=0'),
50 ('Unknown2','<L=0'),
51 ('lastChange','<Q=0'),
52 ('MajorVersion','<L=0'),
53 ('MinorVersion','<L=0'),
54 ('0','<L=0'),
55 ('11','<L=0'),
56 ('OffsetFirstRecord','<L=0'),
57 ('DataSize','<L=0'),
58 ('1111','<L=0'),
59 ('Name','48s=""'),
60 ('Remaining1','411s=b""'),
61 ('CheckSum','<L=0xffffffff'), # Sum of all DWORDs from 0x0 to 0x1FB
62 ('Remaining2','3585s=b""'),
63 )
65class REG_HBIN(Structure):
66 structure = (
67 ('Magic','"hbin'),
68 ('OffsetFirstHBin','<L=0'),
69 ('OffsetNextHBin','<L=0'),
70 ('BlockSize','<L=0'),
71 )
73class REG_HBINBLOCK(Structure):
74 structure = (
75 ('DataBlockSize','<l=0'),
76 ('_Data','_-Data','self["DataBlockSize"]*(-1)-4'),
77 ('Data',':'),
78 )
80class REG_NK(Structure):
81 structure = (
82 ('Magic','"nk'),
83 ('Type','<H=0'),
84 ('lastChange','<Q=0'),
85 ('Unknown','<L=0'),
86 ('OffsetParent','<l=0'),
87 ('NumSubKeys','<L=0'),
88 ('Unknown2','<L=0'),
89 ('OffsetSubKeyLf','<l=0'),
90 ('Unknown3','<L=0'),
91 ('NumValues','<L=0'),
92 ('OffsetValueList','<l=0'),
93 ('OffsetSkRecord','<l=0'),
94 ('OffsetClassName','<l=0'),
95 ('UnUsed','20s=b""'),
96 ('NameLength','<H=0'),
97 ('ClassNameLength','<H=0'),
98 ('_KeyName','_-KeyName','self["NameLength"]'),
99 ('KeyName',':'),
100 )
102class REG_VK(Structure):
103 structure = (
104 ('Magic','"vk'),
105 ('NameLength','<H=0'),
106 ('DataLen','<l=0'),
107 ('OffsetData','<L=0'),
108 ('ValueType','<L=0'),
109 ('Flag','<H=0'),
110 ('UnUsed','<H=0'),
111 ('_Name','_-Name','self["NameLength"]'),
112 ('Name',':'),
113 )
115class REG_LF(Structure):
116 structure = (
117 ('Magic','"lf'),
118 ('NumKeys','<H=0'),
119 ('HashRecords',':'),
120 )
122class REG_LH(Structure):
123 structure = (
124 ('Magic','"lh'),
125 ('NumKeys','<H=0'),
126 ('HashRecords',':'),
127 )
129class REG_RI(Structure):
130 structure = (
131 ('Magic','"ri'),
132 ('NumKeys','<H=0'),
133 ('HashRecords',':'),
134 )
136class REG_SK(Structure):
137 structure = (
138 ('Magic','"sk'),
139 ('UnUsed','<H=0'),
140 ('OffsetPreviousSk','<l=0'),
141 ('OffsetNextSk','<l=0'),
142 ('UsageCounter','<L=0'),
143 ('SizeSk','<L=0'),
144 ('Data',':'),
145 )
147class REG_HASH(Structure):
148 structure = (
149 ('OffsetNk','<L=0'),
150 ('KeyName','4s=b""'),
151 )
153StructMappings = {b'nk': REG_NK,
154 b'vk': REG_VK,
155 b'lf': REG_LF,
156 b'lh': REG_LH,
157 b'ri': REG_RI,
158 b'sk': REG_SK,
159 }
161class Registry:
162 def __init__(self, hive, isRemote = False):
163 self.__hive = hive
164 if isRemote is True:
165 self.fd = self.__hive
166 self.__hive.open()
167 else:
168 self.fd = open(hive,'rb')
169 data = self.fd.read(4096)
170 self.__regf = REG_REGF(data)
171 self.indent = ''
172 self.rootKey = self.__findRootKey()
173 if self.rootKey is None:
174 LOG.error("Can't find root key!")
175 elif self.__regf['MajorVersion'] != 1 and self.__regf['MinorVersion'] > 5:
176 LOG.warning("Unsupported version (%d.%d) - things might not work!" % (self.__regf['MajorVersion'], self.__regf['MinorVersion']))
178 def close(self):
179 self.fd.close()
181 def __del__(self):
182 self.close()
184 def __findRootKey(self):
185 self.fd.seek(0,0)
186 data = self.fd.read(4096)
187 while len(data) > 0:
188 try:
189 hbin = REG_HBIN(data[:0x20])
190 # Read the remaining bytes for this hbin
191 data += self.fd.read(hbin['OffsetNextHBin']-4096)
192 data = data[0x20:]
193 blocks = self.__processDataBlocks(data)
194 for block in blocks:
195 if isinstance(block, REG_NK):
196 if block['Type'] == ROOT_KEY:
197 return block
198 except Exception as e:
199 pass
200 data = self.fd.read(4096)
202 return None
205 def __getBlock(self, offset):
206 self.fd.seek(4096+offset,0)
207 sizeBytes = self.fd.read(4)
208 data = sizeBytes + self.fd.read(unpack('<l',sizeBytes)[0]*-1-4)
209 if len(data) == 0:
210 return None
211 else:
212 block = REG_HBINBLOCK(data)
213 if block['Data'][:2] in StructMappings:
214 return StructMappings[block['Data'][:2]](block['Data'])
215 else:
216 LOG.debug("Unknown type 0x%s" % block['Data'][:2])
217 return block
218 return None
220 def __getValueBlocks(self, offset, count):
221 valueList = []
222 res = []
223 self.fd.seek(4096+offset,0)
224 for i in range(count):
225 valueList.append(unpack('<l',self.fd.read(4))[0])
227 for valueOffset in valueList:
228 if valueOffset > 0:
229 block = self.__getBlock(valueOffset)
230 res.append(block)
231 return res
233 def __getData(self, offset, count):
234 self.fd.seek(4096+offset, 0)
235 return self.fd.read(count)[4:]
237 def __processDataBlocks(self,data):
238 res = []
239 while len(data) > 0:
240 #blockSize = unpack('<l',data[:calcsize('l')])[0]
241 blockSize = unpack('<l',data[:4])[0]
242 block = REG_HBINBLOCK()
243 if blockSize > 0:
244 tmpList = list(block.structure)
245 tmpList[1] = ('_Data','_-Data','self["DataBlockSize"]-4')
246 block.structure = tuple(tmpList)
248 block.fromString(data)
249 blockLen = len(block)
251 if block['Data'][:2] in StructMappings:
252 block = StructMappings[block['Data'][:2]](block['Data'])
254 res.append(block)
255 data = data[blockLen:]
256 return res
258 def __getValueData(self, rec):
259 # We should receive a VK record
260 if rec['DataLen'] == 0:
261 return ''
262 if rec['DataLen'] < 0:
263 # if DataLen < 5 the value itself is stored in the Offset field
264 return rec['OffsetData']
265 else:
266 return self.__getData(rec['OffsetData'], rec['DataLen']+4)
268 def __getLhHash(self, key):
269 res = 0
270 for bb in key.upper():
271 res *= 37
272 res += ord(bb)
273 return res % 0x100000000
275 def __compareHash(self, magic, hashData, key):
276 if magic == 'lf':
277 hashRec = REG_HASH(hashData)
278 if hashRec['KeyName'].strip(b'\x00') == b(key[:4]):
279 return hashRec['OffsetNk']
280 elif magic == 'lh':
281 hashRec = REG_HASH(hashData)
282 if unpack('<L',hashRec['KeyName'])[0] == self.__getLhHash(key):
283 return hashRec['OffsetNk']
284 elif magic == 'ri':
285 # Special case here, don't know exactly why, an ri pointing to a NK :-o
286 offset = unpack('<L', hashData[:4])[0]
287 nk = self.__getBlock(offset)
288 if nk['KeyName'] == key:
289 return offset
290 else:
291 LOG.critical("UNKNOWN Magic %s" % magic)
292 sys.exit(1)
294 return None
296 def __findSubKey(self, parentKey, subKey):
297 lf = self.__getBlock(parentKey['OffsetSubKeyLf'])
298 if lf is not None:
299 data = lf['HashRecords']
300 # Let's search the hash records for the name
301 if lf['Magic'] == 'ri':
302 # ri points to lf/lh records, so we must parse them before
303 records = b''
304 for i in range(lf['NumKeys']):
305 offset = unpack('<L', data[:4])[0]
306 l = self.__getBlock(offset)
307 records = records + l['HashRecords'][:l['NumKeys']*8]
308 data = data[4:]
309 data = records
311 #for record in range(lf['NumKeys']):
312 for record in range(parentKey['NumSubKeys']):
313 hashRec = data[:8]
314 res = self.__compareHash(lf['Magic'], hashRec, subKey)
315 if res is not None:
316 # We have a match, now let's check the whole record
317 nk = self.__getBlock(res)
318 if nk['KeyName'].decode('utf-8') == subKey:
319 return nk
320 data = data[8:]
322 return None
324 def __walkSubNodes(self, rec):
325 nk = self.__getBlock(rec['OffsetNk'])
326 if isinstance(nk, REG_NK):
327 print("%s%s" % (self.indent, nk['KeyName'].decode('utf-8')))
328 self.indent += ' '
329 if nk['OffsetSubKeyLf'] < 0:
330 self.indent = self.indent[:-2]
331 return
332 lf = self.__getBlock(nk['OffsetSubKeyLf'])
333 else:
334 lf = nk
336 data = lf['HashRecords']
338 if lf['Magic'] == 'ri':
339 # ri points to lf/lh records, so we must parse them before
340 records = ''
341 for i in range(lf['NumKeys']):
342 offset = unpack('<L', data[:4])[0]
343 l = self.__getBlock(offset)
344 records = records + l['HashRecords'][:l['NumKeys']*8]
345 data = data[4:]
346 data = records
348 for key in range(lf['NumKeys']):
349 hashRec = REG_HASH(data[:8])
350 self.__walkSubNodes(hashRec)
351 data = data[8:]
353 if isinstance(nk, REG_NK):
354 self.indent = self.indent[:-2]
356 def walk(self, parentKey):
357 key = self.findKey(parentKey)
359 if key is None or key['OffsetSubKeyLf'] < 0:
360 return
362 lf = self.__getBlock(key['OffsetSubKeyLf'])
363 data = lf['HashRecords']
364 for record in range(lf['NumKeys']):
365 hashRec = REG_HASH(data[:8])
366 self.__walkSubNodes(hashRec)
367 data = data[8:]
369 def findKey(self, key):
370 # Let's strip '\' from the beginning, except for the case of
371 # only asking for the root node
372 if key[0] == '\\' and len(key) > 1:
373 key = key[1:]
375 parentKey = self.rootKey
376 if len(key) > 0 and key[0]!='\\':
377 for subKey in key.split('\\'):
378 res = self.__findSubKey(parentKey, subKey)
379 if res is not None:
380 parentKey = res
381 else:
382 #LOG.error("Key %s not found!" % key)
383 return None
385 return parentKey
387 def printValue(self, valueType, valueData):
388 if valueType == REG_SZ or valueType == REG_EXPAND_SZ:
389 if type(valueData) is int:
390 print('NULL')
391 else:
392 print("%s" % (valueData.decode('utf-16le')))
393 elif valueType == REG_BINARY:
394 print('')
395 hexdump(valueData, self.indent)
396 elif valueType == REG_DWORD:
397 print("%d" % valueData)
398 elif valueType == REG_QWORD:
399 print("%d" % (unpack('<Q',valueData)[0]))
400 elif valueType == REG_NONE:
401 try:
402 if len(valueData) > 1:
403 print('')
404 hexdump(valueData, self.indent)
405 else:
406 print(" NULL")
407 except:
408 print(" NULL")
409 elif valueType == REG_MULTISZ:
410 print("%s" % (valueData.decode('utf-16le')))
411 else:
412 print("Unknown Type 0x%x!" % valueType)
413 hexdump(valueData)
415 def enumKey(self, parentKey):
416 res = []
417 # If we're here.. we have a valid NK record for the key
418 # Now let's searcht the subkeys
419 if parentKey['NumSubKeys'] > 0:
420 lf = self.__getBlock(parentKey['OffsetSubKeyLf'])
421 data = lf['HashRecords']
423 if lf['Magic'] == 'ri':
424 # ri points to lf/lh records, so we must parse them before
425 records = ''
426 for i in range(lf['NumKeys']):
427 offset = unpack('<L', data[:4])[0]
428 l = self.__getBlock(offset)
429 records = records + l['HashRecords'][:l['NumKeys']*8]
430 data = data[4:]
431 data = records
433 for i in range(parentKey['NumSubKeys']):
434 hashRec = REG_HASH(data[:8])
435 nk = self.__getBlock(hashRec['OffsetNk'])
436 data = data[8:]
437 res.append('%s'%nk['KeyName'].decode('utf-8'))
438 return res
440 def enumValues(self,key):
441 # If we're here.. we have a valid NK record for the key
442 # Now let's search its values
443 resp = []
444 if key['NumValues'] > 0:
445 valueList = self.__getValueBlocks(key['OffsetValueList'], key['NumValues']+1)
447 for value in valueList:
448 if value['Flag'] > 0:
449 resp.append(value['Name'])
450 else:
451 resp.append(b'default')
453 return resp
455 def getValue(self, keyValue):
456 # returns a tuple with (ValueType, ValueData) for the requested keyValue
457 regKey = ntpath.dirname(keyValue)
458 regValue = ntpath.basename(keyValue)
460 key = self.findKey(regKey)
462 if key is None:
463 return None
465 if key['NumValues'] > 0:
466 valueList = self.__getValueBlocks(key['OffsetValueList'], key['NumValues']+1)
468 for value in valueList:
469 if value['Name'] == b(regValue):
470 return value['ValueType'], self.__getValueData(value)
471 elif regValue == 'default' and value['Flag'] <=0:
472 return value['ValueType'], self.__getValueData(value)
474 return None
476 def getClass(self, className):
478 key = self.findKey(className)
480 if key is None:
481 return None
483 #print key.dump()
484 if key['OffsetClassName'] > 0:
485 value = self.__getBlock(key['OffsetClassName'])
486 return value['Data']