Last active
March 2, 2025 06:21
-
-
Save Al-Azif/71c4bed7c64ae225755ded2c5de6671e to your computer and use it in GitHub Desktop.
Info dumer payload for PS4/PS5. See first comment for more info: https://gist.github.com/Al-Azif/71c4bed7c64ae225755ded2c5de6671e?permalink_comment_id=5462158#gistcomment-5462158
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
// Author: Al Azif | |
// Version: 1.0.0 | |
// Published: 2025-03-01 | |
// GitHub: https://github.com/Al-Azif/ | |
// Contact: https://x.com/_alazif | |
// Changelog: | |
// 0.9.0 (2025-02-28) | |
// - Initial Release | |
// 1.0.0b (2025-03-01) | |
// - Cleaned up code | |
// - Added comments | |
// - Published source | |
// - Added PS5 support | |
// 1.0.0 (2025-03-01) | |
// - Add Makefile for both PS4 and PS5 | |
// - Get Model for PS5 without sflash0 access | |
// Compiling for PS4 or PS5? | |
#if !defined(COMPILE_FOR_PS4) && !defined(COMPILE_FOR_PS5) | |
#error "Define either `COMPILE_FOR_PS4` or `COMPILE_FOR_PS5`" | |
#endif | |
#if defined(COMPILE_FOR_PS4) && defined(COMPILE_FOR_PS5) | |
#error "Define either `COMPILE_FOR_PS4` or `COMPILE_FOR_PS5` not both" | |
#endif | |
// Set headers and constants based on which console we're building for | |
#if defined(COMPILE_FOR_PS4) | |
// https://github.com/Scene-Collective/ps4-payload-sdk | |
#include "ps4.h" | |
#define SFLASH0_SIZE 0x2000000 | |
#define BLOCK_SIZE 0x200 | |
#define SERIAL_OFFSET 0x1C80C0 | |
#define MODEL_OFFSET 0x1C8041 | |
#define NOTIFICATION(...) printf_notification(__VA_ARGS__) | |
// Included in the PS4 Payload SDK: | |
// - printf_notification() | |
// - jailbreak() | |
#elif defined(COMPILE_FOR_PS5) | |
// https://github.com/ps5-payload-dev/sdk | |
#include <fcntl.h> // open | |
#include <ps5/kernel.h> // kernel_get_root_vnode, kernel_set_proc_jaildir, | |
// kernel_set_proc_rootdir, kernel_set_ucred_caps, | |
// kernel_set_ucred_uid | |
#include <stddef.h> // size_t | |
#include <stdint.h> // intptr_t, uint8_t | |
#include <stdio.h> // snprintf, sprintf | |
#include <stdlib.h> // free, malloc | |
#include <string.h> // memcpy, memset, strcpy, strlen | |
#include <sys/sysctl.h> // sysctlbyname | |
#include <sys/types.h> // off_t | |
#include <unistd.h> // close, getpid, lseek, read, usleep, write | |
#define SFLASH0_SIZE 0x200000 | |
#define BLOCK_SIZE 0x200 | |
#define SERIAL_OFFSET 0x1C7250 | |
#define MODEL_OFFSET 0x1C7230 | |
// Notification setup taken from PS4 Payload SDK, same as printf_notification | |
typedef struct { | |
int type; //0x00 | |
int req_id; //0x04 | |
int priority; //0x08 | |
int msg_id; //0x0C | |
int target_id; //0x10 | |
int user_id; //0x14 | |
int unk1; //0x18 | |
int unk2; //0x1C | |
int app_id; //0x20 | |
int error_num; //0x24 | |
int unk3; //0x28 | |
char use_icon_image_uri; //0x2C | |
char message[1024]; //0x2D | |
char uri[1024]; //0x42D | |
char unkstr[1024]; //0x82D | |
} SceNotificationRequest; //Size = 0xC30 | |
int sceKernelSendNotificationRequest(int device, SceNotificationRequest *req, size_t size, int blocking); | |
#define NOTIFICATION(...) \ | |
do { \ | |
SceNotificationRequest noti_buffer; \ | |
char icon_uri[38] = "cxml://psnotification/tex_icon_system"; \ | |
noti_buffer.type = 0; \ | |
noti_buffer.unk3 = 0; \ | |
noti_buffer.use_icon_image_uri = 1; \ | |
noti_buffer.target_id = -1; \ | |
snprintf(noti_buffer.uri, sizeof(noti_buffer.uri), icon_uri); \ | |
snprintf(noti_buffer.message, sizeof(noti_buffer.message), ##__VA_ARGS__); \ | |
sceKernelSendNotificationRequest(0, (SceNotificationRequest *)¬i_buffer, sizeof(noti_buffer), 0); \ | |
} while (0) | |
int jailbreak() { | |
pid_t pid = getpid(); | |
static const uint8_t caps[16] = { | |
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | |
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF | |
}; | |
intptr_t vnode; | |
if (!(vnode = kernel_get_root_vnode())) { | |
return -1; | |
} | |
if (kernel_set_proc_rootdir(pid, vnode)) { | |
return -1; | |
} | |
if (kernel_set_proc_jaildir(pid, 0)) { | |
return -1; | |
} | |
if (kernel_set_ucred_uid(pid, 0)) { | |
return -1; | |
} | |
if (kernel_set_ucred_caps(pid, caps)) { | |
return -1; | |
} | |
return 0; | |
} | |
#endif | |
inline off_t max(off_t a, off_t b) { | |
return a > b ? a : b; | |
} | |
inline off_t align_up(off_t x, off_t align) { | |
off_t y = max(x, align); | |
return (y + (align - 1)) & ~(align - 1); | |
} | |
inline off_t align_down(off_t x, off_t align) { | |
off_t y = max(x, align); | |
return y & ~(align - 1); | |
} | |
// Finds the first space char ('\x20') in a string and changes it, as well as | |
// any following chars, to a null terminator | |
void fix_space_termination(char *output, size_t length) { | |
int found = 0; | |
for (size_t i = 0; i < length; i++) { | |
if (found) { | |
output[i] = '\0'; | |
continue; | |
} | |
if (output[i] == '\x20') { | |
output[i] = '\0'; | |
found = 1; | |
} | |
} | |
} | |
// Checks if `/dev/sflash0` can be accessed | |
int sflash0_accessible() { | |
int fd = open("/dev/sflash0", O_RDONLY | O_NONBLOCK, 0); | |
if (fd < 0) { | |
return 0; | |
} | |
close(fd); | |
return 1; | |
} | |
// Read values from sflash0... it's not as straightforward as you may think | |
void read_sflash0(char *output, off_t offset, size_t length) { | |
// Open `/dev/sflash0` as read only, non-blocking | |
int fd = open("/dev/sflash0", O_RDONLY | O_NONBLOCK, 0); | |
if (fd < 0) { | |
NOTIFICATION("Error opening `/dev/sflash0`"); | |
return; | |
} | |
// This should never trigger, if it does something is seriously wrong | |
if (lseek(fd, 0, SEEK_END) != SFLASH0_SIZE) { | |
NOTIFICATION("Error, incorrect sflash0 size"); | |
close(fd); | |
return; | |
} | |
// Check the size of the input offset and length vs the size of sflash0 to | |
// prevent reading out of bounds | |
if (offset + length > SFLASH0_SIZE) { | |
NOTIFICATION("Error, set offset and length will go read of bounds"); | |
close(fd); | |
return; | |
} | |
///////////////////////////////////////////////////////////////////////////// | |
// The following is done in a weird way, for a reason | |
///////////////////////////////////////////////////////////////////////////// | |
// Seek to our offset within sflash0, aligned down to the nearest block size | |
// We know this will succeed because of the previous checks | |
lseek(fd, align_down(offset, BLOCK_SIZE), SEEK_SET); | |
// Create a buffer the size of our length, aligned up to the nearest block | |
// size | |
char *buffer = malloc(align_up(length, BLOCK_SIZE)); | |
if (!buffer) { | |
NOTIFICATION("Error allocating read buffer"); | |
close(fd); | |
return; | |
} | |
// Read sflash0, from the current location, into the created buffer, read | |
// length aligned up to the nearest block size | |
if (read(fd, buffer, align_up(length, BLOCK_SIZE)) != align_up(length, BLOCK_SIZE)) { | |
NOTIFICATION("Error reading `/dev/sflash0`"); | |
memset(output, '\0', length); | |
free(buffer); | |
close(fd); | |
return; | |
} | |
close(fd); | |
// Copy the buffer from the actual offset we input, minus the aligned offset, | |
// that we actually read from. | |
// Ex. | |
// We wanted 0x208, the block size is 0x200, we read from 0x200 as it is | |
// 0x208 aligned down, 0x200 became 0x0 for the buffer. Meaning we want | |
// to copy length starting from 0x8 | |
memcpy(output, &buffer[offset - align_down(offset, BLOCK_SIZE)], length); | |
free(buffer); | |
} | |
// String returned is 27 chars in length, including the null terminator | |
void get_idps(char *output) { | |
unsigned char idps[16] = {0}; | |
// The two lines below are `sceKernelGetIdPs(idps);` | |
size_t idps_len = sizeof(idps); | |
sysctlbyname("machdep.idps", (char *)idps, &idps_len, 0, 0); | |
for (int i = 0; i < 9; i++) { | |
sprintf(&output[i * 3], "%02X ", idps[i]); | |
} | |
output[26] = '\0'; // Replacing the space at the end of the string | |
} | |
// String returned is 33 chars in length (max), including the null terminator | |
void get_model(char *output) { | |
char model[33] = {0}; | |
size_t model_len = sizeof(model) - 1; | |
sysctlbyname("machdep.icc.hw_model", (char *)model, &model_len, 0, 0); | |
fix_space_termination(model, sizeof(model)); | |
memcpy(output, model, model_len); | |
} | |
// A loop to look for USB drives, if one is found it returns it's name and path | |
// This is the same as the `wait_for_usb` function in the PS4 Payload SDK | |
int await_usb(char *usb_name, char *usb_path) { | |
int row = 0; | |
char probefile[19] = {0}; | |
int fd = -1; | |
while (fd == -1) { | |
usleep(100 * 1000); | |
if (row >= 80) { // 10 attempts at each USB #, reaching 8 resets to 0 | |
row = 0; | |
} else { | |
row += 1; | |
} | |
snprintf(probefile, sizeof(probefile), "/mnt/usb%i/.probe", row / 10); | |
fd = open(probefile, O_WRONLY | O_CREAT | O_TRUNC, 0777); | |
} | |
close(fd); | |
unlink(probefile); | |
sprintf(usb_name, "USB%i", row / 10); | |
sprintf(usb_path, "/mnt/usb%i", row / 10); | |
return 1; | |
} | |
// Write output to path, formatting the data for the wiki before doing so | |
// https://www.psdevwiki.com/ps4/Serial_Database | |
void write_output(char *path, char *serial, char *idps, char *model) { | |
// Open file creating if it doesn't exist and overwriting if it does | |
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0777); | |
if (fd < 0) { | |
NOTIFICATION("Unabled to create data file"); | |
return; | |
} | |
// serial(13) + idps(26) + model(32) + format(13) + null-terminator(1) = 85 | |
char output[85] = {0}; | |
sprintf(output, "| %s || %s || %s ||", serial, idps, model); | |
if (write(fd, output, strlen(output)) < 0) { | |
NOTIFICATION("Error writing data file"); | |
close(fd); | |
return; | |
} | |
close(fd); | |
NOTIFICATION("Data file saved successfully"); | |
} | |
#if defined(COMPILE_FOR_PS4) | |
int _main(struct thread *td) { | |
UNUSED(td); | |
// Initialize Kernel and Libc functions | |
initKernel(); | |
initLibc(); | |
#elif defined(COMPILE_FOR_PS5) | |
int main() { | |
#endif | |
// Need access to `/dev/sflash0` and USB devices | |
jailbreak(); | |
// Partial IDPS | |
char idps[27] = {0}; | |
get_idps(idps); | |
if (strlen(idps) == 0) { | |
strcpy(idps, "ERROR"); | |
} | |
if (sflash0_accessible()) { | |
// Partial Serial | |
char serial[14] = {0}; | |
read_sflash0(serial, SERIAL_OFFSET, sizeof(serial) - 1); | |
if (strlen(serial) == 0) { | |
strcpy(serial, "ERROR"); | |
} | |
// Model | |
char model[33] = {0}; | |
read_sflash0(model, MODEL_OFFSET, sizeof(model) - 1); | |
// The model string is terminated by a space... let's fix that | |
fix_space_termination(model, sizeof(model)); | |
if (strlen(model) == 0) { | |
strcpy(model, "ERROR"); | |
} | |
NOTIFICATION("Partial Serial:\n%s", serial); | |
NOTIFICATION("Partial IDPS:\n%s", idps); | |
NOTIFICATION("Model Number:\n%s", model); | |
// We can save the data anywhere by just calling the `write_output` function | |
// with a custom path rather than looking for a USB device. However, we are | |
// going to look for a USB device this time. | |
// Save the output to a USB drive, if it exists | |
char usb_name[5] = {0}; | |
char usb_path[10] = {0}; | |
if (await_usb(usb_name, usb_path)) { | |
NOTIFICATION("USB device detected\n\nSaving data to %s", usb_name); | |
char output_path[19] = {0}; | |
sprintf(output_path, "%s/data.txt", usb_path); | |
write_output(output_path, serial, idps, model); | |
} | |
} else { | |
NOTIFICATION("Partial IDPS:\n%s", idps); | |
#if defined(COMPILE_FOR_PS5) | |
char model[33] = {0}; | |
get_model(model); | |
NOTIFICATION("Model Number:\n%s", model); | |
#endif | |
} | |
return 0; | |
} |
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
LIBPS4 := $(PS4SDK)/libPS4 | |
CC := gcc | |
OBJCOPY := objcopy | |
ODIR := build | |
SDIR := source | |
IDIRS := -I$(LIBPS4)/include -Iinclude | |
LDIRS := -L$(LIBPS4) | |
MAPFILE := $(shell basename "$(CURDIR)")-ps4.map | |
CFLAGS := $(IDIRS) -Os -std=c11 -ffunction-sections -fdata-sections -fno-builtin -nostartfiles -nostdlib -Wall -Wextra -Werror -masm=intel -march=btver2 -mtune=btver2 -m64 -mabi=sysv -mcmodel=small -fpie -fPIC -DCOMPILE_FOR_PS4 | |
LFLAGS := $(LDIRS) -Xlinker -T $(LIBPS4)/linker.x -Xlinker -Map="$(MAPFILE)" -Wl,--build-id=none -Wl,--gc-sections | |
CFILES := $(wildcard $(SDIR)/*.c) | |
SFILES := $(wildcard $(SDIR)/*.s) | |
OBJS := $(patsubst $(SDIR)/%.c, $(ODIR)/%.o, $(CFILES)) $(patsubst $(SDIR)/%.s, $(ODIR)/%.o, $(SFILES)) | |
LIBS := -lPS4 | |
TARGET = $(shell basename "$(CURDIR)")-ps4.bin | |
$(TARGET): $(ODIR) $(OBJS) | |
$(CC) $(LIBPS4)/crt0.s $(ODIR)/*.o -o temp.t $(CFLAGS) $(LFLAGS) $(LIBS) | |
$(OBJCOPY) -O binary temp.t "$(TARGET)" | |
rm -f temp.t | |
$(ODIR)/%.o: $(SDIR)/%.c | |
$(CC) -c -o $@ $< $(CFLAGS) | |
$(ODIR)/%.o: $(SDIR)/%.s | |
$(CC) -c -o $@ $< $(CFLAGS) | |
$(ODIR): | |
@mkdir $@ | |
.PHONY: clean | |
clean: | |
rm -rf "$(TARGET)" "$(MAPFILE)" $(ODIR) |
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
include $(PS5_PAYLOAD_SDK)/toolchain/prospero.mk | |
ELF := $(shell basename "$(CURDIR)")-ps5.elf | |
CFLAGS := -Os -std=c11 -Wall -Wextra -Wno-format-security -Werror -fpie -fPIC -DCOMPILE_FOR_PS5 | |
LIBS := -lkernel_sys | |
all: $(ELF) | |
$(ELF): source/main.c | |
$(CC) $(CFLAGS) $(LIBS) -o $@ $^ | |
clean: | |
rm -f $(ELF) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
About
Shows Partial Serial , Partial IDPS, and Model as a notification, it will also save the data to a USB device, if one is available, at
/data.txt
. Thedata.txt
file is preformatted for pasting here: PS4 and PS5Function Table
/dev/sflash0
)/dev/sflash0
)Building
PS4
Uses the PS4 Payload SDK
make -f Makefile.ps4
PS5
Uses the PS5 Payload SDK
make -f Makefile.ps5
Notes
00 00 00 01 02 [82] 00 XX XX
then the exploit you are using is spoofing your consoles target ID to be that of a testkit, 82 and lower is developer hardware, if you have retail hardware you should not be getting developer target IDs.