From 7f2b4debdf51f05e5cd56acd67af923538ed8b6a Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 19 May 2022 01:41:18 +0500 Subject: [PATCH 1/2] Preparation for the unavoidable --- blkstuff/aes.c | 50 +++++++++++++++++++++++++++----------- blkstuff/magic_constants.h | 12 +++++++++ blkstuff/util.cpp | 13 +++++++++- blkstuff/util.h | 9 ++++++- 4 files changed, 68 insertions(+), 16 deletions(-) diff --git a/blkstuff/aes.c b/blkstuff/aes.c index eef23a4..be36e85 100644 --- a/blkstuff/aes.c +++ b/blkstuff/aes.c @@ -317,6 +317,29 @@ void oqs_aes128_enc_c(const uint8_t *plaintext, const void *_schedule, uint8_t * xor_round_key(ciphertext, schedule, 10); } +// It's not enc nor dec, it's something in between +void oqs_mhy128_enc_c(const uint8_t *plaintext, const void *_schedule, uint8_t *ciphertext) { + const uint8_t *schedule = (const uint8_t *) _schedule; + int i; // To count the rounds + + // First Round + memcpy(ciphertext, plaintext, 16); + xor_round_key(ciphertext, schedule, 0); + + // Middle rounds + for (i = 0; i < 9; i++) { + sub_bytes_inv(ciphertext, 16); + shift_rows_inv(ciphertext); + mix_cols_inv(ciphertext); + xor_round_key(ciphertext, schedule, i + 1); + } + + // Final Round + sub_bytes_inv(ciphertext, 16); + shift_rows_inv(ciphertext); + xor_round_key(ciphertext, schedule, 10); +} + void oqs_aes128_dec_c(const uint8_t *ciphertext, const void *_schedule, uint8_t *plaintext) { const uint8_t *schedule = (const uint8_t *) _schedule; int i; // To count the rounds @@ -339,25 +362,24 @@ void oqs_aes128_dec_c(const uint8_t *ciphertext, const void *_schedule, uint8_t xor_round_key(plaintext, schedule, 0); } -// It's not enc nor dec, it's something in between -void oqs_mhy128_enc_c(const uint8_t *plaintext, const void *_schedule, uint8_t *ciphertext) { +void oqs_mhy128_dec_c(const uint8_t *ciphertext, const void *_schedule, uint8_t *plaintext) { const uint8_t *schedule = (const uint8_t *) _schedule; int i; // To count the rounds - // First Round - memcpy(ciphertext, plaintext, 16); - xor_round_key(ciphertext, schedule, 0); + // Reverse the final Round + memcpy(plaintext, ciphertext, 16); + xor_round_key(plaintext, schedule, 10); + shift_rows(plaintext); + sub_bytes(plaintext, 16); - // Middle rounds + // Reverse the middle rounds for (i = 0; i < 9; i++) { - sub_bytes_inv(ciphertext, 16); - shift_rows_inv(ciphertext); - mix_cols_inv(ciphertext); - xor_round_key(ciphertext, schedule, i + 1); + xor_round_key(plaintext, schedule, 9 - i); + mix_cols(plaintext); + shift_rows(plaintext); + sub_bytes(plaintext, 16); } - // Final Round - sub_bytes_inv(ciphertext, 16); - shift_rows_inv(ciphertext); - xor_round_key(ciphertext, schedule, 10); + // Reverse the first Round + xor_round_key(plaintext, schedule, 0); } diff --git a/blkstuff/magic_constants.h b/blkstuff/magic_constants.h index 476c9c0..6d288c6 100644 --- a/blkstuff/magic_constants.h +++ b/blkstuff/magic_constants.h @@ -1,4 +1,5 @@ #pragma once +#include #include // giant constants/precalculated stuff go here @@ -693,6 +694,17 @@ static inline uint8_t gf256_mul(uint8_t a, uint8_t b) return gf256_exp[(gf256_log[a] + gf256_log[b]) % 255]; } +static inline uint8_t gf256_div(uint8_t a, uint8_t b) +{ + if (a == 0) + return 0; + + if (b == 0) + assert(b == 0); + + return gf256_exp[(255 + gf256_log[a] - gf256_log[b]) % 255]; +} + #pragma pack(1) struct blk_header { diff --git a/blkstuff/util.cpp b/blkstuff/util.cpp index bab5d58..f4a958a 100644 --- a/blkstuff/util.cpp +++ b/blkstuff/util.cpp @@ -33,6 +33,17 @@ void dump_to_file(const char* name, void* data, size_t size) { fclose(output); } +void read_from_file(const char* name, void* data, size_t size) { + FILE* input = fopen(name, "rb"); + + if (!input) { + printf("failed to open input\n"); + exit(1); + } + fread(data, size, 1, input); + fclose(input); +} + #ifndef memmem // https://stackoverflow.com/questions/52988769/writing-own-memmem-for-windows void* memmem(void* haystack, size_t haystack_len, void* needle, size_t needle_len) { @@ -50,4 +61,4 @@ void* memmem(void* haystack, size_t haystack_len, void* needle, size_t needle_le } return NULL; } -#endif \ No newline at end of file +#endif diff --git a/blkstuff/util.h b/blkstuff/util.h index 1bc1004..3c295a4 100644 --- a/blkstuff/util.h +++ b/blkstuff/util.h @@ -2,6 +2,13 @@ void hexdump(const char* caption, void* ptr, int buflen); void dump_to_file(const char* name, void* data, size_t size); +void read_from_file(const char* name, void* data, size_t size); void* memmem(void* haystack, size_t haystack_len, void* needle, size_t needle_len); -#define MAKE_UINT32(a, b1, b2, b3, b4) (uint8_t)a[b1] | ((uint8_t)a[b2] << 8) | ((uint8_t)a[b3] << 16) | ((uint8_t)a[b4] << 24) \ No newline at end of file +#define MAKE_UINT32(a, b1, b2, b3, b4) (uint8_t)a[b1] | ((uint8_t)a[b2] << 8) | ((uint8_t)a[b3] << 16) | ((uint8_t)a[b4] << 24) +#define PUT_UINT32(a, b1, b2, b3, b4, i) do {\ + a[b1] = i & 0xFF;\ + a[b2] = (i>>8) & 0xFF;\ + a[b3] = (i>>16) & 0xFF;\ + a[b4] = (i>>24) & 0xFF;\ + } while (0) From 60f2fac836b3e110b9266708bac9af24168e8a9c Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 19 May 2022 19:00:58 +0500 Subject: [PATCH 2/2] Repacking, first iteration --- blkstuff/main.cpp | 381 ++++++++++++++++++++++++++++++++++++++++++++-- blkstuff/util.cpp | 10 +- blkstuff/util.h | 2 +- 3 files changed, 378 insertions(+), 15 deletions(-) diff --git a/blkstuff/main.cpp b/blkstuff/main.cpp index 11f47c6..383bd3b 100644 --- a/blkstuff/main.cpp +++ b/blkstuff/main.cpp @@ -12,6 +12,8 @@ using std::cout, std::endl, std::hex, std::dec; +void key_descramble2(uint8_t* key); + // notes are from the genshin impact 1.5 dev build leak UnityEngine.dll (sha256 38399169552791bbfb7b3792dd3e91d3788067e29ffc2437f595060b051d2dd3) void key_scramble1(uint8_t* key) { @@ -20,6 +22,54 @@ void key_scramble1(uint8_t* key) { key[i] = key_scramble_table1[((i & 3) << 8) | key[i]]; } +void create_encrypt_vector(uint8_t* output, uint64_t output_size, uint64_t seed) { + auto mt_rand = std::mt19937_64(seed); + for (uint64_t i = 0; i < output_size >> 3; i++) + ((uint64_t*)output)[i] = mt_rand(); +} + +uint8_t key_scramble_table1_rev(int subtable, uint8_t value) { + int key_table_offset = (subtable & 3) << 8; + + int pp = 0; + for (pp = 0; pp < 256; pp++) { // TODO: not a most efficient but works + if (*(key_scramble_table1 + key_table_offset + pp) == value) + break; + } + + assert(pp < 256); + + return pp; +} + +void key_descramble1(uint8_t* key) { + for (unsigned i = 0; i < 0x10; i++) { + key[i] = key_scramble_table1_rev(i & 3, key[i]); + } +} + +void calc_decrypt_key(uint8_t* key, uint64_t seed, uint8_t* encrypted_data, uint64_t encrypted_size) { + auto* key_qword = (uint64_t*)key; + + uint64_t val = 0xFFFFFFFFFFFFFFFF; + + for (int i = 0; i < encrypted_size >> 3; i++) { + val = ((uint64_t*)encrypted_data)[i] ^ val; + } + + for (int i = 0; i < 8; i++) + key[i] = rand() & 0xFF; + + key_qword[1] = 0x567BA22BABB08098 ^ val ^ key_qword[0] ^ seed; + + uint8_t hard_key[] = { 0xE3, 0xFC, 0x2D, 0x26, 0x9C, 0xC5, 0xA2, 0xEC, 0xD3, 0xF8, 0xC6, 0xD3, 0x77, 0xC2, 0x49, 0xB9 }; + for (int i = 0; i < 16; i++) + key[i] ^= hard_key[i]; + + key_descramble2(key); + key_descramble1(key); +} + void create_decrypt_vector(uint8_t* key, uint8_t* encrypted_data, uint64_t encrypted_size, uint8_t* output, uint64_t output_size) { if (output_size != 4096) { cout << "create_decrypt_vector does not support an output_size other than 4096" << endl; @@ -43,7 +93,7 @@ void create_decrypt_vector(uint8_t* key, uint8_t* encrypted_data, uint64_t encry ((uint64_t*)output)[i] = mt_rand(); } -#if 0 +#if 1 void aes_expand_round_keys(uint8_t* round_keys, const uint8_t* seed) { int N = 4; @@ -87,15 +137,18 @@ void aes_expand_round_keys(uint8_t* round_keys, const uint8_t* seed) 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d}; auto rot_word = [](uint32_t word) { - return (word >> 8) | ((word << 24) & 0xFF000000); + return (word << 8) | ((word >> 24) & 0x000000FF); + //return (word >> 8) | ((word << 24) & 0xFF000000); }; + static const uint8_t* ls = lookup_sbox; + auto sub_word = [](uint32_t word) { return - lookup_sbox[ word & 0xFF] | - lookup_sbox[(word >> 8) & 0xFF] << 8 | - lookup_sbox[(word >> 16) & 0xFF] << 16 | - lookup_sbox[(word >> 24) & 0xFF] << 24 ; + ls[ word & 0xFF] | + ls[(word >> 8) & 0xFF] << 8 | + ls[(word >> 16) & 0xFF] << 16 | + ls[(word >> 24) & 0xFF] << 24 ; }; for (int i = 0; i < 16; i++) @@ -113,10 +166,12 @@ void aes_expand_round_keys(uint8_t* round_keys, const uint8_t* seed) } } + #if 0 for (int round = 0; round <= 10; round++) { hexdump("Derived round key: ", round_keys + round*16, 16); } + #endif } #endif @@ -143,8 +198,9 @@ void kinda_expand_round_keys(uint8_t* round_keys) #endif } -// This function is not exported, so hackaround it +// These functions are not exported, so hackaround it extern "C" void oqs_mhy128_enc_c(const uint8_t *plaintext, const void *_schedule, uint8_t *ciphertext); +extern "C" void oqs_mhy128_dec_c(const uint8_t *chiphertext, const void *_schedule, uint8_t *plaintext); void key_scramble2(uint8_t* key) { uint8_t round_keys[11*16] = {0}; @@ -158,6 +214,18 @@ void key_scramble2(uint8_t* key) { memcpy(key, chip, 16); } +void key_descramble2(uint8_t* key) { + uint8_t round_keys[11*16] = {0}; + + kinda_expand_round_keys(round_keys); + + uint8_t chip[16]; + + oqs_mhy128_dec_c(key, round_keys, chip); + + memcpy(key, chip, 16); +} + void mhy0_header_scramble2(uint8_t* input) { // UnityPlayer:$152300 @@ -191,6 +259,43 @@ void mhy0_header_scramble2(uint8_t* input) } } +void mhy0_header_descramble2(uint8_t* input) +{ + uint8_t tmp[16]; + + uint8_t smol_key[] = { + 0x48, 0x14, 0x36, 0xED, 0x8E, 0x44, 0x5B, 0xB6 + }; + + uint8_t v25[] = { + 0xA7, 0x99, 0x66, 0x50, 0xB9, 0x2D, 0xF0, 0x78 + }; + + uint8_t mhy0_index_scramble[] = { + 0x0B,0x02,0x08,0x0C,0x01,0x05,0x00,0x0F,0x06,0x07,0x09,0x03,0x0D,0x04,0x0E,0x0A, + 0x04,0x05,0x07,0x0A,0x02,0x0F,0x0B,0x08,0x0E,0x0D,0x09,0x06,0x0C,0x03,0x00,0x01, + 0x08,0x00,0x0C,0x06,0x04,0x0B,0x07,0x09,0x05,0x03,0x0F,0x01,0x0D,0x0A,0x02,0x0E, + }; + + for (int k = 0; k < 3; k++) + { + for (int j = 0; j < 16; j++) + { + //int i = mhy0_index_scramble[(2 - k)*16 + j]; + int i = mhy0_index_scramble[k*16 + j]; + + int idx = j % 8; + uint8_t q = smol_key[idx] ^ input[j]; + int key_table_offset = (j & 3) << 8; + + uint8_t pp = key_scramble_table1_rev(j&3, q); + + tmp[i] = gf256_div(pp, v25[idx]); + } + memcpy(input, tmp, 16); + } +} + void mhy0_header_scramble(uint8_t* input, uint64_t limit, uint8_t* input2, uint64_t chunk_size) { if (!((limit == 0x39 && chunk_size == 0x1C) || (limit == 0x21 && chunk_size == 8))) { cout << "unsupported parameters for mhy0_header_scramble" << endl; @@ -224,6 +329,207 @@ void mhy0_header_scramble(uint8_t* input, uint64_t limit, uint8_t* input2, uint6 } } +void mhy0_header_descramble(uint8_t* input, uint64_t limit, uint8_t* input2, uint64_t chunk_size) { + if (!((limit == 0x39 && chunk_size == 0x1C) || (limit == 0x21 && chunk_size == 8))) { + cout << "unsupported parameters for mhy0_header_scramble" << endl; + exit(1); + } + + int rounded_size = (chunk_size + 15) & 0xFFFFFFF0; + uint64_t total_rounded_size = (uint64_t)rounded_size + 4; + + bool finished = false; + while (total_rounded_size < limit && !finished) + { + for (int k = 0; k < chunk_size; ++k) + { + input[k + total_rounded_size] ^= input2[k]; + if (k + total_rounded_size >= limit - 1) + { + finished = true; + break; + } + } + total_rounded_size += chunk_size; + } + + for (int j = 3; j >= 0; j--) + input[j] ^= input2[j]; + + for (int i = 0; i < rounded_size; i += 16) // This can be left as is without reversing the direction + mhy0_header_descramble2(&input[i + 4]); +} + +void mhy0_repack(const char* out_format, int block_index, uint8_t* input, size_t input_size, uint8_t*& output_buf, size_t& output_size) { + // loosely based on UnityPlayer:$1C64C0 + // TODO: bounds checks + if (*(uint32_t*)input != 0x3079686D) { // mhy0 + cout << "decrypted data didn't start with mhy0, so decryption probably failed" << endl; + exit(1); + } + + uint32_t size = *(uint32_t*)(input + 4); + cout << "first size 0x" << std::hex << size << endl; + + if (size > input_size) { + // TODO: this is probably caused by the awful mhy0 searching approach i do instead of properly calculating offsets + cout << "oh shit! attempted to get 0x" << hex << size << " bytes out of a 0x" << hex << input_size << " input! skipping mhy0 " << dec << block_index << endl; + return; + } + + uint8_t* tmp_output_buf = new uint8_t[input_size*2]; // To be 146% sure + + auto* data = new uint8_t[size]; + memcpy(data, input + 8, size); + + //hexdump("initial data", data, size); + + mhy0_header_scramble(data, 0x39, data + 4, 0x1C); + + //hexdump("data after scramble", data, size); + + //dump_to_file("mhy0_header_descrambled.bin", data, size); + + // TODO: there is a different path for calculating this, so this might mess up on some inputs + //uint32_t decomp_size = MAKE_UINT32(data[0x20 + 1], data[0x20 + 6], data[0x20 + 3], data[0x20 + 2]); + uint32_t decomp_size = MAKE_UINT32(data, 0x20 + 1, 0x20 + 6, 0x20 + 3, 0x20 + 2); + cout << "decompressed size: 0x" << hex << decomp_size << endl; + uint8_t* decomp_output = new uint8_t[decomp_size]; + auto lz4_res = LZ4_decompress_safe((const char*)(data + 0x27), (char*)decomp_output, size - 0x27, decomp_size); + if (lz4_res < 0) { + cout << "decompression failed: " << lz4_res << endl; + exit(1); + } + delete[] data; + //dump_to_file("mhy0_header_decompressed.bin", decomp_output, decomp_size); + + //cout << "next data cmp size: 0x" << hex << MAKE_UINT32(decomp_output, 0x11F + 2, 0x11F + 4, 0x11F, 0x11F + 5) << endl; + //cout << "next data decmp size: 0x" << hex << MAKE_UINT32(decomp_output, 0x112 + 1, 0x112 + 6, 0x112 + 3, 0x112 + 2) << endl; + //cout << "unknown 1: 0x" << hex << MAKE_UINT32(decomp_output, 0x10C + 2, 0x10C + 4, 0x10C, 0x10C + 5) << endl; + auto cab_count = MAKE_UINT32(decomp_output, 2, 4, 0, 5); + cout << "cab count: 0x" << hex << cab_count << endl; + //auto entry_count = MAKE_UINT32(decomp_output, 0x119 + 2, 0x119 + 4, 0x119, 0x119 + 5); + auto entry_count = MAKE_UINT32(decomp_output, cab_count * 0x113 + 6 + 2, cab_count * 0x113 + 6 + 4, cab_count * 0x113 + 6, cab_count * 0x113 + 6 + 5); + cout << "entry count: 0x" << hex << entry_count << endl; + //dump_to_file("bruh.bin", decomp_output, decomp_size); + //hexdump("asdf", decomp_output, decomp_size); + //exit(1); + //if (entry_count > 0x10000) { + //hexdump("wtf???? something probably went wrong!", decomp_output, decomp_size); + //cout << "0x" << hex << MAKE_UINT32(decomp_output, 2, 4, 0, 5) << endl; + //exit(1); + //} + + uint8_t* entry_ptr = input + 0x8 + size; + uint8_t* w_entry_ptr = tmp_output_buf; + char filename[0x100] = {}; + //cout << out_format << endl; + snprintf(filename, sizeof(filename), out_format, block_index); + auto* output = fopen(filename, "wb"); + if (!output) { + cout << "failed to open " << filename << endl; + exit(1); + } + for (int i = 0; i < entry_count; i++) { + auto offset = i * 13 + cab_count * 0x113 + 6; + //cout << "processing entry " << i << " offset " << offset << " ptr " << 0x8 + size << endl; + auto entry_cmp_size = MAKE_UINT32(decomp_output, offset + 6 + 2, offset + 6 + 4, offset + 6, offset + 6 + 5); + auto entry_decmp_size = MAKE_UINT32(decomp_output, offset + 0xC + 1, offset + 0xC + 6, offset + 0xC + 3, offset + 0xC + 2); + cout << hex << entry_cmp_size << endl; + cout << hex << entry_decmp_size << endl; + //hexdump("initial data", entry_ptr, entry_cmp_size); + mhy0_header_scramble(entry_ptr, 0x21, entry_ptr + 4, 8); + //hexdump("data after scramble", entry_ptr, entry_cmp_size); + + auto* entry_decmp = new uint8_t[entry_decmp_size]; + auto lz4_res = LZ4_decompress_safe((const char*)(entry_ptr + 0xC), (char*)entry_decmp, entry_cmp_size - 0xC, entry_decmp_size); + if (lz4_res < 0) { + cout << "decompression failed: " << lz4_res << endl; + exit(1); + } + //dump_to_file(filename, entry_decmp, entry_decmp_size); + fwrite(entry_decmp, entry_decmp_size, 1, output); + + dump_to_file("PROCESS.bin", entry_decmp, entry_decmp_size); + + cout << "Process PROCESS.bin file and press Enter when finished. Try not to change size!" << endl; + + getchar(); + + uint8_t* new_entry_data = nullptr; + size_t new_entry_size = 0; + + read_from_file("PROCESS.bin", new_entry_data, new_entry_size); + + if (new_entry_size != entry_decmp_size) { + cout << "WARN: new file size is different: " << new_entry_size << " != " << entry_decmp_size << endl; + } + + memcpy(w_entry_ptr, entry_ptr, 0xC); + auto lz4_c_res = LZ4_compress_default((const char*)new_entry_data, (char*)(w_entry_ptr + 0xC), new_entry_size, (entry_cmp_size - 0xC)*2); // To be 146% sure + if (lz4_c_res == 0) { + cout << "compression failed: " << lz4_c_res << endl; + exit(1); + } + mhy0_header_descramble(w_entry_ptr, 0x21, w_entry_ptr + 4, 8); + lz4_c_res += 0xC; + + delete[] new_entry_data; + + cout << "new comp size " << lz4_c_res << endl; + cout << "new decomp size " << new_entry_size << endl; + + PUT_UINT32(decomp_output, offset + 6 + 2, offset + 6 + 4, offset + 6, offset + 6 + 5, lz4_c_res); + PUT_UINT32(decomp_output, offset + 0xC + 1, offset + 0xC + 6, offset + 0xC + 3, offset + 0xC + 2, new_entry_size); + + delete[] entry_decmp; + entry_ptr += entry_cmp_size; + w_entry_ptr += lz4_c_res; + + entry_cmp_size = MAKE_UINT32(decomp_output, offset + 6 + 2, offset + 6 + 4, offset + 6, offset + 6 + 5); + entry_decmp_size = MAKE_UINT32(decomp_output, offset + 0xC + 1, offset + 0xC + 6, offset + 0xC + 3, offset + 0xC + 2); + cout << "Recorded" << endl; + cout << hex << entry_cmp_size << endl; + cout << hex << entry_decmp_size << endl; + } + fclose(output); + + uint8_t* new_header = new uint8_t[0x27 + 8 + decomp_size*2]; // To be 146% sure + uint8_t* new_data = new_header + 8; + + memcpy(new_data, data, size); + + auto lz4_c_res = LZ4_compress_default((const char*)decomp_output, (char*)(new_data + 0x27), decomp_size, decomp_size*2); // To be 146% sure + if (lz4_c_res == 0) { + cout << "compression failed: " << lz4_c_res << endl; + exit(1); + } + + delete[] decomp_output; + + size = lz4_c_res + 0x27; + + cout << "new size " << size << endl; + + *(uint32_t*)new_header = 0x3079686D; // "mhy0" + *(uint32_t*)(new_header+4) = size; // New compressed size + + mhy0_header_descramble(new_data, 0x39, new_data + 4, 0x1C); + + size_t pure_output_size = w_entry_ptr - tmp_output_buf; + + output_size = pure_output_size + size + 8; + + output_buf = new uint8_t[output_size]; + memcpy(output_buf, new_header, size+8); + memcpy(output_buf+size+8, tmp_output_buf, pure_output_size); + + delete[] new_header; + delete[] tmp_output_buf; + + //dump_to_file("reconstructed.bin", output_buf, output_size); +} + void mhy0_extract(const char* out_format, int block_index, uint8_t* input, size_t input_size) { // loosely based on UnityPlayer:$1C64C0 // TODO: bounds checks @@ -250,7 +556,7 @@ void mhy0_extract(const char* out_format, int block_index, uint8_t* input, size_ //hexdump("data after scramble", data, size); - //dump_to_file("output.bin", data, size); + //dump_to_file("mhy0_header_descrambled.bin", data, size); // TODO: there is a different path for calculating this, so this might mess up on some inputs //uint32_t decomp_size = MAKE_UINT32(data[0x20 + 1], data[0x20 + 6], data[0x20 + 3], data[0x20 + 2]); @@ -263,7 +569,7 @@ void mhy0_extract(const char* out_format, int block_index, uint8_t* input, size_ exit(1); } delete[] data; - //dump_to_file("mhy0_header.bin", decomp_output, decomp_size); + //dump_to_file("mhy0_header_decompressed.bin", decomp_output, decomp_size); //cout << "next data cmp size: 0x" << hex << MAKE_UINT32(decomp_output, 0x11F + 2, 0x11F + 4, 0x11F, 0x11F + 5) << endl; //cout << "next data decmp size: 0x" << hex << MAKE_UINT32(decomp_output, 0x112 + 1, 0x112 + 6, 0x112 + 3, 0x112 + 2) << endl; @@ -292,8 +598,8 @@ void mhy0_extract(const char* out_format, int block_index, uint8_t* input, size_ exit(1); } for (int i = 0; i < entry_count; i++) { - //cout << "processing entry " << i << endl; auto offset = i * 13 + cab_count * 0x113 + 6; + //cout << "processing entry " << i << " offset " << offset << " ptr " << 0x8 + size << endl; auto entry_cmp_size = MAKE_UINT32(decomp_output, offset + 6 + 2, offset + 6 + 4, offset + 6, offset + 6 + 5); auto entry_decmp_size = MAKE_UINT32(decomp_output, offset + 0xC + 1, offset + 0xC + 6, offset + 0xC + 3, offset + 0xC + 2); //cout << hex << entry_cmp_size << endl; @@ -317,7 +623,7 @@ void mhy0_extract(const char* out_format, int block_index, uint8_t* input, size_ delete[] decomp_output; } -int extract_blk(char* in_filename, const char* out_format) { +int extract_blk(char* in_filename, const char* out_format, bool perform_repack = false) { auto* blk_file = fopen(in_filename, "rb"); if (!blk_file) { @@ -325,7 +631,7 @@ int extract_blk(char* in_filename, const char* out_format) { return 1; } - blk_header hdr; + blk_header hdr, hdr_copy; bool fail = false; if (fread(&hdr, sizeof(blk_header), 1, blk_file) < 1) { @@ -348,6 +654,9 @@ int extract_blk(char* in_filename, const char* out_format) { return 1; } + if (perform_repack) + memcpy(&hdr_copy, &hdr, sizeof(blk_header)); + //hexdump("encrypted blk key:", key, sizeof(key)); key_scramble1(hdr.key1); key_scramble2(hdr.key1); @@ -375,6 +684,12 @@ int extract_blk(char* in_filename, const char* out_format) { //fwrite(data, size, 1, output); + uint8_t* output_buf = nullptr; + size_t output_size = 0; + + uint8_t* full_buff = nullptr; + size_t full_size = 0; + std::vector mhy0_locs; size_t last_loc = 0; for (int i = 0; ; i++) { @@ -383,7 +698,15 @@ int extract_blk(char* in_filename, const char* out_format) { auto loc = (uint8_t*)res - data; mhy0_locs.push_back(loc); //cout << "found mhy0 at 0x" << hex << loc << endl; - mhy0_extract(out_format, i, data + loc, size); + if (perform_repack) { + mhy0_repack(out_format, i, data + loc, size, output_buf, output_size); + full_buff = (uint8_t*)realloc(full_buff, full_size + output_size); + memcpy(full_buff+full_size, output_buf, output_size); + full_size += output_size; + delete[] output_buf; + } else { + mhy0_extract(out_format, i, data + loc, size); + } last_loc = loc + 4; } else { break; @@ -392,6 +715,27 @@ int extract_blk(char* in_filename, const char* out_format) { delete[] data; + if (perform_repack) { + for (int i = 0; i < 4096; i++) + xorpad[i] = 0; + + uint64_t seed = 0xDEADBEEF; // TODO: maybe choose a random one? + + create_encrypt_vector(xorpad, sizeof(xorpad), seed); + + for (int i = 0; i < output_size; i++) + full_buff[i] ^= xorpad[i & 0xFFF]; + + calc_decrypt_key(hdr_copy.key1, seed, full_buff, std::min((uint64_t)hdr.block_size, sizeof(xorpad))); // TODO: what if data size is small? + + FILE* repack = fopen("repack.blk", "wb"); + fwrite(&hdr_copy, sizeof(blk_header), 1, repack); + fwrite(full_buff, full_size, 1, repack); + fclose(repack); + + free(full_buff); + } + return 0; } @@ -399,10 +743,15 @@ int main(int argc, char** argv) { #if 0 uint8_t round_keys[11*16] = {0}; uint8_t seed[] = {0x54, 0x2f, 0xed, 0x67, 0x5d, 0xdd, 0x11, 0x2e, 0xb7, 0x40, 0x13, 0xe3, 0x29, 0xab, 0x6d, 0x28}; + uint8_t seed2[] = {0x1e, 0x14, 0x40, 0x12, 0xed, 0x7b, 0x85, 0x47, 0x8d, 0xd2, 0xcd, 0xf8, 0x4d, 0x71, 0xbc, 0x62}; aes_expand_round_keys(round_keys, seed); + puts("--------------------"); memset(round_keys, 0, 11*16); kinda_expand_round_keys(round_keys); + puts("--------------------"); + memset(round_keys, 0, 11*16); + aes_expand_round_keys(round_keys, seed2); exit(0); #endif @@ -448,6 +797,12 @@ int main(int argc, char** argv) { if (!ret) cout << "ok" << endl; } + } else if (!strcmp(argv[1], "repack")) { + if (argc < 3) { + cout << "you need to provide input BLK name" << endl; + return 1; + } + extract_blk(argv[2], "output%d.bin", true); } else { extract_blk(argv[1], "output%d.bin"); } diff --git a/blkstuff/util.cpp b/blkstuff/util.cpp index f4a958a..aec9027 100644 --- a/blkstuff/util.cpp +++ b/blkstuff/util.cpp @@ -2,6 +2,7 @@ #include #include #include +#include // https://stackoverflow.com/questions/29242/off-the-shelf-c-hex-dump-code void hexdump(const char* caption, void* ptr, int buflen) { @@ -33,13 +34,20 @@ void dump_to_file(const char* name, void* data, size_t size) { fclose(output); } -void read_from_file(const char* name, void* data, size_t size) { +void read_from_file(const char* name, uint8_t*& data, size_t& size) { FILE* input = fopen(name, "rb"); if (!input) { printf("failed to open input\n"); exit(1); } + + fseek(input, 0, SEEK_END); + size = ftell(input); + fseek(input, 0, SEEK_SET); + + data = new uint8_t[size]; + fread(data, size, 1, input); fclose(input); } diff --git a/blkstuff/util.h b/blkstuff/util.h index 3c295a4..c6db158 100644 --- a/blkstuff/util.h +++ b/blkstuff/util.h @@ -2,7 +2,7 @@ void hexdump(const char* caption, void* ptr, int buflen); void dump_to_file(const char* name, void* data, size_t size); -void read_from_file(const char* name, void* data, size_t size); +void read_from_file(const char* name, uint8_t*& data, size_t& size); void* memmem(void* haystack, size_t haystack_len, void* needle, size_t needle_len); #define MAKE_UINT32(a, b1, b2, b3, b4) (uint8_t)a[b1] | ((uint8_t)a[b2] << 8) | ((uint8_t)a[b3] << 16) | ((uint8_t)a[b4] << 24)