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

# Coverage for /root/GitHubProjects/impacket/impacket/winregistry.py : 57%

## 284 statements

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# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.

2#

3# This software is provided under under a slightly modified version

4# of the Apache Software License. See the accompanying LICENSE file

5# for more information.

6#

7# Author: Alberto Solino (@agsolino)

8#

9# Description: A Windows Registry Library Parser

10#

11# Data taken from https://bazaar.launchpad.net/~guadalinex-members/dumphive/trunk/view/head:/winreg.txt

12# http://sentinelchicken.com/data/TheWindowsNTRegistryFileFormat.pdf

13#

14#

15# ToDo:

16#

17# [ ] Parse li records, probable the same as the ri but couldn't find any to probe

18

19 from __future__ import division

20 from __future__ import print_function

21 import sys

22 from struct import unpack

23 import ntpath

24 from six import b

25

26 from impacket import LOG

27 from impacket.structure import Structure, hexdump

28

29

30# Constants

31

32 ROOT_KEY = 0x2c

33 REG_NONE = 0x00

34 REG_SZ = 0x01

35 REG_EXPAND_SZ = 0x02

36 REG_BINARY = 0x03

37 REG_DWORD = 0x04

38 REG_MULTISZ = 0x07

39 REG_QWORD = 0x0b

40

41# Structs

42 class REG_REGF(Structure):

43 structure = (

44 ('Magic','"regf'),

45 ('Unknown','<L=0'),

46 ('Unknown2','<L=0'),

47 ('lastChange','<Q=0'),

48 ('MajorVersion','<L=0'),

49 ('MinorVersion','<L=0'),

50 ('0','<L=0'),

51 ('11','<L=0'),

52 ('OffsetFirstRecord','<L=0'),

53 ('DataSize','<L=0'),

54 ('1111','<L=0'),

55 ('Name','48s=""'),

56 ('Remaining1','411s=b""'),

57 ('CheckSum','<L=0xffffffff'), # Sum of all DWORDs from 0x0 to 0x1FB

58 ('Remaining2','3585s=b""'),

59 )

60

61 class REG_HBIN(Structure):

62 structure = (

63 ('Magic','"hbin'),

64 ('OffsetFirstHBin','<L=0'),

65 ('OffsetNextHBin','<L=0'),

66 ('BlockSize','<L=0'),

67 )

68

69 class REG_HBINBLOCK(Structure):

70 structure = (

71 ('DataBlockSize','<l=0'),

72 ('_Data','_-Data','self["DataBlockSize"]*(-1)-4'),

73 ('Data',':'),

74 )

75

76 class REG_NK(Structure):

77 structure = (

78 ('Magic','"nk'),

79 ('Type','<H=0'),

80 ('lastChange','<Q=0'),

81 ('Unknown','<L=0'),

82 ('OffsetParent','<l=0'),

83 ('NumSubKeys','<L=0'),

84 ('Unknown2','<L=0'),

85 ('OffsetSubKeyLf','<l=0'),

86 ('Unknown3','<L=0'),

87 ('NumValues','<L=0'),

88 ('OffsetValueList','<l=0'),

89 ('OffsetSkRecord','<l=0'),

90 ('OffsetClassName','<l=0'),

91 ('UnUsed','20s=b""'),

92 ('NameLength','<H=0'),

93 ('ClassNameLength','<H=0'),

94 ('_KeyName','_-KeyName','self["NameLength"]'),

95 ('KeyName',':'),

96 )

97

98 class REG_VK(Structure):

99 structure = (

100 ('Magic','"vk'),

101 ('NameLength','<H=0'),

102 ('DataLen','<l=0'),

103 ('OffsetData','<L=0'),

104 ('ValueType','<L=0'),

105 ('Flag','<H=0'),

106 ('UnUsed','<H=0'),

107 ('_Name','_-Name','self["NameLength"]'),

108 ('Name',':'),

109 )

110

111 class REG_LF(Structure):

112 structure = (

113 ('Magic','"lf'),

114 ('NumKeys','<H=0'),

115 ('HashRecords',':'),

116 )

117

118 class REG_LH(Structure):

119 structure = (

120 ('Magic','"lh'),

121 ('NumKeys','<H=0'),

122 ('HashRecords',':'),

123 )

124

125 class REG_RI(Structure):

126 structure = (

127 ('Magic','"ri'),

128 ('NumKeys','<H=0'),

129 ('HashRecords',':'),

130 )

131

132 class REG_SK(Structure):

133 structure = (

134 ('Magic','"sk'),

135 ('UnUsed','<H=0'),

136 ('OffsetPreviousSk','<l=0'),

137 ('OffsetNextSk','<l=0'),

138 ('UsageCounter','<L=0'),

139 ('SizeSk','<L=0'),

140 ('Data',':'),

141 )

142

143 class REG_HASH(Structure):

144 structure = (

145 ('OffsetNk','<L=0'),

146 ('KeyName','4s=b""'),

147 )

148

149 StructMappings = {b'nk': REG_NK,

150 b'vk': REG_VK,

151 b'lf': REG_LF,

152 b'lh': REG_LH,

153 b'ri': REG_RI,

154 b'sk': REG_SK,

155 }

156

157 class Registry:

158 def __init__(self, hive, isRemote = False):

159 self.__hive = hive

160 if isRemote is True: 160 ↛ 164line 160 didn't jump to line 164, because the condition on line 160 was never false

161 self.fd = self.__hive

162 self.__hive.open()

163 else:

164 self.fd = open(hive,'rb')

165 data = self.fd.read(4096)

166 self.__regf = REG_REGF(data)

167 self.indent = ''

168 self.rootKey = self.__findRootKey()

169 if self.rootKey is None: 169 ↛ 170line 169 didn't jump to line 170, because the condition on line 169 was never true

170 LOG.error("Can't find root key!")

171 elif self.__regf['MajorVersion'] != 1 and self.__regf['MinorVersion'] > 5: 171 ↛ 172line 171 didn't jump to line 172, because the condition on line 171 was never true

172 LOG.warning("Unsupported version (%d.%d) - things might not work!" % (self.__regf['MajorVersion'], self.__regf['MinorVersion']))

173

174 def close(self):

175 self.fd.close()

176

177 def __del__(self):

178 self.close()

179

180 def __findRootKey(self):

181 self.fd.seek(0,0)

182 data = self.fd.read(4096)

183 while len(data) > 0: 183 ↛ 198line 183 didn't jump to line 198, because the condition on line 183 was never false

184 try:

185 hbin = REG_HBIN(data[:0x20])

186 # Read the remaining bytes for this hbin

187 data += self.fd.read(hbin['OffsetNextHBin']-4096)

188 data = data[0x20:]

189 blocks = self.__processDataBlocks(data)

190 for block in blocks: 190 ↛ 196line 190 didn't jump to line 196, because the loop on line 190 didn't complete

191 if isinstance(block, REG_NK): 191 ↛ 190line 191 didn't jump to line 190, because the condition on line 191 was never false

192 if block['Type'] == ROOT_KEY: 192 ↛ 190line 192 didn't jump to line 190, because the condition on line 192 was never false

193 return block

194 except Exception as e:

195 pass

196 data = self.fd.read(4096)

197

198 return None

199

200

201 def __getBlock(self, offset):

202 self.fd.seek(4096+offset,0)

203 sizeBytes = self.fd.read(4)

204 data = sizeBytes + self.fd.read(unpack('<l',sizeBytes)[0]*-1-4)

205 if len(data) == 0: 205 ↛ 206line 205 didn't jump to line 206, because the condition on line 205 was never true

206 return None

207 else:

208 block = REG_HBINBLOCK(data)

209 if block['Data'][:2] in StructMappings: 209 ↛ 212line 209 didn't jump to line 212, because the condition on line 209 was never false

210 return StructMappings[block['Data'][:2]](block['Data'])

211 else:

212 LOG.debug("Unknown type 0x%s" % block['Data'][:2])

213 return block

214 return None

215

216 def __getValueBlocks(self, offset, count):

217 valueList = []

218 res = []

219 self.fd.seek(4096+offset,0)

220 for i in range(count):

221 valueList.append(unpack('<l',self.fd.read(4))[0])

222

223 for valueOffset in valueList:

224 if valueOffset > 0:

225 block = self.__getBlock(valueOffset)

226 res.append(block)

227 return res

228

229 def __getData(self, offset, count):

230 self.fd.seek(4096+offset, 0)

231 return self.fd.read(count)[4:]

232

233 def __processDataBlocks(self,data):

234 res = []

235 while len(data) > 0:

236 #blockSize = unpack('<l',data[:calcsize('l')])[0]

237 blockSize = unpack('<l',data[:4])[0]

238 block = REG_HBINBLOCK()

239 if blockSize > 0:

240 tmpList = list(block.structure)

241 tmpList[1] = ('_Data','_-Data','self["DataBlockSize"]-4')

242 block.structure = tuple(tmpList)

243

244 block.fromString(data)

245 blockLen = len(block)

246

247 if block['Data'][:2] in StructMappings:

248 block = StructMappings[block['Data'][:2]](block['Data'])

249

250 res.append(block)

251 data = data[blockLen:]

252 return res

253

254 def __getValueData(self, rec):

255 # We should receive a VK record

256 if rec['DataLen'] == 0: 256 ↛ 257line 256 didn't jump to line 257, because the condition on line 256 was never true

257 return ''

258 if rec['DataLen'] < 0:

259 # if DataLen < 5 the value itself is stored in the Offset field

260 return rec['OffsetData']

261 else:

262 return self.__getData(rec['OffsetData'], rec['DataLen']+4)

263

264 def __getLhHash(self, key):

265 res = 0

266 for bb in key.upper():

267 res *= 37

268 res += ord(bb)

269 return res % 0x100000000

270

271 def __compareHash(self, magic, hashData, key):

272 if magic == 'lf': 272 ↛ 276line 272 didn't jump to line 276, because the condition on line 272 was never false

273 hashRec = REG_HASH(hashData)

274 if hashRec['KeyName'].strip(b'\x00') == b(key[:4]):

275 return hashRec['OffsetNk']

276 elif magic == 'lh':

277 hashRec = REG_HASH(hashData)

278 if unpack('<L',hashRec['KeyName'])[0] == self.__getLhHash(key):

279 return hashRec['OffsetNk']

280 elif magic == 'ri':

281 # Special case here, don't know exactly why, an ri pointing to a NK :-o

282 offset = unpack('<L', hashData[:4])[0]

283 nk = self.__getBlock(offset)

284 if nk['KeyName'] == key:

285 return offset

286 else:

287 LOG.critical("UNKNOWN Magic %s" % magic)

288 sys.exit(1)

289

290 return None

291

292 def __findSubKey(self, parentKey, subKey):

293 lf = self.__getBlock(parentKey['OffsetSubKeyLf'])

294 if lf is not None: 294 ↛ 318line 294 didn't jump to line 318, because the condition on line 294 was never false

295 data = lf['HashRecords']

296 # Let's search the hash records for the name

297 if lf['Magic'] == 'ri': 297 ↛ 299line 297 didn't jump to line 299, because the condition on line 297 was never true

298 # ri points to lf/lh records, so we must parse them before

299 records = b''

300 for i in range(lf['NumKeys']):

301 offset = unpack('<L', data[:4])[0]

302 l = self.__getBlock(offset)

303 records = records + l['HashRecords'][:l['NumKeys']*8]

304 data = data[4:]

305 data = records

306

307 #for record in range(lf['NumKeys']):

308 for record in range(parentKey['NumSubKeys']): 308 ↛ 318line 308 didn't jump to line 318, because the loop on line 308 didn't complete

309 hashRec = data[:8]

310 res = self.__compareHash(lf['Magic'], hashRec, subKey)

311 if res is not None:

312 # We have a match, now let's check the whole record

313 nk = self.__getBlock(res)

314 if nk['KeyName'].decode('utf-8') == subKey:

315 return nk

316 data = data[8:]

317

318 return None

319

320 def __walkSubNodes(self, rec):

321 nk = self.__getBlock(rec['OffsetNk'])

322 if isinstance(nk, REG_NK):

323 print("%s%s" % (self.indent, nk['KeyName'].decode('utf-8')))

324 self.indent += ' '

325 if nk['OffsetSubKeyLf'] < 0:

326 self.indent = self.indent[:-2]

327 return

328 lf = self.__getBlock(nk['OffsetSubKeyLf'])

329 else:

330 lf = nk

331

332 data = lf['HashRecords']

333

334 if lf['Magic'] == 'ri':

335 # ri points to lf/lh records, so we must parse them before

336 records = ''

337 for i in range(lf['NumKeys']):

338 offset = unpack('<L', data[:4])[0]

339 l = self.__getBlock(offset)

340 records = records + l['HashRecords'][:l['NumKeys']*8]

341 data = data[4:]

342 data = records

343

344 for key in range(lf['NumKeys']):

345 hashRec = REG_HASH(data[:8])

346 self.__walkSubNodes(hashRec)

347 data = data[8:]

348

349 if isinstance(nk, REG_NK):

350 self.indent = self.indent[:-2]

351

352 def walk(self, parentKey):

353 key = self.findKey(parentKey)

354

355 if key is None or key['OffsetSubKeyLf'] < 0:

356 return

357

358 lf = self.__getBlock(key['OffsetSubKeyLf'])

359 data = lf['HashRecords']

360 for record in range(lf['NumKeys']):

361 hashRec = REG_HASH(data[:8])

362 self.__walkSubNodes(hashRec)

363 data = data[8:]

364

365 def findKey(self, key):

366 # Let's strip '\' from the beginning, except for the case of

367 # only asking for the root node

368 if key[0] == '\\' and len(key) > 1:

369 key = key[1:]

370

371 parentKey = self.rootKey

372 if len(key) > 0 and key[0]!='\\': 372 ↛ 381line 372 didn't jump to line 381, because the condition on line 372 was never false

373 for subKey in key.split('\\'):

374 res = self.__findSubKey(parentKey, subKey)

375 if res is not None: 375 ↛ 379line 375 didn't jump to line 379, because the condition on line 375 was never false

376 parentKey = res

377 else:

378 #LOG.error("Key %s not found!" % key)

379 return None

380

381 return parentKey

382

383 def printValue(self, valueType, valueData):

384 if valueType == REG_SZ or valueType == REG_EXPAND_SZ:

385 if type(valueData) is int:

386 print('NULL')

387 else:

388 print("%s" % (valueData.decode('utf-16le')))

389 elif valueType == REG_BINARY:

390 print('')

391 hexdump(valueData, self.indent)

392 elif valueType == REG_DWORD:

393 print("%d" % valueData)

394 elif valueType == REG_QWORD:

395 print("%d" % (unpack('<Q',valueData)[0]))

396 elif valueType == REG_NONE:

397 try:

398 if len(valueData) > 1:

399 print('')

400 hexdump(valueData, self.indent)

401 else:

402 print(" NULL")

403 except:

404 print(" NULL")

405 elif valueType == REG_MULTISZ:

406 print("%s" % (valueData.decode('utf-16le')))

407 else:

408 print("Unknown Type 0x%x!" % valueType)

409 hexdump(valueData)

410

411 def enumKey(self, parentKey):

412 res = []

413 # If we're here.. we have a valid NK record for the key

414 # Now let's searcht the subkeys

415 if parentKey['NumSubKeys'] > 0: 415 ↛ 434line 415 didn't jump to line 434, because the condition on line 415 was never false

416 lf = self.__getBlock(parentKey['OffsetSubKeyLf'])

417 data = lf['HashRecords']

418

419 if lf['Magic'] == 'ri': 419 ↛ 421line 419 didn't jump to line 421, because the condition on line 419 was never true

420 # ri points to lf/lh records, so we must parse them before

421 records = ''

422 for i in range(lf['NumKeys']):

423 offset = unpack('<L', data[:4])[0]

424 l = self.__getBlock(offset)

425 records = records + l['HashRecords'][:l['NumKeys']*8]

426 data = data[4:]

427 data = records

428

429 for i in range(parentKey['NumSubKeys']):

430 hashRec = REG_HASH(data[:8])

431 nk = self.__getBlock(hashRec['OffsetNk'])

432 data = data[8:]

433 res.append('%s'%nk['KeyName'].decode('utf-8'))

434 return res

435

436 def enumValues(self,key):

437 # If we're here.. we have a valid NK record for the key

438 # Now let's search its values

439 resp = []

440 if key['NumValues'] > 0: 440 ↛ 449line 440 didn't jump to line 449, because the condition on line 440 was never false

441 valueList = self.__getValueBlocks(key['OffsetValueList'], key['NumValues']+1)

442

443 for value in valueList:

444 if value['Flag'] > 0: 444 ↛ 447line 444 didn't jump to line 447, because the condition on line 444 was never false

445 resp.append(value['Name'])

446 else:

447 resp.append(b'default')

448

449 return resp

450

451 def getValue(self, keyValue):

452 # returns a tuple with (ValueType, ValueData) for the requested keyValue

453 regKey = ntpath.dirname(keyValue)

454 regValue = ntpath.basename(keyValue)

455

456 key = self.findKey(regKey)

457

458 if key is None: 458 ↛ 459line 458 didn't jump to line 459, because the condition on line 458 was never true

459 return None

460

461 if key['NumValues'] > 0: 461 ↛ 470line 461 didn't jump to line 470, because the condition on line 461 was never false

462 valueList = self.__getValueBlocks(key['OffsetValueList'], key['NumValues']+1)

463

464 for value in valueList: 464 ↛ 470line 464 didn't jump to line 470, because the loop on line 464 didn't complete

465 if value['Name'] == b(regValue):

466 return value['ValueType'], self.__getValueData(value)

467 elif regValue == 'default' and value['Flag'] <=0:

468 return value['ValueType'], self.__getValueData(value)

469

470 return None

471

472 def getClass(self, className):

473

474 key = self.findKey(className)

475

476 if key is None:

477 return None

478

479 #print key.dump()

480 if key['OffsetClassName'] > 0:

481 value = self.__getBlock(key['OffsetClassName'])

482 return value['Data']