-
Notifications
You must be signed in to change notification settings - Fork 68
Open
Description
Not an issue as such,but current payload injection code can cause errors/crash when copying into a buffer incorrectly or a byte gets scambled. This improved code attempts to fix this by fetching the payload twice and comparing the buffers to make sure there's no corruption, also fetching has error checking and memory checks. I've been using this code for the last week and it's been good and caught a few times times when there was a random error and prevented the ps4 from crashing.
function isAddressFree(addr, size) {
const temp_map = chain.sysp('mmap', addr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_FIXED, -1, 0);
if (temp_map.eq(addr)) {
// Success: Unmap and return true
sysi('munmap', temp_map, size);
return true;
}
else if (!temp_map.isNull()) {
sysi('munmap', temp_map, size);
}
return false; // Already mapped or error
}
async function fetchPayloadWithRetry(url, retries = 3, timeoutMs = 1000) {
for (let i = 0; i < retries; i++) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeoutId);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const arrayBuffer = await response.arrayBuffer();
clearTimeout(timeoutId); // Clear timeout after arrayBuffer() completes
return arrayBuffer;
} catch (err) {
if (i === retries - 1) {
log(`Payload fetch failed ${retries} times, press the PS button to exit and try again`);
} else {
log(`Attempt ${i + 1} failed: ${err.name === 'AbortError' ? 'Timeout' : err.message}. Retrying...`);
}
}
}
}
async function runPayload(path) {
const MAX_RETRIES = 3;
let attempt = 0;
// Declare variables at function scope for proper cleanup
let PLD1 = null, PLD2 = null;
let shellcode1 = null, shellcode2 = null;
let payload_buffer = null;
while (attempt < MAX_RETRIES) {
attempt++;
try {
// Fetch payload twice independently
[PLD1, PLD2] = await Promise.all([
fetchPayloadWithRetry(path),
fetchPayloadWithRetry(path)
]);
if (PLD1.byteLength === 0 || PLD2.byteLength === 0) {
log("Error Fetching " + path);
return false;
}
// Prepare both payloads with proper padding
const preparePayload = (pld) => {
const alignedSize = Math.ceil(pld.byteLength / 4) * 4;
const buffer = new ArrayBuffer(alignedSize);
new Uint8Array(buffer).set(new Uint8Array(pld));
return new Uint32Array(buffer);
};
shellcode1 = preparePayload(PLD1);
shellcode2 = preparePayload(PLD2);
// Verify payload sizes
if (shellcode1.length !== shellcode2.length) {
log(`Payload size mismatch between fetches (Attempt ${attempt}/${MAX_RETRIES})`);
continue; // retry
}
// Verify memory contents
let corrupted = false;
for (let i = 0; i < shellcode1.length; i++) {
if (shellcode1[i] !== shellcode2[i]) {
log(`Mismatch at offset ${i}: PLD1=0x${shellcode1[i].toString(16)}, PLD2=0x${shellcode2[i].toString(16)}`);
corrupted = true;
break;
}
}
if (corrupted) {
log(`Payload verification failed (Attempt ${attempt}/${MAX_RETRIES})`);
continue; // retry
}
// Use the verified payload (shellcode1)
const originalLength = PLD1.byteLength;
const paddingLength = (4 - (originalLength % 4)) % 4;
const totalLength = originalLength + paddingLength;
if (totalLength === 0) {
throw new Error("Payload size is zero");
}
// Fixed high VA base (0x900000000)
const fixed_base = new Int(0, 9);
// Check if fixed address is free
if (isAddressFree(fixed_base, totalLength)) {
// Map with RWX, FIXED to enforce high VA
payload_buffer = chain.sysp('mmap', fixed_base, totalLength, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_FIXED, -1, 0);
if (!payload_buffer.eq(fixed_base)) {
throw new Error("mmap with FIXED failed at 0x900000000");
}
} else {
// Fallback: Kernel-chosen address
payload_buffer = chain.sysp('mmap', 0, totalLength, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON, -1, 0);
}
// Create native view and copy verified shellcode
const native_view = array_from_address(payload_buffer, shellcode1.length);
native_view.set(shellcode1, 0);
// Ensure RWX protection
chain.sys('mprotect', payload_buffer, totalLength, PROT_READ | PROT_WRITE | PROT_EXEC);
// Execute payload
const result = chain.call_void(payload_buffer);
// Cleanup
sysi('munmap', payload_buffer, totalLength);
if (result !== 0) {
return true;
} else {
return false;
}
} catch (error) {
// Cleanup resources
if (payload_buffer && !payload_buffer.isNull()) {
try {
sysi('munmap', payload_buffer, totalLength || 0);
} catch (e) {
// Ignore cleanup errors
}
}
// Ensure cleanup even on error
PLD1 = null;
PLD2 = null;
shellcode1 = null;
shellcode2 = null;
payload_buffer = null;
log(`Payload injection failed (Attempt ${attempt}/${MAX_RETRIES}): ${error.message}`);
continue; // retry
}
}
log(`Failed to load payload after ${MAX_RETRIES} attempts`);
return false;
}
You can try that code if you want.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels