Created
March 19, 2026 11:54
-
-
Save rmuxnet/fff41726596d7da0ba7196e099a8e8de to your computer and use it in GitHub Desktop.
ps4 icc nvs
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
| """ | |
| ps4_icc_nvs.py | |
| -------------- | |
| NVS directory sweep via ICC major 0x01, minor 0x00. | |
| query_nvs_entry(fd, idx) query a single NVS index (0x00-0xFF) | |
| sweep_nvs() sweep all 256 entries, return populated list | |
| print_nvs_directory() formatted table with recovery path notes | |
| Hardware: PS4 Fat (Aeolia CXD90025G) | |
| Kernel: Linux 6.18.18-Strawberry-ThinLTO-LTS+ (CachyOS x86_64) | |
| DEVICE PERMISSIONS | |
| ------------------ | |
| If /dev/icc does not exist or you get PermissionError: | |
| cat /proc/devices | grep icc | |
| sudo mknod /dev/icc c <major> 0 | |
| sudo chmod 666 /dev/icc | |
| Permanent udev rule (survives reboots): | |
| echo 'KERNEL=="icc", MODE="0666"' | sudo tee /etc/udev/rules.d/99-ps4-icc.rules | |
| sudo udevadm control --reload-rules | |
| sudo udevadm trigger | |
| ARCHITECTURE NOTE | |
| ----------------- | |
| The NVS accessed here is syscon's internal flash storage -- distinct from the | |
| EMC-managed sflash NVS areas (csarea, dsarea, osarea, pdarea). Syscon runs ICC | |
| over SPI (SPI + interrupt line) and exposes its NVS index through major 0x01. | |
| WHAT CAN BE DETERMINED (no auth) | |
| ---------------------------------- | |
| - Which indices 0x00-0xFF are populated | |
| - Type code and inferred size of each entry | |
| - That specific entries exist (e.g. Wi-Fi MAC at 0x02, serial at 0x04) | |
| WHAT CANNOT BE DETERMINED (RSA-gated) | |
| --------------------------------------- | |
| Values are gated behind RSA authentication (uareq1 / uareq2 on EMC UART). | |
| Exhaustive ICC probing confirmed no bypass: | |
| 256 x 256 payload sweep, all minors, 7 format strategies, | |
| pre-auth sequence attempts -- zero value bytes returned. | |
| ALTERNATIVE VALUE RECOVERY (no auth required) | |
| ---------------------------------------------- | |
| Wi-Fi MAC /sys/class/net/mlan0/address | |
| Ethernet MAC /sys/class/net/enp0s20f1/address | |
| BD address stale buffer from 0x02/0x01 (see ps4_icc_sysinfo.py) | |
| Serial EMC UART 'dsarea' after RSA auth (uareq1/uareq2) | |
| NVS TYPE CODES | |
| -------------- | |
| 0x00 u8 scalar (1 byte) | |
| 0x01 boolean flag (1 byte) | |
| 0x02 u16 little-endian (2 bytes) | |
| 0x06 6-byte blob (MAC / BD address format) | |
| 0x21 33-byte string (32 ASCII chars + null terminator) | |
| KNOWN INDEX MAP (full 0x00-0xFF sweep, Aeolia Fat unit) | |
| --------------------------------------------------------- | |
| 0x01 0x00 1B unknown scalar | |
| 0x02 0x06 6B Wi-Fi MAC address | |
| 0x03 0x00 1B unknown scalar | |
| 0x04 0x21 33B serial number (32 ASCII + null) | |
| 0x05 0x01 1B boolean flag | |
| 0x06 0x00 1B unknown scalar | |
| 0x07 0x00 1B unknown scalar | |
| 0x08 0x00 1B unknown scalar | |
| 0x09 0x01 1B boolean flag | |
| 0x0A 0x02 2B unknown u16 | |
| 0x0B 0x01 1B boolean flag | |
| 0x10 0x01 1B boolean flag | |
| 0x11 0x00 1B unknown scalar | |
| 0x8C 0x00 1B factory calibration | |
| 0x8D 0x00 1B factory calibration | |
| (all other indices return empty / status 0x02) | |
| """ | |
| from ps4_icc_core import call_icc, ICCError | |
| NVS_TYPES = { | |
| 0x00: ('u8 scalar', 1), | |
| 0x01: ('boolean flag', 1), | |
| 0x02: ('u16 little-endian', 2), | |
| 0x06: ('6-byte blob (MAC/BD)', 6), | |
| 0x21: ('33-byte string (serial)', 33), | |
| } | |
| KNOWN_NVS_ENTRIES = { | |
| 0x01: 'unknown scalar', | |
| 0x02: 'Wi-Fi MAC address', | |
| 0x03: 'unknown scalar', | |
| 0x04: 'serial number (32 ASCII + null)', | |
| 0x05: 'boolean flag', | |
| 0x06: 'unknown scalar', | |
| 0x07: 'unknown scalar', | |
| 0x08: 'unknown scalar', | |
| 0x09: 'boolean flag', | |
| 0x0A: 'unknown u16', | |
| 0x0B: 'boolean flag', | |
| 0x10: 'boolean flag', | |
| 0x11: 'unknown scalar', | |
| 0x8C: 'factory calibration', | |
| 0x8D: 'factory calibration', | |
| } | |
| def query_nvs_entry(fd, idx): | |
| reply = call_icc(fd, 0x01, 0x00, payload=bytes([idx & 0xFF])) | |
| status = reply[0] | |
| exists = (reply[2] == 0x01) if len(reply) > 2 else False | |
| if status == 0x02 or not exists or status not in (0x00, 0x03): | |
| return None | |
| type_code = reply[3] if len(reply) > 3 else 0xFF | |
| type_name, size = NVS_TYPES.get(type_code, (f'unknown(0x{type_code:02X})', '?')) | |
| return { | |
| 'idx': idx, | |
| 'type_code': type_code, | |
| 'type_name': type_name, | |
| 'size': size, | |
| 'known_as': KNOWN_NVS_ENTRIES.get(idx, ''), | |
| } | |
| def sweep_nvs(): | |
| entries = [] | |
| try: | |
| with open('/dev/icc', 'rb') as fd: | |
| for idx in range(256): | |
| try: | |
| entry = query_nvs_entry(fd, idx) | |
| if entry is not None: | |
| entries.append(entry) | |
| except ICCError: | |
| pass | |
| except FileNotFoundError: | |
| raise ICCError("/dev/icc not found. Run: cat /proc/devices | grep icc -> sudo mknod /dev/icc c <major> 0") | |
| except PermissionError: | |
| raise ICCError("/dev/icc permission denied. Fix: sudo chmod 666 /dev/icc") | |
| return entries | |
| def print_nvs_directory(): | |
| print("NVS Directory (major 0x01 / minor 0x00)") | |
| print("Values are RSA-gated -- only metadata returned without auth.") | |
| print() | |
| print(f" {'IDX':>5} {'TYPE':>6} {'SIZE':>5} {'TYPE NAME':<28} KNOWN AS") | |
| print(f" {'-----':>5} {'------':>6} {'-----':>5} {'-'*28} {'-'*28}") | |
| for e in sweep_nvs(): | |
| size_str = str(e['size']) if isinstance(e['size'], int) else e['size'] | |
| print(f" 0x{e['idx']:02X} 0x{e['type_code']:02X} {size_str:>5}B {e['type_name']:<28} {e['known_as']}") | |
| print() | |
| print("Alternative value recovery (no auth):") | |
| print(" Wi-Fi MAC: cat /sys/class/net/mlan0/address") | |
| print(" Ethernet MAC: cat /sys/class/net/enp0s20f1/address") | |
| print(" BD address: stale buffer from 0x02/0x01 (see ps4_icc_sysinfo.py)") | |
| print(" Serial: EMC UART 'dsarea' after uareq1/uareq2 auth") | |
| if __name__ == '__main__': | |
| print_nvs_directory() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment