Editing PS3 Payload Development

Jump to navigation Jump to search
Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.

The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.

Latest revision Your text
Line 1: Line 1:
== Payload benefits (peek/poke advantages in general) ==
Page Will be updated Soon
To make changes to system, without the need (and risk) for hardpatching while making on-demand/live patches possible.
 
* Experiment with CoreOS elements to gain more insight in GameOS/Hypervisor
* On-demand patching (e.g. Hermes' dirty Gamepad patch only when needed)
* Live patching
* Make it easier to maintain
 
== PPU/SPU Assembler ==
It's true, almost no one need assembler anymore but for reversing we need it, because we need to understand what the CPU does. In alot cases we just have a dump to extract this information. I use the PL3 lv2_dump_analyser.idc script to reverse my lv2 dump with IDA.
 
=== Resources ===
* [http://publib.boulder.ibm.com/infocenter/aix/v6r1/index.jsp? IBM Instruction Set]
* [http://pds.twi.tudelft.nl/vakken/in101/labcourse/instruction-set/ Another Instruction Set]
 
== PL3 ==
https://github.com/kakaroto/PL3
 
===Structure of PL3 ===
 
payload_dev.S Is the main "File", this includes everything and has the jailbreak in it (payload_main:)
 
'''pl3.h.S''' Defines the Macros and the "hack" entry point (payload_entry:), which branches payload_main. Also includes the firmware_symbols and config.
 
'''firmware_symbols.h.S''' Defines the most common firmware Symbols (ex. strncmp)
 
'''map_open_path.h.S''' Base function for the Syscall 35 which is added.
 
'''memory_patching.h.S'''
 
'''send_eth.h.S''' Function used to init the eth device.
 
'''send_eth_res.h.S''' Function for sending data via eth. (Protocol is 0x1337)
 
'''memory_patching_res.h.S'''
 
'''dev_syscalls.h.S''' Peek and Poke functions
 
'''open_hook.h.S''' Is the Syscall 35. Hooks the open function.
 
'''print_debug.h.S''' Provides a function for sending debug information via send_eth.
 
'''trace_helpers.h.S''' - Function for saving r3-r12 - Function for loading back r3-r12 - Function for sending information of r3-r12 via eth
 
'''patch_table.h.S''' You can imagine this as an array of patches which are applied to the memory. It says
<pre>
/**
* patch_table:
*
* The patch table used by exploit_main to patch the kernel
* it format is .long address, .long new_value
*
* it will patch its content until the destination address is 0
*
*/
</pre>
 
=== Handy Macros ===
'''MEM_BASE2''' is defined in the '''firmware_symbols.h.S'''
<pre>#define ADDR_IN_PAGE(target) (PAYLOAD_OFFSET_IN_PAGE + (target) - payload_entry)</pre>
 
'''ADDR_IN_MEM2''' calculates the adress relative to the RESIDENT_AREA_OFFSET (resident_area_start)
<pre>#define ADDR_IN_MEM2(target) ((target) - RESIDENT_AREA_OFFSET)</pre>
 
'''ABSOLUTE_MEM2'''
calculates the adress relative to the current position. So you can give your banches an absolute memory address and it calculates the branch address to it for you.
 
<pre>// Absolute branching
#define ABSOLUTE_MEM2(target) (target - (MEM_BASE2 + ADDR_IN_MEM2(.)))</pre>
<pre>// Dynamic macros to load a label into a register
1. define MEM_BASE(dest) \
 
li dest, 1; \
rldicr dest, dest, 63, 0;
</pre>
 
<pre>#define LOAD_LABEL(base, dest, source, address) \
oris dest, source, ((base) + (address))@h; \
ori dest, dest, ((base) + (address))@l;</pre>
 
<pre>#define LOAD_LABEL2(dest, source, address) \
LOAD_LABEL(MEM_BASE2, dest, source, ADDR_IN_MEM2 (address))</pre>
 
<pre>#define LOADI_LABEL2(dest, address) \
LOAD_LABEL2(dest, dest, address)</pre>
 
<pre>#define LOAD_MEM_BASE2(dest) \
MEM_BASE (dest) \
LOAD_LABEL (MEM_BASE2, dest, dest, 0)</pre>
 
<pre>// Add system calls. Use only in exploit_main because of registers used...
1. define ADD_SYSCALL(source, ptr, num) \
 
LOAD_LABEL2 (%r3, source, ptr); \
LOAD_ABS (%r4, source, syscall_table); \
std %r3, 0x08*num(%r4); \</pre>
 
<pre>// For loading an absolute value
1. define LOAD_ABS(dest, source, address) LOAD_LABEL(0, dest, source, address)
2. define LOADI_ABS(dest, address) LOAD_ABS(dest, dest, address)</pre>
 
<pre>// Absolute .quads
// HACK ALERT: the open toolchain bugs during compilation when trying to add
// a 'bignum' with address or MEM_BASE1.. so we split it here into two .long
// makes it easy since PPC is big endian.
 
  1. define QUAD_MEM2(address) \
 
.long 0x80000000; \
.long MEM_BASE2 + ADDR_IN_MEM2(address);</pre>
 
<pre>/* Patch Table Macros */
  1. define PATCH_INST(offset, instruction...) \
 
.long offset; \
instruction;</pre>
 
<pre>#define PATCH_DATA(offset, data...) \
.long offset; \
.long data; </pre>
 
<pre>#define PATCH_BRANCH(offset, op, target) \
.long offset; \
op ((target) - (offset));</pre>
 
<pre>#define PATCH_BRANCH_MEM2(offset, op, target) \
PATCH_BRANCH (offset, op, (MEM_BASE2 + ADDR_IN_MEM2(target)));</pre>
 
<pre>#define BRANCH_ABSOLUTE(dest, target) \
MEM_BASE (dest); \
oris dest, dest, target@h; \
ori dest, dest, target@l; \
mtctr dest; \
bctrl;</pre>
 
<pre>#define DEFINE_FUNC_PTR(function) \
function##_ptr: \
.quad 0; \
function: \
mflr %r0; \
stdu %r1, -0x80(%r1); \
std %r31, 0x70(%r1); \
std %r0, 0x90(%r1); \
BRANCH_FUNC_PTR(%r31, function); \
ld %r31, 0x70(%r1); \
ld %r0, 0x90(%r1); \
addi %r1, %r1, 0x80; \
mtlr %r0; \
blr;</pre>
 
<pre>#define BRANCH_FUNC_PTR(dest, function) \
MEM_BASE (dest); \
LOAD_LABEL2 (dest, dest, function ##_ptr); \
ld dest, 0(dest); \
mtctr dest; \
bctrl;</pre>
 
<pre>#define LOAD_FUNC_PTR(function) \
ALLOC_AND_COPY_PROC(%r31, function ##_start, \
(function ## _end - function##_start)); \
LOAD_LABEL2 (%r6, %r30, function ##_ptr); \
std %r3, 0(%r6);</pre>
 
<pre>#define GET_CURRENT_PAGE(temp, dest) \
bl get_current_page; \
b got_current_page; \
get_current_page: \
mflr dest; \
blr; \
got_current_page: \
li temp, 0xfff; \
nor temp, temp, temp; \
and dest, dest, temp;</pre>
 
<pre>#define PANIC() \
      li      %r3, 0; \
      li      %r11, 255; \
      sc      1;</pre>
 
<pre>#define ALLOCATE_BUFFER(base, variable, size) \
li      %r3, size; \
li      %r4, 0x27; \
BRANCH_ABSOLUTE(%r5, alloc); \
LOAD_LABEL2 (%r4, base, variable); \
std    %r3, 0(%r4);</pre>
 
<pre>// Allocate new memory and copy a function to it. R3 to R11 will be lost
// pl3_memcpy must be included!
 
define ALLOC_AND_COPY_PROC(base_reg, function, size) \</pre>
 
<pre>// Copy functions that need to stay resident in memory to MEM_BASE2
1. define COPY_RESIDENT_AREA(base, page) \
 
LOAD_LABEL (MEM_BASE2, %r3, base, 0); \
addi %r4, page, ADDR_IN_PAGE(RESIDENT_AREA_OFFSET); \
li %r5, RESIDENT_AREA_SIZE; \
bl pl3_memcpy; \
</pre>
 
=== Understanding the Hack ===
(section never written)
 
=== Patching ===
(section never written)
 
=== Hooking a function (strncmp) ===
I try to explain how to do it with the PL3. PL3 loads a patch_table (see below). This patches are applied to the memory.
 
I used this way for the hook of strncmp: Orginal PL3:
<pre>
...
patch_table:
PATCH_DATA(patch_data1, 0x01000000)
PATCH_INST(patch_func1 + patch_func1_offset, ld %r4, rtoc_entry_1(%r2)) //hang
PATCH_INST(patch_func1 + patch_func1_offset + 4, ld %r3, 0x20(%r28))
PATCH_INST(patch_func1 + patch_func1_offset + 8, std %r3, 0(%r4))
#ifdef __MEMORY_PATCHING_H_S__
PATCH_BRANCH_MEM2 (patch_func2 + patch_func2_offset, bl, memory_patching)
#endif
#ifdef __OPEN_HOOK_H_S__
PATCH_BRANCH_MEM2 (patch_func3 + patch_func3_offset, b, hook_open)
#endif
PATCH_INST(patch_func4 + patch_func4_offset, li %r4, 0) //80010009 error
PATCH_INST(patch_func4 + patch_func4_offset + 4, stw %r4, 0(%r3))
PATCH_INST(patch_func4 + patch_func4_offset + 8, blr)
#ifndef NO_UNAUTH_SYSCALL
PATCH_INST(patch_func5 + patch_func5_offset, li %r3, 1) //check feature?
PATCH_INST(patch_func5 + patch_func5_offset + 4, blr)
PATCH_INST(patch_func6 + patch_func6_offset, li %r3, 0)
PATCH_INST(patch_func7 + patch_func7_offset, li %r3, 0)
#endif
// force lv2open return 0
PATCH_INST(patch_func8 + patch_func8_offset1, li %r3, 0)
// disable calls in lv2open to lv1_send_event_locally which makes
// the system crash
PATCH_INST(patch_func8 + patch_func8_offset2, nop)
PATCH_INST(patch_func9 + patch_func9_offset, nop)
#ifdef __SYSCALL_HANDLER_H_S__
PATCH_BRANCH_MEM2 (patch_syscall_func, bl, syscall_handler)
#endif
#ifdef __PRINT_DEBUG_H_S__
//PATCH_BRANCH_MEM2(lv2_printf_null + 8, b, print_debug)
//PATCH_BRANCH_MEM2(lv2_printf_null, b, print_debug)
PATCH_BRANCH_MEM2(hvsc107_1, bl, print_hvsc107)
PATCH_BRANCH_MEM2(hvsc107_2, bl, print_hvsc107)
PATCH_BRANCH_MEM2(hvsc107_3, bl, print_hvsc107)
#endif
.long 0
...
</pre>
 
I added before the end of the Patch Table
 
<pre>#ifdef __STRNCMP_HACK__
PATCH_BRANCH_MEM2 (strncmp, b, hook_strncmp)
#endif
STRNCMP Included Version, if __STRNCMP_HACK__ is defined this is added to the PatchTable. </pre>
 
<pre>...
patch_table:
PATCH_DATA(patch_data1, 0x01000000)
PATCH_INST(patch_func1 + patch_func1_offset, ld %r4, rtoc_entry_1(%r2)) //hang
PATCH_INST(patch_func1 + patch_func1_offset + 4, ld %r3, 0x20(%r28))
PATCH_INST(patch_func1 + patch_func1_offset + 8, std %r3, 0(%r4))
#ifdef __MEMORY_PATCHING_H_S__
PATCH_BRANCH_MEM2 (patch_func2 + patch_func2_offset, bl, memory_patching)
#endif
#ifdef __OPEN_HOOK_H_S__
PATCH_BRANCH_MEM2 (patch_func3 + patch_func3_offset, b, hook_open)
#endif
PATCH_INST(patch_func4 + patch_func4_offset, li %r4, 0) //80010009 error
PATCH_INST(patch_func4 + patch_func4_offset + 4, stw %r4, 0(%r3))
PATCH_INST(patch_func4 + patch_func4_offset + 8, blr)
#ifndef NO_UNAUTH_SYSCALL
PATCH_INST(patch_func5 + patch_func5_offset, li %r3, 1) //check feature?
PATCH_INST(patch_func5 + patch_func5_offset + 4, blr)
PATCH_INST(patch_func6 + patch_func6_offset, li %r3, 0)
PATCH_INST(patch_func7 + patch_func7_offset, li %r3, 0)
#endif
// force lv2open return 0
PATCH_INST(patch_func8 + patch_func8_offset1, li %r3, 0)
// disable calls in lv2open to lv1_send_event_locally which makes
// the system crash
PATCH_INST(patch_func8 + patch_func8_offset2, nop)
PATCH_INST(patch_func9 + patch_func9_offset, nop)
#ifdef __SYSCALL_HANDLER_H_S__
PATCH_BRANCH_MEM2 (patch_syscall_func, bl, syscall_handler)
#endif
#ifdef __PRINT_DEBUG_H_S__
//PATCH_BRANCH_MEM2(lv2_printf_null + 8, b, print_debug)
//PATCH_BRANCH_MEM2(lv2_printf_null, b, print_debug)
PATCH_BRANCH_MEM2(hvsc107_1, bl, print_hvsc107)
PATCH_BRANCH_MEM2(hvsc107_2, bl, print_hvsc107)
PATCH_BRANCH_MEM2(hvsc107_3, bl, print_hvsc107)
#endif
#ifdef __STRNCMP_HACK_H_S__
PATCH_BRANCH_MEM2 (strncmp, b, hook_strncmp)
#endif
.long 0
...</pre>
strncmp is already defined in the '''firmware_symbols.h.S''' so we can easy use it. hook_strncmp is our "injected" function.
 
For example:
<pre>
#ifndef __STRNCMP_HACK_H_S__
#define __STRNCMP_HACK_H_S__
hook_strncmp:
b      ABSOLUTE_MEM2(strncmp + 4)
#endif
</pre>
Now our strncmp gets hooked and our function will get called and we jump back to the strncmp.
 
Why this won't work that easy and why we jump to "strncmp+4" will be more clear in "Recreate overwritten code".
 
=== Recreate overwritten code ===
First we have to know what really happens in the memory.
 
The strncmp function on my 3.41 dump looks like this:
<pre>
ROM:0004D344 strncmp:                             
ROM:0004D344                cmpdi  %r5, 0
ROM:0004D348                beq    loc_4D398
ROM:0004D34C                lbz    %r11, 0(%r4)
ROM:0004D350                lbz    %r9, 0(%r3)
ROM:0004D354                clrlwi  %r0, %r11, 24
ROM:0004D358                cmpw    cr7, %r9, %r11
ROM:0004D35C                bne    cr7, loc_4D3A4
ROM:0004D360                cmpwi  cr7, %r0, 0
ROM:0004D364                mtctr  %r5
ROM:0004D368                bne    cr7, loc_4D38C
ROM:0004D36C                b      loc_4D3A4
ROM:0004D370 loc_4D370:
ROM:0004D370                lbz    %r11, 0(%r4)
ROM:0004D374                lbz    %r9, 0(%r3)
ROM:0004D378                clrlwi  %r0, %r11, 24
ROM:0004D37C                cmpw    cr7, %r9, %r11
ROM:0004D380                cmpwi  cr6, %r0, 0
ROM:0004D384                bne    cr7, loc_4D3A4
ROM:0004D388                beq    cr6, loc_4D3A4
ROM:0004D38C loc_4D38C:
ROM:0004D38C                addi    %r3, %r3, 1
ROM:0004D390                addi    %r4, %r4, 1
ROM:0004D394                bdnz    loc_4D370
ROM:0004D398 loc_4D398:
ROM:0004D398                li      %r0, 0
ROM:0004D39C                extsw  %r3, %r0
ROM:0004D3A0                blr
ROM:0004D3A4 loc_4D3A4:
ROM:0004D3A4                clrlwi  %r9, %r9, 24
ROM:0004D3A8                clrlwi  %r0, %r11, 24
ROM:0004D3AC                subf    %r0, %r0, %r9
ROM:0004D3B0                extsw  %r3, %r0
ROM:0004D3B4                blr
</pre>
 
After the patch is applied it looks like this:
<pre>
ROM:0004D344 strncmp:                             
//This is removed
//ROM:0004D344                cmpdi  %r5, 0
//gets overwritten with our patchtable hack
ROM:0004D344                b      hook_strncmp
ROM:0004D348                beq    loc_4D398
ROM:0004D34C                lbz    %r11, 0(%r4)
ROM:0004D350                lbz    %r9, 0(%r3)
ROM:0004D354                clrlwi  %r0, %r11, 24
ROM:0004D358                cmpw    cr7, %r9, %r11
ROM:0004D35C                bne    cr7, loc_4D3A4
ROM:0004D360                cmpwi  cr7, %r0, 0
ROM:0004D364                mtctr  %r5
ROM:0004D368                bne    cr7, loc_4D38C
ROM:0004D36C                b      loc_4D3A4
ROM:0004D370 loc_4D370:
ROM:0004D370                lbz    %r11, 0(%r4)
ROM:0004D374                lbz    %r9, 0(%r3)
ROM:0004D378                clrlwi  %r0, %r11, 24
ROM:0004D37C                cmpw    cr7, %r9, %r11
ROM:0004D380                cmpwi  cr6, %r0, 0
ROM:0004D384                bne    cr7, loc_4D3A4
ROM:0004D388                beq    cr6, loc_4D3A4
ROM:0004D38C loc_4D38C:
ROM:0004D38C                addi    %r3, %r3, 1
ROM:0004D390                addi    %r4, %r4, 1
ROM:0004D394                bdnz    loc_4D370
ROM:0004D398 loc_4D398:
ROM:0004D398                li      %r0, 0
ROM:0004D39C                extsw  %r3, %r0
ROM:0004D3A0                blr
ROM:0004D3A4 loc_4D3A4:
ROM:0004D3A4                clrlwi  %r9, %r9, 24
ROM:0004D3A8                clrlwi  %r0, %r11, 24
ROM:0004D3AC                subf    %r0, %r0, %r9
ROM:0004D3B0                extsw  %r3, %r0
ROM:0004D3B4                blr
</pre>
 
The Problem: We overwritte code to call our function. We have to recreate the code. Easy if you know what you have overwritten:
 
We have overwritten this: ROM:0004D344 cmpdi  %r5, 0 So our Code changes to:
 
<pre>#ifndef __STRNCMP_HACK_H_S__
#define __STRNCMP_HACK_H_S__
hook_strncmp:
cmpdi  %r5, 0
b      ABSOLUTE_MEM2(strncmp + 4)
#endif
</pre>
That we jump to "strncmp+4" should be clear now, we have to "overjump" our branch to hook_strncmp.
 
=== Saving the Register ===
The Problem is we hook into a function and play around. This will modify our register and if the programm uses those register, we may change the outcome. To prevent this, we save all register we modify and reload them before we jump back to our real code.
 
KaKaRoToKS has made code for us, we can use it. I explain you how this works, so you dont need to use it blind.
 
We look at these lines partial found in '''trace_helpers.h.S.''' This simply stores all register and loads them to/from the address of r1.
 
<pre>
store_regs:
std %r3, 0x70(%r1)
std %r4, 0x78(%r1)
std %r5, 0x80(%r1)
std %r6, 0x88(%r1)
std %r7, 0x90(%r1)
std %r8, 0x98(%r1)
std %r9, 0xA0(%r1)
std %r10, 0xA8(%r1)
std %r11, 0xB0(%r1)
std %r12, 0xB8(%r1)
blr
load_regs:
ld %r3, 0x70(%r1)
ld %r4, 0x78(%r1)
ld %r5, 0x80(%r1)
ld %r6, 0x88(%r1)
ld %r7, 0x90(%r1)
ld %r8, 0x98(%r1)
ld %r9, 0xA0(%r1)
ld %r10, 0xA8(%r1)
ld %r11, 0xB0(%r1)
ld %r12, 0xB8(%r1)
blr
</pre>
How to use this: As simple as understanding everything :D. (Background knowledge: r1 is used as stack pointer).
 
First we save the link register in r0.
 
<pre>mflr %r0</pre>
After that we get space on the stack for our register data.
 
<pre>stdu %r1, -0x100(%r1)</pre>
Saving the register
 
<pre>bl store_regs</pre>
Loading our saved register
 
<pre> bl load_regs</pre>
Free our stack memory
 
<pre> addi %r1, %r1, 0x100 </pre>
Reset the link register
 
<pre> mtlr %r0 </pre>
[Not tested] We use this in our code, so we can do our own crap without destroying something:
 
<pre>
#ifndef __STRNCMP_HACK_H_S__
#define __STRNCMP_HACK_H_S__
#include "trace_helpers.h.S"
 
hook_strncmp:
//Save the link Register
mflr %r0
//Get space on the stack (r1 = StackPointer)
stdu %r1, -0x100(%r1)
//Save the register
bl store_regs
 
//... OUR CODE
 
//Restore register
bl load_regs
//Restore stack
  addi %r1, %r1, 0x100
//Restore link Register
mtlr %r0
//Our overwritten code
cmpdi  %r5, 0
//Branch original strncmp
b      ABSOLUTE_MEM2(strncmp + 4)
#endif</pre>
 
=== Modify a function  ===
(section never written)
 
=== Doing our own crap  ===
(section never written)
 
=== Debug via ETH  ===
Using payloader3 in combination with [https://github.com/AerialX/Kammy kammy] you are able to receive debug via udp over ethernet.
(linux instructions)
# Build payloader3 from the latest source<br/>
# Install the payloader3 pkg on the ps3<br/>
# This may a good place to set export in terminal<br/>
## export PS3LOAD=tcp:ipaddress.of.ps3<br/>
## start socat (socat udp-recv:18194 stdout)<br/>
# Launch payloader3 pkg from ps3<br/>
# Its quite possible you will not be able to see the screen but you will hear a sound this sound is said to be from and old C64 Demo, certain actions can be performed here:
## X then Square will launch ps3load with ethdebug<br/>
## X then circle will return back to the xmb loading ethdebug (for debuging pkg files)<br/>
# When using ps3load mode send self to your ps3 (ps3load selfile.self), you should be seeing debug in your teminal<br/>
# if not using ps3load you will be taken back to xmb where you can load a pkg file and will see debugging in  terminal
 
=== Creating a Syscall (explained on Peek & Poke)  ===
(section never written)
 
=== Compiling PL3 (debian/Ubuntu 10.10) ===
 
====Prerequisites====
Use your package manager to install everything required. Debian systems often need the -dev version of each package.<br />
 
{{info|content=
If you are installing on a system and you do not have root access, then the admin will have to do this command for you and you may need to install the binaries into your home directory
}}
 
* autoconf
* bison
* build-essential
* cpp-4.3
* curl
* g++-4.3
* gcc-4.3
* git-core
* gnupg flex
* gperf
* libesd0-dev
* libncurses5-dev
* libsdl1.2-dev
* libwxgtk2.6-dev
* texinfo
* zip
* zlib1g-dev
 
==== HowTo : Step by step ====
First thing we need to do is install some applications through apt, so open a terminal and do the following:
 
{{keyboard|content=<syntaxhighlight lang="bash">
sudo apt-get install git-core gnupg flex bison gperf libsdl1.2-dev libesd0-dev libwxgtk2.6-dev \
build-essential zip curl libncurses5-dev zlib1g-dev gcc-4.3 cpp-4.3 g++-4.3 texinfo autoconf
</syntaxhighlight>}}
 
Now we need to clone PSFreedom from github to our local PC, pull it's submodules and get the toolchain installed
 
{{keyboard|content=<syntaxhighlight lang="bash">
cd ~/
git clone git://github.com/kakaroto/PSFreedom.git
cd PSFreedom
git submodule update --init
cd pl3
git submodule update --init
cd ps3toolchain
sudo ./toolchain-sudo.sh
</syntaxhighlight>}}
 
Now go get a Coffee/sleep, installing the toolchain can take a few hours (6-7 hours if you are running Ubuntu in a Virtual PC)
 
Ok, so you have had your Coffee or sleep and you have the toolchain installed & PSFreedom cloned to your home directory, the hard part is over.
 
Now you need to edit your .bashrc file:
 
{{keyboard|content=<syntaxhighlight lang="bash">gedit ~/.bashrc</syntaxhighlight>}}
 
Add the following line to the end of the file:
 
{{keyboard|content=<syntaxhighlight lang="bash">export PATH=${PATH}:/usr/local/ps3dev/ppu/bin/</syntaxhighlight>}}
 
To compile PSFreedom's payloads is simple now, open a terminal and do the following:
 
{{keyboard|content=<syntaxhighlight lang="bash">
cd ~/PSFreedom/pl3
make
</syntaxhighlight>}}
 
That's it, you can now find the payload .bin files in the PSFreedom/pl3 folder.
 
== Other ==
* https://github.com/psgroove
* https://github.com/kakaroto/PSFreedom
 
 
''Palmam qui meruit ferat''
 
 
 
{{Development}}<noinclude>[[Category:Main]]</noinclude>
Please note that all contributions to PS3 Developer wiki are considered to be released under the GNU Free Documentation License 1.2 (see PS3 Developer wiki:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!

To protect the wiki against automated edit spam, we kindly ask you to solve the following hCaptcha:

Cancel Editing help (opens in new window)