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

Hello there! I am looking for help with a PS4 slim hdd mounting problem. I am running Debian and using cryptmount (Guide). Actually I tried 2 different PS4 systems before and they worked like a charm (both normal fat ones).

This time I have a PS4 Slim with the model 2216A and it doesn't work. I dumped the NOR several times by hardware like always. It was a 8-WSON chip that got dumped. Size seems perfect. Python script creates EAP Key file. I also unpacked the sflash0.bin and everything seemed in correct.
Code:
C:\PS4>python hdd_scriptv2.py
[DEBUG] e5e5e501
[DEBUG] 9efc8d9dd42597376edd4d827db96b84
[DEBUG] LEN 60 | f20c55b5700c355aef583722de5a99550c6b9cb0d228f5fed3aa4d680b9604e8e2316a4b56d27fd78da82bac276fdb0db028f32aac6d61ba9a428a337ab2dead9efc8d9dd42597376edd4d827db96b84f210d36ab1f32db3c98dd0372583e684
[DEBUG] 00805006
('XTS data key:', '96D27F425BFADDF59E0D9318353864C6')
('XTS tweak key:', '6A4C20B0CA0916542B246E5382F8FF0D')
I was wondering if the whole process is a bit different on a PS4 slim or with a 8-WSON chip. I tried hexediting the keyfile several times.

Since I used the script v2 I thought this wasn't necessary. But typed everything reverse. Tried typing every single line reverse. Typed backwards. Maybe I did a mistake somehow.
Code:
user {
    dev=/dev/sdh27 -- (it's the biggest partition listed with 400+GB)
    dir=/home/imager/ps4/user
    flags=user,nofsck
    fstype=ufs mountoptions=ro,noatime,noexec,ufstype=ufs2
    cipher=aes-xts-plain64
    ivoffset=111669149696 -- (of cause the offset because model 2xxx+, also tryed without offset anyway)
    keyfile=/home/eap_key.bin -- (renamed it to eap_key.bin)
    keyformat=raw
}
The whole process worked with former PS4 fat consoles without editing the key file.

Error Code with PS4 slim:
Code:
wrong fs type, bad option, bad superblock on /dev/mapper/user, missing codepage or helper program, or other error.
Is maybe the partition broken? But let me please know if I missed a step somehow.

Edit: I had problems formatting the code. Here we go again. A little bit cleaner this time.

Python Script:
Code:
C:\PS4>python hdd_scriptv2.py
[DEBUG] e5e5e501
[DEBUG] 9efc8d9dd42597376edd4d827db96b84
[DEBUG] LEN 60 | f20c55b5700c355aef583722de5a99550c6b9cb0d228f5fed3aa4d680b9604e8e2316a4b56d27fd78da82bac276fdb0db028f32aac6d61ba9a428a337ab2dead9efc8d9dd42597376edd4d827db96b84f210d36ab1f32db3c98dd0372583e684
[DEBUG] 00805006
('XTS data key:', '96D27F425BFADDF59E0D9318353864C6')
('XTS tweak key:', '6A4C20B0CA0916542B246E5382F8FF0D')
Cmtab
Code:
user { dev=/dev/sdh27 -- (it's the biggest partition listed with 400+GB)
dir=/home/imager/ps4/user
flags=user,nofsck
fstype=ufs mountoptions=ro,noatime,noexec,ufstype=ufs2
cipher=aes-xts-plain64
ivoffset=111669149696 -- (of cause the offset because model 2xxx+, also tryed without offset anyway)
keyfile=/home/eap_key.bin -- (renamed it to eap_key.bin)
keyformat=raw }
Error Code on Linux with ps4 slim HDD:
Code:
wrong fs type, bad option, bad superblock on /dev/mapper/user, missing codepage or helper program, or other error.
Edit: I decided to make 3 more NOR dumps... HxD says they are identical.

I would share the sflash0.bin if anyone wants to try another hdd_script or maybe wants to check if the NOR.bin is faulty. I cannot afford the Nor Validator program from BwE.

@me1985420 you solved a similar problem 2 years ago. I tried following your comment.
But it didn't work as well. I am going insane. :p

@babadmasa I have also a slim model, using script v2. Did you found a way in?
 
Status
Not open for further replies.
Back
Top