Skip to content

Commit

Permalink
Framework for persisting kernel read/write primitives across JAR exec…
Browse files Browse the repository at this point in the history
…utions (requires new JAR loader):

- Exploit independent base mechanism for kernel memory access persistance
- Implement kernel r/w using ipv6 sockets
- Refactored pointer classes to add some protection against kernel-space page faults
- Added firmware-specific key offsets class. For now, only 1.02 is supported
  • Loading branch information
hammer-83 committed Oct 5, 2024
1 parent e0a2343 commit a7aed15
Show file tree
Hide file tree
Showing 13 changed files with 410 additions and 32 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<xlet.className>org.ps5jb.loader.LoaderXlet</xlet.className>

<!-- Do not use the same version as parent project for Xlet because it will be modified less frequently. We only want to burn new disc if Xlet changes -->
<xlet.version>1.1.2</xlet.version>
<xlet.version>2.0.0</xlet.version>

<!-- Various application configuration properties -->
<loader.port>9025</loader.port>
Expand Down
31 changes: 30 additions & 1 deletion sdk/src/main/java/org/ps5jb/sdk/core/AbstractPointer.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;

/**
* Root parent for any class that implements a pointer to a memory.
*/
public abstract class AbstractPointer {
public abstract class AbstractPointer implements Serializable {
private static final long serialVersionUID = 5085573430112354497L;

/**
* Wrap the given pointer in a non-null check. Returns the same pointer if it is not NULL.
*
Expand Down Expand Up @@ -383,4 +386,30 @@ public boolean equals(Object obj) {
public int hashCode() {
return (new Long(this.addr)).hashCode();
}

/**
* String representation of the pointer
*
* @return Hexadecimal address of the pointer
*/
@Override
public String toString() {
int padLength;
if (addr > 0xFFFFFFFFL) {
padLength = 16;
} else {
padLength = 8;
}

StringBuffer buf = new StringBuffer(padLength);
buf.append("0x");
String hexAddr = Long.toHexString(addr);
int padCount = padLength - hexAddr.length();
for (int i = 0; i < padCount; ++i) {
buf.append("0");
}
buf.append(hexAddr);

return buf.toString();
}
}
5 changes: 2 additions & 3 deletions sdk/src/main/java/org/ps5jb/sdk/core/Pointer.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
* Abstraction over memory pointer operations in user-space.
*/
public class Pointer extends AbstractPointer {
private static final long serialVersionUID = 2230199156786175114L;

/**
* Helper class to obtain an instance of Unsafe in a thread-safe manner.
*/
Expand Down Expand Up @@ -241,9 +243,6 @@ public void write(long offset, byte[] value, int valueOffset, int count) {
* @throws IndexOutOfBoundsException If the read or the write beyond one of the two pointers' sizes occurs.
*/
public void copyTo(Pointer dest, long offset, int size) {
overflow(this, offset, size);
overflow(dest, 0, size);

byte[] data = new byte[size];
read(offset, data, 0, size);
dest.write(0, data, 0, size);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.ps5jb.sdk.core;

/**
* Raised by SDK when a certain functionality requires firmware-specific knowledge,
* but it is not available.
*/
public class SdkSoftwareVersionUnsupportedException extends SdkRuntimeException {
private static final long serialVersionUID = -2958319099920522L;

/**
* Default constructor with no message or cause.
*/
public SdkSoftwareVersionUnsupportedException() {
super();
}

/**
* Constructor with an error message.
*
* @param message Message corresponding to the error condition.
*/
public SdkSoftwareVersionUnsupportedException(String message) {
super(message);
}

/**
* Constructor with a cause.
*
* @param cause Original exception that prompted this exception to be raised.
*/
public SdkSoftwareVersionUnsupportedException(Throwable cause) {
super(cause);
}

/**
* Constructor with error message and cause.
*
* @param message Message corresponding to the error condition.
* @param cause Original exception that prompted this exception to be raised.
*/
public SdkSoftwareVersionUnsupportedException(String message, Throwable cause) {
super(message, cause);
}
}
47 changes: 35 additions & 12 deletions sdk/src/main/java/org/ps5jb/sdk/core/kernel/KernelAccessorIPv6.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package org.ps5jb.sdk.core.kernel;

import java.io.IOException;
import java.io.ObjectInputStream;

import org.ps5jb.loader.KernelAccessor;
import org.ps5jb.sdk.core.Pointer;
import org.ps5jb.sdk.core.SdkException;
import org.ps5jb.sdk.core.SdkRuntimeException;
Expand All @@ -17,29 +21,35 @@
* Requires an existing kernel accessor on creation.
*/
public class KernelAccessorIPv6 implements KernelAccessor {
private final Pointer master_target_buffer;
private final Pointer slave_buffer;
private final Pointer pipemap_buffer;
private final Pointer krw_qword_store;
private static final long serialVersionUID = 8937512308105266960L;

private Pointer master_target_buffer;
private Pointer slave_buffer;
private Pointer pipemap_buffer;
private Pointer krw_qword_store;

private int master_sock;
private int victim_sock;

private final int master_sock;
private final int victim_sock;
private transient LibKernel libKernel;
private transient Socket socket;

private final LibKernel libKernel;
private final Socket socket;
private int[] pipe_fd;
private KernelPointer pipe_addr;

private final int[] pipe_fd;
private final KernelPointer pipe_addr;
private KernelPointer kernelBase;

/**
* Constructor for kernel IPv6 accessor.
*
* @param ofilesAddress Address of "fdt_ofiles" structure from the "proc" structure of the current process.
* @param kernelBase Address of the base of kernel text segment.
* @throws SdkException If an error occurs during accessor creation.
*/
public KernelAccessorIPv6(KernelPointer ofilesAddress) throws SdkException {
public KernelAccessorIPv6(KernelPointer ofilesAddress, KernelPointer kernelBase) throws SdkException {
this.libKernel = new LibKernel();
this.socket = new Socket(this.libKernel);
this.kernelBase = kernelBase;

final long sock_opt_size = 0x14;
master_target_buffer = Pointer.calloc(sock_opt_size);
Expand Down Expand Up @@ -93,7 +103,7 @@ public int getVictimSock() {

/**
* Frees resources in use by this accessor. After calling this method
* this instance should not be used.
* this instance should not be used and kernel access is no longer available.
*/
public synchronized void free() {
if (master_target_buffer != null) {
Expand Down Expand Up @@ -259,4 +269,17 @@ public void write8(long kernelAddress, long value) {
throw new SdkRuntimeException(e);
}
}

@Override
public long getKernelBase() {
return kernelBase == null ? 0 : kernelBase.addr();
}

private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
stream.defaultReadObject();

// Make sure to restore libraries when de-serializing
libKernel = new LibKernel();
socket = new Socket(this.libKernel);
}
}
50 changes: 50 additions & 0 deletions sdk/src/main/java/org/ps5jb/sdk/core/kernel/KernelOffsets.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.ps5jb.sdk.core.kernel;

import java.text.MessageFormat;

import org.ps5jb.sdk.core.SdkSoftwareVersionUnsupportedException;

/**
* Class which is able to return various interesting offsets in kernel based on the console firmware version.
* Note that currently not many firmware versions are supported
*/
public class KernelOffsets {
// Kernel text-relative offsets
public final long OFFSET_KERNEL_DATA;

// Kernel data-relative offsets
public final long OFFSET_KERNEL_DATA_BASE_ALLPROC;
public final long OFFSET_KERNEL_DATA_BASE_SECURITYFLAGS;
public final long OFFSET_KERNEL_DATA_BASE_ROOTVNODE;

/**
* Constructor. The firmware version can be obtained
* by making a call to <code>sceKernelGetProsperoSystemSwVersion</code>
* method in <code>libkernel</code>. Last two bytes of the result return
* the minor and the major version of the firmware.
*
* @param softwareVersion Firmware version in the form 0x[MAJOR BYTE][MINOR BYTE]
*/
public KernelOffsets(int softwareVersion) {
switch (softwareVersion) {
case 0x0102:
{
OFFSET_KERNEL_DATA = 0x01B40000;

OFFSET_KERNEL_DATA_BASE_ALLPROC = 0x026D1BF8;
OFFSET_KERNEL_DATA_BASE_SECURITYFLAGS = 0x06241074;
OFFSET_KERNEL_DATA_BASE_ROOTVNODE = 0x06565540;
break;
}
default:
String strSwVersion = MessageFormat.format(
"{0,number,#0}.{1,number,00}",
new Object[] {
new Integer((softwareVersion >> 8) & 0xFF),
new Integer(softwareVersion & 0xFF)
}
);
throw new SdkSoftwareVersionUnsupportedException(strSwVersion);
}
}
}
44 changes: 31 additions & 13 deletions sdk/src/main/java/org/ps5jb/sdk/core/kernel/KernelPointer.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.ps5jb.sdk.core.kernel;

import org.ps5jb.loader.KernelAccessor;
import org.ps5jb.loader.KernelReadWrite;
import org.ps5jb.sdk.core.AbstractPointer;

/**
Expand All @@ -8,6 +10,11 @@
* by calling {@link KernelReadWrite#setAccessor(KernelAccessor)}.
*/
public class KernelPointer extends AbstractPointer {
private static final long serialVersionUID = 3445279334363239500L;

/** Start of kernel address space. See machine/vmparam.h */
private static final long KERNEL_ADDR_MASK = 0xFFFF800000000000L;

/**
* Returns a pointer instance equivalent to the given native memory address.
*
Expand All @@ -18,6 +25,20 @@ public static KernelPointer valueOf(long addr) {
return addr == 0 ? NULL : new KernelPointer(addr);
}

/**
* Validates that the given kernel pointer has the correct kernel-space range.
*
* @param pointer Pointer to validate.
* @return Same pointer instance.
* @throws IllegalAccessError If the pointer is invalid (including NULL).
*/
public static KernelPointer validRange(KernelPointer pointer) {
if ((((pointer.addr() & KERNEL_ADDR_MASK) != KERNEL_ADDR_MASK) || pointer.addr() == -1)) {
throw new IllegalAccessError(pointer.toString());
}
return pointer;
}

/** Static constant for NULL pointer. */
public static final KernelPointer NULL = new KernelPointer(0);

Expand All @@ -31,31 +52,31 @@ public KernelPointer(long addr, Long size) {

@Override
public byte read1(long offset) {
overflow(this, offset, 1);
overflow(validRange(this), offset, 1);
return KernelReadWrite.getAccessor().read1(this.addr + offset);
}

@Override
public short read2(long offset) {
overflow(this, offset, 2);
overflow(validRange(this), offset, 2);
return KernelReadWrite.getAccessor().read2(this.addr + offset);
}

@Override
public int read4(long offset) {
overflow(this, offset, 4);
overflow(validRange(this), offset, 4);
return KernelReadWrite.getAccessor().read4(this.addr + offset);
}

@Override
public long read8(long offset) {
overflow(this, offset, 8);
overflow(validRange(this), offset, 8);
return KernelReadWrite.getAccessor().read8(this.addr + offset);
}

@Override
public void read(long offset, byte[] value, int valueOffset, int size) {
overflow(this, offset, size);
overflow(validRange(this), offset, size);

// TODO: This can be implemented more efficiently
final KernelAccessor accessor = KernelReadWrite.getAccessor();
Expand All @@ -66,31 +87,31 @@ public void read(long offset, byte[] value, int valueOffset, int size) {

@Override
public void write1(long offset, byte value) {
overflow(this, offset, 1);
overflow(validRange(this), offset, 1);
KernelReadWrite.getAccessor().write1(this.addr + offset, value);
}

@Override
public void write2(long offset, short value) {
overflow(this, offset, 2);
overflow(validRange(this), offset, 2);
KernelReadWrite.getAccessor().write2(this.addr + offset, value);
}

@Override
public void write4(long offset, int value) {
overflow(this, offset, 4);
overflow(validRange(this), offset, 4);
KernelReadWrite.getAccessor().write4(this.addr + offset, value);
}

@Override
public void write8(long offset, long value) {
overflow(this, offset, 8);
overflow(validRange(this), offset, 8);
KernelReadWrite.getAccessor().write8(this.addr + offset, value);
}

@Override
public void write(long offset, byte[] value, int valueOffset, int count) {
overflow(this, offset, count);
overflow(validRange(this), offset, count);

// TODO: This can be implemented more efficiently
final KernelAccessor accessor = KernelReadWrite.getAccessor();
Expand All @@ -108,9 +129,6 @@ public void write(long offset, byte[] value, int valueOffset, int count) {
* @throws IndexOutOfBoundsException If the read or the write beyond one of the two pointers' sizes occurs.
*/
public void copyTo(KernelPointer dest, long offset, int size) {
overflow(this, offset, size);
overflow(dest, 0, size);

byte[] data = new byte[size];
read(offset, data, 0, size);
dest.write(0, data, 0, size);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
* This package contains various classes related to Kernel access.
* Normally, any of the classes in this package are only usable if
* a global kernel accessor has been installed using
* {@link org.ps5jb.sdk.core.kernel.KernelReadWrite#setAccessor(org.ps5jb.sdk.core.kernel.KernelAccessor)}
* {@link org.ps5jb.loader.KernelReadWrite#setAccessor(org.ps5jb.loader.KernelAccessor)}
*/
package org.ps5jb.sdk.core.kernel;
Loading

0 comments on commit a7aed15

Please sign in to comment.