CCAPI: Difference between revisions
m (Reverted edits by 140.186.30.117 (talk) to last revision by 46.205.144.63) Tag: Rollback |
|||
(17 intermediate revisions by 4 users not shown) | |||
Line 246: | Line 246: | ||
= CCAPI 2.70 - 2.80 rev5 = | = CCAPI 2.70 - 2.80 rev5 = | ||
== 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: | Port:6333<br> | ||
Communication: HTTP | Communication: HTTP<br> | ||
<br> | |||
Port:1979 PS3 | Port:1979 PS3 <br> | ||
Communication: TCP/UDP | Communication: TCP/UDP<br><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>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 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 Buzzer(int snd = 1)<br>{<br>char _notify_buffer[512];<br>snprintf(_notify_buffer, 512, "/ccapi/ringbuzzer?type=%i",snd);<br>send_CCAPI_request(_notify_buffer);<br>}<br></code><br> | |||
Source : CCAPI && Webman Http requests for sprx - Jo-Milk | Source : CCAPI && Webman Http requests for sprx - Jo-Milk | ||
https//pastebin.com/RqnvPZ0j | https//pastebin.com/RqnvPZ0j | ||
== Use CCAPI Syscall from | The codes below are not verified to work, these are just example codes for you, | ||
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 281: | Line 285: | ||
} </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. | |||
{| class="wikitable sortable" | {| class="wikitable sortable" | ||
|- | |- | ||
! Command ID !! Description !! Arguments | ! CCAPI Socket Command ID's !! 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() | |||
|- | |||
| 0x314 || {{cellcolors|#88ff88}} Get Temperature (CELL, RSX) || int32_t CCAPIGetTemperature(int32_t* cell, int32_t* rsx) | |||
|- | |||
| 0x3341 || {{cellcolors|#88ff88}} VSH Notify with Icons || int32_t CCAPIVshNotify(int32_t icon, const char* msg) | |||
|- | |||
| 0x28AC || {{cellcolors|#88ff88}} Ring Buzzer || int32_t RingBuzzer(int32_t mode) | |||
|- | |||
| 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() | |||
|- | |- | ||
|} | |} | ||
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> |
Latest revision as of 11:20, 26 December 2022
Intro[edit | edit source]
ControlConsoleAPI is an API for PS3 and PC similar to TMAPI on DEX console, But this one "CCAPI" works for CEX and DEX.
External Sources:
http://frenchmoddingteam.com/shop/application/20-control-console-api-2-60-rev2-rte-cex-dex (Official) http://consolecrunch.com/threads/control-console-api-ccapi-v2-60-released.9055/ http://www.nextgenupdate.com/forums/ps3-cheats-customization/693857-update-controlconsoleapi-2-50-ccapi-37.html http://www.nextgenupdate.com/forums/ps3-cheats-customization/701574-tutorial-how-rtm-ccapi-cex-dex.html http://psx-scene.com/forums/content/controllconsoleapi-v2-50-adds-4-53-4-55-cfw-support-4350/
- Features:
- Compatible CEX/DEX/SEX/(TOOL ?)
- Debug non-fself & fself in real time (vsh.self for example can be debugged in rte, or any game with non debug eboot)
- Debug kernel in real time(lv2_kernel.self and lv1.self)
- Classic functions (RTE on CEX + DEX):
- getProcessMemory
- setProcessMemory
- shutdown/reboot
- work with Wireless connection
- bypass exec pages writing restriction
- bypass lv2 memory protection
- getProcessMemory
- new functionalities like:
- peek/poke lv1 and lv2
- setConsoleID at anytime/anywhere
- setConsoleLed
- ringBuzzer
- getTemperature
- VSH module loading
- notify
- peek/poke lv1 and lv2
- Supported Firmware:
- CFW 4.21,4.30,4.40,4.41,4.46,4.50, 4.53, 4.55, 4.60, 4.65 CEX or DEX (+ 4.66CEX)
- How to install CCAPI:
Just download and run this pkg on your ps3.
It will tell you to reboot, and it's done. You only need to do this once.
- How to uninstall CCAPI
Just run again the pkg.
- Is it risky
In the worst case, you could need to reinstall your firmware.
VSH module loading:
create a plugins directory and put all your sprx plugins into it.
/dev_usb000/plugins
/dev_usb000/plugins/prx_name1.sprx
/dev_usb000/plugins/prx_name2.sprx
All of the sprx that are present in this folder, will be loaded at ps3 boot.
Important:
If some games refuse to work, just don't use plugins, delete plugins folder from /dev_usb000
Changelog
2.80
(rev7) Added 4.86 CEX support
(rev6) Added 4.85 CEX support
(rev5) Added 4.84 DEX support
(rev5) Added 4.84 CEX support
(rev4) Added 4.83 CEX support
(rev3) Added 4.82 DEX support
(rev2) Reduced memory use
Added ps3 TOOL support (with DEX/CEX kernel only)
Added console finder into consolemanager
Added vsh menu
Added 4.82 CEX support
2.70
(rev6) Added 4.81 DEX support
(rev5) Added 4.81 CEX support
(rev4) Added 4.80 DEX support
(rev4) Added 4.80 CEX support
(rev3) Added 4.78 DEX support
(rev2) Added 4.78 CEX support
(rev2) Added 4.76 DEX support
Added other platform support
Optimized network bitrate
2.60
(rev7) Added 4.76 CEX support
(rev6) Added 4.75 DEX support
(rev5) Added 4.75 CEX support
(rev4) Added 4.70 DEX support
(rev3) Added 4.70 CEX support
(rev2) Added 4.66 CEX support
Added 4.60/4.65 support
Added Cobra support
Added SetBootPsid/SetBootIdps/SetPsid
2.50
Added 4.53/4.55 support
Added a console list
2.00
Reduced memory use
DLL (C++ / C#) released to build programs for ccapi
1.00
Original version
- 2.50
- http://www.mediafire.com/download/1xjkdzl77gz4meq/CcApi_package_2.50.rar
- 2.60
- http://www.mediafire.com/download/fz4leo4iov9ut4z/CcApi_package_2.60_Rev2.rar
CCAPI 2.00-2.50[edit | edit source]
You may find information in PrimeTime00 alt source about older encryption used on the tcp packets sent to ccapi v2.00-2.50
CCAPI PrimeTime00 alt source
https//pastebin.com/x12kqNaQ
https//pastebin.com/G9hx9pch
CCAPI 2.60[edit | edit source]
Installation[edit | edit source]
When installing, CCAPI (the ps3 application) creates a config file (/dev_flash/sys/internal/config.cfg) in which some data, depending on the console, is stored. This file has a size of 240 (0xF0) bytes.
- Example from fw 4.46 dex:
Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00000000 04 04 60 00 D4 6F F4 09 80 00 00 00 00 36 6B D0 ..`.Ôoô.€....6kÐ 00000010 80 00 00 00 00 07 22 5C 80 00 00 00 00 08 F9 98 €....."\€.....ù˜ 00000020 80 00 00 00 00 06 68 90 80 00 00 00 00 01 00 C0 €.....h.€......À 00000030 80 00 00 00 00 29 E7 5C 80 00 00 00 00 06 6C CC €....)ç\€.....lÌ 00000040 80 00 00 00 00 01 1F C0 80 00 00 00 00 29 E8 E8 €......À€....)èè 00000050 80 00 00 00 00 00 FE A4 E9 22 AA 78 00 01 C3 38 €.....þ¤é"ªx..Ã8 00000060 80 00 00 00 00 01 1A BC 80 00 00 00 00 01 1B 34 €......¼€......4 00000070 80 00 00 00 00 08 F9 D4 80 00 00 00 00 37 CF E8 €.....ùÔ€....7Ïè 00000080 80 00 00 00 00 3F A8 B0 80 00 00 00 00 49 6F 3C €....?¨°€....Io< 00000090 80 00 00 00 00 4C 99 8C 00 00 00 00 00 00 00 00 €....L™Œ........ 000000A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000B0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000000D0 00 00 00 00 00 00 00 03 00 00 00 00 00 61 D5 E8 .............aÕè 000000E0 00 00 00 00 00 61 DB F4 00 00 00 00 00 04 41 64 .....aÛô......Ad
Usage | Offset | Size | Value | Notes | Used in |
---|---|---|---|---|---|
- | 0x00 | 0x04 | 04 04 60 00 | Firmware: 4.46 | - |
- | 0x04 | 0x04 | D4 6F F4 09 | Console type ? (DEX/CEX) | - |
- | 0x08 | 0x08 | 80 00 00 00 00 36 6B D0 | Address of the lv2 toc (stored at 0x8000000000003000 in lv2) | - |
- | 0x10 | 0x08 | 80 00 00 00 00 07 22 5C | A subroutine inside lv2(extend_kstack(), fixed branch to enable syscall 200/201 read and write) | ccapi.sprx |
- | 0x18 | 0x08 | 80 00 00 00 00 08 F9 98 | A subroutine inside lv2(fixed branch to enable syscall 200/201 read and write) | ccapi.sprx |
- | 0x20 | 0x08 | 80 00 00 00 00 06 68 90 | A subroutine inside lv2(alloc(), fixed branch to enable syscall 200/201 read and write) | ccapi.sprx |
- | 0x28 | 0x08 | 80 00 00 00 00 01 00 C0 | A subroutine inside lv2(copy_from_user(), fixed branch to enable syscall 200/201 read and write) | ccapi.sprx |
- | 0x30 | 0x08 | 80 00 00 00 00 29 E7 5C | A subroutine inside lv2(fixed branch to enable syscall 200/201 read and write) | ccapi.sprx |
- | 0x38 | 0x08 | 80 00 00 00 00 06 6C CC | A subroutine inside lv2(dealloc(), fixed branch to enable syscall 200/201 read and write) | ccapi.sprx |
- | 0x40 | 0x08 | 80 00 00 00 00 01 1F C0 | A subroutine inside lv2(fixed branch to enable syscall 200/201 read and write) | ccapi.sprx |
- | 0x48 | 0x08 | 80 00 00 00 00 29 E8 E8 | A subroutine inside lv2(fixed branch to enable syscall 200/201 read and write) | ccapi.sprx |
- | 0x50 | 0x08 | 80 00 00 00 00 00 FE A4 | A subroutine inside lv2(copy_to_user(), fixed branch to enable syscall 200/201 read and write) | ccapi.sprx |
- | 0x58 | 0x04 | E9 22 AA 78 | - | - |
- | 0x5C | 0x04 | 00 01 C3 38 | - | - |
- | 0x60 | 0x08 | 80 00 00 00 00 01 1A BC | A subroutine inside lv2 | - |
- | 0x68 | 0x08 | 80 00 00 00 00 01 1B 34 | A subroutine inside lv2 | - |
Allow sys_dbg syscalls | 0x70 | 0x08 | 80 00 00 00 00 08 F9 D4 | Ccapi edits the branch at that address to modify the code flow | ccapi.sprx |
Get SysTable from pc dll | 0x78 | 0x08 | 80 00 00 00 00 37 CF E8 | Address of the syscall table | ccapi.sprx |
Set ConsoleID | 0x80 | 0x08 | 80 00 00 00 00 3F A8 B0 | Address of one of the console IDs in lv2 | ccapi.sprx |
Set ConsoleID | 0x88 | 0x08 | 80 00 00 00 00 49 6F 3C | Address of one of the console IDs in lv2 | ccapi.sprx |
Set ConsoleID | 0x90 | 0x08 | 80 00 00 00 00 4C 99 8C | Address of one of the console IDs in lv2 | ccapi.sprx |
Set ConsoleID | 0x98 | 0x08 | 00 00 00 00 00 00 00 00 | Address of one of the console IDs in lv2 | ccapi.sprx |
Set ConsoleID | 0xA0 | 0x08 | 00 00 00 00 00 00 00 00 | Address of one of the console IDs in lv2 | ccapi.sprx |
Set ConsoleID | 0xA8 | 0x08 | 00 00 00 00 00 00 00 00 | Address of one of the console IDs in lv2 | ccapi.sprx |
Set ConsoleID | 0xB0 | 0x08 | 00 00 00 00 00 00 00 00 | Address of one of the console IDs in lv2 | ccapi.sprx |
Set ConsoleID | 0xB8 | 0x08 | 00 00 00 00 00 00 00 00 | Address of one of the console IDs in lv2 | ccapi.sprx |
Set ConsoleID | 0xC0 | 0x08 | 00 00 00 00 00 00 00 00 | Address of one of the console IDs in lv2 | ccapi.sprx |
Set ConsoleID | 0xC8 | 0x08 | 00 00 00 00 00 00 00 00 | Address of one of the console IDs in lv2 | ccapi.sprx |
Set ConsoleID | 0xD0 | 0x08 | 00 00 00 00 00 00 00 03 | Console ID count | ccapi.sprx |
- | 0xD8 | 0x08 | 00 00 00 00 00 61 D5 E8 | Address of sys_prx_load_module in vsh | - |
- | 0xE0 | 0x08 | 00 00 00 00 00 61 DB F4 | Address of sys_prx_start_module in vsh | - |
- | 0xE8 | 0x08 | 00 00 00 00 00 04 41 64 | - | - |
Commands[edit | edit source]
When calling a ccapi function, a packet containing a command id is sent to the ps3. The ps3 then analyzes the packet and makes a switch on the command id.
Command ID | Action | Prototype(s) |
---|---|---|
1 | SetConsoleID | int32_t SetConsoleID(uint8_t *cid) |
2 | ReadProcessMemory | int32_t ReadProcessMemory(sys_pid_t pid, uint64_t address, void *data, size_t size) |
3 | WriteProcessMemory | int32_t WriteProcessMemory(sys_pid_t pid, uint64_t address, const void *data, size_t size) |
4 | Unknown | |
5 | GetProcessInfo | int32_t GetProcessInfo(sys_pid_t pid, sys_process_info_t *info) |
6 | GetTemperature | int32_t GetTemperature(int32_t type, uint32_t *temperature) |
7 | ControlLed | int32_t ControlLed(int32_t ledColor, int32_t ledAction) |
8 | GetLv2Memory | int32_t GetLv2Memory(uint64_t address, size_t num, uint8_t *buffer) |
9 | SetLv2Memory | int32_t SetLv2Memory(uint64_t address, int32_t size, const uint8_t *data) |
10 | GetLv1Memory | int32_t GetLv1Memory(uint64_t address, size_t size, uint8_t *buffer) |
11 | SetLv1Memory | int32_t SetLv1Memory(uint64_t address, size_t size, const uint8_t *data) |
12 | GetFirmwareInfo | int32_t GetFirmware(); int32_t GetCcapiVersion(); int32_t GetConsoleType(uint64_t *type) |
13 | RingBuzzer | int32_t RingBuzzer(int32_t mode) |
14 | Unknown | |
15 | Shutdown | int32_t Shutdown(int32_t mode) |
16 | Notify | int32_t Notify(int32_t texture, const wchar_t *text) |
CCAPI 2.70 - 2.80 rev5[edit | edit source]
Location of ccapi.sprx[edit | edit source]
ccapi.sprx is renamed to sys_audio.sprx and is located to /dev_flash/sys/internal/sys_audio.sprx
Packets and http Requests[edit | edit source]
Port:6333
Communication: HTTP
Port:1979 PS3
Communication: TCP/UDP
static int connect_to_CAPPI(void)
{
struct sockaddr_in sin;
int s;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0x7F000001; //127.0.0.1 (localhost)
sin.sin_port = htons(6333);
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
{
return -1;
}
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
return -1;
}
return s;
}
void DoNotify(char *szFormat,int id = 0)
{
char _notify_buffer[512];
snprintf(_notify_buffer, 512, "/ccapi/notify?id=%i&msg=%s",id,szFormat);
send_CCAPI_request(_notify_buffer);
}
void ShutDownPS3()
{
char _notify_buffer[512];
snprintf(_notify_buffer, 512, "/ccapi/shutdown?mode=1");
send_CCAPI_request(_notify_buffer);
}
void RestartPS3()
{
char _notify_buffer[512];
snprintf(_notify_buffer, 512, "/ccapi/shutdown?mode=2");
send_CCAPI_request(_notify_buffer);
}
void Buzzer(int snd = 1)
{
char _notify_buffer[512];
snprintf(_notify_buffer, 512, "/ccapi/ringbuzzer?type=%i",snd);
send_CCAPI_request(_notify_buffer);
}
Source : CCAPI && Webman Http requests for sprx - Jo-Milk
https//pastebin.com/RqnvPZ0j
The codes below are not verified to work, these are just example codes for you, so you can get a theoretical understanding, on how you could call a CCAPI Syscall.
Use CCAPI Syscall from SPRX (Jo-Milks Approach)[edit | edit source]
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".
"ccsc" = 0xEF455314
#define CcxCall uint64_t __attribute__((naked))
static CcxCall CCAPIWriteProcessMemory(int command_id,sys_pid_t pid, void* destination, const void* source, size_t size)
{
__asm__(
/*"li %r3, 0x123;" R3 is your command_id write */
"sc;" /*You must replace this 4 byte instruction by 0xEF455314*/
"blr;"
);
}
Use CCAPI Syscall from SPRX (PHTNC Approach)[edit | edit source]
#define CCAPICall uint64_t __attribute__((naked))
static CCAPICall WriteProcessMemory(uint32_t pid, void* address, size_t size, const void* buffer)
{
register uint64_t p1 __asm__ ("3") = 0x123;
register uint64_t p2 __asm__ ("4") = (uint64_t)pid;
register uint64_t p3 __asm__ ("5") = address;
register uint64_t p4 __asm__ ("6") = (uint64_t)buffer;
register uint64_t p5 __asm__ ("7") = (uint64_t)size;
register uint64_t p6 __asm__ ("8");
register uint64_t p7 __asm__ ("9");
register uint64_t p8 __asm__ ("10");
register uint64_t p9 __asm__ ("11");
__asm__ volatile (0xEF455314
: "=r" (p1), "=r" (p2), "=r" (p3), "=r" (p4),
"=r" (p5), "=r" (p6), "=r" (p7), "=r" (p8), "=r" (n)
: "r" (p1), "r" (p2), "r" (p3), "r" (p4),
"r" (p5), "r" (n)
: "0", "12", "lr", "ctr", "xer", "cr0", "cr1", "cr5", "cr6", "cr7", "memory");
return (int)p1;
}
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.
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
own environment. CCAPI makes a lot of use of regular LV2 Syscalls like RingBuzzer, Shutdown, or ControlLED. It only makes use of its own
environment, when these LV2 functions are not available for all environments. (WriteProcessMemory, ReadProcessMemory in CEX for example.)
We cannot say how exactly CCAPI initializes itself and its environment, however, I (PHTNC) believe, that CCAPI probably takes a similar approach like Mamba.
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.
Cobra loads itself from lv2kernel.self.
TheRouLetteBois Writeup[edit | edit source]
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.
CCAPI Socket Command ID's | Description | Arguments |
---|---|---|
0x2491 | BSD Sockets Connect Function | int32_t connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen) |
0x10CAA | Unknown | int32_t unk() |
0x314 | Get Temperature (CELL, RSX) | int32_t CCAPIGetTemperature(int32_t* cell, int32_t* rsx) |
0x3341 | VSH Notify with Icons | int32_t CCAPIVshNotify(int32_t icon, const char* msg) |
0x28AC | Ring Buzzer | int32_t RingBuzzer(int32_t mode) |
0x359 | Set Console Led | int32_t SetConsoleLed(int32_t color, int32_t mode) |
0x1527 | Get Process List | int32_t CCAPIGetProcessList(uint32_t* npid, uint32_t* pids) |
0x1BA | Write Process Memory | int32_t CCAPIWriteProcessMemory(int32_t command_id,sys_pid_t pid, void* destination, const void* source, size_t size) |
0xAF | Set IDPS / PSID | int32_t CCAPISetConsoleIds(int32_t idType (idps = 0, psid = 1), const char* id) |
0xF2 | Set Boot IDPS / PSID | int32_t CCAPISetBootConsoleIds(int32_t idType (idps = 0, psid = 1), int32_t on (set it to 1), const char* id) To remove a boot idps/psid, on must be 0, id must be NULL |
0x489A | Spoof OFW Mode | int32_t unk() |
0x32BA | PS3 Power Flags | int32_t CCAPIShutdown(int32_t mode (1 = shutdown, 2 = softreboot, 3 = hardreboot)) |
0x287 | Get Process Name from PID | int32_t CCAPIGetProcessName(u32 pid, char** name) |
0x265E | Unknown | int32_t unk() |
0x16A | Read Process Memory | int32_t CCAPIReadProcessMemory(int command_id, sys_pid_t pid, void* destination, void* source, size_t size) |
0x13461 | Unknown | int32_t unk() |
0x6001 | Unknown | int32_t unk() |
0x240F | Unknown | int32_t unk() |
0x1D88 | Disable TMAPI & Syscall 8 | int32_t unk() |
0x100F | Unknown | int32_t unk() |
0x6BE | 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 | 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.
Then the parameters are passed from left to right to a higher and higher register. See the code examples above.
CCAPI Command IDs | Description | Arguments |
---|---|---|
0x630 | Get Process List | int32_t CCAPIGetProcessList(uint32_t* npid, uint32_t* pids) |
0x700 | Get Process Name from PID | int32_t CCAPIGetProcessName(u32 pid, char** name) |
0x123 | Write Process Memory | int32_t CCAPIWriteProcessMemory(int32_t command_id,sys_pid_t pid, void* destination, const void* source, size_t size) |
0x785 | Read Process Memory | int32_t CCAPIReadProcessMemory(int command_id, sys_pid_t pid, void* destination, void* source, size_t size) |
0x977 | 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 | Disable TMAPI & Syscall 8 | int32_t DisableCobraTMAPI() |
0x780 | Set IDPS / PSID | int32_t CCAPISetConsoleIds(int32_t idType (idps = 0, psid = 1), const char* id) |
0x530 | Spoof OFW Mode | int32_t SpoofOFWMode() |
0x241 | Enable Syscalls | int32_t EnableSyscalls() |
0x117 | Console Shutdown | int32_t CCAPIShutdown(int32_t mode (1 = shutdown, 2 = softreboot, 3 = hardreboot)) |
0x450 | Unknown | int32_t unk() |
0x750 | Unknown | int32_t unk() |
0x211 | Unknown | int32_t unk() |
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.
Let's create a few functions first
(Note that RouLette used C++ here)
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);
}
As you can see, sys_ccapi_call function makes use of the syscall instruction.
Do not call these functions yet, we need to patch them first.
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
How does the custom CPU instruction work?[edit | edit source]
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.
Some people might be wondering, how did Enstone implement a custom CPU instruction that the CPU actually understands?
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)
CellOS (PS3 OS Name) is actually a fork of Free- and NetBSD. So it contains stuff that are also present in different Unix OS'.
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.
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,
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.
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.
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.
CCAPI probably hooks a function in the area, where the SIGILL signal is handled and checks if the invalid instruction is 0xEF455314.
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.
Then it starts executing a CCAPI function and proceeds with the normal code execution once CCAPI finished executing its own functions.
If the hooked function sees another invalid instruction other than 0xEF455314, it will send a SIGILL signal to the affected process.