From d82a679bd6e3bdcdc3ecd6c6a229560aaf8a28d1 Mon Sep 17 00:00:00 2001 From: Tenshi Date: Sat, 11 Nov 2023 18:34:14 +0100 Subject: [PATCH] Add mono module --- src/CMakeLists.txt | 2 + src/injector/Assembly.cpp | 140 ++++++++++++++++ src/injector/Assembly.hpp | 40 +++++ src/injector/CMakeLists.txt | 14 ++ src/injector/MonoModule.cpp | 264 ++++++++++++++++++++++++++++++ src/injector/MonoModule.hpp | 53 ++++++ src/system/CMakeLists.txt | 14 ++ src/system/ExternalProcAdress.cpp | 199 ++++++++++++++++++++++ src/system/ExternalProcAdress.hpp | 44 +++++ src/system/System.cpp | 152 +++++++++++++++++ src/system/System.hpp | 71 ++++++++ 11 files changed, 993 insertions(+) create mode 100644 src/injector/Assembly.cpp create mode 100644 src/injector/Assembly.hpp create mode 100644 src/injector/CMakeLists.txt create mode 100644 src/injector/MonoModule.cpp create mode 100644 src/injector/MonoModule.hpp create mode 100644 src/system/CMakeLists.txt create mode 100644 src/system/ExternalProcAdress.cpp create mode 100644 src/system/ExternalProcAdress.hpp create mode 100644 src/system/System.cpp create mode 100644 src/system/System.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1dad722..2d49940 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,3 +13,5 @@ target_sources( ) add_subdirectory(GUI) +add_subdirectory(injector) +add_subdirectory(system) diff --git a/src/injector/Assembly.cpp b/src/injector/Assembly.cpp new file mode 100644 index 0000000..2665809 --- /dev/null +++ b/src/injector/Assembly.cpp @@ -0,0 +1,140 @@ +#include "Assembly.hpp" + +namespace Xash +{ + namespace Injector + { + void Assembly::ClearInstructions() + { + _asmInstructions.clear(); + } + + const std::vector &Assembly::GetAssembly() const + { + return _asmInstructions; + } + + void Assembly::Ret() + { + _asmInstructions.push_back(0xC3); + } + + void Assembly::CallRax() + { + _asmInstructions.push_back(0xFF); + _asmInstructions.push_back(0xD0); + } + + void Assembly::SubRsp(uint8_t value) + { + _asmInstructions.push_back(0x48); + _asmInstructions.push_back(0x83); + _asmInstructions.push_back(0xEC); + _asmInstructions.push_back(value); + } + + void Assembly::AddRsp(uint8_t value) + { + _asmInstructions.push_back(0x48); + _asmInstructions.push_back(0x83); + _asmInstructions.push_back(0xC4); + _asmInstructions.push_back(value); + } + + void Assembly::MoveIntoRax(void *value) + { + _asmInstructions.push_back(0x48); + _asmInstructions.push_back(0xB8); + _asmInstructions.insert( + _asmInstructions.end(), (uint8_t *)&value, (uint8_t *)&value + sizeof(void *) + ); + } + + void Assembly::MoveRaxIntoValue(void *value) + { + _asmInstructions.push_back(0x48); + _asmInstructions.push_back(0xA3); + _asmInstructions.insert( + _asmInstructions.end(), (uint8_t *)&value, (uint8_t *)&value + sizeof(void *) + ); + } + + void Assembly::XorRaxRax() + { + _asmInstructions.push_back(0x48); + _asmInstructions.push_back(0x31); + _asmInstructions.push_back(0xC0); + } + + void Assembly::XorRcxRcx() + { + _asmInstructions.push_back(0x48); + _asmInstructions.push_back(0x31); + _asmInstructions.push_back(0xC9); + } + + void Assembly::XorRdxRdx() + { + _asmInstructions.push_back(0x48); + _asmInstructions.push_back(0x31); + _asmInstructions.push_back(0xD2); + } + + void Assembly::XorR8R8() + { + _asmInstructions.push_back(0x4D); + _asmInstructions.push_back(0x31); + _asmInstructions.push_back(0xC0); + } + + void Assembly::XorR9R9() + { + _asmInstructions.push_back(0x4D); + _asmInstructions.push_back(0x31); + _asmInstructions.push_back(0xC9); + } + + void Assembly::MoveIntoRcx(void *value) + { + _asmInstructions.push_back(0x48); + _asmInstructions.push_back(0xB9); + _asmInstructions.insert( + _asmInstructions.end(), (uint8_t *)&value, (uint8_t *)&value + sizeof(void *) + ); + } + + void Assembly::MoveIntoRdx(void *value) + { + _asmInstructions.push_back(0x48); + _asmInstructions.push_back(0xBA); + _asmInstructions.insert( + _asmInstructions.end(), (uint8_t *)&value, (uint8_t *)&value + sizeof(void *) + ); + } + + void Assembly::MoveIntoR8(void *value) + { + _asmInstructions.push_back(0x49); + _asmInstructions.push_back(0xB8); + _asmInstructions.insert( + _asmInstructions.end(), (uint8_t *)&value, (uint8_t *)&value + sizeof(void *) + ); + } + + void Assembly::MoveIntoR9(void *value) + { + _asmInstructions.push_back(0x49); + _asmInstructions.push_back(0xB9); + _asmInstructions.insert( + _asmInstructions.end(), (uint8_t *)&value, (uint8_t *)&value + sizeof(void *) + ); + } + + void Assembly::MonoThreadAttach(void *monoAttachFunc, void *MonoDomain) + { + MoveIntoRax(monoAttachFunc); + MoveIntoRcx(MonoDomain); + CallRax(); + } + } // namespace Injector +} // namespace Xash diff --git a/src/injector/Assembly.hpp b/src/injector/Assembly.hpp new file mode 100644 index 0000000..4e27f50 --- /dev/null +++ b/src/injector/Assembly.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +namespace Xash +{ + namespace Injector + { + class Assembly + { + public: + void ClearInstructions(); + const std::vector &GetAssembly() const; + + void Ret(); + void CallRax(); + + void SubRsp(uint8_t value); + void AddRsp(uint8_t value); + + void MoveIntoRax(void *value); + void MoveIntoRcx(void *value); + void MoveIntoRdx(void *value); + void MoveIntoR8(void *value); + void MoveIntoR9(void *value); + void MoveRaxIntoValue(void *value); + + void XorRaxRax(); + void XorRcxRcx(); + void XorRdxRdx(); + void XorR8R8(); + void XorR9R9(); + + void MonoThreadAttach(void *monoAttachFunc, void *MonoDomain); + + private: + std::vector _asmInstructions; + }; + } // namespace Injector +} // namespace Xash diff --git a/src/injector/CMakeLists.txt b/src/injector/CMakeLists.txt new file mode 100644 index 0000000..dcc8c14 --- /dev/null +++ b/src/injector/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.27) + +target_include_directories( + ${PROJECT_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_sources( + ${PROJECT_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/MonoModule.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Assembly.cpp +) diff --git a/src/injector/MonoModule.cpp b/src/injector/MonoModule.cpp new file mode 100644 index 0000000..1d76782 --- /dev/null +++ b/src/injector/MonoModule.cpp @@ -0,0 +1,264 @@ +#include +#include "System.hpp" +#include "MonoModule.hpp" +#include "Assembly.hpp" +#include "ExternalProcAdress.hpp" + +namespace Xash::Injector +{ + MonoModule::MonoModule(HANDLE hProcess) : _hProcess(hProcess) + { + FindMonoModule(); + _monoFunctions = { + {"mono_get_root_domain", nullptr}, + {"mono_thread_attach", nullptr}, + {"mono_assembly_get_image", nullptr}, + {"mono_class_from_name", nullptr}, + {"mono_class_get_method_from_name", nullptr}, + {"mono_runtime_invoke", nullptr}, + {"mono_assembly_close", nullptr}, + {"mono_assembly_open", nullptr} + }; + FindMonoFunctions(); + } + + void MonoModule::FindMonoModule() + { + const std::wstring _monoModuleKeyword = L"MonoBleedingEdge"; + + _monoModule = System::GetModuleByKeyword(_hProcess, _monoModuleKeyword); + if (_monoModule == nullptr) + { + throw std::runtime_error("Error: mono module not found in process"); + } + } + + void MonoModule::FindMonoFunctions() + { + for (auto &[name, address] : _monoFunctions) + { + address = System::GetExternalProcAddress(_hProcess, _monoModule, name); + if (address == nullptr) + { + throw std::runtime_error("Error: " + name + " not found in mono module"); + } + } + } + + bool MonoModule::LoadMod() + { + GetMonoDomain(); + GetMonoAssembly(); + GetMonoImage(); + GetMonoClass(); + GetMonoInitMethod(); + MonoRuntimeInvoke(_monoInitMethod); + return true; + } + + bool MonoModule::UnLoadMod() + { + GetMonoUnLoadMethod(); + MonoRuntimeInvoke(_monoUnloadMethod); + return true; + } + + void MonoModule::GetMonoDomain() + { + std::vector payload; + Assembly assembly; + + LPVOID returnValue = System::AllocateMemoryInProcess(_hProcess, sizeof(LPVOID)); + assembly.MoveIntoRax(_monoFunctions["mono_get_root_domain"]); + assembly.CallRax(); + assembly.MoveRaxIntoValue(returnValue); + assembly.Ret(); + payload = assembly.GetAssembly(); + + LPVOID payloadAdress = System::AllocateAndWriteMemoryInProcess( + _hProcess, payload.data(), payload.size() + ); + + System::RunThreadInProcess(_hProcess, payloadAdress); + + _monoRootDomain = + System::ReadMemoryInProcess(_hProcess, returnValue, sizeof(LPVOID)); + } + + void MonoModule::GetMonoAssembly() + { + std::vector payload; + Assembly assembly; + + LPVOID returnValue = System::AllocateMemoryInProcess(_hProcess, sizeof(LPVOID)); + + LPVOID modAdress = System::AllocateAndWriteMemoryInProcess( + _hProcess, _modPath.c_str(), _modPath.size() + 1 + ); + + assembly.SubRsp(0x18); + assembly.MonoThreadAttach(_monoFunctions["mono_thread_attach"], _monoRootDomain); + assembly.MoveIntoRax(_monoFunctions["mono_assembly_open"]); + assembly.MoveIntoRcx(modAdress); + assembly.XorRdxRdx(); + assembly.CallRax(); + assembly.MoveRaxIntoValue(returnValue); + assembly.AddRsp(0x18); + assembly.Ret(); + payload = assembly.GetAssembly(); + + LPVOID payloadAdress = System::AllocateAndWriteMemoryInProcess( + _hProcess, payload.data(), payload.size() + ); + + System::RunThreadInProcess(_hProcess, payloadAdress); + _monoAssembly = + System::ReadMemoryInProcess(_hProcess, returnValue, sizeof(LPVOID)); + } + + void MonoModule::GetMonoImage() + { + std::vector payload; + Assembly assembly; + + LPVOID returnValue = System::AllocateMemoryInProcess(_hProcess, sizeof(LPVOID)); + assembly.SubRsp(0x18); + assembly.MonoThreadAttach(_monoFunctions["mono_thread_attach"], _monoRootDomain); + assembly.MoveIntoRax(_monoFunctions["mono_assembly_get_image"]); + assembly.MoveIntoRcx(_monoAssembly); + assembly.CallRax(); + assembly.AddRsp(0x18); + assembly.MoveRaxIntoValue(returnValue); + assembly.Ret(); + payload = assembly.GetAssembly(); + + LPVOID payloadAdress = System::AllocateAndWriteMemoryInProcess( + _hProcess, payload.data(), payload.size() + ); + + System::RunThreadInProcess(_hProcess, payloadAdress); + _monoImage = + System::ReadMemoryInProcess(_hProcess, returnValue, sizeof(LPVOID)); + } + + void MonoModule::GetMonoClass() + { + std::vector payload; + Assembly assembly; + + LPVOID namespaceAdress = System::AllocateAndWriteMemoryInProcess( + _hProcess, _modNamespace.c_str(), _modNamespace.size() + 1 + ); + LPVOID classNameAdress = System::AllocateAndWriteMemoryInProcess( + _hProcess, _modClass.c_str(), _modClass.size() + 1 + ); + + LPVOID returnValue = System::AllocateMemoryInProcess(_hProcess, sizeof(LPVOID)); + assembly.SubRsp(0x18); + assembly.MonoThreadAttach(_monoFunctions["mono_thread_attach"], _monoRootDomain); + assembly.MoveIntoRax(_monoFunctions["mono_class_from_name"]); + assembly.MoveIntoRcx(_monoImage); + assembly.MoveIntoRdx(namespaceAdress); + assembly.MoveIntoR8(classNameAdress); + assembly.CallRax(); + assembly.AddRsp(0x18); + assembly.MoveRaxIntoValue(returnValue); + assembly.Ret(); + payload = assembly.GetAssembly(); + + LPVOID payloadAdress = System::AllocateAndWriteMemoryInProcess( + _hProcess, payload.data(), payload.size() + ); + + System::RunThreadInProcess(_hProcess, payloadAdress); + _monoClass = + System::ReadMemoryInProcess(_hProcess, returnValue, sizeof(LPVOID)); + } + + void MonoModule::GetMonoInitMethod() + { + std::vector payload; + Assembly assembly; + + LPVOID methodNameAdress = System::AllocateAndWriteMemoryInProcess( + _hProcess, _modInitMethod.c_str(), _modInitMethod.size() + 1 + ); + + LPVOID returnValue = System::AllocateMemoryInProcess(_hProcess, sizeof(LPVOID)); + assembly.SubRsp(0x20); + assembly.MonoThreadAttach(_monoFunctions["mono_thread_attach"], _monoRootDomain); + assembly.MoveIntoRax(_monoFunctions["mono_class_get_method_from_name"]); + assembly.MoveIntoRcx(_monoClass); + assembly.MoveIntoRdx(methodNameAdress); + assembly.XorR8R8(); + assembly.CallRax(); + assembly.AddRsp(0x20); + assembly.MoveRaxIntoValue(returnValue); + assembly.Ret(); + payload = assembly.GetAssembly(); + + LPVOID payloadAdress = System::AllocateAndWriteMemoryInProcess( + _hProcess, payload.data(), payload.size() + ); + + System::RunThreadInProcess(_hProcess, payloadAdress); + _monoInitMethod = + System::ReadMemoryInProcess(_hProcess, returnValue, sizeof(LPVOID)); + } + + void MonoModule::MonoRuntimeInvoke(LPVOID method) + { + std::vector payload; + Assembly assembly; + + assembly.SubRsp(0x18); + assembly.MonoThreadAttach(_monoFunctions["mono_thread_attach"], _monoRootDomain); + assembly.MoveIntoRax(_monoFunctions["mono_runtime_invoke"]); + assembly.MoveIntoRcx(method); + assembly.XorRdxRdx(); + assembly.XorR8R8(); + assembly.XorR9R9(); + assembly.CallRax(); + assembly.AddRsp(0x18); + assembly.Ret(); + payload = assembly.GetAssembly(); + + LPVOID payloadAdress = System::AllocateAndWriteMemoryInProcess( + _hProcess, payload.data(), payload.size() + ); + + System::RunThreadInProcess(_hProcess, payloadAdress); + System::FreeMemoryInProcess(_hProcess, payloadAdress, payload.size()); + } + + void MonoModule::GetMonoUnLoadMethod() + { + std::vector payload; + Assembly assembly; + + LPVOID methodNameAdress = System::AllocateAndWriteMemoryInProcess( + _hProcess, _modUnloadMethod.c_str(), _modUnloadMethod.size() + 1 + ); + + LPVOID returnValue = System::AllocateMemoryInProcess(_hProcess, sizeof(LPVOID)); + assembly.SubRsp(0x20); + assembly.MonoThreadAttach(_monoFunctions["mono_thread_attach"], _monoRootDomain); + assembly.MoveIntoRax(_monoFunctions["mono_class_get_method_from_name"]); + assembly.MoveIntoRcx(_monoClass); + assembly.MoveIntoRdx(methodNameAdress); + assembly.XorR8R8(); + assembly.CallRax(); + assembly.AddRsp(0x20); + assembly.MoveRaxIntoValue(returnValue); + assembly.Ret(); + payload = assembly.GetAssembly(); + + LPVOID payloadAdress = System::AllocateAndWriteMemoryInProcess( + _hProcess, payload.data(), payload.size() + ); + + System::RunThreadInProcess(_hProcess, payloadAdress); + _monoUnloadMethod = + System::ReadMemoryInProcess(_hProcess, returnValue, sizeof(LPVOID)); + } +} // namespace Xash::Injector diff --git a/src/injector/MonoModule.hpp b/src/injector/MonoModule.hpp new file mode 100644 index 0000000..791bd43 --- /dev/null +++ b/src/injector/MonoModule.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include + +namespace Xash +{ + namespace Injector + { + class MonoModule + { + public: + MonoModule(HANDLE hProcess); + ~MonoModule() = default; + + bool LoadMod(); + bool UnLoadMod(); + + private: + void FindMonoFunctions(); + void FindMonoModule(); + + void GetMonoDomain(); + void GetMonoAssembly(); + void GetMonoImage(); + void GetMonoClass(); + void GetMonoInitMethod(); + void GetMonoUnLoadMethod(); + + void MonoRuntimeInvoke(LPVOID method); + + HANDLE _hProcess; + HMODULE _monoModule; + + LPVOID _monoRootDomain = nullptr; + LPVOID _monoAssembly = nullptr; + LPVOID _monoImage = nullptr; + LPVOID _monoClass = nullptr; + LPVOID _monoInitMethod = nullptr; + LPVOID _monoUnloadMethod = nullptr; + + std::string _modPath = ""; + std::string _modNamespace = ""; + std::string _modClass = ""; + std::string _modInitMethod = ""; + std::string _modUnloadMethod = ""; + std::string _targetedProcessName = ""; + + std::unordered_map _monoFunctions; + }; + } // namespace Injector +} // namespace Xash diff --git a/src/system/CMakeLists.txt b/src/system/CMakeLists.txt new file mode 100644 index 0000000..f587b86 --- /dev/null +++ b/src/system/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.27) + +target_include_directories( + ${PROJECT_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_sources( + ${PROJECT_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/System.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ExternalProcAdress.cpp +) diff --git a/src/system/ExternalProcAdress.cpp b/src/system/ExternalProcAdress.cpp new file mode 100644 index 0000000..966e8cb --- /dev/null +++ b/src/system/ExternalProcAdress.cpp @@ -0,0 +1,199 @@ +#include +#include "ExternalProcAdress.hpp" + +namespace Xash::System +{ + ExternalProcAdressHelper::ExternalProcAdressHelper(HANDLE hProcess, HMODULE hModule) + : _hProcess(hProcess), _hModule(hModule) + { + if (hProcess == nullptr || hModule == nullptr) + { + throw std::runtime_error("Error: hProcess or hModule is nullptr"); + } + FindBasicInfo(); + FindOptionalHeader(); + FindExportDataDirectory(); + GetExportTable(); + } + + LPVOID ExternalProcAdressHelper::GetExternalProcessAddress(const std::string &lpProcName) + { + std::string currentFuncName; + CHAR currentChar = ' '; + + for (DWORD i = 0; i < _exportTable.NumberOfNames; ++i) + { + currentChar = ' '; + currentFuncName.clear(); + for (UINT_PTR j = 0; currentChar != '\0' && j < MAX_PATH; ++j) + { + if (!ReadProcessMemory( + _hProcess, + (LPCVOID)(_moduleBaseAddress + _exportNameTable[i] + j), + ¤tChar, sizeof(currentChar), NULL + )) + { + throw std::runtime_error("ReadProcessMemory failed"); + } + currentFuncName.push_back(currentChar); + } + if (currentFuncName.find(lpProcName) != std::string::npos) + { + LPVOID TempReturn = + (LPVOID)(_moduleBaseAddress + + _exportFunctionTable[_exportOrdinalTable[i]]); + return TempReturn; + } + } + return nullptr; + } + + void ExternalProcAdressHelper::FindBasicInfo() + { + DWORD signature = 0; + MODULEINFO moduleInfo = {0}; + + if (!GetModuleInformation(_hProcess, _hModule, &moduleInfo, sizeof(moduleInfo))) + { + throw std::runtime_error("GetModuleInformation failed"); + } + _moduleBaseAddress = (UINT_PTR)moduleInfo.lpBaseOfDll; + if (!ReadProcessMemory( + _hProcess, (LPCVOID)_moduleBaseAddress, &_dosHeader, sizeof(_dosHeader), + NULL + ) + || _dosHeader.e_magic != IMAGE_DOS_SIGNATURE) + { + throw std::runtime_error("Error: DOS signature invalid"); + } + if (!ReadProcessMemory( + _hProcess, (LPCVOID)(_moduleBaseAddress + _dosHeader.e_lfanew), + &signature, sizeof(DWORD), NULL + ) + || signature != IMAGE_NT_SIGNATURE) + { + throw std::runtime_error("Error: Signature invalid"); + } + if (!ReadProcessMemory( + _hProcess, + (LPCVOID)(_moduleBaseAddress + _dosHeader.e_lfanew + sizeof(DWORD)), + &_fileHeader, sizeof(_fileHeader), NULL + )) + { + throw std::runtime_error("ReadProcessMemory failed"); + } + if (_fileHeader.SizeOfOptionalHeader == sizeof(_optHeader64)) + { + _is64bits = TRUE; + } + else if (_fileHeader.SizeOfOptionalHeader == sizeof(_optHeader32)) + { + _is64bits = FALSE; + } + else + { + throw std::runtime_error("Unknown module type"); + } + } + + void ExternalProcAdressHelper::FindOptionalHeader() + { + if (_is64bits) + { + if (!ReadProcessMemory( + _hProcess, + (LPCVOID)(_moduleBaseAddress + _dosHeader.e_lfanew + sizeof(DWORD) + + sizeof(IMAGE_FILE_HEADER)), + &_optHeader64, _fileHeader.SizeOfOptionalHeader, NULL + ) + || _optHeader64.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + throw std::runtime_error("ReadProcessMemory failed"); + } + } + else + { + if (!ReadProcessMemory( + _hProcess, + (LPCVOID)(_moduleBaseAddress + _dosHeader.e_lfanew + sizeof(DWORD) + + sizeof(IMAGE_FILE_HEADER)), + &_optHeader32, _fileHeader.SizeOfOptionalHeader, NULL + ) + || _optHeader32.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + throw std::runtime_error("ReadProcessMemory failed"); + } + } + } + + void ExternalProcAdressHelper::FindExportDataDirectory() + { + if (_is64bits + && _optHeader64.NumberOfRvaAndSizes >= IMAGE_DIRECTORY_ENTRY_EXPORT + 1) + { + _dataDirectory.VirtualAddress = + (_optHeader64.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]).VirtualAddress; + _dataDirectory.Size = + (_optHeader64.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]).Size; + } + else if (_optHeader32.NumberOfRvaAndSizes >= IMAGE_DIRECTORY_ENTRY_EXPORT + 1) + { + _dataDirectory.VirtualAddress = + (_optHeader32.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]).VirtualAddress; + _dataDirectory.Size = + (_optHeader32.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]).Size; + } + else + { + throw std::runtime_error("ReadProcessMemory failed"); + } + } + + void ExternalProcAdressHelper::GetExportTable() + { + UINT_PTR exportFunctionTable = 0; + UINT_PTR exportNameTable = 0; + UINT_PTR exportOrdinalTable = 0; + if (!ReadProcessMemory( + _hProcess, (LPCVOID)(_moduleBaseAddress + _dataDirectory.VirtualAddress), + &_exportTable, sizeof(_exportTable), NULL + )) + { + throw std::runtime_error("ReadProcessMemory failed"); + } + exportFunctionTable = _moduleBaseAddress + _exportTable.AddressOfFunctions; + exportNameTable = _moduleBaseAddress + _exportTable.AddressOfNames; + exportOrdinalTable = _moduleBaseAddress + _exportTable.AddressOfNameOrdinals; + _exportFunctionTable.resize(_exportTable.NumberOfFunctions); + _exportNameTable.resize(_exportTable.NumberOfNames); + _exportOrdinalTable.resize(_exportTable.NumberOfNames); + if (!ReadProcessMemory( + _hProcess, (LPCVOID)exportFunctionTable, _exportFunctionTable.data(), + _exportTable.NumberOfFunctions * sizeof(DWORD), NULL + )) + { + throw std::runtime_error("ReadProcessMemory failed"); + } + if (!ReadProcessMemory( + _hProcess, (LPCVOID)exportNameTable, _exportNameTable.data(), + _exportTable.NumberOfNames * sizeof(DWORD), NULL + )) + { + throw std::runtime_error("ReadProcessMemory failed"); + } + if (!ReadProcessMemory( + _hProcess, (LPCVOID)exportOrdinalTable, _exportOrdinalTable.data(), + _exportTable.NumberOfNames * sizeof(WORD), NULL + )) + { + throw std::runtime_error("ReadProcessMemory failed"); + } + } + + LPVOID GetExternalProcAddress(HANDLE hProcess, HMODULE hModule, const std::string &lpProcName) + { + ExternalProcAdressHelper helper(hProcess, hModule); + return helper.GetExternalProcessAddress(lpProcName); + } + +} // namespace Xash::System diff --git a/src/system/ExternalProcAdress.hpp b/src/system/ExternalProcAdress.hpp new file mode 100644 index 0000000..b92bd64 --- /dev/null +++ b/src/system/ExternalProcAdress.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include +#include +#include +#include + +namespace Xash +{ + namespace System + { + // Helper class for getExternalProcAddress + class ExternalProcAdressHelper + { + public: + ExternalProcAdressHelper(HANDLE hProcess, HMODULE hModule); + ~ExternalProcAdressHelper() = default; + LPVOID GetExternalProcessAddress(const std::string &lpProcName); + + private: + void FindBasicInfo(); + void GetExportTable(); + void FindOptionalHeader(); + void FindExportDataDirectory(); + HANDLE _hProcess = nullptr; + HMODULE _hModule = nullptr; + bool _is64bits = FALSE; + UINT_PTR _moduleBaseAddress = 0; + IMAGE_DOS_HEADER _dosHeader = {0}; + IMAGE_FILE_HEADER _fileHeader = {0}; + std::vector _exportFunctionTable; + std::vector _exportNameTable; + std::vector _exportOrdinalTable; + IMAGE_OPTIONAL_HEADER64 _optHeader64 = {0}; + IMAGE_OPTIONAL_HEADER32 _optHeader32 = {0}; + IMAGE_DATA_DIRECTORY _dataDirectory = {0}; + IMAGE_EXPORT_DIRECTORY _exportTable = {0}; + }; + + LPVOID GetExternalProcAddress( + HANDLE hProcess, HMODULE hModule, const std::string &lpProcName + ); + } // namespace System +} // namespace Xash diff --git a/src/system/System.cpp b/src/system/System.cpp new file mode 100644 index 0000000..cb1d50a --- /dev/null +++ b/src/system/System.cpp @@ -0,0 +1,152 @@ +#include +#include "System.hpp" + +HMODULE Xash::System::GetModuleByKeyword(HANDLE hProcess, const std::wstring &keyword) +{ + DWORD lpcbNeeded; + HMODULE hMods[1024]; + std::wstring modName; + + if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &lpcbNeeded)) + { + for (int i = 0; i < (lpcbNeeded / sizeof(HMODULE)); i++) + { + WCHAR szModName[MAX_PATH]; + if (GetModuleFileNameExW( + hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(WCHAR) + )) + { + modName = szModName; + if (modName.find(keyword) != std::string::npos) + { + return hMods[i]; + } + } + } + } + return nullptr; +} + +DWORD Xash::System::GetProcessIdByName(const std::wstring &processName) +{ + DWORD processId = 0; + PROCESSENTRY32W tEntry = {0}; + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapshot == INVALID_HANDLE_VALUE) + { + throw std::runtime_error("Failed to create snapshot"); + } + tEntry.dwSize = sizeof(PROCESSENTRY32W); + for (BOOL success = Process32FirstW(hSnapshot, &tEntry); success == TRUE; + success = Process32NextW(hSnapshot, &tEntry)) + { + if (processName.compare(tEntry.szExeFile) == 0) + { + processId = tEntry.th32ProcessID; + break; + } + } + CloseHandle(hSnapshot); + return processId; +} + +HANDLE Xash::System::GetProcessHandleById(DWORD processId) +{ + HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId); + if (processHandle == NULL) + { + throw std::runtime_error("Failed to open process handle"); + } + return processHandle; +} + +LPVOID Xash::System::AllocateMemoryInProcess(HANDLE hProcess, SIZE_T size) +{ + LPVOID lpAddress = nullptr; + + lpAddress = VirtualAllocEx( + hProcess, nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE + ); + return lpAddress; +} + +BOOL Xash::System::FreeMemoryInProcess(HANDLE hProcess, LPVOID lpAddress, SIZE_T size) +{ + return VirtualFreeEx(hProcess, lpAddress, size, MEM_RELEASE); +} + +void Xash::System::RunThreadInProcess( + HANDLE hProcess, LPVOID lpStartAddress, LPVOID lpParameter, SIZE_T dwStackSize, + DWORD dwCreationFlags +) +{ + HANDLE hThread = CreateRemoteThread( + hProcess, nullptr, dwStackSize, (LPTHREAD_START_ROUTINE)lpStartAddress, + lpParameter, dwCreationFlags, nullptr + ); + WaitForSingleObject(hThread, INFINITE); + CloseHandle(hThread); +} + +std::vector Xash::System::GetRunningProcessesId() +{ + PROCESSENTRY32 pe32 = {0}; + std::vector processesID; + HANDLE hProcessSnap = nullptr; + + hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hProcessSnap == INVALID_HANDLE_VALUE) + { + throw std::runtime_error("Error Toolhelp32Snapshot : INVALID_HANDLE_VALUE"); + } + + pe32.dwSize = sizeof(PROCESSENTRY32); + BOOL success = Process32First(hProcessSnap, &pe32); + while (success == TRUE) + { + processesID.push_back(pe32.th32ProcessID); + success = Process32Next(hProcessSnap, &pe32); + } + + CloseHandle(hProcessSnap); + return processesID; +} + +std::string Xash::System::GetProcessNameByID(DWORD processId) +{ + std::string processName; + + HANDLE handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processId); + if (handle != nullptr) + { + char buffer[MAX_PATH]; + DWORD size = sizeof(buffer); + if (QueryFullProcessImageNameA(handle, 0, buffer, &size)) + { + processName = buffer; + } + CloseHandle(handle); + } + return processName; +} + +std::vector Xash::System::GetRunningProcessesNames() +{ + std::vector processesNames; + std::vector processesID = GetRunningProcessesId(); + + for (const auto &processID : processesID) + { + std::string processName = GetProcessNameByID(processID); + size_t lastSlash = processName.find_last_of("\\"); + if (lastSlash != std::string::npos) + { + processName = processName.substr(lastSlash + 1); + } + if (!processName.empty()) + { + processesNames.push_back(processName); + } + } + return processesNames; +} diff --git a/src/system/System.hpp b/src/system/System.hpp new file mode 100644 index 0000000..adf57b1 --- /dev/null +++ b/src/system/System.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace Xash +{ + namespace System + { + HMODULE GetModuleByKeyword(HANDLE hProcess, const std::wstring &keyword); + + DWORD GetProcessIdByName(const std::wstring &processName); + + HANDLE GetProcessHandleById(DWORD processId); + + LPVOID AllocateMemoryInProcess(HANDLE hProcess, SIZE_T size); + + BOOL FreeMemoryInProcess(HANDLE hProcess, LPVOID lpAddress, SIZE_T size); + + std::vector GetRunningProcessesId(); + + std::vector GetRunningProcessesNames(); + + std::string GetProcessNameByID(DWORD processId); + + void RunThreadInProcess( + HANDLE hProcess, LPVOID lpStartAddress, LPVOID lpParameter = nullptr, + SIZE_T dwStackSize = 0, DWORD dwCreationFlags = 0 + ); + + template + void WriteMemoryInProcess(HANDLE hProcess, LPVOID lpAddress, T value, SIZE_T size) + { + SIZE_T bytesWritten = 0; + + BOOL res = + WriteProcessMemory(hProcess, lpAddress, value, size, &bytesWritten); + if (res == FALSE || bytesWritten != size) + { + throw std::runtime_error("Error: writeMemoryInProcess failed"); + } + } + + template + LPVOID AllocateAndWriteMemoryInProcess(HANDLE hProcess, T value, SIZE_T size) + { + LPVOID lpAddress = AllocateMemoryInProcess(hProcess, size); + WriteMemoryInProcess(hProcess, lpAddress, value, size); + return lpAddress; + } + + template + T ReadMemoryInProcess(HANDLE hProcess, LPVOID lpBaseAddress, SIZE_T size) + { + T value; + SIZE_T bytesRead = 0; + + ReadProcessMemory(hProcess, lpBaseAddress, &value, size, &bytesRead); + + if (bytesRead != size) + { + throw std::runtime_error("Error: readMemoryInProcess failed"); + } + return value; + } + + } // namespace System +} // namespace Xash