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

11# 

12# Author: 

13# Alberto Solino (@agsolino) 

14# 

15# ToDo: 

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

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

18# 

19from __future__ import division 

20from __future__ import print_function 

21import random 

22import inspect 

23from struct import pack, unpack_from, calcsize 

24from six import with_metaclass, PY3 

25 

26from impacket import LOG 

27from impacket.dcerpc.v5.enum import Enum 

28from impacket.uuid import uuidtup_to_bin 

29 

30# Something important to have in mind: 

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

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

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

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

35 

36class NDR(object): 

37 """ 

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

39 """ 

40 referent = () 

41 commonHdr = () 

42 commonHdr64 = () 

43 structure = () 

44 structure64 = () 

45 align = 4 

46 item = None 

47 _isNDR64 = False 

48 

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

50 object.__init__(self) 

51 self._isNDR64 = isNDR64 

52 self.fields = {} 

53 

54 if isNDR64 is True: 

55 if self.commonHdr64 != (): 

56 self.commonHdr = self.commonHdr64 

57 if self.structure64 != (): 

58 self.structure = self.structure64 

59 if hasattr(self, 'align64'): 

60 self.align = self.align64 

61 

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

63 if self.isNDR(fieldTypeOrClass): 

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

65 elif fieldTypeOrClass == ':': 

66 self.fields[fieldName] = b'' 

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

68 try: 

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

70 except: 

71 self.fields[fieldName] = None 

72 else: 

73 self.fields[fieldName] = [] 

74 

75 if data is not None: 

76 self.fromString(data) 

77 

78 def changeTransferSyntax(self, newSyntax): 

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

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

81 if self._isNDR64 is False: 

82 # Ok, let's change everything 

83 self._isNDR64 = True 

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

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

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

87 # Finally, I change myself 

88 if self.commonHdr64 != (): 

89 self.commonHdr = self.commonHdr64 

90 if self.structure64 != (): 

91 self.structure = self.structure64 

92 if hasattr(self, 'align64'): 

93 self.align = self.align64 

94 # And check whether the changes changed the data types 

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

96 # old values 

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

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

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

100 backupData = self[fieldName] 

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

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

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

104 else: 

105 self[fieldName] = backupData 

106 

107 else: 

108 if self._isNDR64 is True: 

109 # Ok, nothing for now 

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

111 

112 def __setitem__(self, key, value): 

113 if isinstance(value, NDRPOINTERNULL): 

114 value = NDRPOINTERNULL(isNDR64 = self._isNDR64) 

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

116 self.fields[key] = value 

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

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

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

120 elif isinstance(value, NDR): 

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

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

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

124 self.fields[key] = value 

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

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

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

128 else: 

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

130 else: 

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

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

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

134 else: 

135 self.fields[key] = value 

136 

137 def __getitem__(self, key): 

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

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

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

141 return self.fields[key] 

142 

143 def __str__(self): 

144 return self.getData() 

145 

146 def __len__(self): 

147 # XXX: improve 

148 return len(self.getData()) 

149 

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

151 return len(data) - offset 

152 

153 @staticmethod 

154 def isNDR(field): 

155 if inspect.isclass(field): 

156 myClass = field 

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

158 return True 

159 return False 

160 

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

162 if msg is None: 

163 msg = self.__class__.__name__ 

164 ind = ' '*indent 

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

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

167 i = field[0] 

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

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

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

171 print("%s}" % ind) 

172 

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

174 print("%s[" % ind) 

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

176 if isinstance(j, NDR): 

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

178 print("%s," % ind) 

179 else: 

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

181 print("%s]" % ind) 

182 

183 else: 

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

185 

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

187 if msg is None: 

188 msg = self.__class__.__name__ 

189 ind = ' '*indent 

190 if msg != '': 

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

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

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

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

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

196 else: 

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

198 

199 def getAlignment(self): 

200 return self.align 

201 

202 @staticmethod 

203 def calculatePad(fieldType, soFar): 

204 if isinstance(fieldType, str): 

205 try: 

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

207 except: 

208 alignment = 0 

209 else: 

210 alignment = 0 

211 

212 if alignment > 0: 

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

214 else: 

215 pad = 0 

216 

217 return pad 

218 

219 def getData(self, soFar = 0): 

220 data = b'' 

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

222 try: 

223 # Alignment of Primitive Types 

224 

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

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

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

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

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

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

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

232 pad = self.calculatePad(fieldTypeOrClass, soFar) 

233 if pad > 0: 

234 soFar += pad 

235 data += b'\xbf'*pad 

236 

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

238 

239 data += res 

240 soFar += len(res) 

241 except Exception as e: 

242 LOG.error(str(e)) 

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

244 raise 

245 

246 return data 

247 

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

249 offset0 = offset 

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

251 try: 

252 # Alignment of Primitive Types 

253 

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

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

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

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

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

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

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

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

262 

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

264 except Exception as e: 

265 LOG.error(str(e)) 

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

267 raise 

268 return offset - offset0 

269 

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

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

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

273 

274 data = self.fields[fieldName] 

275 # void specifier 

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

277 return b'' 

278 

279 # code specifier 

280 two = fieldTypeOrClass.split('=') 

281 if len(two) >= 2: 

282 try: 

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

284 except: 

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

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

287 

288 if data is None: 

289 raise Exception('Trying to pack None') 

290 

291 # literal specifier 

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

293 if hasattr(data, 'getData'): 

294 return data.getData() 

295 return data 

296 

297 # struct like specifier 

298 return pack(fieldTypeOrClass, data) 

299 

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

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

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

303 

304 # code specifier 

305 two = fieldTypeOrClass.split('=') 

306 if len(two) >= 2: 

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

308 

309 # literal specifier 

310 if fieldTypeOrClass == ':': 

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

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

313 else: 

314 dataLen = self.getDataLen(data, offset) 

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

316 return dataLen 

317 

318 # struct like specifier 

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

320 

321 return calcsize(fieldTypeOrClass) 

322 

323 def calcPackSize(self, fieldTypeOrClass, data): 

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

325 return len(data) 

326 

327 # code specifier 

328 two = fieldTypeOrClass.split('=') 

329 if len(two) >= 2: 

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

331 

332 # literal specifier 

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

334 return len(data) 

335 

336 # struct like specifier 

337 return calcsize(fieldTypeOrClass) 

338 

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

340 if isinstance(fieldTypeOrClass, str) is False: 

341 return len(data) - offset 

342 

343 # code specifier 

344 two = fieldTypeOrClass.split('=') 

345 if len(two) >= 2: 

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

347 

348 # array specifier 

349 two = fieldTypeOrClass.split('*') 

350 if len(two) == 2: 

351 return len(data) - offset 

352 

353 # literal specifier 

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

355 return len(data) - offset 

356 

357 # struct like specifier 

358 return calcsize(fieldTypeOrClass) 

359 

360# NDR Primitives 

361class NDRSMALL(NDR): 

362 align = 1 

363 structure = ( 

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

365 ) 

366 

367class NDRUSMALL(NDR): 

368 align = 1 

369 structure = ( 

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

371 ) 

372 

373class NDRBOOLEAN(NDRSMALL): 

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

375 if msg is None: 

376 msg = self.__class__.__name__ 

377 if msg != '': 

378 print(msg, end=' ') 

379 

380 if self['Data'] > 0: 

381 print(" TRUE") 

382 else: 

383 print(" FALSE") 

384 

385class NDRCHAR(NDR): 

386 align = 1 

387 structure = ( 

388 ('Data', 'c'), 

389 ) 

390 

391class NDRSHORT(NDR): 

392 align = 2 

393 structure = ( 

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

395 ) 

396 

397class NDRUSHORT(NDR): 

398 align = 2 

399 structure = ( 

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

401 ) 

402 

403class NDRLONG(NDR): 

404 align = 4 

405 structure = ( 

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

407 ) 

408 

409class NDRULONG(NDR): 

410 align = 4 

411 structure = ( 

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

413 ) 

414 

415class NDRHYPER(NDR): 

416 align = 8 

417 structure = ( 

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

419 ) 

420 

421class NDRUHYPER(NDR): 

422 align = 8 

423 structure = ( 

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

425 ) 

426 

427class NDRFLOAT(NDR): 

428 align = 4 

429 structure = ( 

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

431 ) 

432 

433class NDRDOUBLEFLOAT(NDR): 

434 align = 8 

435 structure = ( 

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

437 ) 

438 

439class EnumType(type): 

440 def __getattr__(self, attr): 

441 return self.enumItems[attr].value 

442 

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

444 align = 2 

445 align64 = 4 

446 structure = ( 

447 ('Data', '<H'), 

448 ) 

449 

450 # 2.2.5.2 NDR64 Simple Data Types 

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

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

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

454 structure64 = ( 

455 ('Data', '<L'), 

456 ) 

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

458 class enumItems(Enum): 

459 pass 

460 

461 def __setitem__(self, key, value): 

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

463 self['Data'] = value.value 

464 else: 

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

466 

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

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

469 msg = self.__class__.__name__ 

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

471 print(msg, end=' ') 

472 

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

474 

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

476class NDRCONSTRUCTEDTYPE(NDR): 

477 @staticmethod 

478 def isPointer(field): 

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

480 myClass = field 

481 if issubclass(myClass, NDRPOINTER): 

482 return True 

483 return False 

484 

485 @staticmethod 

486 def isUnion(field): 

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

488 myClass = field 

489 if issubclass(myClass, NDRUNION): 

490 return True 

491 return False 

492 

493 def getDataReferents(self, soFar = 0): 

494 data = b'' 

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

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

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

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

499 return data 

500 

501 def getDataReferent(self, soFar=0): 

502 data = b'' 

503 soFar0 = soFar 

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

505 return b'' 

506 

507 if 'ReferentID' in self.fields: 

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

509 return b'' 

510 

511 for fieldName, fieldTypeOrClass in self.referent: 

512 try: 

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

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

515 # will need to build it later. 

516 if self._isNDR64: 

517 arrayItemSize = 8 

518 arrayPackStr = '<Q' 

519 else: 

520 arrayItemSize = 4 

521 arrayPackStr = '<L' 

522 

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

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

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

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

527 # independently of the structure alignment. 

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

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

530 if pad0 > 0: 

531 soFar += pad0 

532 arrayPadding = b'\xef'*pad0 

533 else: 

534 arrayPadding = b'' 

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

536 soFar += arrayItemSize 

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

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

539 else: 

540 pad = self.calculatePad(fieldTypeOrClass, soFar) 

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

542 soFar += pad 

543 data += b'\xcc'*pad 

544 

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

546 

547 # Any referent information to pack? 

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

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

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

551 soFar = soFar0 + len(data) 

552 

553 except Exception as e: 

554 LOG.error(str(e)) 

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

556 raise 

557 

558 return data 

559 

560 def calcPackSize(self, fieldTypeOrClass, data): 

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

562 return len(data) 

563 

564 # array specifier 

565 two = fieldTypeOrClass.split('*') 

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

567 answer = 0 

568 for each in data: 

569 if self.isNDR(self.item): 

570 item = ':' 

571 else: 

572 item = self.item 

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

574 return answer 

575 else: 

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

577 

578 def getArrayMaximumSize(self, fieldName): 

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

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

581 else: 

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

583 

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

585 if self._isNDR64: 

586 arrayItemSize = 8 

587 arrayUnPackStr = '<Q' 

588 else: 

589 arrayItemSize = 4 

590 arrayUnPackStr = '<L' 

591 

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

593 offset += pad 

594 

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

596 # Array Size is at the very beginning 

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

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

599 # NDRUniConformantVaryingArray Array 

600 # Unpack the Maximum Count 

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

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

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

604 # Unpack the Actual Count 

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

606 else: 

607 # NDRUniVaryingArray Array 

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

609 

610 return arraySize, arrayItemSize+pad 

611 

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

613 offset0 = offset 

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

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

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

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

618 return offset - offset0 

619 

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

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

622 return 0 

623 

624 offset0 = offset 

625 

626 if 'ReferentID' in self.fields: 

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

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

629 return 0 

630 

631 for fieldName, fieldTypeOrClass in self.referent: 

632 try: 

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

634 # Get the array size 

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

636 offset += advanceStream 

637 

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

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

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

641 else: 

642 # ToDo: Align only if not NDR 

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

644 

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

646 

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

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

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

650 offset += size 

651 except Exception as e: 

652 LOG.error(str(e)) 

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

654 raise 

655 

656 return offset-offset0 

657 

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

659 if isinstance(fieldTypeOrClass, str) is False: 

660 return len(data) - offset 

661 

662 two = fieldTypeOrClass.split('*') 

663 if len(two) == 2: 

664 return len(data) - offset 

665 else: 

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

667 

668# Uni-dimensional Fixed Arrays 

669class NDRArray(NDRCONSTRUCTEDTYPE): 

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

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

672 msg = self.__class__.__name__ 

673 ind = ' '*indent 

674 if msg != '': 

675 print(msg, end=' ') 

676 

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

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

679 ind += ' '*4 

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

681 if isinstance(j, NDR): 

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

683 print(",") 

684 else: 

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

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

687 else: 

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

689 

690 def setArraySize(self, size): 

691 self.arraySize = size 

692 

693 def getArraySize(self): 

694 return self.arraySize 

695 

696 def changeTransferSyntax(self, newSyntax): 

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

698 # Only if the item type is NDR 

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

700 if self.isNDR(self.item): 

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

702 item.changeTransferSyntax(newSyntax) 

703 return NDRCONSTRUCTEDTYPE.changeTransferSyntax(self, newSyntax) 

704 

705 def getAlignment(self): 

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

707 # the size information type, if any. 

708 align = 0 

709 # And now the item 

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

711 if self.isNDR(self.item): 

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

713 else: 

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

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

716 align = tmpAlign 

717 return align 

718 

719 def getData(self, soFar = 0): 

720 data = b'' 

721 soFar0 = soFar 

722 for fieldName, fieldTypeOrClass in self.structure: 

723 try: 

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

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

726 # we have to align it 

727 pad = self.calculatePad(fieldTypeOrClass, soFar) 

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

729 soFar += pad 

730 data += b'\xca'*pad 

731 

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

733 data += res 

734 soFar = soFar0 + len(data) 

735 except Exception as e: 

736 LOG.error(str(e)) 

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

738 raise 

739 

740 return data 

741 

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

743 # array specifier 

744 two = fieldTypeOrClass.split('*') 

745 if len(two) == 2: 

746 answer = b'' 

747 if self.isNDR(self.item): 

748 item = ':' 

749 dataClass = self.item 

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

751 else: 

752 item = self.item 

753 dataClass = None 

754 self.fields['_tmpItem'] = item 

755 

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

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

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

759 answer += b'\xdd' * pad 

760 if dataClass is None: 

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

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

763 each = bytes([each]) 

764 answer += pack(item, each) 

765 else: 

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

767 

768 if dataClass is not None: 

769 for each in self.fields[fieldName]: 

770 if isinstance(each, NDRCONSTRUCTEDTYPE): 

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

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

773 

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

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

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

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

778 else: 

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

780 

781 return answer 

782 else: 

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

784 

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

786 offset0 = offset 

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

788 try: 

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

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

791 # we have to align it 

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

793 

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

795 offset += size 

796 except Exception as e: 

797 LOG.error(str(e)) 

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

799 raise 

800 return offset - offset0 

801 

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

803 # array specifier 

804 two = fieldTypeOrClass.split('*') 

805 answer = [] 

806 soFarItems = 0 

807 offset0 = offset 

808 if len(two) == 2: 

809 if isinstance(self, NDRUniConformantArray): 

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

811 numItems = self.getArraySize() 

812 elif isinstance(self, NDRUniConformantVaryingArray): 

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

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

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

816 numItems = self[two[1]] 

817 else: 

818 numItems = self[two[1]] 

819 

820 # The item type is determined by self.item 

821 if self.isNDR(self.item): 

822 item = ':' 

823 dataClassOrCode = self.item 

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

825 else: 

826 item = self.item 

827 dataClassOrCode = None 

828 self.fields['_tmpItem'] = item 

829 

830 nsofar = 0 

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

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

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

834 soFarItems +=pad 

835 if dataClassOrCode is None: 

836 nsofar = soFarItems + calcsize(item) 

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

838 else: 

839 itemn = dataClassOrCode(isNDR64=self._isNDR64) 

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

841 answer.append(itemn) 

842 nsofar += size + pad 

843 numItems -= 1 

844 soFarItems = nsofar 

845 

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

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

848 answer2 = [] 

849 for itemn in answer: 

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

851 soFarItems += size 

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

853 soFarItems += size 

854 answer2.append(itemn) 

855 answer = answer2 

856 del answer2 

857 

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

859 

860 self.fields[fieldName] = answer 

861 return soFarItems + offset - offset0 

862 else: 

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

864 

865class NDRUniFixedArray(NDRArray): 

866 structure = ( 

867 ('Data',':'), 

868 ) 

869 

870# Uni-dimensional Conformant Arrays 

871class NDRUniConformantArray(NDRArray): 

872 item = 'c' 

873 structure = ( 

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

875 ('Data', '*MaximumCount'), 

876 ) 

877 

878 structure64 = ( 

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

880 ('Data', '*MaximumCount'), 

881 ) 

882 

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

884 NDRArray.__init__(self, data, isNDR64) 

885 # Let's store the hidden MaximumCount field 

886 self.fields['MaximumCount'] = 0 

887 

888 def __setitem__(self, key, value): 

889 self.fields['MaximumCount'] = None 

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

891 

892 

893# Uni-dimensional Varying Arrays 

894class NDRUniVaryingArray(NDRArray): 

895 item = 'c' 

896 structure = ( 

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

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

899 ('Data','*ActualCount'), 

900 ) 

901 structure64 = ( 

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

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

904 ('Data','*ActualCount'), 

905 ) 

906 

907 def __setitem__(self, key, value): 

908 self.fields['ActualCount'] = None 

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

910 

911# Uni-dimensional Conformant-varying Arrays 

912class NDRUniConformantVaryingArray(NDRArray): 

913 item = 'c' 

914 commonHdr = ( 

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

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

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

918 ) 

919 commonHdr64 = ( 

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

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

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

923 ) 

924 

925 structure = ( 

926 ('Data','*ActualCount'), 

927 ) 

928 

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

930 NDRArray.__init__(self, data, isNDR64) 

931 # Let's store the hidden MaximumCount field 

932 self.fields['MaximumCount'] = 0 

933 

934 def __setitem__(self, key, value): 

935 self.fields['MaximumCount'] = None 

936 self.fields['ActualCount'] = None 

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

938 

939 def getData(self, soFar = 0): 

940 data = b'' 

941 soFar0 = soFar 

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

943 try: 

944 pad = self.calculatePad(fieldTypeOrClass, soFar) 

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

946 soFar += pad 

947 data += b'\xcb'*pad 

948 

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

950 data += res 

951 soFar = soFar0 + len(data) 

952 except Exception as e: 

953 LOG.error(str(e)) 

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

955 raise 

956 

957 return data 

958 

959# Multidimensional arrays not implemented for now 

960 

961# Varying Strings 

962class NDRVaryingString(NDRUniVaryingArray): 

963 def getData(self, soFar = 0): 

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

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

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

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

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

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

970 else: 

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

972 return NDRUniVaryingArray.getData(self, soFar) 

973 

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

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

976 # Let's take out the last item 

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

978 return ret 

979 

980# Conformant and Varying Strings 

981class NDRConformantVaryingString(NDRUniConformantVaryingArray): 

982 pass 

983 

984# Structures 

985# Structures Containing a Conformant Array  

986# Structures Containing a Conformant and Varying Array  

987class NDRSTRUCT(NDRCONSTRUCTEDTYPE): 

988 def getData(self, soFar = 0): 

989 data = b'' 

990 arrayPadding = b'' 

991 soFar0 = soFar 

992 # 14.3.7.1 Structures Containing a Conformant Array 

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

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

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

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

997 # the end of the structure. 

998 # 14.3.7.2 Structures Containing a Conformant and Varying Array 

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

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

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

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

1003 # immediately preceding the array elements 

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

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

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

1007 # will need to build it later. 

1008 if self._isNDR64: 

1009 arrayItemSize = 8 

1010 arrayPackStr = '<Q' 

1011 else: 

1012 arrayItemSize = 4 

1013 arrayPackStr = '<L' 

1014 

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

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

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

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

1019 # independently of the structure alignment. 

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

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

1022 if pad0 > 0: 

1023 soFar += pad0 

1024 arrayPadding = b'\xee'*pad0 

1025 else: 

1026 arrayPadding = b'' 

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

1028 soFar += arrayItemSize 

1029 else: 

1030 arrayItemSize = 0 

1031 

1032 # Now we need to align the structure  

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

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

1035 # recursively to nested constructed types. 

1036 alignment = self.getAlignment() 

1037 

1038 if alignment > 0: 

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

1040 if pad > 0: 

1041 soFar += pad 

1042 data += b'\xAB'*pad 

1043 

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

1045 try: 

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

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

1048 if isinstance(self, NDRPOINTER): 

1049 pointerData = data[:arrayItemSize] 

1050 data = data[arrayItemSize:] 

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

1052 else: 

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

1054 arrayPadding = b'' 

1055 arrayItemSize = 0 

1056 else: 

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

1058 data += res 

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

1060 except Exception as e: 

1061 LOG.error(str(e)) 

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

1063 raise 

1064 

1065 # 2.2.5.3.4.1 Structure with Trailing Gap 

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

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

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

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

1070 # See the following figure. 

1071 

1072 # 4.8 Example of Structure with Trailing Gap in NDR64 

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

1074 # typedef struct _StructWithPad 

1075 # { 

1076 # long l; 

1077 # short s; 

1078 # } StructWithPad; 

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

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

1081# if self._isNDR64 is True: 

1082# # ToDo add trailing gap here 

1083# if alignment > 0: 

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

1085# if pad > 0: 

1086# soFar += pad 

1087# data += '\xcd'*pad 

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

1089 return data 

1090 

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

1092 offset0 = offset 

1093 # 14.3.7.1 Structures Containing a Conformant Array 

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

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

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

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

1098 # the end of the structure. 

1099 # 14.3.7.2 Structures Containing a Conformant and Varying Array 

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

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

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

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

1104 # immediately preceding the array elements 

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

1106 

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

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

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

1110 if isinstance(self, NDRPOINTER): 

1111 structureFields = self.structure 

1112 

1113 alignment = self.getAlignment() 

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

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

1116 

1117 for fieldName, fieldTypeOrClass in self.commonHdr: 

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

1119 else: 

1120 structureFields = self.commonHdr+self.structure 

1121 

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

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

1124 # will need to build it later. 

1125 if self._isNDR64: 

1126 arrayItemSize = 8 

1127 arrayUnPackStr = '<Q' 

1128 else: 

1129 arrayItemSize = 4 

1130 arrayUnPackStr = '<L' 

1131 

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

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

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

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

1136 # independently of the structure alignment. 

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

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

1139 

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

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

1142 # NDRUniConformantArray 

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

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

1145 else: 

1146 # NDRUniConformantVaryingArray 

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

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

1149 

1150 offset += arrayItemSize 

1151 

1152 # Now we need to align the structure 

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

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

1155 # recursively to nested constructed types. 

1156 alignment = self.getAlignment() 

1157 if alignment > 0: 

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

1159 

1160 for fieldName, fieldTypeOrClass in structureFields: 

1161 try: 

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

1163 except Exception as e: 

1164 LOG.error(str(e)) 

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

1166 raise 

1167 

1168 return offset - offset0 

1169 

1170 def getAlignment(self): 

1171 # Alignment of Constructed Types 

1172 # 

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

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

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

1176 # on an NDR octet stream index of n. 

1177 

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

1179 

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

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

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

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

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

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

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

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

1188 # and is aligned independently of the structure alignment. 

1189 

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

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

1192 # constructed types. 

1193 

1194 align = 0 

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

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

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

1198 else: 

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

1200 if tmpAlign > align: 

1201 align = tmpAlign 

1202 return align 

1203 

1204# Unions  

1205class NDRUNION(NDRCONSTRUCTEDTYPE): 

1206 commonHdr = ( 

1207 ('tag', NDRUSHORT), 

1208 ) 

1209 commonHdr64 = ( 

1210 ('tag', NDRULONG), 

1211 ) 

1212 

1213 union = { 

1214 # For example 

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

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

1217 } 

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

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

1220 self.topLevel = topLevel 

1221 self._isNDR64 = isNDR64 

1222 self.fields = {} 

1223 

1224 if isNDR64 is True: 

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

1226 self.commonHdr = self.commonHdr64 

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

1228 self.structure = self.structure64 

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

1230 self.align = self.align64 

1231 

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

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

1234 if self.isPointer(fieldTypeOrClass): 

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

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

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

1238 else: 

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

1240 elif fieldTypeOrClass == ':': 

1241 self.fields[fieldName] = None 

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

1243 try: 

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

1245 except: 

1246 self.fields[fieldName] = None 

1247 else: 

1248 self.fields[fieldName] = 0 

1249 

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

1251 self.fromString(data) 

1252 

1253 def __setitem__(self, key, value): 

1254 if key == 'tag': 

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

1256 self.structure = () 

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

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

1259 # Init again the structure 

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

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

1262 else: 

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

1264 if 'default' in self.union: 

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

1266 self.structure = () 

1267 else: 

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

1269 # Init again the structure 

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

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

1272 else: 

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

1274 else: 

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

1276 

1277 def getData(self, soFar = 0): 

1278 data = b'' 

1279 soFar0 = soFar 

1280 

1281 # Let's align ourselves 

1282 alignment = self.getAlignment() 

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

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

1285 else: 

1286 pad = 0 

1287 if pad > 0: 

1288 soFar += pad 

1289 data += b'\xbc'*pad 

1290 

1291 for fieldName, fieldTypeOrClass in self.commonHdr: 

1292 try: 

1293 pad = self.calculatePad(fieldTypeOrClass, soFar) 

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

1295 soFar += pad 

1296 data += b'\xbb'*pad 

1297 

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

1299 data += res 

1300 soFar = soFar0 + len(data) 

1301 except Exception as e: 

1302 LOG.error(str(e)) 

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

1304 raise 

1305 

1306 # WARNING 

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

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

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

1310 # a DWORD, or HYPER if NDR64.  

1311 if self._isNDR64: 

1312 align = 8 

1313 else: 

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

1315 align = 1 

1316 else: 

1317 align = 4 

1318 

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

1320 if pad > 0: 

1321 data += b'\xbd'*pad 

1322 soFar += pad 

1323 

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

1325 return data 

1326 

1327 for fieldName, fieldTypeOrClass in self.structure: 

1328 try: 

1329 pad = self.calculatePad(fieldTypeOrClass, soFar) 

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

1331 soFar += pad 

1332 data += b'\xbe'*pad 

1333 

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

1335 data += res 

1336 soFar = soFar0 + len(data) 

1337 except Exception as e: 

1338 LOG.error(str(e)) 

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

1340 raise 

1341 

1342 return data 

1343 

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

1345 offset0 = offset 

1346 # Let's align ourselves 

1347 alignment = self.getAlignment() 

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

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

1350 else: 

1351 pad = 0 

1352 if pad > 0: 

1353 offset += pad 

1354 

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

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

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

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

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

1360 if tag in self.union: 

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

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

1363 else: 

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

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

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

1367 self.structure = () 

1368 else: 

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

1370 # Init again the structure 

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

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

1373 else: 

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

1375 

1376 for fieldName, fieldTypeOrClass in self.commonHdr: 

1377 try: 

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

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

1380 except Exception as e: 

1381 LOG.error(str(e)) 

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

1383 raise 

1384 

1385 # WARNING 

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

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

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

1389 # a DWORD, or HYPER if NDR64.  

1390 if self._isNDR64: 

1391 align = 8 

1392 else: 

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

1394 align = 1 

1395 else: 

1396 align = 4 

1397 

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

1399 

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

1401 return offset-offset0 

1402 

1403 for fieldName, fieldTypeOrClass in self.structure: 

1404 try: 

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

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

1407 except Exception as e: 

1408 LOG.error(str(e)) 

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

1410 raise 

1411 

1412 return offset - offset0 

1413 

1414 def getAlignment(self): 

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

1416 # and all of the union arms. 

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

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

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

1420 align = 0 

1421 if self._isNDR64: 

1422 fields = self.commonHdr+self.structure 

1423 else: 

1424 fields = self.commonHdr 

1425 for fieldName, fieldTypeOrClass in fields: 

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

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

1428 else: 

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

1430 if tmpAlign > align: 

1431 align = tmpAlign 

1432 

1433 if self._isNDR64: 

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

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

1436 if tmpAlign > align: 

1437 align = tmpAlign 

1438 return align 

1439 

1440# Pipes not implemented for now 

1441 

1442# Pointers 

1443class NDRPOINTERNULL(NDR): 

1444 align = 4 

1445 align64 = 8 

1446 structure = ( 

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

1448 ) 

1449 structure64 = ( 

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

1451 ) 

1452 

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

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

1455 msg = self.__class__.__name__ 

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

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

1458 # Here we just print NULL 

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

1460 

1461NULL = NDRPOINTERNULL() 

1462 

1463class NDRPOINTER(NDRSTRUCT): 

1464 align = 4 

1465 align64 = 8 

1466 commonHdr = ( 

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

1468 ) 

1469 commonHdr64 = ( 

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

1471 ) 

1472 

1473 referent = ( 

1474 # This is the representation of the Referent 

1475 ('Data',':'), 

1476 ) 

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

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

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

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

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

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

1483 # Not in the structure one! 

1484 if topLevel is True: 

1485 self.structure = self.referent 

1486 self.referent = () 

1487 

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

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

1490 else: 

1491 self.fromString(data) 

1492 

1493 def __setitem__(self, key, value): 

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

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

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

1497 else: 

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

1499 

1500 def __getitem__(self, key): 

1501 if key in self.fields: 

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

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

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

1505 return self.fields[key] 

1506 else: 

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

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

1509 

1510 def getData(self, soFar = 0): 

1511 # First of all we need to align ourselves 

1512 data = b'' 

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

1514 if pad > 0: 

1515 soFar += pad 

1516 data = b'\xaa'*pad 

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

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

1519 if len(self.referent) > 0: 

1520 self['Data'] = b'' 

1521 else: 

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

1523 return data+b'\x00'*8 

1524 else: 

1525 return data+b'\x00'*4 

1526 

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

1528 

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

1530 # First of all we need to align ourselves 

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

1532 offset += pad 

1533 

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

1535 if self._isNDR64 is True: 

1536 unpackStr = '<Q' 

1537 else: 

1538 unpackStr = '<L' 

1539 

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

1541 # Let's save the value 

1542 self['ReferentID'] = 0 

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

1544 if self._isNDR64 is True: 

1545 return pad + 8 

1546 else: 

1547 return pad + 4 

1548 else: 

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

1550 return retVal + pad 

1551 

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

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

1554 msg = self.__class__.__name__ 

1555 if msg != '': 

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

1557 # Here we just print the referent 

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

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

1560 else: 

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

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

1563 else: 

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

1565 

1566 def getAlignment(self): 

1567 if self._isNDR64 is True: 

1568 return 8 

1569 else: 

1570 return 4 

1571 

1572 

1573# Embedded Reference Pointers not implemented for now 

1574 

1575################################################################################ 

1576# Common RPC Data Types 

1577 

1578class PNDRUniConformantVaryingArray(NDRPOINTER): 

1579 referent = ( 

1580 ('Data', NDRUniConformantVaryingArray), 

1581 ) 

1582 

1583class PNDRUniConformantArray(NDRPOINTER): 

1584 referent = ( 

1585 ('Data', NDRUniConformantArray), 

1586 ) 

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

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

1589 

1590class NDRCALL(NDRCONSTRUCTEDTYPE): 

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

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

1593 # represent the referents when building the final octet stream 

1594 referent = () 

1595 commonHdr = () 

1596 commonHdr64 = () 

1597 structure = () 

1598 structure64 = () 

1599 align = 4 

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

1601 self._isNDR64 = isNDR64 

1602 self.fields = {} 

1603 

1604 if isNDR64 is True: 

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

1606 self.commonHdr = self.commonHdr64 

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

1608 self.structure = self.structure64 

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

1610 self.align = self.align64 

1611 

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

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

1614 if self.isPointer(fieldTypeOrClass): 

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

1616 elif self.isUnion(fieldTypeOrClass): 

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

1618 else: 

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

1620 elif fieldTypeOrClass == ':': 

1621 self.fields[fieldName] = None 

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

1623 try: 

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

1625 except: 

1626 self.fields[fieldName] = None 

1627 else: 

1628 self.fields[fieldName] = 0 

1629 

1630 if data is not None: 

1631 self.fromString(data) 

1632 

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

1634 NDRCONSTRUCTEDTYPE.dump(self, msg, indent) 

1635 print('\n\n') 

1636 

1637 def getData(self, soFar = 0): 

1638 data = b'' 

1639 soFar0 = soFar 

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

1641 try: 

1642 pad = self.calculatePad(fieldTypeOrClass, soFar) 

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

1644 soFar += pad 

1645 data += b'\xab'*pad 

1646 

1647 # Are we dealing with an array? 

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

1649 NDRUniConformantVaryingArray): 

1650 # Align size item 

1651 if self._isNDR64: 

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

1653 else: 

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

1655 # Pack the item 

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

1657 # Yes, get the array size 

1658 arraySize = self.getArrayMaximumSize(fieldName) 

1659 if self._isNDR64: 

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

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

1662 else: 

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

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

1665 else: 

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

1667 

1668 soFar = soFar0 + len(data) 

1669 # Any referent information to pack? 

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

1671 # all the fields at the call level. 

1672 # Guess we'll figure it out testing. 

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

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

1675 soFar = soFar0 + len(data) 

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

1677 soFar = soFar0 + len(data) 

1678 except Exception as e: 

1679 LOG.error(str(e)) 

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

1681 raise 

1682 

1683 return data 

1684 

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

1686 offset0 = offset 

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

1688 try: 

1689 # Are we dealing with an array? 

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

1691 NDRUniConformantVaryingArray): 

1692 # Yes, get the array size 

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

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

1695 offset += advanceStream 

1696 

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

1698 

1699 # Any referent information to unpack? 

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

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

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

1703 offset += size 

1704 except Exception as e: 

1705 LOG.error(str(e)) 

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

1707 raise 

1708 

1709 return offset - offset0 

1710 

1711# Top Level Struct == NDRCALL 

1712NDRTLSTRUCT = NDRCALL 

1713 

1714class UNKNOWNDATA(NDR): 

1715 align = 1 

1716 structure = ( 

1717 ('Data', ':'), 

1718 )