Previously we've seen guides on How to Obtain Your PS4 SFlash alongside some example file dumps, and today developer @zecoxao shared on Twitter SFlash0Unpack which is a script to unpack sflash0 files from PS4 flash dumps for PlayStation 4 scene devs to examine followed by a PS4 SFlash0 Tool by SocraticBliss.
In related news, pearlxcore (Twitter) also made available a PS4 Dump Extractor utilized to extract PlayStation 4 dumps including sflash0 files.
Download: sflash0unpack-master.zip / GIT / ps4_sflash0_pack_tool.py / GIT / ps4_sflash0_tool.py / ps4_sflash0_tool / PS4.Dump.Extractor.exe / PS4 Dump Extractor GIT
Below is main.c from Github followed by the makefile, with a Python version of the PS4 Sflash0 Pack Tool from @SocraticBliss (Twitter) on Github for those interested and also a extractor.pl Perl version from BwE!
Main.c:
To find out (and document) the minimum version of your PS4, download with ftp buf 0x200 payload the file /dev/sflash0s1.cryptx1 (size 512KB) at 0x10008 you can find the minver (endian swapped) next to the SMI header
ps4_sflash0_pack_tool.py
And From the ps4_sflash0_tool README.md: PS4 SFlash0 Tool
SocraticBliss (R)
ps4_sflash_tool.py: Python script for [un]packing your PS4's SFlash0
Usage
Unpacking SFlash0
Packing SFlash0
If you have any suggestions or ideas, please feel free to create pull requests!
To make the most out of this, we have to work together!
decided to release a tool to unpack the sflash0 plaintext partition and the ciphertext counterpart you obtain from PS4 called ps4norunpack. you can find it here:
small python program that calculates the entropy of the file "sflash0":
for those curious about why the revert requires the values at 0x201000 to be of a specific type. the logic behind it is that the bank type is stored there. Sony checks for a 00 or 80 in the first byte for bank 00 or 01 respectively
the reason why option 7 worked was because, when decrypting those values, the first byte happened to match 00 or 80, thus making the console believe it was on firmware 9.00 (which was located in backup bank)
fun fact, all those 8 options could have not worked. there is a 1/128 or 2/256 possibility of the person changing the values gets it right, because one byte has 256 possibilities and we're looking for one out of two
yes. you ftp from your hacked console the file sflash0s1.crypt
you can find the previous firmware *** version in COREOS
In related news, pearlxcore (Twitter) also made available a PS4 Dump Extractor utilized to extract PlayStation 4 dumps including sflash0 files.
Download: sflash0unpack-master.zip / GIT / ps4_sflash0_pack_tool.py / GIT / ps4_sflash0_tool.py / ps4_sflash0_tool / PS4.Dump.Extractor.exe / PS4 Dump Extractor GIT
Below is main.c from Github followed by the makefile, with a Python version of the PS4 Sflash0 Pack Tool from @SocraticBliss (Twitter) on Github for those interested and also a extractor.pl Perl version from BwE!
Main.c:
Code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/stat.h>
/*
0x0 <- Header (0x1000)
0x1000 <- Unk (0x1000)
0x2000 <- MBR1 (0x1000) (for sflash0s1.cryptx3b)
0x3000 <- MBR2 (0x1000) (for sflash0s1.cryptx3)
0x4000 <- sflash0s0x32b (emc_ipl) (0x60000)
0x64000 <- sflash0s0x32 (emc_ipl) (0x60000)
0xC4000 <- sflash0s0x33 (eap_kbl) (0x80000)
0x144000 <- sflash0s0x34 (wifi fw) (0x80000)
0x1C4000 <- sflash0s0x38 (nvs) (0xC000)
0x1D0000 <- sflash0s0x0 (blank1) (0x30000)
0x200000 <- Header2 (0x1000)
0x201000 <- Unk 2(0x1000)
0x202000 <- MBR3(0x1000) (for sflash0s1.cryptx2b)
0x203000 <- MBR4(0x1000) (for sflash0s1.cryptx2)
0x204000 <- sflash0s1.cryptx2b (sam_ipl/secure loader) (0x3E000)
0x242000 <- sflash0s1.cryptx2 (sam_ipl/secure loader) (0x3E000)
0x280000 <- sflash0s1.cryptx1 (idata) (0x80000)
0x300000 <- sflash0s1.cryptx39 (bd_hrl?) (0x80000)
0x380000 <- sflash0s1.cryptx6 (Virtual TRM) (0x40000)
0x3C0000 <- sflash0s1.cryptx3b (secure kernel, secure modules) (0xCC0000)
0x1080000 <- sflash0s1.cryptx3 (secure kernel, secure modules) (0xCC0000)
0x1D40000 <- sflash0s1.cryptx40 (blank2) (0x2C0000)
*/
typedef struct
{
unsigned char header[0x1000];
unsigned char unk[0x1000];
unsigned char mbr1[0x1000];
unsigned char mbr2[0x1000];
unsigned char emc_iplb[0x60000];
unsigned char emc_ipl[0x60000];
unsigned char eap_kbl[0x80000];
unsigned char wifi_fw[0x80000];
unsigned char nvs[0xC000];
unsigned char blank[0x30000];
unsigned char header2[0x1000];
unsigned char unk2[0x1000];
unsigned char mbr3[0x1000];
unsigned char mbr4[0x1000];
unsigned char sam_iplb[0x3E000];
unsigned char sam_ipl[0x3E000];
unsigned char idata[0x80000];
unsigned char bd_hrl[0x80000];
unsigned char vtrm[0x40000];
unsigned char secureb[0xCC0000];
unsigned char secure[0xCC0000];
unsigned char blank2[0x2C0000];
} SFLASH0;
int main(int argc, char **argv){
if(argc != 3){
printf ("\nusage: sflash0unpack [sflash0] [outdir] \n");
return -1;
}
unsigned char out[256];
mkdir(argv[2],0777);
FILE *fp = fopen(argv[1],"rb");
unsigned char *buf = (unsigned char*) malloc (0x2000000);
fread(buf,0x2000000,1,fp);
SFLASH0* entries = (SFLASH0*)buf;
sprintf(out,"%s/header.bin",argv[2]);
FILE *fl = fopen(out,"wb");
fwrite(entries->header,sizeof(entries->header),1,fl);
fclose(fl);
sprintf(out,"%s/emc_ipl.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->emc_ipl,sizeof(entries->emc_ipl),1,fl);
fclose(fl);
sprintf(out,"%s/emc_iplb.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->emc_iplb,sizeof(entries->emc_iplb),1,fl);
fclose(fl);
sprintf(out,"%s/eap_kbl.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->eap_kbl,sizeof(entries->eap_kbl),1,fl);
fclose(fl);
sprintf(out,"%s/wifi_fw.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->wifi_fw,sizeof(entries->wifi_fw),1,fl);
fclose(fl);
sprintf(out,"%s/sam_ipl.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->sam_ipl,sizeof(entries->sam_ipl),1,fl);
fclose(fl);
sprintf(out,"%s/sam_iplb.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->sam_iplb,sizeof(entries->sam_iplb),1,fl);
fclose(fl);
sprintf(out,"%s/idata.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->idata,sizeof(entries->idata),1,fl);
fclose(fl);
sprintf(out,"%s/bd_hrl.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->bd_hrl,sizeof(entries->bd_hrl),1,fl);
fclose(fl);
sprintf(out,"%s/vtrm.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->vtrm,sizeof(entries->vtrm),1,fl);
fclose(fl);
sprintf(out,"%s/secure.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->secure,sizeof(entries->secure),1,fl);
fclose(fl);
sprintf(out,"%s/secureb.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->secureb,sizeof(entries->secureb),1,fl);
fclose(fl);
sprintf(out,"%s/blank.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->blank,sizeof(entries->blank),1,fl);
fclose(fl);
sprintf(out,"%s/nvs.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->nvs,sizeof(entries->nvs),1,fl);
fclose(fl);
sprintf(out,"%s/unk.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->unk,sizeof(entries->unk),1,fl);
fclose(fl);
sprintf(out,"%s/mbr1.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->mbr1,sizeof(entries->mbr1),1,fl);
fclose(fl);
sprintf(out,"%s/mbr2.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->mbr2,sizeof(entries->mbr2),1,fl);
fclose(fl);
sprintf(out,"%s/unk2.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->unk2,sizeof(entries->unk2),1,fl);
fclose(fl);
sprintf(out,"%s/mbr3.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->mbr3,sizeof(entries->mbr3),1,fl);
fclose(fl);
sprintf(out,"%s/mbr4.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->mbr4,sizeof(entries->mbr4),1,fl);
fclose(fl);
sprintf(out,"%s/header2.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->header2,sizeof(entries->header2),1,fl);
fclose(fl);
sprintf(out,"%s/blank2.bin",argv[2]);
fl = fopen(out,"wb");
fwrite(entries->blank2,sizeof(entries->blank2),1,fl);
fclose(fl);
fclose(fp);
return 0;
}
ps4_sflash0_pack_tool.py
Code:
# PS4 Sflash0 Pack Tool
# SocraticBliss (R)
# Thanks to zecoxao <3
import os
import sys
'''
Offsets
0x0 <- Header (0x1000)
0x1000 <- Unk (0x1000)
0x2000 <- MBR1 (for sflash0s1.cryptx3b) (0x1000)
0x3000 <- MBR2 (for sflash0s1.cryptx3) (0x1000)
0x4000 <- sflash0s0x32b (emc_ipl) (0x60000)
0x64000 <- sflash0s0x32 (emc_ipl) (0x60000)
0xC4000 <- sflash0s0x33 (eap_kbl) (0x80000)
0x144000 <- sflash0s0x34 (wifi fw) (0x80000)
0x1C4000 <- sflash0s0x38 (nvs) (0xC000)
0x1D0000 <- sflash0s0x0 (blank) (0x30000)
0x200000 <- Header2 (0x1000)
0x201000 <- Unk2 (0x1000)
0x202000 <- MBR3 (for sflash0s1.cryptx2b) (0x1000)
0x203000 <- MBR4 (for sflash0s1.cryptx2) (0x1000)
0x204000 <- sflash0s1.cryptx2b (sam_ipl/secure loader) (0x3E000)
0x242000 <- sflash0s1.cryptx2 (sam_ipl/secure loader) (0x3E000)
0x280000 <- sflash0s1.cryptx1 (idata) (0x80000)
0x300000 <- sflash0s1.cryptx39 (bd_hrl?) (0x80000)
0x380000 <- sflash0s1.cryptx6 (Virtual TRM) (0x40000)
0x3C0000 <- sflash0s1.cryptx3b (secure kernel, secure modules) (0xCC0000)
0x1080000 <- sflash0s1.cryptx3 (secure kernel, secure modules) (0xCC0000)
0x1D40000 <- sflash0s1.cryptx40 (blank2) (0x2C0000)
'''
SFLASH0 = [
('header.bin', 0x0, 0x1000),
('unknown.bin', 0x1000, 0x1000),
('mbr1.bin', 0x2000, 0x1000),
('mbr2.bin', 0x3000, 0x1000),
('emc_iplb.bin', 0x4000, 0x60000),
('emc_ipl.bin', 0x64000, 0x60000),
('eap_kbl.bin', 0xC4000, 0x80000),
('wifi_fw.bin', 0x144000, 0x80000),
('nvs.bin', 0x1C4000, 0xC000),
('blank.bin', 0x1D0000, 0x30000),
('header2.bin', 0x200000, 0x1000),
('unknown2.bin', 0x201000, 0x1000),
('mbr3.bin', 0x202000, 0x1000),
('mbr4.bin', 0x203000, 0x1000),
('sam_iplb.bin', 0x204000, 0x3E000),
('sam_ipl.bin', 0x242000, 0x3E000),
('idata.bin', 0x280000, 0x80000),
('bd_hrl.bin', 0x300000, 0x80000),
('vtrm.bin', 0x380000, 0x40000),
('secureb.bin', 0x3C0000, 0xCC0000),
('secure.bin', 0x1080000, 0xCC0000),
('blank2.bin', 0x1D40000, 0x2C0000),
]
# Unpack entries from a Sflash0 binary...
def unpack(file, dir):
with open(file, 'rb') as input:
sflash0 = input.read()
# Validate input file...
if sflash0[:0x4] != 'SONY':
raise SystemExit('\nInvalid PS4 Sflash0 binary!')
for num, entry in enumerate(SFLASH0):
with open('%s/%s' % (dir, SFLASH0[num][0]), 'wb') as output:
begin = SFLASH0[num][1]
end = begin + SFLASH0[num][2]
output.write(sflash0[begin:end])
print('Unpacked %s' % SFLASH0[num][0])
# Pack entries into a Sflash0 binary...
def pack(dir, file):
with open(file, 'wb') as output:
try:
for num, entry in enumerate(SFLASH0):
with open('%s/%s' % (dir, SFLASH0[num][0]), 'rb') as input:
output.write(input.read())
except IOError as error:
raise SystemExit('\n%s' % error)
def main(argc, argv):
# Print Usage Statement...
if argc not in [2, 3]:
raise SystemExit('\nUsage: python %s <input> [output]' % argv[0])
# File Input -> Unpack
if os.path.isfile(argv[1]):
# Create a custom directory...
if argc == 3:
try:
os.makedirs(argv[2])
except:
pass
unpack(argv[1], argv[2] if argc == 3 else '.')
# Directory Input -> Pack
elif os.path.isdir(argv[1]):
pack(argv[1], argv[2] if argc == 3 else 'sflash0.bin')
else:
raise SystemExit('\nUsage: python %s <input> [output]' % argv[0])
print('\nDone!')
if __name__ == '__main__':
main(len(sys.argv), sys.argv)
SocraticBliss (R)
ps4_sflash_tool.py: Python script for [un]packing your PS4's SFlash0
Usage
Unpacking SFlash0
Code:
python ps4_sflash_pack_tool.py <sflash0.bin> [Optional Output Directory]
Code:
python ps4_sflash_pack_tool.py <Directory> [Optional Output SFlash0 Name]
To make the most out of this, we have to work together!
decided to release a tool to unpack the sflash0 plaintext partition and the ciphertext counterpart you obtain from PS4 called ps4norunpack. you can find it here:
- ps4norextract.7z (1.58 KB) / PS4NORUnpack GIT - PS4 NOR Unpack parses sflash0/sflash0s0 and sflash0s1.crypt/sflash0s1 (with 0x4000 bytes header from sflash0s1.crypt)
small python program that calculates the entropy of the file "sflash0":
- entropy.py (727 Bytes)
Code:
#!/usr/bin/env python3
import math
def entropy(filename):
with open(filename, "rb") as file:
counters = {byte: 0 for byte in range(2 ** 8)} # start all counters with zeros
for byte in file.read(): # read in chunks for large files
counters[byte] += 1 # increase counter for specified byte
filesize = file.tell() # we can get file size by reading current position
probabilities = [counter / filesize for counter in counters.values()] # calculate probabilities for each byte
entropy = -sum(probability * math.log2(probability) for probability in probabilities if probability > 0) # final sum
print(entropy)
entropy("sflash0")
the reason why option 7 worked was because, when decrypting those values, the first byte happened to match 00 or 80, thus making the console believe it was on firmware 9.00 (which was located in backup bank)
fun fact, all those 8 options could have not worked. there is a 1/128 or 2/256 possibility of the person changing the values gets it right, because one byte has 256 possibilities and we're looking for one out of two
yes. you ftp from your hacked console the file sflash0s1.crypt
you can find the previous firmware *** version in COREOS