Last active
June 18, 2019 00:35
-
-
Save shinyquagsire23/14cd897874112b7c7b6eca49f5de02a1 to your computer and use it in GitHub Desktop.
Python script for verifying ELF aboot images
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2 | |
import struct | |
import sys | |
import hashlib | |
from pyasn1_modules import rfc2437,rfc2459 | |
from pyasn1.codec.der import decoder | |
from pyasn1.codec.native import encoder | |
from Crypto.PublicKey import RSA | |
import rsa | |
from rsa import common, transform, core | |
def sha256tostring(val): | |
a,b,c,d = struct.unpack(">QQQQ", val) | |
return "%016x%016x%016x%016x" % (a, b, c, d) | |
def sha256(data): | |
mode = hashlib.sha256() | |
mode.update(data) | |
return mode.digest() | |
# from https://github.com/nelenkov/aboot-parser | |
def extract_raw_hash(signature, pub_key, is_sha256): | |
hash_size = 0x20 if is_sha256 else 0x18 | |
keylength = common.byte_size(pub_key.n) | |
encrypted = transform.bytes2int(signature) | |
decrypted = core.decrypt_int(encrypted, pub_key.e, pub_key.n) | |
clearsig = transform.int2bytes(decrypted, keylength) | |
# unpad | |
if (clearsig[0] != '\x00' or clearsig[1] != '\x01'): | |
raise Exception('Invalid signature format') | |
null_idx = clearsig.find('\x00', 2) | |
if null_idx < 0: | |
raise Exception('Invalid signature format') | |
padding = clearsig[2:null_idx] | |
if len(padding) != keylength - 2 - 1 - hash_size: | |
raise Exception('Invalid signature format') | |
if not all(p == '\xff' for p in padding): | |
raise Exception('Invalid signature format') | |
raw_hash = clearsig[null_idx + 1:] | |
if len(raw_hash) != hash_size: | |
raise Exception('Invalid signature format.') | |
return raw_hash | |
# BEGIN EXEC | |
contents = open(sys.argv[1], "rb").read() | |
e_magic, e_ident_4, e_ident_5, e_ident_6, e_ident_7, e_ident_8 = struct.unpack("<4sBBBBQ", contents[:0x10]) | |
e_type, e_machine, e_version, e_entry, e_phoff, e_shoff, e_flags, e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx = struct.unpack("<HHLLLLLHHHHHH", contents[0x10:0x34]) | |
if (e_ident_4 == 2): #ELF64 | |
e_type, e_machine, e_version, e_entry, e_phoff, e_shoff, e_flags, e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx = struct.unpack("<HHLQQQLHHHHHH", contents[0x10:0x40]) | |
print e_phentsize | |
if (e_magic != '\x7FELF'): | |
print "aboot image not an ELF!" | |
print "exiting..." | |
exit(0) | |
off_certs = 0 | |
vaddr_certs = 0 | |
hashes = [] | |
print "Sections:" | |
print "Type Offset Vaddr Paddr Filesz Memsz Align Perms Hash" | |
for i in range(0, e_phnum): | |
start = e_phoff + i * e_phentsize | |
p_type, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_flags, p_align = [0,0,0,0,0,0,0,0] | |
if (e_ident_4 == 2): #ELF64 | |
p_type, p_flags, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_align = struct.unpack("<LLQQQQQQ", contents[start:start+e_phentsize]) | |
else: | |
p_type, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_flags, p_align = struct.unpack("<LLLLLLLL", contents[start:start+e_phentsize]) | |
seg_type = p_flags >> 20 | |
seg_perm = p_flags & 0xFF | |
outstr = "" | |
if (seg_type == 0x70): | |
outstr += "HEADER " | |
elif (seg_type == 0x22): | |
outstr += "CERTS " | |
off_certs = p_offset | |
vaddr_certs = p_vaddr | |
else: | |
outstr += "SECT " | |
outstr += '%08x %08x %08x %08x %08x %08x ' % (p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_align) | |
outstr += 'R' if seg_perm & 4 else ' ' | |
outstr += 'W' if seg_perm & 2 else ' ' | |
outstr += 'X' if seg_perm & 1 else ' ' | |
outstr += ' ' | |
mode = hashlib.sha256() | |
mode.update(contents[p_offset:p_offset+p_filesz]) | |
outstr += mode.hexdigest() | |
# Don't calculate CERTS hash | |
if (seg_type == 0x22 or p_filesz == 0): | |
hashes = hashes + ['\0' * 0x20] | |
else: | |
hashes = hashes + [mode.digest()] | |
print outstr | |
# Parse out the hash table | |
a, h_sections, c, h_tablevaddr, h_totalsize, h_hashessize, h_signature, h_signaturesize, h_certchain, h_certchainsize = struct.unpack("<LLLLLLLLLL", contents[off_certs:off_certs+0x28]) | |
off_hashes = off_certs + 0x28 | |
off_sig = off_hashes + h_hashessize | |
off_certchain = off_sig + h_signaturesize | |
print "" | |
print "Stuff %08x %08x %08x" % (a, h_sections, c) | |
print "Hash Table @ %08x (vaddr %08x), total size %08x, hashes size %08x" % (off_hashes, h_tablevaddr, h_totalsize, h_hashessize) | |
print "Signature @ %08x (vaddr %08x), size %08x" % (off_sig, h_signature, h_signaturesize) | |
print "Cert chain @ %08x (vaddr %08x), size %08x" % (off_certchain, h_certchain, h_certchainsize) | |
print "" | |
print "Hash table:" | |
print "---" | |
print "Stored Calculated" | |
for i in range(0, e_phnum): | |
hashval = contents[off_hashes + i * 0x20:off_hashes + (i+1) * 0x20] | |
print sha256tostring(hashval) + " " + sha256tostring(hashes[i]) + " " + ("OK" if hashes[i] == hashval else "BAD") | |
# Save all of this aboot image's certificates | |
certs = [] | |
certs_dec = [] | |
certs_bytes = [] | |
cert_sizes = [] | |
cert_raw = contents[off_certchain:] | |
cert_offset = off_certchain | |
print "" | |
print "Certificates:" | |
while True: | |
certType = rfc2459.Certificate(); | |
try: | |
cert, cert_raw = decoder.decode(cert_raw, asn1Spec=certType) | |
cert_size = len(contents)-cert_offset-len(cert_raw) | |
#decoded_alt_names, _ = decoder.decode(cert.get_extension(0).get_data(), asn1Spec=SubjectAltName()) | |
cert_enc = encoder.encode(cert) | |
ou = cert_enc['tbsCertificate']['subject'][''][1][0]['value'][2:] | |
fname = ou.replace(' ', '_') + ".crt" | |
print "Found: %-40s" % (ou) + "...saving to " + fname | |
open(fname, "wb").write(contents[cert_offset:cert_offset + cert_size]) | |
cert_sizes = cert_sizes + [cert_size] | |
certs = certs + [cert_enc] | |
certs_dec = certs_dec + [cert] | |
certs_bytes = certs_bytes + [contents[cert_offset:cert_offset+cert_size]] | |
cert_offset += cert_size | |
#print cert.prettyPrint() | |
except: | |
break | |
# Grab all of our attestation cert values and print them nicely | |
key_vals = {} | |
print "" | |
print "Attestation Cert:" | |
for val in certs[0]['tbsCertificate']['subject']['']: | |
val = val[0] | |
if val['type'] == '2.5.4.11': | |
line = val['value'][2:] | |
if not line.split(' ')[0].isdigit(): | |
continue | |
split = line.split(' ') | |
key = split[2] | |
key_val = split[1] | |
key_vals[key] = int(key_val, 16) | |
print '%-10s %s' % (key, key_val) | |
# Calculate signature message | |
i_pad = 0x3636363636363636 | |
o_pad = 0x5c5c5c5c5c5c5c5c | |
SW_ID = int(key_vals['SW_ID']) | |
HW_ID = int(key_vals['HW_ID']) | |
h0 = sha256(contents[off_certs:off_sig]) | |
h1 = sha256(struct.pack(">Q", SW_ID ^ i_pad) + h0) | |
# Final message | |
m = struct.pack(">Q", HW_ID ^ o_pad) + h1 | |
hm = sha256(m) | |
rsakey = RSA.importKey(certs_bytes[0]) | |
print "" | |
sighash = '\0' * 0x20 | |
try: | |
sighash = extract_raw_hash(contents[off_sig:off_sig+h_signaturesize], rsakey, True) | |
except: | |
print "Failed to get signature hash!" | |
print "Signature Calculated" | |
print sha256tostring(sighash) + " " + sha256tostring(hm) + " " + ("OK" if sighash == hm else "BAD") | |
# END EXEC |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ python2 aboot-verify.py aboot_a.image | |
Sections: | |
Type Offset Vaddr Paddr Filesz Memsz Align Perms Hash | |
HEADER 00000000 00000000 00000000 000000b4 00000000 00000000 c6c6f853dd5d083775592bf864d12e967b805822410c28eab5c7d64c03251c3e | |
CERTS 00001000 8f685000 8f685000 000019a8 00002000 00001000 19ad70ec604b975aaf0a4fd21d24d5887ebca69f85e1681db354e89a367d84b1 | |
SECT 0008c2d0 8f6842d0 8f6842d0 00000020 00000020 00000004 R 795abb80f531cbdb11cd5ba90a566185b8ba767adf2e96ed81c329b1cc74180b | |
SECT 00008000 8f600000 8f600000 00097df4 000c879c 00008000 RWX c5b90657a99565917ca92db0ac64c421119d5481c37f65a8c7ca0b03e8a3a656 | |
Stuff 00000000 00000003 00000000 | |
Hash Table @ 00001028 (vaddr 8f685028), total size 00001980, hashes size 00000080 | |
Signature @ 000010a8 (vaddr 8f6850a8), size 00000100 | |
Cert chain @ 000011a8 (vaddr 8f6851a8), size 00001800 | |
Hash table: | |
--- | |
Stored Calculated | |
c6c6f853dd5d083775592bf864d12e967b805822410c28eab5c7d64c03251c3e c6c6f853dd5d083775592bf864d12e967b805822410c28eab5c7d64c03251c3e OK | |
0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 OK | |
795abb80f531cbdb11cd5ba90a566185b8ba767adf2e96ed81c329b1cc74180b 795abb80f531cbdb11cd5ba90a566185b8ba767adf2e96ed81c329b1cc74180b OK | |
c5b90657a99565917ca92db0ac64c421119d5481c37f65a8c7ca0b03e8a3a656 c5b90657a99565917ca92db0ac64c421119d5481c37f65a8c7ca0b03e8a3a656 OK | |
Certificates: | |
Found: General LGE attestation ...saving to General_LGE_attestation.crt | |
Found: General LGE attestation CA ...saving to General_LGE_attestation_CA.crt | |
Found: General LGE rootca ...saving to General_LGE_rootca.crt | |
Attestation Cert: | |
SHA256 0001 | |
MODEL_ID 0000 | |
SW_SIZE 000000A8 | |
OEM_ID 0031 | |
DEBUG 0000000000000002 | |
HW_ID 0009A0E100310000 | |
SW_ID 0000000000000009 | |
Signature Calculated | |
aee3035616e5a698b1606300c7323d1ab9b93a17ef4e3cf75022349f99fff4a0 aee3035616e5a698b1606300c7323d1ab9b93a17ef4e3cf75022349f99fff4a0 OK |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment