Skip to content

Enhanced Payload injection code with error checking #56

@mrdude2478

Description

@mrdude2478

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions