-
Notifications
You must be signed in to change notification settings - Fork 58
Build 506: 4.93 firmware support #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| 0CAC8E2164D7AF81CEBB66ECEEBFB697 | ||
| 17CD6AA1B1EFF72C80F70BE277881893 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| 1.0.505.0 | ||
| 1.0.506.0 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| 81CD7ECFF0F7AF98158F1BD2CE61C8DD | ||
| 19E0D306F8EAEA43097CF4A33A7AA4F3 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| noFSM 4.92 (Evilnat based) | ||
| noFSM 4.93 (Evilnat based) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| # tools | ||
|
|
||
| Helpers for adding support for a new PS3 firmware release. | ||
|
|
||
| Adding a firmware has always been a manual process: unpack two PUPs, decrypt | ||
| the CoreOS out of each, MD5 the ROS region, hand-edit `hashlist.xml`, and drop | ||
| the new payload in as `patch.bin`. These scripts cover the parts that can be | ||
| checked by a machine, so a typo in a hex digit fails here instead of on a | ||
| user's dump. | ||
|
|
||
| Python 3. No third-party packages. | ||
|
|
||
| ## ros_hash.py | ||
|
|
||
| Computes the ROS MD5 that `hashlist.xml` stores, and prints the XML entries | ||
| ready to paste. | ||
|
|
||
| ``` | ||
| python tools/ros_hash.py ofw_content cfw_content --version 4.93 | ||
| python tools/ros_hash.py content --name "4.93 CEX" | ||
| python tools/ros_hash.py content --name "4.93 CEX Patched (Evilnat based)" --patched | ||
| ``` | ||
|
|
||
| Input is a *decrypted* CoreOS payload -- the `content` file from an unpacked | ||
| `CORE_OS_PACKAGE.pkg`, not the `.pkg` itself and not the PUP. It is the same | ||
| 7,340,000-byte blob that ships as `Patches/patch.bin`. | ||
|
|
||
| The hash is the MD5 of the first `0x6FFFE0` bytes, matching | ||
| `HashCheck.CheckHash`. The byte-swapped MD5 is printed alongside it: NOR dumps | ||
| store the region swapped and NAND dumps do not, so seeing both makes a | ||
| mismatch obvious rather than silent. | ||
|
|
||
| Sanity check -- this reproduces the 4.92 entry already in `hashlist.xml`: | ||
|
|
||
| ``` | ||
| $ python tools/ros_hash.py src/PS3DumpChecker/Patches/patch.bin \ | ||
| --name "4.92 CEX Patched (Evilnat based)" --patched | ||
| MD5 : 36BD44795F06B59EECBDAAD6982BE426 | ||
| ``` | ||
|
|
||
| ## validate_data.py | ||
|
|
||
| Checks `hashlist.xml` and `config.xml` before they ship. Run it after editing | ||
| either file. | ||
|
|
||
| ``` | ||
| python tools/validate_data.py | ||
| ``` | ||
|
|
||
| Catches duplicate MD5s, duplicate entry names, malformed hashes, non-hex size | ||
| and offset attributes, bad `patched` values, offsets missing attributes, and | ||
| hashes referencing a type that was never declared. Exits non-zero on error. | ||
|
|
||
| ## build_nofsm_patch.py | ||
|
|
||
| Rebuilds the exact `patch.bin` that PS3Xploit flash-writer 4.93 writes to | ||
| flash. Uses only public inputs (OFW 4.93 CoreOS + `flash493.P3T` from | ||
| `aldostools/flashwriter`). Result matches | ||
| MD5 `AFE831050C31EFB381F9BE4098F1834C` byte-for-byte, i.e. the same ROS | ||
| `pyPS3patcher` and the PS3 Toolset will ship for 4.93 -- so the three tools | ||
| stay in sync and the patched ROS is the safe non-Cobra variant, not the | ||
| Cobra CFW CoreOS. | ||
|
|
||
| ``` | ||
| python tools/build_nofsm_patch.py ofw_content flash493.P3T patch.bin | ||
| ``` | ||
|
|
||
| Get the inputs with: | ||
|
|
||
| - `python tools/coreos_decrypt.py PS3UPDAT_493_OFW.PUP ofw_out` | ||
| (yields `ofw_out/content`) | ||
| - `curl -LO https://raw.githubusercontent.com/aldostools/flashwriter/main/493/flash493.P3T` | ||
|
|
||
| The transformation is a byte splice: | ||
|
|
||
| patched_ros = OFW[0 : 0x1D0] + P3T[:] + OFW[0x1D0 + len(P3T) :] | ||
|
|
||
| The P3T is a partial-ROS overlay whose body mirrors the OFW ROS from | ||
| offset 0x1D0 with only a few SELFs actually modified | ||
| (`sdk_version`, `spu_pkg_rvk_verifier.self`, `default.spp`, `lv1.self`). | ||
|
|
||
| ## coreos_decrypt.py | ||
|
|
||
| Decrypts `CORE_OS_PACKAGE.pkg` out of a PUP and writes the `content` blob -- | ||
| the 7,340,000-byte ROS payload `ros_hash.py` needs. | ||
|
|
||
| ``` | ||
| python tools/coreos_decrypt.py PS3UPDAT.PUP out_dir | ||
| python tools/coreos_decrypt.py --extract-selfs PS3UPDAT.PUP out_dir | ||
| ``` | ||
|
|
||
| Requires `pycryptodome` (`python -m pip install --user pycryptodome`). No other | ||
| external tools -- reimplements `pupunpack`, `unpkg`, and `cosunpkg` from | ||
| fail0verflow/ps3tools in Python, using the public retail PKG keys embedded in | ||
| the script. | ||
|
|
||
| `--extract-selfs` writes each SELF from the decrypted content next to | ||
| `content`. Useful for sanity-checking the extraction (each file should start | ||
| with the `SCE\0` magic). | ||
|
|
||
| ## Getting the decrypted CoreOS | ||
|
|
||
| 1. Get both PUPs and verify them: | ||
| - official `PS3UPDAT.PUP` for the firmware, from Sony's update CDN | ||
| - the matching **CEX** Evilnat CFW -- not `noBD`, `noBT` or `noBD+noBT`, | ||
| which drop modules and therefore hash differently | ||
| 2. `python tools/coreos_decrypt.py PS3UPDAT.PUP out_dir` on each. The | ||
| `out_dir/content` file is what `ros_hash.py` consumes. | ||
| 3. `python tools/ros_hash.py ofw_out/content cfw_out/content --version X.XX`. | ||
|
|
||
| `pup_info.py` prints a PUP's entry table if you want to confirm the layout | ||
| before decrypting. | ||
|
|
||
| ## Adding a firmware, end to end | ||
|
|
||
| 1. Verify both PUP downloads against their published checksums. The Evilnat | ||
| `.rar` ships an `md5.txt`; the official PUP has a SHA-256 published | ||
| alongside it. | ||
| 2. `python tools/coreos_decrypt.py OFW.PUP ofw_out` and again for the CFW. | ||
| 3. `python tools/ros_hash.py ofw_out/content cfw_out/content --version X.XX` | ||
| 4. Paste both entries at the top of `<type name="ROS">` in | ||
| `src/PS3DumpChecker/hashlist.xml`, newest first. | ||
| 5. Copy the CFW `content` to `src/PS3DumpChecker/Patches/patch.bin` and update | ||
| `Patches/patch_info.txt` to `noFSM X.XX (Evilnat based)`. | ||
| 6. `python tools/validate_data.py` | ||
| 7. Bump `AssemblyVersion` and `AssemblyFileVersion` in | ||
| `Properties/AssemblyInfo.cs`, and add a changelog entry. | ||
| 8. Build Release_Embedded. The post-build step refreshes | ||
| `Latest Compiled Version/`, which is what the in-app updater serves -- | ||
| `default.cfg`, `default.hashlist` and their `.md5` files must be regenerated | ||
| or existing installs will keep fetching the old data. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| #!/usr/bin/env python3 | ||
| """ | ||
| build_nofsm_patch.py - Reproduce the PS3Xploit flash-writer 4.93 patched | ||
| CoreOS ROS from public inputs. | ||
|
|
||
| Given an OFW 4.93 CoreOS payload (decrypted with `coreos_decrypt.py`) and | ||
| the `flash493.P3T` shipped by `aldostools/flashwriter`, this reconstructs | ||
| byte-for-byte the same 4.93 CEX Patched (Evilnat-based) ROS that | ||
| PS3Xploit's noFSM flow writes to flash. It is the exact blob PS3DumpChecker | ||
| recognises as `4.93 CEX Patched (Evilnat based)`, | ||
| MD5 = `AFE831050C31EFB381F9BE4098F1834C`. | ||
|
|
||
| Transformation (reverse-engineered from the flash-writer ROP flow): | ||
|
|
||
| patched_ros = OFW[0 : 0x1D0] + P3T[:] + OFW[0x1D0 + len(P3T) :] | ||
|
|
||
| The P3T file is a partial ROS overlay whose body is mostly identical to | ||
| the OFW ROS beginning at offset 0x1D0. Only four SELFs are actually | ||
| modified in it (sdk_version, spu_pkg_rvk_verifier.self, default.spp, | ||
| lv1.self). The remaining bytes match the OFW verbatim, so splicing the | ||
| whole P3T over the OFW at 0x1D0 yields the fully-patched CoreOS. | ||
|
|
||
| Usage: | ||
| python build_nofsm_patch.py OFW_CONTENT flash493.P3T OUT_PATH | ||
|
|
||
| Where: | ||
| OFW_CONTENT the decrypted 4.93 OFW CoreOS `content` file | ||
| (7,340,000 bytes = 0x6FFFE0) | ||
| flash493.P3T the PS3Xploit flash-writer 4.93 overlay | ||
| (from aldostools/flashwriter/493/flash493.P3T) | ||
| OUT_PATH where to write the reconstructed patch.bin | ||
|
|
||
| Requires no third-party packages. Exit codes: 0 ok, 1 error. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import argparse | ||
| import hashlib | ||
| import sys | ||
| from pathlib import Path | ||
|
|
||
| OVERLAY_OFFSET = 0x1D0 | ||
| EXPECTED_ROS_SIZE = 0x6FFFE0 | ||
| TARGET_MD5 = "AFE831050C31EFB381F9BE4098F1834C" | ||
|
|
||
|
|
||
| def build_patch(ofw: bytes, p3t: bytes) -> bytes: | ||
| if len(ofw) != EXPECTED_ROS_SIZE: | ||
| raise ValueError( | ||
| "OFW ROS size 0x%X != expected 0x%X" % (len(ofw), EXPECTED_ROS_SIZE) | ||
| ) | ||
| end = OVERLAY_OFFSET + len(p3t) | ||
| if end > len(ofw): | ||
| raise ValueError( | ||
| "P3T overlay end 0x%X exceeds ROS size 0x%X" % (end, len(ofw)) | ||
| ) | ||
| out = bytearray(ofw) | ||
| out[OVERLAY_OFFSET:end] = p3t | ||
| return bytes(out) | ||
|
|
||
|
|
||
| def main(argv: list[str] | None = None) -> int: | ||
| ap = argparse.ArgumentParser( | ||
| description="Rebuild the 4.93 CEX Patched (Evilnat) ROS from OFW + P3T." | ||
| ) | ||
| ap.add_argument("ofw_content", type=Path, | ||
| help="decrypted OFW 4.93 CoreOS content (0x6FFFE0 bytes)") | ||
| ap.add_argument("p3t", type=Path, | ||
| help="flash493.P3T from aldostools/flashwriter") | ||
| ap.add_argument("out", type=Path, help="output patch.bin path") | ||
| args = ap.parse_args(argv) | ||
|
|
||
| try: | ||
| ofw = args.ofw_content.read_bytes() | ||
| p3t = args.p3t.read_bytes() | ||
| patched = build_patch(ofw, p3t) | ||
| except (OSError, ValueError) as exc: | ||
| print("error: %s" % exc, file=sys.stderr) | ||
| return 1 | ||
|
|
||
| args.out.write_bytes(patched) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Write the patch only after the MD5 check succeeds.
Proposed fix- args.out.write_bytes(patched)
md5 = hashlib.md5(patched).hexdigest().upper()
print("OFW : %s (0x%X bytes)" % (args.ofw_content, len(ofw)))
print("P3T : %s (0x%X bytes)" % (args.p3t, len(p3t)))
- print("OUT : %s (0x%X bytes)" % (args.out, len(patched)))
print("MD5 : %s" % md5)
print("WANT : %s" % TARGET_MD5)
if md5 != TARGET_MD5:
print("MISMATCH", file=sys.stderr)
return 1
+ try:
+ args.out.write_bytes(patched)
+ except OSError as exc:
+ print("error: %s" % exc, file=sys.stderr)
+ return 1
+ print("OUT : %s (0x%X bytes)" % (args.out, len(patched)))
print("MATCH")🧰 Tools🪛 ast-grep (0.45.2)[warning] 82-82: Do not use insecure functions (insecure-hash-functions) 🤖 Prompt for AI Agents |
||
| md5 = hashlib.md5(patched).hexdigest().upper() | ||
|
|
||
| print("OFW : %s (0x%X bytes)" % (args.ofw_content, len(ofw))) | ||
| print("P3T : %s (0x%X bytes)" % (args.p3t, len(p3t))) | ||
| print("OUT : %s (0x%X bytes)" % (args.out, len(patched))) | ||
| print("MD5 : %s" % md5) | ||
| print("WANT : %s" % TARGET_MD5) | ||
|
|
||
| if md5 != TARGET_MD5: | ||
| print("MISMATCH", file=sys.stderr) | ||
| return 1 | ||
| print("MATCH") | ||
| return 0 | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| sys.exit(main()) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Use the noFSM builder in the end-to-end workflow.
This section correctly requires the OFW plus
flash493.P3Tsplice. However, the workflow at Lines 123-124 still tells users to copycfw_out/contentdirectly topatch.bin. That can produce a different patch from the required noFSM target. Replace that step withbuild_nofsm_patch.pyand require the target MD5 before updatingpatch_info.txt.Proposed documentation fix
🤖 Prompt for AI Agents