450 lines
14 KiB
JavaScript
450 lines
14 KiB
JavaScript
/**
|
|
* 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);
|