Join Us and become a Member for a Verified Badge to access private areas with the latest PS4 PKGs.
Status
Not open for further replies.
Today PlayStation 4 developer @zecoxao shared a Python Script (hdd_script.py with updated revisions below) via Twitter from 'the usual suspect' that converts the wrapped key blobs stored in sflash0 into eap_hdd_key, which can then be used to retrieve data from any PS4 console (DevKit, Retail, TestKit) with an SFlash dump! 😍

Download: hdd_script.py (5.29 KB) / hdd_script.py (v2) (5.36 KB - outputs the keys to a file called keys.bin, to be used on Linux with Cryptmount) / hdd_script.py (v2) Mirror / hdd_script.py (v3) (5.57 KB - error handling on sflash0 size and on magic check, in order for the hdd script to work, the dump of sflash0 must have EXACTLY 32MB - 0x2000000 bytes): hdd_script.py (v3) Mirror / Mounting PS4 HDD on WSL2 (Windows Subsystem for Linux 2) Guide with PS4 HDD Script by Anonymous (hdd_script.zip - 2.11 KB - includes hdd_script.py) and WSL2_UFS_Support_Read_Only.rar (2.83 GB - WSL2 UFS RO Kernel)
Code:
#!/usr/bin/env python

from binascii import hexlify as hx
from pathlib import Path

import sys, os, struct
import hashlib, hmac

from Crypto.Cipher import AES
from Crypto.Util import Counter

def aes_encrypt_ecb(key, data):
    crypto = AES.new(key, AES.MODE_ECB)
    return crypto.encrypt(data)

def aes_decrypt_ecb(key, data):
    crypto = AES.new(key, AES.MODE_ECB)
    return crypto.decrypt(data)

def aes_encrypt_cbc(key, iv, data):
    crypto = AES.new(key, AES.MODE_CBC, iv)
    return crypto.encrypt(data)

def aes_decrypt_cbc(key, iv, data):
    crypto = AES.new(key, AES.MODE_CBC, iv)
    return crypto.decrypt(data)

def hmac_sha256(key, data):
    return hmac.new(key=key, msg=data, digestmod=hashlib.sha256).digest()

portability_seed_key = 'E973A44C578757A73492625D2CE2D76B'.decode('hex')
portability_seed = 'DF0C2552DFC7F4F089B9D52DAA0E572A'.decode('hex')

# generate portability key
portability_key = aes_encrypt_ecb(portability_seed_key, portability_seed)

# generate keys from seeds
eap_hdd_key_blob_key1_seed = '7A49D928D2243C9C4D6E1EA8F5B4E229317E0DCAD2ABE5C56D2540572FB4B6E3'.decode('hex')
eap_hdd_key_blob_key2_seed = '921CE9C8184C5DD476F4B5D3981F7E2F468193ED071E19FFFD66B693534689D6'.decode('hex')

eap_hdd_key_blob_key1 = aes_encrypt_ecb(portability_key, eap_hdd_key_blob_key1_seed)
eap_hdd_key_blob_key2 = aes_encrypt_ecb(portability_key, eap_hdd_key_blob_key2_seed)

use_new_blob = False

SFLASH0 = open('sflash0', 'rb')
length = Path('sflash0').stat().st_size
if length != 0x2000000:
    raise SystemExit("not correct size! leaving...")
data = SFLASH0.read()

# ICC NVS: block #4, offset 0x200, size 0x40/0x60, magic 0xE5E5E501 (big endian)
#eap_hdd_wrapped_key = <PASTE KEY HERE>.decode('hex')

print('[DEBUG] ' + hx(data[0x1C91FC:0x1C9200]))

if data[0x1C91FC:0x1C9200] == '\xE5\xE5\xE5\x01':
    print('[DEBUG] ' + hx(data[0x1C9240:0x1C9250]))
else:
    raise SystemExit("not correct magic! leaving...")

if data[0x1C9240:0x1C9250] == '\xFF' * 16:
    eap_hdd_wrapped_key = data[0x1C9200:0x1C9240]
    print('[DEBUG] LEN 40 | ' + hx(eap_hdd_wrapped_key))
else:
    eap_hdd_wrapped_key = data[0x1C9200:0x1C9260]
    print('[DEBUG] LEN 60 | ' + hx(eap_hdd_wrapped_key))

# ICC NVS: block #4, offset 0x60, size 0x4
#smi_version = 0x03700000
#smi_version = 0x03150000

print('[DEBUG] ' + hx(data[0x1C9060:0x1C9064]))

smi_version = struct.unpack('<i',data[0x1C9060:0x1C9064])[0]



# verify and decrypt eap key blob
if use_new_blob:
    eap_hdd_key_blob_enc = 'CFFDCB6ECAE612B7A30A9EDBD8F77E261D629DE5E6CA3F22F439211AC033884F4B5D7D16D0A6F65D3173A2586CF819C7C6F437444C1D9499F6EBC4145E0BBAABC1DE7C63ED1F5A1E1946358C7F181B1FAB6DAB31195D8E611A1CB81B9ACF8B38FF21029FAB568C7A1BCC3E2FBEB25B13F1AFD6A3599EEF09EAEBE32684FDDA29'.decode('hex')
    eap_hdd_key_blob_sig = '4798B78DD422601F26A32A1FEC5CAB8B256E50958E0B11A31D77DEE201D4D00E'.decode('hex')
    eap_hdd_key_blob_iv = '462500ECC487F0A8C2F39511E020CC59'.decode('hex')
else:
    eap_hdd_key_blob_enc = 'E073B691E177D39642DF2E1D583D0E9A5A49EDF72BE9412E2B433E51490CE973234B84F49E949F03727331D5456F4598F2EDE6D0C11483B84CE3283243D0DE9DC379E915301A805DFAEB292B30374C9BF1C59041509BF11D215C35D5C08E3330807C8229C930FAB88672C4CF7DACA881C323D72346CA07921DB806FC242A2ED1'.decode('hex')
    eap_hdd_key_blob_sig = 'ED4F32C095847C6D3143EFFD61E7582F75F24465855C4E94DAF34885D8D03463'.decode('hex')
    eap_hdd_key_blob_iv = '3286EA97F3E92C434E1DC170C9289003'.decode('hex')

selected_key = eap_hdd_key_blob_key1
computed_signature = hmac_sha256(selected_key[0x10:0x20], eap_hdd_key_blob_enc)
if computed_signature != eap_hdd_key_blob_sig:
    selected_key = eap_hdd_key_blob_key2
    computed_signature = hmac_sha256(selected_key[0x10:0x20], eap_hdd_key_blob_enc)
    if computed_signature != eap_hdd_key_blob_sig:
        print('error: invalid signature')
        sys.exit()
eap_hdd_key_blob = aes_decrypt_cbc(selected_key[0x00:0x10], eap_hdd_key_blob_iv, eap_hdd_key_blob_enc)
if not eap_hdd_key_blob.startswith('SCE_EAP_HDD__KEY'):
    print('error: invalid magic')
    sys.exit()

eap_hdd_key_blob = 'SCE_EAP_HDD__KEY' + \
    'BB6CD66DDC671FAC3664F7BF5049BAA8C4687904BC31CF4F2F4E9F89FA458793811745E7C7E80D460FAF2326550BD7E4D2A0A0D9729DE5D2117D70676F1D55748DC17CDF29C86A855F2AE9A1AD3E915F0000000000000000000000000000000000000000000000000000000000000000'.decode('hex')

if use_new_blob:
    eap_hdd_unwrapped_key = aes_decrypt_cbc(eap_hdd_key_blob[0x60:0x70], '\0' * 0x10, eap_hdd_wrapped_key[:0x40])
else:
    eap_hdd_unwrapped_key = aes_decrypt_cbc(eap_hdd_key_blob[0x50:0x60], '\0' * 0x10, eap_hdd_wrapped_key[:0x40])
#print('eap_hdd_unwrapped_key', eap_hdd_unwrapped_key.encode('hex').upper())

eap_hdd_key_offset = 0x10 if (smi_version == 0xFFFFFFFF or smi_version < 0x4000000) else 0x20
eap_hdd_unwrapped_key_dec = aes_decrypt_cbc(eap_hdd_key_blob[eap_hdd_key_offset:eap_hdd_key_offset + 0x10], '\0' * 0x10, eap_hdd_unwrapped_key)
if eap_hdd_unwrapped_key_dec[0x10:0x20] != '\0' * 0x10:
    eap_hdd_unwrapped_key_dec = aes_decrypt_cbc(eap_hdd_key_blob[eap_hdd_key_offset:eap_hdd_key_offset + 0x10], '\0' * 0x10, eap_hdd_wrapped_key[:0x10])

if use_new_blob:
    eap_partition_key = hmac_sha256(eap_hdd_unwrapped_key_dec[:0x10], eap_hdd_key_blob[0x40:0x50])
else:
    eap_partition_key = hmac_sha256(eap_hdd_unwrapped_key_dec[:0x10], eap_hdd_key_blob[0x30:0x40])

tweak_key = eap_partition_key[0x00:0x10]
data_key = eap_partition_key[0x10:0x20]

print('XTS data key:', data_key.encode('hex').upper())
print('XTS tweak key:', tweak_key.encode('hex').upper())

keys = open('keys.bin', 'wb')
keys.write(data_key)
keys.write(tweak_key)
As previously noted, in PS4 6.50 Firmware Sony introduced a new EAP HDD kernel... and this comes following a Guide to Obtain Your PS4 SFlash via PS4 Root FTP Server, a Decrypted EAP Partition Key Script (SAMU will decrypt the key), some PS4 EAP Kernel Dumps, a PS4 Registry Editor to examine the resulting system.eap file and a PS4 EAP Key Dumping and Decrypting Guide video tutorial.

Then came a PS4 HDD Reading Config File for Cryptmount and details on how to automatically dump the PS4 EAP Key to /etc/cryptsetp/eap_hdd_key.bin via PSXITArch Linux v2 without using OrbisMAN, SFlash0Unpack and PS4 SFlash0 Tools to unpack SFlash0 files in PS4 Flash dumps alongside a Python version, a Guide for Mounting a PS4 HDD in Linux on PC and a PS4-EAP-KEY-DUMPER-672.bin payload that dumps the PS4 EAP Key to /mnt/usb0/eap_key.bin for use with Jailbroken PS4 6.72 consoles.

Finally, below is a brief summary by @CelesteBlue via Twitter of what's now possible with the hdd_script.py from the Tweets below to quote:
  • That means from now anyone can read his PS4 HDD/SSD data after having used a hardware flasher to dump its flash memory.
  • Before that, only exploited PS4 consoles could, by dumping running kernel memory or with kernel execution.
Summary:

As of now, to be able to access your PS4 HDD/SSD content you can do:
  • for PS4s on FWs <= 6.72: dump by software your PS4 eap_hdd_key using kernel exploit.
  • for PS4s on any FW: dump by hardware your PS4 sflash0 then run the script to convert to eap_hdd_key.
Spoiler: Related Tweets

Decided to create a repo that specializes in decryption of retail ps4 eap hdd partitions:
Credits to @rajeshca911 (Twitter) for supplying the ps4 pro dumps in order to test this.

Note that if your ps4 is a phat 1000 or 1100 model, IVOFFSET will work as 0.
How to mount EAP partitions on Windows:

Mounting PS4 HDD On Windows, Only Specific Partitions
Requirements:
  • .wslconfig (for this you also need wsl2 installed, wsl1 should work as well)
  • bzImage (this is ufs readonly! for ufs rw you need to compile your own bzImage)
Both .wslconfig are place on C:\Users\(your_username)\ (in my case it's zecoxao)

cmtab
eap key partitions (i've chosen eap_vsh, update, user)
folders (according to cmtab)
and of course, your keys

in the case of user partition the number of the partition will be 27 so you do this
subtract 27 with 1
this will give 26
then you left shift 32
this will give 111669149696

Modify .wslconfig and cmtab accordingly! (path to bzImage, directory where to mount eap partitions, keys.bin location, ivoffset, etc)

HDD_Script.py to Retrieve Data from Any PS4 Console via SFlash Dump!.jpg
 

Comments

I remember once using a similar method that allows reading the ps3 disk to be able to rescue the information (system boot failure) and so that the play will not format the hdd, I used a program that allowed me to rescue my first 40 GB isos.. :D Excellent work guys
 
Status
Not open for further replies.
Back
Top