#!/usr/bin/env python """Detailed dump of a PE Authenticode signature (or a standalone .p7b). Pretty-prints every attribute of an Authenticode signature: content info, digest algorithms, certificates, signers, authenticated and unauthenticated attributes (including PKCS9 counter-signatures and MS-specific nested signatures). Can also verify the signature and extract the raw PKCS#7 blob. Example: $ python authenticode_reader.py --all driver.sys $ python authenticode_reader.py --check --allow-expired driver.sys $ python authenticode_reader.py --save out.p7b driver.sys """ import argparse import pathlib import sys import traceback import lief HAS_EXCEPTION = False class exceptions_handler(object): func = None def __init__(self, exceptions, on_except_callback=None): self.exceptions = exceptions self.on_except_callback = on_except_callback def __call__(self, *args, **kwargs): global HAS_EXCEPTION if self.func is None: self.func = args[0] return self try: return self.func(*args, **kwargs) except self.exceptions as e: HAS_EXCEPTION = True if self.on_except_callback is not None: self.on_except_callback(e) else: print("-" * 60, file=sys.stderr) print("Exception in {}: {}".format(self.func.__name__, e)) traceback.print_exc() print("-" * 60, file=sys.stderr) @exceptions_handler(Exception) def print_attr(indent: int, auth: lief.PE.Attribute): if auth.type == lief.PE.Attribute.TYPE.CONTENT_TYPE: print_content_type(indent, auth) elif auth.type == lief.PE.Attribute.TYPE.PKCS9_SIGNING_TIME: print_signing_time(indent, auth) elif auth.type == lief.PE.Attribute.TYPE.MS_SPC_STATEMENT_TYPE: print_ms_statement_type(indent, auth) elif auth.type == lief.PE.Attribute.TYPE.PKCS9_MESSAGE_DIGEST: print_pkcs_msg_dg(indent, auth) elif auth.type == lief.PE.Attribute.TYPE.PKCS9_COUNTER_SIGNATURE: print_pkcs_counter_sig(indent, auth) elif auth.type == lief.PE.Attribute.TYPE.GENERIC_TYPE: print_generic_type(indent, auth) elif auth.type == lief.PE.Attribute.TYPE.SPC_SP_OPUS_INFO: print_spc_sp_opus_info(indent, auth) elif auth.type == lief.PE.Attribute.TYPE.MS_SPC_NESTED_SIGN: print_ms_nested_sig(indent, auth) elif auth.type == lief.PE.Attribute.TYPE.PKCS9_AT_SEQUENCE_NUMBER: print_pkcs9_at_seq_number(indent, auth) else: print(" " * indent, type(auth), auth) @exceptions_handler(Exception) def print_pkcs9_at_seq_number(indent: int, auth: lief.PE.PKCS9AtSequenceNumber): print("{} PKCS #9 sequence number: {}".format(" " * indent, auth.number)) @exceptions_handler(Exception) def print_ms_nested_sig(indent: int, auth: lief.PE.MsSpcNestedSignature): print("{} MS Nested Signature:".format(" " * indent)) print_all(auth.signature, indent + 2) @exceptions_handler(Exception) def print_spc_sp_opus_info(indent: int, auth: lief.PE.SpcSpOpusInfo): if len(auth.program_name) > 0 and len(auth.more_info) > 0: print("{} Info: {} {}".format(" " * indent, auth.program_name, auth.more_info)) elif len(auth.program_name) > 0 and len(auth.more_info) == 0: print("{} Info: {}".format(" " * indent, auth.program_name)) elif len(auth.program_name) == 0 and len(auth.more_info) > 0: print("{} Info: {}".format(" " * indent, auth.more_info)) else: print("{} Info: ".format(" " * indent)) @exceptions_handler(Exception) def print_generic_type(indent: int, auth: lief.PE.GenericType): print( "{} Generic Type {} ({})".format( " " * indent, auth.oid, lief.PE.oid_to_string(auth.oid) ) ) @exceptions_handler(Exception) def print_content_type(indent: int, auth: lief.PE.ContentType): print( "{} Content Type OID: {} ({})".format( " " * indent, auth.oid, lief.PE.oid_to_string(auth.oid) ) ) @exceptions_handler(Exception) def print_signing_time(indent: int, auth: lief.PE.PKCS9SigningTime): print( "{} Signing Time: {}/{:02}/{:02} - {:02}:{:02}:{:02}".format( " " * indent, *auth.time ) ) @exceptions_handler(Exception) def print_ms_statement_type(indent: int, auth: lief.PE.MsSpcStatementType): print( "{} MS Statement type OID: {} ({})".format( " " * indent, auth.oid, lief.PE.oid_to_string(auth.oid) ) ) @exceptions_handler(Exception) def print_pkcs_msg_dg(indent: int, auth: lief.PE.PKCS9MessageDigest): print("{} PKCS9 Message Digest: {}".format(" " * indent, auth.digest.hex())) @exceptions_handler(Exception) def print_crt(indent: int, crt: lief.PE.x509): print("{} Version : {:d}".format(" " * indent, crt.version)) print("{} Issuer : {}".format(" " * indent, crt.issuer)) print("{} Subject : {}".format(" " * indent, crt.subject)) print("{} Serial Number : {}".format(" " * indent, crt.serial_number.hex())) print( "{} Signature Algorithm: {}".format( " " * indent, lief.PE.oid_to_string(crt.signature_algorithm) ) ) print( "{} Valid from : {}/{:02d}/{:02d} - {:02d}:{:02d}:{:02d}".format( " " * indent, *crt.valid_from ) ) print( "{} Valid to : {}/{:02d}/{:02d} - {:02d}:{:02d}:{:02d}".format( " " * indent, *crt.valid_to ) ) if len(crt.key_usage) > 0: print( "{} Key usage : {}".format( " " * indent, " - ".join(str(e).split(".")[-1] for e in crt.key_usage) ) ) if len(crt.ext_key_usage) > 0: print( "{} Ext key usage : {}".format( " " * indent, " - ".join(lief.PE.oid_to_string(e) for e in crt.ext_key_usage), ) ) if crt.rsa_info is not None: rsa_info = crt.rsa_info print("{} RSA key size : {}".format(" " * indent, rsa_info.key_size)) print("{} ===========================================".format(" " * indent)) @exceptions_handler(Exception) def print_pkcs_counter_sig(indent: int, auth: lief.PE.PKCS9CounterSignature): print("{} PKCS9 counter signature".format(" " * indent)) signer = auth.signer print("{} Version : {:d}".format(" " * indent, signer.version)) print( "{} Serial Number : {}".format(" " * indent, signer.serial_number.hex()) ) print("{} Issuer : {}".format(" " * indent, signer.issuer)) print("{} Digest Algorithm : {}".format(" " * indent, signer.digest_algorithm)) print( "{} Encryption Algorithm: {}".format( " " * indent, signer.encryption_algorithm ) ) print( "{} Encrypted Digest : {} ...".format( " " * indent, signer.encrypted_digest.hex()[:20] ) ) if len(signer.authenticated_attributes) > 0: print("{} Authenticated attributes:".format(" " * indent)) for sub_auth in signer.authenticated_attributes: print_attr(indent + 4, sub_auth) if len(signer.unauthenticated_attributes) > 0: print("{} Un-Authenticated attributes:".format(" " * indent)) for sub_auth in signer.unauthenticated_attributes: print_attr(indent + 4, sub_auth) @exceptions_handler(Exception) def print_all(sig: lief.PE.Signature, indent: int = 2): ci: lief.PE.ContentInfo = sig.content_info print("{}Signature version : {}".format(" " * indent, sig.version)) print("{}Digest Algorithm : {!s}".format(" " * indent, sig.digest_algorithm)) print("{}Content Info:".format(" " * indent)) print( "{} Content Type : {!s} ({})".format( " " * indent, ci.content_type, lief.PE.oid_to_string(ci.content_type) ) ) print("{} Digest Algorithm: {!s}".format(" " * indent, ci.digest_algorithm)) print("{} Digest : {!s}".format(" " * indent, ci.digest.hex())) print("{}Certificates".format(" " * indent)) for crt in sig.certificates: print_crt(indent, crt) print("{}Signer(s)".format(" " * indent)) for signer in sig.signers: print("{} Version : {:d}".format(" " * indent, signer.version)) print( "{} Serial Number : {}".format( " " * indent, signer.serial_number.hex() ) ) print("{} Issuer : {}".format(" " * indent, signer.issuer)) print( "{} Digest Algorithm : {}".format(" " * indent, signer.digest_algorithm) ) print( "{} Encryption Algorithm: {}".format( " " * indent, signer.encryption_algorithm ) ) print( "{} Encrypted Digest : {} ...".format( " " * indent, signer.encrypted_digest.hex()[:20] ) ) if len(signer.authenticated_attributes) > 0: print("{} Authenticated attributes:".format(" " * indent)) for auth in signer.authenticated_attributes: print_attr(indent + 4, auth) if len(signer.unauthenticated_attributes) > 0: print("{} Un-authenticated attributes:".format(" " * indent)) for auth in signer.unauthenticated_attributes: print_attr(indent + 4, auth) @exceptions_handler(Exception) def show_crts(sig: lief.PE.Signature, args): for crt in sig.certificates: print_crt(0, crt) @exceptions_handler(Exception) def process_signature(sig: lief.PE.Signature, args): if args.show_all: print_all(sig) if args.show_crt: show_crts(sig, args) if args.show_hash: print("Authentihash: {}".format(sig.content_info.digest.hex())) def main(): parser = argparse.ArgumentParser() parser.add_argument("file") parser.add_argument( "-a", "--all", action="store_true", dest="show_all", help="Show all information" ) parser.add_argument( "-c", "--crt", action="store_true", dest="show_crt", help="Show embedded x509 certificates", ) parser.add_argument( "-H", "--hash", action="store_true", dest="show_hash", help="Show the authentihash value", ) parser.add_argument( "-C", "--check", action="store_true", dest="check_sig", help="Check the signature", ) parser.add_argument( "-D", "--allow-expired", action="store_true", dest="allow_expired", help="Allow expired certificates", ) parser.add_argument( "-s", "--save", dest="ext_file_path", help="Extract and save the PKCS #7" ) # Logging setup logger_group = parser.add_argument_group("Logger") verbosity = logger_group.add_mutually_exclusive_group() verbosity.add_argument( "--debug", dest="main_verbosity", action="store_const", const=lief.logging.LEVEL.DEBUG, ) verbosity.add_argument( "--trace", dest="main_verbosity", action="store_const", const=lief.logging.LEVEL.TRACE, ) verbosity.add_argument( "--info", dest="main_verbosity", action="store_const", const=lief.logging.LEVEL.INFO, ) verbosity.add_argument( "--warn", dest="main_verbosity", action="store_const", const=lief.logging.LEVEL.WARN, ) verbosity.add_argument( "--err", dest="main_verbosity", action="store_const", const=lief.logging.LEVEL.ERROR, ) verbosity.add_argument( "--critical", dest="main_verbosity", action="store_const", const=lief.logging.LEVEL.CRITICAL, ) parser.set_defaults(main_verbosity=lief.logging.LEVEL.WARN) args = parser.parse_args() lief.logging.set_level(args.main_verbosity) if lief.is_pe(args.file): binary = lief.PE.parse(args.file) if binary is None: print("Error while parsing {}".format(args.file)) sys.exit(1) if args.check_sig: flags = lief.PE.Signature.VERIFICATION_CHECKS.DEFAULT if args.allow_expired: flags = lief.PE.Signature.VERIFICATION_CHECKS.SKIP_CERT_TIME res = binary.verify_signature(flags) print(res) if args.show_hash: print( "Binary MD5 authentihash: {}".format(binary.authentihash_md5.hex()) ) print( "Binary SHA-1 authentihash: {}".format(binary.authentihash_sha1.hex()) ) print( "Binary SHA-256 authentihash: {}".format( binary.authentihash_sha256.hex() ) ) for idx, sig in enumerate(binary.signatures): process_signature(sig, args) if args.ext_file_path: path = args.ext_file_path if idx > 0: path += str(idx) if not path.endswith(".p7b"): path += ".p7b" outpath = pathlib.Path(path) outpath.write_bytes(sig.raw_der) print("Signature saved to {}".format(outpath)) else: # Try as a regular p7b signature sig = lief.PE.Signature.parse(args.file) if sig is None: print("Fail to parse the signature") sys.exit(1) process_signature(sig, args) if __name__ == "__main__": main() if HAS_EXCEPTION: sys.exit(1)