From 56043cf24906f91282c62cc8d66ae9b7a05f8caf Mon Sep 17 00:00:00 2001 From: Mike Shal Date: Mon, 3 Nov 2014 11:24:55 -0500 Subject: [PATCH] Bug 1091668 - Import eme voucher script; r=ted, r=gerv --- python/eme/gen-eme-voucher.py | 264 ++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 python/eme/gen-eme-voucher.py diff --git a/python/eme/gen-eme-voucher.py b/python/eme/gen-eme-voucher.py new file mode 100644 index 000000000000..d5ef24e7d57d --- /dev/null +++ b/python/eme/gen-eme-voucher.py @@ -0,0 +1,264 @@ +#!/usr/bin/env python3.3 +# Copyright 2014 Adobe Systems Incorporated. All Rights Reserved. +# +# Adobe permits you to use, modify, and distribute this file in accordance +# with the terms of the Mozilla Public License, v 2.0 accompanying it. If +# a copy of the MPL was not distributed with this file, You can obtain one +# at http://mozilla.org/MPL/2.0/. +# +# Create perforce changelist of modules from FTP server + +import io, argparse,pyasn1, bitstring +from pyasn1.codec.der import encoder, decoder +from pyasn1.type import univ, namedtype, namedval, constraint +import hashlib + +# CodeSectionDigest ::= SEQUENCE { +# offset INTEGER -- section's file offset in the signed binary +# digestAlgorithm OBJECT IDENTIFIER -- algorithm identifier for the hash value below. For now only supports SHA256. +# digestValue OCTET STRING -- hash value of the TEXT segment. +# } +class CodeSectionDigest(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('offset', univ.Integer()), + namedtype.NamedType('digestAlgorithm', univ.ObjectIdentifier()), + namedtype.NamedType('digest', univ.OctetString())) + +# CodeSegmentDigest ::= SEQUENCE { +# offset INTEGER -- TEXT segment's file offset in the signed binary +# codeSectionDigests SET OF CodeSectionDigests +# } + +class SetOfCodeSectionDigest(univ.SetOf): + componentType = CodeSectionDigest() + +class CodeSegmentDigest(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('offset', univ.Integer()), + namedtype.NamedType('codeSectionDigests', SetOfCodeSectionDigest())) + +# ArchitectureDigest ::= SEQUENCE { +# cpuType ENUMERATED CpuType +# cpuSubType ENUMERATED CpuSubType +# CodeSegmentDigests SET OF CodeSegmentDigests +# } + +class SetOfCodeSegmentDigest(univ.SetOf): + componentType = CodeSegmentDigest() + +class CPUType(univ.Enumerated): + namedValues = namedval.NamedValues( + ('IMAGE_FILE_MACHINE_I386', 0x14c), + ('IMAGE_FILE_MACHINE_AMD64',0x8664 ) + ) + subtypeSpec = univ.Enumerated.subtypeSpec + \ + constraint.SingleValueConstraint(0x14c, 0x8664) + + +class ArchitectureDigest(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('cpuType', CPUType()), + namedtype.NamedType('cpuSubType', univ.Integer()), + namedtype.NamedType('CodeSegmentDigests', SetOfCodeSegmentDigest()) + ) + +# ApplicationDigest ::= SEQUENCE { +# version INTEGER +# digests SET OF ArchitectureDigest +# } +class SetOfArchitectureDigest(univ.SetOf): + componentType = ArchitectureDigest() + +class ApplicationDigest(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', univ.Integer()), + namedtype.NamedType('digests', SetOfArchitectureDigest()) + ) + +def meetsRequirements(items, requirements): + for r in requirements: + for n, v in r.items(): + if n not in items or items[n] != v: return False + return True + +# return total number of bytes read from items_in excluding leaves +def parseItems(stream, items_in, items_out): + bits_read = 0 + total_bits_read = 0 + + for item in items_in: + name = item[0] + t = item[1] + bits = 1 if ":" not in t else int(t[t.index(":") + 1:]) + + if ":" in t and t.find("bytes") >= 0: + bits = bits * 8 + + if len(item) == 2: + items_out[name] = stream.read(t) + bits_read += bits + total_bits_read += bits + elif len(item) == 3 or len(item) == 4: + requirements = list(filter(lambda x: isinstance(x, dict), item[2])) + sub_items = list(filter(lambda x: isinstance(x, tuple), item[2])) + + if not meetsRequirements(items_out, requirements): continue + + # has sub-items based on length + items_out[name] = stream.read(t) + bits_read += bits + total_bits_read += bits + + if len(item) == 4: + bit_length = items_out[name] * 8 + + if bit_length > 0: + sub_read, sub_total_read = parseItems(stream, sub_items, items_out) + bit_length -= sub_read + total_bits_read += sub_total_read + + if bit_length > 0: + items_out[item[3]] = stream.read('bits:' + str(bit_length)) + bits_read += bit_length + total_bits_read += bit_length + else: + raise Exception("unrecognized item" + pprint.pformat(item)) + + return bits_read, total_bits_read + + +class SectionHeader: + def __init__(self, stream): + items = [ + ('Name', 'bytes:8'), + ('VirtualSize', 'uintle:32'), + ('VirtualAddress', 'uintle:32'), + ('SizeOfRawData', 'uintle:32'), + ('PointerToRawData', 'uintle:32'), + ('PointerToRelocations', 'uintle:32'), + ('PointerToLineNumber', 'uintle:32'), + ('NumberOfRelocations', 'uintle:16'), + ('NumberOfLineNumbers', 'uintle:16'), + ('Characteristics', 'uintle:32') + ] + self.items = {} + _, self.bits_read = parseItems(stream, items, self.items) + + self.sectionName = self.items['Name'].decode('utf-8') + self.offset = self.items['PointerToRawData'] + + +class COFFFileHeader: + def __init__(self, stream): + items = [ + ('Machine', 'uintle:16'), + ('NumberOfSections', 'uintle:16'), + ('TimeDateStamp', 'uintle:32'), + ('PointerToSymbolTable', 'uintle:32'), + ('NumberOfSymbols', 'uintle:32'), + ('SizeOfOptionalHeader', 'uintle:16'), + ('Characteristics', 'uintle:16') + ] + self.items = {} + _, self.bits_read = parseItems(stream, items, self.items) + + #skip over optional header. + if self.items['SizeOfOptionalHeader'] > 0: + stream.read(self.items['SizeOfOptionalHeader'] * 8) + self.bits_read += self.items['SizeOfOptionalHeader'] * 8 + + #start reading section headers + numberOfSections = self.items['NumberOfSections'] + self.codeSectionHeaders = [] + + while numberOfSections > 0 : + sectionHeader = SectionHeader(stream) + if (sectionHeader.items['Characteristics'] & 0x20000000) == 0x20000000: + self.codeSectionHeaders.append(sectionHeader) + numberOfSections -= 1 + + self.codeSectionHeaders.sort(key=lambda header: header.offset) + + +def main(): + parser = argparse.ArgumentParser(description='PE/COFF Parser.') + parser.add_argument('-input', required=True, help="File to parse.") + parser.add_argument('-output', required=True, help="File to write to.") + parser.add_argument('-verbose', action='store_true', help="Verbose output.") + app_args = parser.parse_args() + + stream = bitstring.ConstBitStream(filename=app_args.input) + # find the COFF header. + # skip forward past the MSDOS stub header to 0x3c. + + stream.bytepos = 0x3c + + # read 4 bytes, this is the file offset of the PE signature. + peSignatureOffset = stream.read('uintle:32') + stream.bytepos = peSignatureOffset + + #read 4 bytes, make sure it's a PE signature. + signature = stream.read('uintle:32') + if signature != 0x00004550 : + return + + + archDigest = ArchitectureDigest() + + codeSegmentDigests = SetOfCodeSegmentDigest() + codeSegmentIdx = 0 + + #after signature is the actual COFF file header. + coffFileHeader = COFFFileHeader(stream) + + if coffFileHeader.items['Machine'] == 0x14c: + archDigest.setComponentByName('cpuType', CPUType('IMAGE_FILE_MACHINE_I386')) + elif coffFileHeader.items['Machine'] == 0x8664: + archDigest.setComponentByName('cpuType', CPUType('IMAGE_FILE_MACHINE_AMD64')) + + archDigest.setComponentByName('cpuSubType', 0) + + + for codeSectionHeader in coffFileHeader.codeSectionHeaders: + stream.bytepos = codeSectionHeader.offset + codeSectionBytes = stream.read('bytes:'+ str(codeSectionHeader.items['SizeOfRawData'])) + if codeSectionHeader.items['SizeOfRawData'] < codeSectionHeader.items['VirtualSize']: + #zero pad up to virtualSize + codeSectionBytes += "\0" * (codeSectionHeader.items['VirtualSize']-codeSectionHeader.items['SizeOfRawData']) + + digester = hashlib.sha256() + digester.update(codeSectionBytes) + digest = digester.digest() + + codeSectionDigest = CodeSectionDigest() + codeSectionDigest.setComponentByName('offset', codeSectionHeader.offset) + codeSectionDigest.setComponentByName('digestAlgorithm', univ.ObjectIdentifier('2.16.840.1.101.3.4.2.1')) + codeSectionDigest.setComponentByName('digest', univ.OctetString(digest)) + + setOfDigest = SetOfCodeSectionDigest() + setOfDigest.setComponentByPosition(0, codeSectionDigest) + + codeSegmentDigest = CodeSegmentDigest() + codeSegmentDigest.setComponentByName('offset', codeSectionHeader.offset) + codeSegmentDigest.setComponentByName('codeSectionDigests', setOfDigest) + + codeSegmentDigests.setComponentByPosition(codeSegmentIdx, codeSegmentDigest) + codeSegmentIdx += 1 + + archDigest.setComponentByName('CodeSegmentDigests', codeSegmentDigests) + + setOfArchDigests = SetOfArchitectureDigest() + setOfArchDigests.setComponentByPosition(0, archDigest) + + appDigest = ApplicationDigest() + + appDigest.setComponentByName('version', 1) + appDigest.setComponentByName('digests', setOfArchDigests) + + binaryDigest = encoder.encode(appDigest) + + outFile = open(app_args.output, 'wb') + outFile.write(binaryDigest) + +if __name__ == '__main__': + main()