Enable plaintext PPR interception through firmware 13.60

This commit is contained in:
Roman Tarasov authored and EchoStretch committed 2026-09-19 16:45:29 -06:00
1 parent 47e6c23311
commit a7ca1c3ad7
9 files changed
+114 -62

No files matched your search

+9 -9
View File
@@ -76,18 +76,18 @@ DEF(cr0_write_ret, -0x581453)
DEF(store_rax_rdi, -0x1EE5B2)
// PPR/fPKG offsets
DEF(ppr_pfs_get_xts_index, -0x0)
DEF(ppr_pfs_get_cmac_index, -0x0)
DEF(ppr_pfs_get_xts_return, -0x0)
DEF(ppr_pfs_get_cmac_return, -0x0)
DEF(ppr_pfs_cleanup_keys, -0x0)
DEF(ppr_pfs_clear_key_missing, -0x0)
DEF(sceSblServiceMailbox_lr_verifyImage, -0x0)
DEF(ppr_pfs_verify_image_no_key_success, -0x0)
DEF(ppr_pfs_get_xts_index, -0x127090)
DEF(ppr_pfs_get_cmac_index, -0x126f30)
DEF(ppr_pfs_get_xts_return, -0x93a5f7)
DEF(ppr_pfs_get_cmac_return, -0x93a5ac)
DEF(ppr_pfs_cleanup_keys, -0x945590)
DEF(ppr_pfs_clear_key_missing, -0xa279ff)
DEF(sceSblServiceMailbox_lr_verifyImage, -0xa28e9d)
DEF(ppr_pfs_verify_image_no_key_success, -0xa28b42)
// non data-relative offsets
DEF(p_sysent, 0xA08)
#include "offset_list.txt"
END_FW()
#endif
#endif
+9 -9
View File
@@ -76,18 +76,18 @@ DEF(cr0_write_ret, -0x581143)
DEF(store_rax_rdi, -0x1EE302)
// PPR/fPKG offsets
DEF(ppr_pfs_get_xts_index, -0x0)
DEF(ppr_pfs_get_cmac_index, -0x0)
DEF(ppr_pfs_get_xts_return, -0x0)
DEF(ppr_pfs_get_cmac_return, -0x0)
DEF(ppr_pfs_cleanup_keys, -0x0)
DEF(ppr_pfs_clear_key_missing, -0x0)
DEF(sceSblServiceMailbox_lr_verifyImage, -0x0)
DEF(ppr_pfs_verify_image_no_key_success, -0x0)
DEF(ppr_pfs_get_xts_index, -0x126e90)
DEF(ppr_pfs_get_cmac_index, -0x126d30)
DEF(ppr_pfs_get_xts_return, -0x93a5f7)
DEF(ppr_pfs_get_cmac_return, -0x93a5ac)
DEF(ppr_pfs_cleanup_keys, -0x945590)
DEF(ppr_pfs_clear_key_missing, -0xa279ff)
DEF(sceSblServiceMailbox_lr_verifyImage, -0xa28e9d)
DEF(ppr_pfs_verify_image_no_key_success, -0xa28b42)
// non data-relative offsets
DEF(p_sysent, 0xA08)
#include "offset_list.txt"
END_FW()
#endif
#endif
+9 -9
View File
@@ -76,18 +76,18 @@ DEF(cr0_write_ret, -0x580E33)
DEF(store_rax_rdi, -0x1EDFF2)
// PPR/fPKG offsets
DEF(ppr_pfs_get_xts_index, -0x0)
DEF(ppr_pfs_get_cmac_index, -0x0)
DEF(ppr_pfs_get_xts_return, -0x0)
DEF(ppr_pfs_get_cmac_return, -0x0)
DEF(ppr_pfs_cleanup_keys, -0x0)
DEF(ppr_pfs_clear_key_missing, -0x0)
DEF(sceSblServiceMailbox_lr_verifyImage, -0x0)
DEF(ppr_pfs_verify_image_no_key_success, -0x0)
DEF(ppr_pfs_get_xts_index, -0x126b80)
DEF(ppr_pfs_get_cmac_index, -0x126a20)
DEF(ppr_pfs_get_xts_return, -0x93a597)
DEF(ppr_pfs_get_cmac_return, -0x93a54c)
DEF(ppr_pfs_cleanup_keys, -0x945530)
DEF(ppr_pfs_clear_key_missing, -0xa2799f)
DEF(sceSblServiceMailbox_lr_verifyImage, -0xa28e3d)
DEF(ppr_pfs_verify_image_no_key_success, -0xa28ae2)
// non data-relative offsets
DEF(p_sysent, 0xA08)
#include "offset_list.txt"
END_FW()
#endif
#endif
+9 -9
View File
@@ -76,18 +76,18 @@ DEF(cr0_write_ret, -0x580E33)
DEF(store_rax_rdi, -0x1EDFF2)
// PPR/fPKG offsets
DEF(ppr_pfs_get_xts_index, -0x0)
DEF(ppr_pfs_get_cmac_index, -0x0)
DEF(ppr_pfs_get_xts_return, -0x0)
DEF(ppr_pfs_get_cmac_return, -0x0)
DEF(ppr_pfs_cleanup_keys, -0x0)
DEF(ppr_pfs_clear_key_missing, -0x0)
DEF(sceSblServiceMailbox_lr_verifyImage, -0x0)
DEF(ppr_pfs_verify_image_no_key_success, -0x0)
DEF(ppr_pfs_get_xts_index, -0x126b80)
DEF(ppr_pfs_get_cmac_index, -0x126a20)
DEF(ppr_pfs_get_xts_return, -0x93a597)
DEF(ppr_pfs_get_cmac_return, -0x93a54c)
DEF(ppr_pfs_cleanup_keys, -0x945530)
DEF(ppr_pfs_clear_key_missing, -0xa2799f)
DEF(sceSblServiceMailbox_lr_verifyImage, -0xa28e3d)
DEF(ppr_pfs_verify_image_no_key_success, -0xa28ae2)
// non data-relative offsets
DEF(p_sysent, 0xA08)
#include "offset_list.txt"
END_FW()
#endif
#endif
+8 -8
View File
@@ -76,14 +76,14 @@ DEF(cr0_write_ret, -0x58E983)
DEF(store_rax_rdi, -0x1F44A2)
// PPR/fPKG offsets
DEF(ppr_pfs_get_xts_index, -0x0)
DEF(ppr_pfs_get_cmac_index, -0x0)
DEF(ppr_pfs_get_xts_return, -0x0)
DEF(ppr_pfs_get_cmac_return, -0x0)
DEF(ppr_pfs_cleanup_keys, -0x0)
DEF(ppr_pfs_clear_key_missing, -0x0)
DEF(sceSblServiceMailbox_lr_verifyImage, -0x0)
DEF(ppr_pfs_verify_image_no_key_success, -0x0)
DEF(ppr_pfs_get_xts_index, -0x12c6e0)
DEF(ppr_pfs_get_cmac_index, -0x12c580)
DEF(ppr_pfs_get_xts_return, -0x949bf7)
DEF(ppr_pfs_get_cmac_return, -0x949bac)
DEF(ppr_pfs_cleanup_keys, -0x954b90)
DEF(ppr_pfs_clear_key_missing, -0xa3795f)
DEF(sceSblServiceMailbox_lr_verifyImage, -0xa38dfd)
DEF(ppr_pfs_verify_image_no_key_success, -0xa38aa2)
// non data-relative offsets
DEF(p_sysent, 0xA08)
+8 -10
View File
@@ -951,23 +951,21 @@ static int install_shellcore_ppr_hook(
size_t game_umount_entry = 0;
size_t ppr_umount_entry = 0;
int game_only = (r0gdb_get_fw_version() >> 16) > 0x1140;
size_t api_count = game_only ? 2 : 4;
const size_t api_count = sizeof(api_got) / sizeof(api_got[0]);
if(!blob_size || blob_size > sizeof(prepared_blob))
return shellcore_ppr_fail("fpkg scope: invalid wrapper blob size");
uint32_t required_mask = SHELLCORE_IMPORT_BIT(SHELLCORE_IMPORT_MOUNT_GAME)
| SHELLCORE_IMPORT_BIT(SHELLCORE_IMPORT_UMOUNT_GAME);
if(!game_only)
required_mask |= SHELLCORE_IMPORT_BIT(SHELLCORE_IMPORT_CLOSE)
| SHELLCORE_IMPORT_BIT(SHELLCORE_IMPORT_OPEN)
| SHELLCORE_IMPORT_BIT(SHELLCORE_IMPORT_PREAD)
| SHELLCORE_IMPORT_BIT(SHELLCORE_IMPORT_MOUNT_PPR)
| SHELLCORE_IMPORT_BIT(SHELLCORE_IMPORT_UMOUNT_PPR);
| SHELLCORE_IMPORT_BIT(SHELLCORE_IMPORT_UMOUNT_GAME)
| SHELLCORE_IMPORT_BIT(SHELLCORE_IMPORT_CLOSE)
| SHELLCORE_IMPORT_BIT(SHELLCORE_IMPORT_OPEN)
| SHELLCORE_IMPORT_BIT(SHELLCORE_IMPORT_PREAD)
| SHELLCORE_IMPORT_BIT(SHELLCORE_IMPORT_MOUNT_PPR)
| SHELLCORE_IMPORT_BIT(SHELLCORE_IMPORT_UMOUNT_PPR);
if(kstuff_shellcore_imports(pid, shellcore_base, required_mask,
import_got))
return shellcore_ppr_fail("fpkg scope: ShellCore imports unavailable");
for(size_t i = 0; i < (game_only ? 0 : 3); i++)
for(size_t i = 0; i < 3; i++)
{
uint64_t lazy_plt;
if(read_shellcore_import(pid, shellcore_base, text_end, import_got[i],
+28 -6
View File
@@ -66,6 +66,7 @@ enum ppr_verify_success_abi
PPR_VERIFY_SUCCESS_R13_STACK_158,
PPR_VERIFY_SUCCESS_R13_STACK_150,
PPR_VERIFY_SUCCESS_PACKED_STACK_158,
PPR_VERIFY_SUCCESS_PACKED_STACK_150,
};
struct ppr_profile
@@ -151,6 +152,16 @@ static const struct ppr_profile* get_ppr_profile(void)
R13, R14, R15, RBX, 0x0a,
PPR_VERIFY_SUCCESS_PACKED_STACK_158,
};
static const struct ppr_profile fw12 = {
0x119, 0x149,
R13, R14, R15, RBX, 0x0a,
PPR_VERIFY_SUCCESS_PACKED_STACK_150,
};
static const struct ppr_profile fw13 = {
0x12b, 0x15b,
R13, R14, R15, RBX, 0x0a,
PPR_VERIFY_SUCCESS_PACKED_STACK_150,
};
switch(FWVER)
{
@@ -178,9 +189,14 @@ static const struct ppr_profile* get_ppr_profile(void)
return &fw9;
case 0x1000: case 0x1001: case 0x1020: case 0x1040: case 0x1060:
return &fw10;
case 0x1100: case 0x1120: case 0x1140:
case 0x1100: case 0x1120: case 0x1140: case 0x1160:
return &fw11;
/* 11.60+ offsets are data-only until runtime interception is validated. */
case 0x1200: case 0x1202: case 0x1220: case 0x1240:
case 0x1260: case 0x1270:
return &fw12;
case 0x1300: case 0x1320: case 0x1340: case 0x1342:
case 0x1360:
return &fw13;
default:
return NULL;
}
@@ -1636,11 +1652,15 @@ int try_handle_fpkg_mailbox(uint64_t* regs, uint64_t lr)
== PPR_VERIFY_SUCCESS_R13_STACK_150)
success_stack_offset = -0x150;
else if(ppr_abi->verify_success_abi
== PPR_VERIFY_SUCCESS_PACKED_STACK_158)
== PPR_VERIFY_SUCCESS_PACKED_STACK_158
|| ppr_abi->verify_success_abi
== PPR_VERIFY_SUCCESS_PACKED_STACK_150)
{
success_stack_offset = -0x158;
success_stack_offset = ppr_abi->verify_success_abi
== PPR_VERIFY_SUCCESS_PACKED_STACK_158
? -0x158 : -0x150;
/*
* The 10.x/11.x no-key continuation loads this qword and
* The 10.x-13.x no-key continuation loads this qword and
* publishes its upper dword as ekey (XTS) and its lower
* dword as skey (CMAC). Keep that order distinct from the
* in-memory key-index pair used later by ppfs cleanup.
@@ -1676,7 +1696,9 @@ int try_handle_fpkg_mailbox(uint64_t* regs, uint64_t lr)
regs[R15] = PPR_PFS_PLAINTEXT_CMAC_HANDLE;
}
else if(ppr_abi->verify_success_abi
!= PPR_VERIFY_SUCCESS_PACKED_STACK_158)
!= PPR_VERIFY_SUCCESS_PACKED_STACK_158
&& ppr_abi->verify_success_abi
!= PPR_VERIFY_SUCCESS_PACKED_STACK_150)
{
regs[R13] = PPR_PFS_PLAINTEXT_CMAC_HANDLE;
}
+32 -2
View File
@@ -209,7 +209,8 @@ def cleanup_body_delta(segments: list[Segment], address: int) -> int:
def locate_cleanup(reference_segments: list[Segment], reference_address: int,
target_segments: list[Segment]) \
target_segments: list[Segment], cleanup_cmac_delta: int,
cleanup_xts_delta: int) \
-> tuple[LocatedSite | None, list[int]]:
site, candidates = locate_site(
reference_segments, reference_address, target_segments
@@ -217,6 +218,30 @@ def locate_cleanup(reference_segments: list[Segment], reference_address: int,
if site is not None:
return site, candidates
# 13.xx adds a branch near the beginning of cleanup_a53io_pkg_keys,
# invalidating the older entry signature. Its two key release calls retain
# a unique local instruction shape. Verify both calls and the entry thunk.
pattern = bytes.fromhex("41 8b 7e 3c e8")
matches = []
for first_load in find_exact(target_segments, pattern):
entry = (first_load + 4 - cleanup_cmac_delta) & MASK64
second_load = (entry + cleanup_xts_delta - 4) & MASK64
if (bytes_at(target_segments, entry, 4)
!= bytes.fromhex("55 48 89 e5")
or bytes_at(target_segments, entry + 0x10, 4)
!= bytes.fromhex("55 48 89 e5")
or bytes_at(target_segments, second_load, 5)
!= bytes.fromhex("41 8b 7e 38 e8")
or rel32_call_target(target_segments, first_load + 4) is None
or rel32_call_target(target_segments,
entry + cleanup_xts_delta) is None):
continue
matches.append(entry)
if len(matches) == 1:
return LocatedSite(matches[0], "paired-key-release-calls"), matches
if matches:
return None, matches
# Newer kernels expose cleanup_a53io_pkg_keys through a short alignment
# thunk. Its rel32 jump changes easily, while the actual body is highly
# distinctive. Match the body and translate back to the public entry.
@@ -422,8 +447,13 @@ def scan(target: Path, reference: Path, header_dir: Path,
name = "ppr_pfs_cleanup_keys"
reference_address = (reference_anchor + reference_known[name]) & MASK64
target_profile = PROFILES.get(ppr_profile_key(major, minor))
if target_profile is None:
raise ValueError(f"no PPR ABI profile for {major}.{minor:02d}")
site, candidates = locate_cleanup(
reference_segments, reference_address, target_segments
reference_segments, reference_address, target_segments,
target_profile.cleanup_cmac_delta,
target_profile.cleanup_xts_delta,
)
if site is None:
ambiguous[name] = [f"{candidate:#x}" for candidate in candidates]
+2
View File
@@ -139,6 +139,8 @@ PROFILES = {
VERIFY_SUCCESS_FW10_11),
12: Profile(0x119, 0x149, bytes.fromhex("bb fe ff ff ff"),
VERIFY_SUCCESS_FW12),
13: Profile(0x12B, 0x15B, bytes.fromhex("bb fe ff ff ff"),
VERIFY_SUCCESS_FW12),
}