Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# Impacket - Collection of Python classes for working with network protocols. 

2# 

3# SECUREAUTH LABS. Copyright (C) 2020 SecureAuth Corporation. All rights reserved. 

4# 

5# This software is provided under a slightly modified version 

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

7# for more information. 

8# 

9 

10from __future__ import division 

11from __future__ import print_function 

12from struct import pack, unpack, calcsize 

13from six import b, PY3 

14 

15class Structure: 

16 """ sublcasses can define commonHdr and/or structure. 

17 each of them is an tuple of either two: (fieldName, format) or three: (fieldName, ':', class) fields. 

18 [it can't be a dictionary, because order is important] 

19  

20 where format specifies how the data in the field will be converted to/from bytes (string) 

21 class is the class to use when unpacking ':' fields. 

22 

23 each field can only contain one value (or an array of values for *) 

24 i.e. struct.pack('Hl',1,2) is valid, but format specifier 'Hl' is not (you must use 2 dfferent fields) 

25 

26 format specifiers: 

27 specifiers from module pack can be used with the same format  

28 see struct.__doc__ (pack/unpack is finally called) 

29 x [padding byte] 

30 c [character] 

31 b [signed byte] 

32 B [unsigned byte] 

33 h [signed short] 

34 H [unsigned short] 

35 l [signed long] 

36 L [unsigned long] 

37 i [signed integer] 

38 I [unsigned integer] 

39 q [signed long long (quad)] 

40 Q [unsigned long long (quad)] 

41 s [string (array of chars), must be preceded with length in format specifier, padded with zeros] 

42 p [pascal string (includes byte count), must be preceded with length in format specifier, padded with zeros] 

43 f [float] 

44 d [double] 

45 = [native byte ordering, size and alignment] 

46 @ [native byte ordering, standard size and alignment] 

47 ! [network byte ordering] 

48 < [little endian] 

49 > [big endian] 

50 

51 usual printf like specifiers can be used (if started with %)  

52 [not recommended, there is no way to unpack this] 

53 

54 %08x will output an 8 bytes hex 

55 %s will output a string 

56 %s\\x00 will output a NUL terminated string 

57 %d%d will output 2 decimal digits (against the very same specification of Structure) 

58 ... 

59 

60 some additional format specifiers: 

61 : just copy the bytes from the field into the output string (input may be string, other structure, or anything responding to __str__()) (for unpacking, all what's left is returned) 

62 z same as :, but adds a NUL byte at the end (asciiz) (for unpacking the first NUL byte is used as terminator) [asciiz string] 

63 u same as z, but adds two NUL bytes at the end (after padding to an even size with NULs). (same for unpacking) [unicode string] 

64 w DCE-RPC/NDR string (it's a macro for [ '<L=(len(field)+1)/2','"\\x00\\x00\\x00\\x00','<L=(len(field)+1)/2',':' ] 

65 ?-field length of field named 'field', formatted as specified with ? ('?' may be '!H' for example). The input value overrides the real length 

66 ?1*?2 array of elements. Each formatted as '?2', the number of elements in the array is stored as specified by '?1' (?1 is optional, or can also be a constant (number), for unpacking) 

67 'xxxx literal xxxx (field's value doesn't change the output. quotes must not be closed or escaped) 

68 "xxxx literal xxxx (field's value doesn't change the output. quotes must not be closed or escaped) 

69 _ will not pack the field. Accepts a third argument, which is an unpack code. See _Test_UnpackCode for an example 

70 ?=packcode will evaluate packcode in the context of the structure, and pack the result as specified by ?. Unpacking is made plain 

71 ?&fieldname "Address of field fieldname". 

72 For packing it will simply pack the id() of fieldname. Or use 0 if fieldname doesn't exists. 

73 For unpacking, it's used to know weather fieldname has to be unpacked or not, i.e. by adding a & field you turn another field (fieldname) in an optional field. 

74  

75 """ 

76 commonHdr = () 

77 structure = () 

78 debug = 0 

79 

80 def __init__(self, data = None, alignment = 0): 

81 if not hasattr(self, 'alignment'): 81 ↛ 84line 81 didn't jump to line 84, because the condition on line 81 was never false

82 self.alignment = alignment 

83 

84 self.fields = {} 

85 self.rawData = data 

86 if data is not None: 

87 self.fromString(data) 

88 else: 

89 self.data = None 

90 

91 @classmethod 

92 def fromFile(self, file): 

93 answer = self() 

94 answer.fromString(file.read(len(answer))) 

95 return answer 

96 

97 def setAlignment(self, alignment): 

98 self.alignment = alignment 

99 

100 def setData(self, data): 

101 self.data = data 

102 

103 def packField(self, fieldName, format = None): 

104 if self.debug: 104 ↛ 105line 104 didn't jump to line 105, because the condition on line 104 was never true

105 print("packField( %s | %s )" % (fieldName, format)) 

106 

107 if format is None: 107 ↛ 108line 107 didn't jump to line 108, because the condition on line 107 was never true

108 format = self.formatForField(fieldName) 

109 

110 if fieldName in self.fields: 

111 ans = self.pack(format, self.fields[fieldName], field = fieldName) 

112 else: 

113 ans = self.pack(format, None, field = fieldName) 

114 

115 if self.debug: 115 ↛ 116line 115 didn't jump to line 116, because the condition on line 115 was never true

116 print("\tanswer %r" % ans) 

117 

118 return ans 

119 

120 def getData(self): 

121 if self.data is not None: 121 ↛ 122line 121 didn't jump to line 122, because the condition on line 121 was never true

122 return self.data 

123 data = bytes() 

124 for field in self.commonHdr+self.structure: 

125 try: 

126 data += self.packField(field[0], field[1]) 

127 except Exception as e: 

128 if field[0] in self.fields: 

129 e.args += ("When packing field '%s | %s | %r' in %s" % (field[0], field[1], self[field[0]], self.__class__),) 

130 else: 

131 e.args += ("When packing field '%s | %s' in %s" % (field[0], field[1], self.__class__),) 

132 raise 

133 if self.alignment: 

134 if len(data) % self.alignment: 

135 data += (b'\x00'*self.alignment)[:-(len(data) % self.alignment)] 

136 

137 #if len(data) % self.alignment: data += ('\x00'*self.alignment)[:-(len(data) % self.alignment)] 

138 return data 

139 

140 def fromString(self, data): 

141 self.rawData = data 

142 for field in self.commonHdr+self.structure: 

143 if self.debug: 143 ↛ 144line 143 didn't jump to line 144, because the condition on line 143 was never true

144 print("fromString( %s | %s | %r )" % (field[0], field[1], data)) 

145 size = self.calcUnpackSize(field[1], data, field[0]) 

146 if self.debug: 146 ↛ 147line 146 didn't jump to line 147, because the condition on line 146 was never true

147 print(" size = %d" % size) 

148 dataClassOrCode = b 

149 if len(field) > 2: 

150 dataClassOrCode = field[2] 

151 try: 

152 self[field[0]] = self.unpack(field[1], data[:size], dataClassOrCode = dataClassOrCode, field = field[0]) 

153 except Exception as e: 

154 e.args += ("When unpacking field '%s | %s | %r[:%d]'" % (field[0], field[1], data, size),) 

155 raise 

156 

157 size = self.calcPackSize(field[1], self[field[0]], field[0]) 

158 if self.alignment and size % self.alignment: 

159 size += self.alignment - (size % self.alignment) 

160 data = data[size:] 

161 

162 return self 

163 

164 def __setitem__(self, key, value): 

165 self.fields[key] = value 

166 self.data = None # force recompute 

167 

168 def __getitem__(self, key): 

169 return self.fields[key] 

170 

171 def __delitem__(self, key): 

172 del self.fields[key] 

173 

174 def __str__(self): 

175 return self.getData() 

176 

177 def __len__(self): 

178 # XXX: improve 

179 return len(self.getData()) 

180 

181 def pack(self, format, data, field = None): 

182 if self.debug: 182 ↛ 183line 182 didn't jump to line 183, because the condition on line 182 was never true

183 print(" pack( %s | %r | %s)" % (format, data, field)) 

184 

185 if field: 

186 addressField = self.findAddressFieldFor(field) 

187 if (addressField is not None) and (data is None): 

188 return b'' 

189 

190 # void specifier 

191 if format[:1] == '_': 

192 return b'' 

193 

194 # quote specifier 

195 if format[:1] == "'" or format[:1] == '"': 

196 return b(format[1:]) 

197 

198 # code specifier 

199 two = format.split('=') 

200 if len(two) >= 2: 

201 try: 

202 return self.pack(two[0], data) 

203 except: 

204 fields = {'self':self} 

205 fields.update(self.fields) 

206 return self.pack(two[0], eval(two[1], {}, fields)) 

207 

208 # address specifier 

209 two = format.split('&') 

210 if len(two) == 2: 

211 try: 

212 return self.pack(two[0], data) 

213 except: 

214 if (two[1] in self.fields) and (self[two[1]] is not None): 

215 return self.pack(two[0], id(self[two[1]]) & ((1<<(calcsize(two[0])*8))-1) ) 

216 else: 

217 return self.pack(two[0], 0) 

218 

219 # length specifier 

220 two = format.split('-') 

221 if len(two) == 2: 

222 try: 

223 return self.pack(two[0],data) 

224 except: 

225 return self.pack(two[0], self.calcPackFieldSize(two[1])) 

226 

227 # array specifier 

228 two = format.split('*') 

229 if len(two) == 2: 

230 answer = bytes() 

231 for each in data: 

232 answer += self.pack(two[1], each) 

233 if two[0]: 

234 if two[0].isdigit(): 234 ↛ 235line 234 didn't jump to line 235, because the condition on line 234 was never true

235 if int(two[0]) != len(data): 

236 raise Exception("Array field has a constant size, and it doesn't match the actual value") 

237 else: 

238 return self.pack(two[0], len(data))+answer 

239 return answer 

240 

241 # "printf" string specifier 

242 if format[:1] == '%': 242 ↛ 244line 242 didn't jump to line 244, because the condition on line 242 was never true

243 # format string like specifier 

244 return b(format % data) 

245 

246 # asciiz specifier 

247 if format[:1] == 'z': 

248 if isinstance(data,bytes): 248 ↛ 250line 248 didn't jump to line 250, because the condition on line 248 was never false

249 return data + b('\0') 

250 return bytes(b(data)+b('\0')) 

251 

252 # unicode specifier 

253 if format[:1] == 'u': 

254 return bytes(data+b('\0\0') + (len(data) & 1 and b('\0') or b'')) 

255 

256 # DCE-RPC/NDR string specifier 

257 if format[:1] == 'w': 

258 if len(data) == 0: 258 ↛ 259line 258 didn't jump to line 259, because the condition on line 258 was never true

259 data = b('\0\0') 

260 elif len(data) % 2: 

261 data = b(data) + b('\0') 

262 l = pack('<L', len(data)//2) 

263 return b''.join([l, l, b('\0\0\0\0'), data]) 

264 

265 if data is None: 

266 raise Exception("Trying to pack None") 

267 

268 # literal specifier 

269 if format[:1] == ':': 

270 if isinstance(data, Structure): 

271 return data.getData() 

272 # If we have an object that can serialize itself, go ahead 

273 elif hasattr(data, "getData"): 273 ↛ 274line 273 didn't jump to line 274, because the condition on line 273 was never true

274 return data.getData() 

275 elif isinstance(data, int): 275 ↛ 276line 275 didn't jump to line 276, because the condition on line 275 was never true

276 return bytes(data) 

277 elif isinstance(data, bytes) is not True: 277 ↛ 278line 277 didn't jump to line 278, because the condition on line 277 was never true

278 return bytes(b(data)) 

279 else: 

280 return data 

281 

282 if format[-1:] == 's': 

283 # Let's be sure we send the right type 

284 if isinstance(data, bytes) or isinstance(data, bytearray): 284 ↛ 287line 284 didn't jump to line 287, because the condition on line 284 was never false

285 return pack(format, data) 

286 else: 

287 return pack(format, b(data)) 

288 

289 # struct like specifier 

290 return pack(format, data) 

291 

292 def unpack(self, format, data, dataClassOrCode = b, field = None): 

293 if self.debug: 293 ↛ 294line 293 didn't jump to line 294, because the condition on line 293 was never true

294 print(" unpack( %s | %r )" % (format, data)) 

295 

296 if field: 

297 addressField = self.findAddressFieldFor(field) 

298 if addressField is not None: 

299 if not self[addressField]: 

300 return 

301 

302 # void specifier 

303 if format[:1] == '_': 

304 if dataClassOrCode != b: 

305 fields = {'self':self, 'inputDataLeft':data} 

306 fields.update(self.fields) 

307 return eval(dataClassOrCode, {}, fields) 

308 else: 

309 return None 

310 

311 # quote specifier 

312 if format[:1] == "'" or format[:1] == '"': 

313 answer = format[1:] 

314 if b(answer) != data: 314 ↛ 315line 314 didn't jump to line 315, because the condition on line 314 was never true

315 raise Exception("Unpacked data doesn't match constant value '%r' should be '%r'" % (data, answer)) 

316 return answer 

317 

318 # address specifier 

319 two = format.split('&') 

320 if len(two) == 2: 

321 return self.unpack(two[0],data) 

322 

323 # code specifier 

324 two = format.split('=') 

325 if len(two) >= 2: 

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

327 

328 # length specifier 

329 two = format.split('-') 

330 if len(two) == 2: 

331 return self.unpack(two[0],data) 

332 

333 # array specifier 

334 two = format.split('*') 

335 if len(two) == 2: 

336 answer = [] 

337 sofar = 0 

338 if two[0].isdigit(): 338 ↛ 339line 338 didn't jump to line 339, because the condition on line 338 was never true

339 number = int(two[0]) 

340 elif two[0]: 

341 sofar += self.calcUnpackSize(two[0], data) 

342 number = self.unpack(two[0], data[:sofar]) 

343 else: 

344 number = -1 

345 

346 while number and sofar < len(data): 

347 nsofar = sofar + self.calcUnpackSize(two[1],data[sofar:]) 

348 answer.append(self.unpack(two[1], data[sofar:nsofar], dataClassOrCode)) 

349 number -= 1 

350 sofar = nsofar 

351 return answer 

352 

353 # "printf" string specifier 

354 if format[:1] == '%': 354 ↛ 356line 354 didn't jump to line 356, because the condition on line 354 was never true

355 # format string like specifier 

356 return format % data 

357 

358 # asciiz specifier 

359 if format == 'z': 

360 if data[-1:] != b('\x00'): 

361 raise Exception("%s 'z' field is not NUL terminated: %r" % (field, data)) 

362 if PY3: 362 ↛ 363line 362 didn't jump to line 363, because the condition on line 362 was never true

363 return data[:-1].decode('latin-1') 

364 else: 

365 return data[:-1] 

366 

367 # unicode specifier 

368 if format == 'u': 

369 if data[-2:] != b('\x00\x00'): 369 ↛ 370line 369 didn't jump to line 370, because the condition on line 369 was never true

370 raise Exception("%s 'u' field is not NUL-NUL terminated: %r" % (field, data)) 

371 return data[:-2] # remove trailing NUL 

372 

373 # DCE-RPC/NDR string specifier 

374 if format == 'w': 

375 l = unpack('<L', data[:4])[0] 

376 return data[12:12+l*2] 

377 

378 # literal specifier 

379 if format == ':': 

380 if isinstance(data, bytes) and dataClassOrCode is b: 

381 return data 

382 return dataClassOrCode(data) 

383 

384 # struct like specifier 

385 return unpack(format, data)[0] 

386 

387 def calcPackSize(self, format, data, field = None): 

388# # print " calcPackSize %s:%r" % (format, data) 

389 if field: 

390 addressField = self.findAddressFieldFor(field) 

391 if addressField is not None: 

392 if not self[addressField]: 

393 return 0 

394 

395 # void specifier 

396 if format[:1] == '_': 

397 return 0 

398 

399 # quote specifier 

400 if format[:1] == "'" or format[:1] == '"': 

401 return len(format)-1 

402 

403 # address specifier 

404 two = format.split('&') 

405 if len(two) == 2: 

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

407 

408 # code specifier 

409 two = format.split('=') 

410 if len(two) >= 2: 

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

412 

413 # length specifier 

414 two = format.split('-') 

415 if len(two) == 2: 

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

417 

418 # array specifier 

419 two = format.split('*') 

420 if len(two) == 2: 

421 answer = 0 

422 if two[0].isdigit(): 422 ↛ 423line 422 didn't jump to line 423, because the condition on line 422 was never true

423 if int(two[0]) != len(data): 

424 raise Exception("Array field has a constant size, and it doesn't match the actual value") 

425 elif two[0]: 

426 answer += self.calcPackSize(two[0], len(data)) 

427 

428 for each in data: 

429 answer += self.calcPackSize(two[1], each) 

430 return answer 

431 

432 # "printf" string specifier 

433 if format[:1] == '%': 433 ↛ 435line 433 didn't jump to line 435, because the condition on line 433 was never true

434 # format string like specifier 

435 return len(format % data) 

436 

437 # asciiz specifier 

438 if format[:1] == 'z': 

439 return len(data)+1 

440 

441 # asciiz specifier 

442 if format[:1] == 'u': 

443 l = len(data) 

444 return l + (l & 1 and 3 or 2) 

445 

446 # DCE-RPC/NDR string specifier 

447 if format[:1] == 'w': 

448 l = len(data) 

449 return 12+l+l % 2 

450 

451 # literal specifier 

452 if format[:1] == ':': 

453 return len(data) 

454 

455 # struct like specifier 

456 return calcsize(format) 

457 

458 def calcUnpackSize(self, format, data, field = None): 

459 if self.debug: 459 ↛ 460line 459 didn't jump to line 460, because the condition on line 459 was never true

460 print(" calcUnpackSize( %s | %s | %r)" % (field, format, data)) 

461 

462 # void specifier 

463 if format[:1] == '_': 

464 return 0 

465 

466 addressField = self.findAddressFieldFor(field) 

467 if addressField is not None: 

468 if not self[addressField]: 

469 return 0 

470 

471 try: 

472 lengthField = self.findLengthFieldFor(field) 

473 return int(self[lengthField]) 

474 except Exception: 

475 pass 

476 

477 # XXX: Try to match to actual values, raise if no match 

478 

479 # quote specifier 

480 if format[:1] == "'" or format[:1] == '"': 

481 return len(format)-1 

482 

483 # address specifier 

484 two = format.split('&') 

485 if len(two) == 2: 

486 return self.calcUnpackSize(two[0], data) 

487 

488 # code specifier 

489 two = format.split('=') 

490 if len(two) >= 2: 

491 return self.calcUnpackSize(two[0], data) 

492 

493 # length specifier 

494 two = format.split('-') 

495 if len(two) == 2: 

496 return self.calcUnpackSize(two[0], data) 

497 

498 # array specifier 

499 two = format.split('*') 

500 if len(two) == 2: 

501 answer = 0 

502 if two[0]: 

503 if two[0].isdigit(): 503 ↛ 504line 503 didn't jump to line 504, because the condition on line 503 was never true

504 number = int(two[0]) 

505 else: 

506 answer += self.calcUnpackSize(two[0], data) 

507 number = self.unpack(two[0], data[:answer]) 

508 

509 while number: 

510 number -= 1 

511 answer += self.calcUnpackSize(two[1], data[answer:]) 

512 else: 

513 while answer < len(data): 

514 answer += self.calcUnpackSize(two[1], data[answer:]) 

515 return answer 

516 

517 # "printf" string specifier 

518 if format[:1] == '%': 518 ↛ 519line 518 didn't jump to line 519, because the condition on line 518 was never true

519 raise Exception("Can't guess the size of a printf like specifier for unpacking") 

520 

521 # asciiz specifier 

522 if format[:1] == 'z': 

523 return data.index(b('\x00'))+1 

524 

525 # asciiz specifier 

526 if format[:1] == 'u': 

527 l = data.index(b('\x00\x00')) 

528 return l + (l & 1 and 3 or 2) 

529 

530 # DCE-RPC/NDR string specifier 

531 if format[:1] == 'w': 

532 l = unpack('<L', data[:4])[0] 

533 return 12+l*2 

534 

535 # literal specifier 

536 if format[:1] == ':': 

537 return len(data) 

538 

539 # struct like specifier 

540 return calcsize(format) 

541 

542 def calcPackFieldSize(self, fieldName, format = None): 

543 if format is None: 543 ↛ 546line 543 didn't jump to line 546, because the condition on line 543 was never false

544 format = self.formatForField(fieldName) 

545 

546 return self.calcPackSize(format, self[fieldName]) 

547 

548 def formatForField(self, fieldName): 

549 for field in self.commonHdr+self.structure: 549 ↛ 552line 549 didn't jump to line 552, because the loop on line 549 didn't complete

550 if field[0] == fieldName: 

551 return field[1] 

552 raise Exception("Field %s not found" % fieldName) 

553 

554 def findAddressFieldFor(self, fieldName): 

555 descriptor = '&%s' % fieldName 

556 l = len(descriptor) 

557 for field in self.commonHdr+self.structure: 

558 if field[1][-l:] == descriptor: 

559 return field[0] 

560 return None 

561 

562 def findLengthFieldFor(self, fieldName): 

563 descriptor = '-%s' % fieldName 

564 l = len(descriptor) 

565 for field in self.commonHdr+self.structure: 

566 if field[1][-l:] == descriptor: 

567 return field[0] 

568 return None 

569 

570 def zeroValue(self, format): 

571 two = format.split('*') 

572 if len(two) == 2: 

573 if two[0].isdigit(): 

574 return (self.zeroValue(two[1]),)*int(two[0]) 

575 

576 if not format.find('*') == -1: 

577 return () 

578 if 's' in format: 

579 return b'' 

580 if format in ['z',':','u']: 

581 return b'' 

582 if format == 'w': 

583 return b('\x00\x00') 

584 

585 return 0 

586 

587 def clear(self): 

588 for field in self.commonHdr + self.structure: 

589 self[field[0]] = self.zeroValue(field[1]) 

590 

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

592 if msg is None: 

593 msg = self.__class__.__name__ 

594 ind = ' '*indent 

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

596 fixedFields = [] 

597 for field in self.commonHdr+self.structure: 

598 i = field[0] 

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

600 fixedFields.append(i) 

601 if isinstance(self[i], Structure): 

602 self[i].dump('%s%s:{' % (ind,i), indent = indent + 4) 

603 print("%s}" % ind) 

604 else: 

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

606 # Do we have remaining fields not defined in the structures? let's  

607 # print them 

608 remainingFields = list(set(self.fields) - set(fixedFields)) 

609 for i in remainingFields: 

610 if isinstance(self[i], Structure): 610 ↛ 611line 610 didn't jump to line 611, because the condition on line 610 was never true

611 self[i].dump('%s%s:{' % (ind,i), indent = indent + 4) 

612 print("%s}" % ind) 

613 else: 

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

615 

616def pretty_print(x): 

617 if chr(x) in '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ': 

618 return chr(x) 

619 else: 

620 return u'.' 

621 

622def hexdump(data, indent = ''): 

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

624 return 

625 if isinstance(data, int): 625 ↛ 626line 625 didn't jump to line 626, because the condition on line 625 was never true

626 data = str(data).encode('utf-8') 

627 x=bytearray(data) 

628 strLen = len(x) 

629 i = 0 

630 while i < strLen: 

631 line = " %s%04x " % (indent, i) 

632 for j in range(16): 

633 if i+j < strLen: 

634 line += "%02X " % x[i+j] 

635 else: 

636 line += u" " 

637 if j%16 == 7: 

638 line += " " 

639 line += " " 

640 line += ''.join(pretty_print(x) for x in x[i:i+16] ) 

641 print (line) 

642 i += 16 

643 

644def parse_bitmask(dict, value): 

645 ret = '' 

646 

647 for i in range(0, 31): 

648 flag = 1 << i 

649 

650 if value & flag == 0: 

651 continue 

652 

653 if flag in dict: 

654 ret += '%s | ' % dict[flag] 

655 else: 

656 ret += "0x%.8X | " % flag 

657 

658 if len(ret) == 0: 

659 return '0' 

660 else: 

661 return ret[:-3]