Hide keyboard shortcuts

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# 

22 

23from __future__ import division 

24from __future__ import print_function 

25import sys 

26from struct import unpack 

27import ntpath 

28from six import b 

29 

30from impacket import LOG 

31from impacket.structure import Structure, hexdump 

32 

33 

34# Constants 

35 

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 

44 

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 ) 

64 

65class REG_HBIN(Structure): 

66 structure = ( 

67 ('Magic','"hbin'), 

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

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

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

71 ) 

72 

73class REG_HBINBLOCK(Structure): 

74 structure = ( 

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

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

77 ('Data',':'), 

78 ) 

79 

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 ) 

101 

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 ) 

114 

115class REG_LF(Structure): 

116 structure = ( 

117 ('Magic','"lf'), 

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

119 ('HashRecords',':'), 

120 ) 

121 

122class REG_LH(Structure): 

123 structure = ( 

124 ('Magic','"lh'), 

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

126 ('HashRecords',':'), 

127 ) 

128 

129class REG_RI(Structure): 

130 structure = ( 

131 ('Magic','"ri'), 

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

133 ('HashRecords',':'), 

134 ) 

135 

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 ) 

146 

147class REG_HASH(Structure): 

148 structure = ( 

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

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

151 ) 

152 

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 } 

160 

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'])) 

177 

178 def close(self): 

179 self.fd.close() 

180 

181 def __del__(self): 

182 self.close() 

183 

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) 

201 

202 return None 

203 

204 

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 

219 

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]) 

226 

227 for valueOffset in valueList: 

228 if valueOffset > 0: 

229 block = self.__getBlock(valueOffset) 

230 res.append(block) 

231 return res 

232 

233 def __getData(self, offset, count): 

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

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

236 

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) 

247 

248 block.fromString(data) 

249 blockLen = len(block) 

250 

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

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

253 

254 res.append(block) 

255 data = data[blockLen:] 

256 return res 

257 

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) 

267 

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 

274 

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) 

293 

294 return None 

295 

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 

310 

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:] 

321 

322 return None 

323 

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 

335 

336 data = lf['HashRecords'] 

337 

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 

347 

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

349 hashRec = REG_HASH(data[:8]) 

350 self.__walkSubNodes(hashRec) 

351 data = data[8:] 

352 

353 if isinstance(nk, REG_NK): 

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

355 

356 def walk(self, parentKey): 

357 key = self.findKey(parentKey) 

358 

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

360 return 

361 

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:] 

368 

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:] 

374 

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 

384 

385 return parentKey 

386 

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) 

414 

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'] 

422 

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 

432 

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 

439 

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) 

446 

447 for value in valueList: 

448 if value['Flag'] > 0: 

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

450 else: 

451 resp.append(b'default') 

452 

453 return resp 

454 

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) 

459 

460 key = self.findKey(regKey) 

461 

462 if key is None: 

463 return None 

464 

465 if key['NumValues'] > 0: 

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

467 

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) 

473 

474 return None 

475 

476 def getClass(self, className): 

477 

478 key = self.findKey(className) 

479 

480 if key is None: 

481 return None 

482 

483 #print key.dump() 

484 if key['OffsetClassName'] > 0: 

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

486 return value['Data']