bucanero / apollo-psp

Apollo Save Tool (PSP)
http://www.bucanero.com.ar/
GNU General Public License v3.0
50 stars 1 forks source link

wire up pspchnnlsv for param.sfo mode 6 hashing #23

Closed Kethen closed 7 months ago

Kethen commented 10 months ago

Mode 6 savedata hash uses kirk cmd 5 at the end, which uses a per console fuse key

Switching to pspsdk pspchnnlsv in theory allows the mode 6 hash to be done correctly on-device during decrypted save importing, binding the imported save to the device in the process

I don't have a psp on hand at the moment however so it was only verified on https://github.com/jpcsp/jpcsp/pull/512, while using utility.prx from firmware 661

On the patched jpcsp, I was able to import a Gran Turismo EU save from ppsspp, which showed up as "from another device" and refused to load (bind flagged as bad by real utility.prx sceUtilitySaveData*), by exporting decrypted then importing decrypted

Would be interesting to see if that works on a real PSP

bucanero commented 10 months ago

thanks for the PR 👍 I'll test on my PSP (slim 2000) and see how it goes, I'll share my feedback once I test on real hardware.

Btw, do you know other PSP games that use Mode-6 so I can test more titles and saves?

Kethen commented 10 months ago

@bucanero hmm I have no idea, GT is the only game I really looked into

Games after fw 2.7.1 would generate a mode 6 hash going by ppsspp and jpcsp's reversing and implementation

https://github.com/hrydgard/ppsspp/blob/1dea3f80eb157eee5060f72f5e5ceac5597267b3/Core/Dialog/SavedataParam.cpp#L1017 , https://github.com/jpcsp/jpcsp/blob/7b43500e27de742e0d6047817d0cf1c77194cb4d/src/jpcsp/crypto/SAVEDATA.java#L765

From the looks of it, real implementation of sceUtilitySaveData* would verify the mode 6 hash at SAVEDATA_PARAMS+0x20 , then populate the bind value at:

https://github.com/jpcsp/jpcsp/blob/7b43500e27de742e0d6047817d0cf1c77194cb4d/src/jpcsp/HLE/kernel/types/SceUtilitySavedataParam.java#L117C16-L117C16 , aka https://pspdev.github.io/pspsdk/structSceUtilitySavedataParam.html#a055ba428b979871426dd80df0d98ce1e , aka https://github.com/hrydgard/ppsspp/blob/1dea3f80eb157eee5060f72f5e5ceac5597267b3/Core/Dialog/SavedataParam.h#L223

Then it's up to the game as to use the bind value or not, at least that's what I found when I was looking into https://github.com/hrydgard/ppsspp/issues/15747

bucanero commented 10 months ago

hi @Kethen , thanks for all the details 🙏 I had no idea that this SaveData struct had this bind parameter.

I was working on Apollo PS3 lately but I'll get back to this as soon as possible

bucanero commented 10 months ago

hi @Kethen , I took some time to test on real hardware today (PSP-2000, CFW 6.60 ME), and after building with your changes the app can't start, it fails with error 0x8002013C. I did a quick search and this error is related to trying to load a kernel module from a user application. I think this makes sense, as Apollo is a regular homebrew app with no kernel permissions, but pspChnnlsv seems to be a privileged module so it fails to start.

I assume that emulators just don't care and don't check this kind of security permissions and execute the code anyways. If you could take a look, I'm pretty sure there should be workarounds to access kernel modules within a PSP homebrew app. Let me know if you can find a fix or workaround. Also if you prefer we can discuss it on the PSPdev discord server so other devs can provide ideas.

btw, I found one sample on the PSP SDK that uses pspchnnlsv but the Readme says that it only works on VSH 🤔 https://github.com/pspdev/pspsdk/tree/master/src/samples/savedata/encrypt

Kethen commented 10 months ago

hmm.... if pspchnnlsv cannot be used in game mode on a real psp, an alternative for creating psp saves with correct device specific hash would be to go through utility.prx itself

https://pspdev.github.io/pspsdk/psputility__savedata_8h.html

I can go bring that up in discord I suppose

bucanero commented 9 months ago

Moving this to draft until there's a solution to load chnnlsv methods on real hardware.

bucanero commented 8 months ago

@Kethen , recently the KIRK engine was reversed even further, and the rest of the CMDs were cracked, even the fuse-related ones. I'm thinking that it could be possible to add the missing CMD5 to Apollo, then just read the 2 fuse values from memory and generate a "mode 6 hash" that should match the original one. What do you think?

Kirk engine: https://www.psdevwiki.com/psp/Kirk#PSP_Individual_Keys Latest implementation of CMD5: https://github.com/ProximaV/kirk-engine-full/blob/master/libkirk/kirk_engine.c#L488

Kethen commented 8 months ago

@Kethen , recently the KIRK engine was reversed even further, and the rest of the CMDs were cracked, even the fuse-related ones. I'm thinking that it could be possible to add the missing CMD5 to Apollo, then just read the 2 fuse values from memory and in theory the generated "mode 6 hash" should be matching the original one. What do you think?

Kirk engine: https://www.psdevwiki.com/psp/Kirk#PSP_Individual_Keys Latest implementation of CMD5: https://github.com/ProximaV/kirk-engine-full/blob/master/libkirk/kirk_engine.c#L488

While the algorithm kirk engine uses is found, which is awesome, I don't think it allows fetching the fuse IDs nor the derived keys from software

If I understand correctly, when kirk carries out encrypt/decrypt operations, it never copies the seeds nor the derived key to memory, and instead would only DMA to read plaintext/ciphertext and write results, with the only exceptions being command 6 and 9, which also read keys from system memory

To really fetch the fuse IDs in software, it'll likely need some kind of hardware flaw/glitch, as those fuse IDs should be wired to kirk only

I could be wrong of course, I'm not an expert in PSP hardware

bucanero commented 8 months ago

I was asking around a bit about the Fuse IDs on Discord, and it seems that fuse values can be recovered by software on the PSP. Here's the notes I have:

  // Get device Fuse ID
  u32 psp_fuse90 = *(u32 *)0xBC100090;
  u32 psp_fuse94 = *(u32 *)0xBC100094;

artart78:

it needs to run in kernel mode, you can't access an address whose MSB is 1 (ie addresses bigger than 0x80000000) in user mode you can either use kubridge from CFW, or use a kxploit

Joel16:

You can use sceSysreg_driver_4F46EEDE() to get the fuseID, but like artart said, you can export it from a kernel plugin and use it in your usermode app. You can also use kubridge's kuKernelCall to call the function from usermode.

Proxima:

if its on a PSP Go, you could probably read it from regular homebrew without a kxploit by using the direct phsyical address 0x1C100090 and 0x1C100094

bucanero commented 8 months ago

🎉 I finally solved it, using the new KIRK implementation for CMD5, and a kernel exploit to recover the per-console FuseID value.

Took this kernel-read code from libPspExploit:

// input: 4-byte-aligned kernel address to a 64-bit integer
// return *addr >= value;
static int is_ge_u64(uint32_t addr, uint32_t *value) {
    return (int)sceRtcCompareTick((uint64_t *)value, (uint64_t *)addr) <= 0;
}

// input: 4-byte-aligned kernel address
// return *addr
uint64_t pspXploitKernelRead64(uint32_t addr) {
    uint32_t value[2] = {0, 0};
    uint32_t res[2] = {0, 0};
    int bit_idx = 0;
    for (; bit_idx < 32; bit_idx++) {
        value[1] = res[1] | (1 << (31 - bit_idx));
        if (is_ge_u64(addr, value))
            res[1] = value[1];
    }
    value[1] = res[1];
    bit_idx = 0;
    for (; bit_idx < 32; bit_idx++) {
        value[0] = res[0] | (1 << (31 - bit_idx));
        if (is_ge_u64(addr, value))
            res[0] = value[0];
    }
    return *(uint64_t*)res;
}

then wired the remaining pieces (had to fix a bug in psp_decrypter.c).

I can confirm that I could load a Gran Turismo save after import/export the game-data file on real hardware (PSP-2000), without getting the error about "the save belongs to another user".

I'll close this PR once I merge my implementation, but I still appreciate your help because without your info and original PR I would have never figured all these pieces to get 100% accurate resigning of PSP saves.

Kethen commented 7 months ago

Awesome! Nice job figuring out how to fetch the kirk cmd5 seeds, on ofw none the less :D

bucanero commented 7 months ago

Awesome! Nice job figuring out how to fetch the kirk cmd5 seeds, on ofw none the less :D

btw, I also made a super simple PSP homebrew that will dump the FuseID values from a PSP, so those values could be used in emulators or other tools that might need it: https://github.com/bucanero/psp-fuseid-dumper

I can also share an EBOOT.PBP, but it should be straightforward to compile with PSPSDK. I'll be including this tool with my Apollo-Vita version so users can grab fuse-id values from psvita's emulator.