CCAPI: Difference between revisions

From PS3 Developer wiki
Jump to navigation Jump to search
No edit summary
(One intermediate revision by the same user not shown)
Line 319: Line 319:
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>
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>
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"
|-
! 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"
{| class="wikitable sortable"
|-
|-
! Command ID !! Description !! Arguments  
! 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()
|-
|-
| 0x241 || {{cellcolors|#88ff88}} Enable CCAPI Syscalls || int32_t unk()
| 0x117 || {{cellcolors|#88ff88}} Console Shutdown || int32_t CCAPIShutdown(int32_t mode (1 = shutdown, 2 = softreboot, 3 = hardreboot))
|-
|-
| 0x785 || {{cellcolors|#88ff88}} CCAPI Read ProcessMemory || int32_t CCAPIReadProcessMemory(int command_id, sys_pid_t pid, void* destination, void* source, size_t size)
| 0x450 || {{cellcolors|#cc0000}} Unknown || int32_t unk()
|-
|-
| 0x123 || {{cellcolors|#88ff88}} CCAPI Write ProcessMemory || int32_t CCAPIWriteProcessMemory(int command_id,sys_pid_t pid, void* destination, const void* source, size_t size)
| 0x750 || {{cellcolors|#cc0000}} Unknown || int32_t unk()
|-
|-
| 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)
| 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>

Revision as of 14:45, 18 October 2021

Intro

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
  • new functionalities like:
    • peek/poke lv1 and lv2
    • setConsoleID at anytime/anywhere
    • setConsoleLed
    • ringBuzzer
    • getTemperature
    • VSH module loading
    • notify
  • 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

CCAPI 2.00-2.50

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

Installation

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

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

Location of ccapi.sprx

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


Packets and http Requests


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)

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)

#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

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?

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.