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

2#

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

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

5# for more information.

6#

7# [C706] Transfer NDR Syntax implementation

8#

9# Author: Alberto Solino (@agsolino)

10#

11# ToDo:

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

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

14#

15 from __future__ import division

16 from __future__ import print_function

17 import random

18 import inspect

19 from struct import pack, unpack_from, calcsize

20 from six import with_metaclass, PY3

21

22 from impacket import LOG

23 from impacket.dcerpc.v5.enum import Enum

24 from impacket.uuid import uuidtup_to_bin

25

26# Something important to have in mind:

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

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

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

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

31

32 class NDR(object):

33 """

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

35 """

36 referent = ()

37 commonHdr = ()

38 commonHdr64 = ()

39 structure = ()

40 structure64 = ()

41 align = 4

42 item = None

43 _isNDR64 = False

44

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

46 object.__init__(self)

47 self._isNDR64 = isNDR64

48 self.fields = {}

49

50 if isNDR64 is True:

51 if self.commonHdr64 != ():

52 self.commonHdr = self.commonHdr64

53 if self.structure64 != ():

54 self.structure = self.structure64

55 if hasattr(self, 'align64'):

56 self.align = self.align64

57

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

59 if self.isNDR(fieldTypeOrClass):

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

61 elif fieldTypeOrClass == ':':

62 self.fields[fieldName] = b''

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

64 try:

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

66 except:

67 self.fields[fieldName] = None

68 else:

69 self.fields[fieldName] = []

70

71 if data is not None:

72 self.fromString(data)

73

74 def changeTransferSyntax(self, newSyntax):

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

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

77 if self._isNDR64 is False:

78 # Ok, let's change everything

79 self._isNDR64 = True

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

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

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

83 # Finally, I change myself

84 if self.commonHdr64 != ():

85 self.commonHdr = self.commonHdr64

86 if self.structure64 != ():

87 self.structure = self.structure64

88 if hasattr(self, 'align64'):

89 self.align = self.align64

90 # And check whether the changes changed the data types

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

92 # old values

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

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

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

96 backupData = self[fieldName]

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

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

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

100 else:

101 self[fieldName] = backupData

102

103 else:

104 if self._isNDR64 is True:

105 # Ok, nothing for now

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

107

108 def __setitem__(self, key, value):

109 if isinstance(value, NDRPOINTERNULL):

110 value = NDRPOINTERNULL(isNDR64 = self._isNDR64)

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

112 self.fields[key] = value

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

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

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

116 elif isinstance(value, NDR):

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

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

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

120 self.fields[key] = value

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

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

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

124 else:

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

126 else:

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

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

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

130 else:

131 self.fields[key] = value

132

133 def __getitem__(self, key):

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

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

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

137 return self.fields[key]

138

139 def __str__(self):

140 return self.getData()

141

142 def __len__(self):

143 # XXX: improve

144 return len(self.getData())

145

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

147 return len(data) - offset

148

149 @staticmethod

150 def isNDR(field):

151 if inspect.isclass(field):

152 myClass = field

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

154 return True

155 return False

156

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

158 if msg is None:

159 msg = self.__class__.__name__

160 ind = ' '*indent

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

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

163 i = field[0]

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

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

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

167 print("%s}" % ind)

168

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

170 print("%s[" % ind)

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

172 if isinstance(j, NDR):

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

174 print("%s," % ind)

175 else:

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

177 print("%s]" % ind)

178

179 else:

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

181

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

183 if msg is None:

184 msg = self.__class__.__name__

185 ind = ' '*indent

186 if msg != '':

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

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

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

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

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

192 else:

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

194

195 def getAlignment(self):

196 return self.align

197

198 @staticmethod

199 def calculatePad(fieldType, soFar):

200 if isinstance(fieldType, str):

201 try:

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

203 except:

204 alignment = 0

205 else:

206 alignment = 0

207

208 if alignment > 0:

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

210 else:

211 pad = 0

212

213 return pad

214

215 def getData(self, soFar = 0):

216 data = b''

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

218 try:

219 # Alignment of Primitive Types

220

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

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

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

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

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

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

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

228 pad = self.calculatePad(fieldTypeOrClass, soFar)

229 if pad > 0:

230 soFar += pad

231 data += b'\xbf'*pad

232

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

234

235 data += res

236 soFar += len(res)

237 except Exception as e:

238 LOG.error(str(e))

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

240 raise

241

242 return data

243

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

245 offset0 = offset

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

247 try:

248 # Alignment of Primitive Types

249

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

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

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

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

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

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

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

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

258

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

260 except Exception as e:

261 LOG.error(str(e))

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

263 raise

264 return offset - offset0

265

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

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

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

269

270 data = self.fields[fieldName]

271 # void specifier

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

273 return b''

274

275 # code specifier

276 two = fieldTypeOrClass.split('=')

277 if len(two) >= 2:

278 try:

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

280 except:

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

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

283

284 if data is None:

285 raise Exception('Trying to pack None')

286

287 # literal specifier

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

289 if hasattr(data, 'getData'):

290 return data.getData()

291 return data

292

293 # struct like specifier

294 return pack(fieldTypeOrClass, data)

295

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

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

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

299

300 # code specifier

301 two = fieldTypeOrClass.split('=')

302 if len(two) >= 2:

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

304

305 # literal specifier

306 if fieldTypeOrClass == ':':

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

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

309 else:

310 dataLen = self.getDataLen(data, offset)

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

312 return dataLen

313

314 # struct like specifier

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

316

317 return calcsize(fieldTypeOrClass)

318

319 def calcPackSize(self, fieldTypeOrClass, data):

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

321 return len(data)

322

323 # code specifier

324 two = fieldTypeOrClass.split('=')

325 if len(two) >= 2:

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

327

328 # literal specifier

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

330 return len(data)

331

332 # struct like specifier

333 return calcsize(fieldTypeOrClass)

334

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

336 if isinstance(fieldTypeOrClass, str) is False:

337 return len(data) - offset

338

339 # code specifier

340 two = fieldTypeOrClass.split('=')

341 if len(two) >= 2:

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

343

344 # array specifier

345 two = fieldTypeOrClass.split('*')

346 if len(two) == 2:

347 return len(data) - offset

348

349 # literal specifier

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

351 return len(data) - offset

352

353 # struct like specifier

354 return calcsize(fieldTypeOrClass)

355

356# NDR Primitives

357 class NDRSMALL(NDR):

358 align = 1

359 structure = (

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

361 )

362

363 class NDRUSMALL(NDR):

364 align = 1

365 structure = (

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

367 )

368

369 class NDRBOOLEAN(NDRSMALL):

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

371 if msg is None:

372 msg = self.__class__.__name__

373 if msg != '':

374 print(msg, end=' ')

375

376 if self['Data'] > 0:

377 print(" TRUE")

378 else:

379 print(" FALSE")

380

381 class NDRCHAR(NDR):

382 align = 1

383 structure = (

384 ('Data', 'c'),

385 )

386

387 class NDRSHORT(NDR):

388 align = 2

389 structure = (

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

391 )

392

393 class NDRUSHORT(NDR):

394 align = 2

395 structure = (

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

397 )

398

399 class NDRLONG(NDR):

400 align = 4

401 structure = (

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

403 )

404

405 class NDRULONG(NDR):

406 align = 4

407 structure = (

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

409 )

410

411 class NDRHYPER(NDR):

412 align = 8

413 structure = (

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

415 )

416

417 class NDRUHYPER(NDR):

418 align = 8

419 structure = (

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

421 )

422

423 class NDRFLOAT(NDR):

424 align = 4

425 structure = (

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

427 )

428

429 class NDRDOUBLEFLOAT(NDR):

430 align = 8

431 structure = (

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

433 )

434

435 class EnumType(type):

436 def __getattr__(self, attr):

437 return self.enumItems[attr].value

438

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

440 align = 2

441 align64 = 4

442 structure = (

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

444 )

445

446 # 2.2.5.2 NDR64 Simple Data Types

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

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

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

450 structure64 = (

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

452 )

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

454 class enumItems(Enum):

455 pass

456

457 def __setitem__(self, key, value):

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

459 self['Data'] = value.value

460 else:

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

462

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

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

465 msg = self.__class__.__name__

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

467 print(msg, end=' ')

468

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

470

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

472 class NDRCONSTRUCTEDTYPE(NDR):

473 @staticmethod

474 def isPointer(field):

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

476 myClass = field

477 if issubclass(myClass, NDRPOINTER):

478 return True

479 return False

480

481 @staticmethod

482 def isUnion(field):

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

484 myClass = field

485 if issubclass(myClass, NDRUNION):

486 return True

487 return False

488

489 def getDataReferents(self, soFar = 0):

490 data = b''

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

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

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

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

495 return data

496

497 def getDataReferent(self, soFar=0):

498 data = b''

499 soFar0 = soFar

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

501 return b''

502

503 if 'ReferentID' in self.fields:

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

505 return b''

506

507 for fieldName, fieldTypeOrClass in self.referent:

508 try:

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

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

511 # will need to build it later.

512 if self._isNDR64:

513 arrayItemSize = 8

514 arrayPackStr = '<Q'

515 else:

516 arrayItemSize = 4

517 arrayPackStr = '<L'

518

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

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

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

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

523 # independently of the structure alignment.

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

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

526 if pad0 > 0:

527 soFar += pad0

528 arrayPadding = b'\xef'*pad0

529 else:

530 arrayPadding = b''

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

532 soFar += arrayItemSize

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

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

535 else:

536 pad = self.calculatePad(fieldTypeOrClass, soFar)

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

538 soFar += pad

539 data += b'\xcc'*pad

540

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

542

543 # Any referent information to pack?

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

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

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

547 soFar = soFar0 + len(data)

548

549 except Exception as e:

550 LOG.error(str(e))

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

552 raise

553

554 return data

555

556 def calcPackSize(self, fieldTypeOrClass, data):

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

558 return len(data)

559

560 # array specifier

561 two = fieldTypeOrClass.split('*')

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

563 answer = 0

564 for each in data:

565 if self.isNDR(self.item):

566 item = ':'

567 else:

568 item = self.item

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

570 return answer

571 else:

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

573

574 def getArrayMaximumSize(self, fieldName):

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

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

577 else:

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

579

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

581 if self._isNDR64:

582 arrayItemSize = 8

583 arrayUnPackStr = '<Q'

584 else:

585 arrayItemSize = 4

586 arrayUnPackStr = '<L'

587

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

589 offset += pad

590

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

592 # Array Size is at the very beginning

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

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

595 # NDRUniConformantVaryingArray Array

596 # Unpack the Maximum Count

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

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

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

600 # Unpack the Actual Count

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

602 else:

603 # NDRUniVaryingArray Array

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

605

606 return arraySize, arrayItemSize+pad

607

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

609 offset0 = offset

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

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

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

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

614 return offset - offset0

615

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

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

618 return 0

619

620 offset0 = offset

621

622 if 'ReferentID' in self.fields:

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

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

625 return 0

626

627 for fieldName, fieldTypeOrClass in self.referent:

628 try:

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

630 # Get the array size

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

632 offset += advanceStream

633

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

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

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

637 else:

638 # ToDo: Align only if not NDR

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

640

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

642

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

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

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

646 offset += size

647 except Exception as e:

648 LOG.error(str(e))

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

650 raise

651

652 return offset-offset0

653

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

655 if isinstance(fieldTypeOrClass, str) is False:

656 return len(data) - offset

657

658 two = fieldTypeOrClass.split('*')

659 if len(two) == 2:

660 return len(data) - offset

661 else:

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

663

664# Uni-dimensional Fixed Arrays

665 class NDRArray(NDRCONSTRUCTEDTYPE):

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

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

668 msg = self.__class__.__name__

669 ind = ' '*indent

670 if msg != '':

671 print(msg, end=' ')

672

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

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

675 ind += ' '*4

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

677 if isinstance(j, NDR):

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

679 print(",")

680 else:

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

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

683 else:

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

685

686 def setArraySize(self, size):

687 self.arraySize = size

688

689 def getArraySize(self):

690 return self.arraySize

691

692 def changeTransferSyntax(self, newSyntax):

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

694 # Only if the item type is NDR

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

696 if self.isNDR(self.item):

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

698 item.changeTransferSyntax(newSyntax)

699 return NDRCONSTRUCTEDTYPE.changeTransferSyntax(self, newSyntax)

700

701 def getAlignment(self):

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

703 # the size information type, if any.

704 align = 0

705 # And now the item

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

707 if self.isNDR(self.item):

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

709 else:

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

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

712 align = tmpAlign

713 return align

714

715 def getData(self, soFar = 0):

716 data = b''

717 soFar0 = soFar

718 for fieldName, fieldTypeOrClass in self.structure:

719 try:

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

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

722 # we have to align it

723 pad = self.calculatePad(fieldTypeOrClass, soFar)

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

725 soFar += pad

726 data += b'\xca'*pad

727

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

729 data += res

730 soFar = soFar0 + len(data)

731 except Exception as e:

732 LOG.error(str(e))

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

734 raise

735

736 return data

737

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

739 # array specifier

740 two = fieldTypeOrClass.split('*')

741 if len(two) == 2:

742 answer = b''

743 if self.isNDR(self.item):

744 item = ':'

745 dataClass = self.item

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

747 else:

748 item = self.item

749 dataClass = None

750 self.fields['_tmpItem'] = item

751

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

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

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

755 answer += b'\xdd' * pad

756 if dataClass is None:

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

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

759 each = bytes([each])

760 answer += pack(item, each)

761 else:

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

763

764 if dataClass is not None:

765 for each in self.fields[fieldName]:

766 if isinstance(each, NDRCONSTRUCTEDTYPE):

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

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

769

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

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

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

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

774 else:

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

776

777 return answer

778 else:

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

780

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

782 offset0 = offset

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

784 try:

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

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

787 # we have to align it

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

789

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

791 offset += size

792 except Exception as e:

793 LOG.error(str(e))

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

795 raise

796 return offset - offset0

797

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

799 # array specifier

800 two = fieldTypeOrClass.split('*')

801 answer = []

802 soFarItems = 0

803 offset0 = offset

804 if len(two) == 2:

805 if isinstance(self, NDRUniConformantArray):

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

807 numItems = self.getArraySize()

808 elif isinstance(self, NDRUniConformantVaryingArray):

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

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

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

812 numItems = self[two[1]]

813 else:

814 numItems = self[two[1]]

815

816 # The item type is determined by self.item

817 if self.isNDR(self.item):

818 item = ':'

819 dataClassOrCode = self.item

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

821 else:

822 item = self.item

823 dataClassOrCode = None

824 self.fields['_tmpItem'] = item

825

826 nsofar = 0

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

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

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

830 soFarItems +=pad

831 if dataClassOrCode is None:

832 nsofar = soFarItems + calcsize(item)

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

834 else:

835 itemn = dataClassOrCode(isNDR64=self._isNDR64)

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

837 answer.append(itemn)

838 nsofar += size + pad

839 numItems -= 1

840 soFarItems = nsofar

841

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

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

844 answer2 = []

845 for itemn in answer:

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

847 soFarItems += size

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

849 soFarItems += size

850 answer2.append(itemn)

851 answer = answer2

852 del answer2

853

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

855

856 self.fields[fieldName] = answer

857 return soFarItems + offset - offset0

858 else:

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

860

861 class NDRUniFixedArray(NDRArray):

862 structure = (

863 ('Data',':'),

864 )

865

866# Uni-dimensional Conformant Arrays

867 class NDRUniConformantArray(NDRArray):

868 item = 'c'

869 structure = (

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

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

872 )

873

874 structure64 = (

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

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

877 )

878

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

880 NDRArray.__init__(self, data, isNDR64)

881 # Let's store the hidden MaximumCount field

882 self.fields['MaximumCount'] = 0

883

884 def __setitem__(self, key, value):

885 self.fields['MaximumCount'] = None

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

887

888

889# Uni-dimensional Varying Arrays

890 class NDRUniVaryingArray(NDRArray):

891 item = 'c'

892 structure = (

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

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

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

896 )

897 structure64 = (

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

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

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

901 )

902

903 def __setitem__(self, key, value):

904 self.fields['ActualCount'] = None

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

906

907# Uni-dimensional Conformant-varying Arrays

908 class NDRUniConformantVaryingArray(NDRArray):

909 item = 'c'

910 commonHdr = (

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

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

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

914 )

915 commonHdr64 = (

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

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

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

919 )

920

921 structure = (

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

923 )

924

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

926 NDRArray.__init__(self, data, isNDR64)

927 # Let's store the hidden MaximumCount field

928 self.fields['MaximumCount'] = 0

929

930 def __setitem__(self, key, value):

931 self.fields['MaximumCount'] = None

932 self.fields['ActualCount'] = None

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

934

935 def getData(self, soFar = 0):

936 data = b''

937 soFar0 = soFar

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

939 try:

940 pad = self.calculatePad(fieldTypeOrClass, soFar)

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

942 soFar += pad

943 data += b'\xcb'*pad

944

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

946 data += res

947 soFar = soFar0 + len(data)

948 except Exception as e:

949 LOG.error(str(e))

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

951 raise

952

953 return data

954

955# Multidimensional arrays not implemented for now

956

957# Varying Strings

958 class NDRVaryingString(NDRUniVaryingArray):

959 def getData(self, soFar = 0):

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

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

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

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

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

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

966 else:

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

968 return NDRUniVaryingArray.getData(self, soFar)

969

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

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

972 # Let's take out the last item

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

974 return ret

975

976# Conformant and Varying Strings

977 class NDRConformantVaryingString(NDRUniConformantVaryingArray):

978 pass

979

980# Structures

981# Structures Containing a Conformant Array

982# Structures Containing a Conformant and Varying Array

983 class NDRSTRUCT(NDRCONSTRUCTEDTYPE):

984 def getData(self, soFar = 0):

985 data = b''

986 arrayPadding = b''

987 soFar0 = soFar

988 # 14.3.7.1 Structures Containing a Conformant Array

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

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

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

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

993 # the end of the structure.

994 # 14.3.7.2 Structures Containing a Conformant and Varying Array

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

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

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

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

999 # immediately preceding the array elements

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

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

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

1003 # will need to build it later.

1004 if self._isNDR64:

1005 arrayItemSize = 8

1006 arrayPackStr = '<Q'

1007 else:

1008 arrayItemSize = 4

1009 arrayPackStr = '<L'

1010

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

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

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

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

1015 # independently of the structure alignment.

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

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

1018 if pad0 > 0:

1019 soFar += pad0

1020 arrayPadding = b'\xee'*pad0

1021 else:

1022 arrayPadding = b''

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

1024 soFar += arrayItemSize

1025 else:

1026 arrayItemSize = 0

1027

1028 # Now we need to align the structure

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

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

1031 # recursively to nested constructed types.

1032 alignment = self.getAlignment()

1033

1034 if alignment > 0:

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

1036 if pad > 0:

1037 soFar += pad

1038 data += b'\xAB'*pad

1039

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

1041 try:

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

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

1044 if isinstance(self, NDRPOINTER):

1045 pointerData = data[:arrayItemSize]

1046 data = data[arrayItemSize:]

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

1048 else:

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

1050 arrayPadding = b''

1051 arrayItemSize = 0

1052 else:

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

1054 data += res

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

1056 except Exception as e:

1057 LOG.error(str(e))

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

1059 raise

1060

1061 # 2.2.5.3.4.1 Structure with Trailing Gap

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

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

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

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

1066 # See the following figure.

1067

1068 # 4.8 Example of Structure with Trailing Gap in NDR64

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

1070 # typedef struct _StructWithPad

1071 # {

1072 # long l;

1073 # short s;

1074 # } StructWithPad;

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

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

1077# if self._isNDR64 is True:

1078# # ToDo add trailing gap here

1079# if alignment > 0:

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

1081# if pad > 0:

1082# soFar += pad

1083# data += '\xcd'*pad

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

1085 return data

1086

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

1088 offset0 = offset

1089 # 14.3.7.1 Structures Containing a Conformant Array

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

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

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

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

1094 # the end of the structure.

1095 # 14.3.7.2 Structures Containing a Conformant and Varying Array

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

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

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

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

1100 # immediately preceding the array elements

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

1102

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

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

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

1106 if isinstance(self, NDRPOINTER):

1107 structureFields = self.structure

1108

1109 alignment = self.getAlignment()

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

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

1112

1113 for fieldName, fieldTypeOrClass in self.commonHdr:

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

1115 else:

1116 structureFields = self.commonHdr+self.structure

1117

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

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

1120 # will need to build it later.

1121 if self._isNDR64:

1122 arrayItemSize = 8

1123 arrayUnPackStr = '<Q'

1124 else:

1125 arrayItemSize = 4

1126 arrayUnPackStr = '<L'

1127

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

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

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

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

1132 # independently of the structure alignment.

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

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

1135

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

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

1138 # NDRUniConformantArray

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

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

1141 else:

1142 # NDRUniConformantVaryingArray

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

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

1145

1146 offset += arrayItemSize

1147

1148 # Now we need to align the structure

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

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

1151 # recursively to nested constructed types.

1152 alignment = self.getAlignment()

1153 if alignment > 0:

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

1155

1156 for fieldName, fieldTypeOrClass in structureFields:

1157 try:

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

1159 except Exception as e:

1160 LOG.error(str(e))

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

1162 raise

1163

1164 return offset - offset0

1165

1166 def getAlignment(self):

1167 # Alignment of Constructed Types

1168 #

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

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

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

1172 # on an NDR octet stream index of n.

1173

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

1175

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

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

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

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

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

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

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

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

1184 # and is aligned independently of the structure alignment.

1185

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

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

1188 # constructed types.

1189

1190 align = 0

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

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

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

1194 else:

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

1196 if tmpAlign > align:

1197 align = tmpAlign

1198 return align

1199

1200# Unions

1201 class NDRUNION(NDRCONSTRUCTEDTYPE):

1202 commonHdr = (

1203 ('tag', NDRUSHORT),

1204 )

1205 commonHdr64 = (

1206 ('tag', NDRULONG),

1207 )

1208

1209 union = {

1210 # For example

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

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

1213 }

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

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

1216 self.topLevel = topLevel

1217 self._isNDR64 = isNDR64

1218 self.fields = {}

1219

1220 if isNDR64 is True:

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

1222 self.commonHdr = self.commonHdr64

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

1224 self.structure = self.structure64

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

1226 self.align = self.align64

1227

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

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

1230 if self.isPointer(fieldTypeOrClass):

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

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

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

1234 else:

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

1236 elif fieldTypeOrClass == ':':

1237 self.fields[fieldName] = None

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

1239 try:

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

1241 except:

1242 self.fields[fieldName] = None

1243 else:

1244 self.fields[fieldName] = 0

1245

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

1247 self.fromString(data)

1248

1249 def __setitem__(self, key, value):

1250 if key == 'tag':

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

1252 self.structure = ()

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

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

1255 # Init again the structure

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

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

1258 else:

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

1260 if 'default' in self.union:

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

1262 self.structure = ()

1263 else:

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

1265 # Init again the structure

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

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

1268 else:

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

1270 else:

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

1272

1273 def getData(self, soFar = 0):

1274 data = b''

1275 soFar0 = soFar

1276

1277 # Let's align ourselves

1278 alignment = self.getAlignment()

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

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

1281 else:

1282 pad = 0

1283 if pad > 0:

1284 soFar += pad

1285 data += b'\xbc'*pad

1286

1287 for fieldName, fieldTypeOrClass in self.commonHdr:

1288 try:

1289 pad = self.calculatePad(fieldTypeOrClass, soFar)

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

1291 soFar += pad

1292 data += b'\xbb'*pad

1293

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

1295 data += res

1296 soFar = soFar0 + len(data)

1297 except Exception as e:

1298 LOG.error(str(e))

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

1300 raise

1301

1302 # WARNING

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

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

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

1306 # a DWORD, or HYPER if NDR64.

1307 if self._isNDR64:

1308 align = 8

1309 else:

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

1311 align = 1

1312 else:

1313 align = 4

1314

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

1316 if pad > 0:

1317 data += b'\xbd'*pad

1318 soFar += pad

1319

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

1321 return data

1322

1323 for fieldName, fieldTypeOrClass in self.structure:

1324 try:

1325 pad = self.calculatePad(fieldTypeOrClass, soFar)

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

1327 soFar += pad

1328 data += b'\xbe'*pad

1329

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

1331 data += res

1332 soFar = soFar0 + len(data)

1333 except Exception as e:

1334 LOG.error(str(e))

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

1336 raise

1337

1338 return data

1339

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

1341 offset0 = offset

1342 # Let's align ourselves

1343 alignment = self.getAlignment()

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

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

1346 else:

1347 pad = 0

1348 if pad > 0:

1349 offset += pad

1350

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

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

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

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

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

1356 if tag in self.union:

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

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

1359 else:

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

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

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

1363 self.structure = ()

1364 else:

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

1366 # Init again the structure

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

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

1369 else:

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

1371

1372 for fieldName, fieldTypeOrClass in self.commonHdr:

1373 try:

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

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

1376 except Exception as e:

1377 LOG.error(str(e))

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

1379 raise

1380

1381 # WARNING

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

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

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

1385 # a DWORD, or HYPER if NDR64.

1386 if self._isNDR64:

1387 align = 8

1388 else:

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

1390 align = 1

1391 else:

1392 align = 4

1393

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

1395

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

1397 return offset-offset0

1398

1399 for fieldName, fieldTypeOrClass in self.structure:

1400 try:

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

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

1403 except Exception as e:

1404 LOG.error(str(e))

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

1406 raise

1407

1408 return offset - offset0

1409

1410 def getAlignment(self):

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

1412 # and all of the union arms.

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

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

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

1416 align = 0

1417 if self._isNDR64:

1418 fields = self.commonHdr+self.structure

1419 else:

1420 fields = self.commonHdr

1421 for fieldName, fieldTypeOrClass in fields:

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

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

1424 else:

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

1426 if tmpAlign > align:

1427 align = tmpAlign

1428

1429 if self._isNDR64:

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

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

1432 if tmpAlign > align:

1433 align = tmpAlign

1434 return align

1435

1436# Pipes not implemented for now

1437

1438# Pointers

1439 class NDRPOINTERNULL(NDR):

1440 align = 4

1441 align64 = 8

1442 structure = (

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

1444 )

1445 structure64 = (

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

1447 )

1448

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

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

1451 msg = self.__class__.__name__

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

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

1454 # Here we just print NULL

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

1456

1457 NULL = NDRPOINTERNULL()

1458

1459 class NDRPOINTER(NDRSTRUCT):

1460 align = 4

1461 align64 = 8

1462 commonHdr = (

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

1464 )

1465 commonHdr64 = (

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

1467 )

1468

1469 referent = (

1470 # This is the representation of the Referent

1471 ('Data',':'),

1472 )

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

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

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

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

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

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

1479 # Not in the structure one!

1480 if topLevel is True:

1481 self.structure = self.referent

1482 self.referent = ()

1483

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

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

1486 else:

1487 self.fromString(data)

1488

1489 def __setitem__(self, key, value):

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

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

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

1493 else:

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

1495

1496 def __getitem__(self, key):

1497 if key in self.fields:

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

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

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

1501 return self.fields[key]

1502 else:

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

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

1505

1506 def getData(self, soFar = 0):

1507 # First of all we need to align ourselves

1508 data = b''

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

1510 if pad > 0:

1511 soFar += pad

1512 data = b'\xaa'*pad

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

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

1515 if len(self.referent) > 0:

1516 self['Data'] = b''

1517 else:

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

1519 return data+b'\x00'*8

1520 else:

1521 return data+b'\x00'*4

1522

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

1524

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

1526 # First of all we need to align ourselves

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

1528 offset += pad

1529

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

1531 if self._isNDR64 is True:

1532 unpackStr = '<Q'

1533 else:

1534 unpackStr = '<L'

1535

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

1537 # Let's save the value

1538 self['ReferentID'] = 0

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

1540 if self._isNDR64 is True:

1541 return pad + 8

1542 else:

1543 return pad + 4

1544 else:

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

1546 return retVal + pad

1547

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

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

1550 msg = self.__class__.__name__

1551 if msg != '':

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

1553 # Here we just print the referent

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

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

1556 else:

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

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

1559 else:

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

1561

1562 def getAlignment(self):

1563 if self._isNDR64 is True:

1564 return 8

1565 else:

1566 return 4

1567

1568

1569# Embedded Reference Pointers not implemented for now

1570

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

1572# Common RPC Data Types

1573

1574 class PNDRUniConformantVaryingArray(NDRPOINTER):

1575 referent = (

1576 ('Data', NDRUniConformantVaryingArray),

1577 )

1578

1579 class PNDRUniConformantArray(NDRPOINTER):

1580 referent = (

1581 ('Data', NDRUniConformantArray),

1582 )

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

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

1585

1586 class NDRCALL(NDRCONSTRUCTEDTYPE):

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

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

1589 # represent the referents when building the final octet stream

1590 referent = ()

1591 commonHdr = ()

1592 commonHdr64 = ()

1593 structure = ()

1594 structure64 = ()

1595 align = 4

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

1597 self._isNDR64 = isNDR64

1598 self.fields = {}

1599

1600 if isNDR64 is True:

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

1602 self.commonHdr = self.commonHdr64

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

1604 self.structure = self.structure64

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

1606 self.align = self.align64

1607

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

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

1610 if self.isPointer(fieldTypeOrClass):

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

1612 elif self.isUnion(fieldTypeOrClass):

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

1614 else:

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

1616 elif fieldTypeOrClass == ':':

1617 self.fields[fieldName] = None

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

1619 try:

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

1621 except:

1622 self.fields[fieldName] = None

1623 else:

1624 self.fields[fieldName] = 0

1625

1626 if data is not None:

1627 self.fromString(data)

1628

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

1630 NDRCONSTRUCTEDTYPE.dump(self, msg, indent)

1631 print('\n\n')

1632

1633 def getData(self, soFar = 0):

1634 data = b''

1635 soFar0 = soFar

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

1637 try:

1638 pad = self.calculatePad(fieldTypeOrClass, soFar)

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

1640 soFar += pad

1641 data += b'\xab'*pad

1642

1643 # Are we dealing with an array?

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

1645 NDRUniConformantVaryingArray):

1646 # Align size item

1647 if self._isNDR64:

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

1649 else:

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

1651 # Pack the item

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

1653 # Yes, get the array size

1654 arraySize = self.getArrayMaximumSize(fieldName)

1655 if self._isNDR64:

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

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

1658 else:

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

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

1661 else:

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

1663

1664 soFar = soFar0 + len(data)

1665 # Any referent information to pack?

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

1667 # all the fields at the call level.

1668 # Guess we'll figure it out testing.

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

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

1671 soFar = soFar0 + len(data)

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

1673 soFar = soFar0 + len(data)

1674 except Exception as e:

1675 LOG.error(str(e))

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

1677 raise

1678

1679 return data

1680

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

1682 offset0 = offset

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

1684 try:

1685 # Are we dealing with an array?

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

1687 NDRUniConformantVaryingArray):

1688 # Yes, get the array size

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

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

1691 offset += advanceStream

1692

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

1694

1695 # Any referent information to unpack?

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

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

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

1699 offset += size

1700 except Exception as e:

1701 LOG.error(str(e))

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

1703 raise

1704

1705 return offset - offset0

1706

1707# Top Level Struct == NDRCALL

1708 NDRTLSTRUCT = NDRCALL

1709

1710 class UNKNOWNDATA(NDR):

1711 align = 1

1712 structure = (

1713 ('Data', ':'),

1714 )