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# 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# [C706] Transfer NDR Syntax implementation 

8# 

9# Author: Alberto Solino (@agsolino) 

10# 

11# ToDo: 

12# [X] Unions and rest of the structured types 

13# [ ] Documentation for this library, especially the support for Arrays 

14# 

15from __future__ import division 

16from __future__ import print_function 

17import random 

18import inspect 

19from struct import pack, unpack_from, calcsize 

20from six import with_metaclass, PY3 

21 

22from impacket import LOG 

23from impacket.dcerpc.v5.enum import Enum 

24from impacket.uuid import uuidtup_to_bin 

25 

26# Something important to have in mind: 

27# Diagrams do not depict the specified alignment gaps, which can appear in the octet stream 

28# before an item (see Section 14.2.2 on page 620.) 

29# Where necessary, an alignment gap, consisting of octets of unspecified value, *precedes* the 

30# representation of a primitive. The gap is of the smallest size sufficient to align the primitive 

31 

32class NDR(object): 

33 """ 

34 This will be the base class for all DCERPC NDR Types and represents a NDR Primitive Type 

35 """ 

36 referent = () 

37 commonHdr = () 

38 commonHdr64 = () 

39 structure = () 

40 structure64 = () 

41 align = 4 

42 item = None 

43 _isNDR64 = False 

44 

45 def __init__(self, data = None, isNDR64 = False): 

46 object.__init__(self) 

47 self._isNDR64 = isNDR64 

48 self.fields = {} 

49 

50 if isNDR64 is True: 

51 if self.commonHdr64 != (): 

52 self.commonHdr = self.commonHdr64 

53 if self.structure64 != (): 

54 self.structure = self.structure64 

55 if hasattr(self, 'align64'): 

56 self.align = self.align64 

57 

58 for fieldName, fieldTypeOrClass in self.commonHdr+self.structure+self.referent: 

59 if self.isNDR(fieldTypeOrClass): 

60 self.fields[fieldName] = fieldTypeOrClass(isNDR64 = self._isNDR64) 

61 elif fieldTypeOrClass == ':': 

62 self.fields[fieldName] = b'' 

63 elif len(fieldTypeOrClass.split('=')) == 2: 

64 try: 

65 self.fields[fieldName] = eval(fieldTypeOrClass.split('=')[1]) 

66 except: 

67 self.fields[fieldName] = None 

68 else: 

69 self.fields[fieldName] = [] 

70 

71 if data is not None: 

72 self.fromString(data) 

73 

74 def changeTransferSyntax(self, newSyntax): 

75 NDR64Syntax = uuidtup_to_bin(('71710533-BEBA-4937-8319-B5DBEF9CCC36', '1.0')) 

76 if newSyntax == NDR64Syntax: 76 ↛ 104line 76 didn't jump to line 104, because the condition on line 76 was never false

77 if self._isNDR64 is False: 

78 # Ok, let's change everything 

79 self._isNDR64 = True 

80 for fieldName in list(self.fields.keys()): 

81 if isinstance(self.fields[fieldName], NDR): 

82 self.fields[fieldName].changeTransferSyntax(newSyntax) 

83 # Finally, I change myself 

84 if self.commonHdr64 != (): 

85 self.commonHdr = self.commonHdr64 

86 if self.structure64 != (): 

87 self.structure = self.structure64 

88 if hasattr(self, 'align64'): 

89 self.align = self.align64 

90 # And check whether the changes changed the data types 

91 # if so, I need to instantiate the new ones and copy the 

92 # old values 

93 for fieldName, fieldTypeOrClass in self.commonHdr+self.structure+self.referent: 

94 if isinstance(self.fields[fieldName], NDR): 

95 if fieldTypeOrClass != self.fields[fieldName].__class__ and isinstance(self.fields[fieldName], NDRPOINTERNULL) is False: 

96 backupData = self[fieldName] 

97 self.fields[fieldName] = fieldTypeOrClass(isNDR64 = self._isNDR64) 

98 if 'Data' in self.fields[fieldName].fields: 98 ↛ 101line 98 didn't jump to line 101, because the condition on line 98 was never false

99 self.fields[fieldName].fields['Data'] = backupData 

100 else: 

101 self[fieldName] = backupData 

102 

103 else: 

104 if self._isNDR64 is True: 

105 # Ok, nothing for now 

106 raise Exception('Shouldn\'t be here') 

107 

108 def __setitem__(self, key, value): 

109 if isinstance(value, NDRPOINTERNULL): 

110 value = NDRPOINTERNULL(isNDR64 = self._isNDR64) 

111 if isinstance(self.fields[key], NDRPOINTER): 

112 self.fields[key] = value 

113 elif 'Data' in self.fields[key].fields: 113 ↛ exitline 113 didn't return from function '__setitem__', because the condition on line 113 was never false

114 if isinstance(self.fields[key].fields['Data'], NDRPOINTER): 

115 self.fields[key].fields['Data'] = value 

116 elif isinstance(value, NDR): 

117 # It's not a null pointer, ok. Another NDR type, but it  

118 # must be the same same as the iteam already in place 

119 if self.fields[key].__class__.__name__ == value.__class__.__name__: 

120 self.fields[key] = value 

121 elif isinstance(self.fields[key]['Data'], NDR): 

122 if self.fields[key]['Data'].__class__.__name__ == value.__class__.__name__: 122 ↛ 125line 122 didn't jump to line 125, because the condition on line 122 was never false

123 self.fields[key]['Data'] = value 

124 else: 

125 LOG.error("Can't setitem with class specified, should be %s" % self.fields[key]['Data'].__class__.__name__) 

126 else: 

127 LOG.error("Can't setitem with class specified, should be %s" % self.fields[key].__class__.__name__) 

128 elif isinstance(self.fields[key], NDR): 

129 self.fields[key]['Data'] = value 

130 else: 

131 self.fields[key] = value 

132 

133 def __getitem__(self, key): 

134 if isinstance(self.fields[key], NDR): 

135 if 'Data' in self.fields[key].fields: 

136 return self.fields[key]['Data'] 

137 return self.fields[key] 

138 

139 def __str__(self): 

140 return self.getData() 

141 

142 def __len__(self): 

143 # XXX: improve 

144 return len(self.getData()) 

145 

146 def getDataLen(self, data, offset=0): 

147 return len(data) - offset 

148 

149 @staticmethod 

150 def isNDR(field): 

151 if inspect.isclass(field): 

152 myClass = field 

153 if issubclass(myClass, NDR): 153 ↛ 155line 153 didn't jump to line 155, because the condition on line 153 was never false

154 return True 

155 return False 

156 

157 def dumpRaw(self, msg = None, indent = 0): 

158 if msg is None: 

159 msg = self.__class__.__name__ 

160 ind = ' '*indent 

161 print("\n%s" % msg) 

162 for field in self.commonHdr+self.structure+self.referent: 

163 i = field[0] 

164 if i in self.fields: 164 ↛ 162line 164 didn't jump to line 162, because the condition on line 164 was never false

165 if isinstance(self.fields[i], NDR): 

166 self.fields[i].dumpRaw('%s%s:{' % (ind,i), indent = indent + 4) 

167 print("%s}" % ind) 

168 

169 elif isinstance(self.fields[i], list): 

170 print("%s[" % ind) 

171 for num,j in enumerate(self.fields[i]): 

172 if isinstance(j, NDR): 

173 j.dumpRaw('%s%s:' % (ind,i), indent = indent + 4) 

174 print("%s," % ind) 

175 else: 

176 print("%s%s: {%r}," % (ind, i, j)) 

177 print("%s]" % ind) 

178 

179 else: 

180 print("%s%s: {%r}" % (ind,i,self[i])) 

181 

182 def dump(self, msg = None, indent = 0): 

183 if msg is None: 

184 msg = self.__class__.__name__ 

185 ind = ' '*indent 

186 if msg != '': 

187 print("%s" % msg, end=' ') 

188 for fieldName, fieldType in self.commonHdr+self.structure+self.referent: 

189 if fieldName in self.fields: 189 ↛ 188line 189 didn't jump to line 188, because the condition on line 189 was never false

190 if isinstance(self.fields[fieldName], NDR): 

191 self.fields[fieldName].dump('\n%s%-31s' % (ind, fieldName+':'), indent = indent + 4), 

192 else: 

193 print(" %r" % (self[fieldName]), end=' ') 

194 

195 def getAlignment(self): 

196 return self.align 

197 

198 @staticmethod 

199 def calculatePad(fieldType, soFar): 

200 if isinstance(fieldType, str): 

201 try: 

202 alignment = calcsize(fieldType.split('=')[0]) 

203 except: 

204 alignment = 0 

205 else: 

206 alignment = 0 

207 

208 if alignment > 0: 

209 pad = (alignment - (soFar % alignment)) % alignment 

210 else: 

211 pad = 0 

212 

213 return pad 

214 

215 def getData(self, soFar = 0): 

216 data = b'' 

217 for fieldName, fieldTypeOrClass in self.commonHdr+self.structure: 

218 try: 

219 # Alignment of Primitive Types 

220 

221 # NDR enforces NDR alignment of primitive data; that is, any primitive of size n 

222 # octets is aligned at a octet stream index that is a multiple of n. 

223 # (In this version of NDR, n is one of {1, 2, 4, 8}.) An octet stream index indicates 

224 # the number of an octet in an octet stream when octets are numbered, beginning with 0, 

225 # from the first octet in the stream. Where necessary, an alignment gap, consisting of 

226 # octets of unspecified value, precedes the representation of a primitive. The gap is 

227 # of the smallest size sufficient to align the primitive. 

228 pad = self.calculatePad(fieldTypeOrClass, soFar) 

229 if pad > 0: 

230 soFar += pad 

231 data += b'\xbf'*pad 

232 

233 res = self.pack(fieldName, fieldTypeOrClass, soFar) 

234 

235 data += res 

236 soFar += len(res) 

237 except Exception as e: 

238 LOG.error(str(e)) 

239 LOG.error("Error packing field '%s | %s' in %s" % (fieldName, fieldTypeOrClass, self.__class__)) 

240 raise 

241 

242 return data 

243 

244 def fromString(self, data, offset=0): 

245 offset0 = offset 

246 for fieldName, fieldTypeOrClass in self.commonHdr+self.structure: 

247 try: 

248 # Alignment of Primitive Types 

249 

250 # NDR enforces NDR alignment of primitive data; that is, any primitive of size n 

251 # octets is aligned at a octet stream index that is a multiple of n. 

252 # (In this version of NDR, n is one of {1, 2, 4, 8}.) An octet stream index indicates 

253 # the number of an octet in an octet stream when octets are numbered, beginning with 0, 

254 # from the first octet in the stream. Where necessary, an alignment gap, consisting of 

255 # octets of unspecified value, precedes the representation of a primitive. The gap is 

256 # of the smallest size sufficient to align the primitive. 

257 offset += self.calculatePad(fieldTypeOrClass, offset) 

258 

259 offset += self.unpack(fieldName, fieldTypeOrClass, data, offset) 

260 except Exception as e: 

261 LOG.error(str(e)) 

262 LOG.error("Error unpacking field '%s | %s | %r'" % (fieldName, fieldTypeOrClass, data[offset:offset+256])) 

263 raise 

264 return offset - offset0 

265 

266 def pack(self, fieldName, fieldTypeOrClass, soFar = 0): 

267 if isinstance(self.fields[fieldName], NDR): 

268 return self.fields[fieldName].getData(soFar) 

269 

270 data = self.fields[fieldName] 

271 # void specifier 

272 if fieldTypeOrClass[:1] == '_': 272 ↛ 273line 272 didn't jump to line 273, because the condition on line 272 was never true

273 return b'' 

274 

275 # code specifier 

276 two = fieldTypeOrClass.split('=') 

277 if len(two) >= 2: 

278 try: 

279 return self.pack(fieldName, two[0], soFar) 

280 except: 

281 self.fields[fieldName] = eval(two[1], {}, self.fields) 

282 return self.pack(fieldName, two[0], soFar) 

283 

284 if data is None: 

285 raise Exception('Trying to pack None') 

286 

287 # literal specifier 

288 if fieldTypeOrClass[:1] == ':': 

289 if hasattr(data, 'getData'): 

290 return data.getData() 

291 return data 

292 

293 # struct like specifier 

294 return pack(fieldTypeOrClass, data) 

295 

296 def unpack(self, fieldName, fieldTypeOrClass, data, offset=0): 

297 if isinstance(self.fields[fieldName], NDR): 

298 return self.fields[fieldName].fromString(data, offset) 

299 

300 # code specifier 

301 two = fieldTypeOrClass.split('=') 

302 if len(two) >= 2: 

303 return self.unpack(fieldName, two[0], data, offset) 

304 

305 # literal specifier 

306 if fieldTypeOrClass == ':': 

307 if isinstance(fieldTypeOrClass, NDR): 307 ↛ 308line 307 didn't jump to line 308, because the condition on line 307 was never true

308 return self.fields[fieldName].fromString(data, offset) 

309 else: 

310 dataLen = self.getDataLen(data, offset) 

311 self.fields[fieldName] = data[offset:offset+dataLen] 

312 return dataLen 

313 

314 # struct like specifier 

315 self.fields[fieldName] = unpack_from(fieldTypeOrClass, data, offset)[0] 

316 

317 return calcsize(fieldTypeOrClass) 

318 

319 def calcPackSize(self, fieldTypeOrClass, data): 

320 if isinstance(fieldTypeOrClass, str) is False: 320 ↛ 321line 320 didn't jump to line 321, because the condition on line 320 was never true

321 return len(data) 

322 

323 # code specifier 

324 two = fieldTypeOrClass.split('=') 

325 if len(two) >= 2: 

326 return self.calcPackSize(two[0], data) 

327 

328 # literal specifier 

329 if fieldTypeOrClass[:1] == ':': 

330 return len(data) 

331 

332 # struct like specifier 

333 return calcsize(fieldTypeOrClass) 

334 

335 def calcUnPackSize(self, fieldTypeOrClass, data, offset=0): 

336 if isinstance(fieldTypeOrClass, str) is False: 

337 return len(data) - offset 

338 

339 # code specifier 

340 two = fieldTypeOrClass.split('=') 

341 if len(two) >= 2: 

342 return self.calcUnPackSize(two[0], data, offset) 

343 

344 # array specifier 

345 two = fieldTypeOrClass.split('*') 

346 if len(two) == 2: 

347 return len(data) - offset 

348 

349 # literal specifier 

350 if fieldTypeOrClass[:1] == ':': 

351 return len(data) - offset 

352 

353 # struct like specifier 

354 return calcsize(fieldTypeOrClass) 

355 

356# NDR Primitives 

357class NDRSMALL(NDR): 

358 align = 1 

359 structure = ( 

360 ('Data', 'b=0'), 

361 ) 

362 

363class NDRUSMALL(NDR): 

364 align = 1 

365 structure = ( 

366 ('Data', 'B=0'), 

367 ) 

368 

369class NDRBOOLEAN(NDRSMALL): 

370 def dump(self, msg = None, indent = 0): 

371 if msg is None: 

372 msg = self.__class__.__name__ 

373 if msg != '': 

374 print(msg, end=' ') 

375 

376 if self['Data'] > 0: 

377 print(" TRUE") 

378 else: 

379 print(" FALSE") 

380 

381class NDRCHAR(NDR): 

382 align = 1 

383 structure = ( 

384 ('Data', 'c'), 

385 ) 

386 

387class NDRSHORT(NDR): 

388 align = 2 

389 structure = ( 

390 ('Data', '<h=0'), 

391 ) 

392 

393class NDRUSHORT(NDR): 

394 align = 2 

395 structure = ( 

396 ('Data', '<H=0'), 

397 ) 

398 

399class NDRLONG(NDR): 

400 align = 4 

401 structure = ( 

402 ('Data', '<l=0'), 

403 ) 

404 

405class NDRULONG(NDR): 

406 align = 4 

407 structure = ( 

408 ('Data', '<L=0'), 

409 ) 

410 

411class NDRHYPER(NDR): 

412 align = 8 

413 structure = ( 

414 ('Data', '<q=0'), 

415 ) 

416 

417class NDRUHYPER(NDR): 

418 align = 8 

419 structure = ( 

420 ('Data', '<Q=0'), 

421 ) 

422 

423class NDRFLOAT(NDR): 

424 align = 4 

425 structure = ( 

426 ('Data', '<f=0'), 

427 ) 

428 

429class NDRDOUBLEFLOAT(NDR): 

430 align = 8 

431 structure = ( 

432 ('Data', '<d=0'), 

433 ) 

434 

435class EnumType(type): 

436 def __getattr__(self, attr): 

437 return self.enumItems[attr].value 

438 

439class NDRENUM(with_metaclass(EnumType, NDR)): 

440 align = 2 

441 align64 = 4 

442 structure = ( 

443 ('Data', '<H'), 

444 ) 

445 

446 # 2.2.5.2 NDR64 Simple Data Types 

447 # NDR64 supports all simple types defined by NDR (as specified in [C706] section 14.2) 

448 # with the same alignment requirements except for enumerated types, which MUST be  

449 # represented as signed long integers (4 octets) in NDR64. 

450 structure64 = ( 

451 ('Data', '<L'), 

452 ) 

453 # enum MUST be an python enum (see enum.py) 

454 class enumItems(Enum): 

455 pass 

456 

457 def __setitem__(self, key, value): 

458 if isinstance(value, Enum): 458 ↛ 459line 458 didn't jump to line 459, because the condition on line 458 was never true

459 self['Data'] = value.value 

460 else: 

461 return NDR.__setitem__(self,key,value) 

462 

463 def dump(self, msg = None, indent = 0): 

464 if msg is None: 464 ↛ 465line 464 didn't jump to line 465, because the condition on line 464 was never true

465 msg = self.__class__.__name__ 

466 if msg != '': 466 ↛ 469line 466 didn't jump to line 469, because the condition on line 466 was never false

467 print(msg, end=' ') 

468 

469 print(" %s" % self.enumItems(self.fields['Data']).name, end=' ') 

470 

471# NDR Constructed Types (arrays, strings, structures, unions, variant structures, pipes and pointers) 

472class NDRCONSTRUCTEDTYPE(NDR): 

473 @staticmethod 

474 def isPointer(field): 

475 if inspect.isclass(field): 475 ↛ 479line 475 didn't jump to line 479, because the condition on line 475 was never false

476 myClass = field 

477 if issubclass(myClass, NDRPOINTER): 

478 return True 

479 return False 

480 

481 @staticmethod 

482 def isUnion(field): 

483 if inspect.isclass(field): 483 ↛ 487line 483 didn't jump to line 487, because the condition on line 483 was never false

484 myClass = field 

485 if issubclass(myClass, NDRUNION): 

486 return True 

487 return False 

488 

489 def getDataReferents(self, soFar = 0): 

490 data = b'' 

491 for fieldName, fieldTypeOrClass in self.commonHdr+self.structure: 

492 if isinstance(self.fields[fieldName], NDRCONSTRUCTEDTYPE): 

493 data += self.fields[fieldName].getDataReferents(len(data)+soFar) 

494 data += self.fields[fieldName].getDataReferent(len(data)+soFar) 

495 return data 

496 

497 def getDataReferent(self, soFar=0): 

498 data = b'' 

499 soFar0 = soFar 

500 if hasattr(self,'referent') is False: 500 ↛ 501line 500 didn't jump to line 501, because the condition on line 500 was never true

501 return b'' 

502 

503 if 'ReferentID' in self.fields: 

504 if self['ReferentID'] == 0: 

505 return b'' 

506 

507 for fieldName, fieldTypeOrClass in self.referent: 

508 try: 

509 if isinstance(self.fields[fieldName], NDRUniConformantArray) or isinstance(self.fields[fieldName], NDRUniConformantVaryingArray): 

510 # So we have an array, first item in the structure must be the array size, although we 

511 # will need to build it later. 

512 if self._isNDR64: 

513 arrayItemSize = 8 

514 arrayPackStr = '<Q' 

515 else: 

516 arrayItemSize = 4 

517 arrayPackStr = '<L' 

518 

519 # The size information is itself aligned according to the alignment rules for 

520 # primitive data types. (See Section 14.2.2 on page 620.) The data of the constructed 

521 # type is then aligned according to the alignment rules for the constructed type. 

522 # In other words, the size information precedes the structure and is aligned 

523 # independently of the structure alignment. 

524 # We need to check whether we need padding or not 

525 pad0 = (arrayItemSize - (soFar % arrayItemSize)) % arrayItemSize 

526 if pad0 > 0: 

527 soFar += pad0 

528 arrayPadding = b'\xef'*pad0 

529 else: 

530 arrayPadding = b'' 

531 # And now, let's pretend we put the item in 

532 soFar += arrayItemSize 

533 data = self.fields[fieldName].getData(soFar) 

534 data = arrayPadding + pack(arrayPackStr, self.getArrayMaximumSize(fieldName)) + data 

535 else: 

536 pad = self.calculatePad(fieldTypeOrClass, soFar) 

537 if pad > 0: 537 ↛ 538line 537 didn't jump to line 538, because the condition on line 537 was never true

538 soFar += pad 

539 data += b'\xcc'*pad 

540 

541 data += self.pack(fieldName, fieldTypeOrClass, soFar) 

542 

543 # Any referent information to pack? 

544 if isinstance(self.fields[fieldName], NDRCONSTRUCTEDTYPE): 544 ↛ 547line 544 didn't jump to line 547, because the condition on line 544 was never false

545 data += self.fields[fieldName].getDataReferents(soFar0 + len(data)) 

546 data += self.fields[fieldName].getDataReferent(soFar0 + len(data)) 

547 soFar = soFar0 + len(data) 

548 

549 except Exception as e: 

550 LOG.error(str(e)) 

551 LOG.error("Error packing field '%s | %s' in %s" % (fieldName, fieldTypeOrClass, self.__class__)) 

552 raise 

553 

554 return data 

555 

556 def calcPackSize(self, fieldTypeOrClass, data): 

557 if isinstance(fieldTypeOrClass, str) is False: 557 ↛ 558line 557 didn't jump to line 558, because the condition on line 557 was never true

558 return len(data) 

559 

560 # array specifier 

561 two = fieldTypeOrClass.split('*') 

562 if len(two) == 2: 562 ↛ 563line 562 didn't jump to line 563, because the condition on line 562 was never true

563 answer = 0 

564 for each in data: 

565 if self.isNDR(self.item): 

566 item = ':' 

567 else: 

568 item = self.item 

569 answer += self.calcPackSize(item, each) 

570 return answer 

571 else: 

572 return NDR.calcPackSize(self, fieldTypeOrClass, data) 

573 

574 def getArrayMaximumSize(self, fieldName): 

575 if self.fields[fieldName].fields['MaximumCount'] is not None and self.fields[fieldName].fields['MaximumCount'] > 0: 

576 return self.fields[fieldName].fields['MaximumCount'] 

577 else: 

578 return self.fields[fieldName].getArraySize() 

579 

580 def getArraySize(self, fieldName, data, offset=0): 

581 if self._isNDR64: 

582 arrayItemSize = 8 

583 arrayUnPackStr = '<Q' 

584 else: 

585 arrayItemSize = 4 

586 arrayUnPackStr = '<L' 

587 

588 pad = (arrayItemSize - (offset % arrayItemSize)) % arrayItemSize 

589 offset += pad 

590 

591 if isinstance(self.fields[fieldName], NDRUniConformantArray): 

592 # Array Size is at the very beginning 

593 arraySize = unpack_from(arrayUnPackStr, data, offset)[0] 

594 elif isinstance(self.fields[fieldName], NDRUniConformantVaryingArray): 594 ↛ 604line 594 didn't jump to line 604, because the condition on line 594 was never false

595 # NDRUniConformantVaryingArray Array 

596 # Unpack the Maximum Count 

597 maximumCount = unpack_from(arrayUnPackStr, data, offset)[0] 

598 # Let's store the Maximum Count for later use 

599 self.fields[fieldName].fields['MaximumCount'] = maximumCount 

600 # Unpack the Actual Count 

601 arraySize = unpack_from(arrayUnPackStr, data, offset+arrayItemSize*2)[0] 

602 else: 

603 # NDRUniVaryingArray Array 

604 arraySize = unpack_from(arrayUnPackStr, data, offset+arrayItemSize)[0] 

605 

606 return arraySize, arrayItemSize+pad 

607 

608 def fromStringReferents(self, data, offset=0): 

609 offset0 = offset 

610 for fieldName, fieldTypeOrClass in self.commonHdr+self.structure: 

611 if isinstance(self.fields[fieldName], NDRCONSTRUCTEDTYPE): 

612 offset += self.fields[fieldName].fromStringReferents(data, offset) 

613 offset += self.fields[fieldName].fromStringReferent(data, offset) 

614 return offset - offset0 

615 

616 def fromStringReferent(self, data, offset=0): 

617 if hasattr(self, 'referent') is not True: 617 ↛ 618line 617 didn't jump to line 618, because the condition on line 617 was never true

618 return 0 

619 

620 offset0 = offset 

621 

622 if 'ReferentID' in self.fields: 

623 if self['ReferentID'] == 0: 

624 # NULL Pointer, there's no referent for it 

625 return 0 

626 

627 for fieldName, fieldTypeOrClass in self.referent: 

628 try: 

629 if isinstance(self.fields[fieldName], NDRUniConformantArray) or isinstance(self.fields[fieldName], NDRUniConformantVaryingArray): 

630 # Get the array size 

631 arraySize, advanceStream = self.getArraySize(fieldName, data, offset) 

632 offset += advanceStream 

633 

634 # Let's tell the array how many items are available 

635 self.fields[fieldName].setArraySize(arraySize) 

636 size = self.fields[fieldName].fromString(data, offset) 

637 else: 

638 # ToDo: Align only if not NDR 

639 offset += self.calculatePad(fieldTypeOrClass, offset) 

640 

641 size = self.unpack(fieldName, fieldTypeOrClass, data, offset) 

642 

643 if isinstance(self.fields[fieldName], NDRCONSTRUCTEDTYPE): 643 ↛ 646line 643 didn't jump to line 646, because the condition on line 643 was never false

644 size += self.fields[fieldName].fromStringReferents(data, offset+size) 

645 size += self.fields[fieldName].fromStringReferent(data, offset+size) 

646 offset += size 

647 except Exception as e: 

648 LOG.error(str(e)) 

649 LOG.error("Error unpacking field '%s | %s | %r'" % (fieldName, fieldTypeOrClass, data[offset:offset+256])) 

650 raise 

651 

652 return offset-offset0 

653 

654 def calcUnPackSize(self, fieldTypeOrClass, data, offset=0): 

655 if isinstance(fieldTypeOrClass, str) is False: 

656 return len(data) - offset 

657 

658 two = fieldTypeOrClass.split('*') 

659 if len(two) == 2: 

660 return len(data) - offset 

661 else: 

662 return NDR.calcUnPackSize(self, fieldTypeOrClass, data, offset) 

663 

664# Uni-dimensional Fixed Arrays 

665class NDRArray(NDRCONSTRUCTEDTYPE): 

666 def dump(self, msg = None, indent = 0): 

667 if msg is None: 667 ↛ 668line 667 didn't jump to line 668, because the condition on line 667 was never true

668 msg = self.__class__.__name__ 

669 ind = ' '*indent 

670 if msg != '': 

671 print(msg, end=' ') 

672 

673 if isinstance(self['Data'], list): 

674 print("\n%s[" % ind) 

675 ind += ' '*4 

676 for num,j in enumerate(self.fields['Data']): 

677 if isinstance(j, NDR): 

678 j.dump('%s' % ind, indent = indent + 4), 

679 print(",") 

680 else: 

681 print("%s %r," % (ind,j)) 

682 print("%s]" % ind[:-4], end=' ') 

683 else: 

684 print(" %r" % self['Data'], end=' ') 

685 

686 def setArraySize(self, size): 

687 self.arraySize = size 

688 

689 def getArraySize(self): 

690 return self.arraySize 

691 

692 def changeTransferSyntax(self, newSyntax): 

693 # Here we gotta go over each item in the array and change the TS  

694 # Only if the item type is NDR 

695 if hasattr(self, 'item') and self.item is not None: 

696 if self.isNDR(self.item): 

697 for item in self.fields['Data']: 

698 item.changeTransferSyntax(newSyntax) 

699 return NDRCONSTRUCTEDTYPE.changeTransferSyntax(self, newSyntax) 

700 

701 def getAlignment(self): 

702 # Array alignment is the largest alignment of the array element type and  

703 # the size information type, if any. 

704 align = 0 

705 # And now the item 

706 if hasattr(self, "item") and self.item is not None: 

707 if self.isNDR(self.item): 

708 tmpAlign = self.item().getAlignment() 

709 else: 

710 tmpAlign = self.calcPackSize(self.item, b'') 

711 if tmpAlign > align: 711 ↛ 713line 711 didn't jump to line 713, because the condition on line 711 was never false

712 align = tmpAlign 

713 return align 

714 

715 def getData(self, soFar = 0): 

716 data = b'' 

717 soFar0 = soFar 

718 for fieldName, fieldTypeOrClass in self.structure: 

719 try: 

720 if self.isNDR(fieldTypeOrClass) is False: 720 ↛ 728line 720 didn't jump to line 728, because the condition on line 720 was never false

721 # If the item is not NDR (e.g. ('MaximumCount', '<L=len(Data)')) 

722 # we have to align it 

723 pad = self.calculatePad(fieldTypeOrClass, soFar) 

724 if pad > 0: 724 ↛ 725line 724 didn't jump to line 725, because the condition on line 724 was never true

725 soFar += pad 

726 data += b'\xca'*pad 

727 

728 res = self.pack(fieldName, fieldTypeOrClass, soFar) 

729 data += res 

730 soFar = soFar0 + len(data) 

731 except Exception as e: 

732 LOG.error(str(e)) 

733 LOG.error("Error packing field '%s | %s' in %s" % (fieldName, fieldTypeOrClass, self.__class__)) 

734 raise 

735 

736 return data 

737 

738 def pack(self, fieldName, fieldTypeOrClass, soFar = 0): 

739 # array specifier 

740 two = fieldTypeOrClass.split('*') 

741 if len(two) == 2: 

742 answer = b'' 

743 if self.isNDR(self.item): 

744 item = ':' 

745 dataClass = self.item 

746 self.fields['_tmpItem'] = dataClass(isNDR64=self._isNDR64) 

747 else: 

748 item = self.item 

749 dataClass = None 

750 self.fields['_tmpItem'] = item 

751 

752 for each in (self.fields[fieldName]): 

753 pad = self.calculatePad(self.item, len(answer)+soFar) 

754 if pad > 0: 754 ↛ 755line 754 didn't jump to line 755, because the condition on line 754 was never true

755 answer += b'\xdd' * pad 

756 if dataClass is None: 

757 if item == 'c' and PY3 and isinstance(each, int): 

758 # Special case when dealing with PY3, here we have an integer we need to convert 

759 each = bytes([each]) 

760 answer += pack(item, each) 

761 else: 

762 answer += each.getData(len(answer)+soFar) 

763 

764 if dataClass is not None: 

765 for each in self.fields[fieldName]: 

766 if isinstance(each, NDRCONSTRUCTEDTYPE): 

767 answer += each.getDataReferents(len(answer)+soFar) 

768 answer += each.getDataReferent(len(answer)+soFar) 

769 

770 del(self.fields['_tmpItem']) 

771 if isinstance(self, NDRUniConformantArray) or isinstance(self, NDRUniConformantVaryingArray): 

772 # First field points to a field with the amount of items 

773 self.setArraySize(len(self.fields[fieldName])) 

774 else: 

775 self.fields[two[1]] = len(self.fields[fieldName]) 

776 

777 return answer 

778 else: 

779 return NDRCONSTRUCTEDTYPE.pack(self, fieldName, fieldTypeOrClass, soFar) 

780 

781 def fromString(self, data, offset=0): 

782 offset0 = offset 

783 for fieldName, fieldTypeOrClass in self.commonHdr+self.structure: 

784 try: 

785 if self.isNDR(fieldTypeOrClass) is False: 785 ↛ 790line 785 didn't jump to line 790, because the condition on line 785 was never false

786 # If the item is not NDR (e.g. ('MaximumCount', '<L=len(Data)')) 

787 # we have to align it 

788 offset += self.calculatePad(fieldTypeOrClass, offset) 

789 

790 size = self.unpack(fieldName, fieldTypeOrClass, data, offset) 

791 offset += size 

792 except Exception as e: 

793 LOG.error(str(e)) 

794 LOG.error("Error unpacking field '%s | %s | %r'" % (fieldName, fieldTypeOrClass, data[offset:offset+256])) 

795 raise 

796 return offset - offset0 

797 

798 def unpack(self, fieldName, fieldTypeOrClass, data, offset=0): 

799 # array specifier 

800 two = fieldTypeOrClass.split('*') 

801 answer = [] 

802 soFarItems = 0 

803 offset0 = offset 

804 if len(two) == 2: 

805 if isinstance(self, NDRUniConformantArray): 

806 # First field points to a field with the amount of items 

807 numItems = self.getArraySize() 

808 elif isinstance(self, NDRUniConformantVaryingArray): 

809 # In this case we have the MaximumCount but it could be different from the ActualCount. 

810 # Let's make the unpack figure this out. 

811 #self.fields['MaximumCount'] = self.getArraySize() 

812 numItems = self[two[1]] 

813 else: 

814 numItems = self[two[1]] 

815 

816 # The item type is determined by self.item 

817 if self.isNDR(self.item): 

818 item = ':' 

819 dataClassOrCode = self.item 

820 self.fields['_tmpItem'] = dataClassOrCode(isNDR64=self._isNDR64) 

821 else: 

822 item = self.item 

823 dataClassOrCode = None 

824 self.fields['_tmpItem'] = item 

825 

826 nsofar = 0 

827 while numItems and soFarItems < len(data) - offset: 

828 pad = self.calculatePad(self.item, soFarItems+offset) 

829 if pad > 0: 829 ↛ 830line 829 didn't jump to line 830, because the condition on line 829 was never true

830 soFarItems +=pad 

831 if dataClassOrCode is None: 

832 nsofar = soFarItems + calcsize(item) 

833 answer.append(unpack_from(item, data, offset+soFarItems)[0]) 

834 else: 

835 itemn = dataClassOrCode(isNDR64=self._isNDR64) 

836 size = itemn.fromString(data, offset+soFarItems) 

837 answer.append(itemn) 

838 nsofar += size + pad 

839 numItems -= 1 

840 soFarItems = nsofar 

841 

842 if dataClassOrCode is not None and isinstance(dataClassOrCode(), NDRCONSTRUCTEDTYPE): 

843 # We gotta go over again, asking for the referents 

844 answer2 = [] 

845 for itemn in answer: 

846 size = itemn.fromStringReferents(data, soFarItems+offset) 

847 soFarItems += size 

848 size = itemn.fromStringReferent(data, soFarItems+offset) 

849 soFarItems += size 

850 answer2.append(itemn) 

851 answer = answer2 

852 del answer2 

853 

854 del(self.fields['_tmpItem']) 

855 

856 self.fields[fieldName] = answer 

857 return soFarItems + offset - offset0 

858 else: 

859 return NDRCONSTRUCTEDTYPE.unpack(self, fieldName, fieldTypeOrClass, data, offset) 

860 

861class NDRUniFixedArray(NDRArray): 

862 structure = ( 

863 ('Data',':'), 

864 ) 

865 

866# Uni-dimensional Conformant Arrays 

867class NDRUniConformantArray(NDRArray): 

868 item = 'c' 

869 structure = ( 

870 #('MaximumCount', '<L=len(Data)'), 

871 ('Data', '*MaximumCount'), 

872 ) 

873 

874 structure64 = ( 

875 #('MaximumCount', '<Q=len(Data)'), 

876 ('Data', '*MaximumCount'), 

877 ) 

878 

879 def __init__(self, data = None, isNDR64 = False): 

880 NDRArray.__init__(self, data, isNDR64) 

881 # Let's store the hidden MaximumCount field 

882 self.fields['MaximumCount'] = 0 

883 

884 def __setitem__(self, key, value): 

885 self.fields['MaximumCount'] = None 

886 return NDRArray.__setitem__(self, key, value) 

887 

888 

889# Uni-dimensional Varying Arrays 

890class NDRUniVaryingArray(NDRArray): 

891 item = 'c' 

892 structure = ( 

893 ('Offset','<L=0'), 

894 ('ActualCount','<L=len(Data)'), 

895 ('Data','*ActualCount'), 

896 ) 

897 structure64 = ( 

898 ('Offset','<Q=0'), 

899 ('ActualCount','<Q=len(Data)'), 

900 ('Data','*ActualCount'), 

901 ) 

902 

903 def __setitem__(self, key, value): 

904 self.fields['ActualCount'] = None 

905 return NDRArray.__setitem__(self, key, value) 

906 

907# Uni-dimensional Conformant-varying Arrays 

908class NDRUniConformantVaryingArray(NDRArray): 

909 item = 'c' 

910 commonHdr = ( 

911 #('MaximumCount', '<L=len(Data)'), 

912 ('Offset','<L=0'), 

913 ('ActualCount','<L=len(Data)'), 

914 ) 

915 commonHdr64 = ( 

916 #('MaximumCount', '<Q=len(Data)'), 

917 ('Offset','<Q=0'), 

918 ('ActualCount','<Q=len(Data)'), 

919 ) 

920 

921 structure = ( 

922 ('Data','*ActualCount'), 

923 ) 

924 

925 def __init__(self, data = None, isNDR64 = False): 

926 NDRArray.__init__(self, data, isNDR64) 

927 # Let's store the hidden MaximumCount field 

928 self.fields['MaximumCount'] = 0 

929 

930 def __setitem__(self, key, value): 

931 self.fields['MaximumCount'] = None 

932 self.fields['ActualCount'] = None 

933 return NDRArray.__setitem__(self, key, value) 

934 

935 def getData(self, soFar = 0): 

936 data = b'' 

937 soFar0 = soFar 

938 for fieldName, fieldTypeOrClass in self.commonHdr+self.structure: 

939 try: 

940 pad = self.calculatePad(fieldTypeOrClass, soFar) 

941 if pad > 0: 941 ↛ 942line 941 didn't jump to line 942, because the condition on line 941 was never true

942 soFar += pad 

943 data += b'\xcb'*pad 

944 

945 res = self.pack(fieldName, fieldTypeOrClass, soFar) 

946 data += res 

947 soFar = soFar0 + len(data) 

948 except Exception as e: 

949 LOG.error(str(e)) 

950 LOG.error("Error packing field '%s | %s' in %s" % (fieldName, fieldTypeOrClass, self.__class__)) 

951 raise 

952 

953 return data 

954 

955# Multidimensional arrays not implemented for now 

956 

957# Varying Strings 

958class NDRVaryingString(NDRUniVaryingArray): 

959 def getData(self, soFar = 0): 

960 # The last element of a string is a terminator of the same size as the other elements.  

961 # If the string element size is one octet, the terminator is a NULL character.  

962 # The terminator for a string of multi-byte characters is the array element zero (0). 

963 if self["Data"][-1:] != b'\x00': 963 ↛ 968line 963 didn't jump to line 968, because the condition on line 963 was never false

964 if PY3 and isinstance(self["Data"],list) is False: 

965 self["Data"] = self["Data"] + b'\x00' 

966 else: 

967 self["Data"] = b''.join(self["Data"]) + b'\x00' 

968 return NDRUniVaryingArray.getData(self, soFar) 

969 

970 def fromString(self, data, offset = 0): 

971 ret = NDRUniVaryingArray.fromString(self, data, offset) 

972 # Let's take out the last item 

973 self["Data"] = self["Data"][:-1] 

974 return ret 

975 

976# Conformant and Varying Strings 

977class NDRConformantVaryingString(NDRUniConformantVaryingArray): 

978 pass 

979 

980# Structures 

981# Structures Containing a Conformant Array  

982# Structures Containing a Conformant and Varying Array  

983class NDRSTRUCT(NDRCONSTRUCTEDTYPE): 

984 def getData(self, soFar = 0): 

985 data = b'' 

986 arrayPadding = b'' 

987 soFar0 = soFar 

988 # 14.3.7.1 Structures Containing a Conformant Array 

989 # A structure can contain a conformant array only as its last member. 

990 # In the NDR representation of a structure that contains a conformant array,  

991 # the unsigned long integers that give maximum element counts for dimensions of the array  

992 # are moved to the beginning of the structure, and the array elements appear in place at  

993 # the end of the structure. 

994 # 14.3.7.2 Structures Containing a Conformant and Varying Array 

995 # A structure can contain a conformant and varying array only as its last member. 

996 # In the NDR representation of a structure that contains a conformant and varying array,  

997 # the maximum counts for dimensions of the array are moved to the beginning of the structure,  

998 # but the offsets and actual counts remain in place at the end of the structure,  

999 # immediately preceding the array elements 

1000 lastItem = (self.commonHdr+self.structure)[-1][0] 

1001 if isinstance(self.fields[lastItem], NDRUniConformantArray) or isinstance(self.fields[lastItem], NDRUniConformantVaryingArray): 

1002 # So we have an array, first item in the structure must be the array size, although we 

1003 # will need to build it later. 

1004 if self._isNDR64: 

1005 arrayItemSize = 8 

1006 arrayPackStr = '<Q' 

1007 else: 

1008 arrayItemSize = 4 

1009 arrayPackStr = '<L' 

1010 

1011 # The size information is itself aligned according to the alignment rules for  

1012 # primitive data types. (See Section 14.2.2 on page 620.) The data of the constructed  

1013 # type is then aligned according to the alignment rules for the constructed type.  

1014 # In other words, the size information precedes the structure and is aligned  

1015 # independently of the structure alignment. 

1016 # We need to check whether we need padding or not 

1017 pad0 = (arrayItemSize - (soFar % arrayItemSize)) % arrayItemSize 

1018 if pad0 > 0: 

1019 soFar += pad0 

1020 arrayPadding = b'\xee'*pad0 

1021 else: 

1022 arrayPadding = b'' 

1023 # And now, let's pretend we put the item in 

1024 soFar += arrayItemSize 

1025 else: 

1026 arrayItemSize = 0 

1027 

1028 # Now we need to align the structure  

1029 # The alignment of a structure in the octet stream is the largest of the alignments of the fields it 

1030 # contains. These fields may also be constructed types. The same alignment rules apply  

1031 # recursively to nested constructed types. 

1032 alignment = self.getAlignment() 

1033 

1034 if alignment > 0: 

1035 pad = (alignment - (soFar % alignment)) % alignment 

1036 if pad > 0: 

1037 soFar += pad 

1038 data += b'\xAB'*pad 

1039 

1040 for fieldName, fieldTypeOrClass in self.commonHdr+self.structure: 

1041 try: 

1042 if isinstance(self.fields[fieldName], NDRUniConformantArray) or isinstance(self.fields[fieldName], NDRUniConformantVaryingArray): 

1043 res = self.fields[fieldName].getData(soFar) 

1044 if isinstance(self, NDRPOINTER): 

1045 pointerData = data[:arrayItemSize] 

1046 data = data[arrayItemSize:] 

1047 data = pointerData + arrayPadding + pack(arrayPackStr ,self.getArrayMaximumSize(fieldName)) + data 

1048 else: 

1049 data = arrayPadding + pack(arrayPackStr, self.getArrayMaximumSize(fieldName)) + data 

1050 arrayPadding = b'' 

1051 arrayItemSize = 0 

1052 else: 

1053 res = self.pack(fieldName, fieldTypeOrClass, soFar) 

1054 data += res 

1055 soFar = soFar0 + len(data) + len(arrayPadding) + arrayItemSize 

1056 except Exception as e: 

1057 LOG.error(str(e)) 

1058 LOG.error("Error packing field '%s | %s' in %s" % (fieldName, fieldTypeOrClass, self.__class__)) 

1059 raise 

1060 

1061 # 2.2.5.3.4.1 Structure with Trailing Gap 

1062 # NDR64 represents a structure as an ordered sequence of representations of the 

1063 # structure members. The trailing gap from the last nonconformant and nonvarying 

1064 # field to the alignment of the structure MUST be represented as a trailing pad. 

1065 # The size of the structure MUST be a multiple of its alignment. 

1066 # See the following figure. 

1067 

1068 # 4.8 Example of Structure with Trailing Gap in NDR64 

1069 # This example shows a structure with a trailing gap in NDR64. 

1070 # typedef struct _StructWithPad 

1071 # { 

1072 # long l; 

1073 # short s; 

1074 # } StructWithPad; 

1075 # The size of the structure in the octet stream MUST contain a 2-byte trailing 

1076 # gap to make its size 8, a multiple of the structure's alignment, 4. 

1077# if self._isNDR64 is True: 

1078# # ToDo add trailing gap here 

1079# if alignment > 0: 

1080# pad = (alignment - (soFar % alignment)) % alignment 

1081# if pad > 0: 

1082# soFar += pad 

1083# data += '\xcd'*pad 

1084# print self.__class__ , alignment, pad, hex(soFar) 

1085 return data 

1086 

1087 def fromString(self, data, offset = 0 ): 

1088 offset0 = offset 

1089 # 14.3.7.1 Structures Containing a Conformant Array 

1090 # A structure can contain a conformant array only as its last member. 

1091 # In the NDR representation of a structure that contains a conformant array,  

1092 # the unsigned long integers that give maximum element counts for dimensions of the array  

1093 # are moved to the beginning of the structure, and the array elements appear in place at  

1094 # the end of the structure. 

1095 # 14.3.7.2 Structures Containing a Conformant and Varying Array 

1096 # A structure can contain a conformant and varying array only as its last member. 

1097 # In the NDR representation of a structure that contains a conformant and varying array,  

1098 # the maximum counts for dimensions of the array are moved to the beginning of the structure,  

1099 # but the offsets and actual counts remain in place at the end of the structure,  

1100 # immediately preceding the array elements 

1101 lastItem = (self.commonHdr+self.structure)[-1][0] 

1102 

1103 # If it's a pointer, let's parse it here because 

1104 # we are going to parse the next MaximumCount field(s) manually 

1105 # when it's a Conformant or Conformant and Varying array 

1106 if isinstance(self, NDRPOINTER): 

1107 structureFields = self.structure 

1108 

1109 alignment = self.getAlignment() 

1110 if alignment > 0: 1110 ↛ 1113line 1110 didn't jump to line 1113, because the condition on line 1110 was never false

1111 offset += (alignment - (offset % alignment)) % alignment 

1112 

1113 for fieldName, fieldTypeOrClass in self.commonHdr: 

1114 offset += self.unpack(fieldName, fieldTypeOrClass, data, offset) 

1115 else: 

1116 structureFields = self.commonHdr+self.structure 

1117 

1118 if isinstance(self.fields[lastItem], NDRUniConformantArray) or isinstance(self.fields[lastItem], NDRUniConformantVaryingArray): 

1119 # So we have an array, first item in the structure must be the array size, although we 

1120 # will need to build it later. 

1121 if self._isNDR64: 

1122 arrayItemSize = 8 

1123 arrayUnPackStr = '<Q' 

1124 else: 

1125 arrayItemSize = 4 

1126 arrayUnPackStr = '<L' 

1127 

1128 # The size information is itself aligned according to the alignment rules for 

1129 # primitive data types. (See Section 14.2.2 on page 620.) The data of the constructed  

1130 # type is then aligned according to the alignment rules for the constructed type.  

1131 # In other words, the size information precedes the structure and is aligned  

1132 # independently of the structure alignment. 

1133 # We need to check whether we need padding or not 

1134 offset += (arrayItemSize - (offset % arrayItemSize)) % arrayItemSize 

1135 

1136 # And let's extract the array size for later use 

1137 if isinstance(self.fields[lastItem], NDRUniConformantArray): 

1138 # NDRUniConformantArray 

1139 arraySize = unpack_from(arrayUnPackStr, data, offset)[0] 

1140 self.fields[lastItem].setArraySize(arraySize) 

1141 else: 

1142 # NDRUniConformantVaryingArray 

1143 maximumCount = unpack_from(arrayUnPackStr, data, offset)[0] 

1144 self.fields[lastItem].fields['MaximumCount'] = maximumCount 

1145 

1146 offset += arrayItemSize 

1147 

1148 # Now we need to align the structure 

1149 # The alignment of a structure in the octet stream is the largest of the alignments of the fields it 

1150 # contains. These fields may also be constructed types. The same alignment rules apply  

1151 # recursively to nested constructed types. 

1152 alignment = self.getAlignment() 

1153 if alignment > 0: 

1154 offset += (alignment - (offset % alignment)) % alignment 

1155 

1156 for fieldName, fieldTypeOrClass in structureFields: 

1157 try: 

1158 offset += self.unpack(fieldName, fieldTypeOrClass, data, offset) 

1159 except Exception as e: 

1160 LOG.error(str(e)) 

1161 LOG.error("Error unpacking field '%s | %s | %r'" % (fieldName, fieldTypeOrClass, data[offset:offset+256])) 

1162 raise 

1163 

1164 return offset - offset0 

1165 

1166 def getAlignment(self): 

1167 # Alignment of Constructed Types 

1168 # 

1169 # NDR enforces NDR alignment of structured data. As with primitive data types, an alignment, n, is determined 

1170 # for the structure. Where necessary, an alignment gap of octets of unspecified value precedes the data in 

1171 # the NDR octet stream. This gap is the smallest size sufficient to align the first field of the structure 

1172 # on an NDR octet stream index of n. 

1173 

1174 # The rules for calculating the alignment of constructed types are as follows: 

1175 

1176 # 1) If a conformant structure-that is, a conformant or conformant varying array, or a structure containing 

1177 # a conformant or conformant varying array-is embedded in the constructed type, and is the outermost 

1178 # structure-that is, is not contained in another structure-then the size information from the contained 

1179 # conformant structure is positioned so that it precedes both the containing constructed type and any 

1180 # alignment gap for the constructed type. (See Section 14.3.7 for information about structures containing 

1181 # arrays.) The size information is itself aligned according to the alignment rules for primitive data 

1182 # types. (See Section 14.2.2 on page 620.) The data of the constructed type is then aligned according to 

1183 # the alignment rules for the constructed type. In other words, the size information precedes the structure 

1184 # and is aligned independently of the structure alignment. 

1185 

1186 # 2) The alignment of a structure in the octet stream is the largest of the alignments of the fields it 

1187 # contains. These fields may also be constructed types. The same alignment rules apply recursively to nested 

1188 # constructed types. 

1189 

1190 align = 0 

1191 for fieldName, fieldTypeOrClass in self.commonHdr+self.structure+self.referent: 

1192 if isinstance(self.fields[fieldName], NDR): 

1193 tmpAlign = self.fields[fieldName].getAlignment() 

1194 else: 

1195 tmpAlign = self.calcPackSize(fieldTypeOrClass, b'') 

1196 if tmpAlign > align: 

1197 align = tmpAlign 

1198 return align 

1199 

1200# Unions  

1201class NDRUNION(NDRCONSTRUCTEDTYPE): 

1202 commonHdr = ( 

1203 ('tag', NDRUSHORT), 

1204 ) 

1205 commonHdr64 = ( 

1206 ('tag', NDRULONG), 

1207 ) 

1208 

1209 union = { 

1210 # For example 

1211 #1: ('pStatusChangeParam1', PSERVICE_NOTIFY_STATUS_CHANGE_PARAMS_1), 

1212 #2: ('pStatusChangeParams', PSERVICE_NOTIFY_STATUS_CHANGE_PARAMS_2), 

1213 } 

1214 def __init__(self, data = None, isNDR64=False, topLevel = False): 

1215 #ret = NDR.__init__(self,None, isNDR64=isNDR64) 

1216 self.topLevel = topLevel 

1217 self._isNDR64 = isNDR64 

1218 self.fields = {} 

1219 

1220 if isNDR64 is True: 

1221 if self.commonHdr64 != (): 1221 ↛ 1223line 1221 didn't jump to line 1223, because the condition on line 1221 was never false

1222 self.commonHdr = self.commonHdr64 

1223 if self.structure64 != (): 1223 ↛ 1224line 1223 didn't jump to line 1224, because the condition on line 1223 was never true

1224 self.structure = self.structure64 

1225 if hasattr(self, 'align64'): 1225 ↛ 1226line 1225 didn't jump to line 1226, because the condition on line 1225 was never true

1226 self.align = self.align64 

1227 

1228 for fieldName, fieldTypeOrClass in self.commonHdr+self.structure+self.referent: 

1229 if self.isNDR(fieldTypeOrClass): 1229 ↛ 1236line 1229 didn't jump to line 1236, because the condition on line 1229 was never false

1230 if self.isPointer(fieldTypeOrClass): 

1231 self.fields[fieldName] = fieldTypeOrClass(isNDR64 = self._isNDR64, topLevel = topLevel) 

1232 elif self.isUnion(fieldTypeOrClass): 1232 ↛ 1233line 1232 didn't jump to line 1233, because the condition on line 1232 was never true

1233 self.fields[fieldName] = fieldTypeOrClass(isNDR64 = self._isNDR64, topLevel = topLevel) 

1234 else: 

1235 self.fields[fieldName] = fieldTypeOrClass(isNDR64 = self._isNDR64) 

1236 elif fieldTypeOrClass == ':': 

1237 self.fields[fieldName] = None 

1238 elif len(fieldTypeOrClass.split('=')) == 2: 

1239 try: 

1240 self.fields[fieldName] = eval(fieldTypeOrClass.split('=')[1]) 

1241 except: 

1242 self.fields[fieldName] = None 

1243 else: 

1244 self.fields[fieldName] = 0 

1245 

1246 if data is not None: 1246 ↛ 1247line 1246 didn't jump to line 1247, because the condition on line 1246 was never true

1247 self.fromString(data) 

1248 

1249 def __setitem__(self, key, value): 

1250 if key == 'tag': 

1251 # We're writing the tag, we now should set the right item for the structure 

1252 self.structure = () 

1253 if value in self.union: 1253 ↛ 1260line 1253 didn't jump to line 1260, because the condition on line 1253 was never false

1254 self.structure = (self.union[value]), 

1255 # Init again the structure 

1256 self.__init__(None, isNDR64=self._isNDR64, topLevel = self.topLevel) 

1257 self.fields['tag']['Data'] = value 

1258 else: 

1259 # Let's see if we have a default value 

1260 if 'default' in self.union: 

1261 if self.union['default'] is None: 

1262 self.structure = () 

1263 else: 

1264 self.structure = (self.union['default']), 

1265 # Init again the structure 

1266 self.__init__(None, isNDR64=self._isNDR64, topLevel = self.topLevel) 

1267 self.fields['tag']['Data'] = 0xffff 

1268 else: 

1269 raise Exception("Unknown tag %d for union!" % value) 

1270 else: 

1271 return NDRCONSTRUCTEDTYPE.__setitem__(self,key,value) 

1272 

1273 def getData(self, soFar = 0): 

1274 data = b'' 

1275 soFar0 = soFar 

1276 

1277 # Let's align ourselves 

1278 alignment = self.getAlignment() 

1279 if alignment > 0: 1279 ↛ 1282line 1279 didn't jump to line 1282, because the condition on line 1279 was never false

1280 pad = (alignment - (soFar % alignment)) % alignment 

1281 else: 

1282 pad = 0 

1283 if pad > 0: 

1284 soFar += pad 

1285 data += b'\xbc'*pad 

1286 

1287 for fieldName, fieldTypeOrClass in self.commonHdr: 

1288 try: 

1289 pad = self.calculatePad(fieldTypeOrClass, soFar) 

1290 if pad > 0: 1290 ↛ 1291line 1290 didn't jump to line 1291, because the condition on line 1290 was never true

1291 soFar += pad 

1292 data += b'\xbb'*pad 

1293 

1294 res = self.pack(fieldName, fieldTypeOrClass, soFar) 

1295 data += res 

1296 soFar = soFar0 + len(data) 

1297 except Exception as e: 

1298 LOG.error(str(e)) 

1299 LOG.error("Error packing field '%s | %s' in %s" % (fieldName, fieldTypeOrClass, self.__class__)) 

1300 raise 

1301 

1302 # WARNING 

1303 # Now we need to align what's coming next. 

1304 # This doesn't come from the documentation but from seeing the packets in the wire 

1305 # for some reason, even if the next field is a SHORT, it should be aligned to 

1306 # a DWORD, or HYPER if NDR64.  

1307 if self._isNDR64: 

1308 align = 8 

1309 else: 

1310 if hasattr(self, 'notAlign'): 1310 ↛ 1311line 1310 didn't jump to line 1311, because the condition on line 1310 was never true

1311 align = 1 

1312 else: 

1313 align = 4 

1314 

1315 pad = (align - (soFar % align)) % align 

1316 if pad > 0: 

1317 data += b'\xbd'*pad 

1318 soFar += pad 

1319 

1320 if self.structure == (): 1320 ↛ 1321line 1320 didn't jump to line 1321, because the condition on line 1320 was never true

1321 return data 

1322 

1323 for fieldName, fieldTypeOrClass in self.structure: 

1324 try: 

1325 pad = self.calculatePad(fieldTypeOrClass, soFar) 

1326 if pad > 0: 1326 ↛ 1327line 1326 didn't jump to line 1327, because the condition on line 1326 was never true

1327 soFar += pad 

1328 data += b'\xbe'*pad 

1329 

1330 res = self.pack(fieldName, fieldTypeOrClass, soFar) 

1331 data += res 

1332 soFar = soFar0 + len(data) 

1333 except Exception as e: 

1334 LOG.error(str(e)) 

1335 LOG.error("Error packing field '%s | %s' in %s" % (fieldName, fieldTypeOrClass, self.__class__)) 

1336 raise 

1337 

1338 return data 

1339 

1340 def fromString(self, data, offset=0): 

1341 offset0 = offset 

1342 # Let's align ourselves 

1343 alignment = self.getAlignment() 

1344 if alignment > 0: 1344 ↛ 1347line 1344 didn't jump to line 1347, because the condition on line 1344 was never false

1345 pad = (alignment - (offset % alignment)) % alignment 

1346 else: 

1347 pad = 0 

1348 if pad > 0: 

1349 offset += pad 

1350 

1351 if len(data)-offset > 4: 1351 ↛ 1372line 1351 didn't jump to line 1372, because the condition on line 1351 was never false

1352 # First off, let's see what the tag is: 

1353 # We need to know the tag type and unpack it 

1354 tagtype = self.commonHdr[0][1].structure[0][1].split('=')[0] 

1355 tag = unpack_from(tagtype, data, offset)[0] 

1356 if tag in self.union: 

1357 self.structure = (self.union[tag]), 

1358 self.__init__(None, isNDR64=self._isNDR64, topLevel = self.topLevel) 

1359 else: 

1360 # Let's see if we have a default value 

1361 if 'default' in self.union: 1361 ↛ 1362line 1361 didn't jump to line 1362, because the condition on line 1361 was never true

1362 if self.union['default'] is None: 

1363 self.structure = () 

1364 else: 

1365 self.structure = (self.union['default']), 

1366 # Init again the structure 

1367 self.__init__(None, isNDR64=self._isNDR64, topLevel = self.topLevel) 

1368 self.fields['tag']['Data'] = 0xffff 

1369 else: 

1370 raise Exception("Unknown tag %d for union!" % tag) 

1371 

1372 for fieldName, fieldTypeOrClass in self.commonHdr: 

1373 try: 

1374 offset += self.calculatePad(fieldTypeOrClass, offset) 

1375 offset += self.unpack(fieldName, fieldTypeOrClass, data, offset) 

1376 except Exception as e: 

1377 LOG.error(str(e)) 

1378 LOG.error("Error unpacking field '%s | %s | %r'" % (fieldName, fieldTypeOrClass, data[offset:offset+256])) 

1379 raise 

1380 

1381 # WARNING 

1382 # Now we need to align what's coming next. 

1383 # This doesn't come from the documentation but from seeing the packets in the wire 

1384 # for some reason, even if the next field is a SHORT, it should be aligned to 

1385 # a DWORD, or HYPER if NDR64.  

1386 if self._isNDR64: 

1387 align = 8 

1388 else: 

1389 if hasattr(self, 'notAlign'): 1389 ↛ 1390line 1389 didn't jump to line 1390, because the condition on line 1389 was never true

1390 align = 1 

1391 else: 

1392 align = 4 

1393 

1394 offset += (align - (offset % align)) % align 

1395 

1396 if self.structure == (): 1396 ↛ 1397line 1396 didn't jump to line 1397, because the condition on line 1396 was never true

1397 return offset-offset0 

1398 

1399 for fieldName, fieldTypeOrClass in self.structure: 

1400 try: 

1401 offset += self.calculatePad(fieldTypeOrClass, offset) 

1402 offset += self.unpack(fieldName, fieldTypeOrClass, data, offset) 

1403 except Exception as e: 

1404 LOG.error(str(e)) 

1405 LOG.error("Error unpacking field '%s | %s | %r'" % (fieldName, fieldTypeOrClass, data[offset:offset+256])) 

1406 raise 

1407 

1408 return offset - offset0 

1409 

1410 def getAlignment(self): 

1411 # Union alignment is the largest alignment of the union discriminator  

1412 # and all of the union arms. 

1413 # WRONG, I'm calculating it just with the tag, if I do it with the  

1414 # arms I get bad stub data. Something wrong I'm doing or the standard 

1415 # is wrong (most probably it's me :s ) 

1416 align = 0 

1417 if self._isNDR64: 

1418 fields = self.commonHdr+self.structure 

1419 else: 

1420 fields = self.commonHdr 

1421 for fieldName, fieldTypeOrClass in fields: 

1422 if isinstance(self.fields[fieldName], NDR): 1422 ↛ 1425line 1422 didn't jump to line 1425, because the condition on line 1422 was never false

1423 tmpAlign = self.fields[fieldName].getAlignment() 

1424 else: 

1425 tmpAlign = self.calcPackSize(fieldTypeOrClass, b'') 

1426 if tmpAlign > align: 

1427 align = tmpAlign 

1428 

1429 if self._isNDR64: 

1430 for fieldName, fieldTypeOrClass in self.union.values(): 

1431 tmpAlign = fieldTypeOrClass(isNDR64 = self._isNDR64).getAlignment() 

1432 if tmpAlign > align: 

1433 align = tmpAlign 

1434 return align 

1435 

1436# Pipes not implemented for now 

1437 

1438# Pointers 

1439class NDRPOINTERNULL(NDR): 

1440 align = 4 

1441 align64 = 8 

1442 structure = ( 

1443 ('Data', '<L=0'), 

1444 ) 

1445 structure64 = ( 

1446 ('Data', '<Q=0'), 

1447 ) 

1448 

1449 def dump(self, msg = None, indent = 0): 

1450 if msg is None: 1450 ↛ 1451line 1450 didn't jump to line 1451, because the condition on line 1450 was never true

1451 msg = self.__class__.__name__ 

1452 if msg != '': 1452 ↛ 1455line 1452 didn't jump to line 1455, because the condition on line 1452 was never false

1453 print("%s" % msg, end=' ') 

1454 # Here we just print NULL 

1455 print(" NULL", end=' ') 

1456 

1457NULL = NDRPOINTERNULL() 

1458 

1459class NDRPOINTER(NDRSTRUCT): 

1460 align = 4 

1461 align64 = 8 

1462 commonHdr = ( 

1463 ('ReferentID','<L=0xff'), 

1464 ) 

1465 commonHdr64 = ( 

1466 ('ReferentID','<Q=0xff'), 

1467 ) 

1468 

1469 referent = ( 

1470 # This is the representation of the Referent 

1471 ('Data',':'), 

1472 ) 

1473 def __init__(self, data = None, isNDR64=False, topLevel = False): 

1474 NDRSTRUCT.__init__(self,None, isNDR64=isNDR64) 

1475 # If we are being called from a NDRCALL, it's a TopLevelPointer, 

1476 # if not, it's a embeeded pointer. 

1477 # It is *very* important, for every subclass of NDRPointer 

1478 # you have to declare the referent in the referent variable 

1479 # Not in the structure one! 

1480 if topLevel is True: 

1481 self.structure = self.referent 

1482 self.referent = () 

1483 

1484 if data is None: 1484 ↛ 1487line 1484 didn't jump to line 1487, because the condition on line 1484 was never false

1485 self.fields['ReferentID'] = random.randint(1,65535) 

1486 else: 

1487 self.fromString(data) 

1488 

1489 def __setitem__(self, key, value): 

1490 if (key in self.fields) is False: 

1491 # Key not found.. let's send it to the referent to handle, maybe it's there 

1492 return self.fields['Data'].__setitem__(key,value) 

1493 else: 

1494 return NDRSTRUCT.__setitem__(self,key,value) 

1495 

1496 def __getitem__(self, key): 

1497 if key in self.fields: 

1498 if isinstance(self.fields[key], NDR): 

1499 if 'Data' in self.fields[key].fields: 

1500 return self.fields[key]['Data'] 

1501 return self.fields[key] 

1502 else: 

1503 # Key not found, let's send it to the referent, maybe it's there 

1504 return self.fields['Data'].__getitem__(key) 

1505 

1506 def getData(self, soFar = 0): 

1507 # First of all we need to align ourselves 

1508 data = b'' 

1509 pad = self.calculatePad(self.commonHdr[0][1], soFar) 

1510 if pad > 0: 

1511 soFar += pad 

1512 data = b'\xaa'*pad 

1513 # If we have a ReferentID == 0, means there's no data 

1514 if self.fields['ReferentID'] == 0: 

1515 if len(self.referent) > 0: 

1516 self['Data'] = b'' 

1517 else: 

1518 if self._isNDR64 is True: 1518 ↛ 1519line 1518 didn't jump to line 1519, because the condition on line 1518 was never true

1519 return data+b'\x00'*8 

1520 else: 

1521 return data+b'\x00'*4 

1522 

1523 return data + NDRSTRUCT.getData(self, soFar) 

1524 

1525 def fromString(self, data, offset=0): 

1526 # First of all we need to align ourselves 

1527 pad = self.calculatePad(self.commonHdr[0][1], offset) 

1528 offset += pad 

1529 

1530 # Do we have a Referent ID == 0? 

1531 if self._isNDR64 is True: 

1532 unpackStr = '<Q' 

1533 else: 

1534 unpackStr = '<L' 

1535 

1536 if unpack_from(unpackStr, data, offset)[0] == 0: 

1537 # Let's save the value 

1538 self['ReferentID'] = 0 

1539 self.fields['Data'] = b'' 

1540 if self._isNDR64 is True: 

1541 return pad + 8 

1542 else: 

1543 return pad + 4 

1544 else: 

1545 retVal = NDRSTRUCT.fromString(self, data, offset) 

1546 return retVal + pad 

1547 

1548 def dump(self, msg = None, indent = 0): 

1549 if msg is None: 1549 ↛ 1550line 1549 didn't jump to line 1550, because the condition on line 1549 was never true

1550 msg = self.__class__.__name__ 

1551 if msg != '': 

1552 print("%s" % msg, end=' ') 

1553 # Here we just print the referent 

1554 if isinstance(self.fields['Data'], NDR): 

1555 self.fields['Data'].dump('', indent = indent) 

1556 else: 

1557 if self['ReferentID'] == 0: 1557 ↛ 1560line 1557 didn't jump to line 1560, because the condition on line 1557 was never false

1558 print(" NULL", end=' ') 

1559 else: 

1560 print(" %r" % (self['Data']), end=' ') 

1561 

1562 def getAlignment(self): 

1563 if self._isNDR64 is True: 

1564 return 8 

1565 else: 

1566 return 4 

1567 

1568 

1569# Embedded Reference Pointers not implemented for now 

1570 

1571################################################################################ 

1572# Common RPC Data Types 

1573 

1574class PNDRUniConformantVaryingArray(NDRPOINTER): 

1575 referent = ( 

1576 ('Data', NDRUniConformantVaryingArray), 

1577 ) 

1578 

1579class PNDRUniConformantArray(NDRPOINTER): 

1580 referent = ( 

1581 ('Data', NDRUniConformantArray), 

1582 ) 

1583 def __init__(self, data = None, isNDR64 = False, topLevel = False): 

1584 NDRPOINTER.__init__(self,data,isNDR64,topLevel) 

1585 

1586class NDRCALL(NDRCONSTRUCTEDTYPE): 

1587 # This represents a group of NDR instances that conforms an NDR Call. 

1588 # The only different between a regular NDR instance is a NDR call must 

1589 # represent the referents when building the final octet stream 

1590 referent = () 

1591 commonHdr = () 

1592 commonHdr64 = () 

1593 structure = () 

1594 structure64 = () 

1595 align = 4 

1596 def __init__(self, data = None, isNDR64 = False): 

1597 self._isNDR64 = isNDR64 

1598 self.fields = {} 

1599 

1600 if isNDR64 is True: 

1601 if self.commonHdr64 != (): 1601 ↛ 1602line 1601 didn't jump to line 1602, because the condition on line 1601 was never true

1602 self.commonHdr = self.commonHdr64 

1603 if self.structure64 != (): 1603 ↛ 1604line 1603 didn't jump to line 1604, because the condition on line 1603 was never true

1604 self.structure = self.structure64 

1605 if hasattr(self, 'align64'): 1605 ↛ 1606line 1605 didn't jump to line 1606, because the condition on line 1605 was never true

1606 self.align = self.align64 

1607 

1608 for fieldName, fieldTypeOrClass in self.commonHdr+self.structure+self.referent: 

1609 if self.isNDR(fieldTypeOrClass): 1609 ↛ 1616line 1609 didn't jump to line 1616, because the condition on line 1609 was never false

1610 if self.isPointer(fieldTypeOrClass): 

1611 self.fields[fieldName] = fieldTypeOrClass(isNDR64 = self._isNDR64, topLevel = True) 

1612 elif self.isUnion(fieldTypeOrClass): 

1613 self.fields[fieldName] = fieldTypeOrClass(isNDR64 = self._isNDR64, topLevel = True) 

1614 else: 

1615 self.fields[fieldName] = fieldTypeOrClass(isNDR64 = self._isNDR64) 

1616 elif fieldTypeOrClass == ':': 

1617 self.fields[fieldName] = None 

1618 elif len(fieldTypeOrClass.split('=')) == 2: 

1619 try: 

1620 self.fields[fieldName] = eval(fieldTypeOrClass.split('=')[1]) 

1621 except: 

1622 self.fields[fieldName] = None 

1623 else: 

1624 self.fields[fieldName] = 0 

1625 

1626 if data is not None: 

1627 self.fromString(data) 

1628 

1629 def dump(self, msg = None, indent = 0): 

1630 NDRCONSTRUCTEDTYPE.dump(self, msg, indent) 

1631 print('\n\n') 

1632 

1633 def getData(self, soFar = 0): 

1634 data = b'' 

1635 soFar0 = soFar 

1636 for fieldName, fieldTypeOrClass in self.commonHdr+self.structure: 

1637 try: 

1638 pad = self.calculatePad(fieldTypeOrClass, soFar) 

1639 if pad > 0: 1639 ↛ 1640line 1639 didn't jump to line 1640, because the condition on line 1639 was never true

1640 soFar += pad 

1641 data += b'\xab'*pad 

1642 

1643 # Are we dealing with an array? 

1644 if isinstance(self.fields[fieldName], NDRUniConformantArray) or isinstance(self.fields[fieldName], 

1645 NDRUniConformantVaryingArray): 

1646 # Align size item 

1647 if self._isNDR64: 

1648 pad = (8 - (soFar % 8)) % 8 

1649 else: 

1650 pad = (4 - (soFar % 4)) % 4 

1651 # Pack the item 

1652 res = self.pack(fieldName, fieldTypeOrClass, soFar+pad) 

1653 # Yes, get the array size 

1654 arraySize = self.getArrayMaximumSize(fieldName) 

1655 if self._isNDR64: 

1656 pad = (8 - (soFar % 8)) % 8 

1657 data += b'\xce'*pad + pack('<Q', arraySize) + res 

1658 else: 

1659 pad = (4 - (soFar % 4)) % 4 

1660 data += b'\xce'*pad + pack('<L', arraySize) + res 

1661 else: 

1662 data += self.pack(fieldName, fieldTypeOrClass, soFar) 

1663 

1664 soFar = soFar0 + len(data) 

1665 # Any referent information to pack? 

1666 # I'm still not sure whether this should go after processing 

1667 # all the fields at the call level. 

1668 # Guess we'll figure it out testing. 

1669 if isinstance(self.fields[fieldName], NDRCONSTRUCTEDTYPE): 

1670 data += self.fields[fieldName].getDataReferents(soFar) 

1671 soFar = soFar0 + len(data) 

1672 data += self.fields[fieldName].getDataReferent(soFar) 

1673 soFar = soFar0 + len(data) 

1674 except Exception as e: 

1675 LOG.error(str(e)) 

1676 LOG.error("Error packing field '%s | %s' in %s" % (fieldName, fieldTypeOrClass, self.__class__)) 

1677 raise 

1678 

1679 return data 

1680 

1681 def fromString(self, data, offset=0): 

1682 offset0 = offset 

1683 for fieldName, fieldTypeOrClass in self.commonHdr+self.structure: 

1684 try: 

1685 # Are we dealing with an array? 

1686 if isinstance(self.fields[fieldName], NDRUniConformantArray) or isinstance(self.fields[fieldName], 

1687 NDRUniConformantVaryingArray): 

1688 # Yes, get the array size 

1689 arraySize, advanceStream = self.getArraySize(fieldName, data, offset) 

1690 self.fields[fieldName].setArraySize(arraySize) 

1691 offset += advanceStream 

1692 

1693 size = self.unpack(fieldName, fieldTypeOrClass, data, offset) 

1694 

1695 # Any referent information to unpack? 

1696 if isinstance(self.fields[fieldName], NDRCONSTRUCTEDTYPE): 

1697 size += self.fields[fieldName].fromStringReferents(data, offset+size) 

1698 size += self.fields[fieldName].fromStringReferent(data, offset+size) 

1699 offset += size 

1700 except Exception as e: 

1701 LOG.error(str(e)) 

1702 LOG.error("Error unpacking field '%s | %s | %r'" % (fieldName, fieldTypeOrClass, data[offset:offset+256])) 

1703 raise 

1704 

1705 return offset - offset0 

1706 

1707# Top Level Struct == NDRCALL 

1708NDRTLSTRUCT = NDRCALL 

1709 

1710class UNKNOWNDATA(NDR): 

1711 align = 1 

1712 structure = ( 

1713 ('Data', ':'), 

1714 )