Editing CCAPI

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 246: Line 246:
= CCAPI 2.70 - 2.80 rev5 =
= CCAPI 2.70 - 2.80 rev5 =


== Location of ccapi.sprx ==
''' Location of ccapi.sprx '''
 
ccapi.sprx is renamed to sys_audio.sprx and is located to /dev_flash/sys/internal/sys_audio.sprx


ccapi.sprx is renamed to sys_audio.sprx and is located to '''/dev_flash/sys/internal/sys_audio.sprx'''


== Packets and http Requests ==
== Packets and http Requests ==
<br>
 
Port:6333<br>
Port:6333
Communication: HTTP<br>
Communication: HTTP
<br>
 
Port:1979 PS3 <br>
Port:1979 PS3  
Communication: TCP/UDP<br><br>
Communication: TCP/UDP<br>
<code>static int connect_to_CAPPI(void)<br>{<br>struct sockaddr_in sin;<br>int s;<br>sin.sin_family = AF_INET;<br>sin.sin_addr.s_addr = 0x7F000001; //127.0.0.1 (localhost)<br>sin.sin_port = htons(6333);<br>s = socket(AF_INET, SOCK_STREAM, 0);<br>if (s < 0)<br>{<br>return -1;<br>}<br>if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)<br>{<br>return -1;<br>}<br>return s;<br>}<br></code><br>
<code>static int connect_to_CAPPI(void)<br>{<br>struct sockaddr_in sin;<br>int s;<br>sin.sin_family = AF_INET;<br>sin.sin_addr.s_addr = 0x7F000001; //127.0.0.1 (localhost)<br>sin.sin_port = htons(6333);<br>s = socket(AF_INET, SOCK_STREAM, 0);<br>if (s < 0)<br>{<br>return -1;<br>}<br>if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)<br>{<br>return -1;<br>}<br>return s;<br>}<br></code>
<code>void DoNotify(char *szFormat,int id = 0) <br>{<br>char _notify_buffer[512];<br>snprintf(_notify_buffer, 512, "/ccapi/notify?id=%i&msg=%s",id,szFormat);<br>send_CCAPI_request(_notify_buffer);<br>}<br></code><br>
<code>void DoNotify(char *szFormat,int id = 0) <br>{<br>char _notify_buffer[512];<br>snprintf(_notify_buffer, 512, "/ccapi/notify?id=%i&msg=%s",id,szFormat);<br>send_CCAPI_request(_notify_buffer);<br>}<br></code>
<br><br>
<code>void ShutDownPS3() <br>{<br>char _notify_buffer[512];<br>snprintf(_notify_buffer, 512, "/ccapi/shutdown?mode=1");<br>send_CCAPI_request(_notify_buffer);<br>}<br></code><br>
<code>void ShutDownPS3() <br>{<br>char _notify_buffer[512];<br>snprintf(_notify_buffer, 512, "/ccapi/shutdown?mode=1");<br>send_CCAPI_request(_notify_buffer);<br>}<br></code><br>
<code>void RestartPS3()<br> {<br>char _notify_buffer[512];<br>snprintf(_notify_buffer, 512, "/ccapi/shutdown?mode=2");<br>send_CCAPI_request(_notify_buffer);<br>}<br></code><br>
<code>void RestartPS3()<br> {<br>char _notify_buffer[512];<br>snprintf(_notify_buffer, 512, "/ccapi/shutdown?mode=2");<br>send_CCAPI_request(_notify_buffer);<br>}<br></code><br>
Line 265: Line 267:
https//pastebin.com/RqnvPZ0j
https//pastebin.com/RqnvPZ0j


The codes below are not verified to work, these are just example codes for you,
== Use CCAPI Syscall from sprx ==
so you can get a theoretical understanding, on how you could call a CCAPI Syscall.
 
== Use CCAPI Syscall from SPRX (Jo-Milks Approach) ==


CCAPI uses a unique PPC instruction that it implemented it. I named it "ccsc" as it works similarly to "sc" the syscall instruction in PPC. Here is the HEX value that represents "ccsc".
CCAPI uses a unique PPC instruction that it implemented it. I named it "ccsc" as it works similarly to "sc" the syscall instruction in PPC. Here is the HEX value that represents "ccsc".
Line 285: Line 284:
} </code>
} </code>


== Use CCAPI Syscall from SPRX (PHTNC Approach) ==
<code>#define CCAPICall uint64_t __attribute__((naked))</code>
<code>static CCAPICall WriteProcessMemory(uint32_t pid, void* address, size_t size, const void* buffer)<br>
{<br>
register uint64_t p1 __asm__ ("3") = 0x123;<br>
register uint64_t p2 __asm__ ("4") = (uint64_t)pid;<br>
register uint64_t p3 __asm__ ("5") = address;<br>
register uint64_t p4 __asm__ ("6") = (uint64_t)buffer;<br>
register uint64_t p5 __asm__ ("7") = (uint64_t)size;<br>
register uint64_t p6 __asm__ ("8");<br>             
register uint64_t p7 __asm__ ("9");<br>                                       
register uint64_t p8 __asm__ ("10");<br>
register uint64_t p9 __asm__ ("11");<br>
__asm__ volatile (0xEF455314<br>                                                   
: "=r" (p1), "=r" (p2), "=r" (p3), "=r" (p4),<br>
"=r" (p5), "=r" (p6), "=r" (p7), "=r" (p8), "=r" (n)<br> 
: "r" (p1), "r" (p2), "r" (p3), "r" (p4),<br>
"r" (p5), "r" (n)<br>                                     
: "0", "12", "lr", "ctr", "xer", "cr0", "cr1", "cr5", "cr6", "cr7", "memory");<br>
return (int)p1;<br>
}<br>
</code>
R3 or the first argument will be the value of the Command ID that will be processed by a kernel in a function implemented by CCAPI.<br>
CCAPI Syscalls do not seem use R11 to provide a syscall number like LV2 syscalls do. Not every function of CCAPI is called through its<br>
own environment. CCAPI makes a lot of use of regular LV2 Syscalls like RingBuzzer, Shutdown, or ControlLED. It only makes use of its own<br>
environment, when these LV2 functions are not available for all environments. (WriteProcessMemory, ReadProcessMemory in CEX for example.)<br>
We cannot say how exactly CCAPI initializes itself and its environment, however, I (PHTNC) believe, that CCAPI probably takes a similar approach like Mamba.<br>
Right now, I cannot say, which system file(s) CCAPI does modify to load itself. Mamba for example loads itself from a modified sys_init_osd.self file.<br>
Cobra loads itself from lv2kernel.self.<br>
== TheRouLetteBois Writeup ==
jo-milk and I were the ones that dumped enstones menus. I was requested to stay anonymous with related to the crack and in the credits (fuck it I guess).  Milk was the one that found that custom syscall instruction by crashing into a specific packet in the switch case then looking at the call stack and I did the kernel research.
The way enstones custom instruction works is that he hooks where exceptions are handled and that way he checks for invalid instruction exception and proceeds to validate his specific instruction.
At this location is where you will find the ccapi sycall switch case 0x8000000000691840
In reference the the offset above it is for lv2 4.84 dex with ccapi 2.80 rev5. The offset changes on each console boot but it should be relatively easy to find by searching for one of the syscall commands like 0x977. The method used to block TMAPI and CCAPI is by returning 0 in GetProcessList function in the kernel.
In relation to CCAPI VSH plugin since Milk found where "ccapi syscall" was located at we decided to hook into it using HookFunctionStart. So I went and bought a FMT mod menu "Fury" and decided to inject it while having the hook active. That way I was able to log all the parameters for each ccapi syscall command that the Fury menu used.
After a dozen dumps we got an error saying "An error occurred while starting menu: 70000006" which I believe means we got blacklisted because I ended up buying the same menu with a different account which allowed me to inject the menu again. To this day my first account I used to inject the menus is unable to use fury. While researching CCAPI I wanted to know how ccapi injected and started the mod menu. So the process goes as followed
1) ccapi plugin calls CCAPIEnableSysCall
2) downloads the files from FMT servers
3) CCAPIAllocatePage is called with file size
4) CCAPIWriteProcessMemory is called to write the downloaded file into the allocated page memory address. Each write memory has the size of 0x6000 until the menu is fully written into memory.
5) CCAPICreateProcessThread is called to start the mod menu thread aka payload
Once I knew how ccapi injected the menus I wanted to see if I could do it using my own syscalls. So I created syscall 207 in kernel to invoke the following functions create_process_thread, get_process_module_info, set_process_memory,  get_process_memory, process_page_allocate. After that I was using FMT menus using my own syscalls and that inspired adding these functions into COBRA 8.1. A note about set_process_memory before COBRA 8.1 the method used to write memory was essentially a memcpy (copy_to_process) which meant when writing into protected memory (eg: RX) would result in a crash in the game OS. I went and took a look into old ccapi versions 2.50 then searched for syscall 201 and saw that it used process_write_memory which means the memory address maps lpar then memcpy's causing it to bypass the write protection.
When install CCAPI you get a config file. In 2.60 the file was located in /dev_flash/sys/internal/config.cfg but since 2.70 the path has been changed to /dev_flash2/config.cfg and appears to be encrypted,
Since ccapi 2.80 rev6 and above enstone has completely gotten rid of the custom instruction 0xEF455314. In kernel there have been massive changes, he now deletes the initializing of hooks and function setup including the opd sections of the code which is much harder to reverse engineer. In the CCAPI VSH plugin he did some similar things like applying anti debugging techniques.


Moving forward what can we do reverse engineer the new updates of ccapi example ccapi rev6 and above? I was thinking just like the cobra payload ccapi must have a similar payload located somewhere in the ps3 file system and knowing enstone it will likely be encrypted so if we find we will need to decrypt the payload then having ccapi easier to reverse engineer. To get the payload file  we could hook into cellFsOpen in kernel and see which files are being read and hopefully find the payload.
R3 or the first argument will be the value of the Command ID that will be processed by a kernel in a function implemented by CCAPI.


{| class="wikitable sortable"
{| class="wikitable sortable"
|-
|-
! CCAPI Socket Command ID's !! Description !! Arguments  
! Command ID !! Description !! Arguments  
|-
| 0x2491 || {{cellcolors|#88ff88}} BSD Sockets Connect Function || int32_t connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen)
|-
|-
| 0x10CAA || {{cellcolors|#cc0000}} Unknown || int32_t unk()
| 0x241 || {{cellcolors|#88ff88}} Enable CCAPI Syscalls || int32_t unk()
|-
|-
| 0x314 || {{cellcolors|#88ff88}} Get Temperature (CELL, RSX) || int32_t CCAPIGetTemperature(int32_t* cell, int32_t* rsx)
| 0x785 || {{cellcolors|#88ff88}} CCAPI Read ProcessMemory || int32_t CCAPIReadProcessMemory(int command_id, sys_pid_t pid, void* destination, void* source, size_t size)
|-
|-
| 0x3341 || {{cellcolors|#88ff88}} VSH Notify with Icons || int32_t CCAPIVshNotify(int32_t icon, const char* msg)
| 0x123 || {{cellcolors|#88ff88}} CCAPI Write ProcessMemory || int32_t CCAPIWriteProcessMemory(int command_id,sys_pid_t pid, void* destination, const void* source, size_t size)
|-
|-
| 0x28AC || {{cellcolors|#88ff88}} Ring Buzzer || int32_t RingBuzzer(int32_t mode)
| 0x357 || {{cellcolors|#88ff88}} CCAPI Create Process Thread || int32_t CCAPICreateProcessThread(int command_id,sys_pid_t pid, thread_t* thread, void* entry, uint64_t arg, int prio, size_t stacksize, const char* threadname)
|-
| 0x359 || {{cellcolors|#88ff88}} Set Console Led || int32_t SetConsoleLed(int32_t color, int32_t mode)
|-
| 0x1527 || {{cellcolors|#88ff88}} Get Process List || int32_t CCAPIGetProcessList(uint32_t* npid, uint32_t* pids)
|-
| 0x1BA || {{cellcolors|#88ff88}} Write Process Memory || int32_t CCAPIWriteProcessMemory(int32_t command_id,sys_pid_t pid, void* destination, const void* source, size_t size)
|-
| 0xAF || {{cellcolors|#88ff88}} Set IDPS / PSID || int32_t CCAPISetConsoleIds(int32_t idType (idps = 0, psid = 1), const char* id)
|-
| 0xF2 || {{cellcolors|#88ff88}} Set Boot IDPS / PSID || int32_t CCAPISetBootConsoleIds(int32_t idType (idps = 0, psid = 1), int32_t on (set it to 1), const char* id)<br>To remove a boot idps/psid, on must be 0, id must be NULL
|-
| 0x489A || {{cellcolors|#faff00}} Spoof OFW Mode || int32_t unk()
|-
| 0x32BA || {{cellcolors|#88ff88}} PS3 Power Flags || int32_t CCAPIShutdown(int32_t mode (1 = shutdown, 2 = softreboot, 3 = hardreboot))
|-
| 0x287 || {{cellcolors|#88ff88}} Get Process Name from PID || int32_t CCAPIGetProcessName(u32 pid, char** name)
|-
| 0x265E || {{cellcolors|#cc0000}} Unknown || int32_t unk()
|-
| 0x16A || {{cellcolors|#88ff88}} Read Process Memory || int32_t CCAPIReadProcessMemory(int command_id, sys_pid_t pid, void* destination, void* source, size_t size)
|-
| 0x13461 || {{cellcolors|#cc0000}} Unknown || int32_t unk()
|-
| 0x6001 || {{cellcolors|#cc0000}} Unknown || int32_t unk()
|-
| 0x240F || {{cellcolors|#cc0000}} Unknown || int32_t unk()
|-
| 0x1D88 || {{cellcolors|#faff00}} Disable TMAPI & Syscall 8 || int32_t unk()
|-
| 0x100F || {{cellcolors|#cc0000}} Unknown || int32_t unk()
|-
| 0x6BE || {{cellcolors|#88ff88}} Allocate Process Page || int32_t CCAPIAllocatePage(int command_id,sys_pid_t pid, uint64_t size, uint64_t page_size, uint64_t flags, uint64_t is_executable, uint64_t* kernel_page_adr, uint64_t* game_page_adr)
|-
| 0x1600 || {{cellcolors|#88ff88}} Create Process Thread || int32_t CCAPICreateProcessThread(int command_id,sys_pid_t pid, thread_t* thread, void* entry, uint64_t arg, int prio, size_t stacksize, const char* threadname)
|-
|}
 
These are the Command IDs for CCAPI's Syscall system. Like mentioned above, the Command IDs are passed to Register R3.<br>
Then the parameters are passed from left to right to a higher and higher register. See the code examples above.
 
{| class="wikitable sortable"
|-
! CCAPI Command IDs !! Description !! Arguments
|-
| 0x630 || {{cellcolors|#88ff88}} Get Process List || int32_t CCAPIGetProcessList(uint32_t* npid, uint32_t* pids)
|-
| 0x700 || {{cellcolors|#88ff88}} Get Process Name from PID || int32_t CCAPIGetProcessName(u32 pid, char** name)
|-
| 0x123 || {{cellcolors|#88ff88}} Write Process Memory || int32_t CCAPIWriteProcessMemory(int32_t command_id,sys_pid_t pid, void* destination, const void* source, size_t size)
|-
| 0x785 || {{cellcolors|#88ff88}} Read Process Memory || int32_t CCAPIReadProcessMemory(int command_id, sys_pid_t pid, void* destination, void* source, size_t size)
|-
| 0x977 || {{cellcolors|#88ff88}} Allocate Process Page || int32_t CCAPIAllocatePage(int command_id,sys_pid_t pid, uint64_t size, uint64_t page_size, uint64_t flags, uint64_t is_executable, uint64_t* kernel_page_adr, uint64_t* game_page_adr)
|-
| 0xCCC || {{cellcolors|#88ff88}} Disable TMAPI & Syscall 8 || int32_t DisableCobraTMAPI()
|-
| 0x780 || {{cellcolors|#88ff88}} Set IDPS / PSID || int32_t CCAPISetConsoleIds(int32_t idType (idps = 0, psid = 1), const char* id)
|-
| 0x530 || {{cellcolors|#88ff88}} Spoof OFW Mode || int32_t SpoofOFWMode()
|-
| 0x241 || {{cellcolors|#88ff88}} Enable Syscalls || int32_t EnableSyscalls()
|-
| 0x117 || {{cellcolors|#88ff88}} Console Shutdown || int32_t CCAPIShutdown(int32_t mode (1 = shutdown, 2 = softreboot, 3 = hardreboot))
|-
| 0x450 || {{cellcolors|#cc0000}} Unknown || int32_t unk()
|-
| 0x750 || {{cellcolors|#cc0000}} Unknown || int32_t unk()
|-
| 0x211 || {{cellcolors|#cc0000}} Unknown || int32_t unk()
|-
|-
| 0x977 || {{cellcolors|#88ff88}} CCAPI Allocate Page || int32_t CCAPIAllocatePage(int command_id,sys_pid_t pid, uint64_t size, uint64_t page_size, uint64_t flags, uint64_t is_executable, uint64_t* kernel_page_adr, uint64_t* game_page_adr)
|}
|}


Reference for this info : offset 0x448 ccapi.prx in ccapi 2.80 rev5 this is the function called to use ccsc(command id, ... );
Reference for this info : offset 0x448 ccapi.prx in ccapi 2.80 rev5 this is the function called to use ccsc(command id, ... );
This is how you can create CCAPI syscall functions.<br>
Let's create a few functions first<br>
(Note that RouLette used C++ here)<br>
<code>
typedef struct
{
    void *unk_0; // ptr to some funcs
    uint64_t unk_8;
    uint32_t unk_10;
    uint32_t unk_14;
    void *unk_18;
    void *unk_20; // same as unk_18? :S
    uint64_t unk_28[3];
    void *unk_40; // same as unk_0?
                  // ...
} thread_t;
template <typename... Args>
static CcxCall sys_ccapi_call(uint64_t num, Args... arg)
{
    __asm__
    (
        "sc;" // ccsc = EF455314
        "blr;"
    );
}
uint8_t* CCAPIStringToArray(const char* s, uint8_t* id)
{
    uint32_t len = strlen(s);
    if (!len)
    {
        return id;
    }
    int j = 0;
    uint32_t i;
    for (i = 0; i < (len + 1); i += 2)
    {
        char b[3] = { 0,0,0 };
        strncpy(b, &s[i], 2);
        b[1] = b[1] ? b[1] : '0';
        id[j++] = strtoul(b, NULL, 16);
    }
    return id;
}
enum ConsoleIdType
{
    Idps = 0,
    Psid = 1,
};
struct ConsoleId
{
    uint8_t value[16];
};
struct ProcessName
{
    char value[512];
};
typedef enum ConsoleIdType ConsoleIdType;
typedef struct ConsoleId ConsoleId;
int CCAPIEnableSysCall()
{
    return sys_ccapi_call(0x241);
}
int CCAPIGetProcessList(uint32_t* npid, uint32_t* pids)
{
    return sys_ccapi_call(0x630, npid, pids);
}
int CCAPIGetProcessName(uint32_t pid, ProcessName* name)
{
    return sys_ccapi_call(0x700, pid, name);
}
int CCAPISetMemory(sys_pid_t pid, void* destination, size_t size, const void* source)
{
    return sys_ccapi_call(0x123, pid, destination, size, source);
}
int CCAPIGetMemory(sys_pid_t pid, void* destination, size_t size, void* source)
{
    return sys_ccapi_call(0x785, pid, destination, size, source);
}
int CCAPIAllocatePage(sys_pid_t pid, uint64_t size, uint64_t page_size, uint64_t flags, uint64_t is_executable, uint64_t* kernel_page_adr, uint64_t* game_page_adr)
{
    return sys_ccapi_call(0x977, pid, size, page_size, flags, is_executable, kernel_page_adr, game_page_adr);
}
int CCAPICreateProcessThread(uint32_t num, sys_pid_t pid, thread_t* thread, void* entry, uint64_t arg, int prio, size_t stacksize, const char* threadname)
{
    return sys_ccapi_call(0x357, pid, thread, entry, arg, prio, stacksize, threadname);
}
int CCAPIDisableTmapiAndSyscall8()
{
    return sys_ccapi_call(0xCCC);
}
int CCAPIConsoleShutDown(uint32_t shutdown_type)
{
    return sys_ccapi_call(0x117, shutdown_type);
}
int CCAPISpoofOFW()
{
    return sys_ccapi_call(0x530);
}
int CCAPISetConsoleIds(ConsoleIdType type, ConsoleId* id)
{
    return sys_ccapi_call(0x780, type, id);
}
int CCAPISetConsoleIdsString(ConsoleIdType idType, const char* id)
{
    ConsoleId cid;
    CCAPIStringToArray(id, cid.value);
    return CCAPISetConsoleIds(idType, &cid);
}
</code>
As you can see, sys_ccapi_call function makes use of the syscall instruction.<br>
Do not call these functions yet, we need to patch them first.<br>
<code>
static uint32_t FindSyscallInstruction(uint32_t address)
{
    while (true)
    {
        if (!*(uint32_t *)address)
        {
            address = (address - 0x4); // 0x4 for each instruction
            break;
        }
        else if (*(uint32_t *)(address) == 0x44000002) // 44000002 sc
        {
            break;
        }
        else
        {
            address = (address + 0x4); // 0x4 for each instruction
        }
    }
    return address;
}
void ReplaceSyscallInstruction(uint32_t func) // (dex only) dex can only write to sprx functions
{
    // call this function before calling sys_ccapi_call
    opd_s *opd = (opd_s *)(func);
    uint32_t ccsc_syscall_instruction = 0xEF455314; // ccsc = EF455314
    uint32_t addr = FindSyscallInstruction(opd->sub);
    sys_dbg_write_process_memory(addr, &ccsc_syscall_instruction, 0x4);
}
ReplaceSyscallInstruction((uint32_t)sys_ccapi_call<>); // enable ccapi syscall
ReplaceSyscallInstruction((uint32_t)sys_ccapi_call<sys_pid_t, void *, size_t, const void *>); // write process params
ReplaceSyscallInstruction((uint32_t)sys_ccapi_call<sys_pid_t, void *, size_t, void *>); // read process params
ReplaceSyscallInstruction((uint32_t)sys_ccapi_call<sys_pid_t, thread_t*, void *, uint64_t, int, size_t, const char *>); // create thread params
ReplaceSyscallInstruction((uint32_t)sys_ccapi_call<sys_pid_t, uint64_t, uint64_t, uint64_t, uint64_t, uint32_t*, uint32_t*>); // allocate
</code>
== How does the custom CPU instruction work? ==
This is a writeup by PHTNC, I can't say if this is the exact way on how it works, I assume this is how it works.<br>
Some people might be wondering, how did Enstone implement a custom CPU instruction that the CPU actually understands?<br>
While the PS3 OS is closed source and seems to look like to be a completely different OS than what we know today (Linux, BSD, macOS, Windows)<br>
CellOS (PS3 OS Name) is actually a fork of Free- and NetBSD. So it contains stuff that are also present in different Unix OS'.<br>
You have to imagine it like that, the OS and CPU communicate with each other and tell each other on what's valid and what's invalid running on the CPU.<br>
This also goes for instructions. That means, when for example the CPU reads an (for the CPU) invalid instruction in kernel or process space, it tells the OS that it has no clue what this is,<br>
it proceeds to "ask" the OS if the OS knows what the instruction means. In normal cases, the OS doesn't know either what the read instruction means.<br>
At this point, it depends on what OS you're on. In regular Unix systems like Linux or BSD, a system exception called SIGILL (SIGnal ILLegal instruction) is raised.<br>
The OS sends a SIGILL Signal to the affected process, this causes the process to terminate itself immediately, signals like that cannot be caught by the process.<br>
CCAPI probably hooks a function in the area, where the SIGILL signal is handled and checks if the invalid instruction is 0xEF455314.<br>
If the hooked function sees, that the CPU hit 0xEF455314, then it proceeds to read a value from register R3 and makes a switch on that value.<br>
Then it starts executing a CCAPI function and proceeds with the normal code execution once CCAPI finished executing its own functions.<br>
If the hooked function sees another invalid instruction other than 0xEF455314, it will send a SIGILL signal to the affected process.


{{Reverse engineering}}<noinclude>[[Category:Main]]</noinclude>
{{Reverse engineering}}<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)