Add C version
This commit is contained in:
parent
f4aceaac7e
commit
793df0c426
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,126 @@
|
|||
/*********************************************************************
|
||||
* Filename: aes.h
|
||||
* Author: Brad Conte (brad AT bradconte.com)
|
||||
* Copyright:
|
||||
* Disclaimer: This code is presented "as is" without any guarantees.
|
||||
* Details: Defines the API for the corresponding AES implementation.
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef AES_H
|
||||
#define AES_H
|
||||
|
||||
/*************************** HEADER FILES ***************************/
|
||||
#include <stddef.h>
|
||||
|
||||
/****************************** MACROS ******************************/
|
||||
#define AES_BLOCK_SIZE 16 // AES operates on 16 bytes at a time
|
||||
|
||||
/**************************** DATA TYPES ****************************/
|
||||
typedef unsigned char BYTE; // 8-bit byte
|
||||
typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
|
||||
|
||||
/*********************** FUNCTION DECLARATIONS **********************/
|
||||
///////////////////
|
||||
// AES
|
||||
///////////////////
|
||||
// Key setup must be done before any AES en/de-cryption functions can be used.
|
||||
void aes_key_setup(const BYTE key[], // The key, must be 128, 192, or 256 bits
|
||||
WORD w[], // Output key schedule to be used later
|
||||
int keysize); // Bit length of the key, 128, 192, or 256
|
||||
|
||||
void aes_encrypt(const BYTE in[], // 16 bytes of plaintext
|
||||
BYTE out[], // 16 bytes of ciphertext
|
||||
const WORD key[], // From the key setup
|
||||
int keysize); // Bit length of the key, 128, 192, or 256
|
||||
|
||||
void aes_decrypt(const BYTE in[], // 16 bytes of ciphertext
|
||||
BYTE out[], // 16 bytes of plaintext
|
||||
const WORD key[], // From the key setup
|
||||
int keysize); // Bit length of the key, 128, 192, or 256
|
||||
|
||||
///////////////////
|
||||
// AES - CBC
|
||||
///////////////////
|
||||
int aes_encrypt_cbc(const BYTE in[], // Plaintext
|
||||
size_t in_len, // Must be a multiple of AES_BLOCK_SIZE
|
||||
BYTE out[], // Ciphertext, same length as plaintext
|
||||
const WORD key[], // From the key setup
|
||||
int keysize, // Bit length of the key, 128, 192, or 256
|
||||
const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long
|
||||
|
||||
// Only output the CBC-MAC of the input.
|
||||
int aes_encrypt_cbc_mac(const BYTE in[], // plaintext
|
||||
size_t in_len, // Must be a multiple of AES_BLOCK_SIZE
|
||||
BYTE out[], // Output MAC
|
||||
const WORD key[], // From the key setup
|
||||
int keysize, // Bit length of the key, 128, 192, or 256
|
||||
const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long
|
||||
|
||||
///////////////////
|
||||
// AES - CTR
|
||||
///////////////////
|
||||
void increment_iv(BYTE iv[], // Must be a multiple of AES_BLOCK_SIZE
|
||||
int counter_size); // Bytes of the IV used for counting (low end)
|
||||
|
||||
void aes_encrypt_ctr(const BYTE in[], // Plaintext
|
||||
size_t in_len, // Any byte length
|
||||
BYTE out[], // Ciphertext, same length as plaintext
|
||||
const WORD key[], // From the key setup
|
||||
int keysize, // Bit length of the key, 128, 192, or 256
|
||||
const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long
|
||||
|
||||
void aes_decrypt_ctr(const BYTE in[], // Ciphertext
|
||||
size_t in_len, // Any byte length
|
||||
BYTE out[], // Plaintext, same length as ciphertext
|
||||
const WORD key[], // From the key setup
|
||||
int keysize, // Bit length of the key, 128, 192, or 256
|
||||
const BYTE iv[]); // IV, must be AES_BLOCK_SIZE bytes long
|
||||
|
||||
///////////////////
|
||||
// AES - CCM
|
||||
///////////////////
|
||||
// Returns True if the input parameters do not violate any constraint.
|
||||
int aes_encrypt_ccm(const BYTE plaintext[], // IN - Plaintext.
|
||||
WORD plaintext_len, // IN - Plaintext length.
|
||||
const BYTE associated_data[], // IN - Associated Data included in authentication, but not encryption.
|
||||
unsigned short associated_data_len, // IN - Associated Data length in bytes.
|
||||
const BYTE nonce[], // IN - The Nonce to be used for encryption.
|
||||
unsigned short nonce_len, // IN - Nonce length in bytes.
|
||||
BYTE ciphertext[], // OUT - Ciphertext, a concatination of the plaintext and the MAC.
|
||||
WORD *ciphertext_len, // OUT - The length of the ciphertext, always plaintext_len + mac_len.
|
||||
WORD mac_len, // IN - The desired length of the MAC, must be 4, 6, 8, 10, 12, 14, or 16.
|
||||
const BYTE key[], // IN - The AES key for encryption.
|
||||
int keysize); // IN - The length of the key in bits. Valid values are 128, 192, 256.
|
||||
|
||||
// Returns True if the input parameters do not violate any constraint.
|
||||
// Use mac_auth to ensure decryption/validation was preformed correctly.
|
||||
// If authentication does not succeed, the plaintext is zeroed out. To overwride
|
||||
// this, call with mac_auth = NULL. The proper proceedure is to decrypt with
|
||||
// authentication enabled (mac_auth != NULL) and make a second call to that
|
||||
// ignores authentication explicitly if the first call failes.
|
||||
int aes_decrypt_ccm(const BYTE ciphertext[], // IN - Ciphertext, the concatination of encrypted plaintext and MAC.
|
||||
WORD ciphertext_len, // IN - Ciphertext length in bytes.
|
||||
const BYTE assoc[], // IN - The Associated Data, required for authentication.
|
||||
unsigned short assoc_len, // IN - Associated Data length in bytes.
|
||||
const BYTE nonce[], // IN - The Nonce to use for decryption, same one as for encryption.
|
||||
unsigned short nonce_len, // IN - Nonce length in bytes.
|
||||
BYTE plaintext[], // OUT - The plaintext that was decrypted. Will need to be large enough to hold ciphertext_len - mac_len.
|
||||
WORD *plaintext_len, // OUT - Length in bytes of the output plaintext, always ciphertext_len - mac_len .
|
||||
WORD mac_len, // IN - The length of the MAC that was calculated.
|
||||
int *mac_auth, // OUT - TRUE if authentication succeeded, FALSE if it did not. NULL pointer will ignore the authentication.
|
||||
const BYTE key[], // IN - The AES key for decryption.
|
||||
int keysize); // IN - The length of the key in BITS. Valid values are 128, 192, 256.
|
||||
|
||||
///////////////////
|
||||
// Test functions
|
||||
///////////////////
|
||||
int aes_test();
|
||||
int aes_ecb_test();
|
||||
int aes_cbc_test();
|
||||
int aes_ctr_test();
|
||||
int aes_ccm_test();
|
||||
|
||||
|
||||
#include "aes.c"
|
||||
|
||||
#endif // AES_H
|
|
@ -0,0 +1,416 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "aes.h"
|
||||
#include "md5.c"
|
||||
|
||||
#define DEBUG 1
|
||||
|
||||
#define PSSE_BLOCK_SIZE (0x8000)
|
||||
#define PSSE_SIG_BLOCK_SIZE (0x80000)
|
||||
#define PSSE_SIG_SIZE (0x400)
|
||||
|
||||
#define AES_KEY_SIZE (0x10)
|
||||
#define AES_IV_SIZE (0x10)
|
||||
|
||||
const uint8_t header_iv[AES_IV_SIZE] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; // IV for the encrypted PSSE Header
|
||||
const uint8_t header_key[AES_KEY_SIZE] = {0x4E, 0x29, 0x8B, 0x40, 0xF5, 0x31, 0xF4, 0x69, 0xD2, 0x1F, 0x75, 0xB1, 0x33, 0xC3, 0x07, 0xBE}; // Key used to decrypt the encrypted PSSE Header
|
||||
|
||||
const uint8_t runtime_title_key[AES_KEY_SIZE] = {0xA8, 0x69, 0x3C, 0x4D, 0xF0, 0xAE, 0xED, 0xBC, 0x9A, 0xBF, 0xD8, 0x21, 0x36, 0x92, 0x91, 0x2D}; // Header used to decrypt runtime libaries, eg. Sce.PlayStation.Core.dll.
|
||||
const uint8_t header_key_psmdev[AES_KEY_SIZE] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; // Key used to decrypt the encrypted PSSE Header in PSM dev,
|
||||
|
||||
const char* runtime_content_id = "IP9100-NPXS10074_00-0000000000000000";
|
||||
|
||||
static char title_content_id[0x30];
|
||||
static uint8_t title_iv[AES_IV_SIZE];
|
||||
static uint8_t title_key[AES_KEY_SIZE];
|
||||
|
||||
// psse header
|
||||
typedef struct psse_header{
|
||||
char magic[0x4];
|
||||
uint32_t version;
|
||||
uint64_t file_size;
|
||||
uint32_t psse_type;
|
||||
char content_id[0x2C];
|
||||
uint8_t md5_hash[0x10];
|
||||
uint8_t file_name[0x20];
|
||||
uint8_t file_iv[0x10];
|
||||
uint8_t unk[0x600];
|
||||
} psse_header;
|
||||
|
||||
// rif header
|
||||
typedef struct ScePsmDrmLicense {
|
||||
char magic[0x8];
|
||||
uint32_t unk1;
|
||||
uint32_t unk2;
|
||||
uint64_t account_id;
|
||||
uint32_t unk3;
|
||||
uint32_t unk4;
|
||||
uint64_t start_time;
|
||||
uint64_t expiration_time;
|
||||
uint8_t activation_checksum[0x20];
|
||||
char content_id[0x30];
|
||||
uint8_t unk5[0x80];
|
||||
uint8_t unk6[0x20];
|
||||
uint8_t key[0x10];
|
||||
uint8_t signature[0x1D0];
|
||||
uint8_t rsa_signature[0x100];
|
||||
} ScePsmDrmLicense;
|
||||
|
||||
// psse_block_ref struct
|
||||
typedef struct psse_block_ref{
|
||||
uint8_t block_data[PSSE_BLOCK_SIZE];
|
||||
size_t block_size;
|
||||
} psse_block_ref;
|
||||
|
||||
typedef struct decrypted_file{
|
||||
uint8_t* data;
|
||||
size_t file_size;
|
||||
} decrypted_file;
|
||||
|
||||
// Debug print buffer contents
|
||||
void print_buffer(char* buffer_title, uint8_t* buffer, size_t buffer_sz){
|
||||
printf("[*] %s: ", buffer_title);
|
||||
for(int i = 0; i < buffer_sz; i++) {
|
||||
printf("%02X", buffer[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// Convert a path to one for whatever OS your using.
|
||||
void fix_paths(char* path){
|
||||
size_t sz = strlen(path);
|
||||
for(int i = 0; i < sz; i++){
|
||||
#ifdef _WIN32
|
||||
if(path[i] == '/')
|
||||
#else
|
||||
if(path[i] == '\\')
|
||||
#endif
|
||||
{
|
||||
#ifdef _WIN32
|
||||
path[i] = '\\';
|
||||
#else
|
||||
path[i] = '/';
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert a path to one for whatever OS your using.
|
||||
int is_dir(char* path){
|
||||
size_t sz = strlen(path);
|
||||
if(path[sz] == '/' || path[sz] == '\\')
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Reads title key and content id from a rif
|
||||
// returns <0 on fail, 0 on success.
|
||||
int read_rif(char* rif_path){
|
||||
ScePsmDrmLicense rif;
|
||||
|
||||
FILE* rif_fd = fopen(rif_path, "rb");
|
||||
if(rif_fd == NULL){
|
||||
return -1;
|
||||
}
|
||||
fread(&rif, sizeof(ScePsmDrmLicense), 1, rif_fd);
|
||||
fclose(rif_fd);
|
||||
|
||||
// Read important stuff.
|
||||
strncpy(title_content_id, rif.content_id, sizeof(title_content_id));
|
||||
memcpy(title_key, rif.key, sizeof(title_key));
|
||||
|
||||
if(strlen(title_content_id) != 0x24){
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
// Returns the total filesize of a file.
|
||||
size_t get_file_size(char* file_path){
|
||||
FILE* get_size_fd = fopen(file_path, "rb");
|
||||
fseek(get_size_fd, 0, SEEK_END);
|
||||
size_t size = ftell(get_size_fd);
|
||||
fclose(get_size_fd);
|
||||
return size;
|
||||
}
|
||||
|
||||
// Calculate IV for this block.
|
||||
uint8_t* roll_iv(uint64_t block_id) {
|
||||
uint8_t* new_iv = (uint8_t*)malloc(sizeof(title_iv));
|
||||
|
||||
memset(new_iv,0x00, sizeof(title_iv));
|
||||
memcpy(new_iv, &block_id, sizeof(uint64_t));
|
||||
for(int i = 0; i < sizeof(title_iv); i++){
|
||||
new_iv[i] = new_iv[i] ^ title_iv[i];
|
||||
}
|
||||
return new_iv;
|
||||
}
|
||||
|
||||
// Decrypts a specific block inside a PSSE file.
|
||||
psse_block_ref decrypt_block(FILE* psse_file, uint64_t block_id, uint64_t total_blocks, uint32_t file_size){
|
||||
psse_block_ref ref;
|
||||
memset(&ref, 0x00, sizeof(psse_block_ref));
|
||||
|
||||
uint8_t* new_iv = roll_iv(block_id);
|
||||
uint64_t block_loc = block_id * PSSE_BLOCK_SIZE;
|
||||
size_t total_read = PSSE_BLOCK_SIZE;
|
||||
uint64_t trim_to = total_read;
|
||||
|
||||
if(block_id == 0){ // Skip to filedata
|
||||
block_loc = sizeof(psse_header);
|
||||
total_read -= sizeof(psse_header);
|
||||
trim_to = total_read;
|
||||
}
|
||||
else if(block_loc % PSSE_SIG_BLOCK_SIZE == 0){ // Skip signature block
|
||||
block_loc += PSSE_SIG_SIZE;
|
||||
total_read -= PSSE_SIG_SIZE;
|
||||
trim_to = total_read;
|
||||
}
|
||||
|
||||
uint64_t rd_amt = ((block_loc - sizeof(psse_header)) - (PSSE_SIG_SIZE*(block_loc / PSSE_SIG_BLOCK_SIZE))); // Total amount of bytes read so far.
|
||||
|
||||
if (block_id >= total_blocks) { // Is this the last block?
|
||||
total_read = file_size - rd_amt;
|
||||
trim_to = total_read;
|
||||
total_read += ((AES_BLOCK_SIZE) - (total_read % (AES_BLOCK_SIZE)));
|
||||
}
|
||||
|
||||
uint8_t* block_data = malloc(total_read);
|
||||
|
||||
fseek(psse_file, block_loc, SEEK_SET);
|
||||
fread(block_data, total_read, 0x1, psse_file);
|
||||
ref.block_size = trim_to;
|
||||
|
||||
// Decrypt block
|
||||
uint32_t aes_game_ctx[0x3C];
|
||||
aes_key_setup(title_key, aes_game_ctx, 0x80);
|
||||
aes_decrypt_cbc(block_data, total_read, ref.block_data, aes_game_ctx, 0x80, new_iv);
|
||||
|
||||
free(block_data);
|
||||
free(new_iv);
|
||||
return ref;
|
||||
}
|
||||
|
||||
// returns decrypted data on success, empty filesize on error
|
||||
decrypted_file decrypt_file(char* psse_file){
|
||||
size_t total_filesize = get_file_size(psse_file);
|
||||
uint64_t total_blocks = (total_filesize / 0x8000);
|
||||
|
||||
uint8_t title_key_copy[sizeof(title_key)];
|
||||
|
||||
uint32_t aes_header_ctx[0x3C];
|
||||
|
||||
FILE* psse_file_fd = fopen(psse_file, "rb");
|
||||
|
||||
decrypted_file plaintext_file;
|
||||
memset(&plaintext_file, 0x00, sizeof(decrypted_file));
|
||||
|
||||
psse_header file_psse_header;
|
||||
memset(&file_psse_header, 0x00, sizeof(psse_header));
|
||||
fread(&file_psse_header, sizeof(psse_header), 0x1, psse_file_fd);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[*] Decrypting: %s\n", psse_file);
|
||||
#endif
|
||||
|
||||
// Check magic number
|
||||
if(!((strncmp(file_psse_header.magic, "PSSE", 0x4) == 0) || (strncmp(file_psse_header.magic, "PSME", 0x4) == 0))){
|
||||
#ifdef DEBUG
|
||||
printf("[*] %s Is not a valid PSSE file.\n", psse_file);
|
||||
#endif
|
||||
return plaintext_file;
|
||||
}
|
||||
|
||||
// Chceck version
|
||||
if(file_psse_header.version != 0x01){
|
||||
#ifdef DEBUG
|
||||
printf("[*] %s has unknown PSSE version %i.\n", psse_file, file_psse_header.version);
|
||||
#endif
|
||||
return plaintext_file;
|
||||
}
|
||||
|
||||
// Check psse type
|
||||
if(file_psse_header.psse_type != 0x01){
|
||||
#ifdef DEBUG
|
||||
printf("[*] %s has unknown PSSE type %i.\n", psse_file, file_psse_header.version);
|
||||
#endif
|
||||
return plaintext_file;
|
||||
}
|
||||
|
||||
int psm_dev = 0;
|
||||
|
||||
// Check content id
|
||||
if(strlen(file_psse_header.content_id) == 0x24) {
|
||||
if(strcmp(file_psse_header.content_id, title_content_id) == 0){ // Retail PSM
|
||||
aes_key_setup(header_key, aes_header_ctx, 0x80);
|
||||
}
|
||||
else if(strcmp(file_psse_header.content_id, runtime_content_id)){ // Runtime Libary
|
||||
memcpy(title_key_copy, title_key, sizeof(title_key));
|
||||
memcpy(title_key, runtime_title_key, sizeof(title_key));
|
||||
aes_key_setup(header_key, aes_header_ctx, 0x80);
|
||||
psm_dev = 1;
|
||||
}
|
||||
else{
|
||||
return plaintext_file;
|
||||
}
|
||||
}
|
||||
else { // Debug PSM
|
||||
aes_key_setup(header_key_psmdev, aes_header_ctx, 0x80);
|
||||
}
|
||||
|
||||
aes_decrypt_cbc(file_psse_header.file_iv, sizeof(title_iv), title_iv, aes_header_ctx, 0x80, header_iv);
|
||||
|
||||
char* plaintext = malloc(file_psse_header.file_size);
|
||||
uintptr_t plaintext_ptr = 0;
|
||||
|
||||
MD5Context md5_ctx;
|
||||
md5Init(&md5_ctx);
|
||||
|
||||
for(int i = 0; i <= total_blocks; i++){
|
||||
psse_block_ref block_ref = decrypt_block(psse_file_fd, i, total_blocks, file_psse_header.file_size);
|
||||
|
||||
md5Update(&md5_ctx, block_ref.block_data, block_ref.block_size);
|
||||
|
||||
memcpy(plaintext+plaintext_ptr, block_ref.block_data, block_ref.block_size);
|
||||
plaintext_ptr += block_ref.block_size;
|
||||
}
|
||||
|
||||
md5Finalize(&md5_ctx);
|
||||
|
||||
if(memcmp(file_psse_header.md5_hash, md5_ctx.digest, 0x10) != 0){
|
||||
#ifdef DEBUG
|
||||
printf("[*] MD5 Hash did not match expected.\n");
|
||||
print_buffer("Got MD5", md5_ctx.digest, 0x10);
|
||||
print_buffer("Expected MD5", file_psse_header.md5_hash, 0x10);
|
||||
#endif
|
||||
return plaintext_file;
|
||||
}
|
||||
|
||||
|
||||
fclose(psse_file_fd);
|
||||
|
||||
plaintext_file.data = plaintext;
|
||||
plaintext_file.file_size = file_psse_header.file_size;
|
||||
|
||||
if(psm_dev){
|
||||
memcpy(title_key, title_key_copy, sizeof(title_key));
|
||||
}
|
||||
|
||||
return plaintext_file;
|
||||
}
|
||||
|
||||
int decrypt_all_files(char* application_folder, char* psse_list, size_t psse_list_sz){
|
||||
uint64_t start_point = 0;
|
||||
size_t sz = 0;
|
||||
|
||||
char rel_path[PATH_MAX];
|
||||
for(uint64_t i = 0; i < psse_list_sz; i++){
|
||||
|
||||
if(psse_list[i] == '\r' || psse_list[i] == '\n') {
|
||||
|
||||
if (sz == 0){
|
||||
goto next;
|
||||
}
|
||||
|
||||
char* cur_filename = (char*)malloc(sz+1);
|
||||
memset(cur_filename, 0x00, sz+1);
|
||||
strncpy(cur_filename, (psse_list+start_point), sz);
|
||||
|
||||
memset(rel_path, 0x00, sizeof(rel_path));
|
||||
snprintf(rel_path, sizeof(rel_path), "%s\\%s", application_folder, cur_filename);
|
||||
free(cur_filename);
|
||||
|
||||
fix_paths(rel_path);
|
||||
|
||||
if(is_dir(rel_path)){
|
||||
goto next;
|
||||
}
|
||||
|
||||
decrypted_file dec_file = decrypt_file(rel_path);
|
||||
|
||||
if(dec_file.data == NULL){
|
||||
#ifdef DEBUG
|
||||
printf("[*] Decryption failed.\n");
|
||||
#endif
|
||||
return -3;
|
||||
}
|
||||
|
||||
// Write decrypted file to disk.
|
||||
FILE* dec_file_fd = fopen(rel_path, "wb");
|
||||
if(dec_file_fd == NULL){
|
||||
#ifdef DEBUG
|
||||
printf("[*] Failed to open %s for writing.\n", rel_path);
|
||||
#endif
|
||||
return -4;
|
||||
}
|
||||
fwrite(dec_file.data, dec_file.file_size, 0x1, dec_file_fd);
|
||||
fclose(dec_file_fd);
|
||||
|
||||
free(dec_file.data);
|
||||
|
||||
next:
|
||||
start_point = i+1;
|
||||
sz = 0;
|
||||
continue;
|
||||
}
|
||||
sz++;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
memset(title_content_id, 0x00, sizeof(title_content_id));
|
||||
memset(title_iv, 0x00, sizeof(title_iv));
|
||||
memset(title_key, 0x00, sizeof(title_key));
|
||||
|
||||
if(argc <= 1){
|
||||
printf("PSSE Decryptor.\n");
|
||||
printf("Usage: <PSM_FOLDER>\n",argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* psm_folder = argv[1];
|
||||
|
||||
char application_folder[PATH_MAX];
|
||||
char psse_list[PATH_MAX];
|
||||
char rif_file[PATH_MAX];
|
||||
|
||||
snprintf(application_folder, sizeof(application_folder), "%s\\RO\\Application", psm_folder);
|
||||
snprintf(psse_list, sizeof(psse_list), "%s\\psse.list", application_folder);
|
||||
snprintf(rif_file, sizeof(rif_file), "%s\\RO\\License\\FAKE.rif", psm_folder);
|
||||
|
||||
fix_paths(application_folder);
|
||||
fix_paths(psse_list);
|
||||
fix_paths(rif_file);
|
||||
|
||||
if(read_rif(rif_file) < 0){
|
||||
printf("[*] Unable to read RIF: %s\n", rif_file);
|
||||
return -1;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
print_buffer("Title Key", title_key, sizeof(title_key));
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
decrypted_file plaintext_psse_list = decrypt_file(psse_list);
|
||||
if(plaintext_psse_list.data == NULL){
|
||||
printf("[*] Decryption failed.\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
// Write decrypted psse.list.
|
||||
FILE* psse_list_fd = fopen(psse_list, "wb");
|
||||
fwrite(plaintext_psse_list.data, plaintext_psse_list.file_size, 0x1, psse_list_fd);
|
||||
fclose(psse_list_fd);
|
||||
|
||||
int res = decrypt_all_files(application_folder, plaintext_psse_list.data, plaintext_psse_list.file_size);
|
||||
if (res < 0){
|
||||
return res;
|
||||
}
|
||||
free(plaintext_psse_list.data);
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm
|
||||
* and modified slightly to be functionally identical but condensed into control structures.
|
||||
*/
|
||||
|
||||
#include "md5.h"
|
||||
|
||||
/*
|
||||
* Constants defined by the MD5 algorithm
|
||||
*/
|
||||
#define A 0x67452301
|
||||
#define B 0xefcdab89
|
||||
#define C 0x98badcfe
|
||||
#define D 0x10325476
|
||||
|
||||
static uint32_t S[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
||||
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
||||
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
|
||||
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};
|
||||
|
||||
static uint32_t K[] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
|
||||
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
||||
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
||||
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
|
||||
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
|
||||
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
||||
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
||||
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
||||
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
|
||||
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
||||
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
|
||||
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
||||
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
||||
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};
|
||||
|
||||
/*
|
||||
* Bit-manipulation functions defined by the MD5 algorithm
|
||||
*/
|
||||
#define F(X, Y, Z) ((X & Y) | (~X & Z))
|
||||
#define G(X, Y, Z) ((X & Z) | (Y & ~Z))
|
||||
#define H(X, Y, Z) (X ^ Y ^ Z)
|
||||
#define I(X, Y, Z) (Y ^ (X | ~Z))
|
||||
|
||||
/*
|
||||
* Padding used to make the size (in bits) of the input congruent to 448 mod 512
|
||||
*/
|
||||
static uint8_t PADDING[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
/*
|
||||
* Initialize a context
|
||||
*/
|
||||
void md5Init(MD5Context *ctx){
|
||||
ctx->size = (uint64_t)0;
|
||||
|
||||
ctx->buffer[0] = (uint32_t)A;
|
||||
ctx->buffer[1] = (uint32_t)B;
|
||||
ctx->buffer[2] = (uint32_t)C;
|
||||
ctx->buffer[3] = (uint32_t)D;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add some amount of input to the context
|
||||
*
|
||||
* If the input fills out a block of 512 bits, apply the algorithm (md5Step)
|
||||
* and save the result in the buffer. Also updates the overall size.
|
||||
*/
|
||||
void md5Update(MD5Context *ctx, uint8_t *input_buffer, size_t input_len){
|
||||
uint32_t input[16];
|
||||
unsigned int offset = ctx->size % 64;
|
||||
ctx->size += (uint64_t)input_len;
|
||||
|
||||
// Copy each byte in input_buffer into the next space in our context input
|
||||
for(unsigned int i = 0; i < input_len; ++i){
|
||||
ctx->input[offset++] = (uint8_t)*(input_buffer + i);
|
||||
|
||||
// If we've filled our context input, copy it into our local array input
|
||||
// then reset the offset to 0 and fill in a new buffer.
|
||||
// Every time we fill out a chunk, we run it through the algorithm
|
||||
// to enable some back and forth between cpu and i/o
|
||||
if(offset % 64 == 0){
|
||||
for(unsigned int j = 0; j < 16; ++j){
|
||||
// Convert to little-endian
|
||||
// The local variable `input` our 512-bit chunk separated into 32-bit words
|
||||
// we can use in calculations
|
||||
input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |
|
||||
(uint32_t)(ctx->input[(j * 4) + 2]) << 16 |
|
||||
(uint32_t)(ctx->input[(j * 4) + 1]) << 8 |
|
||||
(uint32_t)(ctx->input[(j * 4)]);
|
||||
}
|
||||
md5Step(ctx->buffer, input);
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Pad the current input to get to 448 bytes, append the size in bits to the very end,
|
||||
* and save the result of the final iteration into digest.
|
||||
*/
|
||||
void md5Finalize(MD5Context *ctx){
|
||||
uint32_t input[16];
|
||||
unsigned int offset = ctx->size % 64;
|
||||
unsigned int padding_length = offset < 56 ? 56 - offset : (56 + 64) - offset;
|
||||
|
||||
// Fill in the padding andndo the changes to size that resulted from the update
|
||||
md5Update(ctx, PADDING, padding_length);
|
||||
ctx->size -= (uint64_t)padding_length;
|
||||
|
||||
// Do a final update (internal to this function)
|
||||
// Last two 32-bit words are the two halves of the size (converted from bytes to bits)
|
||||
for(unsigned int j = 0; j < 14; ++j){
|
||||
input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |
|
||||
(uint32_t)(ctx->input[(j * 4) + 2]) << 16 |
|
||||
(uint32_t)(ctx->input[(j * 4) + 1]) << 8 |
|
||||
(uint32_t)(ctx->input[(j * 4)]);
|
||||
}
|
||||
input[14] = (uint32_t)(ctx->size * 8);
|
||||
input[15] = (uint32_t)((ctx->size * 8) >> 32);
|
||||
|
||||
md5Step(ctx->buffer, input);
|
||||
|
||||
// Move the result into digest (convert from little-endian)
|
||||
for(unsigned int i = 0; i < 4; ++i){
|
||||
ctx->digest[(i * 4) + 0] = (uint8_t)((ctx->buffer[i] & 0x000000FF));
|
||||
ctx->digest[(i * 4) + 1] = (uint8_t)((ctx->buffer[i] & 0x0000FF00) >> 8);
|
||||
ctx->digest[(i * 4) + 2] = (uint8_t)((ctx->buffer[i] & 0x00FF0000) >> 16);
|
||||
ctx->digest[(i * 4) + 3] = (uint8_t)((ctx->buffer[i] & 0xFF000000) >> 24);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Step on 512 bits of input with the main MD5 algorithm.
|
||||
*/
|
||||
void md5Step(uint32_t *buffer, uint32_t *input){
|
||||
uint32_t AA = buffer[0];
|
||||
uint32_t BB = buffer[1];
|
||||
uint32_t CC = buffer[2];
|
||||
uint32_t DD = buffer[3];
|
||||
|
||||
uint32_t E;
|
||||
|
||||
unsigned int j;
|
||||
|
||||
for(unsigned int i = 0; i < 64; ++i){
|
||||
switch(i / 16){
|
||||
case 0:
|
||||
E = F(BB, CC, DD);
|
||||
j = i;
|
||||
break;
|
||||
case 1:
|
||||
E = G(BB, CC, DD);
|
||||
j = ((i * 5) + 1) % 16;
|
||||
break;
|
||||
case 2:
|
||||
E = H(BB, CC, DD);
|
||||
j = ((i * 3) + 5) % 16;
|
||||
break;
|
||||
default:
|
||||
E = I(BB, CC, DD);
|
||||
j = (i * 7) % 16;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t temp = DD;
|
||||
DD = CC;
|
||||
CC = BB;
|
||||
BB = BB + rotateLeft(AA + E + K[i] + input[j], S[i]);
|
||||
AA = temp;
|
||||
}
|
||||
|
||||
buffer[0] += AA;
|
||||
buffer[1] += BB;
|
||||
buffer[2] += CC;
|
||||
buffer[3] += DD;
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions that will return a pointer to the hash of the provided input
|
||||
*/
|
||||
uint8_t* md5String(char *input){
|
||||
MD5Context ctx;
|
||||
md5Init(&ctx);
|
||||
md5Update(&ctx, (uint8_t *)input, strlen(input));
|
||||
md5Finalize(&ctx);
|
||||
|
||||
uint8_t *result = malloc(16);
|
||||
memcpy(result, ctx.digest, 16);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t* md5File(FILE *file){
|
||||
char *input_buffer = malloc(1024);
|
||||
size_t input_size = 0;
|
||||
|
||||
MD5Context ctx;
|
||||
md5Init(&ctx);
|
||||
|
||||
while((input_size = fread(input_buffer, 1, 1024, file)) > 0){
|
||||
md5Update(&ctx, (uint8_t *)input_buffer, input_size);
|
||||
}
|
||||
|
||||
md5Finalize(&ctx);
|
||||
|
||||
free(input_buffer);
|
||||
|
||||
uint8_t *result = malloc(16);
|
||||
memcpy(result, ctx.digest, 16);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Rotates a 32-bit word left by n bits
|
||||
*/
|
||||
uint32_t rotateLeft(uint32_t x, uint32_t n){
|
||||
return (x << n) | (x >> (32 - n));
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct{
|
||||
uint64_t size; // Size of input in bytes
|
||||
uint32_t buffer[4]; // Current accumulation of hash
|
||||
uint8_t input[64]; // Input to be used in the next step
|
||||
uint8_t digest[16]; // Result of algorithm
|
||||
}MD5Context;
|
||||
|
||||
void md5Init(MD5Context *ctx);
|
||||
void md5Update(MD5Context *ctx, uint8_t *input, size_t input_len);
|
||||
void md5Finalize(MD5Context *ctx);
|
||||
void md5Step(uint32_t *buffer, uint32_t *input);
|
||||
|
||||
uint8_t* md5String(char *input);
|
||||
uint8_t* md5File(FILE *file);
|
||||
|
||||
uint32_t F(uint32_t X, uint32_t Y, uint32_t Z);
|
||||
uint32_t G(uint32_t X, uint32_t Y, uint32_t Z);
|
||||
uint32_t H(uint32_t X, uint32_t Y, uint32_t Z);
|
||||
uint32_t I(uint32_t X, uint32_t Y, uint32_t Z);
|
||||
|
||||
uint32_t rotateLeft(uint32_t x, uint32_t n);
|
|
@ -225,7 +225,7 @@ if len(sys.argv) <= 1:
|
|||
file = sys.argv[1]
|
||||
fpath = file.encode("UTF-8")
|
||||
|
||||
# Some dumb dirbustnig shit.
|
||||
# Some dumb dirbusting shit.
|
||||
applications_folder = b"/RO/Application/"
|
||||
license_file = os.path.normpath(fpath+b"/RO/License/FAKE.rif")
|
||||
psse_list = os.path.normpath(fpath+applications_folder+b"psse.list")
|
||||
|
|
Loading…
Reference in New Issue