diff --git a/src/EA/IO/FileStream.cpp b/src/EA/IO/FileStream.cpp index 3978342..91e0c21 100644 --- a/src/EA/IO/FileStream.cpp +++ b/src/EA/IO/FileStream.cpp @@ -4,17 +4,105 @@ #include "FileStream.h" +#include "../../core/system/Logger.h" + namespace EA::IO { - FileStream::FileStream(const std::filesystem::path &path): path(path) { + + FileError MapWindowsErrorToFileError(DWORD errorCode) { + switch (errorCode) { + case ERROR_SUCCESS: + return FileError::Success; + case ERROR_INVALID_HANDLE: + return FileError::InvalidHandle; + case ERROR_OUTOFMEMORY: + case ERROR_NOT_ENOUGH_MEMORY: + return FileError::OutOfMemory; + case ERROR_FILE_NOT_FOUND: + return FileError::FileNotFound; + case ERROR_PATH_NOT_FOUND: + case ERROR_INVALID_DRIVE: + return FileError::PathNotFound; + case ERROR_ACCESS_DENIED: + return FileError::AccessDenied; + case ERROR_WRITE_PROTECT: + return FileError::WriteProtect; + case ERROR_CURRENT_DIRECTORY: + return FileError::CurrentDirectory; + case ERROR_NOT_READY: + return FileError::NotReady; + case ERROR_CRC: + return FileError::CRC; + default: + return FileError::Other; + } + } + + FileStream::FileStream(const std::filesystem::path &path, const AccessFlags access_flags, const CD cd): path(path), cd(cd), accessFlags(access_flags) { + DWORD wCd = 0; + DWORD wAccess = 0; + DWORD wFileShare = 0; + + switch (cd) { + case CD::CreateNew: + wCd = CREATE_NEW; + break; + case CD::CreateAlways: + wCd = CREATE_ALWAYS; + break; + case CD::OpenExisting: + wCd = OPEN_EXISTING; + break; + case CD::OpenAlways: + wCd = OPEN_ALWAYS; + break; + case CD::TruncateExisting: + wCd = TRUNCATE_EXISTING; + break; + default: + case CD::LoadAllFiles: + case CD::Default: + wCd = OPEN_EXISTING; + break; + } + + switch (access_flags) { + case AccessFlags::Read: + wAccess = GENERIC_READ; + wFileShare = FILE_SHARE_READ; + break; + case AccessFlags::Write: + wAccess = GENERIC_WRITE; + wFileShare = FILE_SHARE_WRITE; + break; + case AccessFlags::ReadWrite: + wAccess = GENERIC_READ | GENERIC_WRITE; + wFileShare = FILE_SHARE_READ | FILE_SHARE_WRITE; + break; + default: + case AccessFlags::None: + wAccess = GENERIC_READ; + wFileShare = FILE_SHARE_READ; + break; + } + hFile = CreateFileW( path.c_str(), - GENERIC_READ, - FILE_SHARE_READ, + wAccess, + wFileShare, nullptr, - OPEN_EXISTING, + wCd, FILE_ATTRIBUTE_NORMAL, nullptr ); + + fileError = MapWindowsErrorToFileError(GetLastError()); + + if (hFile == INVALID_HANDLE_VALUE) { + MSML_LOG_ERROR("Failed to open file %s", path.string().c_str()); + } + } + + FileStream::FileStream(const std::filesystem::path &path): FileStream(path, AccessFlags::Read, CD::Default) { } uint32_t FileStream::GetType() const { @@ -22,11 +110,11 @@ namespace EA::IO { } AccessFlags FileStream::GetAccessFlags() const { - return (hFile != INVALID_HANDLE_VALUE) ? AccessFlags::Read : AccessFlags::None; + return (hFile != INVALID_HANDLE_VALUE) ? accessFlags : AccessFlags::None; } FileError FileStream::GetState() const { - return (hFile != INVALID_HANDLE_VALUE) ? FileError::Success : FileError::InvalidHandle; + return fileError; } bool FileStream::Close() { @@ -100,6 +188,8 @@ namespace EA::IO { if (ReadFile(hFile, pData, static_cast(nSize), &bytesRead, nullptr)) return bytesRead; + fileError = MapWindowsErrorToFileError(GetLastError()); + return -1; } @@ -109,7 +199,15 @@ namespace EA::IO { } size_t FileStream::Write(const void *pData, size_t nSize) { - return 0; + if (hFile == INVALID_HANDLE_VALUE || pData == nullptr) return -1; + + DWORD bytesWritten = 0; + if (WriteFile(hFile, pData, static_cast(nSize), &bytesWritten, nullptr)) + return bytesWritten; + + fileError = MapWindowsErrorToFileError(GetLastError()); + + return -1; } std::filesystem::path FileStream::GetPath() { diff --git a/src/EA/IO/FileStream.h b/src/EA/IO/FileStream.h index f60a3a4..1444d54 100644 --- a/src/EA/IO/FileStream.h +++ b/src/EA/IO/FileStream.h @@ -16,9 +16,13 @@ namespace EA::IO { class FileStream final : public IStream { std::filesystem::path path; HANDLE hFile = INVALID_HANDLE_VALUE; + CD cd = CD::Default; + AccessFlags accessFlags = AccessFlags::None; + FileError fileError = FileError::Success; public: static constexpr uint32_t Type = 0xb7faadfe; + FileStream(const std::filesystem::path &path, AccessFlags access_flags, CD cd); explicit FileStream(const std::filesystem::path &path); [[nodiscard]] uint32_t GetType() const override; diff --git a/src/EA/IO/IStream.h b/src/EA/IO/IStream.h index 835287e..8ce37e6 100644 --- a/src/EA/IO/IStream.h +++ b/src/EA/IO/IStream.h @@ -145,6 +145,24 @@ namespace EA::IO { stream_name->Write((str), sizeof(char) * strlen(str)); \ } while (0) +#define WRITE_LEN_STRING(stream_name, str) \ + do { \ + WRITE_VALUE(stream_name, uint32_t, sizeof(char) * strlen(str)); \ + stream_name->Write((str), sizeof(char) * strlen(str)); \ + } while (0) + +#define READ_LEN_STRING(stream_name, out_str) \ + do { \ + uint32_t __len = 0; \ + stream_name->Read(&__len, sizeof(uint32_t)); \ + if (__len > 0) { \ + (out_str).resize(__len); \ + stream_name->Read(&(out_str)[0], __len); \ + } else { \ + (out_str).clear(); \ + } \ + } while (0) + #define READ_CSTRING(stream_name, str) \ do { \ std::vector _tmp_str; \ diff --git a/src/core/hooks/Hooks.cpp b/src/core/hooks/Hooks.cpp index 92111fd..4c1d248 100644 --- a/src/core/hooks/Hooks.cpp +++ b/src/core/hooks/Hooks.cpp @@ -4,16 +4,19 @@ #include "Hooks.h" #include "MinHook.h" +#include "../modloader/ModLoader.h" #include "../system/Logger.h" namespace msml::core::Hooks { void Install(void* target, void *detour, void** original) { if (target == nullptr) { MSML_LOG_ERROR("Failed to install hook"); + ModLoader::SoftCrash(); } if (MH_CreateHook(target, detour, original) != MH_OK) { MSML_LOG_ERROR("Failed to install hook"); + ModLoader::SoftCrash(); } } diff --git a/src/core/modloader/ModLoader.cpp b/src/core/modloader/ModLoader.cpp index 90373b9..fb22c3c 100644 --- a/src/core/modloader/ModLoader.cpp +++ b/src/core/modloader/ModLoader.cpp @@ -68,7 +68,7 @@ namespace msml::core { void ModLoader::SoftCrash() { MessageBox(nullptr, "The application is going to crash, check the logs.", "ModLoader::SoftCrash", 1); - exit(0); + exit(1); } ModLoader::ModLoader() { diff --git a/src/core/signatures/Signatures.cpp b/src/core/signatures/Signatures.cpp index 7f60e5e..c5ba628 100644 --- a/src/core/signatures/Signatures.cpp +++ b/src/core/signatures/Signatures.cpp @@ -8,12 +8,17 @@ #include "windows.h" #include "sigdef.h" +#include "../../Version.h" #include "../../EA/IO/FileStream.h" +#include "../../EA/IO/MemoryStream.h" #include "../../include/hash_sha256.h" #include "../system/Logger.h" #include "../util/StreamUtil.h" namespace msml::core { + + static constexpr uint32_t SIGNATURE_VERSION = 0; + Signatures & Signatures::GetInstance() { static Signatures signatures; return signatures; @@ -48,7 +53,7 @@ namespace msml::core { return false; } - void Signatures::Append(std::string name, SigSearchBase* sig){ + void Signatures::Append(const std::string& name, SigSearchBase* sig){ signatures.emplace(name, sig); } @@ -72,16 +77,16 @@ namespace msml::core { bool Signatures::SearchAll() { const auto start = std::chrono::high_resolution_clock::now(); - if (!LoadDatabase()) { - for(const auto &val: signatures | std::views::values) { - if (!val->Search()) { - return false; - } + LoadDatabase(); + + for(const auto &val: signatures | std::views::values) { + if (val->GetAddress() == nullptr && !val->Search()) { + return false; } } - auto elapsed = std::chrono::high_resolution_clock::now() - start; - double elapsed_ms = std::chrono::duration_cast(elapsed).count() / 1000.0; + const auto elapsed = std::chrono::high_resolution_clock::now() - start; + const double elapsed_ms = std::chrono::duration_cast(elapsed).count() / 1000.0; MSML_LOG_INFO("Search time: %.3f milliseconds", elapsed_ms); SaveDatabase(); @@ -94,83 +99,78 @@ namespace msml::core { context = target.in_module("MySims.exe"); } -#define READ_BIN(var) infile.read((char*)&var, sizeof(var)); -#define WRITE_BIN(var) outfile.write((char*)&var, sizeof(var)) - -#define SIGCACHE_DB_NAME "sigcache.db" - bool Signatures::LoadDatabase() { #ifndef NDEBUG return false; #endif + const auto stream = new EA::IO::FileStream("signatures.db"); + + uint32_t sig_version = UINT32_MAX; + READ(stream, sig_version); - std::ifstream infile(SIGCACHE_DB_NAME, std::ios::in | std::ios::binary); - if (!infile.good()) { - MSML_LOG_ERROR("Failed to open sigcache"); + if (sig_version != SIGNATURE_VERSION) { return false; } + std::string msml_version; + READ_LEN_STRING(stream, msml_version); + std::array checksum{}; for (auto i = 0; i < 32U; i++) { - READ_BIN(checksum[i]); + READ(stream, checksum[i]); } - if (std::array currentChecksum = GetCheckSum(); checksum != currentChecksum) { + if (const std::array currentChecksum = GetCheckSum(); checksum != currentChecksum) { MSML_LOG_ERROR("Checksum mismatch, searching for new signatures"); return false; } uint32_t sigCount; - READ_BIN(sigCount); - for (uint32_t i = 0; i < sigCount; i++) { - uint32_t length; - READ_BIN(length); - - char name[1024]; - infile.read(name, length); + READ(stream, sigCount); - auto name_str = std::string(name, length); + for (int i =0; i < sigCount; i++) { + std::string sig_name; + READ_LEN_STRING(stream, sig_name); + uint64_t address; + READ(stream, address); - uintptr_t addr; - READ_BIN(addr); - - if (signatures.contains(name_str)) { - signatures[name_str]->ApplyAddress(reinterpret_cast(addr)); + if (signatures.contains(sig_name)) { + signatures[sig_name]->ApplyAddress(reinterpret_cast(address)); } else { - MSML_LOG_ERROR("Failed to find signatures with name %s", name_str); + MSML_LOG_ERROR("Failed to find signatures with name %s", sig_name.c_str()); + return false; } } + MSML_LOG_INFO("Read %d signatures from signatures.db", sigCount); + + stream->Close(); + return true; } - void Signatures::SaveDatabase() { + void Signatures::SaveDatabase() const { #ifndef NDEBUG return; #endif + const auto stream = new EA::IO::FileStream("signatures.db", EA::IO::AccessFlags::Write, EA::IO::CD::CreateAlways); - std::ofstream outfile(SIGCACHE_DB_NAME, std::ios::out | std::ios::binary); - - std::array checksum = GetCheckSum(); - size_t count = signatures.size(); + WRITE(stream, SIGNATURE_VERSION); + WRITE_LEN_STRING(stream, MSML_VERSION); + const std::array checksum = GetCheckSum(); for (auto i = 0; i < 32U; i++) { - WRITE_BIN(checksum[i]); + WRITE(stream, checksum[i]); } - WRITE_BIN(count); - - for (const auto &sig: signatures) { - std::string name = sig.first; - size_t length = name.length(); - auto addr = reinterpret_cast(sig.second->GetAddress()); - WRITE_BIN(length); - outfile.write(name.c_str(), length); - WRITE_BIN(addr); + WRITE_VALUE(stream, uint32_t, signatures.size()); + for (const auto& signature : signatures) { + WRITE_LEN_STRING(stream, signature.first.c_str()); + WRITE_VALUE(stream, uint64_t, reinterpret_cast(signature.second->GetAddress())); } MSML_LOG_INFO("Signature cache saved"); - outfile.close(); + stream->Close(); } } diff --git a/src/core/signatures/Signatures.h b/src/core/signatures/Signatures.h index 5d80ffe..173eaf8 100644 --- a/src/core/signatures/Signatures.h +++ b/src/core/signatures/Signatures.h @@ -28,8 +28,8 @@ namespace msml::core { static Signatures& GetInstance(); bool Search(const sigmatch::signature &sig, void*& address, uint32_t offset, bool first) const; bool SearchAll(); - void Append(std::string name, SigSearchBase* sig); - std::array GetCheckSum(); + void Append(const std::string& name, SigSearchBase* sig); + static std::array GetCheckSum(); private: Signatures(); @@ -37,7 +37,7 @@ namespace msml::core { sigmatch::search_context context; bool LoadDatabase(); - void SaveDatabase(); + void SaveDatabase() const; }; }