Skip to content

Commit

Permalink
Keyfile support
Browse files Browse the repository at this point in the history
Removed broken signing code
Updated documentation
  • Loading branch information
acabey committed Oct 4, 2017
1 parent b0d49f0 commit 7baeed2
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 123 deletions.
42 changes: 32 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,7 @@ Detect type of file as well as partial files (ie. extracted bootloader)


# Usage:
python3 flash-dump.py image.bin -c cpukey -x section

-c CPU key
Required to decrypt bootloaders CD and onward on retails with CB >= 1920
Required to decrypt keyvault
Required to replace encrypted sections

ex. python3 flash-dump.py image.bin -c 48a3e35253c20bcc796d6ec1d5d3d811
./flash-dump.py image.bin -c cpukey -x section

-x Extract section(s)

Expand Down Expand Up @@ -46,6 +39,7 @@ Detect type of file as well as partial files (ie. extracted bootloader)
ex. python3 flash-dump.py image.bin -r se se_patched_plain.bin
ex. python3 flash-dump.py image.bin -r kernel xboxkrnl_patched_plain_dec.bin


-d Decrypt section(s)

Attempts to decrypt the given sections in place or treat input as decrypted
Expand All @@ -56,9 +50,37 @@ Detect type of file as well as partial files (ie. extracted bootloader)

ex. python3 flash-dump.py image.bin -d sb smc -x sb sd smc

--debug Verbose output for debugging

-v Version
-c CPU key
Required to decrypt bootloaders CD and onward on retails with CB >= 1920
Required to decrypt keyvault
Required to replace encrypted sections

By default the programs looks for a binary file, "cpukey" in the "data" directory

ex. python3 flash-dump.py image.bin -c 48a3e35253c20bcc796d6ec1d5d3d811


-k Key file path

By default the programs looks for a binary file, "keyfile" in the "data" directory

This file should be the binary representation of the 1BL key

ex. python3 flash-dump.py image.bin -x kernel -k /path/to/keys.txt

-s Sign
Provided file must be decrypted (plaintext) SD

Signs the given 4BL with the 4BL private key

ex. python3 flash-dump.py -s SD_dec.bin


--debug Verbose output for debugging


-v Version

# Requirements:
* libssl
Expand Down
6 changes: 4 additions & 2 deletions bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,10 @@ def __init__(self, data_encrypted, header):
"""
def sign(self):

raise NotImplementedError('bootloader signing is incomplete, disable this check if you know what you are doing')

m = sha()
bl4_key = RSA.construct((Constants.BL4_MOD, Constants.BL4_PUBEXP, Constants.BL4_PRIVEXP, Constants.BL4_P, Constants.BL4_Q))
bl4_key = Constants.BL4_KEY

# Compute SHA1 digest of the header (0x0:0x10) and payload data (0x120:)
#
Expand All @@ -394,7 +396,7 @@ def sign(self):
m.update(bl4data[0:0x10])
m.update(bl4data[0x120:])
digest = m.digest()
print(digest)
print(digest[0:0x14])

with open('output/bl4_readsignature.bin', 'w+b') as sigout:
sigout.write(digest)
Expand Down
55 changes: 33 additions & 22 deletions common.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,47 @@
#!/usr/bin/env python3

DEBUG = True
DEBUG = False

class Constants():
SHADOWBOOT_SIZE = 851968 # Seems to be the max size (bytes) for a shadowboot ROM based on official samples. Don't where limit is imposed
HV_SIZE = 0x40000
SECRET_ZERO = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
SECRET_1BL = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
CPUKEY = None

try:
with open('data/keyfile', 'rb') as keyfile:
SECRET_1BL = keyfile.read()
except Exception as e:
SECRET_1BL = None

try:
with open('cpukey', 'rb') as keyfile:
CPUKEY = keyfile.read()
except Exception as e:
CPUKEY = None

# BL4 Key stuff
BL4_SALT = b'XBOX_ROM_4'

endianness = 'little'
signed = False
modulusFilePath = './data/keys/LE/BL4_signKey_Modulus.bin'
publicFilePath = './data/keys/LE/BL4_signKey_DP.bin'
privateFilePath = './data/keys/LE/BL4_signKey_DQ.bin'
pFilePath = './data/keys/LE/BL4_signKey_P.bin'
qFilePath = './data/keys/LE/BL4_signKey_Q.bin'
with open(modulusFilePath, 'rb') as modulusFile:
with open(publicFilePath, 'rb') as publicFile:
with open(privateFilePath, 'rb') as privateFile:
with open(pFilePath, 'rb') as pFile:
with open(qFilePath, 'rb') as qFile:
BL4_MOD = int.from_bytes(modulusFile.read(), byteorder=endianness, signed=signed)
BL4_PUBEXP = int.from_bytes(publicFile.read(), byteorder=endianness, signed=signed)
BL4_PRIVEXP = int.from_bytes(privateFile.read(), byteorder=endianness, signed=signed)
BL4_P = int.from_bytes(pFile.read(), byteorder=endianness, signed=signed)
BL4_Q = int.from_bytes(qFile.read(), byteorder=endianness, signed=signed)

unsignedFilePath = './data/keys/Expected/BL4_unsigned.bin'
#endianness = 'little'
#signed = False
#modulusFilePath = './data/keys/LE/BL4_signKey_Modulus.bin'
#publicFilePath = './data/keys/LE/BL4_signKey_DP.bin'
#privateFilePath = './data/keys/LE/BL4_signKey_DQ.bin'
#pFilePath = './data/keys/LE/BL4_signKey_P.bin'
#qFilePath = './data/keys/LE/BL4_signKey_Q.bin'
#with open(modulusFilePath, 'rb') as modulusFile:
# with open(publicFilePath, 'rb') as publicFile:
# with open(privateFilePath, 'rb') as privateFile:
# with open(pFilePath, 'rb') as pFile:
# with open(qFilePath, 'rb') as qFile:
# BL4_MOD = int.from_bytes(modulusFile.read(), byteorder=endianness, signed=signed)
# BL4_PUBEXP = int.from_bytes(publicFile.read(), byteorder=endianness, signed=signed)
# BL4_PRIVEXP = int.from_bytes(privateFile.read(), byteorder=endianness, signed=signed)
# BL4_P = int.from_bytes(pFile.read(), byteorder=endianness, signed=signed)
# BL4_Q = int.from_bytes(qFile.read(), byteorder=endianness, signed=signed)
#BL4_KEY = RSA.construct((Constants.BL4_MOD, Constants.BL4_PUBEXP, Constants.BL4_PRIVEXP, Constants.BL4_P, Constants.BL4_Q))

#unsignedFilePath = './data/keys/Expected/BL4_unsigned.bin'


def dbgprint(string):
Expand Down
Binary file added data/cpukey
Binary file not shown.
Binary file added data/keyfile
Binary file not shown.
143 changes: 54 additions & 89 deletions flash-dump.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,6 @@
Usage: python3 flash-dump.py image.bin -c cpukey -x section
-c CPU key
Required to decrypt bootloaders CD and onward on retails with CB >= 1920
Required to decrypt keyvault
Required to replace encrypted sections
ex. python3 flash-dump.py image.bin -c 48a3e35253c20bcc796d6ec1d5d3d811
-x Extract section(s)
Valid sections are:
Expand Down Expand Up @@ -96,20 +88,30 @@
# ex. python3 flash-dump.py image.bin -bs smc_plain.bin sb_plain.bin sc_plain.bin sd_plain.bin se_plain.bin
#-k Key file path
#
# By default the programs looks for a plaintext file called "keys" in the local directory
#
# Provided file must follow the format where line 1 is the 1BL RC4 key and line 2 the 4BL private key
#
# ex. python3 flash-dump.py image.bin -x kernel -k /path/to/keys.txt
#
#-s Sign
# Provided file must be decrypted (plaintext) SD
#
# Signs the given 4BL with the 4BL private key
#
# ex. python3 flash-dump.py -s SD_dec.bin
-c CPU key
Required to decrypt bootloaders CD and onward on retails with CB >= 1920
Required to decrypt keyvault
Required to replace encrypted sections
By default the programs looks for a binary file, "cpukey" in the "data" directory
ex. python3 flash-dump.py image.bin -c 48a3e35253c20bcc796d6ec1d5d3d811
-k Key file path
By default the programs looks for a binary file, "keyfile" in the "data" directory
This file should be the binary representation of the 1BL key
ex. python3 flash-dump.py image.bin -x kernel -k /path/to/keys.txt
-s Sign
Provided file must be decrypted (plaintext) SD
Signs the given 4BL with the 4BL private key
ex. python3 flash-dump.py -s SD_dec.bin
--debug Verbose output for debugging
Expand Down Expand Up @@ -171,17 +173,39 @@ def main(argv):
Load NAND/shadowboot structures
============================================================================
"""
Image.identifyAvailableStructures(args.target)
availablesections = Image.getAvailableStructures()
try:
Image.identifyAvailableStructures(args.target)
availablesections = Image.getAvailableStructures()
except Exception as e:
failprint('Failed to identify available structures: ' + str(e))


"""
============================================================================
Manipulate input
============================================================================
"""
Constants.DEBUG = args.debug

dbgprint('args: ' + str(args))

# CPU key if available
if not args.cpukey == None:
try:
Constants.CPUKEY = bytes.fromhex(args.cpukey[0])
except Exception as e:
Constants.CPUKEY = None
failprint('Failed to set given CPU key: ' + str(e))

# BL1 key if available
if not args.keyfile == None:
try:
with open(args.keyfile[0], 'rb') as keyfile:
Constants.SECRET_1BL = keyfile.read()
except Exception as e:
Constants.SECRET_1BL = None
failprint('Failed to set 1BL key from keyfile' + str(e))

# Enumerate if available
if not args.enumerate == None:
for section in args.enumerate:
Expand Down Expand Up @@ -245,6 +269,12 @@ def main(argv):
This is code from prior revisions that I have not refactored.
It will be kept (inaccesible) as a reference until implemented in new structure
============================================================================
"""

"""
============================================================================
NAND Extras
============================================================================
"""
if False:
Expand All @@ -257,15 +287,13 @@ def main(argv):
# Make sure SMC is not null
if not all(b == 0 for b in smcdata):
smc = SMC(smcdata, nandheader.smcoffset)
# TODO Unifying interface for nandsections
# nandsections.append(smc)
print('Found valid SMC at ' + str(hex(smc.offset)))
else:
print('SMC is null, skipping SMC')
else:
print('SMC offset is null, skipping SMC')

# TODO
# Check for Keyvault
# Because the keyvault's length is stored in its header, we first create the header object
if not nandheader.kvoffset == 0:
Expand All @@ -289,69 +317,6 @@ def main(argv):
else:
print('Keyvault offset is null, skipping keyvault')

"""
============================================================================
NAND Extras
============================================================================
"""

# Check for 5BL (CF/SE)
# Retails will have 5BL (CF) and 6BL (CG)
if imagetype == ImageType.Retail:
pass
# Check for 5BL (CF)

# Check for 6BL (CG)

# Check for 5BL2 (CF)

# Check for 6BL2 (CG)
# Devkits and shadowboot ROMs will have 5BL (SE)
else:
# Check for 5BL (SE)
if bl4header:
bl5offset = bl4header.offset + bl4header.length
image.seek(bl5offset, 0)
bl5headerdata = image.read(BootloaderHeader.HEADER_SIZE)
# Make sure BL5 header is not null
if not all(b == 0 for b in bl5headerdata):
bl5header = BootloaderHeader(bl5headerdata, bl5offset)

image.seek(bl5offset, 0)
bl5data = image.read(bl5header.length)
# Make sure BL4 is not null
if not all(b == 0 for b in bl5data):
# Make proper bootloader object
bl5 = Bootloader(bl5data, bl5header)
nandsections.append(bl5)
print('Found valid BL5: ' + bl5.header.name + ' at ' + str(hex(bl5.header.offset)))
if bl4:
bl5.updateKey(bl4.key)
bl5.decrypt()
print('Decrypted BL5')
else:
print('BL4 is null, cannot decrypt BL5')
else:
print('BL4 data is null, skipping BL5')
else:
print('BL4 header is null, skipping BL5')
else:
print('BL4 (header) missing, skipping BL5')

# Determine if CPU key required (CD)
"""
Check versions to see if CPU key will be required
Retail:
- CB version >= 1920
- Required for CD, keyvault, SMC config?
XDK
- Required for keyvault
Shadowboot
- Not required
"""

sys.exit(0)

Expand Down

0 comments on commit 7baeed2

Please sign in to comment.