Last active
January 17, 2024 12:30
-
-
Save Alyinghood/21bdb81d6f83114604082bd019482ae3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
// See https://forums.nrvnqsr.com/showthread.php/9708-Mahoutsukai-no-Yoru-HD-PC-file-format-deciphering | |
#include <windows.h> | |
#include <string.h> | |
#include <stdio.h> | |
#include <stdbool.h> | |
#include <stdlib.h> | |
#include <stdint.h> | |
// Dependency: https://github.com/TsudaKageyu/minhook | |
#include "MinHook.h" | |
static uint64_t work_image_base = 0x00007ff6bf390000; | |
static void *image_base = NULL; | |
int trymap(char *exe_path) | |
{ | |
#if defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64) | |
// PE headers | |
PIMAGE_DOS_HEADER pidh; | |
PIMAGE_NT_HEADERS pinh; | |
PIMAGE_SECTION_HEADER pish; | |
// pointer to virtually allocated memory | |
LPVOID lpAddress = NULL; | |
// read executable file from storage | |
BYTE *data = NULL; | |
ULONG size = 0; | |
{ | |
FILE* input_file = fopen(exe_path, "rb"); | |
if (!input_file) | |
return 1; | |
fseek(input_file, 0, SEEK_END); | |
uint32_t input_file_size = ftell(input_file); | |
fseek(input_file, 0, SEEK_SET); | |
uint8_t *exe_bytes = malloc(input_file_size); | |
if (!exe_bytes) | |
return 1; | |
size_t input_file_bytes_read = fread(exe_bytes, 1, input_file_size, input_file); | |
if (input_file_bytes_read != input_file_size) | |
return 1; | |
data = exe_bytes; | |
size = input_file_size; | |
} | |
// check if valid DOS header | |
pidh = (PIMAGE_DOS_HEADER)data; | |
if (pidh->e_magic != IMAGE_DOS_SIGNATURE) | |
{ | |
printf("DOS signature error: %x\n", GetLastError()); | |
return 1; | |
} | |
// check if valid pe file | |
pinh = (PIMAGE_NT_HEADERS)((ULONG_PTR)data + pidh->e_lfanew); | |
if (pinh->Signature != IMAGE_NT_SIGNATURE) | |
{ | |
printf("PE signature error: %x\n", GetLastError()); | |
return 1; | |
} | |
image_base = (void *)(pinh->OptionalHeader.ImageBase); | |
if (pinh->OptionalHeader.SizeOfImage != 0x621db1) | |
{ | |
printf("Size of image not known (was executable updated?): %x\n", pinh->OptionalHeader.SizeOfImage); | |
return 1; | |
} | |
// allocate virtual space for process | |
lpAddress = VirtualAllocEx(GetCurrentProcess(), (PVOID)pinh->OptionalHeader.ImageBase, pinh->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); | |
if (lpAddress == NULL) | |
{ | |
printf("Virtual allocation error: %x\n", GetLastError()); | |
return 1; | |
} | |
// write headers into memory | |
if (WriteProcessMemory(GetCurrentProcess(), (PVOID)pinh->OptionalHeader.ImageBase, data, pinh->OptionalHeader.SizeOfHeaders, NULL) == FALSE) | |
{ | |
printf("Write PE header error: %x\n", GetLastError()); | |
return 1; | |
} | |
// write each section into memory | |
for (int i = 0; i < pinh->FileHeader.NumberOfSections; i++) | |
{ | |
// calculate section header of each section | |
pish = (PIMAGE_SECTION_HEADER)((ULONG_PTR)data + pidh->e_lfanew + sizeof (IMAGE_NT_HEADERS) + sizeof (IMAGE_SECTION_HEADER) * i); | |
// write section data into memory | |
if (WriteProcessMemory(GetCurrentProcess(), (PVOID)(pinh->OptionalHeader.ImageBase + pish->VirtualAddress), (LPVOID)((ULONG_PTR)data + pish->PointerToRawData), pish->SizeOfRawData, NULL) == FALSE) | |
{ | |
DWORD last_error = GetLastError(); | |
if (last_error == 87) | |
{ | |
printf("Got ERROR_INVALID_PARAMETER but continuing\n"); | |
} | |
else | |
{ | |
printf("Write section memory error: %x\n", last_error); | |
return 1; | |
} | |
} | |
} | |
return 0; | |
#endif | |
return 1; | |
} | |
struct struct_cbt_this | |
{ | |
BYTE gap0[8]; | |
struct struct_threadjob *csid; | |
void *element10; | |
DWORD jobid; | |
DWORD jobcount; | |
}; | |
typedef void *(__fastcall *workercb)(struct struct_cbt_this *a1); | |
struct struct_threadjob | |
{ | |
DWORD dw00[6]; | |
workercb thread_job_worker_func; | |
void *thread_job_worker_func2; | |
struct struct_cbt_this *thread_job_worker_userdata; | |
}; | |
typedef void *(__fastcall *MallocCallback)(size_t); | |
static MallocCallback fpMalloc = NULL; | |
static MallocCallback fpMalloc2 = NULL; | |
static void * __fastcall DetourMalloc(size_t size) | |
{ | |
return malloc(size); | |
} | |
typedef void (__fastcall *FreeCallback)(void *); | |
static FreeCallback fpFree = NULL; | |
static void __fastcall DetourFree(void *ptr) | |
{ | |
// stubbed | |
} | |
typedef void (__fastcall *WaitJobCallback)(struct struct_threadjob *a1); | |
static WaitJobCallback fpWaitJob = NULL; | |
static void __fastcall DetourWaitJob(struct struct_threadjob *a1) | |
{ | |
if (a1->thread_job_worker_func2 != NULL) | |
{ | |
fprintf(stderr, "Unsupported feature usage detected\n"); | |
exit(1); | |
} | |
a1->thread_job_worker_userdata->jobid = 0; | |
a1->dw00[2] = 0; | |
for (int i = 0; i < a1->thread_job_worker_userdata->jobcount; i += 1) | |
{ | |
a1->thread_job_worker_func(a1->thread_job_worker_userdata); | |
} | |
} | |
typedef void (__stdcall *FailureCallback)(); | |
static FailureCallback fpCxxThrowException = NULL; | |
static FailureCallback fpAbort = NULL; | |
static FailureCallback fpAbort2 = NULL; | |
static void __stdcall DetourFailure() | |
{ | |
fprintf(stderr, "Failure detected, aborting\n"); | |
exit(1); | |
} | |
typedef struct ptr_range_lz_ | |
{ | |
uint8_t *dataptr; | |
uint32_t datasz; | |
uint32_t padd; | |
uint64_t dataszused; | |
} ptr_range_lz; | |
int main(int argc, char** argv) | |
{ | |
if (argc != 4) | |
return 1; | |
if (trymap(argv[1]) != 0 || image_base == NULL) | |
return 2; | |
{ | |
MH_Initialize(); | |
if (MH_CreateHook(((uint8_t *)image_base) + (0x7FF6BF50D8FC - work_image_base), &DetourMalloc, (LPVOID *)&fpMalloc) != MH_OK) | |
{ | |
return 3; | |
} | |
if (MH_CreateHook(((uint8_t *)image_base) + (0x7FF6BF50D60C - work_image_base), &DetourMalloc, (LPVOID *)&fpMalloc2) != MH_OK) | |
{ | |
return 3; | |
} | |
if (MH_CreateHook(((uint8_t *)image_base) + (0x7FF6BF50D4C4 - work_image_base), &DetourFree, (LPVOID *)&fpFree) != MH_OK) | |
{ | |
return 3; | |
} | |
if (MH_CreateHook(((uint8_t *)image_base) + (0x7FF6BF3EF7F0 - work_image_base), &DetourWaitJob, (LPVOID *)&fpWaitJob) != MH_OK) | |
{ | |
return 3; | |
} | |
if (MH_CreateHook(((uint8_t *)image_base) + (0x7FF6BF52BC98 - work_image_base), &DetourFailure, (LPVOID *)&fpCxxThrowException) != MH_OK) | |
{ | |
return 3; | |
} | |
if (MH_CreateHook(((uint8_t *)image_base) + (0x7FF6BF52EE8C - work_image_base), &DetourFailure, (LPVOID *)&fpAbort) != MH_OK) | |
{ | |
return 3; | |
} | |
if (MH_CreateHook(((uint8_t *)image_base) + (0x7FF6BF3A4670 - work_image_base), &DetourFailure, (LPVOID *)&fpAbort2) != MH_OK) | |
{ | |
return 3; | |
} | |
if (MH_EnableHook(MH_ALL_HOOKS) != MH_OK) | |
{ | |
return 5; | |
} | |
} | |
FILE* input_file = fopen(argv[2], "rb"); | |
if (!input_file) | |
return 6; | |
fseek(input_file, 0, SEEK_END); | |
uint32_t input_file_size = ftell(input_file); | |
fseek(input_file, 0, SEEK_SET); | |
uint8_t *compressed_bytes = malloc(input_file_size); | |
if (!compressed_bytes) | |
return 7; | |
size_t input_file_bytes_read = fread(compressed_bytes, 1, input_file_size, input_file); | |
if (input_file_bytes_read != input_file_size) | |
return 8; | |
fclose(input_file); | |
{ | |
int64_t res; | |
unsigned __int8 *uncompressed_data; | |
unsigned int uncompressed_size; | |
struct struct_threadjob tj; | |
int64_t (__fastcall *x_sub_7FF6BF455670_extraction)(unsigned __int8 **unz_data_ptr, unsigned int *unz_data_sz, unsigned __int8 *indata, unsigned int indatasz, __int64 a5, struct struct_threadjob *a6) = (void *)(((uint8_t *)image_base) + (0x7FF6BF455670 - work_image_base)); | |
uncompressed_data = NULL; | |
uncompressed_size = 0; | |
memset(&tj, 0, sizeof(tj)); | |
res = x_sub_7FF6BF455670_extraction(&uncompressed_data, &uncompressed_size, compressed_bytes, input_file_bytes_read, 0, &tj); | |
if (res != 0) | |
{ | |
fprintf(stderr, "Unusual result: %llx\n", res); | |
return 6; | |
} | |
if ( uncompressed_data == NULL ) | |
{ | |
return 6; | |
} | |
if ( uncompressed_size == 0 ) | |
{ | |
return 6; | |
} | |
FILE* out_file = fopen(argv[3], "wb"); | |
if (!out_file) | |
return 7; | |
fwrite(uncompressed_data, 1, uncompressed_size, out_file); | |
fclose(out_file); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment