Coverage for /root/GitHubProjects/impacket/impacket/dcerpc/v5/ndr.py: 84%

# Coverage for /root/GitHubProjects/impacket/impacket/dcerpc/v5/ndr.py : 84%

## 1050 statements

Hot-keys on this page

r m x p toggle line displays

j k next/prev highlighted chunk

0 (zero) top of page

1 (one) first highlighted chunk

1# 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#

19 from __future__ import division

20 from __future__ import print_function

21 import random

22 import inspect

23 from struct import pack, unpack_from, calcsize

24 from six import with_metaclass, PY3

25

26 from impacket import LOG

27 from impacket.dcerpc.v5.enum import Enum

28 from 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

36 class 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

361 class NDRSMALL(NDR):

362 align = 1

363 structure = (

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

365 )

366

367 class NDRUSMALL(NDR):

368 align = 1

369 structure = (

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

371 )

372

373 class 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

385 class NDRCHAR(NDR):

386 align = 1

387 structure = (

388 ('Data', 'c'),

389 )

390

391 class NDRSHORT(NDR):

392 align = 2

393 structure = (

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

395 )

396

397 class NDRUSHORT(NDR):

398 align = 2

399 structure = (

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

401 )

402

403 class NDRLONG(NDR):

404 align = 4

405 structure = (

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

407 )

408

409 class NDRULONG(NDR):

410 align = 4

411 structure = (

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

413 )

414

415 class NDRHYPER(NDR):

416 align = 8

417 structure = (

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

419 )

420

421 class NDRUHYPER(NDR):

422 align = 8

423 structure = (

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

425 )

426

427 class NDRFLOAT(NDR):

428 align = 4

429 structure = (

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

431 )

432

433 class NDRDOUBLEFLOAT(NDR):

434 align = 8

435 structure = (

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

437 )

438

439 class EnumType(type):

440 def __getattr__(self, attr):

441 return self.enumItems[attr].value

442

443 class 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)

476 class 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

669 class 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

865 class NDRUniFixedArray(NDRArray):

866 structure = (

867 ('Data',':'),

868 )

869

870# Uni-dimensional Conformant Arrays

871 class 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

894 class 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

912 class 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

962 class 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

981 class NDRConformantVaryingString(NDRUniConformantVaryingArray):

982 pass

983

984# Structures

985# Structures Containing a Conformant Array

986# Structures Containing a Conformant and Varying Array

987 class 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

1205 class 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

1443 class 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

1461 NULL = NDRPOINTERNULL()

1462

1463 class 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

1578 class PNDRUniConformantVaryingArray(NDRPOINTER):

1579 referent = (

1580 ('Data', NDRUniConformantVaryingArray),

1581 )

1582

1583 class 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

1590 class 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

1712 NDRTLSTRUCT = NDRCALL

1713

1714 class UNKNOWNDATA(NDR):

1715 align = 1

1716 structure = (

1717 ('Data', ':'),

1718 )