Delete unsegareborn directory

This commit is contained in:
Eren
2025-06-24 13:24:16 +03:00
parent 8b600f4388
commit 9735cb2cf5
13 changed files with 0 additions and 2631 deletions
-7
View File
@@ -1,7 +0,0 @@
#include "bootid.h"
#include <stdio.h>
void format_timestamp(const Timestamp* ts, char* buffer, size_t buffer_size) {
snprintf(buffer, buffer_size, "%04d%02d%02d%02d%02d%02d",
ts->year, ts->month, ts->day, ts->hour, ts->minute, ts->second);
}
-74
View File
@@ -1,74 +0,0 @@
#ifndef BOOTID_H
#define BOOTID_H
#include <stdint.h>
#include <stdbool.h>
static const uint8_t BOOTID_KEY[16] = {
0x09, 0xCA, 0x5E, 0xFD, 0x30, 0xC9, 0xAA, 0xEF,
0x38, 0x04, 0xD0, 0xA7, 0xE3, 0xFA, 0x71, 0x20
};
static const uint8_t BOOTID_IV[16] = {
0xB1, 0x55, 0xC2, 0x2C, 0x2E, 0x7F, 0x04, 0x91,
0xFA, 0x7F, 0x0F, 0xDC, 0x21, 0x7A, 0xFF, 0x90
};
enum ContainerType {
CONTAINER_TYPE_OS = 0x00,
CONTAINER_TYPE_APP = 0x01,
CONTAINER_TYPE_OPTION = 0x02
};
#pragma pack(push, 1)
typedef struct {
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t unk1;
} Timestamp;
typedef struct {
uint8_t release;
uint8_t minor;
uint16_t major;
} Version;
typedef union {
Version version;
uint8_t option[4];
} GameVersion;
typedef struct {
uint32_t crc32;
uint32_t length;
uint8_t signature[4];
uint8_t unk1;
uint8_t container_type;
uint8_t sequence_number;
bool use_custom_iv;
uint8_t game_id[4];
Timestamp target_timestamp;
GameVersion target_version;
uint64_t block_count;
uint64_t block_size;
uint64_t header_block_count;
uint64_t unk2;
uint8_t os_id[3];
uint8_t os_generation;
Timestamp source_timestamp;
Version source_version;
Version os_version;
uint8_t padding[8];
uint8_t extra_padding[4];
} BootId;
#pragma pack(pop)
void format_timestamp(const Timestamp* ts, char* buffer, size_t buffer_size);
#endif // BOOTID_H
-11
View File
@@ -1,11 +0,0 @@
#ifndef COMMON_H
#include <direct.h>
#define PATH_SEPARATOR "\\"
#define MAX_PATH_LENGTH 256
#define MAX_FILENAME_LENGTH 256
#define MKDIR(path) _mkdir(path)
#endif // !COMMON_H
-344
View File
@@ -1,344 +0,0 @@
#include "crypto.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
#include <openssl/aes.h>
void calculate_page_iv(uint64_t file_offset, const uint8_t* file_iv, uint8_t* page_iv) {
for (int i = 0; i < 16; i++) {
page_iv[i] = file_iv[i] ^ ((file_offset >> (8 * (i % 8))) & 0xFF);
}
}
bool calculate_file_iv(
const uint8_t key[16],
const uint8_t expected_header[16],
const uint8_t* first_page,
uint8_t out_iv[16]
) {
uint8_t iv[16];
uint8_t header[16];
memcpy(header, first_page, 16);
calculate_page_iv(0, expected_header, iv);
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
if (!ctx)
return false;
int len;
int plaintext_len;
if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_128_cbc(), NULL, key, iv)) {
EVP_CIPHER_CTX_free(ctx);
return false;
}
EVP_CIPHER_CTX_set_padding(ctx, 0);
uint8_t decrypted[16];
if (1 != EVP_DecryptUpdate(ctx, decrypted, &len, header, 16)) {
EVP_CIPHER_CTX_free(ctx);
return false;
}
plaintext_len = len;
if (1 != EVP_DecryptFinal_ex(ctx, decrypted + len, &len)) {
EVP_CIPHER_CTX_free(ctx);
return false;
}
plaintext_len += len;
EVP_CIPHER_CTX_free(ctx);
if (plaintext_len != 16)
return false;
memcpy(out_iv, decrypted, 16);
return true;
}
typedef struct {
const char* game_id;
uint8_t key[16];
uint8_t iv[16];
bool has_iv;
} GameKeyEntry;
// game keys array
static const GameKeyEntry game_keys[] = {
// Nu Firmware / Hardware Test
{"SBZS", {0x2e, 0xcb, 0xcf, 0xf6, 0x5c, 0xe0, 0xab, 0xec, 0xc1, 0x05, 0x47, 0xf8, 0xac, 0x83, 0x51, 0xd8}, {0xf2, 0xac, 0x6c, 0x28, 0x17, 0xd0, 0x57, 0x4b, 0xba, 0x11, 0x3d, 0x49, 0x7e, 0x31, 0x9f, 0x3e}, true},
// KEY CHIP NU FACTORY / KEY CHIP NUSX FACTORY
{"SBZT", {0x9a, 0xb9, 0xce, 0x55, 0xed, 0x9c, 0x19, 0x4a, 0x71, 0x5a, 0x73, 0xa7, 0x69, 0x9f, 0x79, 0x5b}, {0x85, 0x52, 0xde, 0x88, 0xfe, 0xdd, 0xa6, 0xe8, 0x59, 0x36, 0x9f, 0xb0, 0x00, 0xf4, 0x4d, 0x5b}, true},
// Nu Firmware / Hardware Test
{"SBZU", {0xeb, 0x12, 0x28, 0x25, 0x4c, 0xdd, 0x30, 0x77, 0xeb, 0x3e, 0x44, 0x1c, 0x02, 0x27, 0xbf, 0x40}, {0x3f, 0x9b, 0x46, 0x76, 0x11, 0x8c, 0xee, 0x12, 0x9f, 0xe2, 0xf1, 0xcb, 0x27, 0x47, 0xbc, 0xa5}, true},
// Project DIVA Arcade Future Tone
{"SBZV", {0x32, 0x74, 0xa3, 0x99, 0x59, 0x4d, 0x84, 0x77, 0x96, 0x25, 0x94, 0x0b, 0x69, 0xc0, 0x2d, 0x3f}, {0x67, 0x5b, 0xa6, 0x6d, 0x29, 0xc8, 0x79, 0x23, 0xf5, 0xf1, 0x54, 0xc4, 0x06, 0xaf, 0xee, 0x42}, true},
// Wonderland Wars
{"SDAP", {0x41, 0xb5, 0x02, 0x7c, 0x5e, 0x99, 0xd9, 0x4a, 0xa9, 0x33, 0x5d, 0x6d, 0x71, 0x83, 0x8e, 0xcf}, {0x41, 0xb5, 0x02, 0x7c, 0x5e, 0x99, 0xd9, 0x4a, 0xa9, 0x33, 0x5d, 0x6d, 0x71, 0x83, 0x8e, 0xcf}, true},
// Herobank Arcade
{"SDAQ", {0xc2, 0x8f, 0x22, 0xbc, 0x1b, 0x33, 0x9a, 0xe6, 0x41, 0x80, 0x73, 0x98, 0x86, 0xdc, 0x83, 0xd6}, {0x0a, 0x29, 0xfd, 0x14, 0x5d, 0x72, 0xbf, 0x8d, 0xed, 0xd4, 0x36, 0x02, 0x5d, 0xf0, 0xa9, 0xfc}, true},
// Uranai Collection: Torotte
{"SDAV", {0xee, 0xd9, 0x55, 0x13, 0x26, 0x6a, 0x49, 0x9a, 0x55, 0xe2, 0x65, 0xb0, 0x49, 0x16, 0x9c, 0x44}, {0x84, 0xc0, 0xe5, 0x93, 0x1d, 0x91, 0xa6, 0xa4, 0x77, 0xd6, 0x2c, 0x27, 0x15, 0x46, 0x05, 0x6e}, true},
// Shin Kouchuu Ouja Mushiking
{"SDBE", {0x70, 0x53, 0xfb, 0x94, 0x45, 0x72, 0xe5, 0xb6, 0x31, 0xa6, 0x65, 0xce, 0xf4, 0xb5, 0xbc, 0xdd}, {0xae, 0x4d, 0x7e, 0x88, 0x40, 0x02, 0xc7, 0x9e, 0xb3, 0x57, 0x11, 0x55, 0x4d, 0x61, 0x30, 0x57}, true},
// E-DEL Sand
{"SDBN", {0xc1, 0xf1, 0x4a, 0xe2, 0xe8, 0x5b, 0x09, 0x5e, 0x31, 0x3c, 0x8b, 0xae, 0xc1, 0x25, 0x80, 0x5e}, {0x3c, 0x53, 0x8e, 0xea, 0x66, 0x25, 0x1a, 0xcd, 0x54, 0x04, 0xb9, 0x3f, 0x89, 0x76, 0xa7, 0xf7}, true},
// CHUNITHM
{"SDBT", {0xa6, 0xa8, 0x70, 0x67, 0x1f, 0xd4, 0x32, 0xec, 0x63, 0x7a, 0xdf, 0x7a, 0x82, 0x2f, 0x97, 0xda}, {0x2c, 0x27, 0x7f, 0x31, 0xcd, 0x55, 0x0c, 0xfa, 0x2c, 0x99, 0x3b, 0x4d, 0xd5, 0x6b, 0x85, 0xae}, true},
// Sonic Dash Extreme
{"SDBX", {0x3d, 0xc1, 0x9c, 0x2d, 0x0c, 0x20, 0xac, 0x19, 0x9d, 0x5f, 0xa4, 0x6e, 0x7f, 0x63, 0x35, 0xa6}, {0xd8, 0xf0, 0x29, 0xec, 0x90, 0xfe, 0x55, 0xbe, 0x67, 0x58, 0x4f, 0x74, 0x2c, 0x55, 0xef, 0x8b}, true},
// Kancolle Arcade
{"SDBZ", {0x52, 0x1b, 0xde, 0x44, 0x60, 0xf4, 0x18, 0x4e, 0xdd, 0x87, 0x91, 0x36, 0xad, 0xee, 0xa5, 0xee}, {0x1b, 0x83, 0x24, 0x03, 0x2d, 0xb6, 0x9d, 0x7b, 0x09, 0x54, 0x79, 0x4a, 0xa2, 0x29, 0xfe, 0x68}, true},
// crossbeats REV.
{"SDCA", {0x16, 0x49, 0x49, 0x0a, 0x03, 0xd6, 0xc2, 0xae, 0xc1, 0xc4, 0x96, 0x98, 0x2c, 0xb0, 0x40, 0x5c}, {0x46, 0x80, 0x71, 0x1c, 0x7e, 0x67, 0xa2, 0x6f, 0x92, 0x30, 0xd5, 0xaf, 0x74, 0xb5, 0xdc, 0xfb}, true},
// nailpuri
{"SDCD", {0x43, 0xb3, 0x85, 0x02, 0xd8, 0xf6, 0xd3, 0xc7, 0xb0, 0x2b, 0x95, 0xfc, 0x28, 0xdb, 0x53, 0x08}, {0x6d, 0xfc, 0xb9, 0x4b, 0xf7, 0x4f, 0x15, 0x2b, 0x55, 0xf3, 0xe0, 0xc7, 0xf3, 0x5b, 0x44, 0xb5}, true},
// Luigi's Mansion Arcade
{"SDCF", {0xdf, 0x98, 0x68, 0x83, 0xda, 0x83, 0x75, 0x38, 0xe3, 0x7b, 0x95, 0x9a, 0x3e, 0x41, 0x17, 0xcd}, {0xda, 0xbf, 0x53, 0x97, 0x38, 0x85, 0x2f, 0x17, 0x71, 0x48, 0x11, 0xaf, 0x70, 0x43, 0x5a, 0x83}, true},
// Mario & Sonic at Rio Olympic Games
{"SDCH", {0xe2, 0xda, 0x76, 0x9e, 0x94, 0xf1, 0xd3, 0xac, 0xa1, 0x93, 0x0c, 0xdb, 0xe0, 0x70, 0x8c, 0x9f}, {0xc7, 0xdc, 0xce, 0x20, 0x3c, 0x84, 0xab, 0x04, 0x77, 0x23, 0x6d, 0x69, 0x75, 0x70, 0xda, 0xdc}, true},
// KEY CHIP NUSX EDB SOC
{"SDCR", {0x49, 0x61, 0xa5, 0x1f, 0xd3, 0x6f, 0x14, 0xe7, 0x26, 0x64, 0xf5, 0x23, 0x73, 0x05, 0x21, 0x60}, {0x25, 0xd7, 0xd1, 0x34, 0x1a, 0x28, 0x2c, 0x5e, 0x0a, 0x34, 0xc6, 0x45, 0x62, 0xc0, 0x23, 0xec}, true},
// Celevie
{"SDCT", {0xd6, 0xae, 0x51, 0xf1, 0x0e, 0xc7, 0x6d, 0xa9, 0x3c, 0x98, 0x18, 0x00, 0xfc, 0x3a, 0xd3, 0xcb}, {0xfb, 0x8e, 0x43, 0xe2, 0x80, 0xd3, 0x30, 0xd0, 0x65, 0x81, 0x73, 0x2f, 0x2e, 0x11, 0xa6, 0xdc}, true},
// CYTUS Ω
{"SDCX", {0x79, 0x50, 0x4c, 0xcc, 0x50, 0x9b, 0x67, 0xd1, 0xf7, 0xa3, 0xf5, 0x93, 0xe6, 0xf9, 0xd9, 0xd6}, {0x15, 0x51, 0xea, 0x89, 0x26, 0xf2, 0xae, 0xe2, 0x33, 0xee, 0xc3, 0x09, 0xde, 0x3e, 0x5f, 0x3c}, true},
// KEY CHIP NUSX1.1 TDW
{"SDDB", {0x87, 0x56, 0x79, 0xb2, 0xcd, 0x16, 0x37, 0x96, 0x2b, 0x0d, 0xb2, 0x5c, 0x51, 0xfb, 0x21, 0xa6}, {0x8e, 0xf4, 0x47, 0x22, 0xa0, 0x56, 0x6e, 0x8f, 0x57, 0x23, 0x56, 0x24, 0x56, 0x87, 0xfb, 0xe5}, true},
// Sangokushi Taisen
{"SDDD", {0x56, 0x4e, 0x96, 0x78, 0x73, 0xde, 0x6c, 0xbc, 0xd2, 0x2e, 0xfe, 0xca, 0x69, 0x52, 0xe9, 0xdc}, {0x4e, 0x3d, 0xd4, 0x65, 0xcf, 0x09, 0xcd, 0x82, 0xb2, 0x59, 0xf7, 0xbe, 0xd5, 0xfc, 0x2d, 0x6d}, true},
// Initial D Arcade Stage Zero
{"SDDF", {0x65, 0x05, 0x85, 0x73, 0xa0, 0xcb, 0x81, 0x74, 0x9e, 0x69, 0x4a, 0xe1, 0x64, 0xc6, 0x1b, 0x04}, {0x98, 0x1c, 0x4f, 0x45, 0xe3, 0xc6, 0x95, 0x8f, 0x05, 0x4e, 0x5d, 0x00, 0x91, 0x6b, 0xdf, 0x2b}, true},
// KEY CHIP NU1.1 ESC
{"SDDJ", {0x63, 0x0f, 0xe5, 0x22, 0x76, 0x53, 0x7b, 0xd7, 0xfb, 0x26, 0x7a, 0xdf, 0x17, 0x5f, 0x4e, 0x99}, {0xdc, 0x57, 0x55, 0xbe, 0x57, 0xde, 0xd2, 0xcd, 0xb3, 0x44, 0x33, 0xbb, 0xba, 0x22, 0x04, 0xff}, true},
// APM3 Sample Program 2
{"SDDL", {0x99, 0x24, 0x58, 0x29, 0x5f, 0xd0, 0x6d, 0x6a, 0x8a, 0xf0, 0xdf, 0xb3, 0xf6, 0x85, 0x4c, 0x19}, {0x84, 0x84, 0x90, 0x6d, 0x4c, 0xd5, 0xfd, 0x22, 0x5e, 0x03, 0x28, 0x43, 0xed, 0x37, 0x49, 0x5d}, true},
// ALLS MX Factory Dummy
{"SDDM", {0x01, 0x27, 0x95, 0x82, 0x10, 0xf6, 0xae, 0x9b, 0xde, 0xb8, 0x97, 0x50, 0x18, 0xb5, 0xaf, 0x24}, {0x18, 0x17, 0x16, 0xba, 0xdc, 0xcf, 0xf4, 0xbc, 0x2b, 0x1e, 0x29, 0xae, 0x02, 0xa1, 0xbb, 0xbb}, true},
// Demo ID
{"SDDN", {0x41, 0xdd, 0x8e, 0x66, 0x29, 0x01, 0x17, 0xac, 0x67, 0xd3, 0x11, 0xa2, 0xf0, 0xa6, 0x41, 0x6e}, {0x73, 0xe1, 0x8e, 0x84, 0x18, 0xf6, 0xce, 0xef, 0xb1, 0x1e, 0x27, 0x67, 0xfd, 0xea, 0x19, 0x0c}, true},
// Soul Reverse
{"SDDP", {0xcf, 0x6d, 0x64, 0x42, 0x7e, 0xec, 0xa4, 0x76, 0x74, 0xe1, 0x7b, 0xcd, 0x46, 0xd1, 0xea, 0x8c}, {0xce, 0x51, 0x74, 0x09, 0x3d, 0x26, 0xca, 0x2a, 0x31, 0xb5, 0x85, 0x41, 0xe8, 0x5a, 0xc2, 0x76}, true},
// SEGA World Driver Championship
{"SDDS", {0x16, 0x1b, 0xec, 0x6d, 0x90, 0x98, 0x9d, 0x0e, 0x26, 0xd7, 0x91, 0x17, 0x06, 0x07, 0xa4, 0x40}, {0x81, 0xdc, 0x26, 0xa2, 0x70, 0x28, 0xe2, 0x09, 0x23, 0x32, 0x03, 0x8a, 0xa1, 0xbf, 0xfc, 0x47}, true},
// O.N.G.E.K.I.
{"SDDT", {0x3f, 0x76, 0x58, 0x72, 0x8b, 0x95, 0x17, 0xd3, 0x31, 0x4e, 0x68, 0x4f, 0xa2, 0xe2, 0xa0, 0x45}, {0x41, 0x57, 0x88, 0x33, 0xc5, 0x47, 0xaa, 0xff, 0x04, 0xdb, 0x59, 0x7a, 0x6e, 0x9e, 0xb7, 0x84}, true},
// Let's dzuri GO!
{"SDDU", {0x64, 0x9a, 0xe9, 0x98, 0x26, 0x25, 0xf9, 0x0c, 0x55, 0xaf, 0x86, 0x71, 0x3c, 0x55, 0xd3, 0xfd}, {0x18, 0x71, 0x16, 0xfc, 0x46, 0x47, 0xa7, 0xd3, 0xb6, 0xf2, 0x30, 0x3a, 0x34, 0xf0, 0xa2, 0xfe}, true},
// ALLS X / X2 Research & Development
{"SDDW", {0x11, 0x85, 0x65, 0xd3, 0x44, 0xf3, 0xe1, 0x4c, 0xa6, 0x92, 0x99, 0xee, 0xac, 0x04, 0x9b, 0xb9}, {0x9d, 0x6d, 0x39, 0x2e, 0xc3, 0x5e, 0xd9, 0x4e, 0xf9, 0xfe, 0x0a, 0x5b, 0xe0, 0x57, 0x39, 0x81}, true},
// KEY CHIP ALLS X FACTORY
{"SDDX", {0x42, 0x8b, 0xff, 0x0f, 0x9e, 0x7a, 0xaf, 0xc1, 0x69, 0xa7, 0xa7, 0x57, 0x51, 0xff, 0xda, 0x98}, {0xf8, 0x25, 0x05, 0x94, 0xf4, 0x25, 0x33, 0x2c, 0x6d, 0x34, 0x9d, 0x7e, 0xa0, 0xe8, 0x66, 0x69}, true},
// Shin Kouchuu Ouja Mushiking TWN
{"SDEA", {0x9f, 0x9c, 0xf1, 0x48, 0xac, 0x3c, 0x50, 0xaa, 0xf9, 0x25, 0xaf, 0x1d, 0xfb, 0x27, 0xf5, 0x8b}, {0x4d, 0x8e, 0xbb, 0xd9, 0x71, 0x89, 0x6b, 0x8a, 0x4a, 0x3d, 0xd8, 0x4a, 0x23, 0xb3, 0x29, 0xfc}, true},
// WCCF FOOTISTA
{"SDEB", {0xd5, 0x11, 0xed, 0x69, 0x04, 0x15, 0xf6, 0x35, 0x98, 0x43, 0xa1, 0x34, 0xfd, 0x47, 0x83, 0x6a}, {0xac, 0x13, 0x9b, 0x38, 0x2a, 0xcd, 0xd1, 0x12, 0xe3, 0x15, 0x64, 0xea, 0x7f, 0x38, 0x18, 0x6c}, true},
// Chrono Regalia
{"SDEC", {0xf2, 0x72, 0xe5, 0x01, 0x68, 0x63, 0xaf, 0x2b, 0xa0, 0x33, 0x7f, 0x50, 0xde, 0x68, 0x6f, 0x6e}, {0x53, 0x27, 0xe1, 0x32, 0x63, 0x1e, 0x7f, 0x71, 0xb6, 0x1b, 0xe7, 0xcc, 0x0d, 0xf3, 0x82, 0xce}, true},
// CARD MAKER
{"SDED", {0x21, 0xfc, 0xec, 0x77, 0x9a, 0x16, 0x76, 0x9f, 0x52, 0x77, 0xa3, 0x6f, 0xb5, 0x42, 0x99, 0x2c}, {0x22, 0xb5, 0x02, 0x39, 0xf1, 0xb4, 0x0c, 0xcc, 0x3e, 0x55, 0xa2, 0xd6, 0x9c, 0x69, 0xb1, 0x60}, true},
// House of the Dead: Scarlet Dawn
{"SDEE", {0x19, 0x1e, 0xb7, 0x44, 0x06, 0x72, 0xda, 0xb0, 0x8d, 0xdb, 0xb7, 0x19, 0x5e, 0xfb, 0x35, 0x6f}, {0xc2, 0x78, 0xb5, 0x38, 0x6d, 0xc3, 0x8b, 0xd7, 0x6d, 0x71, 0xdb, 0xcd, 0x82, 0x69, 0x54, 0xcf}, true},
// FiZ
{ "SDEG", {0x72, 0x18, 0x53, 0xdb, 0xe2, 0xd3, 0x0b, 0xaf, 0xe2, 0x4f, 0x0e, 0xdb, 0xd2, 0x10, 0xde, 0xeb}, {0x4d, 0xfb, 0x0b, 0xce, 0xc8, 0x61, 0x59, 0xaa, 0xb2, 0x97, 0x16, 0x6b, 0xcd, 0x50, 0x9e, 0x6f}, true },
// Fate/Grand Order Arcade
{ "SDEJ", {0x9d, 0xe1, 0xea, 0x6a, 0xe3, 0x8d, 0x90, 0x11, 0xf5, 0x5d, 0x8e, 0xe8, 0x64, 0x39, 0x5d, 0x24}, {0xf6, 0x0c, 0xde, 0x21, 0x98, 0x28, 0x76, 0xd1, 0x2d, 0x17, 0x66, 0x2a, 0x48, 0xd9, 0x08, 0x36}, true },
// ALL.Net P.ras multi Ver.3
{ "SDEM", {0x70, 0x06, 0x17, 0xf2, 0x93, 0x69, 0x6c, 0x07, 0xfb, 0x9f, 0x35, 0x6d, 0x3b, 0x99, 0x24, 0x0d}, {0x66, 0x7d, 0x02, 0x6d, 0x6c, 0xdf, 0x32, 0x9f, 0xf3, 0x51, 0xdb, 0xaf, 0x70, 0x98, 0xe8, 0x1d}, true },
// StarHorse4 (Server) / MESTA Medal Station
{ "SDEP", {0xfa, 0x2b, 0x7c, 0xa5, 0x3a, 0x82, 0x3c, 0x15, 0x2d, 0x94, 0x09, 0x72, 0xcb, 0xf5, 0x32, 0xf5}, {0xf4, 0xaf, 0x35, 0x12, 0x0c, 0x48, 0x61, 0x77, 0x04, 0xbb, 0x5b, 0x84, 0x71, 0x79, 0x7a, 0x62}, true },
// Initial D Arcade Stage Zero (CHN)
{ "SDER", {0x7d, 0x73, 0x36, 0x7e, 0xbb, 0x21, 0x8e, 0xc8, 0x29, 0x30, 0xd5, 0x8d, 0xc6, 0xd7, 0x95, 0x0b}, {0x97, 0x88, 0xc3, 0xec, 0xa2, 0xdb, 0x6b, 0xa9, 0x2b, 0xac, 0x4f, 0x6f, 0x7b, 0x70, 0x63, 0x08}, true },
// House of the Dead: Scarlet Dawn (EXP)
{ "SDET", {0x46, 0x43, 0xe7, 0xb2, 0xc3, 0x00, 0x6e, 0x02, 0x64, 0x16, 0x3e, 0xdc, 0x85, 0x45, 0xfb, 0x72}, {0x61, 0x2b, 0xca, 0x81, 0xea, 0x29, 0x58, 0xff, 0xba, 0xc3, 0x6f, 0x78, 0x0f, 0x1e, 0xd6, 0x88}, true },
// KEY CHIP ALLS X HDZ
{ "SDEU", {0x23, 0xb3, 0xe9, 0xbb, 0x47, 0xe3, 0xac, 0x99, 0x98, 0xf6, 0xe6, 0xc1, 0xad, 0xc4, 0xae, 0x33}, {0xa9, 0x64, 0x71, 0x4c, 0xea, 0x60, 0x68, 0x84, 0x07, 0xbf, 0x55, 0x4b, 0xd1, 0xc2, 0x7e, 0xc2}, true },
// House of the Dead: Scarlet Dawn (CHN)
{ "SDEV", {0x3c, 0x1f, 0x01, 0x8d, 0x88, 0x92, 0x6d, 0x98, 0x16, 0x3b, 0x07, 0xa1, 0x56, 0x3a, 0x48, 0x18}, {0xca, 0x73, 0x73, 0xc9, 0xc7, 0xdf, 0xeb, 0xac, 0x0f, 0xc2, 0x42, 0x54, 0xc0, 0x30, 0xe4, 0xad}, true },
// maimai DX
{ "SDEZ", {0xd1, 0x36, 0xeb, 0xa0, 0x5d, 0x40, 0xe8, 0x26, 0x82, 0xe6, 0xaa, 0xd8, 0xd9, 0xe8, 0x68, 0x8c}, {0xc4, 0x84, 0xde, 0xea, 0xa0, 0x24, 0x9e, 0xf4, 0x66, 0x95, 0xf6, 0x36, 0x94, 0xb7, 0x37, 0x2f}, true },
// KEY CHIP ALLS X REC
{ "SDFA", {0x8e, 0x81, 0x6b, 0x43, 0x62, 0xdb, 0x24, 0xa2, 0x30, 0x87, 0x78, 0x85, 0x86, 0x4d, 0x20, 0x6d}, {0x8e, 0x5a, 0x0b, 0xa6, 0xa0, 0xa1, 0x15, 0x0d, 0x47, 0xd1, 0x2b, 0xdb, 0x64, 0xde, 0xbb, 0xa7}, true },
// WACCA
{ "SDFE", {0xf6, 0x17, 0x19, 0xc3, 0x71, 0xe5, 0xbc, 0xa6, 0x78, 0x8c, 0x13, 0x9a, 0x53, 0x09, 0x16, 0x17}, {0x67, 0xd4, 0x31, 0x73, 0xe3, 0x43, 0x81, 0x3f, 0xa2, 0x09, 0x7f, 0xd3, 0x29, 0x92, 0xa8, 0xe2}, true },
// SANDRA
{ "SDFG", {0x33, 0x98, 0xfb, 0x86, 0xbf, 0xe6, 0x30, 0xa1, 0x49, 0x79, 0x41, 0x18, 0x79, 0x86, 0x1a, 0xc7}, {0xa7, 0x94, 0xc4, 0x9c, 0x2c, 0x76, 0x39, 0xcd, 0x80, 0x57, 0x18, 0x07, 0xc1, 0x72, 0x46, 0xff}, true },
// Kemono Friends 3: Planet Tours
{ "SDFL", {0x24, 0x49, 0xb4, 0x80, 0x67, 0xb9, 0x17, 0x6a, 0x6e, 0x0f, 0x95, 0x63, 0x48, 0x1e, 0x97, 0xf4}, {0x61, 0x6f, 0x87, 0x10, 0x45, 0x46, 0x32, 0xeb, 0x4f, 0xb1, 0xd8, 0x9d, 0x8c, 0x19, 0xc1, 0x9a}, true },
// KEY CHIP ALLS X CASJ
{ "SDFN", {0x29, 0xf6, 0x2e, 0x22, 0xc6, 0xa9, 0xfd, 0x8b, 0xe3, 0x27, 0x63, 0x1c, 0x68, 0x54, 0x64, 0x05}, {0x2a, 0x86, 0x09, 0x76, 0xe6, 0xd9, 0x85, 0x13, 0x82, 0x5f, 0x29, 0x1e, 0x56, 0xcf, 0xb5, 0xee}, true },
// KEY CHIP NUSX FUTURE
{ "SDFP", {0x57, 0x0b, 0x87, 0x26, 0x3a, 0x7c, 0xa0, 0xaa, 0x4c, 0x13, 0x88, 0xe2, 0x04, 0xee, 0x6d, 0x4b}, {0x76, 0x40, 0x88, 0x60, 0x11, 0xa2, 0x30, 0x0a, 0x91, 0xfa, 0xd9, 0xf3, 0x6a, 0x8c, 0x47, 0x75}, true },
// StarHorse4
{ "SDFT", {0x92, 0xa2, 0x5f, 0x38, 0x8c, 0x50, 0x73, 0x7e, 0x39, 0xc3, 0xc2, 0xf0, 0x06, 0x64, 0x5f, 0x31}, {0xa9, 0x7e, 0x72, 0xf9, 0x90, 0x41, 0x74, 0x88, 0xcb, 0x4c, 0x67, 0xf8, 0xf0, 0xc3, 0xfb, 0x25}, true },
// Mario & Sonic at TOKYO Olympic
{ "SDFV", {0xfe, 0x82, 0xdb, 0x9a, 0x60, 0x29, 0x5d, 0x82, 0x9b, 0x95, 0xf0, 0x3c, 0x22, 0x76, 0x01, 0x8b}, {0x34, 0xd8, 0x27, 0x72, 0xae, 0x18, 0x17, 0x4f, 0x0a, 0x18, 0x1d, 0xc5, 0x33, 0x99, 0xea, 0x9c}, true },
// maimai DX (EXP)
{ "SDGA", {0x0a, 0x66, 0x10, 0xa6, 0x2e, 0xf6, 0x70, 0xc6, 0x5b, 0x7e, 0x7b, 0x17, 0x50, 0xff, 0xb7, 0xa1}, {0x17, 0xa2, 0xa2, 0x29, 0x15, 0xf8, 0x1c, 0x58, 0x96, 0xed, 0xbb, 0xa4, 0xc4, 0x12, 0x58, 0x5e}, true },
// maimai DX (CHN)
{ "SDGB", {0x7c, 0xa4, 0xe6, 0xb6, 0xf3, 0xd6, 0xe8, 0xb2, 0x64, 0x72, 0x97, 0x38, 0x87, 0xd7, 0xfa, 0x3a}, {0x53, 0xfe, 0x71, 0x35, 0x76, 0x2d, 0xe3, 0xf9, 0x7e, 0x7f, 0xe7, 0x6b, 0x0f, 0xef, 0x3f, 0x27}, true },
// Puyo Puyo e-Sports Arcade
{ "SDGH", {0xb3, 0xe3, 0x0e, 0x7e, 0xab, 0xac, 0x37, 0x67, 0xad, 0xe1, 0x3c, 0x69, 0xc9, 0xb2, 0xf2, 0x2b}, {0x03, 0xde, 0xae, 0xa3, 0x74, 0x2d, 0x69, 0x67, 0x5b, 0x36, 0xcd, 0xdc, 0x8b, 0x15, 0xac, 0x91}, true },
// WCCF FOOTISTA (EXP)
{ "SDGK", {0x9d, 0xc4, 0xa1, 0x7f, 0xc3, 0x9f, 0xca, 0x5a, 0x8a, 0x35, 0x89, 0x84, 0x80, 0x1c, 0xaa, 0xa7}, {0xe0, 0x44, 0x5b, 0x11, 0xdc, 0xfa, 0x0d, 0xae, 0x56, 0xc8, 0x5e, 0x87, 0x87, 0xe1, 0x1d, 0x9b}, true },
// KEY CHIP ALLS X HDZ CASJ
{ "SDGP", {0xc8, 0x7a, 0xb3, 0x12, 0x47, 0xe7, 0xb6, 0xff, 0x95, 0xfd, 0xd7, 0x9f, 0xb9, 0x1f, 0x9f, 0x37}, {0x24, 0x67, 0xab, 0x3c, 0x03, 0x1e, 0x3d, 0xc0, 0x56, 0x8b, 0x70, 0x77, 0xef, 0xd2, 0x7c, 0x36}, true },
// ROKUMEN
{ "SDGQ", {0xc5, 0x35, 0x6d, 0xae, 0x7b, 0x06, 0x6b, 0xce, 0x88, 0x98, 0x4a, 0xec, 0x36, 0xde, 0xb6, 0x2d}, {0x4e, 0x9f, 0x29, 0x82, 0x46, 0x0e, 0x2f, 0xd9, 0x07, 0xbd, 0xe1, 0x57, 0x09, 0xed, 0xfb, 0xa7}, true },
// CHUNITHM (EXP)
{ "SDGS", {0xa5, 0x15, 0x0c, 0xc5, 0x06, 0x5d, 0x2c, 0x59, 0xee, 0x2f, 0x8f, 0x33, 0x2c, 0xbd, 0x29, 0xd5}, {0x84, 0x01, 0x4d, 0x26, 0x69, 0x6f, 0x29, 0x0a, 0xd7, 0xea, 0xd7, 0x0c, 0x75, 0x49, 0xbd, 0x81}, true },
// Initial D THE ARCADE
{ "SDGT", {0x9d, 0x0b, 0xba, 0x20, 0xd1, 0xe8, 0x4f, 0x24, 0x59, 0x39, 0x9f, 0x53, 0x83, 0xbe, 0xee, 0x72}, {0x5d, 0x34, 0x00, 0x13, 0xfd, 0xfb, 0x24, 0x64, 0xd2, 0x53, 0x09, 0x36, 0x02, 0xfe, 0x4b, 0x64}, true },
// Pokemon COROGARENA
{ "SDGV", {0x57, 0x3f, 0x5c, 0x8c, 0xc4, 0x4f, 0x10, 0xf3, 0x1e, 0xc7, 0x49, 0xb6, 0x95, 0xeb, 0xe8, 0x86}, {0x6b, 0xfc, 0xa8, 0x6f, 0x9d, 0x20, 0x8a, 0x79, 0x44, 0xcd, 0xfc, 0x25, 0xea, 0x3c, 0xd2, 0x20}, true },
// Eiketsu Taisen: Sanzensekai no Hadou
{ "SDGY", {0xc0, 0x4b, 0x66, 0x3a, 0x59, 0x05, 0x5a, 0xcb, 0xdf, 0xeb, 0xc6, 0xd3, 0xdf, 0x0e, 0x6a, 0x04}, {0x76, 0xfc, 0x5f, 0x1d, 0x88, 0x60, 0x51, 0x07, 0x94, 0x7d, 0x0c, 0x1f, 0xf3, 0x47, 0x02, 0x2d}, true },
// Hori a Tale
{ "SDGZ", {0x9a, 0xd7, 0x4e, 0xfb, 0x20, 0x8d, 0x6e, 0xe4, 0xfe, 0x5e, 0xe7, 0x70, 0x33, 0x17, 0x12, 0xcf}, {0x45, 0xf6, 0xe5, 0x3f, 0x0e, 0xb8, 0xfa, 0xe6, 0x66, 0x5b, 0x45, 0x44, 0x4a, 0x61, 0xe2, 0x66}, true },
// CHUNITHM NEW!!
{ "SDHD", {0x3a, 0xbd, 0x00, 0xd7, 0xa8, 0x20, 0xce, 0x86, 0x2e, 0xaf, 0x47, 0x4b, 0xf6, 0xc8, 0xf3, 0x3e}, {0x0f, 0x1e, 0x7e, 0xea, 0x78, 0xda, 0x7e, 0x03, 0x7e, 0x05, 0x52, 0xc2, 0x84, 0x3e, 0x1b, 0x6a}, true },
// Gutsdzuri GO!
{ "SDHH", {0xfc, 0x6f, 0x88, 0x7f, 0x37, 0x17, 0xc5, 0xd6, 0x71, 0x31, 0x13, 0xb9, 0x2f, 0xa3, 0xfb, 0x27}, {0xad, 0x76, 0x60, 0x64, 0x60, 0xdb, 0xe1, 0xe9, 0x1e, 0x41, 0xbe, 0xf7, 0xab, 0x0c, 0x15, 0x35}, true },
// CHUNITHM (CHN)
{ "SDHJ", {0x98, 0x5e, 0xa6, 0x6e, 0xcb, 0x5b, 0x1f, 0x20, 0x8c, 0x90, 0xe2, 0xb8, 0x98, 0xf0, 0xb0, 0x73}, {0x16, 0x4a, 0x65, 0x42, 0x2e, 0x7f, 0x01, 0xb7, 0xf1, 0xb0, 0x84, 0x9f, 0xc7, 0x73, 0x7c, 0xdb}, true },
// UFO CATCHER LINK STATION
{ "SDHK", {0xbc, 0x92, 0xd6, 0x3c, 0x2a, 0x09, 0x9c, 0xa2, 0x31, 0x5a, 0x48, 0x3c, 0x30, 0x41, 0xfd, 0xd7}, {0xb1, 0x4d, 0x84, 0x49, 0xb6, 0xd4, 0x32, 0x5d, 0x83, 0xa2, 0x77, 0x4b, 0x13, 0xdd, 0x21, 0xff}, true },
// WACCA (CHN)
{ "SDHN", {0x89, 0x21, 0x23, 0xa2, 0x6d, 0x7c, 0x03, 0xd4, 0x9e, 0xdd, 0x12, 0xa8, 0x0e, 0xe0, 0xc5, 0x8f}, {0x76, 0xaa, 0x15, 0xa6, 0x86, 0x8b, 0x8d, 0xbd, 0xf7, 0x20, 0x79, 0x06, 0x35, 0x4d, 0x51, 0x69}, true },
// meityromantic
{ "SDHR", {0x1f, 0xb8, 0x97, 0xca, 0xb9, 0x7c, 0x81, 0x70, 0xa6, 0xac, 0x0a, 0x21, 0x68, 0x5c, 0x58, 0xd9}, {0xf9, 0xb6, 0x0f, 0x65, 0xb0, 0x1e, 0x8e, 0x83, 0x6a, 0x4b, 0xc2, 0x0f, 0x7d, 0x39, 0xfa, 0xf5}, true },
// ALLS System
{ "ACA", {0xe4, 0x28, 0x1b, 0xcf, 0x48, 0xc4, 0xd2, 0x8e, 0xb0, 0x57, 0x72, 0xce, 0x6f, 0x98, 0x58, 0x7a}, {0x6c, 0xee, 0x7f, 0x5a, 0x2c, 0x4b, 0x5f, 0x1e, 0x93, 0xc5, 0x94, 0x91, 0x14, 0xff, 0x0b, 0x74}, true },
// Read from {game_id}.bin for unknown keys.
{ NULL, {0}, {0}, false }
};
bool get_game_keys(const char* game_id, GameKeys* out_keys) {
const GameKeyEntry* entry = game_keys;
while (entry->game_id != NULL) {
if (strcmp(entry->game_id, game_id) == 0) {
memcpy(out_keys->key, entry->key, 16);
if (entry->has_iv) {
memcpy(out_keys->iv, entry->iv, 16);
out_keys->has_iv = true;
}
else {
memset(out_keys->iv, 0, 16);
out_keys->has_iv = false;
}
return true;
}
entry++;
}
char filename[256];
sprintf(filename, "%s.bin", game_id);
FILE* file = fopen(filename, "rb");
if (!file) {
return false;
}
uint8_t buffer[32];
size_t read_size = fread(buffer, 1, sizeof(buffer), file);
fclose(file);
if (read_size == 16) {
memcpy(out_keys->key, buffer, 16);
memset(out_keys->iv, 0, 16);
out_keys->has_iv = false;
return true;
}
else if (read_size == 32) {
memcpy(out_keys->key, buffer, 16);
memcpy(out_keys->iv, buffer + 16, 16);
if (memcmp(out_keys->iv, NTFS_HEADER, 16) == 0 || memcmp(out_keys->iv, EXFAT_HEADER, 16) == 0) {
out_keys->has_iv = false;
}
else {
out_keys->has_iv = true;
}
return true;
}
else {
return false;
}
}
-44
View File
@@ -1,44 +0,0 @@
#ifndef CRYPTO_H
#define CRYPTO_H
#include <stdint.h>
#include <stdbool.h>
static const uint8_t NTFS_HEADER[16] = {
0xeb, 0x52, 0x90, 0x4e, 0x54, 0x46, 0x53, 0x20,
0x20, 0x20, 0x20, 0x00, 0x10, 0x01, 0x00, 0x00
};
static const uint8_t EXFAT_HEADER[16] = {
0xeb, 0x76, 0x90, 0x45, 0x58, 0x46, 0x41, 0x54,
0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const uint8_t OPTION_KEY[16] = {
0x5c, 0x84, 0xa9, 0xe7, 0x26, 0xea, 0xa5, 0xdd,
0x35, 0x1f, 0x2b, 0x07, 0x50, 0xc2, 0x36, 0x97
};
static const uint8_t OPTION_IV[16] = {
0xc0, 0x63, 0xbf, 0x6f, 0x56, 0x2d, 0x08, 0x4d,
0x79, 0x63, 0xc9, 0x87, 0xf5, 0x28, 0x17, 0x61
};
typedef struct {
uint8_t key[16];
uint8_t iv[16];
bool has_iv;
} GameKeys;
void calculate_page_iv(uint64_t file_offset, const uint8_t* file_iv, uint8_t* page_iv);
bool calculate_file_iv(
const uint8_t key[16],
const uint8_t expected_header[16],
const uint8_t* first_page,
uint8_t out_iv[16]
);
bool get_game_keys(const char* game_id, GameKeys* out_keys);
#endif // CRYPTO_H
-268
View File
@@ -1,268 +0,0 @@
#include "exfat.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <locale.h>
static bool create_directories(const char* path) {
char temp[MAX_PATH_LENGTH];
char* p = NULL;
size_t len;
bool result = true;
strncpy(temp, path, MAX_PATH_LENGTH - 1);
temp[MAX_PATH_LENGTH - 1] = '\0';
len = strlen(temp);
if (len > 0 && (temp[len - 1] == '/' || temp[len - 1] == '\\')) {
temp[len - 1] = '\0';
}
for (p = temp + 1; *p; p++) {
if (*p == '/' || *p == '\\') {
*p = '\0';
if (MKDIR(temp) != 0 && errno != EEXIST) {
result = false;
break;
}
*p = PATH_SEPARATOR[0];
}
}
if (result && MKDIR(temp) != 0 && errno != EEXIST) {
result = false;
}
return result;
}
static uint32_t get_cluster_offset(ExfatContext* ctx, uint32_t cluster) {
return ctx->cluster_heap_offset_bytes + ((cluster - 2) * ctx->bytes_per_cluster);
}
static bool read_cluster(ExfatContext* ctx, uint32_t cluster, void* buffer) {
uint32_t offset = get_cluster_offset(ctx, cluster);
if (fseek(ctx->fp, offset, SEEK_SET) != 0) {
return false;
}
return fread(buffer, 1, ctx->bytes_per_cluster, ctx->fp) == ctx->bytes_per_cluster;
}
static uint32_t get_next_cluster(ExfatContext* ctx, uint32_t cluster) {
uint32_t next = ctx->fat[cluster];
if (next >= 0xFFFFFFF8) {
return 0; // end-of-chain
}
if (next == 0)
{
return cluster + 1; // if cluster in FAT is 0, it means the cluster heap is continous (exfat only)
}
return next;
}
static void combine_path(char* dest, size_t dest_size, const char* dir, const char* name) {
size_t dir_len = strlen(dir);
size_t name_len = strlen(name);
if (dir_len + name_len + 2 > dest_size) {
dest[0] = '\0';
return;
}
strcpy(dest, dir);
if (dir_len > 0 && dir[dir_len - 1] != '/' && dir[dir_len - 1] != '\\') {
strcat(dest, PATH_SEPARATOR);
}
strcat(dest, name);
}
static bool extract_file(ExfatContext* ctx, ExfatFileInfo* file, const char* output_path) {
FILE* out = fopen(output_path, "wb");
if (!out) {
return false;
}
uint32_t current_cluster = file->first_cluster;
uint64_t remaining = file->data_length;
uint8_t* buffer = malloc(ctx->bytes_per_cluster);
if (!buffer) {
fclose(out);
return false;
}
bool success = true;
while (remaining > 0 && current_cluster != 0 && success) {
if (!read_cluster(ctx, current_cluster, buffer)) {
success = false;
break;
}
size_t write_size = (remaining > ctx->bytes_per_cluster) ? ctx->bytes_per_cluster : (size_t)remaining;
if (fwrite(buffer, 1, write_size, out) != write_size) {
success = false;
break;
}
remaining -= write_size;
current_cluster = get_next_cluster(ctx, current_cluster);
}
free(buffer);
fclose(out);
return success;
}
static bool process_directory(ExfatContext* ctx, uint32_t start_cluster, const char* output_dir) {
uint8_t* cluster_buffer = malloc(ctx->bytes_per_cluster);
if (!cluster_buffer) {
return false;
}
uint32_t current_cluster = start_cluster;
bool finished = false;
while (!finished && current_cluster != 0) {
if (!read_cluster(ctx, current_cluster, cluster_buffer)) {
free(cluster_buffer);
return false;
}
uint32_t entries_per_cluster = ctx->bytes_per_cluster / EXFAT_ENTRY_SIZE;
uint8_t* entry_ptr = cluster_buffer;
for (uint32_t i = 0; i < entries_per_cluster; ) {
uint8_t entry_type = *entry_ptr;
if (entry_type == EXFAT_ENTRY_EOD) {
finished = true;
break;
}
if (entry_type == EXFAT_ENTRY_FILE) {
ExfatFileEntry* file_entry = (ExfatFileEntry*)entry_ptr;
ExfatStreamEntry* stream_entry = (ExfatStreamEntry*)(entry_ptr + EXFAT_ENTRY_SIZE);
if (stream_entry->entry_type != EXFAT_ENTRY_STREAM) {
i++;
entry_ptr += EXFAT_ENTRY_SIZE;
continue;
}
int total_name_chars = stream_entry->name_length;
int num_name_entries = (total_name_chars + 14) / 15;
char full_name[MAX_FILENAME_LENGTH];
wchar_t full_name_unicode[MAX_FILENAME_LENGTH];
int pos = 0;
uint8_t* name_entry_ptr = entry_ptr + EXFAT_ENTRY_SIZE * 2;
for (int k = 0; k < num_name_entries; k++) {
ExfatFileNameEntry* name_entry = (ExfatFileNameEntry*)(name_entry_ptr + k * EXFAT_ENTRY_SIZE);
int chars_in_this_entry = (total_name_chars - k * 15 < 15) ? (total_name_chars - k * 15) : 15;
for (int j = 0; j < chars_in_this_entry; j++) {
if (pos < MAX_FILENAME_LENGTH - 1) {
// you shouldn't cut of the wide char
full_name_unicode[pos++] = (wchar_t)name_entry->file_name[j];
}
}
}
full_name_unicode[pos] = L'\0';
// just convert it
_locale_t locale = _create_locale(LC_ALL, "");
_wcstombs_l(full_name, full_name_unicode, MAX_FILENAME_LENGTH, locale);
ExfatFileInfo file_info;
memset(&file_info, 0, sizeof(file_info));
strncpy(file_info.name, full_name, MAX_PATH_LENGTH - 1);
file_info.name[MAX_PATH_LENGTH - 1] = '\0';
file_info.first_cluster = stream_entry->first_cluster;
file_info.data_length = stream_entry->data_length;
file_info.is_directory = ((file_entry->file_attributes & 0x10) != 0);
char full_path[MAX_PATH_LENGTH];
combine_path(full_path, sizeof(full_path), output_dir, file_info.name);
if (file_info.is_directory) {
if (create_directories(full_path)) {
process_directory(ctx, file_info.first_cluster, full_path);
}
}
else {
extract_file(ctx, &file_info, full_path);
}
int total_entries = 2 + num_name_entries;
i += total_entries;
entry_ptr += EXFAT_ENTRY_SIZE * total_entries;
continue;
}
else {
i++;
entry_ptr += EXFAT_ENTRY_SIZE;
}
}
if (!finished) {
current_cluster = get_next_cluster(ctx, current_cluster);
}
}
free(cluster_buffer);
return true;
}
bool exfat_init(ExfatContext* ctx, const char* filename) {
memset(ctx, 0, sizeof(ExfatContext));
ctx->fp = fopen(filename, "rb");
if (!ctx->fp) {
return false;
}
if (fread(&ctx->boot_sector, sizeof(ExfatBootSector), 1, ctx->fp) != 1) {
fclose(ctx->fp);
return false;
}
ctx->bytes_per_sector = (1 << ctx->boot_sector.bytes_per_sector_shift);
ctx->bytes_per_cluster = ctx->bytes_per_sector * (1 << ctx->boot_sector.sectors_per_cluster_shift);
ctx->cluster_heap_offset_bytes = ctx->boot_sector.cluster_heap_offset * ctx->bytes_per_sector;
ctx->fat_offset_bytes = ctx->boot_sector.fat_offset * ctx->bytes_per_sector;
ctx->fat_length_bytes = ctx->boot_sector.fat_length * ctx->bytes_per_sector;
ctx->fat = malloc(ctx->fat_length_bytes);
if (!ctx->fat) {
fclose(ctx->fp);
return false;
}
if (fseek(ctx->fp, ctx->fat_offset_bytes, SEEK_SET) != 0) {
free(ctx->fat);
fclose(ctx->fp);
return false;
}
if (fread(ctx->fat, 1, ctx->fat_length_bytes, ctx->fp) != ctx->fat_length_bytes) {
free(ctx->fat);
fclose(ctx->fp);
return false;
}
return true;
}
bool exfat_extract_all(ExfatContext* ctx, const char* output_dir) {
if (!create_directories(output_dir)) {
return false;
}
return process_directory(ctx, ctx->boot_sector.first_cluster_of_root_dir, output_dir);
}
void exfat_close(ExfatContext* ctx) {
if (ctx->fp) {
fclose(ctx->fp);
ctx->fp = NULL;
}
if (ctx->fat) {
free(ctx->fat);
ctx->fat = NULL;
}
}
-103
View File
@@ -1,103 +0,0 @@
#ifndef EXFAT_H
#define EXFAT_H
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "common.h"
#define EXFAT_ENTRY_SIZE 32
#define EXFAT_ENTRY_EOD 0x00
#define EXFAT_ENTRY_BITMAP 0x81
#define EXFAT_ENTRY_FILE 0x85
#define EXFAT_ENTRY_STREAM 0xC0
#define EXFAT_ENTRY_FILENAME 0xC1
#pragma pack(push, 1)
typedef struct {
uint8_t jump_boot[3];
uint8_t fs_name[8];
uint8_t must_be_zero[53];
uint64_t partition_offset;
uint64_t volume_length;
uint32_t fat_offset;
uint32_t fat_length;
uint32_t cluster_heap_offset;
uint32_t cluster_count;
uint32_t first_cluster_of_root_dir;
uint32_t volume_serial_number;
uint16_t fs_revision;
uint16_t volume_flags;
uint8_t bytes_per_sector_shift;
uint8_t sectors_per_cluster_shift;
uint8_t number_of_fats;
uint8_t drive_select;
uint8_t percent_in_use;
uint8_t reserved[7];
uint8_t boot_code[390];
uint16_t boot_signature;
} ExfatBootSector;
typedef struct {
uint8_t entry_type;
uint8_t secondary_count;
uint16_t set_checksum;
uint16_t file_attributes;
uint16_t reserved1;
uint32_t create_timestamp;
uint32_t last_modified_timestamp;
uint32_t last_access_timestamp;
uint8_t create_10ms;
uint8_t last_modified_10ms;
uint8_t create_utc_offset;
uint8_t last_modified_utc_offset;
uint8_t last_access_utc_offset;
uint8_t reserved2[7];
} ExfatFileEntry;
typedef struct {
uint8_t entry_type;
uint8_t flags;
uint8_t reserved1;
uint8_t name_length;
uint16_t name_hash;
uint16_t reserved2;
uint64_t valid_data_length;
uint32_t reserved3;
uint32_t first_cluster;
uint64_t data_length;
} ExfatStreamEntry;
typedef struct {
uint8_t entry_type;
uint8_t flags;
uint16_t file_name[15]; // please work
} ExfatFileNameEntry;
#pragma pack(pop)
typedef struct {
char name[MAX_PATH_LENGTH];
uint32_t first_cluster;
uint64_t data_length;
bool is_directory;
} ExfatFileInfo;
typedef struct {
FILE* fp;
ExfatBootSector boot_sector;
uint32_t bytes_per_sector;
uint32_t bytes_per_cluster;
uint32_t cluster_heap_offset_bytes;
uint32_t fat_offset_bytes;
uint32_t fat_length_bytes;
uint32_t* fat;
} ExfatContext;
bool exfat_init(ExfatContext* ctx, const char* filename);
bool exfat_extract_all(ExfatContext* ctx, const char* output_dir);
void exfat_close(ExfatContext* ctx);
#endif // EXFAT_H
-517
View File
@@ -1,517 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "bootid.h"
#include "crypto.h"
#include "exfat.h"
#include "ntfs.h"
#include <openssl/evp.h>
#include <openssl/aes.h>
#define PAGE_SIZE 4096
#define BUFFER_SIZE (PAGE_SIZE * 256)
#define MAX_PATH_LENGTH 256
char* g_output_filename = NULL;
int process_file(const char* path, bool extract_fs) {
uint8_t* bootid_bytes = malloc(96);
uint8_t* read_buffer = malloc(BUFFER_SIZE);
uint8_t* write_buffer = malloc(BUFFER_SIZE);
uint8_t* decrypted_buffer = malloc(BUFFER_SIZE);
uint8_t iv[16];
uint8_t page_iv[16];
uint8_t key[16];
if (!bootid_bytes || !read_buffer || !write_buffer || !decrypted_buffer) {
printf("Memory allocation failed\n");
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
FILE* file = fopen(path, "rb");
if (!file) {
printf("Could not open file %s\n", path);
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
if (fread(bootid_bytes, 1, 96, file) != 96) {
printf("Could not read BootId from %s\n", path);
fclose(file);
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
uint8_t decrypted_bootid_bytes[96];
int out_len = 0;
EVP_CIPHER_CTX* bootid_ctx = EVP_CIPHER_CTX_new();
if (!bootid_ctx) {
printf("Could not create cipher context\n");
fclose(file);
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
EVP_DecryptInit_ex(bootid_ctx, EVP_aes_128_cbc(), NULL, BOOTID_KEY, BOOTID_IV);
EVP_CIPHER_CTX_set_padding(bootid_ctx, 0);
if (!EVP_DecryptUpdate(bootid_ctx, decrypted_bootid_bytes, &out_len, bootid_bytes, 96)) {
printf("Could not decrypt BootId in %s\n", path);
EVP_CIPHER_CTX_free(bootid_ctx);
fclose(file);
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
int final_len = 0;
if (!EVP_DecryptFinal_ex(bootid_ctx, decrypted_bootid_bytes + out_len, &final_len)) {
printf("Could not finalize decryption\n");
EVP_CIPHER_CTX_free(bootid_ctx);
fclose(file);
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
EVP_CIPHER_CTX_free(bootid_ctx);
BootId bootid;
memcpy(&bootid, decrypted_bootid_bytes, sizeof(BootId));
if (bootid.container_type != CONTAINER_TYPE_OS &&
bootid.container_type != CONTAINER_TYPE_APP &&
bootid.container_type != CONTAINER_TYPE_OPTION) {
printf("Unknown container type %d\n", bootid.container_type);
fclose(file);
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
char target_timestamp_str[20];
format_timestamp(&bootid.target_timestamp, target_timestamp_str, sizeof(target_timestamp_str));
char os_id[4];
char game_id[5];
memcpy(os_id, bootid.os_id, 3);
os_id[3] = '\0';
memcpy(game_id, bootid.game_id, 4);
game_id[4] = '\0';
const char* id = (bootid.container_type == CONTAINER_TYPE_OS) ? os_id : game_id;
GameKeys keys;
bool got_keys = false;
if (bootid.container_type == CONTAINER_TYPE_OS || bootid.container_type == CONTAINER_TYPE_APP) {
got_keys = get_game_keys(id, &keys);
}
else {
memcpy(keys.key, OPTION_KEY, 16);
memcpy(keys.iv, OPTION_IV, 16);
keys.has_iv = true;
got_keys = true;
}
if (!got_keys) {
printf("Decryption key invalid or not found.\n");
fclose(file);
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
uint64_t data_offset = bootid.header_block_count * bootid.block_size;
bool has_iv = false;
memcpy(key, keys.key, 16);
if (bootid.use_custom_iv) {
has_iv = false;
}
else {
has_iv = keys.has_iv;
if (has_iv) {
memcpy(iv, keys.iv, 16);
}
}
if (!has_iv) {
if (fseek(file, data_offset, SEEK_SET) != 0) {
printf("Could not seek to data offset\n");
fclose(file);
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
size_t read_size = fread(read_buffer, 1, PAGE_SIZE, file);
if (read_size != PAGE_SIZE) {
printf("Could not read data page\n");
fclose(file);
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
if (bootid.container_type == CONTAINER_TYPE_OPTION) {
if (!calculate_file_iv(key, EXFAT_HEADER, read_buffer, iv)) {
printf("Could not calculate file IV\n");
fclose(file);
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
}
else {
if (!calculate_file_iv(key, NTFS_HEADER, read_buffer, iv)) {
printf("Could not calculate file IV\n");
fclose(file);
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
}
has_iv = true;
}
char* output_filename = malloc(MAX_PATH_LENGTH);
if (!output_filename) {
printf("Memory allocation failed\n");
fclose(file);
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
if (bootid.container_type == CONTAINER_TYPE_OS) {
snprintf(output_filename, MAX_PATH_LENGTH, "%s_%04d%02d%02d_%s_%d.ntfs",
os_id,
bootid.os_version.major,
bootid.os_version.minor,
bootid.os_version.release,
target_timestamp_str,
bootid.sequence_number);
}
else if (bootid.container_type == CONTAINER_TYPE_APP) {
if (bootid.sequence_number > 0) {
snprintf(output_filename, MAX_PATH_LENGTH, "%s_%d%02d%02d_%s_%d_%d%02d%02d.ntfs",
game_id,
bootid.target_version.version.major,
bootid.target_version.version.minor,
bootid.target_version.version.release,
target_timestamp_str,
bootid.sequence_number,
bootid.source_version.major,
bootid.source_version.minor,
bootid.source_version.release);
}
else {
snprintf(output_filename, MAX_PATH_LENGTH, "%s_%d%02d%02d_%s_%d.ntfs",
game_id,
bootid.target_version.version.major,
bootid.target_version.version.minor,
bootid.target_version.version.release,
target_timestamp_str,
bootid.sequence_number);
}
}
else if (bootid.container_type == CONTAINER_TYPE_OPTION) {
char option_str[5];
memcpy(option_str, bootid.target_version.option, 4);
option_str[4] = '\0';
snprintf(output_filename, MAX_PATH_LENGTH, "%s_%s_%s_%d.exfat",
game_id,
option_str,
target_timestamp_str,
bootid.sequence_number);
}
FILE* output_file = fopen(output_filename, "wb");
if (!output_file) {
printf("Could not create output file %s\n", output_filename);
free(output_filename);
fclose(file);
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
uint64_t output_size = (bootid.block_count - bootid.header_block_count) * bootid.block_size;
if (fseek(file, data_offset, SEEK_SET) != 0) {
printf("Could not seek to data offset\n");
fclose(file);
fclose(output_file);
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
EVP_CIPHER_CTX* page_ctx = EVP_CIPHER_CTX_new();
if (!page_ctx) {
printf("Could not create cipher context\n");
fclose(file);
fclose(output_file);
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 1;
}
EVP_DecryptInit_ex(page_ctx, EVP_aes_128_cbc(), NULL, key, NULL);
EVP_CIPHER_CTX_set_padding(page_ctx, 0);
printf("\nDecrypting file...\n");
time_t last_update_time = time(NULL);
int last_percentage = -1;
uint64_t total_bytes_read = 0;
uint64_t bytes_remaining = output_size;
while (bytes_remaining > 0) {
size_t chunk_size = (bytes_remaining > BUFFER_SIZE) ? BUFFER_SIZE : (size_t)bytes_remaining;
size_t read_size = fread(read_buffer, 1, chunk_size, file);
if (read_size != chunk_size) {
printf("\nCould not read data\n");
break;
}
size_t offset = 0;
while (offset < read_size) {
size_t block_size = (read_size - offset > PAGE_SIZE) ? PAGE_SIZE : (read_size - offset);
uint64_t file_offset = data_offset + total_bytes_read + offset - data_offset;
calculate_page_iv(file_offset, iv, page_iv);
EVP_DecryptInit_ex(page_ctx, NULL, NULL, NULL, page_iv);
int out_len1 = 0, out_len2 = 0;
if (!EVP_DecryptUpdate(page_ctx, decrypted_buffer + offset, &out_len1, read_buffer + offset, block_size)) {
printf("\nCould not decrypt data\n");
break;
}
if (!EVP_DecryptFinal_ex(page_ctx, decrypted_buffer + offset + out_len1, &out_len2)) {
printf("\nCould not finalize decryption\n");
break;
}
if (out_len1 + out_len2 != (int)block_size) {
printf("\nDecrypted data size mismatch\n");
break;
}
offset += block_size;
}
if (fwrite(decrypted_buffer, 1, read_size, output_file) != read_size) {
printf("\nCould not write to output file\n");
break;
}
total_bytes_read += read_size;
bytes_remaining -= read_size;
time_t current_time = time(NULL);
if (current_time != last_update_time) {
int percentage = (int)((total_bytes_read * 100) / output_size);
if (percentage != last_percentage) {
printf("\rProgress: %d%%", percentage);
fflush(stdout);
last_percentage = percentage;
}
last_update_time = current_time;
}
}
printf("\rProgress: 100%%\n");
EVP_CIPHER_CTX_free(page_ctx);
fclose(file);
fclose(output_file);
memset(read_buffer, 0, BUFFER_SIZE);
memset(decrypted_buffer, 0, BUFFER_SIZE);
memset(page_iv, 0, sizeof(page_iv));
memset(bootid_bytes, 0, 96);
memset(iv, 0, sizeof(iv));
printf("Decryption finalized: %s\n", output_filename);
if (extract_fs) {
if (g_output_filename) {
free(g_output_filename);
}
g_output_filename = output_filename;
}
else {
free(output_filename);
}
free(bootid_bytes);
free(read_buffer);
free(write_buffer);
free(decrypted_buffer);
return 0;
}
int main(int argc, char* argv[]) {
bool extract_fs = true;
int start_index = 1;
if (argc < 2) {
printf("usage: unsegaREBORN [-no] <input_file1> [<input_file2> ...]\n");
printf(" -no Do not extract filesystem archives after decryption\n");
return 0;
}
if (strcmp(argv[1], "-no") == 0) {
extract_fs = false;
start_index = 2;
if (argc < 3) {
printf("No input files specified\n");
return 1;
}
}
for (int i = start_index; i < argc; ++i) {
const char* file_path = argv[i];
printf("Processing file: %s\n", file_path);
if (process_file(file_path, extract_fs) == 0) {
if (extract_fs && g_output_filename) {
char output_dir[MAX_PATH_LENGTH];
strncpy(output_dir, g_output_filename, sizeof(output_dir) - 1);
output_dir[sizeof(output_dir) - 1] = '\0';
char* ext = strrchr(output_dir, '.');
if (ext) *ext = '\0';
if (strstr(g_output_filename, ".exfat") != NULL) {
ExfatContext ctx;
if (exfat_init(&ctx, g_output_filename)) {
if (exfat_extract_all(&ctx, output_dir)) {
printf("\nExFAT extraction completed successfully\n");
}
else {
printf("\nFailed to extract ExFAT archive\n");
}
exfat_close(&ctx);
}
else {
printf("\nFailed to initialize ExFAT context\n");
}
}
else if (strstr(g_output_filename, ".ntfs") != NULL) {
NTFSContext ctx = { 0 };
if (ntfs_init(&ctx, g_output_filename, output_dir)) {
printf("\nExtracting NTFS archive...\n");
if (ntfs_extract_all(&ctx)) {
printf("\nNTFS extraction completed successfully\n");
char vhd_path[MAX_PATH_LENGTH];
bool found_child = false;
for (int vhd_num = 0; vhd_num < 10; vhd_num++) {
snprintf(vhd_path, sizeof(vhd_path), "%s%sinternal_%d.vhd",
output_dir, PATH_SEPARATOR, vhd_num);
FILE* test = fopen(vhd_path, "rb");
if (!test) continue;
fclose(test);
if (vhd_num > 0) {
printf("\nChild internal VHD identified, finalizing process.\n");
found_child = true;
break;
}
char vhd_output_dir[MAX_PATH_LENGTH];
snprintf(vhd_output_dir, sizeof(vhd_output_dir), "%s%scontents",
output_dir, PATH_SEPARATOR);
NTFSContext vhd_ctx = { 0 };
if (ntfs_init(&vhd_ctx, vhd_path, vhd_output_dir)) {
printf("\nExtracting from internal VHD...\n");
if (ntfs_extract_all(&vhd_ctx)) {
printf("\nInternal VHD extraction completed successfully\n");
}
else {
printf("\nFailed to extract VHD contents\n");
}
ntfs_close(&vhd_ctx);
}
else {
printf("\nFailed to open internal VHD\n");
}
break;
}
}
else {
printf("\nFailed to extract NTFS archive\n");
}
ntfs_close(&ctx);
}
else {
printf("\nFailed to initialize NTFS context\n");
}
}
else {
printf("\nUnknown filesystem type for file %s\n", g_output_filename);
}
free(g_output_filename);
g_output_filename = NULL;
}
}
else {
printf("Failed to process %s\n", file_path);
}
}
if (g_output_filename) {
free(g_output_filename);
}
return 0;
}
-837
View File
@@ -1,837 +0,0 @@
#include "ntfs.h"
#include <time.h>
#include <locale.h>
#define BUFFER_SIZE 65536
static uint16_t swap16(uint16_t value) {
return ((value & 0xFF00) >> 8) | ((value & 0x00FF) << 8);
}
static uint32_t swap32(uint32_t value) {
return ((value & 0xFF000000) >> 24) |
((value & 0x00FF0000) >> 8) |
((value & 0x0000FF00) << 8) |
((value & 0x000000FF) << 24);
}
static uint64_t swap64(uint64_t value) {
return ((value & 0xFF00000000000000ULL) >> 56) |
((value & 0x00FF000000000000ULL) >> 40) |
((value & 0x0000FF0000000000ULL) >> 24) |
((value & 0x000000FF00000000ULL) >> 8) |
((value & 0x00000000FF000000ULL) << 8) |
((value & 0x0000000000FF0000ULL) << 24) |
((value & 0x000000000000FF00ULL) << 40) |
((value & 0x00000000000000FFULL) << 56);
}
static bool create_directories(const char* path) {
char* tmp = _strdup(path);
if (!tmp) return false;
bool success = true;
char* p = tmp;
if (strlen(p) > 2) {
if (p[1] == ':') p += 2;
if (*p == '\\' || *p == '/') p++;
}
while ((p = strchr(p, PATH_SEPARATOR[0])) != NULL) {
*p = '\0';
if (strlen(tmp) > 0) {
if (MKDIR(tmp) != 0 && errno != EEXIST) {
success = false;
break;
}
}
*p = PATH_SEPARATOR[0];
p++;
}
if (success && strlen(tmp) > 0) {
if (MKDIR(tmp) != 0 && errno != EEXIST) {
success = false;
}
}
free(tmp);
return success;
}
static void convert_name_to_ascii(const uint16_t* utf16_name, int name_length, char* ascii_name) {
// just convert it
_locale_t locale = _create_locale(LC_ALL, "");
int real_name_length = min(name_length, MAX_FILENAME_LENGTH - 1);
_wcstombs_l(ascii_name, utf16_name, real_name_length, locale);
ascii_name[real_name_length] = '\0';
}
static bool ntfs_read(NTFSContext* ctx, void* buffer, uint64_t offset, size_t size) {
if (ctx->is_vhd) {
return vhd_read(&ctx->vhd, buffer, offset, size);
}
if (fseeko(ctx->raw.fp, offset, SEEK_SET) != 0) {
return false;
}
return fread(buffer, 1, size, ctx->raw.fp) == size;
}
static bool read_file_info(NTFSContext* ctx, uint64_t ref_number, FileInfo* info) {
memset(info, 0, sizeof(FileInfo));
uint64_t mft_offset = ctx->mft_offset + (ref_number * ctx->mft_record_size);
uint8_t* record_buffer = malloc(ctx->mft_record_size);
if (!record_buffer) {
return false;
}
bool success = false;
if (ntfs_read(ctx, record_buffer, mft_offset, ctx->mft_record_size)) {
const MFTRecordHeader* record = (const MFTRecordHeader*)record_buffer;
if (memcmp(record->magic, "FILE", 4) == 0 && (record->flags & MFT_RECORD_IN_USE)) {
info->is_directory = (record->flags & MFT_RECORD_IS_DIRECTORY) != 0;
const uint8_t* attr = (const uint8_t*)record + record->attrs_offset;
while (attr < (const uint8_t*)record + record->bytes_used) {
const AttributeHeader* header = (const AttributeHeader*)attr;
if (header->type == 0xFFFFFFFF || header->length == 0) {
break;
}
if (header->type == FILE_NAME_ATTR && !header->non_resident) {
const FileNameAttribute* fname =
(const FileNameAttribute*)(attr + header->data.resident.value_offset);
if (fname->namespace != 2) {
convert_name_to_ascii(fname->name, fname->name_length, info->name);
info->parent_ref = fname->parent_directory & 0xFFFFFFFFFFFF;
info->valid = true;
success = true;
break;
}
}
attr += header->length;
}
}
}
free(record_buffer);
return success;
}
static bool init_directory_cache(DirectoryCache* cache) {
cache->capacity = 1024;
cache->count = 0;
cache->directories = malloc(cache->capacity * sizeof(DirectoryInfo));
if (!cache->directories) return false;
cache->directories[0].ref_number = 5;
cache->directories[0].path[0] = '\0';
cache->count = 1;
return true;
}
static void free_directory_cache(DirectoryCache* cache) {
free(cache->directories);
cache->directories = NULL;
cache->capacity = 0;
cache->count = 0;
}
static bool add_directory_to_cache(DirectoryCache* cache, uint64_t ref_number, const char* path) {
for (size_t i = 0; i < cache->count; i++) {
if (cache->directories[i].ref_number == ref_number) {
strncpy(cache->directories[i].path, path, MAX_PATH_LENGTH - 1);
cache->directories[i].path[MAX_PATH_LENGTH - 1] = '\0';
return true;
}
}
if (cache->count >= cache->capacity) {
size_t new_capacity = cache->capacity * 2;
DirectoryInfo* new_dirs = realloc(cache->directories,
new_capacity * sizeof(DirectoryInfo));
if (!new_dirs) return false;
cache->directories = new_dirs;
cache->capacity = new_capacity;
}
if (cache->count < cache->capacity) {
cache->directories[cache->count].ref_number = ref_number;
strncpy(cache->directories[cache->count].path, path, MAX_PATH_LENGTH - 1);
cache->directories[cache->count].path[MAX_PATH_LENGTH - 1] = '\0';
cache->count++;
return true;
}
return false;
}
static const char* get_cached_path(DirectoryCache* cache, uint64_t ref_number) {
for (size_t i = 0; i < cache->count; i++) {
if (cache->directories[i].ref_number == ref_number) {
return cache->directories[i].path;
}
}
return NULL;
}
static bool build_path_recursively(NTFSContext* ctx, uint64_t ref_number, char* buffer, size_t buffer_size) {
if (ref_number == 5) {
buffer[0] = '\0';
return true;
}
const char* cached_path = get_cached_path(&ctx->dir_cache, ref_number);
if (cached_path) {
strncpy(buffer, cached_path, buffer_size - 1);
buffer[buffer_size - 1] = '\0';
return true;
}
FileInfo info;
if (!read_file_info(ctx, ref_number, &info) || !info.valid) {
return false;
}
if (info.name[0] == '$') {
return false;
}
char parent_path[MAX_PATH_LENGTH];
if (!build_path_recursively(ctx, info.parent_ref, parent_path, sizeof(parent_path))) {
return false;
}
if (parent_path[0] == '\0') {
strncpy(buffer, info.name, buffer_size - 1);
}
else {
snprintf(buffer, buffer_size, "%s%s%s", parent_path, PATH_SEPARATOR, info.name);
}
buffer[buffer_size - 1] = '\0';
if (info.is_directory) {
add_directory_to_cache(&ctx->dir_cache, ref_number, buffer);
}
return true;
}
static void get_full_path(const NTFSContext* ctx, uint64_t parent_ref, const char* name,
char* out_path, size_t out_size) {
char parent_path[MAX_PATH_LENGTH];
if (!build_path_recursively(ctx, parent_ref, parent_path, sizeof(parent_path))) {
snprintf(out_path, out_size, "%s%s%s",
ctx->base_path,
PATH_SEPARATOR,
name);
return;
}
if (parent_path[0] == '\0') {
snprintf(out_path, out_size, "%s%s%s",
ctx->base_path,
PATH_SEPARATOR,
name);
}
else {
snprintf(out_path, out_size, "%s%s%s%s%s",
ctx->base_path,
PATH_SEPARATOR,
parent_path,
PATH_SEPARATOR,
name);
}
}
static bool extract_data_from_runs(NTFSContext* ctx, const DataRun* runs, int run_count,
uint64_t data_size, FILE* out_file) {
uint8_t* temp_buffer = malloc(BUFFER_SIZE);
if (!temp_buffer) return false;
uint64_t total_written = 0;
bool success = true;
for (int i = 0; i < run_count && total_written < data_size; i++) {
uint64_t cluster_offset = ctx->data_start_offset +
(runs[i].offset * ctx->bytes_per_cluster);
uint64_t length = runs[i].length * ctx->bytes_per_cluster;
if (length > data_size - total_written) {
length = data_size - total_written;
}
uint64_t remaining = length;
while (remaining > 0 && success) {
size_t to_read = (remaining > BUFFER_SIZE) ? BUFFER_SIZE : (size_t)remaining;
if (!ntfs_read(ctx, temp_buffer, cluster_offset, to_read)) {
success = false;
break;
}
if (fwrite(temp_buffer, 1, to_read, out_file) != to_read) {
success = false;
break;
}
cluster_offset += to_read;
remaining -= to_read;
total_written += to_read;
}
}
free(temp_buffer);
return success;
}
static int parse_data_runs(const uint8_t* run_list, DataRun* runs, int max_runs) {
int count = 0;
uint64_t offset_base = 0;
const uint8_t* p = run_list;
while (*p != 0 && count < max_runs) {
uint8_t header = *p++;
int length_size = header & 0xF;
int offset_size = header >> 4;
if (length_size == 0) break;
uint64_t length = 0;
for (int i = 0; i < length_size; i++) {
length |= ((uint64_t)*p++) << (i * 8);
}
int64_t offset = 0;
if (offset_size > 0) {
for (int i = 0; i < offset_size; i++) {
offset |= ((uint64_t)*p++) << (i * 8);
}
if (offset & ((uint64_t)1 << ((offset_size * 8) - 1))) {
offset |= ~((uint64_t)(1ULL << (offset_size * 8)) - 1);
}
}
offset_base += offset;
runs[count].offset = offset_base;
runs[count].length = length;
count++;
}
return count;
}
static bool extract_file(NTFSContext* ctx, const MFTRecordHeader* record,
const char* full_path) {
char parent_path[MAX_PATH_LENGTH];
strncpy(parent_path, full_path, sizeof(parent_path) - 1);
parent_path[sizeof(parent_path) - 1] = '\0';
char* last_separator = strrchr(parent_path, PATH_SEPARATOR[0]);
if (last_separator) {
*last_separator = '\0';
create_directories(parent_path);
}
FILE* out_file = fopen(full_path, "wb");
if (!out_file) {
printf("Failed to create file: %s\n", full_path);
return false;
}
bool success = false;
const uint8_t* attr = (const uint8_t*)record + record->attrs_offset;
while (attr < (const uint8_t*)record + record->bytes_used) {
const AttributeHeader* header = (const AttributeHeader*)attr;
if (header->type == 0xFFFFFFFF || header->length == 0) {
break;
}
if (header->type == DATA_ATTR && header->name_length == 0) {
if (header->non_resident) {
DataRun runs[256];
const uint8_t* run_list = attr + header->data.non_resident.mapping_pairs_offset;
int run_count = parse_data_runs(run_list, runs, 256);
success = extract_data_from_runs(ctx, runs, run_count,
header->data.non_resident.data_size,
out_file);
}
else {
const uint8_t* data = attr + header->data.resident.value_offset;
success = (fwrite(data, 1, header->data.resident.value_length, out_file) ==
header->data.resident.value_length);
}
break;
}
attr += header->length;
}
fclose(out_file);
if (!success) {
remove(full_path);
}
return success;
}
static bool process_mft_record(NTFSContext* ctx, const uint8_t* record_data) {
const MFTRecordHeader* record = (const MFTRecordHeader*)record_data;
if (memcmp(record->magic, "FILE", 4) != 0 || !(record->flags & MFT_RECORD_IN_USE)) {
return true;
}
char filename[MAX_FILENAME_LENGTH];
uint64_t parent_ref = 0;
bool got_filename = false;
bool is_directory = (record->flags & MFT_RECORD_IS_DIRECTORY) != 0;
uint64_t record_num = record->record_number & 0xFFFFFFFFFFFF;
const uint8_t* attr = (const uint8_t*)record + record->attrs_offset;
while (attr < (const uint8_t*)record + record->bytes_used) {
const AttributeHeader* header = (const AttributeHeader*)attr;
if (header->type == 0xFFFFFFFF || header->length == 0) {
break;
}
if (header->type == FILE_NAME_ATTR && !header->non_resident) {
const FileNameAttribute* fname =
(const FileNameAttribute*)(attr + header->data.resident.value_offset);
if (fname->namespace != 2) {
convert_name_to_ascii(fname->name, fname->name_length, filename);
parent_ref = fname->parent_directory & 0xFFFFFFFFFFFF;
got_filename = true;
break;
}
}
attr += header->length;
}
if (!got_filename || filename[0] == '$') {
return true;
}
char full_path[MAX_PATH_LENGTH];
get_full_path(ctx, parent_ref, filename, full_path, sizeof(full_path));
if (is_directory) {
if (!create_directories(full_path)) {
printf("Failed to create directory: %s\n", full_path);
return false;
}
const char* relative_path = full_path + strlen(ctx->base_path);
while (*relative_path == PATH_SEPARATOR[0]) relative_path++;
if (!add_directory_to_cache(&ctx->dir_cache, record_num, relative_path)) {
printf("Failed to cache directory: %s\n", filename);
return false;
}
return true;
}
return extract_file(ctx, record, full_path);
}
static bool vhd_read(VHDContext* ctx, void* buffer, uint64_t offset, size_t size) {
if (ctx->footer.disk_type == VHD_TYPE_FIXED) {
if (fseeko(ctx->fp, offset, SEEK_SET) != 0) {
return false;
}
return fread(buffer, 1, size, ctx->fp) == size;
}
else if (ctx->footer.disk_type == VHD_TYPE_DYNAMIC) {
uint8_t* buf = (uint8_t*)buffer;
uint64_t block_size = ctx->dyn_header.block_size;
while (size > 0) {
uint32_t block_idx = (uint32_t)(offset / block_size);
uint32_t block_offset = (uint32_t)(offset % block_size);
if (block_idx >= ctx->dyn_header.max_bat_entries) {
return false;
}
uint32_t bat_entry = ctx->bat[block_idx];
if (bat_entry == VHD_BAT_ENTRY_RESERVED) {
size_t chunk = (size < (block_size - block_offset)) ?
size : (size_t)(block_size - block_offset);
memset(buf, 0, chunk);
buf += chunk;
offset += chunk;
size -= chunk;
}
else {
uint64_t sector_offset = ((uint64_t)bat_entry) * VHD_SECTOR_SIZE;
if (fseeko(ctx->fp, sector_offset, SEEK_SET) != 0) {
return false;
}
if (fread(ctx->sector_bitmap, 1, ctx->sector_bitmap_size, ctx->fp)
!= ctx->sector_bitmap_size) {
return false;
}
if (fread(ctx->block_buffer, 1, block_size, ctx->fp) != block_size) {
return false;
}
size_t chunk = (size < (block_size - block_offset)) ?
size : (size_t)(block_size - block_offset);
memcpy(buf, ctx->block_buffer + block_offset, chunk);
buf += chunk;
offset += chunk;
size -= chunk;
}
}
return true;
}
return false;
}
static bool vhd_init(VHDContext* ctx, const char* filename) {
memset(ctx, 0, sizeof(VHDContext));
ctx->fp = fopen(filename, "rb");
if (!ctx->fp) {
printf("Failed to open file: %s\n", filename);
return false;
}
if (fseeko(ctx->fp, -((int64_t)VHD_FOOTER_SIZE), SEEK_END) != 0) {
printf("Failed to seek to VHD footer\n");
fclose(ctx->fp);
return false;
}
if (fread(&ctx->footer, 1, sizeof(VHDFooter), ctx->fp) != sizeof(VHDFooter)) {
printf("Failed to read VHD footer\n");
fclose(ctx->fp);
return false;
}
if (memcmp(ctx->footer.cookie, VHD_COOKIE, strlen(VHD_COOKIE)) != 0) {
printf("Invalid VHD signature\n");
fclose(ctx->fp);
return false;
}
ctx->footer.features = swap32(ctx->footer.features);
ctx->footer.version = swap32(ctx->footer.version);
ctx->footer.data_offset = swap64(ctx->footer.data_offset);
ctx->footer.timestamp = swap32(ctx->footer.timestamp);
ctx->footer.creator_app = swap32(ctx->footer.creator_app);
ctx->footer.creator_ver = swap32(ctx->footer.creator_ver);
ctx->footer.creator_os = swap32(ctx->footer.creator_os);
ctx->footer.original_size = swap64(ctx->footer.original_size);
ctx->footer.current_size = swap64(ctx->footer.current_size);
ctx->footer.cylinder = swap16(ctx->footer.cylinder);
ctx->footer.disk_type = swap32(ctx->footer.disk_type);
ctx->footer.checksum = swap32(ctx->footer.checksum);
if (ctx->footer.disk_type == VHD_TYPE_DYNAMIC) {
if (fseeko(ctx->fp, ctx->footer.data_offset, SEEK_SET) != 0) {
printf("Failed to seek to dynamic header\n");
fclose(ctx->fp);
return false;
}
if (fread(&ctx->dyn_header, 1, sizeof(VHDDynamicHeader), ctx->fp) != sizeof(VHDDynamicHeader)) {
printf("Failed to read dynamic header\n");
fclose(ctx->fp);
return false;
}
if (memcmp(ctx->dyn_header.cookie, VHD_DYNAMIC_COOKIE, strlen(VHD_DYNAMIC_COOKIE)) != 0) {
printf("Invalid dynamic disk header signature\n");
fclose(ctx->fp);
return false;
}
ctx->dyn_header.data_offset = swap64(ctx->dyn_header.data_offset);
ctx->dyn_header.bat_offset = swap64(ctx->dyn_header.bat_offset);
ctx->dyn_header.head_vers = swap32(ctx->dyn_header.head_vers);
ctx->dyn_header.max_bat_entries = swap32(ctx->dyn_header.max_bat_entries);
ctx->dyn_header.block_size = swap32(ctx->dyn_header.block_size);
size_t bat_size = (size_t)ctx->dyn_header.max_bat_entries * sizeof(uint32_t);
if (bat_size == 0 || bat_size > (1ULL << 30)) {
printf("Invalid BAT size\n");
fclose(ctx->fp);
return false;
}
ctx->bat = malloc(bat_size);
if (!ctx->bat) {
printf("Failed to allocate BAT memory\n");
fclose(ctx->fp);
return false;
}
if (fseeko(ctx->fp, ctx->dyn_header.bat_offset, SEEK_SET) != 0) {
printf("Failed to seek to BAT\n");
free(ctx->bat);
fclose(ctx->fp);
return false;
}
if (fread(ctx->bat, 1, bat_size, ctx->fp) != bat_size) {
printf("Failed to read BAT\n");
free(ctx->bat);
fclose(ctx->fp);
return false;
}
for (uint32_t i = 0; i < ctx->dyn_header.max_bat_entries; i++) {
ctx->bat[i] = swap32(ctx->bat[i]);
}
ctx->sector_bitmap_size = (ctx->dyn_header.block_size / VHD_SECTOR_SIZE + 7) / 8;
ctx->sector_bitmap = malloc(ctx->sector_bitmap_size);
ctx->block_buffer = malloc(ctx->dyn_header.block_size);
if (!ctx->sector_bitmap || !ctx->block_buffer) {
printf("Failed to allocate dynamic disk buffers\n");
free(ctx->bat);
free(ctx->sector_bitmap);
free(ctx->block_buffer);
fclose(ctx->fp);
return false;
}
}
return true;
}
bool ntfs_init(NTFSContext* ctx, const char* path, const char* extract_path) {
memset(ctx, 0, sizeof(NTFSContext));
strncpy(ctx->base_path, extract_path, sizeof(ctx->base_path) - 1);
if (!init_directory_cache(&ctx->dir_cache)) {
return false;
}
FILE* fp = fopen(path, "rb");
if (!fp) return false;
if (fseeko(fp, -512, SEEK_END) == 0) {
char signature[9] = { 0 };
if (fread(signature, 1, 8, fp) == 8 && memcmp(signature, VHD_COOKIE, 8) == 0) {
fclose(fp);
ctx->is_vhd = true;
if (!vhd_init(&ctx->vhd, path)) {
free_directory_cache(&ctx->dir_cache);
return false;
}
}
else {
rewind(fp);
ctx->is_vhd = false;
ctx->raw.fp = fp;
}
}
uint64_t ntfs_offset = 0;
bool found_ntfs = false;
if (ctx->is_vhd) {
uint8_t sector[VHD_SECTOR_SIZE];
if (ntfs_read(ctx, sector, 0, VHD_SECTOR_SIZE)) {
if (sector[0x1FE] == 0x55 && sector[0x1FF] == 0xAA) {
for (int i = 0; i < 4; i++) {
const uint8_t* part = sector + 0x1BE + (i * 16);
if (part[4] == NTFS_PARTITION_TYPE) {
uint32_t start_sector =
(uint32_t)part[8] |
((uint32_t)part[9] << 8) |
((uint32_t)part[10] << 16) |
((uint32_t)part[11] << 24);
ntfs_offset = (uint64_t)start_sector * VHD_SECTOR_SIZE;
if (ntfs_read(ctx, sector, ntfs_offset, VHD_SECTOR_SIZE) &&
memcmp(sector + 3, NTFS_SIGNATURE, 8) == 0) {
found_ntfs = true;
break;
}
}
}
}
}
if (!found_ntfs) {
const uint64_t offsets[] = { 0, 0x100000, 0x200000, 0x400000, 0x800000, 0 };
for (int i = 0; offsets[i]; i++) {
if (ntfs_read(ctx, sector, offsets[i], VHD_SECTOR_SIZE) &&
memcmp(sector + 3, NTFS_SIGNATURE, 8) == 0) {
ntfs_offset = offsets[i];
found_ntfs = true;
break;
}
}
}
}
else {
uint8_t boot[512];
if (ntfs_read(ctx, boot, 0, sizeof(boot)) &&
boot[0] == 0xEB && boot[1] == 0x52 && boot[2] == 0x90 &&
memcmp(boot + 3, NTFS_SIGNATURE, 8) == 0) {
found_ntfs = true;
}
}
if (!found_ntfs) {
printf("No NTFS filesystem found\n");
ntfs_close(ctx);
return false;
}
ctx->data_start_offset = ntfs_offset;
if (!ntfs_read(ctx, &ctx->boot, ntfs_offset, sizeof(NTFSBootSector))) {
printf("Failed to read NTFS boot sector\n");
ntfs_close(ctx);
return false;
}
ctx->bytes_per_cluster = (uint32_t)ctx->boot.bytes_per_sector * ctx->boot.sectors_per_cluster;
ctx->mft_offset = ntfs_offset + (ctx->boot.mft_cluster_number * ctx->bytes_per_cluster);
if (ctx->boot.clusters_per_mft_record > 0) {
ctx->mft_record_size = ctx->boot.clusters_per_mft_record * ctx->bytes_per_cluster;
}
else {
ctx->mft_record_size = 1U << (-ctx->boot.clusters_per_mft_record);
}
uint8_t* mft_record = malloc(ctx->mft_record_size);
if (!mft_record) {
printf("Failed to allocate memory for MFT record\n");
ntfs_close(ctx);
return false;
}
if (!ntfs_read(ctx, mft_record, ctx->mft_offset, ctx->mft_record_size)) {
printf("Failed to read MFT record 0\n");
free(mft_record);
ntfs_close(ctx);
return false;
}
const MFTRecordHeader* record = (const MFTRecordHeader*)mft_record;
if (memcmp(record->magic, "FILE", 4) != 0) {
printf("Invalid MFT record signature\n");
free(mft_record);
ntfs_close(ctx);
return false;
}
const uint8_t* attr = mft_record + record->attrs_offset;
while (attr < mft_record + record->bytes_used) {
const AttributeHeader* header = (const AttributeHeader*)attr;
if (header->type == 0xFFFFFFFF || header->length == 0) break;
if (header->type == DATA_ATTR && header->name_length == 0) {
if (header->non_resident) {
uint64_t mft_data_size = header->data.non_resident.data_size;
ctx->mft_data_size = mft_data_size;
ctx->total_mft_records = mft_data_size / ctx->mft_record_size;
}
break;
}
attr += header->length;
}
free(mft_record);
return true;
}
bool ntfs_extract_all(NTFSContext* ctx) {
if (!create_directories(ctx->base_path)) {
printf("Failed to create output directory\n");
return false;
}
uint8_t* record_buffer = malloc(ctx->mft_record_size);
if (!record_buffer) {
printf("Failed to allocate MFT record buffer\n");
return false;
}
printf("Extraction in progress...\n");
uint64_t current_offset = ctx->mft_offset;
uint64_t total_records = ctx->total_mft_records;
uint64_t processed_records = 0;
uint64_t extracted_records = 0;
time_t last_update_time = time(NULL);
int last_percentage = -1;
// Process MFT records
for (uint64_t i = 0; i < total_records; i++) {
if (!ntfs_read(ctx, record_buffer, current_offset, ctx->mft_record_size)) {
printf("Failed to read MFT record at offset 0x%llX\n",
(unsigned long long)current_offset);
break;
}
const MFTRecordHeader* record = (const MFTRecordHeader*)record_buffer;
if (memcmp(record->magic, "FILE", 4) == 0) {
processed_records++;
if (process_mft_record(ctx, record_buffer)) {
extracted_records++;
}
}
current_offset += ctx->mft_record_size;
time_t current_time = time(NULL);
if (current_time != last_update_time) {
int percentage = (int)((i + 1) * 100 / total_records);
if (percentage != last_percentage) {
printf("\rProgress: %d%%", percentage);
fflush(stdout);
last_percentage = percentage;
}
last_update_time = current_time;
}
}
printf("\rProgress: 100%%\n");
printf("Extraction completed.\n");
free(record_buffer);
return true;
}
void ntfs_close(NTFSContext* ctx) {
if (ctx->is_vhd) {
if (ctx->vhd.fp) {
fclose(ctx->vhd.fp);
free(ctx->vhd.bat);
free(ctx->vhd.sector_bitmap);
free(ctx->vhd.block_buffer);
}
}
else {
if (ctx->raw.fp) {
fclose(ctx->raw.fp);
}
}
free_directory_cache(&ctx->dir_cache);
memset(ctx, 0, sizeof(NTFSContext));
}
-224
View File
@@ -1,224 +0,0 @@
#ifndef NTFS_H
#define NTFS_H
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "common.h"
#define fseeko _fseeki64
#define ftello _ftelli64
#define VHD_FOOTER_SIZE 512
#define VHD_SECTOR_SIZE 512
#define VHD_BAT_ENTRY_RESERVED 0xFFFFFFFF
#define NTFS_RECORD_SIZE 1024
#define MFT_RECORD_MAGIC "FILE"
#define VHD_COOKIE "conectix"
#define VHD_DYNAMIC_COOKIE "cxsparse"
#define VHD_TYPE_FIXED 2
#define VHD_TYPE_DYNAMIC 3
#define FILE_NAME_ATTR 0x30
#define DATA_ATTR 0x80
#define INDEX_ROOT_ATTR 0x90
#define INDEX_ALLOCATION_ATTR 0xA0
#define NTFS_SIGNATURE "NTFS "
#define NTFS_PARTITION_TYPE 0x07
#define MFT_RECORD_IN_USE 0x0001
#define MFT_RECORD_IS_DIRECTORY 0x0002
typedef struct {
uint64_t ref_number;
char path[MAX_PATH_LENGTH];
} DirectoryInfo;
typedef struct {
DirectoryInfo* directories;
size_t capacity;
size_t count;
} DirectoryCache;
#pragma pack(push, 1)
typedef struct {
char cookie[8];
uint32_t features;
uint32_t version;
uint64_t data_offset;
uint32_t timestamp;
uint32_t creator_app;
uint32_t creator_ver;
uint32_t creator_os;
uint64_t original_size;
uint64_t current_size;
uint16_t cylinder;
uint8_t heads;
uint8_t sectors;
uint32_t disk_type;
uint32_t checksum;
uint8_t unique_id[16];
uint8_t saved_state;
uint8_t reserved[427];
} VHDFooter;
typedef struct {
char cookie[8];
uint64_t data_offset;
uint64_t bat_offset;
uint32_t head_vers;
uint32_t max_bat_entries;
uint32_t block_size;
uint32_t checksum;
uint8_t parent_id[16];
uint32_t parent_timestamp;
uint32_t reserved1;
uint16_t parent_name[256];
uint8_t parent_loc[8][512];
uint8_t reserved2[256];
} VHDDynamicHeader;
typedef struct {
uint8_t jump[3];
uint8_t signature[8];
uint16_t bytes_per_sector;
uint8_t sectors_per_cluster;
uint16_t reserved_sectors;
uint8_t always_zero1[3];
uint16_t not_used1;
uint8_t media_descriptor;
uint16_t always_zero2;
uint16_t sectors_per_track;
uint16_t number_of_heads;
uint32_t hidden_sectors;
uint32_t not_used2;
uint32_t not_used3;
uint64_t total_sectors;
uint64_t mft_cluster_number;
uint64_t mft_mirror_cluster_number;
int8_t clusters_per_mft_record;
uint8_t not_used4[3];
int8_t clusters_per_index_record;
uint8_t not_used5[3];
uint64_t volume_serial_number;
uint32_t checksum;
} NTFSBootSector;
typedef struct {
char magic[4];
uint16_t usa_offset;
uint16_t usa_count;
uint64_t lsn;
uint16_t sequence_number;
uint16_t link_count;
uint16_t attrs_offset;
uint16_t flags;
uint32_t bytes_used;
uint32_t bytes_allocated;
uint64_t base_ref;
uint16_t next_attr_id;
uint16_t record_number;
uint16_t usa_value;
} MFTRecordHeader;
typedef struct {
uint32_t type;
uint32_t length;
uint8_t non_resident;
uint8_t name_length;
uint16_t name_offset;
uint16_t flags;
uint16_t attribute_id;
union {
struct {
uint32_t value_length;
uint16_t value_offset;
uint16_t flags;
} resident;
struct {
uint64_t lowest_vcn;
uint64_t highest_vcn;
uint16_t mapping_pairs_offset;
uint16_t compression_unit;
uint32_t padding;
uint64_t allocated_size;
uint64_t data_size;
uint64_t initialized_size;
uint64_t compressed_size;
} non_resident;
} data;
} AttributeHeader;
typedef struct {
uint64_t parent_directory;
uint64_t creation_time;
uint64_t modification_time;
uint64_t mft_modification_time;
uint64_t access_time;
uint64_t allocated_size;
uint64_t real_size;
uint32_t flags;
uint32_t reparse_value;
uint8_t name_length;
uint8_t namespace;
uint16_t name[256];
} FileNameAttribute;
typedef struct {
uint64_t offset;
uint64_t length;
} DataRun;
#pragma pack(pop)
typedef struct {
FILE* fp;
VHDFooter footer;
VHDDynamicHeader dyn_header;
uint32_t* bat;
uint32_t sector_bitmap_size;
uint8_t* sector_bitmap;
uint8_t* block_buffer;
} VHDContext;
typedef struct {
char name[MAX_FILENAME_LENGTH];
uint64_t parent_ref;
bool is_directory;
bool valid;
} FileInfo;
typedef struct {
FILE* fp;
} RawNTFSContext;
typedef struct {
union {
VHDContext vhd;
RawNTFSContext raw;
};
bool is_vhd;
NTFSBootSector boot;
uint32_t bytes_per_cluster;
uint64_t mft_offset;
uint32_t mft_record_size;
uint64_t mft_data_size;
uint64_t total_mft_records;
char base_path[MAX_PATH_LENGTH];
DirectoryCache dir_cache;
uint64_t data_start_offset;
} NTFSContext;
bool ntfs_init(NTFSContext* ctx, const char* vhd_path, const char* extract_path);
bool ntfs_extract_all(NTFSContext* ctx);
void ntfs_close(NTFSContext* ctx);
static bool vhd_read(VHDContext* ctx, void* buffer, uint64_t offset, size_t size);
static bool create_directories(const char* path);
static void convert_name_to_ascii(const uint16_t* utf16_name, int name_length, char* ascii_name);
static bool add_directory_to_cache(DirectoryCache* cache, uint64_t ref_number, const char* path);
static bool vhd_init(VHDContext* ctx, const char* filename);
static bool ntfs_read(NTFSContext* ctx, void* buffer, uint64_t offset, size_t size);
#endif // NTFS_H
-150
View File
@@ -1,150 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{4b2c8a0e-0be1-4ed1-8108-6124c58f3a46}</ProjectGuid>
<RootNamespace>unsegareturn</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>unsegareborn</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>"D:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\WS2_32.Lib";"D:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\User32.Lib";"D:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\Crypt32.Lib";"C:\Users\User\vcpkg\buildtrees\openssl\x64-windows-rel\libssl_static.lib";"C:\Users\User\vcpkg\buildtrees\openssl\x64-windows-rel\libcrypto_static.lib";%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>"D:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\WS2_32.Lib";"D:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\User32.Lib";"D:\Program Files (x86)\Windows Kits\10\Lib\10.0.22621.0\um\x64\Crypt32.Lib";"C:\Users\User\vcpkg\buildtrees\openssl\x64-windows-rel\libssl_static.lib";"C:\Users\User\vcpkg\buildtrees\openssl\x64-windows-rel\libcrypto_static.lib";%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="bootid.c" />
<ClCompile Include="crypto.c" />
<ClCompile Include="exfat.c" />
<ClCompile Include="main.c" />
<ClCompile Include="ntfs.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="bootid.h" />
<ClInclude Include="crypto.h" />
<ClInclude Include="exfat.h" />
<ClInclude Include="ntfs.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
-48
View File
@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="bootid.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="crypto.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="main.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="exfat.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ntfs.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="crypto.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="bootid.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="exfat.h">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="ntfs.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
</Project>
-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>