Full Code of yifanlu/psvimgtools for AI

master f446c5663804 cached
30 files
102.5 KB
36.9k tokens
86 symbols
1 requests
Download .txt
Repository: yifanlu/psvimgtools
Branch: master
Commit: f446c5663804
Files: 30
Total size: 102.5 KB

Directory structure:
gitextract_sc23yeqq/

├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
├── aes256.c
├── aes256.h
├── backup.c
├── backup.h
├── cmake/
│   └── Modules/
│       ├── FindLibGcrypt.cmake
│       └── Findzlib.cmake
├── crypto.h
├── dump_partials/
│   ├── CMakeLists.txt
│   ├── debugScreen.h
│   ├── debugScreenFont.c
│   ├── dump_partials.h
│   ├── kernel.c
│   ├── kernel.yml
│   └── main.c
├── endian-utils.h
├── psvimg-create.c
├── psvimg-extract.c
├── psvimg-keyfind.c
├── psvimg.h
├── psvmd-decrypt.c
├── restore.c
├── restore.h
├── sha256.c
├── sha256.h
├── utils.c
└── utils.h

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
CMakeCache.txt
CMakeFiles
CMakeScripts
Makefile
cmake_install.cmake
install_manifest.txt
CTestTestfile.cmake
.DS_Store


================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 2.8)

project(psvimgtools C)

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
find_package(zlib REQUIRED)
find_package(LibGcrypt)

include_directories(${zlib_INCLUDE_DIRS})
include_directories(${LIBGCRYPT_INCLUDE_DIRS})

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99")
if(${LIBGCRYPT_FOUND})
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_GCRYPT")
else(${LIBGCRYPT_FOUND})
  set(sw_crypto aes256.c sha256.c)
  message(WARNING "libgcrypt was not found. Using slow software crypto.")
endif(${LIBGCRYPT_FOUND})

add_executable(psvimg-extract psvimg-extract.c restore.c utils.c ${sw_crypto})
add_executable(psvmd-decrypt psvmd-decrypt.c restore.c utils.c ${sw_crypto})
add_executable(psvimg-create psvimg-create.c backup.c utils.c ${sw_crypto})

target_link_libraries(psvimg-extract ${zlib_LIBRARIES} ${LIBGCRYPT_LIBRARIES})
target_link_libraries(psvmd-decrypt ${zlib_LIBRARIES} ${LIBGCRYPT_LIBRARIES})
target_link_libraries(psvimg-create ${zlib_LIBRARIES} ${LIBGCRYPT_LIBRARIES})

install(TARGETS psvimg-extract DESTINATION bin)
install(TARGETS psvmd-decrypt DESTINATION bin)
install(TARGETS psvimg-create DESTINATION bin)

if(${LIBGCRYPT_FOUND})
  add_executable(psvimg-keyfind psvimg-keyfind.c utils.c)
  set_target_properties(psvimg-keyfind PROPERTIES COMPILE_FLAGS "-O3")
  target_link_libraries(psvimg-keyfind ${LIBGCRYPT_LIBRARIES})
  install(TARGETS psvimg-keyfind DESTINATION bin)
endif(${LIBGCRYPT_FOUND})


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2017 Yifan Lu

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
psvimgtools
===========
This is a set of tools that let you decrypt, extract, and repack Vita CMA backup images. To use this you need your backup key which is tied to your PSN AID.

## Building

You should have `cmake` and `zlib` installed. To enable hardware accelerated crypto, make sure `libgcrypt` is installed. Windows users should install either Cygwin or Bash on Ubuntu for Windows.

Then just run
```
mkdir build && cd build
cmake ..
make
```

## Usage

### psvimg-extract

This is used to extract `.psvimg` files. The extracted output includes a directory for each backup set (e.g: `ur0:appmeta`, `ux0:iconlayout.ini`, and `ur0:tmp/registry` are three separate sets). Each backup set contains zero or more files and directories. A special file `VITA_PATH.TXT` is created for each set to remember what the original path was before extraction (this is used for repacking). A set can be only a single file (for example `ux0:iconlayout.ini`). In that case, the file `VITA_DATA.BIN` is created to host the contents of the file.

### psvmd-decrypt

This decrypts and decompresses `.psvmd` files. The contents of which are defined in `psvimg.h`. This contains information such as the firmware version of the system that created the backup and the unique PSID of the system. Extracting this file is not required for repacking and is provided for reverse engineering/debugging purposes.

### psvimg-create

This repacks extracted files and creates the associated `.psvimg` and `.psvmd` files. If you have a _decrypted_ `.psvmd`, you may pass it in with `-m` and the tool will reuse as many fields as possible (exception: size fields). No validity checks will be performed. If you do not have a decrypted `.psvmd`, you should use the `-n` option and specify the name of the backup. You should use the same name (the file name without the `.psvimg` extension) when repacking because CMA does check for a valid name. For example, if you are repacking `license.psvimg`, you should specify `-n license`.

The pack input directory should follow the same format as the output of `psvimg-extract`. The means a separate directory for each backup set (there may only be one set, in which your input directory will contain one subdirectory) each with a `VITA_PATH.TXT` file specifying the Vita path and optionally a `VITA_DATA.BIN` file if the set is a file.

Note that CMA does check the paths of the backup sets. Trying to add a backup set with a custom path may result in failure.

## psvimg-keyfind

This is a brute-force backup key find tool. You should generate a valid `partials.bin` file using the provided "dump_partials" Vita homebrew that runs on HENkaku enabled consoles. You can generate partials for other people as well if you know their AID. The `partials.bin` file does not contain any console-unique information but is derived from the provided PSN AID. The AID is the 16 hex characters in your CMA backup path. For example, if I wish to decrypt `PS Vita/PGAME/xxxxxxxxxxxxxxxx/NPJH00053/game/game.psvimg` then my AID is `xxxxxxxxxxxxxxxx`.

The `-n` option specifies the number of threads to run. On Linux, each thread tries to run on a separate processor. On OSX/Windows, it is up to the scheduler to make such decisions. You should not specify too high of a number here, as running multiple threads on a single CPU will result in diminishing returns. A good rule of thumb is to specify the number of CPU cores on your system.


================================================
FILE: aes256.c
================================================
/*
*   Byte-oriented AES-256 implementation.
*   All lookup tables replaced with 'on the fly' calculations.
*
*   Copyright (c) 2007-2011 Ilya O. Levin, http://www.literatecode.com
*   Other contributors: Hal Finney
*
*   Permission to use, copy, modify, and distribute this software for any
*   purpose with or without fee is hereby granted, provided that the above
*   copyright notice and this permission notice appear in all copies.
*
*   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
*   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
*   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
*   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
*   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
*   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
*   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "aes256.h"

#define FD(x)  (((x) >> 1) ^ (((x) & 1) ? 0x8d : 0))

#define BACK_TO_TABLES

static uint8_t rj_xtime(uint8_t);
static void aes_subBytes(uint8_t *);
static void aes_subBytes_inv(uint8_t *);
static void aes_addRoundKey(uint8_t *, uint8_t *);
static void aes_addRoundKey_cpy(uint8_t *, uint8_t *, uint8_t *);
static void aes_shiftRows(uint8_t *);
static void aes_shiftRows_inv(uint8_t *);
static void aes_mixColumns(uint8_t *);
static void aes_mixColumns_inv(uint8_t *);
static void aes_expandEncKey(uint8_t *, uint8_t *);
static void aes_expandDecKey(uint8_t *, uint8_t *);
#ifndef BACK_TO_TABLES
static uint8_t gf_alog(uint8_t);
static uint8_t gf_log(uint8_t);
static uint8_t gf_mulinv(uint8_t);
static uint8_t rj_sbox(uint8_t);
static uint8_t rj_sbox_inv(uint8_t);
#endif

#ifdef BACK_TO_TABLES

static const uint8_t sbox[256] =
{
    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
    0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
    0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
    0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
    0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
    0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
    0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
    0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
    0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
    0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
    0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
    0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
    0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
    0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
    0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
    0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
    0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};
static const uint8_t sboxinv[256] =
{
    0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
    0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
    0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
    0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
    0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
    0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
    0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
    0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
    0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
    0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
    0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
    0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
    0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
    0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
    0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
    0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
    0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
    0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
    0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
    0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
    0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
    0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
    0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
    0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
    0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
    0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
    0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
    0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
    0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
    0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
    0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
    0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
};

#define rj_sbox(x)     sbox[(x)]
#define rj_sbox_inv(x) sboxinv[(x)]

#else /* tableless subroutines */

/* -------------------------------------------------------------------------- */
static uint8_t gf_alog(uint8_t x) // calculate anti-logarithm gen 3
{
    uint8_t y = 1, i;

    for (i = 0; i < x; i++) y ^= rj_xtime(y);

    return y;
} /* gf_alog */

/* -------------------------------------------------------------------------- */
static uint8_t gf_log(uint8_t x) // calculate logarithm gen 3
{
    uint8_t y, i = 0;

    if (x)
        for (i = 1, y = 1; i > 0; i++ )
        {
            y ^= rj_xtime(y);
            if (y == x) break;
        }

    return i;
} /* gf_log */


/* -------------------------------------------------------------------------- */
static uint8_t gf_mulinv(uint8_t x) // calculate multiplicative inverse
{
    return (x) ? gf_alog(255 - gf_log(x)) : 0;
} /* gf_mulinv */

/* -------------------------------------------------------------------------- */
static uint8_t rj_sbox(uint8_t x)
{
    uint8_t y, sb;

    sb = y = gf_mulinv(x);
    y = (uint8_t)(y << 1) | (y >> 7), sb ^= y;
    y = (uint8_t)(y << 1) | (y >> 7), sb ^= y;
    y = (uint8_t)(y << 1) | (y >> 7), sb ^= y;
    y = (uint8_t)(y << 1) | (y >> 7), sb ^= y;

    return (sb ^ 0x63);
} /* rj_sbox */

/* -------------------------------------------------------------------------- */
static uint8_t rj_sbox_inv(uint8_t x)
{
    uint8_t y, sb;

    y = x ^ 0x63;
    sb = y = (uint8_t)(y << 1) | (y >> 7);
    y = (uint8_t)(y << 2) | (y >> 6);
    sb ^= y;
    y = (uint8_t)(y << 3) | (y >> 5);
    sb ^= y;

    return gf_mulinv(sb);
} /* rj_sbox_inv */

#endif

/* -------------------------------------------------------------------------- */
static uint8_t rj_xtime(uint8_t x)
{
    uint8_t y = (uint8_t)(x << 1);
    return (x & 0x80) ? (y ^ 0x1b) : y;
} /* rj_xtime */

/* -------------------------------------------------------------------------- */
static void aes_subBytes(uint8_t *buf)
{
    register uint8_t i = 16;

    while (i--) buf[i] = rj_sbox(buf[i]);
} /* aes_subBytes */

/* -------------------------------------------------------------------------- */
static void aes_subBytes_inv(uint8_t *buf)
{
    register uint8_t i = 16;

    while (i--) buf[i] = rj_sbox_inv(buf[i]);
} /* aes_subBytes_inv */

/* -------------------------------------------------------------------------- */
static void aes_addRoundKey(uint8_t *buf, uint8_t *key)
{
    register uint8_t i = 16;

    while (i--) buf[i] ^= key[i];
} /* aes_addRoundKey */

/* -------------------------------------------------------------------------- */
static void aes_addRoundKey_cpy(uint8_t *buf, uint8_t *key, uint8_t *cpk)
{
    register uint8_t i = 16;

    while (i--)  buf[i] ^= (cpk[i] = key[i]), cpk[16 + i] = key[16 + i];
} /* aes_addRoundKey_cpy */


/* -------------------------------------------------------------------------- */
static void aes_shiftRows(uint8_t *buf)
{
    register uint8_t i, j; /* to make it potentially parallelable :) */

    i = buf[1], buf[1] = buf[5], buf[5] = buf[9], buf[9] = buf[13], buf[13] = i;
    i = buf[10], buf[10] = buf[2], buf[2] = i;
    j = buf[3], buf[3] = buf[15], buf[15] = buf[11], buf[11] = buf[7], buf[7] = j;
    j = buf[14], buf[14] = buf[6], buf[6]  = j;

} /* aes_shiftRows */

/* -------------------------------------------------------------------------- */
static void aes_shiftRows_inv(uint8_t *buf)
{
    register uint8_t i, j; /* same as above :) */

    i = buf[1], buf[1] = buf[13], buf[13] = buf[9], buf[9] = buf[5], buf[5] = i;
    i = buf[2], buf[2] = buf[10], buf[10] = i;
    j = buf[3], buf[3] = buf[7], buf[7] = buf[11], buf[11] = buf[15], buf[15] = j;
    j = buf[6], buf[6] = buf[14], buf[14] = j;

} /* aes_shiftRows_inv */

/* -------------------------------------------------------------------------- */
static void aes_mixColumns(uint8_t *buf)
{
    register uint8_t i, a, b, c, d, e;

    for (i = 0; i < 16; i += 4)
    {
        a = buf[i];
        b = buf[i + 1];
        c = buf[i + 2];
        d = buf[i + 3];
        e = a ^ b ^ c ^ d;
        buf[i] ^= e ^ rj_xtime(a ^ b);
        buf[i + 1] ^= e ^ rj_xtime(b ^ c);
        buf[i + 2] ^= e ^ rj_xtime(c ^ d);
        buf[i + 3] ^= e ^ rj_xtime(d ^ a);
    }
} /* aes_mixColumns */

/* -------------------------------------------------------------------------- */
void aes_mixColumns_inv(uint8_t *buf)
{
    register uint8_t i, a, b, c, d, e, x, y, z;

    for (i = 0; i < 16; i += 4)
    {
        a = buf[i];
        b = buf[i + 1];
        c = buf[i + 2];
        d = buf[i + 3];
        e = a ^ b ^ c ^ d;
        z = rj_xtime(e);
        x = e ^ rj_xtime(rj_xtime(z ^ a ^ c));
        y = e ^ rj_xtime(rj_xtime(z ^ b ^ d));
        buf[i] ^= x ^ rj_xtime(a ^ b);
        buf[i + 1] ^= y ^ rj_xtime(b ^ c);
        buf[i + 2] ^= x ^ rj_xtime(c ^ d);
        buf[i + 3] ^= y ^ rj_xtime(d ^ a);
    }
} /* aes_mixColumns_inv */

/* -------------------------------------------------------------------------- */
static void aes_expandEncKey(uint8_t *k, uint8_t *rc)
{
    register uint8_t i;

    k[0] ^= rj_sbox(k[29]) ^ (*rc);
    k[1] ^= rj_sbox(k[30]);
    k[2] ^= rj_sbox(k[31]);
    k[3] ^= rj_sbox(k[28]);
    *rc = rj_xtime( *rc);

    for(i = 4; i < 16; i += 4)  k[i] ^= k[i - 4],   k[i + 1] ^= k[i - 3],
                                            k[i + 2] ^= k[i - 2], k[i + 3] ^= k[i - 1];
    k[16] ^= rj_sbox(k[12]);
    k[17] ^= rj_sbox(k[13]);
    k[18] ^= rj_sbox(k[14]);
    k[19] ^= rj_sbox(k[15]);

    for(i = 20; i < 32; i += 4) k[i] ^= k[i - 4],   k[i + 1] ^= k[i - 3],
                                            k[i + 2] ^= k[i - 2], k[i + 3] ^= k[i - 1];

} /* aes_expandEncKey */

/* -------------------------------------------------------------------------- */
void aes_expandDecKey(uint8_t *k, uint8_t *rc)
{
    uint8_t i;

    for(i = 28; i > 16; i -= 4) k[i + 0] ^= k[i - 4], k[i + 1] ^= k[i - 3],
                                                k[i + 2] ^= k[i - 2], k[i + 3] ^= k[i - 1];

    k[16] ^= rj_sbox(k[12]);
    k[17] ^= rj_sbox(k[13]);
    k[18] ^= rj_sbox(k[14]);
    k[19] ^= rj_sbox(k[15]);

    for(i = 12; i > 0; i -= 4)  k[i + 0] ^= k[i - 4], k[i + 1] ^= k[i - 3],
                                                k[i + 2] ^= k[i - 2], k[i + 3] ^= k[i - 1];

    *rc = FD(*rc);
    k[0] ^= rj_sbox(k[29]) ^ (*rc);
    k[1] ^= rj_sbox(k[30]);
    k[2] ^= rj_sbox(k[31]);
    k[3] ^= rj_sbox(k[28]);
} /* aes_expandDecKey */


/* -------------------------------------------------------------------------- */
void aes256_init(aes256_context *ctx, uint8_t *k)
{
    uint8_t rcon = 1;
    register uint8_t i;

    for (i = 0; i < sizeof(ctx->key); i++) ctx->enckey[i] = ctx->deckey[i] = k[i];
    for (i = 8; --i;) aes_expandEncKey(ctx->deckey, &rcon);
} /* aes256_init */

/* -------------------------------------------------------------------------- */
void aes256_done(aes256_context *ctx)
{
    register uint8_t i;

    for (i = 0; i < sizeof(ctx->key); i++)
        ctx->key[i] = ctx->enckey[i] = ctx->deckey[i] = 0;
} /* aes256_done */

/* -------------------------------------------------------------------------- */
void aes256_encrypt_ecb(aes256_context *ctx, uint8_t *buf)
{
    uint8_t i, rcon;

    aes_addRoundKey_cpy(buf, ctx->enckey, ctx->key);
    for(i = 1, rcon = 1; i < 14; ++i)
    {
        aes_subBytes(buf);
        aes_shiftRows(buf);
        aes_mixColumns(buf);
        if( i & 1 ) aes_addRoundKey( buf, &ctx->key[16]);
        else aes_expandEncKey(ctx->key, &rcon), aes_addRoundKey(buf, ctx->key);
    }
    aes_subBytes(buf);
    aes_shiftRows(buf);
    aes_expandEncKey(ctx->key, &rcon);
    aes_addRoundKey(buf, ctx->key);
} /* aes256_encrypt */

/* -------------------------------------------------------------------------- */
void aes256_decrypt_ecb(aes256_context *ctx, uint8_t *buf)
{
    uint8_t i, rcon;

    aes_addRoundKey_cpy(buf, ctx->deckey, ctx->key);
    aes_shiftRows_inv(buf);
    aes_subBytes_inv(buf);

    for (i = 14, rcon = 0x80; --i;)
    {
        if( ( i & 1 ) )
        {
            aes_expandDecKey(ctx->key, &rcon);
            aes_addRoundKey(buf, &ctx->key[16]);
        }
        else aes_addRoundKey(buf, ctx->key);
        aes_mixColumns_inv(buf);
        aes_shiftRows_inv(buf);
        aes_subBytes_inv(buf);
    }
    aes_addRoundKey( buf, ctx->key);
} /* aes256_decrypt */


================================================
FILE: aes256.h
================================================
/*  
*   Byte-oriented AES-256 implementation.
*   All lookup tables replaced with 'on the fly' calculations. 
*
*   Copyright (c) 2007-2009 Ilya O. Levin, http://www.literatecode.com
*   Other contributors: Hal Finney
*
*   Permission to use, copy, modify, and distribute this software for any
*   purpose with or without fee is hereby granted, provided that the above
*   copyright notice and this permission notice appear in all copies.
*
*   THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
*   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
*   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
*   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
*   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
*   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
*   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef uint8_t
#define uint8_t  unsigned char
#endif

#ifdef __cplusplus
extern "C" { 
#endif

    typedef struct {
        uint8_t key[32]; 
        uint8_t enckey[32]; 
        uint8_t deckey[32];
    } aes256_context; 


    void aes256_init(aes256_context *, uint8_t * /* key */);
    void aes256_done(aes256_context *);
    void aes256_encrypt_ecb(aes256_context *, uint8_t * /* plaintext */);
    void aes256_decrypt_ecb(aes256_context *, uint8_t * /* cipertext */);

#ifdef __cplusplus
}
#endif


================================================
FILE: backup.c
================================================
/* Copyright (C) 2017 Yifan Lu
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#include <dirent.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <utime.h>
#include <zlib.h>
#include "crypto.h"
#include "backup.h"
#include "endian-utils.h"
#include "psvimg.h"
#include "utils.h"

#define MAX_PATH_LEN 1024

void *encrypt_thread(void *pargs) {
  args_t *args = (args_t *)pargs;
  SHA256_CTX ctx, tmp;
  uint8_t iv[AES_BLOCK_SIZE];
  uint8_t buffer[PSVIMG_BLOCK_SIZE + SHA256_BLOCK_SIZE + 0x10];
  ssize_t rd;
  union {
    struct {
      uint32_t padding;
      uint32_t unused;
      uint64_t total;
    } __attribute__((packed)) data;
    uint8_t raw[AES_BLOCK_SIZE];
  } footer;

  // write iv
  memcpy(iv, args->iv, AES_BLOCK_SIZE);
  aes256_encrypt(iv, args->key);
  write_block(args->out, iv, AES_BLOCK_SIZE);

  // encrypt blocks
  sha256_init(&ctx);
  footer.data.total = AES_BLOCK_SIZE; // from iv
  footer.data.padding = 0;
  footer.data.unused = 0;
  while ((rd = read_block(args->in, buffer, PSVIMG_BLOCK_SIZE)) > 0) {
    // generate hash
    sha256_update(&ctx, buffer, rd);
    sha256_copy(&tmp, &ctx);
    sha256_final(&tmp, &buffer[rd]);
    rd += SHA256_BLOCK_SIZE;

    // add padding
    if (footer.data.padding != 0) { // we should be reading 0x8000 blocks! only need padding once
      fprintf(stderr, "an internal error has occured!\n");
      goto end;
    }
    if (rd & (AES_BLOCK_SIZE-1)) {
      footer.data.padding = AES_BLOCK_SIZE - (rd & (AES_BLOCK_SIZE-1));
      rd += footer.data.padding;
    }

    // encrypt
    aes256_cbc_encrypt(buffer, args->key, iv, rd / AES_BLOCK_SIZE);

    // save next iv
    memcpy(iv, &buffer[rd - AES_BLOCK_SIZE], AES_BLOCK_SIZE);

    // write output
    write_block(args->out, buffer, rd);

    footer.data.total += rd;
  }

  if (rd < 0) {
    fprintf(stderr, "Read error occured!\n");
    goto end;
  }

  // send footer
  footer.data.total += 0x10;
  footer.data.padding = htole32(footer.data.padding);
  footer.data.total = htole64(footer.data.total);
  aes256_cbc_encrypt(footer.raw, args->key, iv, 1);
  write_block(args->out, footer.raw, AES_BLOCK_SIZE);

end:
  close(args->out);
  close(args->in);
  return NULL;
}

void *compress_thread(void *pargs) {
  args_t *args = (args_t *)pargs;

  int ret;
  unsigned have;
  z_stream strm;
  unsigned char in[PSVIMG_BLOCK_SIZE];
  unsigned char out[PSVIMG_BLOCK_SIZE];
  ssize_t rd;

  /* allocate deflate state */
  strm.zalloc = Z_NULL;
  strm.zfree = Z_NULL;
  strm.opaque = Z_NULL;
  ret = deflateInit(&strm, Z_DEFAULT_COMPRESSION);
  if (ret != Z_OK) {
    fprintf(stderr, "error init zlib\n");
    goto end;
  }

  /* compress until end of file */
  do {
    strm.avail_in = rd = read_block(args->in, in, sizeof(in));
    if (rd < 0) {
      fprintf(stderr, "error reading\n");
      goto end;
    }
    strm.next_in = in;

    /* run deflate() on input until output buffer not full, finish
       compression if all of source has been read in */
    do {
      strm.avail_out = PSVIMG_BLOCK_SIZE;
      strm.next_out = out;
      ret = deflate(&strm, Z_NO_FLUSH);    /* no bad return value */
      if (ret == Z_STREAM_ERROR) {  /* state not clobbered */
        fprintf(stderr, "zlib internal error\n");
        goto end;
      }
      have = PSVIMG_BLOCK_SIZE - strm.avail_out;
      if (write_block(args->out, out, have) < have) {
        fprintf(stderr, "error writing\n");
        goto end;
      }
    } while (strm.avail_out == 0);
    if (strm.avail_in != 0){     /* all input will be used */
      fprintf(stderr, "zlib internal error\n");
      goto end;
    }

    /* done when last data in file processed */
  } while (rd > 0);
  if (deflate(&strm, Z_FINISH) == Z_STREAM_ERROR) {  /* state not clobbered */
    fprintf(stderr, "zlib internal error\n");
    goto end;
  }
  have = PSVIMG_BLOCK_SIZE - strm.avail_out;
  if (write_block(args->out, out, have) < have) {
    fprintf(stderr, "error writing\n");
    goto end;
  }

  /* clean up and return */
  (void)deflateEnd(&strm);

end:
  close(args->out);
  close(args->in);
  return NULL;
}

static void time_to_scetime(const time_t *time, SceDateTime *sce) {
  struct tm *tmp;
  tmp = localtime(time);
  sce->second = htole32(tmp->tm_sec);
  sce->minute = htole32(tmp->tm_min);
  sce->hour = htole32(tmp->tm_hour);
  sce->day = htole32(tmp->tm_mday);
  sce->month = htole32(tmp->tm_mon + 1);
  sce->year = htole32(tmp->tm_year + 1900);
  sce->microsecond = htole32(0);
}

static int scestat(const char *path, SceIoStat *sce) {
  struct stat st;

  if (stat(path, &st) < 0) {
    return -1;
  }

  sce->sst_mode = 0;
  if (S_ISDIR(st.st_mode)) {
    sce->sst_mode |= SCE_S_IFDIR;
  } else {
    sce->sst_mode |= SCE_S_IFREG;
  }
  if ((st.st_mode & S_IRUSR) == S_IRUSR) {
    sce->sst_mode |= SCE_S_IRUSR;
  }
  if ((st.st_mode & S_IWUSR) == S_IWUSR) {
    sce->sst_mode |= SCE_S_IWUSR;
  }
  if ((st.st_mode & S_IRGRP) == S_IRGRP) {
    sce->sst_mode |= SCE_S_IRGRP;
  }
  if ((st.st_mode & S_IWGRP) == S_IWGRP) {
    sce->sst_mode |= SCE_S_IWGRP;
  }
  if ((st.st_mode & S_IROTH) == S_IROTH) {
    sce->sst_mode |= SCE_S_IROTH;
  }
  if ((st.st_mode & S_IWOTH) == S_IWOTH) {
    sce->sst_mode |= SCE_S_IWOTH;
  }
  sce->sst_mode = htole32(sce->sst_mode);
  sce->sst_attr = htole32(0);
  sce->sst_size = htole64(st.st_size);
  time_to_scetime(&st.st_ctime, &sce->sst_ctime);
  time_to_scetime(&st.st_atime, &sce->sst_atime);
  time_to_scetime(&st.st_mtime, &sce->sst_mtime);
  memset(sce->sst_private, 0, sizeof(sce->sst_private));

  return 0;
}

static ssize_t add_all_files(int fd, const char *parent, const char *rel, const char *host);

static ssize_t add_file(int fd, const char *parent, const char *rel, const char *host) {
  PsvImgHeader_t header;
  PsvImgTailer_t tailer;
  uint64_t fsize;
  char *buffer;
  int file;
  int padding;

  // create header
  memset(&header, PSVIMG_HEADER_FILLER, sizeof(header));
  header.systime = htole64(0);
  header.flags = htole64(0);
  if (scestat(host, &header.stat) < 0) {
    fprintf(stderr, "error getting stat for %s\n", host);
    return -1;
  }
  strncpy(header.path_parent, parent, sizeof(header.path_parent));
  header.unk_16C = htole32(1);
  strncpy(header.path_rel, rel, sizeof(header.path_rel));
  memcpy(header.end, PSVIMG_ENDOFHEADER, sizeof(header.end));

  // send header
  write_block(fd, &header, sizeof(header));

  // send file
  if (SCE_S_ISREG(le32toh(header.stat.sst_mode))) {
    fsize = le64toh(header.stat.sst_size);
    printf("packing file %s%s (%llx bytes)...\n", header.path_parent, header.path_rel, fsize);
    file = open(host, O_RDONLY);
    if (file < 0) {
      fprintf(stderr, "error opening %s\n", host);
      return -1;
    }
    if (copy_block(fd, file, fsize) < fsize) {
      fprintf(stderr, "error writing file data\n");
      close(file);
      return -1;
    }
    close(file);
  } else {
    fsize = 0;
  }

  // send padding
  if (fsize & (PSVIMG_ENTRY_ALIGN-1)) {
    padding = PSVIMG_ENTRY_ALIGN - (fsize & (PSVIMG_ENTRY_ALIGN-1));
  } else {
    padding = 0;
  }
  while (padding --> 0) {
    char ch = (padding == 0) ? '\n' : PSVIMG_PADDING_FILLER;
    write_block(fd, &ch, 1);
  }

  // send tailer
  memset(&tailer, PSVIMG_TAILER_FILLER, sizeof(tailer));
  tailer.flags = htole64(0);
  memcpy(tailer.end, PSVIMG_ENDOFTAILER, sizeof(tailer.end));
  write_block(fd, &tailer, sizeof(tailer));

  if (SCE_S_ISDIR(le32toh(header.stat.sst_mode))) {
    printf("packing directory %s%s...\n", header.path_parent, header.path_rel);
    return add_all_files(fd, parent, rel, host);
  } else {
    return (ssize_t)fsize;
  }
}

static ssize_t add_all_files(int fd, const char *parent, const char *rel, const char *host) {
  char new_rel[256];
  char new_host[MAX_PATH_LEN];
  DIR *dir;
  struct dirent *dent;
  ssize_t fsize, total;

  if ((dir = opendir(host)) == NULL) {
    fprintf(stderr, "cannot open %s\n", host);
    return -1;
  }

  total = 0;
  while ((dent = readdir(dir)) != NULL) {
    if (rel[0] == '\0' && strcmp(dent->d_name, "VITA_PATH.TXT") == 0) {
      // skip this file
      continue;
    }

    if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
      continue;
    }

#ifdef __APPLE__
    if (strcmp(dent->d_name, ".DS_Store") == 0) {
      continue; // ignore annoying OSX specific files
    }
#endif

    if (rel[0] == '\0' && strcmp(dent->d_name, "VITA_DATA.BIN") == 0) { // special file
      new_rel[0] = '\0';
    } else {
      if (snprintf(new_rel, sizeof(new_rel), "%s/%s", rel, dent->d_name) == sizeof(new_rel)) {
        fprintf(stderr, "path is too long! cannot add %s/%s\n", rel, dent->d_name);
        goto err;
      }
    }
    snprintf(new_host, sizeof(new_host), "%s/%s", host, dent->d_name);

    dent = NULL; // so we don't actually reuse it
    // add file/directory to psvimg
    if ((fsize = add_file(fd, parent, new_rel, new_host)) < 0) {
      goto err;
    }
    total += fsize;
  }

  closedir(dir);
  return total;

err:
  closedir(dir);
  return -1;
}

void *pack_thread(void *pargs) {
  args_t *args = (args_t *)pargs;
  DIR *dir;
  struct dirent *dent;
  struct stat st;
  char parent[256];
  char host[MAX_PATH_LEN];
  char parent_file[MAX_PATH_LEN];
  int fd;
  ssize_t rd, wr;
  
  if ((dir = opendir(args->prefix)) == NULL) {
    fprintf(stderr, "cannot open %s\n", args->prefix);
    goto end2;
  }

  while ((dent = readdir(dir)) != NULL) {
    if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {
      continue;
    }

#ifdef __APPLE__
    if (strcmp(dent->d_name, ".DS_Store") == 0) {
      continue; // ignore annoying OSX specific files
    }
#endif

    snprintf(host, sizeof(host), "%s/%s", args->prefix, dent->d_name);
    if (stat(host, &st) < 0) {
      fprintf(stderr, "internal error\n");
      goto end;
    }

    if (S_ISDIR(st.st_mode)) {
      snprintf(parent_file, sizeof(parent_file), "%s/%s", host, "VITA_PATH.TXT");
      if ((fd = open(parent_file, O_RDONLY)) < 0) {
        fprintf(stderr, "WARNING: skipping %s because VITA_PATH.TXT is not found!\n", host);
        continue;
      }
      if ((rd = read_block(fd, parent, sizeof(parent)-1)) < 0) {
        fprintf(stderr, "error reading %s\n", parent_file);
        goto end;
      }
      close(fd);
      parent[rd] = '\0';
      dent = NULL; // so we don't actually reuse it
      printf("adding files for %s\n", parent);
      if ((wr = add_all_files(args->out, parent, "\0", host)) < 0) {
        goto end;
      }
      args->content_size += wr;
    } else {
      fprintf(stderr, "WARNING: skipping %s because it is not a directory!\n", host);
    }
  }
  
end:
  closedir(dir);
end2:
  close(args->out);
  close(args->in);
  return NULL;
}


================================================
FILE: backup.h
================================================
/* Copyright (C) 2017 Yifan Lu
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#ifndef BACKUP_H__
#define BACKUP_H__

typedef struct args {
  int in;
  int out;
  unsigned char key[32];
  unsigned char iv[16];
  const char *prefix;
  size_t content_size;
} args_t;

void *encrypt_thread(void *pargs);
void *compress_thread(void *pargs);
void *pack_thread(void *pargs);

#endif


================================================
FILE: cmake/Modules/FindLibGcrypt.cmake
================================================
#.rst
# FindLibGcrypt
# -------------
#
# Finds the Libgcrypt library.
#
# This will define the following variables:
#
# ``LIBGCRYPT_FOUND``
#     True if the requested version of gcrypt was found
# ``LIBGCRYPT_VERSION``
#     The version of gcrypt that was found
# ``LIBGCRYPT_INCLUDE_DIRS``
#     The gcrypt include directories
# ``LIBGCRYPT_LIBRARIES``
#     The linker libraries needed to use the gcrypt library

# Copyright 2014 Nicolás Alvarez <nicolas.alvarez@gmail.com>
# Copyright 2016 Igalia S.L
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. The name of the author may not be used to endorse or promote products
#    derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

find_program(LIBGCRYPTCONFIG_SCRIPT NAMES libgcrypt-config)
if (LIBGCRYPTCONFIG_SCRIPT)
    execute_process(
        COMMAND "${LIBGCRYPTCONFIG_SCRIPT}" --prefix
        RESULT_VARIABLE CONFIGSCRIPT_RESULT
        OUTPUT_VARIABLE PREFIX
        OUTPUT_STRIP_TRAILING_WHITESPACE
    )
    if (CONFIGSCRIPT_RESULT EQUAL 0)
        set(LIBGCRYPT_LIB_HINT "${PREFIX}/lib")
        set(LIBGCRYPT_INCLUDE_HINT "${PREFIX}/include")
    endif ()
endif ()

find_library(LIBGCRYPT_LIBRARY
    NAMES gcrypt
    HINTS ${LIBGCRYPT_LIB_HINT}
)
find_path(LIBGCRYPT_INCLUDE_DIR
    NAMES gcrypt.h
    HINTS ${LIBGCRYPT_INCLUDE_HINT}
)

if (LIBGCRYPT_INCLUDE_DIR)
    file(STRINGS ${LIBGCRYPT_INCLUDE_DIR}/gcrypt.h GCRYPT_H REGEX "^#define GCRYPT_VERSION ")
    string(REGEX REPLACE "^#define GCRYPT_VERSION \"(.*)\".*$" "\\1" LIBGCRYPT_VERSION "${GCRYPT_H}")
endif ()

include(FindPackageHandleStandardArgs)

find_package_handle_standard_args(LibGcrypt
    FOUND_VAR LIBGCRYPT_FOUND
    REQUIRED_VARS LIBGCRYPT_LIBRARY LIBGCRYPT_INCLUDE_DIR
    VERSION_VAR LIBGCRYPT_VERSION
)
if (LIBGCRYPT_FOUND)
    set(LIBGCRYPT_LIBRARIES ${LIBGCRYPT_LIBRARY})
    set(LIBGCRYPT_INCLUDE_DIRS ${LIBGCRYPT_INCLUDE_DIR})
endif ()

mark_as_advanced(LIBGCRYPT_LIBRARY LIBGCRYPT_INCLUDE_DIR)

include(FeatureSummary)
set_package_properties(LibGcrypt PROPERTIES
    DESCRIPTION "A general purpose cryptographic library based on the code from GnuPG."
    URL "http://www.gnu.org/software/libgcrypt/"
)



================================================
FILE: cmake/Modules/Findzlib.cmake
================================================
find_package(PkgConfig)
pkg_check_modules(PC_zlib zlib>=1.2.8)

find_path(zlib_INCLUDE_DIR zlib.h
          HINTS ${PC_zlib_INCLUDEDIR} ${PC_zlib_INCLUDE_DIRS})

find_library(zlib_LIBRARY NAMES z
             HINTS ${PC_zlib_LIBDIR} ${PC_zlib_LIBRARY_DIRS} )

set(zlib_LIBRARIES ${zlib_LIBRARY})
set(zlib_INCLUDE_DIRS ${zlib_INCLUDE_DIR})

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(zlib DEFAULT_MSG
                                  zlib_LIBRARY zlib_INCLUDE_DIR)

mark_as_advanced(zlib_INCLUDE_DIR zlib_LIBRARY )


================================================
FILE: crypto.h
================================================
/* Copyright (C) 2017 Yifan Lu
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#ifndef CRYPTO_H__
#define CRYPTO_H__

#include <stdint.h>
#ifdef HAVE_GCRYPT
#include <gcrypt.h>
#endif

#define AES_BLOCK_SIZE 16
#define SHA256_BLOCK_SIZE 32

#ifdef HAVE_GCRYPT
static inline void aes256_cbc_decrypt(uint8_t *buffer, uint8_t *key, uint8_t *iv, size_t blocks) {
  gcry_cipher_hd_t ctx;

  gcry_cipher_open(&ctx, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
  gcry_cipher_setkey(ctx, key, 32);
  gcry_cipher_setiv(ctx, iv, AES_BLOCK_SIZE);
  gcry_cipher_decrypt(ctx, buffer, blocks * AES_BLOCK_SIZE, NULL, 0);
  gcry_cipher_close(ctx);
}

static inline void aes256_cbc_encrypt(uint8_t *buffer, uint8_t *key, uint8_t *iv, size_t blocks) {
  gcry_cipher_hd_t ctx;

  gcry_cipher_open(&ctx, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
  gcry_cipher_setkey(ctx, key, 32);
  gcry_cipher_setiv(ctx, iv, AES_BLOCK_SIZE);
  gcry_cipher_encrypt(ctx, buffer, blocks * AES_BLOCK_SIZE, NULL, 0);
  gcry_cipher_close(ctx);
}

static inline void aes256_encrypt(uint8_t *buffer, uint8_t *key) {
  gcry_cipher_hd_t ctx;

  gcry_cipher_open(&ctx, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB, 0);
  gcry_cipher_setkey(ctx, key, 32);
  gcry_cipher_encrypt(ctx, buffer, AES_BLOCK_SIZE, NULL, 0);
  gcry_cipher_close(ctx);
}
#else // HAVE_GCRYPT
#include "aes256.h"
static inline void aes256_cbc_decrypt(uint8_t *buffer, uint8_t *key, uint8_t *iv, size_t blocks) {
  aes256_context ctx;

  aes256_init(&ctx, key);
  for (int i = blocks-1; i >= 0; i--) {
    aes256_decrypt_ecb(&ctx, &buffer[i * AES_BLOCK_SIZE]);
    for (int j = 0; j < AES_BLOCK_SIZE; j++) {
      buffer[i*AES_BLOCK_SIZE + j] ^= (i == 0) ? iv[j] : buffer[(i-1)*AES_BLOCK_SIZE + j];
    }
  }
  aes256_done(&ctx);
}

static inline void aes256_cbc_encrypt(uint8_t *buffer, uint8_t *key, uint8_t *iv, size_t blocks) {
  aes256_context ctx;

  aes256_init(&ctx, key);
  for (int i = 0; i < blocks; i++) {
    for (int j = 0; j < AES_BLOCK_SIZE; j++) {
      buffer[i*AES_BLOCK_SIZE + j] ^= (i == 0) ? iv[j] : buffer[(i-1)*AES_BLOCK_SIZE + j];
    }
    aes256_encrypt_ecb(&ctx, &buffer[i * AES_BLOCK_SIZE]);
  }
  aes256_done(&ctx);
}

static inline void aes256_encrypt(uint8_t *buffer, uint8_t *key) {
  aes256_context ctx;

  aes256_init(&ctx, key);
  aes256_encrypt_ecb(&ctx, buffer);
  aes256_done(&ctx);
}
#endif // HAVE_GCRYPT

#ifdef HAVE_GCRYPT
#define SHA256_CTX gcry_md_hd_t
#define sha256_init(ctx) gcry_md_open(ctx, GCRY_MD_SHA256, 0)
#define sha256_update(ctx, data, len) gcry_md_write(*(ctx), data, len)
#define sha256_copy(ctx1, ctx2) gcry_md_copy(ctx1, *(ctx2))
#define sha256_final(ctx, hash) do { \
  memcpy(hash, gcry_md_read(*(ctx), 0), SHA256_BLOCK_SIZE); \
  gcry_md_close(*(ctx)); \
} while (0)
#else // HAVE_GCRYPT
#include "sha256.h"
#endif // HAVE_GCRYPT

#endif


================================================
FILE: dump_partials/CMakeLists.txt
================================================
cmake_minimum_required(VERSION 2.8)

if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
  if(DEFINED ENV{VITASDK})
    set(CMAKE_TOOLCHAIN_FILE "$ENV{VITASDK}/share/vita.toolchain.cmake" CACHE PATH "toolchain file")
  else()
    message(FATAL_ERROR "Please define VITASDK to point to your SDK path!")
  endif()
endif()

set(SHORT_NAME dump_partials)
project(${SHORT_NAME})
include("${VITASDK}/share/vita.cmake" REQUIRED)

set(VITA_APP_NAME "Dump Partials")
set(VITA_TITLEID  "DUMP0900D")

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu11")

add_executable(${SHORT_NAME}
  main.c
  ../sha256.c
)

target_link_libraries(${SHORT_NAME}
  taihen_stub
  SceDisplay_stub
  SceCtrl_stub
  SceGxm_stub
  SceAppUtil_stub
  SceCommonDialog_stub
)

vita_create_self(${SHORT_NAME}.self ${SHORT_NAME} UNSAFE)

add_executable(kernel
  kernel.c
)

target_link_libraries(kernel
  SceCpuForDriver_stub
  SceSblAuthMgrForKernel_stub
  SceSysmemForDriver_stub
)

set_target_properties(kernel
  PROPERTIES LINK_FLAGS "-nostdlib"
  COMPILE_FLAGS "-D__VITA_KERNEL__"
)(${SHORT_NAME} kernel)

vita_create_self(kernel.skprx kernel
  UNSAFE
  CONFIG ${CMAKE_SOURCE_DIR}/kernel.yml
)

vita_create_vpk(${SHORT_NAME}.vpk ${VITA_TITLEID} ${SHORT_NAME}.self
  VERSION ${VITA_VERSION}
  NAME ${VITA_APP_NAME}
  FILE ${CMAKE_CURRENT_BINARY_DIR}/kernel.skprx kernel.skprx
)

add_dependencies(${SHORT_NAME}.vpk kernel.skprx)


================================================
FILE: dump_partials/debugScreen.h
================================================
#ifndef DEBUG_SCREEN_H
#define DEBUG_SCREEN_H

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <inttypes.h>

#include <psp2/display.h>
#include <psp2/kernel/sysmem.h>
#include <psp2/kernel/threadmgr.h>

#include "debugScreenFont.c"

#define SCREEN_WIDTH    (960)
#define SCREEN_HEIGHT   (544)
#define SCREEN_FB_WIDTH (960)
#define SCREEN_FB_SIZE  (2 * 1024 * 1024)
#define SCREEN_FB_ALIGN (256 * 1024)
#define SCREEN_GLYPH_W  (8)
#define SCREEN_GLYPH_H  (8)

#define COLOR_BLACK      0xFF000000
#define COLOR_RED        0xFF0000FF
#define COLOR_BLUE       0xFF00FF00
#define COLOR_YELLOW     0xFF00FFFF
#define COLOR_GREEN      0xFFFF0000
#define COLOR_MAGENTA    0xFFFF00FF
#define COLOR_CYAN       0xFFFFFF00
#define COLOR_WHITE      0xFFFFFFFF
#define COLOR_GREY       0xFF808080
#define COLOR_DEFAULT_FG COLOR_WHITE
#define COLOR_DEFAULT_BG COLOR_BLACK

static int psvDebugScreenMutex; /*< avoid race condition when outputing strings */
static uint32_t psvDebugScreenCoordX = 0;
static uint32_t psvDebugScreenCoordY = 0;
static uint32_t psvDebugScreenColorFg = COLOR_DEFAULT_FG;
static uint32_t psvDebugScreenColorBg = COLOR_DEFAULT_BG;
static SceDisplayFrameBuf psvDebugScreenFrameBuf = {
		sizeof(SceDisplayFrameBuf), NULL, SCREEN_WIDTH, 0, SCREEN_WIDTH, SCREEN_HEIGHT};

uint32_t psvDebugScreenSetFgColor(uint32_t color) {
	uint32_t prev_color = psvDebugScreenColorFg;
	psvDebugScreenColorFg = color;
	return prev_color;
}

uint32_t psvDebugScreenSetBgColor(uint32_t color) {
	uint32_t prev_color = psvDebugScreenColorBg;
	psvDebugScreenColorBg = color;
	return prev_color;
}

static size_t psvDebugScreenEscape(const char *str){
	int i,j, p=0, params[8]={};
	for(i=0; i<8 && str[i]!='\0'; i++){
		if(str[i] >= '0' && str[i] <= '9'){
			params[p]=(params[p]*10) + (str[i] - '0');
		}else if(str[i] == ';'){
			p++;
		}else if(str[i] == 'f' || str[i] == 'H'){
			psvDebugScreenCoordX = params[0] * SCREEN_GLYPH_W;
			psvDebugScreenCoordY = params[1] * SCREEN_GLYPH_H;
			break;
		}else if (str[i] == 'm'){
			for(j=0; j<=p; j++){
				switch(params[j]/10){/*bold,dim,underline,blink,invert,hidden => unsupported yet */
				#define BIT2BYTE(bit)    ( ((!!(bit&4))<<23) | ((!!(bit&2))<<15) | ((!!(bit&1))<<7) )
				case  0:psvDebugScreenSetFgColor(COLOR_DEFAULT_FG);psvDebugScreenSetBgColor(COLOR_DEFAULT_BG);break;
				case  3:psvDebugScreenSetFgColor(BIT2BYTE(params[j]%10));break;
				case  9:psvDebugScreenSetFgColor(BIT2BYTE(params[j]%10) | 0x7F7F7F7F);break;
				case  4:psvDebugScreenSetBgColor(BIT2BYTE(params[j]%10));break;
				case 10:psvDebugScreenSetBgColor(BIT2BYTE(params[j]%10) | 0x7F7F7F7F);break;
				#undef BIT2BYTE
				}
			}
			break;
		}
	}
	return i;
}

int psvDebugScreenSet() {
	SceDisplayFrameBuf framebuf = {
		.size = sizeof(framebuf),
		.base = psvDebugScreenFrameBuf.base,
		.pitch = SCREEN_WIDTH,
		.pixelformat = SCE_DISPLAY_PIXELFORMAT_A8B8G8R8,
		.width = SCREEN_WIDTH,
		.height = SCREEN_HEIGHT,
	};

	return sceDisplaySetFrameBuf(&framebuf, SCE_DISPLAY_SETBUF_NEXTFRAME);
}

int psvDebugScreenInit() {
	psvDebugScreenMutex = sceKernelCreateMutex("log_mutex", 0, 0, NULL);
	SceUID displayblock = sceKernelAllocMemBlock("display", SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, SCREEN_FB_SIZE, NULL);
	sceKernelGetMemBlockBase(displayblock, (void**)&psvDebugScreenFrameBuf.base);

	return psvDebugScreenSet();
}

void psvDebugScreenClear(int bg_color){
	psvDebugScreenCoordX = psvDebugScreenCoordY = 0;
	int i;
	for(i = 0; i < SCREEN_WIDTH * SCREEN_HEIGHT; i++) {
		((uint32_t*)psvDebugScreenFrameBuf.base)[i] = bg_color;
	}
}

int psvDebugScreenPuts(const char * text){
	int c, i, j, l;
	uint8_t *font;
	uint32_t *vram_ptr;
	uint32_t *vram;

	sceKernelLockMutex(psvDebugScreenMutex, 1, NULL);

	for (c = 0; text[c] != '\0' ; c++) {
		if (psvDebugScreenCoordX + 8 > SCREEN_WIDTH) {
			psvDebugScreenCoordY += SCREEN_GLYPH_H;
			psvDebugScreenCoordX = 0;
		}
		if (psvDebugScreenCoordY + 8 > SCREEN_HEIGHT) {
			psvDebugScreenClear(psvDebugScreenColorBg);
		}
		if (text[c] == '\n') {
			psvDebugScreenCoordX = 0;
			psvDebugScreenCoordY += SCREEN_GLYPH_H;
			continue;
		} else if (text[c] == '\r') {
			psvDebugScreenCoordX = 0;
			continue;
		} else if ((text[c] == '\e') && (text[c+1] == '[')) { /* escape code (change color, position ...) */
			c+=psvDebugScreenEscape(text+2)+2;
			continue;
		}

		vram = ((uint32_t*)psvDebugScreenFrameBuf.base) + psvDebugScreenCoordX + psvDebugScreenCoordY * SCREEN_FB_WIDTH;

		font = &psvDebugScreenFont[ (int)text[c] * 8];
		for (i = l = 0; i < SCREEN_GLYPH_W; i++, l += SCREEN_GLYPH_W, font++) {
			vram_ptr  = vram;
			for (j = 0; j < SCREEN_GLYPH_W; j++) {
				if ((*font & (128 >> j))) *vram_ptr = psvDebugScreenColorFg;
				else *vram_ptr = psvDebugScreenColorBg;
				vram_ptr++;
			}
			vram += SCREEN_FB_WIDTH;
		}
		psvDebugScreenCoordX += SCREEN_GLYPH_W;
	}

	sceKernelUnlockMutex(psvDebugScreenMutex, 1);
	return c;
}

int psvDebugScreenPrintf(const char *format, ...) {
	char buf[512];

	va_list opt;
	va_start(opt, format);
	int ret = vsnprintf(buf, sizeof(buf), format, opt);
	psvDebugScreenPuts(buf);
	va_end(opt);

	return ret;
}

#endif

================================================
FILE: dump_partials/debugScreenFont.c
================================================
/*
 * PSP Software Development Kit - http://www.pspdev.org
 * -----------------------------------------------------------------------
 * Licensed under the BSD license, see LICENSE in PSPSDK root for details.
 *
 * font.c - Debug Font.
 *
 * Copyright (c) 2005 Marcus R. Brown <mrbrown@ocgnet.org>
 * Copyright (c) 2005 James Forshaw <tyranid@gmail.com>
 * Copyright (c) 2005 John Kelley <ps2dev@kelley.ca>
 *
 * $Id: font.c 540 2005-07-08 19:35:10Z warren $
 */

unsigned char psvDebugScreenFont[]=
"\x00\x00\x00\x00\x00\x00\x00\x00\x3c\x42\xa5\x81\xa5\x99\x42\x3c"
"\x3c\x7e\xdb\xff\xff\xdb\x66\x3c\x6c\xfe\xfe\xfe\x7c\x38\x10\x00"
"\x10\x38\x7c\xfe\x7c\x38\x10\x00\x10\x38\x54\xfe\x54\x10\x38\x00"
"\x10\x38\x7c\xfe\xfe\x10\x38\x00\x00\x00\x00\x30\x30\x00\x00\x00"
"\xff\xff\xff\xe7\xe7\xff\xff\xff\x38\x44\x82\x82\x82\x44\x38\x00"
"\xc7\xbb\x7d\x7d\x7d\xbb\xc7\xff\x0f\x03\x05\x79\x88\x88\x88\x70"
"\x38\x44\x44\x44\x38\x10\x7c\x10\x30\x28\x24\x24\x28\x20\xe0\xc0"
"\x3c\x24\x3c\x24\x24\xe4\xdc\x18\x10\x54\x38\xee\x38\x54\x10\x00"
"\x10\x10\x10\x7c\x10\x10\x10\x10\x10\x10\x10\xff\x00\x00\x00\x00"
"\x00\x00\x00\xff\x10\x10\x10\x10\x10\x10\x10\xf0\x10\x10\x10\x10"
"\x10\x10\x10\x1f\x10\x10\x10\x10\x10\x10\x10\xff\x10\x10\x10\x10"
"\x10\x10\x10\x10\x10\x10\x10\x10\x00\x00\x00\xff\x00\x00\x00\x00"
"\x00\x00\x00\x1f\x10\x10\x10\x10\x00\x00\x00\xf0\x10\x10\x10\x10"
"\x10\x10\x10\x1f\x00\x00\x00\x00\x10\x10\x10\xf0\x00\x00\x00\x00"
"\x81\x42\x24\x18\x18\x24\x42\x81\x01\x02\x04\x08\x10\x20\x40\x80"
"\x80\x40\x20\x10\x08\x04\x02\x01\x00\x10\x10\xff\x10\x10\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x20\x20\x20\x20\x00\x00\x20\x00"
"\x50\x50\x50\x00\x00\x00\x00\x00\x50\x50\xf8\x50\xf8\x50\x50\x00"
"\x20\x78\xa0\x70\x28\xf0\x20\x00\xc0\xc8\x10\x20\x40\x98\x18\x00"
"\x40\xa0\x40\xa8\x90\x98\x60\x00\x10\x20\x40\x00\x00\x00\x00\x00"
"\x10\x20\x40\x40\x40\x20\x10\x00\x40\x20\x10\x10\x10\x20\x40\x00"
"\x20\xa8\x70\x20\x70\xa8\x20\x00\x00\x20\x20\xf8\x20\x20\x00\x00"
"\x00\x00\x00\x00\x00\x20\x20\x40\x00\x00\x00\x78\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x60\x60\x00\x00\x00\x08\x10\x20\x40\x80\x00"
"\x70\x88\x98\xa8\xc8\x88\x70\x00\x20\x60\xa0\x20\x20\x20\xf8\x00"
"\x70\x88\x08\x10\x60\x80\xf8\x00\x70\x88\x08\x30\x08\x88\x70\x00"
"\x10\x30\x50\x90\xf8\x10\x10\x00\xf8\x80\xe0\x10\x08\x10\xe0\x00"
"\x30\x40\x80\xf0\x88\x88\x70\x00\xf8\x88\x10\x20\x20\x20\x20\x00"
"\x70\x88\x88\x70\x88\x88\x70\x00\x70\x88\x88\x78\x08\x10\x60\x00"
"\x00\x00\x20\x00\x00\x20\x00\x00\x00\x00\x20\x00\x00\x20\x20\x40"
"\x18\x30\x60\xc0\x60\x30\x18\x00\x00\x00\xf8\x00\xf8\x00\x00\x00"
"\xc0\x60\x30\x18\x30\x60\xc0\x00\x70\x88\x08\x10\x20\x00\x20\x00"
"\x70\x88\x08\x68\xa8\xa8\x70\x00\x20\x50\x88\x88\xf8\x88\x88\x00"
"\xf0\x48\x48\x70\x48\x48\xf0\x00\x30\x48\x80\x80\x80\x48\x30\x00"
"\xe0\x50\x48\x48\x48\x50\xe0\x00\xf8\x80\x80\xf0\x80\x80\xf8\x00"
"\xf8\x80\x80\xf0\x80\x80\x80\x00\x70\x88\x80\xb8\x88\x88\x70\x00"
"\x88\x88\x88\xf8\x88\x88\x88\x00\x70\x20\x20\x20\x20\x20\x70\x00"
"\x38\x10\x10\x10\x90\x90\x60\x00\x88\x90\xa0\xc0\xa0\x90\x88\x00"
"\x80\x80\x80\x80\x80\x80\xf8\x00\x88\xd8\xa8\xa8\x88\x88\x88\x00"
"\x88\xc8\xc8\xa8\x98\x98\x88\x00\x70\x88\x88\x88\x88\x88\x70\x00"
"\xf0\x88\x88\xf0\x80\x80\x80\x00\x70\x88\x88\x88\xa8\x90\x68\x00"
"\xf0\x88\x88\xf0\xa0\x90\x88\x00\x70\x88\x80\x70\x08\x88\x70\x00"
"\xf8\x20\x20\x20\x20\x20\x20\x00\x88\x88\x88\x88\x88\x88\x70\x00"
"\x88\x88\x88\x88\x50\x50\x20\x00\x88\x88\x88\xa8\xa8\xd8\x88\x00"
"\x88\x88\x50\x20\x50\x88\x88\x00\x88\x88\x88\x70\x20\x20\x20\x00"
"\xf8\x08\x10\x20\x40\x80\xf8\x00\x70\x40\x40\x40\x40\x40\x70\x00"
"\x00\x00\x80\x40\x20\x10\x08\x00\x70\x10\x10\x10\x10\x10\x70\x00"
"\x20\x50\x88\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x00"
"\x40\x20\x10\x00\x00\x00\x00\x00\x00\x00\x70\x08\x78\x88\x78\x00"
"\x80\x80\xb0\xc8\x88\xc8\xb0\x00\x00\x00\x70\x88\x80\x88\x70\x00"
"\x08\x08\x68\x98\x88\x98\x68\x00\x00\x00\x70\x88\xf8\x80\x70\x00"
"\x10\x28\x20\xf8\x20\x20\x20\x00\x00\x00\x68\x98\x98\x68\x08\x70"
"\x80\x80\xf0\x88\x88\x88\x88\x00\x20\x00\x60\x20\x20\x20\x70\x00"
"\x10\x00\x30\x10\x10\x10\x90\x60\x40\x40\x48\x50\x60\x50\x48\x00"
"\x60\x20\x20\x20\x20\x20\x70\x00\x00\x00\xd0\xa8\xa8\xa8\xa8\x00"
"\x00\x00\xb0\xc8\x88\x88\x88\x00\x00\x00\x70\x88\x88\x88\x70\x00"
"\x00\x00\xb0\xc8\xc8\xb0\x80\x80\x00\x00\x68\x98\x98\x68\x08\x08"
"\x00\x00\xb0\xc8\x80\x80\x80\x00\x00\x00\x78\x80\xf0\x08\xf0\x00"
"\x40\x40\xf0\x40\x40\x48\x30\x00\x00\x00\x90\x90\x90\x90\x68\x00"
"\x00\x00\x88\x88\x88\x50\x20\x00\x00\x00\x88\xa8\xa8\xa8\x50\x00"
"\x00\x00\x88\x50\x20\x50\x88\x00\x00\x00\x88\x88\x98\x68\x08\x70"
"\x00\x00\xf8\x10\x20\x40\xf8\x00\x18\x20\x20\x40\x20\x20\x18\x00"
"\x20\x20\x20\x00\x20\x20\x20\x00\xc0\x20\x20\x10\x20\x20\xc0\x00"
"\x40\xa8\x10\x00\x00\x00\x00\x00\x00\x00\x20\x50\xf8\x00\x00\x00"
"\x70\x88\x80\x80\x88\x70\x20\x60\x90\x00\x00\x90\x90\x90\x68\x00"
"\x10\x20\x70\x88\xf8\x80\x70\x00\x20\x50\x70\x08\x78\x88\x78\x00"
"\x48\x00\x70\x08\x78\x88\x78\x00\x20\x10\x70\x08\x78\x88\x78\x00"
"\x20\x00\x70\x08\x78\x88\x78\x00\x00\x70\x80\x80\x80\x70\x10\x60"
"\x20\x50\x70\x88\xf8\x80\x70\x00\x50\x00\x70\x88\xf8\x80\x70\x00"
"\x20\x10\x70\x88\xf8\x80\x70\x00\x50\x00\x00\x60\x20\x20\x70\x00"
"\x20\x50\x00\x60\x20\x20\x70\x00\x40\x20\x00\x60\x20\x20\x70\x00"
"\x50\x00\x20\x50\x88\xf8\x88\x00\x20\x00\x20\x50\x88\xf8\x88\x00"
"\x10\x20\xf8\x80\xf0\x80\xf8\x00\x00\x00\x6c\x12\x7e\x90\x6e\x00"
"\x3e\x50\x90\x9c\xf0\x90\x9e\x00\x60\x90\x00\x60\x90\x90\x60\x00"
"\x90\x00\x00\x60\x90\x90\x60\x00\x40\x20\x00\x60\x90\x90\x60\x00"
"\x40\xa0\x00\xa0\xa0\xa0\x50\x00\x40\x20\x00\xa0\xa0\xa0\x50\x00"
"\x90\x00\x90\x90\xb0\x50\x10\xe0\x50\x00\x70\x88\x88\x88\x70\x00"
"\x50\x00\x88\x88\x88\x88\x70\x00\x20\x20\x78\x80\x80\x78\x20\x20"
"\x18\x24\x20\xf8\x20\xe2\x5c\x00\x88\x50\x20\xf8\x20\xf8\x20\x00"
"\xc0\xa0\xa0\xc8\x9c\x88\x88\x8c\x18\x20\x20\xf8\x20\x20\x20\x40"
"\x10\x20\x70\x08\x78\x88\x78\x00\x10\x20\x00\x60\x20\x20\x70\x00"
"\x20\x40\x00\x60\x90\x90\x60\x00\x20\x40\x00\x90\x90\x90\x68\x00"
"\x50\xa0\x00\xa0\xd0\x90\x90\x00\x28\x50\x00\xc8\xa8\x98\x88\x00"
"\x00\x70\x08\x78\x88\x78\x00\xf8\x00\x60\x90\x90\x90\x60\x00\xf0"
"\x20\x00\x20\x40\x80\x88\x70\x00\x00\x00\x00\xf8\x80\x80\x00\x00"
"\x00\x00\x00\xf8\x08\x08\x00\x00\x84\x88\x90\xa8\x54\x84\x08\x1c"
"\x84\x88\x90\xa8\x58\xa8\x3c\x08\x20\x00\x00\x20\x20\x20\x20\x00"
"\x00\x00\x24\x48\x90\x48\x24\x00\x00\x00\x90\x48\x24\x48\x90\x00"
"\x28\x50\x20\x50\x88\xf8\x88\x00\x28\x50\x70\x08\x78\x88\x78\x00"
"\x28\x50\x00\x70\x20\x20\x70\x00\x28\x50\x00\x20\x20\x20\x70\x00"
"\x28\x50\x00\x70\x88\x88\x70\x00\x50\xa0\x00\x60\x90\x90\x60\x00"
"\x28\x50\x00\x88\x88\x88\x70\x00\x50\xa0\x00\xa0\xa0\xa0\x50\x00"
"\xfc\x48\x48\x48\xe8\x08\x50\x20\x00\x50\x00\x50\x50\x50\x10\x20"
"\xc0\x44\xc8\x54\xec\x54\x9e\x04\x10\xa8\x40\x00\x00\x00\x00\x00"
"\x00\x20\x50\x88\x50\x20\x00\x00\x88\x10\x20\x40\x80\x28\x00\x00"
"\x7c\xa8\xa8\x68\x28\x28\x28\x00\x38\x40\x30\x48\x48\x30\x08\x70"
"\x00\x00\x00\x00\x00\x00\xff\xff\xf0\xf0\xf0\xf0\x0f\x0f\x0f\x0f"
"\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x3c\x3c\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00"
"\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x0f\x0f\x0f\x0f\xf0\xf0\xf0\xf0"
"\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\x03\x03\x03\x03\x03\x03\x03\x03"
"\x3f\x3f\x3f\x3f\x3f\x3f\x3f\x3f\x11\x22\x44\x88\x11\x22\x44\x88"
"\x88\x44\x22\x11\x88\x44\x22\x11\xfe\x7c\x38\x10\x00\x00\x00\x00"
"\x00\x00\x00\x00\x10\x38\x7c\xfe\x80\xc0\xe0\xf0\xe0\xc0\x80\x00"
"\x01\x03\x07\x0f\x07\x03\x01\x00\xff\x7e\x3c\x18\x18\x3c\x7e\xff"
"\x81\xc3\xe7\xff\xff\xe7\xc3\x81\xf0\xf0\xf0\xf0\x00\x00\x00\x00"
"\x00\x00\x00\x00\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x00\x00\x00\x00"
"\x00\x00\x00\x00\xf0\xf0\xf0\xf0\x33\x33\xcc\xcc\x33\x33\xcc\xcc"
"\x00\x20\x20\x50\x50\x88\xf8\x00\x20\x20\x70\x20\x70\x20\x20\x00"
"\x00\x00\x00\x50\x88\xa8\x50\x00\xff\xff\xff\xff\xff\xff\xff\xff"
"\x00\x00\x00\x00\xff\xff\xff\xff\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0"
"\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\xff\xff\xff\xff\x00\x00\x00\x00"
"\x00\x00\x68\x90\x90\x90\x68\x00\x30\x48\x48\x70\x48\x48\x70\xc0"
"\xf8\x88\x80\x80\x80\x80\x80\x00\xf8\x50\x50\x50\x50\x50\x98\x00"
"\xf8\x88\x40\x20\x40\x88\xf8\x00\x00\x00\x78\x90\x90\x90\x60\x00"
"\x00\x50\x50\x50\x50\x68\x80\x80\x00\x50\xa0\x20\x20\x20\x20\x00"
"\xf8\x20\x70\xa8\xa8\x70\x20\xf8\x20\x50\x88\xf8\x88\x50\x20\x00"
"\x70\x88\x88\x88\x50\x50\xd8\x00\x30\x40\x40\x20\x50\x50\x50\x20"
"\x00\x00\x00\x50\xa8\xa8\x50\x00\x08\x70\xa8\xa8\xa8\x70\x80\x00"
"\x38\x40\x80\xf8\x80\x40\x38\x00\x70\x88\x88\x88\x88\x88\x88\x00"
"\x00\xf8\x00\xf8\x00\xf8\x00\x00\x20\x20\xf8\x20\x20\x00\xf8\x00"
"\xc0\x30\x08\x30\xc0\x00\xf8\x00\x18\x60\x80\x60\x18\x00\xf8\x00"
"\x10\x28\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\xa0\x40"
"\x00\x20\x00\xf8\x00\x20\x00\x00\x00\x50\xa0\x00\x50\xa0\x00\x00"
"\x00\x18\x24\x24\x18\x00\x00\x00\x00\x30\x78\x78\x30\x00\x00\x00"
"\x00\x00\x00\x00\x30\x00\x00\x00\x3e\x20\x20\x20\xa0\x60\x20\x00"
"\xa0\x50\x50\x50\x00\x00\x00\x00\x40\xa0\x20\x40\xe0\x00\x00\x00"
"\x00\x38\x38\x38\x38\x38\x38\x00\x00\x00\x00\x00\x00\x00\x00";





================================================
FILE: dump_partials/dump_partials.h
================================================
/* Copyright (C) 2017 Yifan Lu
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#ifndef DUMP_PARTIALS_H__
#define DUMP_PARTIALS_H__

#define AES_BLOCK_SIZE 16

typedef struct {
    char key_seed[0x20];
    int key_size;
    int dmac5_cmd;
    void *output;
} args_t;

#endif


================================================
FILE: dump_partials/kernel.c
================================================
/* Copyright (C) 2017 Yifan Lu
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#include <psp2kern/kernel/cpu.h>
#include <psp2kern/kernel/modulemgr.h>
#include <psp2kern/kernel/sysmem.h>
#include <psp2kern/sblauthmgr.h>
#include "dump_partials.h"

#define printf(...)

#define KEY_SLOT (1)

int memset(void *ptr, int ch, size_t len) {
    for (int i = 0; i < len; i += 4) {
      *(uint32_t *)(ptr + i) = ch;
    }
}

void _start() __attribute__ ((weak, alias ("module_start")));
int module_start(SceSize argc, const void *pargs) {
    args_t *args = (args_t *)pargs;

    if (argc != sizeof(*args)) {
      printf("invalid arguments\n");
      return SCE_KERNEL_START_FAILED;
    }
                                      
    int wrk = ksceKernelAllocMemBlock("wrk", 0x1050D006, 0x2000, 0);
    void *base;
    ksceKernelGetMemBlockBase(wrk, &base);
    uint8_t *src = (uint8_t*)base;
    uint8_t *dst = (uint8_t*)base + 0x1000;
    memset(base, 0, 0x2000);
    
    // Memory map the dmac5 and keyring devices
    SceKernelAllocMemBlockKernelOpt opt = {0};
    opt.size = sizeof(opt);
    opt.attr = 2;
    opt.paddr = 0xE0410000;
    int block = ksceKernelAllocMemBlock("SceDmacmgrDmac5Reg", 0x20100206, 0x1000, &opt);
    volatile uint32_t *device;
    ksceKernelGetMemBlockBase(block, (void **)&device);
    printf("dmac5 block: 0x%x | va: 0x%x\n", block, device);
    opt.size = sizeof(opt);
    opt.attr = 2;
    opt.paddr = 0xE04E0000;
    int block2 = ksceKernelAllocMemBlock("SceSblDMAC5DmacKRBase", 0x20100206, 0x1000, &opt);
    volatile uint32_t *keyring;
    ksceKernelGetMemBlockBase(block2, (void **)&keyring);
    printf("keyring block: 0x%x | va: 0x%x\n", block2, keyring);
    
    printf("start\n");
    uintptr_t src_pa, dst_pa, key_pa;
    ksceKernelGetPaddr(src, &src_pa);
    ksceKernelGetPaddr(dst, &dst_pa);
    
    #define COMMIT_WAIT device[10] = device[10]; device[7] = 1; while(device[9] & 1){};

    for (int i = 0; i < args->key_size / 4; i++) {
      // clear buffer
      memset(base, 0, 0x2000);

      // set key
      ksceSblAuthMgrClearDmac5Key(KEY_SLOT, 0);
      ksceSblAuthMgrSetDmac5Key(args->key_seed, args->key_size, KEY_SLOT, 0x10000);
      
      // clear all except one dword
      for (int j = 0; j < args->key_size / 4; j++) {
        if (i != j) {
          keyring[(8*KEY_SLOT)+j] = 0;
        }
      }

      // do encryption on zeros
      __asm__ ("dmb sy");
      __asm__ ("dsb sy");
      ksceKernelCpuDcacheAndL2WritebackRange(dst, 0x1000); 
      ksceKernelCpuDcacheAndL2WritebackRange(src, 0x1000);
      device[0] = (int)src_pa;
      device[1] = (int)dst_pa;
      device[2] = 0x10; // len
      device[3] = args->dmac5_cmd;
      device[4] = KEY_SLOT;
      device[5] = 0;
      // device[8] = 0;
      device[11] = 0xE070;
      device[12] = 0x700070;
      COMMIT_WAIT;
      __asm__ ("dmb sy");
      __asm__ ("dsb sy");     
      ksceKernelCpuDcacheAndL2InvalidateRange(dst, 0x1000);

      // write result
      ksceKernelMemcpyKernelToUser((uintptr_t)args->output + i * AES_BLOCK_SIZE, dst, AES_BLOCK_SIZE);
    }   

    #undef COMMIT_WAIT
    ksceKernelFreeMemBlock(block);
    ksceKernelFreeMemBlock(block2);
    ksceKernelFreeMemBlock(wrk);

    return SCE_KERNEL_START_NO_RESIDENT;
}


================================================
FILE: dump_partials/kernel.yml
================================================
dump_partials_helper:
  attributes: 0
  version:
    major: 1
    minor: 1
  main:
    start: module_start


================================================
FILE: dump_partials/main.c
================================================
/* Copyright (C) 2017 Yifan Lu
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#include <psp2/kernel/processmgr.h>
#include <psp2/io/fcntl.h>
#include <psp2/ctrl.h>
#include <psp2/kernel/processmgr.h>
#include <psp2/message_dialog.h>
#include <psp2/ime_dialog.h>
#include <psp2/display.h>
#include <psp2/apputil.h>
#include <psp2/gxm.h>
#include <taihen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../sha256.h"
#include "dump_partials.h"
#include "debugScreen.h"

#define MAGIC_WORDS "Sri Jayewardenepura Kotte"

static void get_key_seed(const char aid[8], char hash[0x20]) {
    SHA256_CTX ctx;

    sha256_init(&ctx);
    sha256_update(&ctx, (void *)aid, 8);
    sha256_update(&ctx, MAGIC_WORDS, sizeof(MAGIC_WORDS)-1);
    sha256_final(&ctx, hash);
}

#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
#define DISPLAY_WIDTH           960
#define DISPLAY_HEIGHT          544
#define DISPLAY_STRIDE_IN_PIXELS    1024
#define DISPLAY_BUFFER_COUNT        2
#define DISPLAY_MAX_PENDING_SWAPS   1

typedef struct{
    void*data;
    SceGxmSyncObject*sync;
    SceGxmColorSurface surf;
    SceUID uid;
}displayBuffer;

unsigned int backBufferIndex = 0;
unsigned int frontBufferIndex = 0;
/* could be converted as struct displayBuffer[] */
displayBuffer dbuf[DISPLAY_BUFFER_COUNT];

void *dram_alloc(unsigned int size, SceUID *uid){
    void *mem;
    *uid = sceKernelAllocMemBlock("gpu_mem", SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, ALIGN(size,256*1024), NULL);
    sceKernelGetMemBlockBase(*uid, &mem);
    sceGxmMapMemory(mem, ALIGN(size,256*1024), SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE);
    return mem;
}
void gxm_vsync_cb(const void *callback_data){
    sceDisplaySetFrameBuf(&(SceDisplayFrameBuf){sizeof(SceDisplayFrameBuf),
        *((void **)callback_data),DISPLAY_STRIDE_IN_PIXELS, 0,
        DISPLAY_WIDTH,DISPLAY_HEIGHT}, SCE_DISPLAY_SETBUF_NEXTFRAME);
}
void gxm_init(){
    sceGxmInitialize(&(SceGxmInitializeParams){0,DISPLAY_MAX_PENDING_SWAPS,gxm_vsync_cb,sizeof(void *),SCE_GXM_DEFAULT_PARAMETER_BUFFER_SIZE});
    unsigned int i;
    for (i = 0; i < DISPLAY_BUFFER_COUNT; i++) {
        dbuf[i].data = dram_alloc(4*DISPLAY_STRIDE_IN_PIXELS*DISPLAY_HEIGHT, &dbuf[i].uid);
        sceGxmColorSurfaceInit(&dbuf[i].surf,SCE_GXM_COLOR_FORMAT_A8B8G8R8,SCE_GXM_COLOR_SURFACE_LINEAR,SCE_GXM_COLOR_SURFACE_SCALE_NONE,SCE_GXM_OUTPUT_REGISTER_SIZE_32BIT,DISPLAY_WIDTH,DISPLAY_HEIGHT,DISPLAY_STRIDE_IN_PIXELS,dbuf[i].data);
        sceGxmSyncObjectCreate(&dbuf[i].sync);
    }
}
void gxm_swap(){
    sceGxmPadHeartbeat(&dbuf[backBufferIndex].surf, dbuf[backBufferIndex].sync);
    sceGxmDisplayQueueAddEntry(dbuf[frontBufferIndex].sync, dbuf[backBufferIndex].sync, &dbuf[backBufferIndex].data);
    frontBufferIndex = backBufferIndex;
    backBufferIndex = (backBufferIndex + 1) % DISPLAY_BUFFER_COUNT;
}
void gxm_term(){
    for (int i = 0; i < DISPLAY_BUFFER_COUNT; i++) {
        sceGxmUnmapMemory(dbuf[i].data);
        sceKernelFreeMemBlock(dbuf[i].uid);
    }
    sceGxmTerminate();
}

int enter_aid(char aid[8]) {
    int ret;
    uint16_t aid_user[16 + 1];

    gxm_init();
    if (sceImeDialogInit(&(SceImeDialogParam){.title=u"Enter your AID (CMA backup folder name)", sizeof(aid_user)-1, u"", aid_user}) < 0) {
        psvDebugScreenPrintf("show dialog failed!\n");
        return -1;
    }

    while (1) {
        ret = sceImeDialogGetStatus();
        if (ret < 0) {
            break;
        } else if (ret == SCE_COMMON_DIALOG_STATUS_FINISHED) {
            SceImeDialogResult result={};
            sceImeDialogGetResult(&result);
            if (result.button != SCE_IME_DIALOG_BUTTON_CLOSE) {
                for (int i = 0; i < 8; i++) {
                    char tmp[3];
                    tmp[0] = aid_user[2*i];
                    tmp[1] = aid_user[2*i+1];
                    tmp[2] = '\0';
                    aid[i] = strtol(tmp, NULL, 16);
                }
            }
            break;
        }

        sceCommonDialogUpdate(&(SceCommonDialogUpdateParam){{
            NULL,dbuf[backBufferIndex].data,0,0,
            DISPLAY_WIDTH,DISPLAY_HEIGHT,DISPLAY_STRIDE_IN_PIXELS},
            dbuf[backBufferIndex].sync});

        gxm_swap();
        sceDisplayWaitVblankStart();
    }
    gxm_term();
    sceImeDialogTerm();
    return ret;
}

int get_aid_from_ux0(char aid[8]) {
    int fd;
    char buf[1024];
    char *at;

    memset(buf, 0, sizeof(buf));
    fd = sceIoOpen("ux0:id.dat", SCE_O_RDONLY, 0);
    if (fd >= 0) {
        sceIoRead(fd, buf, sizeof(buf)-1);
        at = strstr(buf, "\r\nAID=");
        if (at != NULL) {
            for (int i = 0; i < 8; i++) {
                char tmp[3];
                *(uint16_t *)tmp = *(uint16_t *)&at[6 + 2*i];
                tmp[2] = '\0';
                aid[8-i-1] = strtol(tmp, NULL, 16);
            }
        }
        sceIoClose(fd);
    }

    return fd;
}

const char *aid_string(const char aid[8], char *buf) {
    for (int i = 0; i < 8; i++) {
        snprintf(buf + 2*i, 3, "%02X", (unsigned char)aid[i]);
    }
    return buf;
}

int dump_partials(char aid[8]) {
    args_t arg;
    char path[256];
    char buffer[8 * AES_BLOCK_SIZE];
    char tmp[16+1];
    int ret, res;

    get_key_seed(aid, arg.key_seed);
    arg.key_size = 0x20;
    arg.dmac5_cmd = 0x301;
    arg.output = buffer;

    snprintf(path, 256, "ux0:data/partials-%s.bin", aid_string(aid, tmp));

    ret = taiLoadKernelModule("ux0:app/DUMP0900D/kernel.skprx", 0, NULL);
    if (ret < 0) {
        psvDebugScreenPrintf("Kernel load: %x\n", ret);
    } else {
        ret = taiStartKernelModule(ret, sizeof(arg), &arg, 0, NULL, &res);
    }

    psvDebugScreenPrintf("Kernel start: %x, %x\n", ret, res);

    if (ret >= 0) {
        int fd = sceIoOpen(path, SCE_O_WRONLY | SCE_O_CREAT | SCE_O_TRUNC, 0x6);
        sceIoWrite(fd, buffer, sizeof(buffer));
        sceIoClose(fd);

        psvDebugScreenPrintf("Partials written to %s.\n\n", path);
    }

    return ret;
}

int main(int argc, char *argv[]) {
    char aid[8];
    char tmp[16+1];

    psvDebugScreenInit();
    sceAppUtilInit(&(SceAppUtilInitParam){}, &(SceAppUtilBootParam){});
    sceCommonDialogSetConfigParam(&(SceCommonDialogConfigParam){});

    psvDebugScreenPrintf("Started!\n\n");
    memset(aid, 0, sizeof(aid));
    get_aid_from_ux0(aid);

    while (1) {
        psvDebugScreenPrintf("Your AID is: %s\n\n", aid_string(aid, tmp));
        psvDebugScreenPrintf("Press Square to enter a new AID to dump.\nPress X to dump partials.\nPress Circle to exit.\n\n");

        SceCtrlData ctrl;
        do {
            sceCtrlPeekBufferPositive(0, &ctrl, 1);
        } while ((ctrl.buttons & (SCE_CTRL_SQUARE | SCE_CTRL_CROSS | SCE_CTRL_CIRCLE)) == 0);

        if (ctrl.buttons & SCE_CTRL_CIRCLE) {
            break;
        } else if (ctrl.buttons & SCE_CTRL_SQUARE) {
            enter_aid(aid);
            psvDebugScreenSet();
        } else if (ctrl.buttons & SCE_CTRL_CROSS) {
            dump_partials(aid);
            sceKernelDelayThread(1*1000*1000);
        }
    }

    return 0;
}


================================================
FILE: endian-utils.h
================================================
#ifndef ENDIAN_UTILS_H
#define ENDIAN_UTILS_H

#ifndef __MINGW32__

#ifdef __APPLE__

#include <machine/endian.h>
#include <libkern/OSByteOrder.h>

#define htobe16(x) OSSwapHostToBigInt16(x)
#define htole16(x) OSSwapHostToLittleInt16(x)
#define be16toh(x) OSSwapBigToHostInt16(x)
#define le16toh(x) OSSwapLittleToHostInt16(x)
 
#define htobe32(x) OSSwapHostToBigInt32(x)
#define htole32(x) OSSwapHostToLittleInt32(x)
#define be32toh(x) OSSwapBigToHostInt32(x)
#define le32toh(x) OSSwapLittleToHostInt32(x)
 
#define htobe64(x) OSSwapHostToBigInt64(x)
#define htole64(x) OSSwapHostToLittleInt64(x)
#define be64toh(x) OSSwapBigToHostInt64(x)
#define le64toh(x) OSSwapLittleToHostInt64(x)

#define __BYTE_ORDER    BYTE_ORDER
#define __BIG_ENDIAN    BIG_ENDIAN
#define __LITTLE_ENDIAN LITTLE_ENDIAN
#define __PDP_ENDIAN    PDP_ENDIAN

#else

#ifdef USE_BUNDLED_ENDIAN_H

// Taken from FreeBSD
#define	bswap16(x) (uint16_t) \
	((x >> 8) | (x << 8))

#define	bswap32(x) (uint32_t) \
	((x >> 24) | ((x >> 8) & 0xff00) | ((x << 8) & 0xff0000) | (x << 24))

#define	bswap64(x) (uint64_t) \
	((x >> 56) | ((x >> 40) & 0xff00) | ((x >> 24) & 0xff0000) | \
	((x >> 8) & 0xff000000) | ((x << 8) & ((uint64_t)0xff << 32)) | \
	((x << 24) & ((uint64_t)0xff << 40)) | \
	((x << 40) & ((uint64_t)0xff << 48)) | ((x << 56)))

/*
 * Host to big endian, host to little endian, big endian to host, and little
 * endian to host byte order functions as detailed in byteorder(9).
 */
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define	htobe16(x)	bswap16((uint16_t)(x))
#define	htobe32(x)	bswap32((uint32_t)(x))
#define	htobe64(x)	bswap64((uint64_t)(x))
#define	htole16(x)	((uint16_t)(x))
#define	htole32(x)	((uint32_t)(x))
#define	htole64(x)	((uint64_t)(x))

#define	be16toh(x)	bswap16((uint16_t)(x))
#define	be32toh(x)	bswap32((uint32_t)(x))
#define	be64toh(x)	bswap64((uint64_t)(x))
#define	le16toh(x)	((uint16_t)(x))
#define	le32toh(x)	((uint32_t)(x))
#define	le64toh(x)	((uint64_t)(x))
#else /* _BYTE_ORDER != _LITTLE_ENDIAN */
#define	htobe16(x)	((uint16_t)(x))
#define	htobe32(x)	((uint32_t)(x))
#define	htobe64(x)	((uint64_t)(x))
#define	htole16(x)	bswap16((uint16_t)(x))
#define	htole32(x)	bswap32((uint32_t)(x))
#define	htole64(x)	bswap64((uint64_t)(x))

#define	be16toh(x)	((uint16_t)(x))
#define	be32toh(x)	((uint32_t)(x))
#define	be64toh(x)	((uint64_t)(x))
#define	le16toh(x)	bswap16((uint16_t)(x))
#define	le32toh(x)	bswap32((uint32_t)(x))
#define	le64toh(x)	bswap64((uint64_t)(x))
#endif /* _BYTE_ORDER == _LITTLE_ENDIAN */

#else
#include <endian.h>
#endif

#endif

#else
/* Todo: make these definitions work on BE systems! */
# define htole32(value) (value)
# define htole16(value) (value)

#define le32toh(value) htole32(value)
#define le16toh(value) htole16(value)
#endif

#endif


================================================
FILE: psvimg-create.c
================================================
/* Copyright (C) 2017 Yifan Lu
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include "backup.h"
#include "psvimg.h"
#include "utils.h"

#define MAX_PATH_LEN 1024

static int is_backup(const char *title) {
  if (strcmp(title, "app") == 0 ||
      strcmp(title, "patch") == 0 ||
      strcmp(title, "addcont") == 0 ||
      strcmp(title, "savedata") == 0 ||
      strcmp(title, "appmeta") == 0 ||
      strcmp(title, "license") == 0 ||
      strcmp(title, "game") == 0) {
    return 0;
  } else {
    return 1;
  }
}

int main(int argc, const char *argv[]) {
  args_t args1, args2;
  struct stat st;
  int fds[4];
  char path[MAX_PATH_LEN];
  int mfd;
  PsvMd_t md;
  pid_t pid;
  int status;

  if (argc < 7) {
    fprintf(stderr, "usage: psvimg-create [-m metadata|-n name] -K key inputdir outputdir\n");
    fprintf(stderr, "  specify either a decrypted metadata file as a template or\n");
    fprintf(stderr, "  a name and other metadata fields will retain default values\n");
    return 1;
  }

  // TODO: support more types
  if (strcmp(argv[1], "-m") == 0) {
    mfd = open(argv[2], O_RDONLY);
    if (mfd < 0) {
      perror("metadata");
      return 1;
    }
    if (read_block(mfd, &md, sizeof(md) - sizeof(md.add_data)) < sizeof(md) - sizeof(md.add_data)) {
      fprintf(stderr, "invalid metadata size\n");
      return 1;
    }
    if (md.type != 2) {
      fprintf(stderr, "metadata type not supported\n");
      close(mfd);
      return 1;
    }
    if (read_block(mfd, &md.add_data, sizeof(md.add_data)) < sizeof(md.add_data)) {
      fprintf(stderr, "invalid metadata size\n");
      close(mfd);
      return 1;
    }
    close(mfd);
  } else if (strcmp(argv[1], "-n") == 0) {
    memset(&md, 0, sizeof(md));
    strncpy(md.name, argv[2], sizeof(md.name));
    md.magic = is_backup(md.name) ? PSVMD_BACKUP_MAGIC : PSVMD_CONTENT_MAGIC;
    md.type = 2;
    md.version = 2;
    md.add_data = 1;
    srand(time(NULL));
    for (int i = 0; i < sizeof(md.iv); i++) {
      md.iv[i] = rand() % 0xFF;
    }
  } else {
    fprintf(stderr, "you must specify either -m or -n!\n");
    return 1;
  }

  if (pipe(fds) < 0) {
    perror("pipe 1");
    return 1;
  }

  args1.in = 0;
  args1.prefix = argv[5];
  args1.content_size = 0;
  args1.out = fds[1];

  if (stat(argv[6], &st) < 0) {
    mkdir(argv[6], 0700);
  }

  snprintf(path, sizeof(path), "%s/%s.psvimg", argv[6], md.name);

  args2.out = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
  if (args2.out < 0) {
    perror("psvimg output");
    return 1;
  }
  args2.in = fds[0];

  if (parse_key(argv[4], args2.key) < 0) {
    fprintf(stderr, "invalid key\n");
    return 1;
  }

  memcpy(args2.iv, md.iv, sizeof(args2.iv));

  if ((pid = fork()) == 0) {
    close(args1.in);
    close(args1.out);
    encrypt_thread(&args2);
    return 0;
  } else if (pid > 0) {
    close(args2.in);
    close(args2.out);
    pack_thread(&args1);
  } else {
    perror("fork");
    return 1;
  }

  if (waitpid(pid, &status, 0) < 0) {
    perror("waitpid");
    return 1;
  }

  if (!WIFEXITED(status)) {
    fprintf(stderr, "child process returned error\n");
    return 1;
  }

  if (stat(path, &st) < 0) {
    perror("stat");
    return 1;
  }
  fprintf(stderr, "created %s (size: %llx, content size: %zx)\n", path, st.st_size, args1.content_size);
  md.total_size = args1.content_size;
  md.psvimg_size = st.st_size;

  // now create the psvmd
  snprintf(path, sizeof(path), "%s/%s.psvmd", argv[6], md.name);

  if (pipe(fds) < 0) {
    perror("pipe 2");
    return 1;
  }
  if (pipe(&fds[2]) < 0) {
    perror("pipe 3");
    return 1;
  }

  args1.in = fds[0];
  args1.out = fds[3];

  args2.in = fds[2];
  args2.out = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
  if (args2.out < 0) {
    perror("psvmd output");
    return 1;
  }
  for (int i = 0; i < sizeof(md.iv); i++) {
    args2.iv[i] = rand() % 0xFF;
  }

  if ((pid = fork()) == 0) {
    close(args1.in);
    close(args1.out);
    if ((pid = fork()) == 0) {
      close(args2.in);
      close(args2.out);
      write_block(fds[1], &md, sizeof(md));
      close(fds[1]);
      return 0;
    } else {
      close(fds[1]);
    }
    encrypt_thread(&args2);
    return 0;
  } else if (pid > 0) {
    close(fds[1]);
    close(args2.in);
    close(args2.out);
    compress_thread(&args1);
  } else {
    perror("fork");
    return 1;
  }

  if (waitpid(pid, &status, 0) < 0) {
    perror("waitpid");
    return 1;
  }

  if (!WIFEXITED(status)) {
    fprintf(stderr, "child process returned error\n");
    return 1;
  }
  
  fprintf(stderr, "created %s\n", path);

  // finally create the psvinf
  if (is_backup(md.name)) {
    snprintf(path, sizeof(path), "%s/%s.psvinf", argv[6], md.name);
    mfd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    write_block(mfd, md.name, strnlen(md.name, 64) + 1);
    close(mfd);
    fprintf(stderr, "created %s\n", path);
  }

  return 0;
}


================================================
FILE: psvimg-extract.c
================================================
/* Copyright (C) 2017 Yifan Lu
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "restore.h"
#include "utils.h"

int main(int argc, const char *argv[]) {
  args_t args1, args2;
  struct stat st;
  int fds[2];
  pid_t pid;
  int status;

  if (argc < 5) {
    fprintf(stderr, "usage: psvimg-extract -K key input.psvimg outputdir\n");
    return 1;
  }

  if (pipe(fds) < 0) {
    perror("pipe");
    return 1;
  }

  args1.in = open(argv[3], O_RDONLY);
  if (args1.in < 0) {
    perror("open");
    return 1;
  }
  args1.out = fds[1];
  args2.in = fds[0];
  args2.out = 0;

  if (parse_key(argv[2], args1.key) < 0) {
    fprintf(stderr, "invalid key\n");
    return 1;
  }

  args2.prefix = argv[4];
  if (stat(args2.prefix, &st) < 0) {
    mkdir(args2.prefix, 0700);
  }

  if ((pid = fork()) == 0) {
    close(args1.in);
    close(args1.out);
    unpack_thread(&args2);
    return 0;
  } else if (pid > 0) {
    close(args2.in);
    close(args2.out);
    decrypt_thread(&args1);
  } else {
    perror("fork");
    return 1;
  }

  if (waitpid(pid, &status, 0) < 0) {
    perror("waitpid");
    return 1;
  }

  if (!WIFEXITED(status)) {
    fprintf(stderr, "child process returned error\n");
    return 1;
  }

  fprintf(stderr, "all done.\n");

  return 0;
}


================================================
FILE: psvimg-keyfind.c
================================================
/* Copyright (C) 2017 Yifan Lu
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#define _GNU_SOURCE
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "crypto.h"
#include "utils.h"
#ifdef __linux__
#include <sched.h>
#endif

#ifndef HAVE_GCRYPT
#error "libgcrypt is required for psvimg-keyfind!"
#else
#include <gcrypt.h>
#endif

typedef struct {
  uint32_t found;
  uint32_t at;
} status_t;

#define PBSTR "============================================================"
#define PBWIDTH 50

#define KEY_LEN (32)
#define PROGRESS_UPDATE (0x10000)

static void print_progress(uint32_t total) {
  double percent;
  int lpad, rpad;
  percent = total * 1.0 / 0x100000000LL;
  lpad = (int) (percent * PBWIDTH);
  rpad = PBWIDTH - lpad;
  printf("\r%3d%% [%.*s%*s] (left: 0x%08x)", (int)(percent * 100), lpad, PBSTR, rpad, "", ~total);
  fflush(stdout);
}

static void print_key(uint32_t guess[KEY_LEN/sizeof(uint32_t)], int knownlen) {
  uint8_t *key = (uint8_t *)guess;
  for (int i = 0; i < KEY_LEN; i++) {
    if (i < knownlen) {
      printf("%02X", key[i]);
    } else {
      printf("**");
    }
  }
  printf("\n");
}

int find_key(int fd, uint32_t guess[KEY_LEN/sizeof(uint32_t)], int idx, uint8_t partial[AES_BLOCK_SIZE], uint32_t start, uint32_t end) {
  gcry_cipher_hd_t ctx;
  uint8_t zeros[AES_BLOCK_SIZE];
  uint8_t tmp[AES_BLOCK_SIZE];
  status_t st;

  memset(guess, 0, KEY_LEN);
  memset(zeros, 0, sizeof(zeros));
  st.found = 0;
  gcry_cipher_open(&ctx, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB, 0);
  for (st.at = start; st.at < end; st.at++) {
    guess[idx] = st.at;
    gcry_cipher_setkey(ctx, guess, KEY_LEN);
    gcry_cipher_encrypt(ctx, tmp, sizeof(tmp), zeros, sizeof(zeros));
    if (memcmp(tmp, partial, AES_BLOCK_SIZE) == 0) {
      st.found = 1;
      goto end;
    }
    if ((st.at % PROGRESS_UPDATE) == 0) {
      if (write_block(fd, &st, sizeof(st)) < 0) {
        goto end;
      }
    }
  }
  // last one
  guess[idx] = st.at;
  gcry_cipher_setkey(ctx, guess, KEY_LEN);
  gcry_cipher_encrypt(ctx, tmp, sizeof(tmp), zeros, sizeof(zeros));
  if (memcmp(tmp, partial, AES_BLOCK_SIZE) == 0) {
    st.found = 1;
  }
end:
  write_block(fd, &st, sizeof(st));
  close(fd);
  gcry_cipher_close(ctx);
  return st.found;
}

int dispatch_jobs(int num_jobs, uint32_t guess[KEY_LEN/sizeof(uint32_t)], int idx, uint8_t partial[AES_BLOCK_SIZE]) {
  struct pollfd fds[num_jobs];
  pid_t pids[num_jobs];
  uint32_t part_size;
  uint32_t at[num_jobs];

  part_size = 0x100000000LL / num_jobs;
  fprintf(stderr, "dispatching %d jobs with 0x%X tries per job.\n", num_jobs, part_size == 0 ? 0xFFFFFFFF : part_size);
  for (int i = 0; i < num_jobs; i++) {
    int tmp[2];
    if (pipe(tmp) < 0) {
      perror("pipe");
      return 0;
    }
    fds[i].fd = tmp[0];
    fds[i].events = POLLIN;
    at[i] = i * part_size;
    if ((pids[i] = fork()) < 0) {
      perror("fork");
      return 0;
    } else if (pids[i] == 0) {
      uint32_t end;
      close(tmp[0]);
      end = (i+1)*part_size-1;
      if (end < at[i]) {
        end = 0xFFFFFFFF;
      }
#ifdef __linux__
      cpu_set_t cpu;
      CPU_ZERO(&cpu);
      CPU_SET(i, &cpu);
      sched_setaffinity(0, sizeof(cpu), &cpu);
#endif
      exit(find_key(tmp[1], guess, idx, partial, at[i], end));
    } else {
      close(tmp[1]);
    }
  }

  // wait for completion
  status_t st;
  int status;
  uint32_t total_progress = 0;
  st.found = 0;
  printf("\n");
  while (num_jobs > 0) {
    if (poll(fds, num_jobs, -1) < 0) {
      perror("poll");
      break;
    }
    for (int i = 0; i < num_jobs; i++) {
      if (fds[i].revents != 0) {
        if (read_block(fds[i].fd, &st, sizeof(st)) < sizeof(st)) {
          close(fds[i].fd);
          waitpid(pids[i], &status, 0);
          fds[i].fd = fds[num_jobs-1].fd;
          pids[i] = pids[num_jobs-1];
          at[i] = at[num_jobs-1];
          num_jobs--;
        } else {
          total_progress += (st.at - at[i]);
          at[i] = st.at;

          print_progress(total_progress);

          if (st.found) {
            guess[idx] = st.at;
            break;
          }
        }
      }
    }
    if (st.found) {
      break;
    }
  }
  for (int i = 0; i < num_jobs; i++) {
    close(fds[i].fd);
    kill(pids[i], SIGTERM);
  }
  printf("\n");
  return st.found;
}

int main(int argc, const char *argv[]) {
  int threads;
  int fd;
  uint32_t guess[KEY_LEN/sizeof(uint32_t)];
  uint8_t partial[AES_BLOCK_SIZE];

  if (argc < 3) {
    fprintf(stderr, "usage: psvimg-keyfind threads partial\n");
    return 0;
  }

  threads = atoi(argv[1]);
  if (threads == 0) {
    threads = 1;
  }

  if ((fd = open(argv[2], O_RDONLY)) < 0) {
    perror("partial");
    return 0;
  }

  for (int i = 0; i < KEY_LEN/sizeof(uint32_t); i++) {
    if (read_block(fd, partial, sizeof(partial)) < sizeof(partial)) {
      fprintf(stderr, "invalid partial file\n");
      close(fd);
      return 0;
    }

    printf("Found %d/%d words, current knowledge:\n  ", i, (int)(KEY_LEN/sizeof(uint32_t)));
    print_key(guess, i*4);

    if (dispatch_jobs(threads, guess, i, partial) != 1) {
      fprintf(stderr, "brute force failed. are your partials valid?\n");
      close(fd);
      return 0;
    }
  }

  printf("Key found: ");
  print_key(guess, KEY_LEN);

  return 1;
}


================================================
FILE: psvimg.h
================================================
/* Copyright (C) 2017 Yifan Lu
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#ifndef PSVIMG_H__
#define PSVIMG_H__

#include <stdint.h>

#define PSVMD_CONTENT_MAGIC (0xFEE1900D)
#define PSVMD_BACKUP_MAGIC (0xFEE1900E)
#define PSVIMG_ENDOFHEADER "EndOfHeader\n"
#define PSVIMG_ENDOFTAILER "EndOfTailer\n"
#define PSVIMG_HEADER_FILLER ('x')
#define PSVIMG_TAILER_FILLER ('z')
#define PSVIMG_PADDING_FILLER ('+')
#define PSVIMG_ENTRY_ALIGN (0x400)
#define PSVIMG_BLOCK_SIZE (0x8000)

/** Access modes for st_mode in SceIoStat (confirm?). */
enum {
  /** Format bits mask */
  SCE_S_IFMT    = 0xF000,
  /** Symbolic link */
  SCE_S_IFLNK   = 0x4000,
  /** Directory */
  SCE_S_IFDIR   = 0x1000,
  /** Regular file */
  SCE_S_IFREG   = 0x2000,

  /** Set UID */
  SCE_S_ISUID   = 0x0800,
  /** Set GID */
  SCE_S_ISGID   = 0x0400,
  /** Sticky */
  SCE_S_ISVTX   = 0x0200,

  /** Others access rights mask */
  SCE_S_IRWXO   = 0x01C0,
  /** Others read permission */
  SCE_S_IROTH   = 0x0100,
  /** Others write permission */
  SCE_S_IWOTH   = 0x0080,
  /** Others execute permission */
  SCE_S_IXOTH   = 0x0040,

  /** Group access rights mask */
  SCE_S_IRWXG   = 0x0038,
  /** Group read permission */
  SCE_S_IRGRP   = 0x0020,
  /** Group write permission */
  SCE_S_IWGRP   = 0x0010,
  /** Group execute permission */
  SCE_S_IXGRP   = 0x0008,

  /** User access rights mask */
  SCE_S_IRWXU   = 0x0007,
  /** User read permission */
  SCE_S_IRUSR   = 0x0004,
  /** User write permission */
  SCE_S_IWUSR   = 0x0002,
  /** User execute permission */
  SCE_S_IXUSR   = 0x0001,
};

// File mode checking macros
#define SCE_S_ISLNK(m)  (((m) & SCE_S_IFMT) == SCE_S_IFLNK)
#define SCE_S_ISREG(m)  (((m) & SCE_S_IFMT) == SCE_S_IFREG)
#define SCE_S_ISDIR(m)  (((m) & SCE_S_IFMT) == SCE_S_IFDIR)

/** File modes, used for the st_attr parameter in SceIoStat (confirm?). */
enum {
  /** Format mask */
  SCE_SO_IFMT             = 0x0038,               // Format mask
  /** Symlink */
  SCE_SO_IFLNK            = 0x0008,               // Symbolic link
  /** Directory */
  SCE_SO_IFDIR            = 0x0010,               // Directory
  /** Regular file */
  SCE_SO_IFREG            = 0x0020,               // Regular file

  /** Hidden read permission */
  SCE_SO_IROTH            = 0x0004,               // read
  /** Hidden write permission */
  SCE_SO_IWOTH            = 0x0002,               // write
  /** Hidden execute permission */
  SCE_SO_IXOTH            = 0x0001,               // execute
};

// File mode checking macros
#define SCE_SO_ISLNK(m) (((m) & SCE_SO_IFMT) == SCE_SO_IFLNK)
#define SCE_SO_ISREG(m) (((m) & SCE_SO_IFMT) == SCE_SO_IFREG)
#define SCE_SO_ISDIR(m) (((m) & SCE_SO_IFMT) == SCE_SO_IFDIR)

typedef struct SceDateTime {
    uint16_t year;
    uint16_t month;
    uint16_t day;
    uint16_t hour;
    uint16_t minute;
    uint16_t second;
    uint32_t microsecond;
} __attribute__((packed)) SceDateTime;

/** Structure to hold the status information about a file */
typedef struct SceIoStat {
  uint32_t sst_mode;
  unsigned int  sst_attr;
  /** Size of the file in bytes. */
  uint64_t  sst_size;
  /** Creation time. */
  SceDateTime sst_ctime;
  /** Access time. */
  SceDateTime sst_atime;
  /** Modification time. */
  SceDateTime sst_mtime;
  /** Device-specific data. */
  uint32_t  sst_private[6];
} __attribute__((packed)) SceIoStat;

#define PSVMD_FW_1_00 (0x01000000)

typedef struct PsvMd {
  uint32_t magic;
  uint32_t type;
  uint64_t fw_version;
  uint8_t  psid[16];
  char     name[64];
  uint64_t psvimg_size;
  uint64_t version; // only support 2
  uint64_t total_size;
  uint8_t  iv[16];
  uint64_t ux0_info;
  uint64_t ur0_info;
  uint64_t unused_98;
  uint64_t unused_A0;
  uint32_t add_data;
} __attribute__((packed)) PsvMd_t;

/** This file (and backup) can only be restored with the same PSID */
#define PSVIMG_HEADER_FLAG_CONSOLE_UNIQUE (0x1)

typedef struct PsvImgHeader {
  uint64_t  systime;
  uint64_t  flags;
  SceIoStat stat;
  char      path_parent[256];
  uint32_t  unk_16C; // set to 1
  char      path_rel[256];
  char      unused[904];
  char      end[12];
} __attribute__((packed)) PsvImgHeader_t;

/** The file/directory will be _removed_ (not restored). */
#define PSVIMG_TAILER_FLAG_REMOVE (0x1)

typedef struct PsvImgTailer {
  uint64_t  flags;
  char      unused[1004];
  char      end[12];
} __attribute__((packed)) PsvImgTailer_t;

#endif


================================================
FILE: psvmd-decrypt.c
================================================
/* Copyright (C) 2017 Yifan Lu
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "restore.h"
#include "utils.h"

int main(int argc, const char *argv[]) {
  args_t args1, args2;
  struct stat st;
  int fds[2];
  pid_t pid;
  int status;

  if (argc < 5) {
    fprintf(stderr, "usage: psvmd-decrypt -K key input.psvmd output\n");
    return 1;
  }

  if (pipe(fds) < 0) {
    perror("pipe");
    return 1;
  }

  args1.in = open(argv[3], O_RDONLY);
  if (args1.in < 0) {
    perror("input");
    return 1;
  }
  args1.out = fds[1];
  args2.in = fds[0];
  args2.out = open(argv[4], O_WRONLY | O_CREAT | O_TRUNC, 0644);
  if (args2.out < 0) {
    perror("output");
    return 1;
  }

  if (parse_key(argv[2], args1.key) < 0) {
    fprintf(stderr, "invalid key\n");
    return 1;
  }

  if ((pid = fork()) == 0) {
    close(args1.in);
    close(args1.out);
    decompress_thread(&args2);
    return 0;
  } else if (pid > 0) {
    close(args2.in);
    close(args2.out);
    decrypt_thread(&args1);
  } else {
    perror("fork");
    return 1;
  }

  if (waitpid(pid, &status, 0) < 0) {
    perror("waitpid");
    return 1;
  }

  if (!WIFEXITED(status)) {
    fprintf(stderr, "child process returned error\n");
    return 1;
  }

  fprintf(stderr, "all done.\n");

  return 0;
}


================================================
FILE: restore.c
================================================
/* Copyright (C) 2017 Yifan Lu
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <utime.h>
#include <zlib.h>
#include "crypto.h"
#include "endian-utils.h"
#include "restore.h"
#include "psvimg.h"
#include "utils.h"

#define MAX_PATH_LEN 1024

static void print_hash(const char *title, uint8_t hash[SHA256_BLOCK_SIZE]) {
  fprintf(stderr, "%s: ", title);
  for (int i = 0; i < SHA256_BLOCK_SIZE; i++) {
    fprintf(stderr, "%02X", hash[i]);
  }
  fprintf(stderr, "\n");
}

void *decrypt_thread(void *pargs) {
  args_t *args = (args_t *)pargs;
  SHA256_CTX ctx, tmp;
  uint8_t iv[AES_BLOCK_SIZE];
  uint8_t next_iv[AES_BLOCK_SIZE];
  uint8_t hash[SHA256_BLOCK_SIZE];
  uint8_t buffer[PSVIMG_BLOCK_SIZE + SHA256_BLOCK_SIZE];
  ssize_t rd, total;

  // read iv
  if (read_block(args->in, iv, AES_BLOCK_SIZE) < AES_BLOCK_SIZE) {
    fprintf(stderr, "file too small! cannot read IV!\n");
    goto end;
  }

  // decrypt blocks
  sha256_init(&ctx);
  total = AES_BLOCK_SIZE;
  while ((rd = read_block(args->in, buffer, sizeof(buffer))) > 0) {
    // save next iv
    memcpy(next_iv, &buffer[rd - AES_BLOCK_SIZE], AES_BLOCK_SIZE);

    // decrypt
    aes256_cbc_decrypt(buffer, args->key, iv, rd / AES_BLOCK_SIZE);

    if (rd != sizeof(buffer)) {
      total += rd;
      break; // last block requires special processing
    }

    // TODO: there's actually a bug in Sony's implementation where 
    // if the last block is exactly 0x8000 (before the SHA256) then 
    // it won't know about the last block and terminate in error. 
    // In other words, we don't have to handle this edge case... for now.

    // validate hash
    sha256_update(&ctx, buffer, rd - SHA256_BLOCK_SIZE);
    sha256_copy(&tmp, &ctx);
    sha256_final(&tmp, hash);
    if (memcmp(&buffer[rd-SHA256_BLOCK_SIZE], hash, SHA256_BLOCK_SIZE) != 0) {
      fprintf(stderr, "hash mismatch at offset 0x%zx, (buffer size 0x%zx)\n", total + rd - SHA256_BLOCK_SIZE, rd);
      print_hash("expected", &buffer[rd-SHA256_BLOCK_SIZE]);
      print_hash("actual", hash);
      goto end;
    }

    // write output
    write_block(args->out, buffer, rd - SHA256_BLOCK_SIZE);

    memcpy(iv, next_iv, AES_BLOCK_SIZE);
    total += rd;
  }

  if (rd < 0) {
    fprintf(stderr, "Read error occured!\n");
    goto end;
  }

  // handle last block specially
  uint64_t exp_total;
  uint32_t exp_padding;

  exp_padding = le32toh(*(uint32_t *)&buffer[rd-0x10]);
  exp_total = le64toh(*(uint64_t *)&buffer[rd-0x8]);
  if (exp_total != total) {
    fprintf(stderr, "read size mismatch. expected: 0x%llx, actual: 0x%zx\n", exp_total, total);
    goto end;
  }
  exp_padding += 0x10;
  if (rd >= SHA256_BLOCK_SIZE + exp_padding) {
    sha256_update(&ctx, buffer, rd - SHA256_BLOCK_SIZE - exp_padding);
    tmp = ctx;
    sha256_final(&tmp, hash);
    if (memcmp(&buffer[rd-SHA256_BLOCK_SIZE-exp_padding], hash, SHA256_BLOCK_SIZE) != 0) {
      fprintf(stderr, "hash mismatch at offset 0x%lx (final block), (buffer size 0x%zx)\n", total + rd - SHA256_BLOCK_SIZE - exp_padding, rd);
      print_hash("expected", &buffer[rd-SHA256_BLOCK_SIZE]);
      print_hash("actual", hash);
      goto end;
    }
    write_block(args->out, buffer, rd - SHA256_BLOCK_SIZE - exp_padding);
  }

end:
  close(args->out);
  close(args->in);
  return NULL;
}

void *decompress_thread(void *pargs) {
  args_t *args = (args_t *)pargs;

  int ret;
  unsigned have;
  z_stream strm;
  unsigned char in[PSVIMG_BLOCK_SIZE];
  unsigned char out[PSVIMG_BLOCK_SIZE];
  ssize_t rd;

  /* allocate inflate state */
  strm.zalloc = Z_NULL;
  strm.zfree = Z_NULL;
  strm.opaque = Z_NULL;
  strm.avail_in = 0;
  strm.next_in = Z_NULL;
  ret = inflateInit(&strm);
  if (ret != Z_OK) {
    fprintf(stderr, "error init zlib\n");
    goto end;
  }

  /* decompress until deflate stream ends or end of file */
  do {
    strm.avail_in = rd = read_block(args->in, in, sizeof(in));
    if (rd < 0) {
      fprintf(stderr, "error reading\n");
      goto end;
    }
    if (strm.avail_in == 0)
      break;
    strm.next_in = in;

    /* run inflate() on input until output buffer not full */
    do {
      strm.avail_out = PSVIMG_BLOCK_SIZE;
      strm.next_out = out;
      ret = inflate(&strm, Z_NO_FLUSH);
      switch (ret) {
      case Z_NEED_DICT:
        ret = Z_DATA_ERROR;     /* and fall through */
      case Z_DATA_ERROR:
      case Z_MEM_ERROR:
        fprintf(stderr, "error inflating (bad file?)\n");
        (void)inflateEnd(&strm);
        goto end;
      }
      have = PSVIMG_BLOCK_SIZE - strm.avail_out;
      if (write_block(args->out, out, have) < have) {
        fprintf(stderr, "error writing\n");
        goto end;
      }
    } while (strm.avail_out == 0);

    /* done when inflate() says it's done */
  } while (ret != Z_STREAM_END);

  /* clean up and return */
  (void)inflateEnd(&strm);

end:
  close(args->out);
  close(args->in);
  return NULL;
}

static void sanatize_name(const char *bad, char *good, int len) {
  size_t sz;

  sz = strnlen(bad, len);
  for (int i = 0; i < sz; i++) {
    if (bad[i] == ':') {
      good[i] = '_';
    } else if (bad[i] == '/') {
      good[i] = '_';
    } else if (bad[i] == '\\') {
      good[i] = '_';
    } else {
      good[i] = bad[i];
    }
  }
  good[sz] = '\0';
}

static void scetime_to_tm(SceDateTime *sce, struct tm *tm) {
  tm->tm_sec = le16toh(sce->second);
  tm->tm_min = le16toh(sce->minute);
  tm->tm_hour = le16toh(sce->hour);
  tm->tm_mday = le16toh(sce->day);
  tm->tm_mon = le16toh(sce->month) - 1;
  tm->tm_year = le16toh(sce->year) - 1900;
  tm->tm_wday = 0;
  tm->tm_yday = 0;
  tm->tm_isdst = 0;
}

static mode_t scemode_to_posix(int sce_mode) {
  int mode = 0;
  if ((sce_mode & SCE_S_IRUSR) == SCE_S_IRUSR) {
    mode |= S_IRUSR;
  }
  if ((sce_mode & SCE_S_IWUSR) == SCE_S_IWUSR) {
    mode |= S_IWUSR;
  }
  if ((sce_mode & SCE_S_IRGRP) == SCE_S_IRGRP) {
    mode |= S_IRGRP;
  }
  if ((sce_mode & SCE_S_IWGRP) == SCE_S_IWGRP) {
    mode |= S_IWGRP;
  }
  if ((sce_mode & SCE_S_IROTH) == SCE_S_IROTH) {
    mode |= S_IROTH;
  }
  if ((sce_mode & SCE_S_IWOTH) == SCE_S_IWOTH) {
    mode |= S_IWOTH;
  }
  return mode;
}

static int write_file(PsvImgHeader_t *header, int in_fd, const char *prefix) {
  char good_parent[256];
  char full_parent[MAX_PATH_LEN];
  char full_path[MAX_PATH_LEN];
  struct stat st;
  int fd;
  struct tm tm;
  struct utimbuf times;

  sanatize_name(header->path_parent, good_parent, 256);
  snprintf(full_parent, MAX_PATH_LEN, "%s/%s", prefix, good_parent);

  // create parent directory if needed
  if (stat(full_parent, &st) < 0) {
    mkdir(full_parent, 0700);
    snprintf(full_path, MAX_PATH_LEN, "%s/%s", full_parent, "VITA_PATH.TXT");
    fd = open(full_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    write_block(fd, header->path_parent, strnlen(header->path_parent, 256)+1);
    close(fd);
  }

  // create file
  if (header->path_rel[0] == '\0') {
    strcpy(header->path_rel, "VITA_DATA.BIN");
  }
  snprintf(full_path, MAX_PATH_LEN, "%s/%s", full_parent, header->path_rel);
  if (SCE_S_ISREG(le32toh(header->stat.sst_mode))) {
    fd = open(full_path, O_WRONLY | O_CREAT | O_TRUNC, scemode_to_posix(le32toh(header->stat.sst_mode)));
    if (copy_block(fd, in_fd, le64toh(header->stat.sst_size)) < le64toh(header->stat.sst_size)) {
      fprintf(stderr, "error extracting %s\n", full_path);
      close(fd);
      return -1;
    }
    close(fd);
  } else {
    if (mkdir(full_path, scemode_to_posix(le32toh(header->stat.sst_mode)) | S_IXUSR) < 0) {
      fprintf(stderr, "error creating %s\n", full_path);
      return -1;
    }
  }

  // set creation time
  scetime_to_tm(&header->stat.sst_atime, &tm);
  times.actime = mktime(&tm);
  scetime_to_tm(&header->stat.sst_mtime, &tm);
  times.modtime = mktime(&tm);
  if (utime(full_path, &times) < 0) {
    fprintf(stderr, "error setting time\n");
    return -1;
  }

  return 0;
}

void *unpack_thread(void *pargs) {
  args_t *args = (args_t *)pargs;
  PsvImgHeader_t header;
  PsvImgTailer_t tailer;
  int padding;
  char *buffer;
  uint64_t fsize;

  while (read_block(args->in, &header, sizeof(header)) > 0) {
    if (memcmp(header.end, PSVIMG_ENDOFHEADER, sizeof(header.end)) != 0) {
      fprintf(stderr, "invalid header (bad file?)\n");
      goto end;
    }

    if (SCE_S_ISREG(le32toh(header.stat.sst_mode))) {
      fsize = le64toh(header.stat.sst_size);
      printf("creating file %s%s (%llx bytes)...\n", header.path_parent, header.path_rel, fsize);
    } else {
      fsize = 0;
      printf("creating directory %s%s...\n", header.path_parent, header.path_rel);
    }

    if (write_file(&header, args->in, args->prefix) < 0) {
      goto end;
    }

    // read padding
    if (fsize & (PSVIMG_ENTRY_ALIGN-1)) {
      padding = PSVIMG_ENTRY_ALIGN - (fsize & (PSVIMG_ENTRY_ALIGN-1));
    } else {
      padding = 0;
    }
    while (padding --> 0) {
      char ch;
      if (read_block(args->in, &ch, 1) < 1) {
        fprintf(stderr, "error reading padding\n");
        goto end;
      }
    }

    // read tailer
    if (read_block(args->in, &tailer, sizeof(tailer)) < sizeof(tailer)) {
      fprintf(stderr, "error reading tailer\n");
      goto end;
    }

    if (memcmp(tailer.end, PSVIMG_ENDOFTAILER, sizeof(tailer.end)) != 0) {
      fprintf(stderr, "invalid tailer (bad file?)\n");
      goto end;
    }
  }
  
end:
  close(args->out);
  close(args->in);
  return NULL;
}


================================================
FILE: restore.h
================================================
/* Copyright (C) 2017 Yifan Lu
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#ifndef RESTORE_H__
#define RESTORE_H__

typedef struct args {
  int in;
  int out;
  unsigned char key[32];
  const char *prefix;
} args_t;

void *decrypt_thread(void *pargs);
void *decompress_thread(void *pargs);
void *unpack_thread(void *pargs);

#endif


================================================
FILE: sha256.c
================================================
#include "sha256.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#define READ_BUFFER	(1*1024*1024)

uint32_t k[64] = {
   0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
   0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
   0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
   0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
   0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
   0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
   0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
   0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2
};

void sha256_transform(SHA256_CTX *ctx, uint8_t data[])
{  
   uint32_t a,b,c,d,e,f,g,h,i,j,t1,t2,m[64];
      
   for (i=0,j=0; i < 16; ++i, j += 4)
      m[i] = (data[j] << 24) | (data[j+1] << 16) | (data[j+2] << 8) | (data[j+3]);
   for ( ; i < 64; ++i)
      m[i] = SIG1(m[i-2]) + m[i-7] + SIG0(m[i-15]) + m[i-16];

   a = ctx->state[0];
   b = ctx->state[1];
   c = ctx->state[2];
   d = ctx->state[3];
   e = ctx->state[4];
   f = ctx->state[5];
   g = ctx->state[6];
   h = ctx->state[7];
   
   for (i = 0; i < 64; ++i) {
      t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i];
      t2 = EP0(a) + MAJ(a,b,c);
      h = g;
      g = f;
      f = e;
      e = d + t1;
      d = c;
      c = b;
      b = a;
      a = t1 + t2;
   }
   
   ctx->state[0] += a;
   ctx->state[1] += b;
   ctx->state[2] += c;
   ctx->state[3] += d;
   ctx->state[4] += e;
   ctx->state[5] += f;
   ctx->state[6] += g;
   ctx->state[7] += h;
}  

void sha256_init(SHA256_CTX *ctx)
{  
   ctx->datalen = 0; 
   ctx->bitlen[0] = 0; 
   ctx->bitlen[1] = 0; 
   ctx->state[0] = 0x6a09e667;
   ctx->state[1] = 0xbb67ae85;
   ctx->state[2] = 0x3c6ef372;
   ctx->state[3] = 0xa54ff53a;
   ctx->state[4] = 0x510e527f;
   ctx->state[5] = 0x9b05688c;
   ctx->state[6] = 0x1f83d9ab;
   ctx->state[7] = 0x5be0cd19;
}

void sha256_update(SHA256_CTX *ctx, uint8_t data[], uint32_t len)
{  
   uint32_t i;
   
   for (i=0; i < len; ++i) { 
      ctx->data[ctx->datalen] = data[i]; 
      ctx->datalen++; 
      if (ctx->datalen == 64) { 
         sha256_transform(ctx,ctx->data);
         DBL_INT_ADD(ctx->bitlen[0],ctx->bitlen[1],512); 
         ctx->datalen = 0; 
      }  
   }  
}  

void sha256_final(SHA256_CTX *ctx, uint8_t hash[])
{  
   uint32_t i; 
   
   i = ctx->datalen; 
   
   // Pad whatever data is left in the buffer. 
   if (ctx->datalen < 56) { 
      ctx->data[i++] = 0x80; 
      while (i < 56) 
         ctx->data[i++] = 0x00; 
   }  
   else { 
      ctx->data[i++] = 0x80; 
      while (i < 64) 
         ctx->data[i++] = 0x00; 
      sha256_transform(ctx,ctx->data);
      memset(ctx->data,0,56); 
   }  
   
   // Append to the padding the total message's length in bits and transform. 
   DBL_INT_ADD(ctx->bitlen[0],ctx->bitlen[1],ctx->datalen * 8);
   ctx->data[63] = ctx->bitlen[0]; 
   ctx->data[62] = ctx->bitlen[0] >> 8; 
   ctx->data[61] = ctx->bitlen[0] >> 16; 
   ctx->data[60] = ctx->bitlen[0] >> 24; 
   ctx->data[59] = ctx->bitlen[1]; 
   ctx->data[58] = ctx->bitlen[1] >> 8; 
   ctx->data[57] = ctx->bitlen[1] >> 16;  
   ctx->data[56] = ctx->bitlen[1] >> 24; 
   sha256_transform(ctx,ctx->data);
   
   // Since this implementation uses little endian byte ordering and SHA uses big endian,
   // reverse all the bytes when copying the final state to the output hash. 
   for (i=0; i < 4; ++i) { 
      hash[i]    = (ctx->state[0] >> (24-i*8)) & 0x000000ff; 
      hash[i+4]  = (ctx->state[1] >> (24-i*8)) & 0x000000ff; 
      hash[i+8]  = (ctx->state[2] >> (24-i*8)) & 0x000000ff;
      hash[i+12] = (ctx->state[3] >> (24-i*8)) & 0x000000ff;
      hash[i+16] = (ctx->state[4] >> (24-i*8)) & 0x000000ff;
      hash[i+20] = (ctx->state[5] >> (24-i*8)) & 0x000000ff;
      hash[i+24] = (ctx->state[6] >> (24-i*8)) & 0x000000ff;
      hash[i+28] = (ctx->state[7] >> (24-i*8)) & 0x000000ff;
   }  
}


/**
 * sha256_vector - SHA256 hash for data vector
 * @num_elem: Number of elements in the data vector
 * @addr: Pointers to the data areas
 * @len: Lengths of the data blocks
 * @mac: Buffer for the hash
 */
void sha256_vector(size_t num_elem,  uint8_t *addr[],  size_t *len,
         uint8_t *mac)
{
    SHA256_CTX ctx;
    size_t i;

    sha256_init(&ctx);
    for (i = 0; i < num_elem; i++)
        sha256_update(&ctx, addr[i], len[i]);
    sha256_final(&ctx, mac);
}


================================================
FILE: sha256.h
================================================
#ifndef SHA256_H__
#define SHA256_H__

#include <stdint.h>
#include <string.h>

// DBL_INT_ADD treats two unsigned ints a and b as one 64-bit integer and adds c to it
#define DBL_INT_ADD(a,b,c) if (a > 0xffffffff - (c)) ++b; a += c;
#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b))))
#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b))))

#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22))
#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25))
#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3))
#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10))

typedef struct {
   uint8_t data[64];
   uint32_t datalen;
   uint32_t bitlen[2];
   uint32_t state[8];
} SHA256_CTX;


void sha256_vector(size_t num_elem,  uint8_t *addr[],  size_t *len,
         uint8_t *mac);

void sha256_transform(SHA256_CTX *ctx, uint8_t data[]);
void sha256_init(SHA256_CTX *ctx);
void sha256_update(SHA256_CTX *ctx, uint8_t data[], uint32_t len);
void sha256_final(SHA256_CTX *ctx, uint8_t hash[]);
void sha256_vector(size_t num_elem,  uint8_t *addr[],  size_t *len,
         uint8_t *mac);

#endif


================================================
FILE: utils.c
================================================
/* Copyright (C) 2017 Yifan Lu
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

#define COPY_BLOCK_SIZE (1024)

ssize_t read_block(int fd, void *buf, size_t nbyte) {
  ssize_t rd;
  size_t total;
  total = 0;
  while ((rd = read(fd, buf, nbyte)) > 0) {
    nbyte -= rd;
    buf = (char *)buf + rd;
    total += rd;
  }
  if (rd < 0) {
    return rd;
  } else {
    return total;
  }
}

ssize_t write_block(int fd, const void *buf, size_t nbyte) {
  ssize_t wr;
  size_t total;
  total = 0;
  while ((wr = write(fd, buf, nbyte)) > 0) {
    nbyte -= wr;
    buf = (char *)buf + wr;
    total += wr;
  }
  if (wr < 0) {
    return wr;
  } else {
    return total;
  }
}

ssize_t copy_block(int fd_out, int fd_in, size_t nbyte) {
  char buffer[COPY_BLOCK_SIZE];
  ssize_t rd;
  size_t total;

  total = 0;
  while (nbyte > 0 && (rd = read_block(fd_in, buffer, nbyte > sizeof(buffer) ? sizeof(buffer) : nbyte)) > 0) {
    if (write_block(fd_out, buffer, rd) < rd) {
      return -1;
    }
    total += rd;
    nbyte -= rd;
  }
  if (rd < 0) {
    return rd;
  } else {
    return total;
  }
}

int parse_key(const char *ascii, uint8_t key[0x20]) {
  int i;
  for (i = 0; i < 0x20; i++) {
    char byte[3];
    memcpy(byte, &ascii[2*i], 2);
    byte[2] = '\0';
    key[i] = strtol(byte, NULL, 16);
    if (key[i] == 0 && !(byte[0] == '0' && byte[1] == '0')) {
      return -1;
    }
  }
  if (ascii[2*i] != '\0') {
    return -1;
  } else {
    return 0;
  }
}


================================================
FILE: utils.h
================================================
/* Copyright (C) 2017 Yifan Lu
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */
#ifndef UTILS_H__
#define UTILS_H__

#include <stdint.h>

ssize_t read_block(int fd, void *buf, size_t nbyte);
ssize_t write_block(int fd, const void *buf, size_t nbyte);
ssize_t copy_block(int fd_out, int fd_in, size_t nbyte);
int parse_key(const char *ascii, uint8_t key[0x20]);

#endif
Download .txt
gitextract_sc23yeqq/

├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
├── aes256.c
├── aes256.h
├── backup.c
├── backup.h
├── cmake/
│   └── Modules/
│       ├── FindLibGcrypt.cmake
│       └── Findzlib.cmake
├── crypto.h
├── dump_partials/
│   ├── CMakeLists.txt
│   ├── debugScreen.h
│   ├── debugScreenFont.c
│   ├── dump_partials.h
│   ├── kernel.c
│   ├── kernel.yml
│   └── main.c
├── endian-utils.h
├── psvimg-create.c
├── psvimg-extract.c
├── psvimg-keyfind.c
├── psvimg.h
├── psvmd-decrypt.c
├── restore.c
├── restore.h
├── sha256.c
├── sha256.h
├── utils.c
└── utils.h
Download .txt
SYMBOL INDEX (86 symbols across 19 files)

FILE: aes256.c
  function gf_alog (line 124) | static uint8_t gf_alog(uint8_t x) // calculate anti-logarithm gen 3
  function gf_log (line 134) | static uint8_t gf_log(uint8_t x) // calculate logarithm gen 3
  function gf_mulinv (line 150) | static uint8_t gf_mulinv(uint8_t x) // calculate multiplicative inverse
  function rj_sbox (line 156) | static uint8_t rj_sbox(uint8_t x)
  function rj_sbox_inv (line 170) | static uint8_t rj_sbox_inv(uint8_t x)
  function rj_xtime (line 187) | static uint8_t rj_xtime(uint8_t x)
  function aes_subBytes (line 194) | static void aes_subBytes(uint8_t *buf)
  function aes_subBytes_inv (line 202) | static void aes_subBytes_inv(uint8_t *buf)
  function aes_addRoundKey (line 210) | static void aes_addRoundKey(uint8_t *buf, uint8_t *key)
  function aes_addRoundKey_cpy (line 218) | static void aes_addRoundKey_cpy(uint8_t *buf, uint8_t *key, uint8_t *cpk)
  function aes_shiftRows (line 227) | static void aes_shiftRows(uint8_t *buf)
  function aes_shiftRows_inv (line 239) | static void aes_shiftRows_inv(uint8_t *buf)
  function aes_mixColumns (line 251) | static void aes_mixColumns(uint8_t *buf)
  function aes_mixColumns_inv (line 270) | void aes_mixColumns_inv(uint8_t *buf)
  function aes_expandEncKey (line 292) | static void aes_expandEncKey(uint8_t *k, uint8_t *rc)
  function aes_expandDecKey (line 315) | void aes_expandDecKey(uint8_t *k, uint8_t *rc)
  function aes256_init (line 339) | void aes256_init(aes256_context *ctx, uint8_t *k)
  function aes256_done (line 349) | void aes256_done(aes256_context *ctx)
  function aes256_encrypt_ecb (line 358) | void aes256_encrypt_ecb(aes256_context *ctx, uint8_t *buf)
  function aes256_decrypt_ecb (line 378) | void aes256_decrypt_ecb(aes256_context *ctx, uint8_t *buf)

FILE: aes256.h
  type aes256_context (line 28) | typedef struct {

FILE: backup.c
  function time_to_scetime (line 168) | static void time_to_scetime(const time_t *time, SceDateTime *sce) {
  function scestat (line 180) | static int scestat(const char *path, SceIoStat *sce) {
  function add_file (line 224) | static ssize_t add_file(int fd, const char *parent, const char *rel, con...
  function add_all_files (line 292) | static ssize_t add_all_files(int fd, const char *parent, const char *rel...
  type dirent (line 350) | struct dirent
  type stat (line 351) | struct stat

FILE: backup.h
  type args_t (line 9) | typedef struct args {

FILE: crypto.h
  function aes256_cbc_decrypt (line 18) | static inline void aes256_cbc_decrypt(uint8_t *buffer, uint8_t *key, uin...
  function aes256_cbc_encrypt (line 28) | static inline void aes256_cbc_encrypt(uint8_t *buffer, uint8_t *key, uin...
  function aes256_encrypt (line 38) | static inline void aes256_encrypt(uint8_t *buffer, uint8_t *key) {
  function aes256_cbc_decrypt (line 48) | static inline void aes256_cbc_decrypt(uint8_t *buffer, uint8_t *key, uin...
  function aes256_cbc_encrypt (line 61) | static inline void aes256_cbc_encrypt(uint8_t *buffer, uint8_t *key, uin...
  function aes256_encrypt (line 74) | static inline void aes256_encrypt(uint8_t *buffer, uint8_t *key) {

FILE: dump_partials/debugScreen.h
  function psvDebugScreenSetFgColor (line 43) | uint32_t psvDebugScreenSetFgColor(uint32_t color) {
  function psvDebugScreenSetBgColor (line 49) | uint32_t psvDebugScreenSetBgColor(uint32_t color) {
  function psvDebugScreenEscape (line 55) | static size_t psvDebugScreenEscape(const char *str){
  function psvDebugScreenSet (line 84) | int psvDebugScreenSet() {
  function psvDebugScreenInit (line 97) | int psvDebugScreenInit() {
  function psvDebugScreenClear (line 105) | void psvDebugScreenClear(int bg_color){
  function psvDebugScreenPuts (line 113) | int psvDebugScreenPuts(const char * text){
  function psvDebugScreenPrintf (line 160) | int psvDebugScreenPrintf(const char *format, ...) {

FILE: dump_partials/dump_partials.h
  type args_t (line 11) | typedef struct {

FILE: dump_partials/kernel.c
  function memset (line 16) | int memset(void *ptr, int ch, size_t len) {
  function module_start (line 23) | int module_start(SceSize argc, const void *pargs) {

FILE: dump_partials/main.c
  function get_key_seed (line 25) | static void get_key_seed(const char aid[8], char hash[0x20]) {
  type displayBuffer (line 41) | typedef struct{
  function gxm_vsync_cb (line 60) | void gxm_vsync_cb(const void *callback_data){
  function gxm_init (line 65) | void gxm_init(){
  function gxm_swap (line 74) | void gxm_swap(){
  function gxm_term (line 80) | void gxm_term(){
  function enter_aid (line 88) | int enter_aid(char aid[8]) {
  function get_aid_from_ux0 (line 130) | int get_aid_from_ux0(char aid[8]) {
  function dump_partials (line 161) | int dump_partials(char aid[8]) {
  function main (line 195) | int main(int argc, char *argv[]) {

FILE: psvimg-create.c
  function is_backup (line 21) | static int is_backup(const char *title) {
  function main (line 35) | int main(int argc, const char *argv[]) {

FILE: psvimg-extract.c
  function main (line 15) | int main(int argc, const char *argv[]) {

FILE: psvimg-keyfind.c
  type status_t (line 28) | typedef struct {
  function print_progress (line 39) | static void print_progress(uint32_t total) {
  function print_key (line 49) | static void print_key(uint32_t guess[KEY_LEN/sizeof(uint32_t)], int know...
  function find_key (line 61) | int find_key(int fd, uint32_t guess[KEY_LEN/sizeof(uint32_t)], int idx, ...
  function dispatch_jobs (line 99) | int dispatch_jobs(int num_jobs, uint32_t guess[KEY_LEN/sizeof(uint32_t)]...
  function main (line 183) | int main(int argc, const char *argv[]) {

FILE: psvimg.h
  type SceDateTime (line 96) | typedef struct SceDateTime {
  type SceIoStat (line 107) | typedef struct SceIoStat {
  type PsvMd_t (line 124) | typedef struct PsvMd {
  type PsvImgHeader_t (line 144) | typedef struct PsvImgHeader {
  type PsvImgTailer_t (line 158) | typedef struct PsvImgTailer {

FILE: psvmd-decrypt.c
  function main (line 15) | int main(int argc, const char *argv[]) {

FILE: restore.c
  function print_hash (line 23) | static void print_hash(const char *title, uint8_t hash[SHA256_BLOCK_SIZE...
  function sanatize_name (line 185) | static void sanatize_name(const char *bad, char *good, int len) {
  function scetime_to_tm (line 203) | static void scetime_to_tm(SceDateTime *sce, struct tm *tm) {
  function mode_t (line 215) | static mode_t scemode_to_posix(int sce_mode) {
  function write_file (line 238) | static int write_file(PsvImgHeader_t *header, int in_fd, const char *pre...

FILE: restore.h
  type args_t (line 9) | typedef struct args {

FILE: sha256.c
  function sha256_transform (line 19) | void sha256_transform(SHA256_CTX *ctx, uint8_t data[])
  function sha256_init (line 60) | void sha256_init(SHA256_CTX *ctx)
  function sha256_update (line 75) | void sha256_update(SHA256_CTX *ctx, uint8_t data[], uint32_t len)
  function sha256_final (line 90) | void sha256_final(SHA256_CTX *ctx, uint8_t hash[])
  function sha256_vector (line 144) | void sha256_vector(size_t num_elem,  uint8_t *addr[],  size_t *len,

FILE: sha256.h
  type SHA256_CTX (line 19) | typedef struct {

FILE: utils.c
  function read_block (line 13) | ssize_t read_block(int fd, void *buf, size_t nbyte) {
  function write_block (line 29) | ssize_t write_block(int fd, const void *buf, size_t nbyte) {
  function copy_block (line 45) | ssize_t copy_block(int fd_out, int fd_in, size_t nbyte) {
  function parse_key (line 65) | int parse_key(const char *ascii, uint8_t key[0x20]) {
Condensed preview — 30 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (113K chars).
[
  {
    "path": ".gitignore",
    "chars": 119,
    "preview": "CMakeCache.txt\nCMakeFiles\nCMakeScripts\nMakefile\ncmake_install.cmake\ninstall_manifest.txt\nCTestTestfile.cmake\n.DS_Store\n"
  },
  {
    "path": "CMakeLists.txt",
    "chars": 1475,
    "preview": "cmake_minimum_required(VERSION 2.8)\n\nproject(psvimgtools C)\n\nset(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} \"${CMAKE_SOURCE_"
  },
  {
    "path": "LICENSE",
    "chars": 1065,
    "preview": "MIT License\n\nCopyright (c) 2017 Yifan Lu\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\no"
  },
  {
    "path": "README.md",
    "chars": 3436,
    "preview": "psvimgtools\n===========\nThis is a set of tools that let you decrypt, extract, and repack Vita CMA backup images. To use "
  },
  {
    "path": "aes256.c",
    "chars": 13581,
    "preview": "/*\n*   Byte-oriented AES-256 implementation.\n*   All lookup tables replaced with 'on the fly' calculations.\n*\n*   Copyri"
  },
  {
    "path": "aes256.h",
    "chars": 1462,
    "preview": "/*  \n*   Byte-oriented AES-256 implementation.\n*   All lookup tables replaced with 'on the fly' calculations. \n*\n*   Cop"
  },
  {
    "path": "backup.c",
    "chars": 10907,
    "preview": "/* Copyright (C) 2017 Yifan Lu\n *\n * This software may be modified and distributed under the terms\n * of the MIT license"
  },
  {
    "path": "backup.h",
    "chars": 459,
    "preview": "/* Copyright (C) 2017 Yifan Lu\n *\n * This software may be modified and distributed under the terms\n * of the MIT license"
  },
  {
    "path": "cmake/Modules/FindLibGcrypt.cmake",
    "chars": 3343,
    "preview": "#.rst\n# FindLibGcrypt\n# -------------\n#\n# Finds the Libgcrypt library.\n#\n# This will define the following variables:\n#\n#"
  },
  {
    "path": "cmake/Modules/Findzlib.cmake",
    "chars": 545,
    "preview": "find_package(PkgConfig)\npkg_check_modules(PC_zlib zlib>=1.2.8)\n\nfind_path(zlib_INCLUDE_DIR zlib.h\n          HINTS ${PC_z"
  },
  {
    "path": "crypto.h",
    "chars": 2911,
    "preview": "/* Copyright (C) 2017 Yifan Lu\n *\n * This software may be modified and distributed under the terms\n * of the MIT license"
  },
  {
    "path": "dump_partials/CMakeLists.txt",
    "chars": 1374,
    "preview": "cmake_minimum_required(VERSION 2.8)\n\nif(NOT DEFINED CMAKE_TOOLCHAIN_FILE)\n  if(DEFINED ENV{VITASDK})\n    set(CMAKE_TOOLC"
  },
  {
    "path": "dump_partials/debugScreen.h",
    "chars": 5145,
    "preview": "#ifndef DEBUG_SCREEN_H\n#define DEBUG_SCREEN_H\n\n#include <stdio.h>\n#include <string.h>\n#include <stdarg.h>\n#include <intt"
  },
  {
    "path": "dump_partials/debugScreenFont.c",
    "chars": 9076,
    "preview": "/*\n * PSP Software Development Kit - http://www.pspdev.org\n * ----------------------------------------------------------"
  },
  {
    "path": "dump_partials/dump_partials.h",
    "chars": 356,
    "preview": "/* Copyright (C) 2017 Yifan Lu\n *\n * This software may be modified and distributed under the terms\n * of the MIT license"
  },
  {
    "path": "dump_partials/kernel.c",
    "chars": 3342,
    "preview": "/* Copyright (C) 2017 Yifan Lu\n *\n * This software may be modified and distributed under the terms\n * of the MIT license"
  },
  {
    "path": "dump_partials/kernel.yml",
    "chars": 107,
    "preview": "dump_partials_helper:\n  attributes: 0\n  version:\n    major: 1\n    minor: 1\n  main:\n    start: module_start\n"
  },
  {
    "path": "dump_partials/main.c",
    "chars": 7168,
    "preview": "/* Copyright (C) 2017 Yifan Lu\n *\n * This software may be modified and distributed under the terms\n * of the MIT license"
  },
  {
    "path": "endian-utils.h",
    "chars": 2777,
    "preview": "#ifndef ENDIAN_UTILS_H\n#define ENDIAN_UTILS_H\n\n#ifndef __MINGW32__\n\n#ifdef __APPLE__\n\n#include <machine/endian.h>\n#inclu"
  },
  {
    "path": "psvimg-create.c",
    "chars": 5163,
    "preview": "/* Copyright (C) 2017 Yifan Lu\n *\n * This software may be modified and distributed under the terms\n * of the MIT license"
  },
  {
    "path": "psvimg-extract.c",
    "chars": 1475,
    "preview": "/* Copyright (C) 2017 Yifan Lu\n *\n * This software may be modified and distributed under the terms\n * of the MIT license"
  },
  {
    "path": "psvimg-keyfind.c",
    "chars": 5492,
    "preview": "/* Copyright (C) 2017 Yifan Lu\n *\n * This software may be modified and distributed under the terms\n * of the MIT license"
  },
  {
    "path": "psvimg.h",
    "chars": 4486,
    "preview": "/* Copyright (C) 2017 Yifan Lu\n *\n * This software may be modified and distributed under the terms\n * of the MIT license"
  },
  {
    "path": "psvmd-decrypt.c",
    "chars": 1487,
    "preview": "/* Copyright (C) 2017 Yifan Lu\n *\n * This software may be modified and distributed under the terms\n * of the MIT license"
  },
  {
    "path": "restore.c",
    "chars": 9605,
    "preview": "/* Copyright (C) 2017 Yifan Lu\n *\n * This software may be modified and distributed under the terms\n * of the MIT license"
  },
  {
    "path": "restore.h",
    "chars": 418,
    "preview": "/* Copyright (C) 2017 Yifan Lu\n *\n * This software may be modified and distributed under the terms\n * of the MIT license"
  },
  {
    "path": "sha256.c",
    "chars": 4798,
    "preview": "#include \"sha256.h\"\r\n#include <stdint.h>\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n\r\n#define READ_BUFFER\t(1*1024*1024)\r\n"
  },
  {
    "path": "sha256.h",
    "chars": 1272,
    "preview": "#ifndef SHA256_H__\r\n#define SHA256_H__\r\n\r\n#include <stdint.h>\r\n#include <string.h>\r\n\r\n// DBL_INT_ADD treats two unsigned"
  },
  {
    "path": "utils.c",
    "chars": 1617,
    "preview": "/* Copyright (C) 2017 Yifan Lu\n *\n * This software may be modified and distributed under the terms\n * of the MIT license"
  },
  {
    "path": "utils.h",
    "chars": 450,
    "preview": "/* Copyright (C) 2017 Yifan Lu\n *\n * This software may be modified and distributed under the terms\n * of the MIT license"
  }
]

About this extraction

This page contains the full source code of the yifanlu/psvimgtools GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 30 files (102.5 KB), approximately 36.9k tokens, and a symbol index with 86 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!