Skip to content

Instantly share code, notes, and snippets.

@archshift
Created January 13, 2015 07:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save archshift/9d48b03581dcea71dd0c to your computer and use it in GitHub Desktop.
Save archshift/9d48b03581dcea71dd0c to your computer and use it in GitHub Desktop.
#include <string>
#include <cinttypes>
#include <cstdlib>
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
template <typename T>
struct array_ptr {
T* ptr;
const size_t size;
array_ptr(const size_t& size):
ptr(new T[size]),
size(size)
{
}
~array_ptr()
{
delete[] ptr;
}
};
enum : size_t {
NCCH_OFFSET_MAGIC = 0x100,
NCCH_OFFSET_EXHEADER_SIZE = 0x180,
NCCH_OFFSET_FLAGS = 0x188,
NCCH_OFFSET_EXEFS_OFFSET = 0x1A0,
NCCH_OFFSET_EXEFS_SIZE = 0x1A4,
NCCH_OFFSET_ROMFS_OFFSET = 0x1B0,
NCCH_OFFSET_ROMFS_SIZE = 0x1B4,
NCCH_OFFSET_EXHEADER = 0x200,
};
enum : size_t {
NCCH_OFFSET_FLAG_CRYPTO = 0x180 + 3,
NCCH_OFFSET_FLAG_CONTENT_TYPE = 0x180 + 5,
NCCH_OFFSET_FLAG_DATA_FORMAT = 0x180 + 7,
};
enum : size_t {
FLAG_DATA = 0x1,
FLAG_EXEC = 0x2,
FLAG_NOROMFS = 0x2,
FLAG_NOCRYPTO = 0x4,
};
void StripNullBytes(std::string* str)
{
size_t end_pos = str->find_last_not_of('\0');
if (end_pos != std::string::npos)
*str = str->substr(0, end_pos + 1);
}
size_t WriteBufferToFile(const std::string& filename, const array_ptr<u8>& buffer)
{
FILE* file = fopen(filename.c_str(), "wb");
size_t size_written = fwrite(buffer.ptr, 1, buffer.size, file);
fclose(file);
return size_written;
}
array_ptr<u8> ReadBinaryFile(const std::string& filename)
{
FILE* bin_file = fopen(filename.c_str(), "rb");
fseek(bin_file, 0, SEEK_END);
size_t out_size = ftell(bin_file);
fseek(bin_file, 0, SEEK_SET);
array_ptr<u8> file_buf(out_size);
fread(file_buf.ptr, 1, file_buf.size, bin_file);
fclose(bin_file);
return file_buf;
};
void DecryptCXI(array_ptr<u8> app_file_buf,
const array_ptr<u8>& exhead_xorpad,
const array_ptr<u8>& exefs_xorpad,
const array_ptr<u8>& romfs_xorpad)
{
size_t exhead_size = *reinterpret_cast<u32*>(app_file_buf.ptr + NCCH_OFFSET_EXHEADER_SIZE);
for (size_t byte = 0; byte < exhead_size; ++byte) {
app_file_buf.ptr[byte + NCCH_OFFSET_EXHEADER] ^= exhead_xorpad.ptr[byte];
}
size_t exefs_offset = *reinterpret_cast<u32*>(app_file_buf.ptr + NCCH_OFFSET_EXEFS_OFFSET) * 0x200;
size_t exefs_size = *reinterpret_cast<u32*>(app_file_buf.ptr + NCCH_OFFSET_EXEFS_SIZE) * 0x200;
for (size_t byte = 0; byte < exefs_size; ++byte) {
app_file_buf.ptr[byte + exefs_offset] ^= exefs_xorpad.ptr[byte];
}
size_t romfs_offset = *reinterpret_cast<u32*>(app_file_buf.ptr + NCCH_OFFSET_ROMFS_OFFSET) * 0x200;
size_t romfs_size = *reinterpret_cast<u32*>(app_file_buf.ptr + NCCH_OFFSET_ROMFS_SIZE) * 0x200;
for (size_t byte = 0; byte < romfs_size; ++byte) {
app_file_buf.ptr[byte + romfs_offset] ^= romfs_xorpad.ptr[byte];
}
app_file_buf.ptr[NCCH_OFFSET_FLAG_CRYPTO] = 0;
app_file_buf.ptr[NCCH_OFFSET_FLAG_DATA_FORMAT] |= FLAG_NOCRYPTO;
}
int main(int argc, char** argv)
{
if (argc < 5) {
printf("Usage: xorer file{.app|.cxi} exheader.xorpad exefs.xorpad romfs.xorpad\n");
return -1;
}
array_ptr<u8> app_file = ReadBinaryFile(argv[1]);
bool confirm_magic = memcmp(app_file.ptr + NCCH_OFFSET_MAGIC, "NCCH", 4) == 0;
printf("Magic confirmed: %s\n", confirm_magic ? "true" : "false");
u8 data_format_flag = *(app_file.ptr + NCCH_OFFSET_FLAG_DATA_FORMAT);
if ((data_format_flag & FLAG_DATA) && !(data_format_flag & FLAG_EXEC)) {
// File is a CFA
printf("Filetype: CFA\n");
printf("CFA not yet supported!\n");
return -1;
} else {
// File is a CXI
printf("Filetype: CXI\n");
DecryptCXI(app_file, ReadBinaryFile(argv[2]), ReadBinaryFile(argv[3]), ReadBinaryFile(argv[4]));
WriteBufferToFile(std::string(argv[1]) + ".cxi", app_file);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment