1
0
mirror of synced 2024-11-28 01:10:55 +01:00

Working Libtomcrypt Implementation

LayeredFS now allows for encrypting game files dynamically
This commit is contained in:
Farewell_ 2024-06-09 18:19:15 +02:00
parent 8214d961a9
commit 15c01fbe7c
9 changed files with 308 additions and 29 deletions

2
.gitignore vendored
View File

@ -11,6 +11,8 @@ subprojects/xxHash-0.8.2
subprojects/safetyhook
subprojects/zxing-cpp-2.2.1
subprojects/stb
subprojects/zlib
libtomcrypt-1.18.2
dist.7z
.vscode
out/

View File

@ -11,5 +11,5 @@ dist-no-7z: all
@cp -r dist/* out/
dist: dist-no-7z
@cd out && 7z a -t7z ../dist.7z .
@cd out && 7zz a -t7z ../dist.7z .
@rm -rf out

2
dist/config.toml vendored
View File

@ -48,3 +48,5 @@ jp_layout = false
[layeredfs]
enabled = false
datatable_key = ""
fumen_key = ""

View File

@ -40,6 +40,10 @@ stb = subproject('stb')
opt_var.add_cmake_defines({'BUILD_EXAMPLES': false})
zxing_proj = cmake.subproject('zxing', options: opt_var)
zxing_dep = zxing_proj.dependency('ZXing')
zlib_proj = subproject('zlib')
zlib_dep = zlib_proj.get_variable('zlib_dep')
libtomcrypt = subproject('libtomcrypt')
libtomcrypt_dep = libtomcrypt.get_variable('tomcrypt_dep')
library(
'bnusio',
@ -48,6 +52,8 @@ library(
tomlc99.get_variable('tomlc99_lib'),
sdl2.get_variable('sdl2'),
xxhash.get_variable('xxhash'),
zlib_proj.get_variable('zlib_lib'),
libtomcrypt.get_variable('tomcryptlib'),
],
include_directories: [
'src',
@ -55,11 +61,16 @@ library(
tomlc99.get_variable('tomlc99_inc'),
sdl2.get_variable('core_inc'),
xxhash.get_variable('inc'),
zlib_proj.get_variable('zlib_inc'),
libtomcrypt.get_variable('core_inc'),
],
dependencies: [stb.get_variable('stb_dep'),
dependencies: [
stb.get_variable('stb_dep'),
zxing_dep,
safetyhook_dep,
zydis_dep,
zlib_dep,
libtomcrypt_dep,
],
sources : [
'src/dllmain.cpp',

View File

@ -3,6 +3,9 @@
#include "helpers.h"
#include "patches/patches.h"
#include "poll.h"
#include <zlib.h>
#include <tomcrypt.h>
#define POLY 0x82f63b78
GameVersion gameVersion = GameVersion::UNKNOWN;
std::vector<HMODULE> plugins;
@ -24,6 +27,159 @@ char chipId2[33] = "00000000000000000000000000000002";
bool autoIME = false;
bool jpLayout = false;
bool useLayeredFs = false;
std::string datatableKey = "0000000000000000000000000000000000000000000000000000000000000000";
std::string fumenKey = "0000000000000000000000000000000000000000000000000000000000000000";
uint32_t crc32c(uint32_t crc, const unsigned char *buf, size_t len)
{
int k;
crc = ~crc;
while (len--) {
crc ^= *buf++;
for (k = 0; k < 8; k++)
crc = (crc >> 1) ^ (POLY & (0 - (crc & 1)));
}
return ~crc;
}
bool checkCRC(const std::string &path, uint32_t crc){
if (std::filesystem::exists(path)){
std::filesystem::path crc_path = path;
crc_path.replace_extension(".crc");
std::ifstream crc_file(crc_path, std::ios::binary);
std::string crc_content((std::istreambuf_iterator<char>(crc_file)), std::istreambuf_iterator<char>());
return std::stoul(crc_content) != crc;
}
return 1;
}
void create_directories(const std::string &path) {
size_t pos = 0;
std::string delimiter = "\\";
std::string current_path;
while ((pos = path.find(delimiter, pos)) != std::string::npos) {
current_path = path.substr(0, pos++);
if (!current_path.empty() && !CreateDirectory(current_path.c_str(), NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
throw std::runtime_error("Error creating directory: " + current_path);
}
}
if (!path.empty() && !CreateDirectory(path.c_str(), NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
throw std::runtime_error("Error creating directory: " + path);
}
}
void write_file(const std::string &filename, const std::vector<uint8_t> &data, uint32_t original_crc) {
std::string::size_type pos = filename.find_last_of("\\");
if (pos != std::string::npos) {
std::string directory = filename.substr(0, pos);
create_directories(directory);
}
std::filesystem::path crc_path = filename;
crc_path.replace_extension(".crc");
std::ofstream crc_file(crc_path.string());
crc_file << std::to_string(original_crc);
crc_file.close();
std::ofstream file(filename, std::ios::binary);
file.write(reinterpret_cast<const char*>(data.data()), data.size());
}
std::vector<unsigned char> gzip_compress(const std::vector<unsigned char>& data) {
z_stream deflate_stream;
deflate_stream.zalloc = Z_NULL;
deflate_stream.zfree = Z_NULL;
deflate_stream.opaque = Z_NULL;
deflate_stream.avail_in = data.size();
deflate_stream.next_in = const_cast<Bytef*>(data.data());
deflateInit2(&deflate_stream, Z_BEST_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
std::vector<unsigned char> compressed_data;
compressed_data.resize(deflateBound(&deflate_stream, data.size()));
deflate_stream.avail_out = compressed_data.size();
deflate_stream.next_out = compressed_data.data();
deflate(&deflate_stream, Z_FINISH);
deflateEnd(&deflate_stream);
compressed_data.resize(deflate_stream.total_out);
return compressed_data;
}
// Function to pad data according to PKCS7
std::vector<uint8_t> pad_data(const std::vector<uint8_t> &data, size_t block_size) {
size_t padding = block_size - (data.size() % block_size);
std::vector<uint8_t> padded_data = data;
padded_data.insert(padded_data.end(), padding, static_cast<uint8_t>(padding));
return padded_data;
}
std::vector<uint8_t> hex_to_bytes(const std::string &hex) {
std::vector<uint8_t> bytes;
for (size_t i = 0; i < hex.length(); i += 2) {
uint8_t byte = static_cast<uint8_t>(std::stoi(hex.substr(i, 2), nullptr, 16));
bytes.push_back(byte);
}
return bytes;
}
std::vector<uint8_t> encrypt_file(const std::string &input_file, const std::string &hex_key) {
// Convert the key from hex to bytes
std::vector<uint8_t> key = hex_to_bytes(hex_key);
// Generate the 128 bits IV
std::vector<uint8_t> iv(16);
for (size_t i = 0; i < iv.size(); ++i) iv[i] = static_cast<uint8_t>(i);
// Read the entire file into memory
std::ifstream file(input_file, std::ios::binary);
std::vector<uint8_t> data((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
// Compress the data
std::vector<uint8_t> compressed_data = gzip_compress(data);
// Pad the compressed data
std::vector<uint8_t> padded_data = pad_data(compressed_data, 16);
// Encrypt the data
symmetric_CBC cbc;
if (cbc_start(find_cipher("aes"), iv.data(), key.data(), key.size(), 0, &cbc) != CRYPT_OK) {
throw std::runtime_error("Error initializing CBC");
}
std::vector<uint8_t> encrypted_data(padded_data.size());
if (cbc_encrypt(padded_data.data(), encrypted_data.data(), padded_data.size(), &cbc) != CRYPT_OK) {
throw std::runtime_error("Error during encryption");
}
cbc_done(&cbc);
// Return IV concatenated with the encrypted data
encrypted_data.insert(encrypted_data.begin(), iv.begin(), iv.end());
return encrypted_data;
}
bool is_fumen_encrypted(const std::string& filename) {
std::ifstream file(filename, std::ios::binary);
file.seekg(0x210, std::ios::beg);
std::vector<unsigned char> buffer(32);
file.read(reinterpret_cast<char*>(buffer.data()), buffer.size());
// Check if the read bytes match the expected pattern
std::vector<unsigned char> expected_bytes = {
0x00, 0x01, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00
};
return buffer != expected_bytes;
}
HOOK (i32, ShowMouse, PROC_ADDRESS ("user32.dll", "ShowCursor"), bool) { return originalShowMouse (true); }
HOOK (i32, ExitWindows, PROC_ADDRESS ("user32.dll", "ExitWindowsEx")) { ExitProcess (0); }
@ -51,48 +207,102 @@ HOOK (HANDLE, CreateFileAHook, PROC_ADDRESS ("kernel32.dll", "CreateFileA"), LPC
if (!path.is_absolute ()) path = std::filesystem::absolute (path);
auto originalDataFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data" / "x64";
auto originalLayeredFsFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data_mods" / "x64";
auto encryptedLayeredFsFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data_mods" / "x64_enc";
if (path.string ().find (originalDataFolder.string ()) == 0) {
auto newPath = path.string ();
auto encPath = path.string ();
newPath.replace (0, originalDataFolder.string ().length (), originalLayeredFsFolder.string ());
encPath.replace (0, originalDataFolder.string ().length (), encryptedLayeredFsFolder.string ());
if (std::filesystem::exists (newPath)) {
std::cout << "Redirecting " << path << " to " << newPath << std::endl;
//The following code handles file redirection and if need be, file encryption.
//It's a bit of a mess but it works well ! -Kit
if (std::filesystem::exists (newPath)) { //If a file exists in the datamod folder
if(is_fumen_encrypted(newPath)){ //And if it's an encrypted fumen or a different type of file, use it.
std::cout << "Redirecting " << std::filesystem::relative (path).string() << std::endl;
return originalCreateFileAHook (newPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile);
}
else{ //Otherwise if it's an unencrypted fumen.
if (!std::filesystem::exists(encPath)) { //We check if we don't already have a cached file.
if(fumenKey.length() == 64){
std::cout << "Encrypting " << std::filesystem::relative (newPath) << std::endl; // If we don't we encrypt the file
std::ifstream crc_file(newPath, std::ios::binary);
std::vector<uint8_t> crc_vector((std::istreambuf_iterator<char>(crc_file)), std::istreambuf_iterator<char>());
uint32_t crc = crc32c(0, crc_vector.data(), crc_vector.size());
write_file(encPath, encrypt_file(newPath, fumenKey), crc); // And we save it
} else {
std::cout << "Missing or invalid fumen key : " << std::filesystem::relative (newPath) << " couldn't be encrypted." << std::endl;
encPath = path.string();
}
} else std::cout << "Using cached file for " << std::filesystem::relative (newPath) << std::endl;
return originalCreateFileAHook (encPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile);
}
}
//We check separately for unencrypted json files.
std::filesystem::path json_path = newPath;
json_path.replace_extension(".json");
if (std::filesystem::exists(json_path)) { //If a json file exists in the folder
bool crcBool = false;
if (std::filesystem::exists(encPath)){
std::ifstream crc_file(json_path, std::ios::binary);
std::vector<uint8_t> crc_vector((std::istreambuf_iterator<char>(crc_file)), std::istreambuf_iterator<char>());
uint32_t crc = crc32c(0, crc_vector.data(), crc_vector.size());
crcBool = checkCRC(encPath, crc);
}
if (!std::filesystem::exists(encPath) || crcBool) { //And if it hasn't been encrypted before
if(datatableKey.length() == 64){
std::cout << "Encrypting " << std::filesystem::relative (json_path) << std::endl; //Encrypt the file
std::ifstream crc_file(json_path.string(), std::ios::binary);
std::vector<uint8_t> crc_vector((std::istreambuf_iterator<char>(crc_file)), std::istreambuf_iterator<char>());
uint32_t crc = crc32c(0, crc_vector.data(), crc_vector.size());
write_file(encPath, encrypt_file(json_path.string(), datatableKey), crc); //And save it
} else {
std::cout << "Missing or invalid datatable key : " << std::filesystem::relative (newPath) << " couldn't be encrypted." << std::endl;
encPath = path.string();
}
}
else std::cout << "Using cached file for " << std::filesystem::relative (json_path) << std::endl; //Otherwise use the already encrypted file.
return originalCreateFileAHook (encPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile);
}
}
return originalCreateFileAHook (lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
hTemplateFile);
}
HOOK (HANDLE, CreateFileWHook, PROC_ADDRESS ("kernel32.dll", "CreateFileW"), LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
std::string strFileName = converter.to_bytes (lpFileName);
// HOOK (HANDLE, CreateFileWHook, PROC_ADDRESS ("kernel32.dll", "CreateFileW"), LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
// LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) {
// std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
// std::string strFileName = converter.to_bytes (lpFileName);
std::filesystem::path path (strFileName);
if (!path.is_absolute ()) path = std::filesystem::absolute (path);
// std::filesystem::path path (strFileName);
// if (!path.is_absolute ()) path = std::filesystem::absolute (path);
auto originalDataFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data" / "x64";
auto originalLayeredFsFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data_mods" / "x64";
// auto originalDataFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data" / "x64";
// auto originalLayeredFsFolder = std::filesystem::current_path ().parent_path ().parent_path () / "Data_mods" / "x64";
if (path.string ().find (originalDataFolder.string ()) != std::string::npos) {
auto newPath = path.string ();
newPath.replace (0, originalDataFolder.string ().length (), originalLayeredFsFolder.string ());
// if (path.string ().find (originalDataFolder.string ()) != std::string::npos) {
// auto newPath = path.string ();
// newPath.replace (0, originalDataFolder.string ().length (), originalLayeredFsFolder.string ());
if (std::filesystem::exists (newPath)) {
std::wstring wNewPath = converter.from_bytes (newPath);
std::wcout << L"Redirecting " << lpFileName << L" to " << wNewPath << std::endl;
return originalCreateFileWHook (wNewPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
dwFlagsAndAttributes, hTemplateFile);
}
}
// if (std::filesystem::exists (newPath)) {
// std::wstring wNewPath = converter.from_bytes (newPath);
// std::wcout << L"Redirecting " << lpFileName << L" to " << wNewPath << std::endl;
// return originalCreateFileWHook (wNewPath.c_str (), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition,
// dwFlagsAndAttributes, hTemplateFile);
// }
// }
return originalCreateFileWHook (lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
hTemplateFile);
}
// return originalCreateFileWHook (lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
// hTemplateFile);
// }
void
GetGameVersion () {
@ -184,7 +394,11 @@ DllMain (HMODULE module, DWORD reason, LPVOID reserved) {
}
auto layeredFs = openConfigSection (config, "layeredfs");
if (layeredFs) useLayeredFs = readConfigBool (layeredFs, "enabled", useLayeredFs);
if (layeredFs) {
useLayeredFs = readConfigBool (layeredFs, "enabled", useLayeredFs);
datatableKey = readConfigString (layeredFs, "datatable_key", datatableKey);
fumenKey = readConfigString (layeredFs, "fumen_key", fumenKey);
}
}
if (version == "auto") {
@ -243,8 +457,9 @@ DllMain (HMODULE module, DWORD reason, LPVOID reserved) {
if (useLayeredFs) {
std::wcout << "Using LayeredFs!" << std::endl;
register_cipher(&aes_desc);
INSTALL_HOOK (CreateFileAHook);
INSTALL_HOOK (CreateFileWHook);
// INSTALL_HOOK (CreateFileWHook);
}
bnusio::Init ();

View File

@ -1,3 +1,4 @@
#include <winsock2.h>
#include <MinHook.h>
#include <bits/stdc++.h>
#include <format>
@ -8,7 +9,6 @@
#include <stdlib.h>
#include <toml.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
/*

View File

@ -0,0 +1,12 @@
[wrap-file]
directory = libtomcrypt-1.18.2
source_url = https://github.com/libtom/libtomcrypt/releases/download/v1.18.2/crypt-1.18.2.tar.xz
source_filename = crypt-1.18.2.tar.xz
source_hash = 96ad4c3b8336050993c5bc2cf6c057484f2b0f9f763448151567fbab5e767b84
patch_filename = libtomcrypt_1.18.2-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/libtomcrypt_1.18.2-1/get_patch
patch_hash = 0ca889b864e5a23525774438439d9e385268ff7234d49029902375a6c5e18f14
[provide]
libtomcrypt = tomcrypt_dep

View File

@ -0,0 +1,33 @@
+++ zlib/meson.build
@@ -0,0 +1,31 @@
+project('zlib', 'c', version: '1.0.0')
+
+zlib_inc = include_directories('.')
+zlib_sources = files(
+ 'adler32.c',
+ 'compress.c',
+ 'crc32.c',
+ 'deflate.c',
+ 'gzclose.c',
+ 'gzlib.c',
+ 'gzread.c',
+ 'gzwrite.c',
+ 'infback.c',
+ 'inffast.c',
+ 'inflate.c',
+ 'inftrees.c',
+ 'trees.c',
+ 'uncompr.c',
+ 'zutil.c'
+)
+
+zlib_lib = static_library(
+ 'zlib',
+ zlib_sources,
+ include_directories: zlib_inc
+)
+
+zlib_dep = declare_dependency(
+ include_directories: zlib_inc,
+ link_with: zlib_lib
+)

4
subprojects/zlib.wrap Executable file
View File

@ -0,0 +1,4 @@
[wrap-git]
url = https://github.com/madler/zlib
revision = 51b7f2abdade71cd9bb0e7a373ef2610ec6f9daf
diff_files = zlib.patch