unME11.py 60 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439
  1. #!/usr/bin/env python2
  2. import os, sys, struct, hashlib, platform
  3. import subprocess
  4. try:
  5. import HuffDec11
  6. HuffDecoder11 = HuffDec11.HuffDecoder()
  7. except:
  8. HuffDecoder11 = None
  9. try:
  10. import HuffDec12
  11. HuffDecoder12 = HuffDec12.HuffDecoder()
  12. except:
  13. HuffDecoder12 = None
  14. class Globals(object):
  15. HuffDecoder = HuffDecoder11
  16. dumpManifest = True # Dump CPD manifest
  17. dumpMeta = True # Dump modules metadata
  18. dumpRaw = False # Dump raw modules data (compressed/encrypted)
  19. dumpChunks = False # Dump HUFF chunks
  20. g = Globals()
  21. class Error(Exception): pass
  22. def BitFields(obj, val, bitDef):
  23. def bitf(val, lo, hi): return (val & ((2<<hi)-1)) >> lo
  24. for name, lo, hi in bitDef: setattr(obj, name, bitf(val, lo, hi))
  25. def ListTrueBools(obj, bitDef):
  26. return [v[0] for v in filter(lambda x: x[1] == x[2] and getattr(obj, x[0]), bitDef)]
  27. class StructReader(object):
  28. def __init__(self, ab, base=0, isLE=True):
  29. self.ab = ab
  30. self.base = base
  31. self.o = self.base
  32. self.cE = "<" if isLE else ">"
  33. def sizeLeft(self):
  34. return len(self.ab) - self.o
  35. def getData(self, o, cb):
  36. o += self.base
  37. if o < len(self.ab) and cb >= 0 and o + cb <= len(self.ab):
  38. return self.ab[o:o+cb]
  39. def read(self, obj, stDef, o=None):
  40. if o is None: o = self.o
  41. self.o += self.base
  42. for fldDef in stDef: # Walk field definitions
  43. name = fldDef[0]
  44. fmt = self.cE + fldDef[1]
  45. val, = struct.unpack_from(fmt, self.ab, o)
  46. if 3 == len(fldDef):
  47. expected = fldDef[2]
  48. if isinstance(expected, (list, tuple)):
  49. if not val in expected:
  50. print >>sys.stderr, "- %s.%s: not %s in %s" % (obj.__class__.__name__, name, val, expected)
  51. else:
  52. if val != expected:
  53. print >>sys.stderr, "- %s.%s:" % (obj.__class__.__name__, name),
  54. if isinstance(val, str): print >>sys.stderr, "Got %s, expected %s" % (val.encode("hex"), expected.encode("hex"))
  55. else: print >>sys.stderr, "Got [%s], expected [%s]" % (repr(val), repr(expected))
  56. else: assert val == expected
  57. setattr(obj, name, val)
  58. o += struct.calcsize(fmt)
  59. self.o = o
  60. def done(self):
  61. assert len(self.ab) == self.o
  62. aPubKeyHash = [v.decode("hex") for v in (
  63. "EA6FA86514FA887C9044218EDB4D70BB3BCC7C2D37587EA8F760BAFBE158C587",
  64. "A24E0682EDC8870DCA947C01603D19818AF714BEE9F39D2872D79B8C422F3890",
  65. "EA3E9C34C8FD6BDEA277F0A8C6AC5A37E8E39256469C89D279FA86A7317B21AE",
  66. "C8E7AA2C5F691F63A892BC044CD3935C5E77C6CB71C8E8627BE4987DFB730856",
  67. "3D512A6DB7C855E9F6328DB8B2C259A2C0F291BB6E3EC74A2FB811AD84C5D404",
  68. "04A6F35B14628879050AB0B3459326DDF946AE4E5EFD7BB1930883F57F68D084",
  69. "980F9572AC1B5BDC9A5F3E89F2503A624C9C5BDF97B72D9031DCCDAB11A9F7A8",
  70. "C468E6BA739856797BAF70910861BDE4C3BA95C956B1DCE24B738D614F1211BA",
  71. )]
  72. #***************************************************************************
  73. #***************************************************************************
  74. #***************************************************************************
  75. args_lzma = {
  76. "Windows": ["lzma", "d", "-si", "-so"],
  77. "Linux": ["lzma", "-d"],
  78. "Darwin": ["lzma", "-d"], # "brew install xz" or "sudo port install xz"
  79. }[platform.system()]
  80. def LZMA_decompress(compdata):
  81. process = subprocess.Popen(args_lzma, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  82. output, errout = process.communicate(compdata)
  83. retcode = process.poll()
  84. if retcode: raise Error(errout)
  85. return output
  86. def decompress(data, compType, length):
  87. if compType is None:
  88. return data
  89. elif "lzma" == compType:
  90. if not data.startswith("36004000".decode("hex")):
  91. print >>sys.stderr, "- Bad LZMA[0x%X] header %s" % (len(data), data[:17].encode("hex"))
  92. return None
  93. assert data.startswith("36004000".decode("hex"))
  94. assert '\0\0\0' == data[14:17]
  95. return LZMA_decompress(data[:14] + data[17:])
  96. elif "huff" == compType:
  97. return g.HuffDecoder.decompress(data, length) if g.HuffDecoder else None
  98. else:
  99. raise Error("Invalid compType %s" % compType)
  100. RESTART_NOT_ALLOWED = 0
  101. RESTART_IMMEDIATLY = 1
  102. RESTART_ON_NEXT_BOOT = 2
  103. # MODULE_TYPES
  104. PROCESS_TYPE = 0
  105. SHARED_LIBRARY_TYPE = 1
  106. DATA_TYPE = 2
  107. IUNIT_TYPE = 3 # v12
  108. # PARTITION_TYPES
  109. FPT_AREATYPE_GENERIC = 1
  110. FPT_AREATYPE_CODE = 0
  111. FPT_AREATYPE_ROM = 1
  112. FPT_AREATYPE_DATA = 1
  113. # COMPRESSION_TYPE
  114. COMP_TYPE_NOT_COMPRESSED = 0
  115. COMP_TYPE_HUFFMAN = 1
  116. COMP_TYPE_LZMA = 2
  117. dCompType = {
  118. COMP_TYPE_NOT_COMPRESSED : None,
  119. COMP_TYPE_HUFFMAN: "huff",
  120. COMP_TYPE_LZMA: "lzma",
  121. }
  122. #***************************************************************************
  123. #***************************************************************************
  124. #***************************************************************************
  125. class Extension(object):
  126. NAME = None
  127. TYPE = None
  128. LIST = None
  129. def Banner(self, noSize=False):
  130. nItems = "" if noSize or (self.LIST is None) else "[%d]" % len(getattr(self, self.LIST))
  131. return ". Ext#%d %s%s:" % (self.TYPE, self.NAME, nItems)
  132. def PrintItems(self, flog):
  133. for i,e in enumerate(getattr(self, self.LIST)): print >>flog, "%6d: %s" % (i+1, e)
  134. def LoadItems(self, stR, cls, cnt=None):
  135. if cnt is None:
  136. lst = []
  137. while stR.o < len(stR.ab): lst.append(cls(stR))
  138. else:
  139. lst = [cls(stR) for i in xrange(cnt)]
  140. stR.done()
  141. setattr(self, self.LIST, lst)
  142. #***************************************************************************
  143. #***************************************************************************
  144. #***************************************************************************
  145. #***************************************************************************
  146. #***************************************************************************
  147. #***************************************************************************
  148. class System_Info_Ext(Extension): # 0 : used in Mainfist
  149. NAME = "SystemInfo"
  150. TYPE = 0 # for system info extension
  151. LIST = "indParts"
  152. SYSTEM_INFO_EXTENSION = (
  153. ("uma_size", "L", ), # Minimum UMA size required for this SKU in bytes
  154. ("chipset_version", "L", ), # Chipset version
  155. ("img_default_hash", "32s", ), # SHA2 hash of a 'defaults' file added to the image. (/intel.cfg). The load manager is responsible for verifying the hash of this file and creating the default files at the first system boot.
  156. ("pageable_uma_size", "L", ), # Size of pageable space within UMA in bytes. Must be divisible by 4K.
  157. ("reserved_0", "Q", 0), #
  158. ("reserved_1", "L", 0), #
  159. # INDEPENDENT_PARTITION_ENTRY[]
  160. )
  161. def __init__(self, ab):
  162. stR = StructReader(ab)
  163. stR.read(self, self.SYSTEM_INFO_EXTENSION)
  164. self.img_default_hash = self.img_default_hash[::-1] # Reverse?
  165. self.LoadItems(stR, Independent_Partition_Entry)
  166. def dump(self, flog=sys.stdout):
  167. print >>flog, "%s uma_size:0x%X, chipset_version:0x%X, pageable_uma_size:0x%X defaults_h:%s" % (self.Banner(), self.uma_size, self.chipset_version, self.pageable_uma_size, self.img_default_hash.encode("hex"))
  168. self.PrintItems(flog)
  169. #***************************************************************************
  170. class Independent_Partition_Entry:
  171. INDEPENDENT_PARTITION_ENTRY = (
  172. ("name", "4s", ), #
  173. ("version", "L", ), #
  174. ("user_id", "H", ), #
  175. ("reserved", "H", ), #
  176. )
  177. def __init__(self, stR):
  178. stR.read(self, self.INDEPENDENT_PARTITION_ENTRY)
  179. self.name = self.name.rstrip('\0')
  180. def __str__(self):
  181. return "[%-4s] user_id:0x%04X ver:0x%08X %X" % (self.name, self.user_id, self.version, self.reserved)
  182. #***************************************************************************
  183. #***************************************************************************
  184. #***************************************************************************
  185. class Init_Script_Ext(Extension): # 1 : used in Mainfist
  186. NAME = "InitScript"
  187. TYPE = 1 # for initialization script extension
  188. LIST = "scripts"
  189. # length: In bytes; equals (16 + 52*n) for this version where n is the number of modules in the initialization script
  190. INIT_SCRIPT = (
  191. ("reserved", "L", 0), # Reserved for future use.
  192. ("number_of_modules", "L", ), # Number of modules in this initialization script. Cannot be more than MAX_MODULES (this is a configuration parameter defining the maximum number of modules supported by the system set at system build time).
  193. # INIT_SCRIPT_ENTRY[] # initialization script extension entries
  194. )
  195. def __init__(self, ab):
  196. stR = StructReader(ab)
  197. stR.read(self, self.INIT_SCRIPT)
  198. clsSize, remainder = divmod(stR.sizeLeft(), self.number_of_modules)
  199. if remainder: raise Error("Init_Script_Ext data size == %d is not miltiple of nItems == %d" % stR.sizeLeft(), self.number_of_modules)
  200. cls = {24: Init_Script_Entry, 28: Init_Script_Entry_v12}[clsSize]
  201. self.LoadItems(stR, cls, self.number_of_modules)
  202. def dump(self, flog=sys.stdout):
  203. print >>flog, self.Banner()
  204. self.PrintItems(flog)
  205. #***************************************************************************
  206. class Init_Script_Entry:
  207. INIT_SCRIPT_ENTRY = (
  208. ("partition_name", "4s", ), # Manifest Partition Name. This field identifies the manifest in which this module's hash will be found irregardles of manifest's physical location (i.e. FTP manifest may be physically located in NFTP flash partition during FW update).
  209. ("name", "12s", ), # Module Name
  210. ("bf_init_flags", "L", ), # Flags used govern initialization flow.
  211. ("bf_boot_type", "L", ), # Boot path flag bits to indicate which boot path(s) this module is applicable to. Bit 0 - Normal Bit 1 - HAP Bit 2 - HMRFPO Bit 3 - Temp Disable Bit 4 - Recovery Bit 5 - Safe Mode Bit 6 - FW Update Bits 7:31 - Reserved
  212. )
  213. def __init__(self, stR):
  214. self.unk = None
  215. stR.read(self, self.INIT_SCRIPT_ENTRY)
  216. self.partition_name = self.partition_name.rstrip('\0')
  217. self.name = self.name.rstrip('\0')
  218. self.init_flags = Init_Script_Flags(self.bf_init_flags)
  219. self.boot_type = Init_Script_Boot_Type(self.bf_boot_type)
  220. def __str__(self):
  221. return "%4s:%-12s Init: %08X (%s) Boot: %08X (%s)" % (self.partition_name, self.name, self.bf_init_flags, self.init_flags, self.bf_boot_type, self.boot_type)
  222. #***************************************************************************
  223. class Init_Script_Entry_v12:
  224. INIT_SCRIPT_ENTRY = (
  225. ("partition_name", "4s", ), # Manifest Partition Name. This field identifies the manifest in which this module's hash will be found irregardles of manifest's physical location (i.e. FTP manifest may be physically located in NFTP flash partition during FW update).
  226. ("name", "12s", ), # Module Name
  227. ("bf_init_flags", "L", ), # Flags used govern initialization flow.
  228. ("bf_boot_type", "L", ), # Boot path flag bits to indicate which boot path(s) this module is applicable to. Bit 0 - Normal Bit 1 - HAP Bit 2 - HMRFPO Bit 3 - Temp Disable Bit 4 - Recovery Bit 5 - Safe Mode Bit 6 - FW Update Bits 7:31 - Reserved
  229. ("unk", "L", ), #
  230. )
  231. def __init__(self, stR):
  232. stR.read(self, self.INIT_SCRIPT_ENTRY)
  233. self.partition_name = self.partition_name.rstrip('\0')
  234. self.name = self.name.rstrip('\0')
  235. self.init_flags = Init_Script_Flags(self.bf_init_flags)
  236. self.boot_type = Init_Script_Boot_Type(self.bf_boot_type)
  237. def __str__(self):
  238. return "%4s:%-12s Init: %08X (%s) Boot: %08X (%s) Unk: %X" % (self.partition_name, self.name, self.bf_init_flags, self.init_flags, self.bf_boot_type, self.boot_type, self.unk)
  239. #***************************************************************************
  240. class Init_Script_Flags: # !!! Not sure...
  241. dRestart = {
  242. RESTART_NOT_ALLOWED : "Not Allowed", # 0
  243. RESTART_IMMEDIATLY : "Immediatly", # 1
  244. RESTART_ON_NEXT_BOOT: "On Next Boot", # 2
  245. }
  246. INIT_SCRIPT_FLAGS = ( # BitFields
  247. ("Ibl", 0, 0),
  248. ("IsRemovable", 1, 1),
  249. ("InitImmediately", 2, 2),
  250. ("RestartPolicy", 3, 15),
  251. ("Cm0_u", 16, 16),
  252. ("Cm0_nu", 17, 17),
  253. ("Cm3", 18, 18),
  254. # ("reserved", 19, 31),
  255. )
  256. def __init__(self, dw):
  257. BitFields(self, dw, self.INIT_SCRIPT_FLAGS)
  258. def __str__(self):
  259. r = ListTrueBools(self, self.INIT_SCRIPT_FLAGS)
  260. if self.RestartPolicy: r.append("Restart %s" % self.dRestart[self.RestartPolicy])
  261. return ", ".join(r)
  262. #***************************************************************************
  263. class Init_Script_Boot_Type:
  264. INIT_SCRIPT_BOOT_TYPE = ( # BitFields
  265. ("Normal", 0, 0),
  266. ("HAP", 1, 1),
  267. ("HMRFPO", 2, 2),
  268. ("TmpDisable", 3, 3),
  269. ("Recovery", 4, 4),
  270. ("SafeMode", 5, 5),
  271. ("FWUpdate", 6, 6),
  272. # ("reserved", 7, 31),
  273. )
  274. def __init__(self, dw):
  275. BitFields(self, dw, self.INIT_SCRIPT_BOOT_TYPE)
  276. def __str__(self):
  277. return ", ".join(ListTrueBools(self, self.INIT_SCRIPT_BOOT_TYPE))
  278. #***************************************************************************
  279. #***************************************************************************
  280. #***************************************************************************
  281. class Feature_Permissions_Ext(Extension): # 2 : used in Mainfist
  282. NAME = "FeaturePermissions"
  283. TYPE = 2 # for feature permission extension
  284. LIST = "permissions"
  285. # length: In bytes; equals (12 + 2*n) for this version where n is the number of features in this extension
  286. FEATURE_PERMISSIONS_EXTENSION = (
  287. ("num_of_features", "L", ), # Number of features feature numbering always starts from 0.
  288. # FEATURE_PERMISSION_ENTRY[] # feature permission extension entries
  289. )
  290. def __init__(self, ab):
  291. stR = StructReader(ab)
  292. stR.read(self, self.FEATURE_PERMISSIONS_EXTENSION)
  293. self.LoadItems(stR, Feature_Permission_Entry, self.num_of_features)
  294. def dump(self, flog=sys.stdout):
  295. print >>flog, "%s [%s]" % (self.Banner(), ", ".join("0x%04X" % e.user_id for e in self.permissions))
  296. #***************************************************************************
  297. class Feature_Permission_Entry:
  298. FEATURE_PERMISSION_ENTRY = (
  299. ("user_id", "H", ), # User ID that may change feature state for feature 0.
  300. ("reserved", "H", 0), #
  301. )
  302. def __init__(self, stR):
  303. stR.read(self, self.FEATURE_PERMISSION_ENTRY)
  304. #***************************************************************************
  305. #***************************************************************************
  306. #***************************************************************************
  307. class Partition_Info_Ext(Extension): # 3 : used in Mainfist
  308. NAME = "PartitionInfo"
  309. TYPE = 3 # for partition info extension
  310. LIST = "modules"
  311. # length: In bytes; equals (92 + 52*n) for this version where n is the number of modules in the manifest
  312. MANIFEST_PARTITION_INFO_EXT = (
  313. ("partition_name", "4s", ), # Name of the partition
  314. ("partition_length", "L", ), # Length of complete partition before any process have been removed by the OEM or the firmware update process
  315. ("partition_hash", "32s", ), # SHA256 hash of the original complete partition covering everything in the partition except for the manifest (directory binaries and LUT)
  316. ("version_control_number", "L", ), # The version control number (VCN) is incremented whenever a change is made to the FW that makes it incompatible from an update perspective with previously released versions of the FW.
  317. ("partition_version", "L", ), # minor number
  318. ("data_format_version", "L", ), #
  319. ("instance_id", "L", ), #
  320. ("flags", "L", ), # Support multiple instances Y/N. Used for independently updated partitions that may have multiple instances (such as WLAN uCode or Localization)
  321. ("reserved", "16s", ('\0'*16, '\xFF'*16,)), # set to 0xff
  322. ("unknown0", "l", (0, 1, 3, -1)), # Was 0xffffffff
  323. # MANIFEST_MODULE_INFO_EXT[] # Module info extension entries
  324. )
  325. def __init__(self, ab):
  326. stR = StructReader(ab)
  327. stR.read(self, self.MANIFEST_PARTITION_INFO_EXT)
  328. self.partition_name = self.partition_name.rstrip('\0')
  329. self.partition_hash = self.partition_hash[::-1] # Reverse?
  330. self.LoadItems(stR, Module_Info)
  331. def dump(self, flog=sys.stdout):
  332. print >>flog, self.Banner(True)
  333. print >>flog, " Name: [%s]" % self.partition_name
  334. print >>flog, " Length: %08X" % self.partition_length
  335. print >>flog, " Hash: %s" % self.partition_hash.encode("hex")
  336. print >>flog, " VCN: %d" % self.version_control_number
  337. print >>flog, " Ver: %X, %X" % (self.partition_version, self.data_format_version)
  338. print >>flog, " Instance ID: %d" % self.instance_id
  339. print >>flog, " Flags: %d" % self.flags
  340. print >>flog, " Unknown: %d" % self.unknown0
  341. print >>flog, " Modules[%d]:" % len(self.modules)
  342. self.PrintItems(flog)
  343. #***************************************************************************
  344. class Module_Info:
  345. dModType = {
  346. PROCESS_TYPE: "Proc", # 0
  347. SHARED_LIBRARY_TYPE: "Lib ", # 1
  348. DATA_TYPE: "Data", # 2
  349. IUNIT_TYPE: "iUnt", # 3 v12
  350. }
  351. MANIFEST_MODULE_INFO_EXT = (
  352. ("name", "12s", ), # Character array. If name length is shorter than field size the name is padded with 0 bytes
  353. ("type", "B", (0,1,2,3)), # 0 - Process; 1 - Shared Library; 2 - Data; 3 - iUnit
  354. ("reserved0", "B", ), #
  355. ("reserved1", "H", (0, 0xFFFF)), # set to 0xffff
  356. ("metadata_size", "L", ), #
  357. ("metadata_hash", "32s" ), # For a process/shared library this is the SHA256 of the module metadata file; for a data module this is the SHA256 hash of the module binary itself
  358. )
  359. def __init__(self, stR):
  360. stR.read(self, self.MANIFEST_MODULE_INFO_EXT)
  361. self.name = self.name.rstrip('\0')
  362. self.metadata_hash = self.metadata_hash[::-1] # Reverse
  363. def __str__(self):
  364. return "%-4s, Meta cb:%4X h:%s %s" % (self.dModType[self.type], self.metadata_size, self.metadata_hash.encode("hex"), self.name)
  365. #***************************************************************************
  366. #***************************************************************************
  367. #***************************************************************************
  368. class Shared_Lib_Ext(Extension): # 4 : used in Metadata
  369. NAME = "SharedLib"
  370. TYPE = 4 # for shared library extension
  371. # length: In bytes equals 52 for this version
  372. SHARED_LIB_EXTENSION = (
  373. ("context_size", "L", ), # Size in bytes of the shared library context
  374. ("total_alloc_virtual_space", "L", ), # Including padding pages for library growth. Currently set to a temporary value. This needs to be updated once the SHARED_CONTEXT_SIZE symbol is defined in the build process.
  375. ("code_base_address", "L", ), # Base address for the library private code in VAS. Must be 4KB aligned.
  376. ("tls_size", "L", ), # Size of Thread-Local-Storage used by the shared library.
  377. ("reserved", "L", ), # reserved bytes set to 0xffffffff
  378. )
  379. def __init__(self, ab):
  380. stR = StructReader(ab)
  381. stR.read(self, self.SHARED_LIB_EXTENSION)
  382. stR.done()
  383. def dump(self, flog=sys.stdout):
  384. print >>flog, "%s context_size:0x%X, total_alloc_virtual_space:0x%X, code_base_address:0x%X, tls_size:0x%x" % (self.Banner(), self.context_size, self.total_alloc_virtual_space, self.code_base_address, self.tls_size)
  385. #***************************************************************************
  386. #***************************************************************************
  387. #***************************************************************************
  388. class Man_Process_Ext(Extension): # 5 : used in Metadata
  389. NAME = "Process"
  390. TYPE = 5 # for process attribute extension
  391. # length: In bytes equals 160 + 2*n for this version where n is the number of group IDs entries in the extension
  392. MAN_PROCESS_EXTENSION = (
  393. ("bf_flags", "L", ), # Flags
  394. ("main_thread_id", "L", ), # TID for main thread. Optional for IBL processes only. Must be 0 for other processes.
  395. ("priv_code_base_address", "L", ), # Base address for code. Address is in LAS for Bringup/Kernel VAS for other processes. Must be 4KB aligned
  396. ("uncompressed_priv_code_size","L", ), # Size of uncompressed process code. Does not include code for shared library.
  397. ("cm0_heap_size", "L", ), # Size of Thread-Local-Storage for the process
  398. ("bss_size", "L", ), #
  399. ("default_heap_size", "L", ), #
  400. ("main_thread_entry", "L", ), # VAS of entry point function for the process main thread
  401. ("allowed_sys_calls", "12s", ), # Bitmask of allowed system calls by the process
  402. ("user_id", "H", ), # Runtime User ID for process
  403. ("reserved_0", "L", ), # Temporary placeholder for thread base
  404. ("reserved_1", "H", 0), # Must be 0
  405. ("reserved_2", "Q", ), #
  406. # group_ids['H'] # Group ID for process
  407. )
  408. def __init__(self, ab):
  409. stR = StructReader(ab)
  410. stR.read(self, self.MAN_PROCESS_EXTENSION)
  411. abGIDs = stR.ab[stR.o:]
  412. self.group_ids = list(struct.unpack("<%dH" % (len(abGIDs) / 2), abGIDs))
  413. self.flags = Man_Process_Flags(self.bf_flags)
  414. def dump(self, flog=sys.stdout):
  415. print >>flog, self.Banner()
  416. print >>flog, " flags: %s" % self.flags
  417. print >>flog, " main_thread_id: 0x%X" % self.main_thread_id
  418. print >>flog, " priv_code_base_address: 0x%08X" % self.priv_code_base_address
  419. print >>flog, " uncompressed_priv_code_size: 0x%X" % self.uncompressed_priv_code_size
  420. print >>flog, " cm0_heap_size: 0x%X" % self.cm0_heap_size
  421. print >>flog, " bss_size: 0x%X" % self.bss_size
  422. print >>flog, " default_heap_size: 0x%X" % self.default_heap_size
  423. print >>flog, " main_thread_entry: 0x%08X" % self.main_thread_entry
  424. print >>flog, " allowed_sys_calls: %s" % self.allowed_sys_calls.encode("hex")
  425. print >>flog, " user_id: 0x%04X" % self.user_id
  426. print >>flog, " group_ids[%d]: [%s]" % (len(self.group_ids), ", ".join("0x%04X" % gid for gid in self.group_ids))
  427. #***************************************************************************
  428. class Man_Process_Flags:
  429. MAN_PROCESS_FLAGS = ( # BitFields
  430. ("fault_tolerant", 0, 0), # Kernel exception policy: 0 - Reset System, 1 - Terminate Process
  431. ("permanent_process", 1, 1), # permanent process Y/N. A permanent process' code/rodata sections are not removed from RAM when it terminates normally in order to optimize its reload flow.
  432. ("single_instance", 2, 2), # Single Instance Y/N. When the process is spawned if it is already running in the system the spawn will fail.
  433. ("trusted_snd_rev_sender", 3, 3), # Trusted SendReceive Sender Y/N. If set this process is allowed to send IPC_SendReceive messages to any process (not only public).
  434. ("trusted_notify_sender", 4, 4), # Trusted Notify Sender Y/N. If set this process is allowed to send IPC_Notify notifications to any process (not only public).
  435. ("public_snd_rev_receiver", 5, 5), # Public SendReceive Receiver Y/N. If set any other process is allowed to send IPC_SendReceive messages to it (not only trusted).
  436. ("public_notify_receiver", 6, 6), # Public Notify Receiver Y/N. If set any other process is allowed to IPC_Notify notifications messages to it (not only trusted).
  437. #("reserved", 7, 31), # reserved. Set to 0
  438. )
  439. def __init__(self, dw):
  440. BitFields(self, dw, self.MAN_PROCESS_FLAGS)
  441. def __str__(self):
  442. return ", ".join(ListTrueBools(self, self.MAN_PROCESS_FLAGS))
  443. #***************************************************************************
  444. #***************************************************************************
  445. #***************************************************************************
  446. class Threads_Ext(Extension): # 6 : used in Metadata
  447. NAME = "Threads"
  448. TYPE = 6 # for threads extension
  449. LIST = "threads"
  450. def __init__(self, ab):
  451. self.LoadItems(StructReader(ab), Thread_Entry)
  452. def dump(self, flog=sys.stdout):
  453. print >>flog, self.Banner()
  454. self.PrintItems(flog)
  455. #***************************************************************************
  456. class Thread_Entry:
  457. THREAD_ENTRY = (
  458. ("stack_size", "L", ), # Size of main thread stack in bytes (not including guard page including space reserved for TLS). Must be divisible by 4K with the following exception: if the default heap size is smaller than 4K the last thread's stack size may have any size.
  459. ("flags", "L", ), # Bit0 - set to 0 for live thread 1 for CM0-U-only thread; Bits 1-31 - reserved must be 0
  460. ("scheduling_policy", "L", ), # Bits 0-7: Scheduling Policy, 0 -> fixed priority; Bits 8-31: Scheduling attributes. For a fixed priority policy this is the scheduling priority of the thread.
  461. ("reserved", "L", ), #
  462. )
  463. def __init__(self, stR):
  464. stR.read(self, self.THREAD_ENTRY)
  465. def __str__(self):
  466. return "stack_size:0x%08X, flags:%X, scheduling_policy:%08X" % (self.stack_size, self.flags, self.scheduling_policy)
  467. #***************************************************************************
  468. #***************************************************************************
  469. #***************************************************************************
  470. class Device_Ids_Ext(Extension): # 7 : used in Metadata
  471. NAME = "DeviceIds"
  472. TYPE = 7 # for device ids extension
  473. LIST = "device_id_group"
  474. def __init__(self, ab):
  475. self.LoadItems(StructReader(ab), Device_Entry)
  476. def dump(self, flog=sys.stdout):
  477. print >>flog, "%s [%s]" % (self.Banner(), ", ".join("%08X" % v.device_id for v in self.device_id_group))
  478. #***************************************************************************
  479. class Device_Entry:
  480. DEVICE_ENTRY = (
  481. ("device_id", "L", ), #
  482. ("reserved", "L", ), #
  483. )
  484. def __init__(self, stR):
  485. stR.read(self, self.DEVICE_ENTRY)
  486. #***************************************************************************
  487. #***************************************************************************
  488. #***************************************************************************
  489. class Mmio_Ranges_Ext(Extension): # 8 : used in Metadata
  490. NAME = "MmioRanges"
  491. TYPE = 8 # for mmio ranges extension
  492. LIST = "mmio_range_defs"
  493. def __init__(self, ab):
  494. self.LoadItems(StructReader(ab), Mmio_Range_Def)
  495. def dump(self, flog=sys.stdout):
  496. print >>flog, self.Banner()
  497. # self.PrintItems(flog)
  498. for i,e in enumerate(getattr(self, self.LIST)): print >>flog, " %s" % (e)
  499. #***************************************************************************
  500. class Mmio_Range_Def:
  501. MMIO_RANGE_DEF = (
  502. ("base", "L", ), # Base address of the MMIO range
  503. ("size", "L", ), # Limit in bytes of the MMIO range
  504. ("flags", "L", ), # Read access Y/N
  505. )
  506. def __init__(self, stR):
  507. stR.read(self, self.MMIO_RANGE_DEF)
  508. def __str__(self):
  509. return "base:%08X, size:%08X, flags:%08X" % (self.base, self.size, self.flags)
  510. #***************************************************************************
  511. #***************************************************************************
  512. #***************************************************************************
  513. class Special_File_Producer_Ext(Extension): # 9 : used in Metadata
  514. NAME = "SpecialFileProducer"
  515. TYPE = 9 # for special file producer extension
  516. LIST = "files"
  517. SPECIAL_FILE_PRODUCER_EXTENSION = (
  518. ("major_number", "H", ), #
  519. ("flags", "H", ), #
  520. # SPECIAL_FILE_DEF[]
  521. )
  522. def __init__(self, ab):
  523. stR = StructReader(ab)
  524. stR.read(self, self.SPECIAL_FILE_PRODUCER_EXTENSION)
  525. self.LoadItems(stR, Special_File_Def)
  526. def dump(self, flog=sys.stdout):
  527. print >>flog, "%s major_number=0x%04X" % (self.Banner(), self.major_number)
  528. self.PrintItems(flog)
  529. #***************************************************************************
  530. class Special_File_Def:
  531. SPECIAL_FILE_DEF = (
  532. ("name", "12s", ), #
  533. ("access_mode", "H", ), #
  534. ("user_id", "H", ), #
  535. ("group_id", "H", ), #
  536. ("minor_number", "B", ), #
  537. ("reserved0", "B", ), #
  538. ("reserved1", "L", ), #
  539. )
  540. def __init__(self, stR):
  541. stR.read(self, self.SPECIAL_FILE_DEF)
  542. self.name = self.name.rstrip('\0')
  543. def __str__(self):
  544. return "%-12s access_mode:%04o, user_id:0x%04X group_id:0x%04X minor_number:%02X" % (self.name, self.access_mode, self.user_id, self.group_id, self.minor_number)
  545. #***************************************************************************
  546. #***************************************************************************
  547. #***************************************************************************
  548. class Mod_Attr_Ext(Extension): # 10 : used in Metadata
  549. NAME = "ModAttr"
  550. TYPE = 10 # for this module attribute extension
  551. # length: In bytes; equals 56 for this version
  552. dCompType = {
  553. COMP_TYPE_NOT_COMPRESSED:" ",
  554. COMP_TYPE_HUFFMAN:"Huff",
  555. COMP_TYPE_LZMA:"LZMA",
  556. }
  557. MOD_ATTR_EXTENSION = (
  558. ("compression_type", "B", (0,1,2,)), # 0 - Uncompressed; 1 - Huffman Compressed; 2 - LZMA Compressed
  559. ("encrypted", "B", (0,1)), # Used as "encrypted" flag
  560. ("reserved1", "B", 0), # Must be 0
  561. ("reserved2", "B", 0), # Must be 0
  562. ("uncompressed_size", "L", ), # Uncompressed image size must be divisible by 4K
  563. ("compressed_size", "L", ), # Compressed image size. This is applicable for LZMA compressed modules only. For other modules should be the same as uncompressed_size field.
  564. ("module_number", "H", ), # Module number unique in the scope of the vendor.
  565. ("vendor_id", "H", 0x8086), # Vendor ID (PCI style). For Intel modules must be 0x8086.
  566. ("image_hash", "32s", ), # SHA2 Hash of uncompressed image
  567. )
  568. def __init__(self, ab):
  569. stR = StructReader(ab)
  570. stR.read(self, self.MOD_ATTR_EXTENSION)
  571. self.image_hash = self.image_hash[::-1] # Reverse
  572. stR.done()
  573. def dump(self, flog=sys.stdout):
  574. print >>flog, "%s %s enc=%d %08X->%08X id:%04X.%04X h:%s" % (self.Banner(), self.dCompType[self.compression_type], self.encrypted, self.compressed_size, self.uncompressed_size, self.module_number, self.vendor_id, self.image_hash.encode("hex"))
  575. #***************************************************************************
  576. #***************************************************************************
  577. #***************************************************************************
  578. class Locked_Ranges_Ext(Extension): # 11 : used in Metadata
  579. NAME = "LockedRanges"
  580. TYPE = 11 # for unknown 11 extension
  581. LIST = "ranges"
  582. def __init__(self, ab):
  583. self.LoadItems(StructReader(ab), Locked_Range)
  584. def dump(self, flog=sys.stdout):
  585. print >>flog, self.Banner()
  586. self.PrintItems(flog)
  587. #***************************************************************************
  588. class Locked_Range:
  589. LOCKED_RANGE = (
  590. ("base", "L", ), # Base address in VAS of range to be locked. Must be divisible in 4KB.
  591. ("size", "L", ), # Size of range to be locked. Must be divisible in 4KB.
  592. )
  593. def __init__(self, stR):
  594. stR.read(self, self.LOCKED_RANGE)
  595. def __str__(self):
  596. return "base:0x%08X, size:%X" % (self.base, self.size)
  597. #***************************************************************************
  598. #***************************************************************************
  599. #***************************************************************************
  600. class Client_System_Info_Ext(Extension): # 12 : used in Manifest
  601. NAME = "ClientSystemInfo"
  602. TYPE = 12 # for client system info extension
  603. CLIENT_SYSTEM_INFO_EXTENSION = (
  604. ("fw_sku_caps", "L", ), #
  605. ("fw_sku_caps_reserved", "28s", '\xFF'*28), #
  606. ("bf_fw_sku_attributes", "Q", ), # Bits 0:3 - CSE region size in multiples of 0.5 MB Bits 4:6 - firmware sku; 0 for 5.0MB 1 for 1.5MB 2 for slim sku. Bit 7 - Patsberg support Y/N Bit 8 - M3 support Y/N Bit 9 - M0 support Y/N Bits 10:11 - reserved Bits 12:15 - Si class (all H M L) Bits 16:63 - reserved
  607. )
  608. def __init__(self, ab):
  609. stR = StructReader(ab)
  610. stR.read(self, self.CLIENT_SYSTEM_INFO_EXTENSION)
  611. self.attr = Client_System_Sku_Attributes(self.bf_fw_sku_attributes)
  612. stR.done()
  613. def dump(self, flog=sys.stdout):
  614. print >>flog, self.Banner()
  615. print >>flog, " fw_sku_caps: %x" % self.fw_sku_caps
  616. print >>flog, " fw_sku_attributes: %s" % self.attr
  617. #***************************************************************************
  618. class Client_System_Sku_Attributes:
  619. dFirmwareSKU = {
  620. 0: "5.0MB",
  621. 1: "1.5MB",
  622. 2: "Slim",
  623. 3: "SPS",
  624. }
  625. CLIENT_SYSTEM_SKU_ATTRIBUTES = ( # BitFields
  626. ("CSE_region_size", 0, 3), # Bits 0:3 - CSE region size in multiples of 0.5 MB
  627. ("firmware_sku", 4, 6), # Bits 4:6 - firmware sku; 0 for 5.0MB 1 for 1.5MB 2 for slim sku.
  628. ("Patsberg", 7, 7), # Bit 7 - Patsberg support Y/N
  629. ("M3", 8, 8), # Bit 8 - M3 support Y/N
  630. ("M0", 9, 9), # Bit 9 - M0 support Y/N
  631. # ("reserved0", 10, 11), # Bits 10:11 - reserved
  632. ("Si_class", 12, 15), # Bits 12:15 - Si class (all H M L)
  633. # ("reserved1", 16, 63), # Bits 16:63 - reserved
  634. )
  635. def __init__(self, qw):
  636. BitFields(self, qw, self.CLIENT_SYSTEM_SKU_ATTRIBUTES)
  637. def __str__(self):
  638. return "CSE region size: %.2f, firmware sku: %s, Si class: %X, %s" % (0.5*self.CSE_region_size, self.dFirmwareSKU[self.firmware_sku], self.Si_class, ", ".join(ListTrueBools(self, self.CLIENT_SYSTEM_SKU_ATTRIBUTES)))
  639. #***************************************************************************
  640. #***************************************************************************
  641. #***************************************************************************
  642. class User_Info_Ext(Extension): # 13 : used in Manifest
  643. NAME = "UserInfo"
  644. TYPE = 13 # for user info extension
  645. LIST = "users"
  646. def __init__(self, ab):
  647. try:
  648. self.LoadItems(StructReader(ab), User_Info_Entry)
  649. except:
  650. self.LoadItems(StructReader(ab), User_Info_Entry_new)
  651. def dump(self, flog=sys.stdout):
  652. print >>flog, self.Banner()
  653. self.PrintItems(flog)
  654. #***************************************************************************
  655. class User_Info_Entry:
  656. USER_INFO_ENTRY = (
  657. ("user_id", "H", ), # User ID.
  658. ("reserved", "H", (0,1)), # Must be 0.
  659. ("non_volatile_storage_quota","L", ), # Maximum size of non-volatile storage area.
  660. ("ram_storage_quota", "L", ), # Maximum size of RAM storage area.
  661. ("wop_quota", "L", ), # Quota to use in wear-out prevention algorithm; in most cases this should match the non-volatile storage quota; however it is possible to virtually add quota to a user to allow it to perform more write operations on expense of another user. At build time the build system will check that the sum of all users WOP quota is not more than the sum of all users non-volatile storage quota.
  662. ("working_dir", "36s", ), # Starting directory for the user. Used when accessing files with a relative path. Character array; if name length is shorter than field size the name is padded with 0 bytes.
  663. )
  664. def __init__(self, stR):
  665. stR.read(self, self.USER_INFO_ENTRY)
  666. self.working_dir = self.working_dir.rstrip('\0')
  667. assert self.working_dir.find('\0') < 0
  668. def __str__(self):
  669. return "user id:0x%04X, NV quota:%8X, RAM quota:%8X, WOP quota:%8X, working dir: [%s]" % (self.user_id, self.non_volatile_storage_quota, self.ram_storage_quota, self.wop_quota, self.working_dir)
  670. #***************************************************************************
  671. class User_Info_Entry_new:
  672. USER_INFO_ENTRY = (
  673. ("user_id", "H", ), # User ID.
  674. ("reserved", "H", 0), # Must be 0.
  675. ("non_volatile_storage_quota","L", ), # Maximum size of non-volatile storage area.
  676. ("ram_storage_quota", "L", ), # Maximum size of RAM storage area.
  677. ("wop_quota", "L", ), # Quota to use in wear-out prevention algorithm; in most cases this should match the non-volatile storage quota; however it is possible to virtually add quota to a user to allow it to perform more write operations on expense of another user. At build time the build system will check that the sum of all users WOP quota is not more than the sum of all users non-volatile storage quota.
  678. )
  679. def __init__(self, stR):
  680. stR.read(self, self.USER_INFO_ENTRY)
  681. def __str__(self):
  682. return "user id:0x%04X, NV quota:%8X, RAM quota:%8X, WOP quota:%8X" % (self.user_id, self.non_volatile_storage_quota, self.ram_storage_quota, self.wop_quota)
  683. #***************************************************************************
  684. #***************************************************************************
  685. #***************************************************************************
  686. class Package_Info_Ext(Extension): # 15 : used in TXE Mainfist
  687. NAME = "PackageInfo"
  688. TYPE = 15 # for partition info extension
  689. LIST = "modules"
  690. SIGNED_PACKAGE_INFO_EXT = (
  691. ("package_name", "4s", ), # Name of the partition
  692. ("version_control_number", "L", ), # The version control number (VCN) is incremented whenever a change is made to the FW that makes it incompatible from an update perspective with previously released versions of the FW.
  693. ("usage_bitmap", "16s", ), # Bitmap of usages depicted by this manifest, indicating which key is used to sign the manifest
  694. ("svn", "L", ), # Secure Version Number
  695. ("unknown", "L", ), #
  696. ("reserved", "12s", '\x00'*12), # Must be 0
  697. # SIGNED_PACKAGE_INFO_EXT_ENTRY[] # Module info extension entries
  698. )
  699. def __init__(self, ab):
  700. stR = StructReader(ab)
  701. stR.read(self, self.SIGNED_PACKAGE_INFO_EXT)
  702. self.package_name = self.package_name.rstrip('\0')
  703. self.LoadItems(stR, Package_Info_Ext_Entry)
  704. def dump(self, flog=sys.stdout):
  705. print >>flog, self.Banner(True)
  706. print >>flog, " Name: [%s]" % self.package_name
  707. print >>flog, " VCN: %d" % self.version_control_number
  708. print >>flog, " Usage Bitmap: %s" % self.usage_bitmap.encode("hex")
  709. print >>flog, " svn: %d" % self.svn
  710. print >>flog, " unknown: 0x%X" % self.unknown
  711. print >>flog, " Modules[%d]:" % len(self.modules)
  712. self.PrintItems(flog)
  713. #***************************************************************************
  714. class Package_Info_Ext_Entry:
  715. dModType = {
  716. PROCESS_TYPE: "Proc", # 0
  717. SHARED_LIBRARY_TYPE: "Lib ", # 1
  718. DATA_TYPE: "Data", # 2
  719. IUNIT_TYPE: "iUnt", # 3 v12
  720. }
  721. dHashAlgorithm = {
  722. 1: "SHA1",
  723. 2: "SHA256",
  724. }
  725. SIGNED_PACKAGE_INFO_EXT_ENTRY = (
  726. ("name", "12s", ), # Character array. If name length is shorter than field size the name is padded with 0 bytes
  727. ("type", "B", (0,1,2,3)), # 0 - Process; 1 - Shared Library; 2 - Data; 3 - iUnit
  728. ("hash_algorithm", "B", 2), # 0 - Reserved; 1 - SHA1; 2 - SHA256
  729. ("hash_size", "H", 32), # Size of Hash in bytes = N; BXT to support only SHA256. So N=32.
  730. ("metadata_size", "L", ), # Size of metadata file
  731. ("metadata_hash", "32s" ), # The SHA2 of the module metadata file
  732. )
  733. def __init__(self, stR):
  734. stR.read(self, self.SIGNED_PACKAGE_INFO_EXT_ENTRY)
  735. self.name = self.name.rstrip('\0')
  736. self.metadata_hash = self.metadata_hash[::-1] # Reverse
  737. def __str__(self):
  738. return "%-4s, Meta cb:%4X h=%s[%d]:%s %s" % (self.dModType[self.type], self.metadata_size, self.dHashAlgorithm[self.hash_algorithm], self.hash_size, self.metadata_hash.encode("hex"), self.name)
  739. #***************************************************************************
  740. #***************************************************************************
  741. #***************************************************************************
  742. class Unk_16_Ext(Extension): # 16 : used in Manifest (for iUnit)
  743. NAME = "Unk_iUnit_16"
  744. TYPE = 16 # for iUnit extension
  745. UNK_IUNIT_16_EXT = (
  746. ("v0_1", "L", 1), #
  747. ("unk16", "16s", '\0'*16), #
  748. ("v2_3", "L", 3), #
  749. ("v3", "L", ), #
  750. ("v4_1", "L", 1), #
  751. ("h", "32s", ), #
  752. ("reserved", "24s", '\0'*24), #
  753. )
  754. def __init__(self, ab):
  755. stR = StructReader(ab)
  756. stR.read(self, self.UNK_IUNIT_16_EXT)
  757. # self.h = self.h[::-1] # Reverse?
  758. stR.done()
  759. def dump(self, flog=sys.stdout):
  760. print >>flog, self.Banner()
  761. print >>flog, " %X %X %X %X h=%s" % (self.v0_1, self.v2_3, self.v3, self.v4_1, self.h.encode("hex"))
  762. #***************************************************************************
  763. #***************************************************************************
  764. #***************************************************************************
  765. class Unk_18_Ext(Extension): # 18 : used in Manifest
  766. NAME = "Unk_18"
  767. TYPE = 18 # for user info extension
  768. LIST = "records"
  769. UNK_18_EXT = (
  770. ("items", "L", ), #
  771. ("unk", "16s", ), #
  772. )
  773. def __init__(self, ab):
  774. stR = StructReader(ab)
  775. stR.read(self, self.UNK_18_EXT)
  776. self.LoadItems(stR, Unk_18_Ext_Entry)
  777. def dump(self, flog=sys.stdout):
  778. print >>flog, self.Banner()
  779. print >>flog, " Records[%d] %s:" % (self.items, self.unk.encode("hex"))
  780. self.PrintItems(flog)
  781. #***************************************************************************
  782. class Unk_18_Ext_Entry:
  783. UNK_18_EXT_ENTRY = (
  784. ("ab", "56s", ), #
  785. )
  786. def __init__(self, stR):
  787. stR.read(self, self.UNK_18_EXT_ENTRY)
  788. def __str__(self):
  789. return self.ab.encode("hex")
  790. #***************************************************************************
  791. #***************************************************************************
  792. #***************************************************************************
  793. class Unk_22_Ext(Extension): # 22 : used in Manifest (v12)
  794. NAME = "Unk_22"
  795. TYPE = 22 # for v12 extension
  796. UNK_22_EXT = (
  797. ("name", "4s", ), #
  798. ("unk24", "24s", ), #
  799. ("h", "32s", ), #
  800. ("reserved", "20s", '\0'*20), #
  801. )
  802. def __init__(self, ab):
  803. stR = StructReader(ab)
  804. stR.read(self, self.UNK_22_EXT)
  805. # self.h = self.h[::-1] # Reverse?
  806. stR.done()
  807. def dump(self, flog=sys.stdout):
  808. print >>flog, "%s [%s] u=%s h=%s" % (self.Banner(), self.name, self.unk24.encode("hex"), self.h.encode("hex"))
  809. #***************************************************************************
  810. #***************************************************************************
  811. #***************************************************************************
  812. class Unk_50_Ext(Extension): # 50 : used in Manifest (HP)
  813. NAME = "Unk_50"
  814. TYPE = 50 # for iUnit extension
  815. UNK_50_EXT = (
  816. ("name", "4s", ), #
  817. ("dw0", "L", 0), #
  818. )
  819. def __init__(self, ab):
  820. stR = StructReader(ab)
  821. stR.read(self, self.UNK_50_EXT)
  822. stR.done()
  823. def dump(self, flog=sys.stdout):
  824. print >>flog, "%s [%s]" % (self.Banner(), self.name)
  825. #***************************************************************************
  826. #***************************************************************************
  827. #***************************************************************************
  828. aExtHandlers = (
  829. System_Info_Ext, # 0
  830. Init_Script_Ext, # 1
  831. Feature_Permissions_Ext, # 2
  832. Partition_Info_Ext, # 3
  833. Shared_Lib_Ext, # 4
  834. Man_Process_Ext, # 5
  835. Threads_Ext, # 6
  836. Device_Ids_Ext, # 7
  837. Mmio_Ranges_Ext, # 8
  838. Special_File_Producer_Ext, # 9
  839. Mod_Attr_Ext, # 10
  840. Locked_Ranges_Ext, # 11
  841. Client_System_Info_Ext, # 12
  842. User_Info_Ext, # 13
  843. # None, # 14
  844. Package_Info_Ext, # 15
  845. Unk_16_Ext, # 16
  846. Unk_18_Ext, # 18
  847. Unk_22_Ext, # 22
  848. Unk_50_Ext, # 50
  849. )
  850. dExtHandlers = {ext.TYPE: ext for ext in aExtHandlers}
  851. def Ext_ParseAll(obj, ab, o=0):
  852. def EnumTags(ab, o=0):
  853. while o < len(ab):
  854. tag, cb = struct.unpack_from("<LL", ab, o)
  855. assert cb >= 8
  856. assert o+cb <= len(ab)
  857. yield tag, ab[o+8:o+cb]
  858. o += cb
  859. obj.extList = []
  860. for extType, extData in EnumTags(ab, o):
  861. ext = dExtHandlers.get(extType, None)
  862. if ext is not None:
  863. extObj = ext(extData)
  864. setattr(obj, ext.NAME, extObj)
  865. obj.extList.append(extObj)
  866. # else: raise Error("Extension #%d[%d] not supported" % (extType, len(extData)))
  867. else:
  868. parent = obj.name + (".met" if isinstance(obj, CPD_Entry) else "")
  869. print >>sys.stderr, "- %s: Unknown extType#%d[%d] %s" % (parent, extType, len(extData), extData.encode("hex"))
  870. def Ext_DumpAll(obj, flog=sys.stdout):
  871. if hasattr(obj, "extList"):
  872. for extObj in obj.extList: extObj.dump(flog)
  873. # for ext in aExtHandlers:
  874. # if hasattr(obj, ext.NAME): getattr(obj, ext.NAME).dump(flog)
  875. #***************************************************************************
  876. #***************************************************************************
  877. #***************************************************************************
  878. class CPD_Manifest:
  879. MARKER = "$MN2"
  880. MANIFEST_HEADER = (
  881. ("type", "L", 4), # Must be 0x4
  882. ("length", "L", 161), # in Dwords equals 161 for this version
  883. ("version", "L", 0x10000), # 0x1000 for this version
  884. ("flags", "L", ), # Debug intel owned
  885. ("vendor", "L", 0x8086), # 0x8086 for intel
  886. ("date", "L", ), # yyymmdd in BCD format
  887. ("size", "L", ), # in Dwords size of the entire manifest. Maximum size is 2K DWORDS (8KB)
  888. ("header_id", "4s", MARKER), # Magic number. Equals $MN2 for this version
  889. ("reserved0", "L", ), # Must be 0x4 [not True: it is 0!]
  890. ("version_major", "H", ), # Major Version [== 11]
  891. ("version_minor", "H", ), # Minor Version
  892. ("version_hotfix", "H", ), # Hotfix
  893. ("version_build", "H", ), # Build number
  894. ("svn", "L", ), # Secure Version Number
  895. ("reserved1", "Q", 0), # must be 0
  896. ("reserved2", "64s", '\0'*64), # will be set to 0
  897. ("modulus_size", "L", 64), # In DWORDs; 64 for pkcs 1.5-2048
  898. ("exponent_size", "L", 1), # In DWORDs; 1 for pkcs 1.5-2048
  899. )
  900. CRYPTO_BLOCK = (
  901. ("public_key", "256s", ), # Public Key
  902. ("exponent", "L", ), # Exponent [== 17]
  903. ("signature", "256s" ), # RSA signature of manifest
  904. )
  905. def __init__(self, ab, name):
  906. self.name = name
  907. self.stR = StructReader(ab)
  908. self.stR.read(self, self.MANIFEST_HEADER)
  909. self.stR.read(self, self.CRYPTO_BLOCK)
  910. assert 4*self.size == len(ab)
  911. Ext_ParseAll(self, ab, 4*self.length) # Parse all Extensions
  912. if 12 == self.version_major: g.HuffDecoder = HuffDecoder12
  913. def dump(self, flog=sys.stdout):
  914. print >>flog, "CPD Manifest"
  915. print >>flog, " Date: %08X" % self.date
  916. print >>flog, " Version: %d.%d.%d.%d" % (self.version_major, self.version_minor, self.version_hotfix, self.version_build)
  917. print >>flog, " SVN: %08X" % self.svn
  918. # Validate RSA Public Key hash
  919. h = hashlib.sha256(self.public_key + struct.pack("<L", self.exponent)).digest()
  920. try:
  921. print >>flog, " RSA Modulus: known[%d]," % aPubKeyHash.index(h),
  922. except:
  923. print >>flog, "- RSA Modulus is unknown,",
  924. # Validate RSA Signatire
  925. h = hashlib.sha256(self.stR.ab[:0x80] + self.stR.ab[4*self.length:]).digest()
  926. modulus = int(self.public_key[::-1].encode("hex"), 16)
  927. sign = int(self.signature[::-1].encode("hex"), 16)
  928. decoded = ("%0512X" % pow(sign, self.exponent, modulus)).decode("hex")
  929. expected = "\x00\x01" + "\xFF"*202 + "003031300D060960864801650304020105000420".decode("hex") + h
  930. print >>flog, "Exponent:%d, Verification:%s" % (self.exponent, "OK" if decoded == expected else "FAILED")
  931. Ext_DumpAll(self, flog) # Dump all extensions
  932. #***************************************************************************
  933. #***************************************************************************
  934. #***************************************************************************
  935. class CPD_Entry:
  936. CPD_ENTRY_OFFSET = ( # BitFields
  937. ("address", 0, 24),
  938. ("compress_flag", 25, 25),
  939. # ("offset_reserved", 26, 31),
  940. )
  941. CPD_ENTRY = (
  942. ("name", "12s", ),
  943. ("bf_offset", "L", ),
  944. ("length", "L", ),
  945. ("reserved", "L", ),
  946. )
  947. def __init__(self, parent):
  948. self.cpd = parent
  949. self.cpd.stR.read(self, self.CPD_ENTRY)
  950. self.name = self.name.rstrip('\0')
  951. BitFields(self, self.bf_offset, self.CPD_ENTRY_OFFSET)
  952. self.mod = None
  953. self.metadata = None
  954. def __str__(self):
  955. return "%-12s %s %8X %-8X" % (self.name, "HUFF" if self.compress_flag else " ", self.address, self.length)
  956. def getData(self):
  957. return self.cpd.stR.getData(self.address, self.ModAttr.compressed_size if self.compress_flag else self.length)
  958. def saveRaw(self, baseDir, name=None):
  959. if name is None: name = self.name
  960. with open (os.path.join(baseDir, name), "wb") as fo: fo.write(self.getData())
  961. #***************************************************************************
  962. #***************************************************************************
  963. #***************************************************************************
  964. class CPD: # Code Partition Directory
  965. MARKER = "$CPD"
  966. CPD_HEADER = (
  967. ("marker", "4s", MARKER),
  968. ("entries", "L", ),
  969. ("header_version", "B", 0x01),
  970. ("entry_version", "B", 0x01),
  971. ("header_length", "B", 0x10),
  972. ("checksum", "B", ),
  973. ("partition_name", "4s", ),
  974. )
  975. def __init__(self, ab, base):
  976. self.stR = StructReader(ab, base)
  977. self.stR.read(self, self.CPD_HEADER) # Read header
  978. self.partition_name = self.partition_name.rstrip('\0')
  979. self.files = [CPD_Entry(self) for i in xrange(self.entries)] # Read directory entries
  980. self.d = {e.name:e for e in self.files} # Dict maps name CPD_Entry
  981. e = self.files[0] # Access Manifest (very first entry in lookup table)
  982. self.Manifest = CPD_Manifest(e.getData(), e.name) if e.name.endswith(".man") else None
  983. # assert self.partition_name + ".man" == e.name
  984. self.modules = None
  985. if self.Manifest:
  986. if hasattr(self.Manifest, "PackageInfo"):
  987. self.modules = self.Manifest.PackageInfo.modules
  988. elif hasattr(self.Manifest, "PartitionInfo"):
  989. self.modules = self.Manifest.PartitionInfo.modules
  990. if len(self.files) != 1 + 2*len(self.modules): # Manfest + nFiles * (Data + Metadata)
  991. print >>sys.stderr, "- Partition holds %d files but only %d module[s] (%d expected)" % (len(self.files), len(self.modules), (len(self.files)-1)/2)
  992. if self.modules: # Try to attach Module Info and Metadata to Entry
  993. for i,mod in enumerate(self.modules): # Walk through modules listed in partion manifest
  994. e = self.d[mod.name] # Access CPD_Entry by module name
  995. e.mod = mod # Attach Module Info to entry
  996. metaName = e.name if e.name.endswith(".met") else e.name + ".met"
  997. e.metadata = self.d[metaName].getData() # Get Metadata content
  998. assert len(e.metadata) == e.mod.metadata_size # Check Metadata length
  999. # assert hashlib.sha256(e.metadata).digest() == mod.metadata_hash # Check Metadata hash
  1000. if hashlib.sha256(e.metadata).digest() != e.mod.metadata_hash: # Check Metadata hash
  1001. print >>sys.stderr, "MetaHash %s[%d]: %s != %s" % (e.name, e.mod.metadata_size, hashlib.sha256(e.metadata).hexdigest(), e.mod.metadata_hash.encode("hex"))
  1002. Ext_ParseAll(e, e.metadata) # Parse all Metadata Extensions and store them in CPD_Entry
  1003. def dump(self, flog=sys.stdout, baseDir=None):
  1004. print >>flog, "%08X: CPD %-4s.%02X [%d]" % (self.stR.base, self.partition_name, self.checksum, self.entries)
  1005. if baseDir is not None:
  1006. baseDir = os.path.join(baseDir, "%08X.%s" % (self.stR.base, self.partition_name))
  1007. if not os.path.exists(baseDir): os.makedirs(baseDir)
  1008. if self.Manifest:
  1009. with open(os.path.join(baseDir, "Manifest.txt"), "wt") as fo: self.Manifest.dump(fo)
  1010. if self.Manifest: self.Manifest.dump(flog)
  1011. print >>flog, "\nCPD Files[%d]:" % len(self.files)
  1012. for i,e in enumerate(self.files):
  1013. print >>flog, "=================================\n%4d: %s" % (i+1, e)
  1014. Ext_DumpAll(e, flog) # Dump all Metadata Extensions
  1015. if baseDir is None: continue
  1016. fileName = e.name
  1017. fileExt = os.path.splitext(fileName)[1]
  1018. bSaveRaw = False
  1019. if ".man" == fileExt: # CPD Manifest
  1020. if g.dumpManifest: bSaveRaw = True
  1021. elif ".met" == fileExt: # Module Metadata
  1022. if g.dumpMeta: bSaveRaw = True
  1023. else: # Module
  1024. if e.metadata: # Module (with metadata)
  1025. if g.dumpRaw:
  1026. bSaveRaw = True
  1027. fileName += ".raw"
  1028. else: bSaveRaw = True # Not a module (without metadata) - always dump "as is"
  1029. if bSaveRaw: e.saveRaw(baseDir, fileName) # Save raw file data (compressed/encrypted)
  1030. if e.metadata: # Only for modules with metadata
  1031. with open(os.path.join(baseDir, "%s.txt" % e.name), "wt") as fo: # Dump module info
  1032. print >>fo, "%4d: %s" % (i+1, e)
  1033. Ext_DumpAll(e, fo) # Dump all Metadata Extensions
  1034. if not hasattr(e, "ModAttr"):
  1035. e.saveRaw(baseDir)
  1036. continue
  1037. compType = dCompType[e.ModAttr.compression_type]
  1038. data = e.getData()
  1039. if e.ModAttr.encrypted:
  1040. print >>sys.stderr, "- Module %s is encrypted" % e.name
  1041. if "huff" == compType:
  1042. assert e.length == e.ModAttr.uncompressed_size
  1043. nChunks, left = divmod(e.length, 0x1000)
  1044. assert 0 == left
  1045. plain = decompress(data, compType, e.length)
  1046. hashChecked = False
  1047. if "huff" != compType and hashlib.sha256(data).digest() == e.ModAttr.image_hash:
  1048. # print "%8s: data" % e.name
  1049. hashChecked = True
  1050. if plain:
  1051. if not hashChecked:
  1052. # assert hashlib.sha256(plain).digest() == e.ModAttr.image_hash
  1053. if hashlib.sha256(plain).digest() == e.ModAttr.image_hash:
  1054. hashChecked = True
  1055. # print "%8s: plain" % e.name
  1056. with open(os.path.join(baseDir, "%s.mod" % e.name), "wb") as fo: fo.write(plain)
  1057. else:
  1058. if "huff" == compType:
  1059. chunks = HUFF_chunks(nChunks, data)
  1060. chunks.save(os.path.join(baseDir, "%s.%s" % (e.name, compType)))
  1061. if g.dumpChunks: chunks.dump(os.path.join(baseDir, "%s" % e.name))
  1062. else:
  1063. with open(os.path.join(baseDir, "%s.%s" % (e.name, compType)), "wb") as fo:
  1064. fo.write(data)
  1065. if not hashChecked: print >>sys.stderr, "- hash %s.%s[%s]: %s" % (self.partition_name, e.name, compType, e.ModAttr.image_hash.encode("hex"))
  1066. # print
  1067. #***************************************************************************
  1068. #***************************************************************************
  1069. #***************************************************************************
  1070. class HUFF_chunks:
  1071. def __init__(self, nChunks, data=None):
  1072. if isinstance(nChunks, str):
  1073. fn = nChunks
  1074. with open(fn, "rb") as f:
  1075. nChunks = struct.unpack("<L", f.read(4))
  1076. data = f.read()
  1077. self.nChunks = nChunks
  1078. self.data = data
  1079. self.a = []
  1080. base = 4 * self.nChunks
  1081. for v in struct.unpack_from("<%dL" % self.nChunks, self.data):
  1082. opt, offs = divmod(v, 0x40000000)
  1083. if len(self.a): self.a[-1].append(base + offs) # curr.offs is prev.end
  1084. self.a.append([base + offs, opt])
  1085. self.a[-1].append(len(data)) # Add End-Of-Data as last.end
  1086. for iChunk in xrange(self.nChunks): # Verify chunks
  1087. offs, opt, end = self.a[iChunk]
  1088. assert opt in (1, 3)
  1089. assert offs <= end
  1090. def save(self, fn):
  1091. with open(fn, "wb") as fo:
  1092. fo.write(struct.pack("<L", self.nChunks))
  1093. fo.write(self.data)
  1094. def __len__(self): return self.nChunks
  1095. def __getitem__(self, iChunk):
  1096. offs, opt, end = self.a[iChunk]
  1097. return opt, self.data[offs:end]
  1098. def dump(self, baseName):
  1099. chunksDir = baseName + ".chunks"
  1100. if not os.path.exists(chunksDir): os.makedirs(chunksDir)
  1101. for iChunk in xrange(self.nChunks): # Verify chunks
  1102. opt, ab = self[iChunk]
  1103. with open(os.path.join(chunksDir, "%d.%08X" % (opt, iChunk*0x1000)), "wb") as fo: fo.write(ab)
  1104. #***************************************************************************
  1105. #***************************************************************************
  1106. #***************************************************************************
  1107. class FPT_Entry_Attributes:
  1108. dAreaType = {
  1109. FPT_AREATYPE_CODE: "Code",
  1110. FPT_AREATYPE_DATA: "Data",
  1111. }
  1112. FPT_ENTRY_ATTRIBUTES = ( # BitFields
  1113. ("type", 0, 6),
  1114. # ("rsvd0", 7, 14),
  1115. ("bwl0", 15, 15),
  1116. ("bwl1", 16, 16),
  1117. # ("rsvd1", 17, 23),
  1118. ("entry_invalid", 24, 31),
  1119. )
  1120. def __init__(self, dw): BitFields(self, dw, self.FPT_ENTRY_ATTRIBUTES)
  1121. def __str__(self):
  1122. r = [self.dAreaType[self.type]] + ListTrueBools(self, self.FPT_ENTRY_ATTRIBUTES)
  1123. if self.entry_invalid: r.append("entry_invalid")
  1124. return ", ".join(r)
  1125. # return "%s bwl0=%d, bwl1=%d, entry_valid=%02X" % (, self.bwl0, self.bwl1, self.entry_valid)
  1126. #***************************************************************************
  1127. #***************************************************************************
  1128. #***************************************************************************
  1129. class FPT_Entry:
  1130. FPT_ENTRY = (
  1131. ("name", "4s", ),
  1132. ("reserved", "L", ),
  1133. ("offset", "L", ),
  1134. ("length", "L", ),
  1135. ("reserved1", "L", ),
  1136. ("reserved2", "L", ),
  1137. ("reserved3", "L", ),
  1138. ("bf_attributes", "L", ), # class:FPT_ENTRY_ATTRIBUTES
  1139. )
  1140. def __init__(self, parent):
  1141. self.fpt = parent
  1142. self.fpt.stR.read(self, self.FPT_ENTRY)
  1143. self.name = self.name.rstrip('\0')
  1144. self.attributes = FPT_Entry_Attributes(self.bf_attributes)
  1145. def getData(self):
  1146. return self.fpt.stR.getData(self.offset, self.length)
  1147. def __str__(self):
  1148. return "[%-4s] %8X:%-8X %s" % (self.name, self.offset, self.length, self.attributes)
  1149. #***************************************************************************
  1150. #***************************************************************************
  1151. #***************************************************************************
  1152. class FPT: # Flash Partition Table
  1153. MARKER = '$FPT'
  1154. FPT_HEADER = (
  1155. ("RomBypass", "16s", ),
  1156. ("HeaderMarker", "4s", MARKER),
  1157. ("NumFptEntries", "L", ),
  1158. ("HeaderVersion", "B", 0x20),
  1159. ("EntryVersion", "B", 0x10),
  1160. ("HeaderLength", "B", 0x20),
  1161. ("HeaderChecksum", "B", ),
  1162. ("TicksToAdd", "H", ),
  1163. ("TokensToAdd", "H", ),
  1164. ("reserved", "L", ),
  1165. ("FlashLayout", "L", ),
  1166. ("FitcMajor", "H", ),
  1167. ("FitcMinor", "H", ),
  1168. ("FitcHotfix", "H", ),
  1169. ("FitcBuild", "H", ),
  1170. )
  1171. def __init__(self, ab, base=0):
  1172. self.stR = StructReader(ab, base)
  1173. self.stR.read(self, self.FPT_HEADER) # Read header
  1174. self.partitions = [FPT_Entry(self) for i in xrange(self.NumFptEntries)] # Read entries
  1175. def dump(self, flog=sys.stdout, baseDir=None):
  1176. print >>flog, "NumFptEntries: %d" % self.NumFptEntries
  1177. print >>flog, "HeaderVersion: %d.%d" % divmod(self.HeaderVersion, 16)
  1178. print >>flog, "EntryVersion: %d.%d" % divmod(self.EntryVersion, 16)
  1179. print >>flog, "HeaderLength: 0x%02X" % self.HeaderLength
  1180. print >>flog, "HeaderChecksum: 0x%02X" % self.HeaderChecksum
  1181. print >>flog, "TicksToAdd: 0x%04X" % self.TicksToAdd
  1182. print >>flog, "TokensToAdd: 0x%04X" % self.TokensToAdd
  1183. print >>flog, "FlashLayout: 0x%X" % self.FlashLayout
  1184. print >>flog, "Fitc: %d.%d.%d.%d" % (self.FitcMajor, self.FitcMinor, self.FitcHotfix, self.FitcBuild)
  1185. print >>flog, "ROM Bypass instruction: %s" % (self.RomBypass.encode("hex") if self.RomBypass.rstrip('\0') else "<None>")
  1186. for i,e in enumerate(self.partitions):
  1187. print >>flog, "%4d: %s" % (i+1, e)
  1188. if baseDir is None: continue # Do not write files
  1189. data = e.getData()
  1190. if data:
  1191. with open(os.path.join(baseDir, "%08X.%d.%s.part" % (e.offset, e.attributes.type, e.name)), "wb") as fo:
  1192. fo.write(data)
  1193. #***************************************************************************
  1194. #***************************************************************************
  1195. #***************************************************************************
  1196. class ME11:
  1197. def __init__(self, fn):
  1198. self.fn = fn
  1199. with open (self.fn, "rb") as f: self.ab = f.read()
  1200. for o in xrange(0, len(self.ab), 0x1000): # Search for FPT
  1201. if not self.ab[o+16:o+16+4] == FPT.MARKER: continue
  1202. self.fpt = FPT(self.ab, o)
  1203. break
  1204. else:
  1205. print "FPT not found"
  1206. self.fpt = None
  1207. # raise Error("FPT not found")
  1208. o = 0
  1209. self.CPDs = []
  1210. while True: # Search for CPDs
  1211. o = self.ab.find(CPD.MARKER, o)
  1212. if o < 0: break
  1213. if "\x01\x01\x10" == self.ab[o+8:o+11]:
  1214. # print "%s at %08X" % (CPD.MARKER, o)
  1215. print ". Processing CPD at 0x%X" % o
  1216. self.CPDs.append(CPD(self.ab, o))
  1217. # try: except: pass
  1218. o += 4
  1219. def dump(self):
  1220. baseDir = os.path.splitext(self.fn)[0]
  1221. # baseDir = None
  1222. if baseDir:
  1223. if not os.path.exists(baseDir): os.makedirs(baseDir)
  1224. flog = open(baseDir + ".txt", "wt")
  1225. else: flog = sys.stdout
  1226. if self.fpt: self.fpt.dump(flog, baseDir)
  1227. for cpd in self.CPDs:
  1228. print >>flog
  1229. cpd.dump(flog, baseDir)
  1230. if flog != sys.stdout: flog.close()
  1231. #***************************************************************************
  1232. #***************************************************************************
  1233. #***************************************************************************
  1234. def main(argv):
  1235. for fn in argv[1:]:
  1236. me = ME11(fn)
  1237. me.dump()
  1238. if __name__=="__main__": main(sys.argv)