Compare commits

...

6 Commits

Author SHA1 Message Date
Roflmuffin
c01aeec14b [no ci] chore: fix dependabot prefix 2024-05-08 20:13:19 +10:00
dependabot[bot]
41e7bee85a chore(deps): update hl2sdk: bump libraries/metamod-source from e857fbe to 607301a (#450)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-08 20:10:01 +10:00
Roflmuffin
9834271956 [no ci] chore: add metamod source dependabot config 2024-05-08 19:59:53 +10:00
Michael Wilson
fb5967d102 Back to C++17 (#447) 2024-05-08 16:34:51 +10:00
dependabot[bot]
928bc3f74d chore(deps): update hl2sdk: bump libraries/hl2sdk-cs2 from 3fc8d0f to 9ddef9a (#446)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-08 15:27:48 +10:00
Roflmuffin
6a7d7dba4f [no ci] feat: add hl2sdk-cs2 dependabot 2024-05-08 14:30:51 +10:00
7 changed files with 269 additions and 215 deletions

12
.github/dependabot.yaml vendored Normal file
View File

@@ -0,0 +1,12 @@
version: 2
updates:
# Update the submodule in libraries/hl2sdk-cs2 every day
- package-ecosystem: "gitsubmodule"
directory: "/"
allow:
- dependency-name: "libraries/hl2sdk-cs2"
- dependency-name: "libraries/metamod-source"
schedule:
interval: "daily"
commit-message:
prefix: "chore(deps)"

View File

@@ -1,6 +1,5 @@
#set(_ITERATOR_DEBUG_LEVEL 2)
add_definitions(-D_LINUX -DPOSIX -DLINUX -DGNUC -DCOMPILER_GCC -DPLATFORM_64BITS)
add_definitions(-D_LINUX -DPOSIX -DLINUX -DGNUC -DCOMPILER_GCC -DPLATFORM_64BITS -D_FILE_OFFSET_BITS=64)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dstrnicmp=strncasecmp -D_snprintf=snprintf")
@@ -16,16 +15,18 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof -Wno-reorder")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -msse -fno-strict-aliasing")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-threadsafe-statics -v -fvisibility=default")
SET(
COUNTER_STRIKE_SHARP_LINK_LIBRARIES
${SOURCESDK_LIB}/linux64/libtier0.so
${SOURCESDK_LIB}/linux64/tier1.a
${SOURCESDK_LIB}/linux64/interfaces.a
${SOURCESDK_LIB}/linux64/mathlib.a
spdlog
dynload_s
dyncall_s
distorm
funchook-static
dynohook
)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libgcc -static-libstdc++")
set(
COUNTER_STRIKE_SHARP_LINK_LIBRARIES
${SOURCESDK_LIB}/linux64/libtier0.so
${SOURCESDK_LIB}/linux64/tier1.a
${SOURCESDK_LIB}/linux64/interfaces.a
${SOURCESDK_LIB}/linux64/mathlib.a
spdlog
dynload_s
dyncall_s
distorm
funchook-static
dynohook
)

View File

@@ -1,8 +1,8 @@
if (UNIX AND NOT APPLE)
if(UNIX AND NOT APPLE)
set(LINUX TRUE)
endif()
if (WIN32 AND NOT MSVC)
if(WIN32 AND NOT MSVC)
message(FATAL "MSVC restricted.")
endif()
@@ -11,9 +11,9 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING
FORCE
)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 17)
if (LINUX)
if(LINUX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
endif()
@@ -67,4 +67,4 @@ include_directories(
libraries
)
include(${CMAKE_CURRENT_LIST_DIR}/metamod/configure_metamod.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/metamod/configure_metamod.cmake)

View File

@@ -1,4 +1,5 @@
#include "core/gameconfig.h"
#include <fstream>
#include "log.h"
@@ -13,7 +14,8 @@ CGameConfig::~CGameConfig() = default;
bool CGameConfig::Init(char* conf_error, int conf_error_size)
{
std::ifstream ifs(m_sPath);
if (!ifs) {
if (!ifs)
{
V_snprintf(conf_error, conf_error_size, "Gamedata file not found.");
return false;
}
@@ -26,44 +28,53 @@ bool CGameConfig::Init(char* conf_error, int conf_error_size)
constexpr auto platform = "linux";
#endif
try {
for (auto& [k, v] : m_json.items()) {
if (v.contains("signatures")) {
if (auto library = v["signatures"]["library"]; library.is_string()) {
try
{
for (auto& [k, v] : m_json.items())
{
if (v.contains("signatures"))
{
if (auto library = v["signatures"]["library"]; library.is_string())
{
m_umLibraries[k] = library.get<std::string>();
}
if (auto signature = v["signatures"][platform]; signature.is_string()) {
if (auto signature = v["signatures"][platform]; signature.is_string())
{
m_umSignatures[k] = signature.get<std::string>();
}
}
if (v.contains("offsets")) {
if (auto offset = v["offsets"][platform]; offset.is_number_integer()) {
if (v.contains("offsets"))
{
if (auto offset = v["offsets"][platform]; offset.is_number_integer())
{
m_umOffsets[k] = offset.get<std::int64_t>();
}
}
if (v.contains("patches")) {
if (auto patch = v["patches"][platform]; patch.is_string()) {
if (v.contains("patches"))
{
if (auto patch = v["patches"][platform]; patch.is_string())
{
m_umPatches[k] = patch.get<std::string>();
}
}
}
} catch (const std::exception& ex) {
}
catch (const std::exception& ex)
{
V_snprintf(conf_error, conf_error_size, "Failed to parse gamedata file: %s", ex.what());
return false;
}
return true;
}
const std::string CGameConfig::GetPath()
{
return m_sPath;
}
const std::string CGameConfig::GetPath() { return m_sPath; }
const char* CGameConfig::GetLibrary(const std::string& name)
{
// My recommendation is switch to C++20.
auto it = m_umLibraries.find(name);
if (it == m_umLibraries.end()) {
if (it == m_umLibraries.end())
{
return nullptr;
}
return it->second.c_str();
@@ -72,7 +83,8 @@ const char* CGameConfig::GetLibrary(const std::string& name)
const char* CGameConfig::GetSignature(const std::string& name)
{
auto it = m_umSignatures.find(name);
if (it == m_umSignatures.end()) {
if (it == m_umSignatures.end())
{
return nullptr;
}
return it->second.c_str();
@@ -82,7 +94,8 @@ const char* CGameConfig::GetSymbol(const char* name)
{
const char* symbol = this->GetSignature(name);
if (!symbol || strlen(symbol) <= 1) {
if (!symbol || strlen(symbol) <= 1)
{
CSSHARP_CORE_ERROR("Missing symbol: {}\n", name);
return nullptr;
}
@@ -92,7 +105,8 @@ const char* CGameConfig::GetSymbol(const char* name)
const char* CGameConfig::GetPatch(const std::string& name)
{
auto it = m_umPatches.find(name);
if (it == m_umPatches.end()) {
if (it == m_umPatches.end())
{
return nullptr;
}
return it->second.c_str();
@@ -101,14 +115,14 @@ const char* CGameConfig::GetPatch(const std::string& name)
int CGameConfig::GetOffset(const std::string& name)
{
auto it = m_umOffsets.find(name);
if (it == m_umOffsets.end()) {
if (it == m_umOffsets.end())
{
return -1;
}
return it->second;
}
void* CGameConfig::GetAddress(const std::string& name, void* engine, void* server, char* error,
int maxlen)
void* CGameConfig::GetAddress(const std::string& name, void* engine, void* server, char* error, int maxlen)
{
CSSHARP_CORE_ERROR("Not implemented.");
return nullptr;
@@ -117,11 +131,9 @@ void* CGameConfig::GetAddress(const std::string& name, void* engine, void* serve
modules::CModule** CGameConfig::GetModule(const char* name)
{
const char* library = this->GetLibrary(name);
if (!library)
return nullptr;
if (!library) return nullptr;
if (strcmp(library, "engine") == 0)
return &modules::engine;
if (strcmp(library, "engine") == 0) return &modules::engine;
else if (strcmp(library, "server") == 0)
return &modules::server;
else if (strcmp(library, "vscript") == 0)
@@ -135,7 +147,8 @@ modules::CModule** CGameConfig::GetModule(const char* name)
bool CGameConfig::IsSymbol(const char* name)
{
const char* sigOrSymbol = this->GetSignature(name);
if (!sigOrSymbol || strlen(sigOrSymbol) <= 0) {
if (!sigOrSymbol || strlen(sigOrSymbol) <= 0)
{
CSSHARP_CORE_ERROR("Missing signature or symbol: {}\n", name);
return false;
}
@@ -145,22 +158,28 @@ bool CGameConfig::IsSymbol(const char* name)
void* CGameConfig::ResolveSignature(const char* name)
{
modules::CModule** module = this->GetModule(name);
if (!module || !(*module)) {
if (!module || !(*module))
{
CSSHARP_CORE_ERROR("Invalid Module {}\n", name);
return nullptr;
}
void* address = nullptr;
if (this->IsSymbol(name)) {
if (this->IsSymbol(name))
{
const char* symbol = this->GetSymbol(name);
if (!symbol) {
if (!symbol)
{
CSSHARP_CORE_ERROR("Invalid symbol for {}\n", name);
return nullptr;
}
address = (*module)->FindSymbol(symbol);
} else {
}
else
{
const char* signature = this->GetSignature(name);
if (!signature) {
if (!signature)
{
CSSHARP_CORE_ERROR("Failed to find signature for {}\n", name);
return nullptr;
}
@@ -168,7 +187,8 @@ void* CGameConfig::ResolveSignature(const char* name)
address = (*module)->FindSignature(signature);
}
if (!address) {
if (!address)
{
CSSHARP_CORE_ERROR("Failed to find address for {}\n", name);
return nullptr;
}
@@ -180,7 +200,8 @@ std::string CGameConfig::GetDirectoryName(const std::string& directoryPathInput)
std::string directoryPath = std::string(directoryPathInput);
size_t found = std::string(directoryPath).find_last_of("/\\");
if (found != std::string::npos) {
if (found != std::string::npos)
{
return std::string(directoryPath, found + 1);
}
return "";
@@ -188,17 +209,15 @@ std::string CGameConfig::GetDirectoryName(const std::string& directoryPathInput)
std::vector<int16_t> CGameConfig::HexToByte(std::string_view src)
{
if (src.empty()) {
if (src.empty())
{
return {};
}
auto hex_char_to_byte = [](char c) -> int16_t {
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
// a valid hex char can never go up to 0xFF
return -1;
@@ -211,36 +230,38 @@ std::vector<int16_t> CGameConfig::HexToByte(std::string_view src)
const std::string_view pattern = is_code_style ? R"(\x)" : " ";
const std::string_view wildcard = is_code_style ? "2A" : "?";
auto split = std::views::split(src, pattern);
std::string::size_type pos = 0;
for (auto&& str : split) {
if (str.empty()) [[unlikely]] {
continue;
while (pos < src.size())
{
std::string::size_type found = src.find(pattern, pos);
if (found == std::string::npos)
{
found = src.size();
}
std::string_view str = src.substr(pos, found - pos);
pos = found + pattern.size();
// std::string_view(std::subrange) constructor only exists in C++23 or above
// use this when compiler is GCC >= 12.1 or Clang >= 16
// const std::string_view byte(str.begin(), str.end());
// a workaround for GCC < 12.1, it doesn't work with Clang < 16
// https://stackoverflow.com/a/48403210
std::string_view byte (&*str.begin(), std::ranges::distance(str));
if (str.empty()) continue;
if (byte.starts_with(wildcard)) {
std::string byte(str.data(), str.size());
if (byte.substr(0, wildcard.size()) == wildcard)
{
result.emplace_back(-1);
continue;
}
if (byte.size() < 2) [[unlikely]] {
if (byte.size() < 2)
{
return {};
}
const auto high = hex_char_to_byte(byte[0]);
const auto low = hex_char_to_byte(byte[1]);
// if then is malformed, return nothing
// maybe print error message here?
if (high == 0xFF || low == 0xFF) [[unlikely]] {
if (high == 0xFF || low == 0xFF)
{
return {};
}

View File

@@ -1,97 +1,86 @@
#include "core/memory_module.h"
#include "core/globals.h"
#include "platform.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <memory>
#include <string_view>
#include <algorithm>
#include "core/globals.h"
#include "platform.h"
#if _WIN32
#include <Psapi.h>
#include <winternl.h>
#else
#include <link.h>
#include <elf.h>
#include <link.h>
#endif
#include "dbg.h"
#include "log.h"
#include "core/gameconfig.h"
#include "core/memory.h"
#include "dbg.h"
#include "log.h"
#include "metamod_oslink.h"
namespace counterstrikesharp::modules {
void Initialize()
{
if (!moduleList.empty())
return;
if (!moduleList.empty()) return;
#ifdef _WIN32
// walk through peb to get modules
const auto pteb = reinterpret_cast<PTEB>(
__readgsqword(reinterpret_cast<DWORD_PTR>(&static_cast<NT_TIB*>(nullptr)->Self)));
const auto pteb = reinterpret_cast<PTEB>(__readgsqword(reinterpret_cast<DWORD_PTR>(&static_cast<NT_TIB*>(nullptr)->Self)));
const auto peb = pteb->ProcessEnvironmentBlock;
for (auto entry = peb->Ldr->InMemoryOrderModuleList.Flink;
entry != &peb->Ldr->InMemoryOrderModuleList; entry = entry->Flink) {
const auto module_entry =
CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
for (auto entry = peb->Ldr->InMemoryOrderModuleList.Flink; entry != &peb->Ldr->InMemoryOrderModuleList; entry = entry->Flink)
{
const auto module_entry = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
std::wstring_view w_name = module_entry->FullDllName.Buffer;
// a hack way to do so
std::string name(w_name.begin(), w_name.end());
std::ranges::replace(name, '\\', '/');
std::replace(name.begin(), name.end(), '\\', '/');
// check for extension first
if (!name.ends_with(MODULE_EXT))
continue;
if (name.rfind(MODULE_EXT) != name.length() - strlen(MODULE_EXT)) continue;
// no addons
if (name.find(R"(csgo/addons/)") != std::string::npos)
continue;
if (name.find(R"(csgo/addons/)") != std::string::npos) continue;
// we need only modules from ROOTBIN and GAMEBIN
bool isFromRootBin = name.find(ROOTBIN) != std::string::npos;
bool isFromGameBin = name.find(GAMEBIN) != std::string::npos;
if (!isFromGameBin && !isFromRootBin)
continue;
if (!isFromGameBin && !isFromRootBin) continue;
auto mod = std::make_unique<CModule>(
name, reinterpret_cast<std::uintptr_t>(module_entry->DllBase));
auto mod = std::make_unique<CModule>(name, reinterpret_cast<std::uintptr_t>(module_entry->DllBase));
// it will delete itself after going out of scope
if (!mod->IsInitialized())
continue;
if (!mod->IsInitialized()) continue;
moduleList.emplace_back(std::move(mod));
}
#else
dl_iterate_phdr(
[](struct dl_phdr_info* info, size_t, void*) {
std::string name = info->dlpi_name;
std::string name = info->dlpi_name;
if (!name.ends_with(MODULE_EXT))
return 0;
if (name.rfind(MODULE_EXT) != name.length() - strlen(MODULE_EXT)) return 0;
if (name.find("csgo/addons") != std::string::npos)
return 0;
if (name.find("csgo/addons") != std::string::npos) return 0;
bool isFromRootBin = name.find(ROOTBIN) != std::string::npos;
bool isFromGameBin = name.find(GAMEBIN) != std::string::npos;
if (!isFromGameBin && !isFromRootBin)
return 0;
bool isFromRootBin = name.find(ROOTBIN) != std::string::npos;
bool isFromGameBin = name.find(GAMEBIN) != std::string::npos;
if (!isFromGameBin && !isFromRootBin) return 0;
auto mod = std::make_unique<CModule>(name, info);
if (!mod->IsInitialized())
return 0;
auto mod = std::make_unique<CModule>(name, info);
if (!mod->IsInitialized()) return 0;
moduleList.emplace_back(std::move(mod));
return 0;
},
moduleList.emplace_back(std::move(mod));
return 0;
},
nullptr);
#endif
}
@@ -100,14 +89,16 @@ CModule* GetModuleByName(std::string name)
{
#ifdef _WIN32
// or add this in GetGameDirectory()?
std::ranges::replace(name, '\\', '/');
std::replace(name.begin(), name.end(), '\\', '/');
#endif
const auto it = std::ranges::find_if(moduleList, [name](const std::unique_ptr<CModule>& i) {
return name.ends_with(i->m_pszModule);
const auto it = std::find_if(moduleList.begin(), moduleList.end(), [&name](const std::unique_ptr<CModule>& i) {
return !i->m_pszModule.empty() && name.size() >= i->m_pszModule.size() &&
name.substr(name.size() - i->m_pszModule.size()) == i->m_pszModule;
});
if (it == moduleList.end()) {
if (it == moduleList.end())
{
CSSHARP_CORE_ERROR("Cannot find module {}", name);
return nullptr;
@@ -125,12 +116,14 @@ constexpr std::array modules_to_read_from_disk = {
CModule::CModule(std::string_view path, std::uint64_t base)
{
const auto dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(base);
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
{
return;
}
const auto nt_header = reinterpret_cast<PIMAGE_NT_HEADERS>(base + dos_header->e_lfanew);
if (nt_header->Signature != IMAGE_NT_SIGNATURE) {
if (nt_header->Signature != IMAGE_NT_SIGNATURE)
{
return;
}
@@ -140,13 +133,16 @@ CModule::CModule(std::string_view path, std::uint64_t base)
m_baseAddress = base;
m_size = nt_header->OptionalHeader.SizeOfImage;
const bool should_read_from_disk = std::ranges::any_of(modules_to_read_from_disk,
[&](const auto& i) { return m_pszModule == i; });
const bool should_read_from_disk = std::any_of(modules_to_read_from_disk.begin(), modules_to_read_from_disk.end(), [&](const auto& i) {
return m_pszModule == i;
});
std::vector<std::uint8_t> disk_data{};
if (should_read_from_disk) {
if (should_read_from_disk)
{
std::ifstream stream(m_pszPath, std::ios::in | std::ios::binary);
if (!stream.good()) {
if (!stream.good())
{
CSSHARP_CORE_ERROR("Cannot open file {}", m_pszPath);
return;
}
@@ -156,11 +152,13 @@ CModule::CModule(std::string_view path, std::uint64_t base)
auto section = IMAGE_FIRST_SECTION(nt_header);
for (auto i = 0; i < nt_header->FileHeader.NumberOfSections; i++, section++) {
for (auto i = 0; i < nt_header->FileHeader.NumberOfSections; i++, section++)
{
const auto is_executable = (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
const auto is_readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0;
if (is_executable && is_readable) {
if (is_executable && is_readable)
{
const auto start = this->m_baseAddress + section->VirtualAddress;
const auto size = (std::min)(section->SizeOfRawData, section->Misc.VirtualSize);
const auto data = reinterpret_cast<std::uint8_t*>(start);
@@ -170,8 +168,10 @@ CModule::CModule(std::string_view path, std::uint64_t base)
segment.address = start;
segment.bytes.reserve(size);
if (should_read_from_disk) {
if (auto bytes = GetOriginalBytes(disk_data, start - m_baseAddress, size)) {
if (should_read_from_disk)
{
if (auto bytes = GetOriginalBytes(disk_data, start - m_baseAddress, size))
{
CSSHARP_CORE_INFO("Copying bytes from disk for {}", m_pszPath);
segment.bytes = bytes.value();
continue;
@@ -186,8 +186,7 @@ CModule::CModule(std::string_view path, std::uint64_t base)
DumpSymbols();
if (m_fnCreateInterface == nullptr)
return;
if (m_fnCreateInterface == nullptr) return;
m_bInitialized = true;
}
@@ -198,13 +197,16 @@ CModule::CModule(std::string_view path, dl_phdr_info* info)
m_pszPath = path.data();
m_baseAddress = info->dlpi_addr;
const bool should_read_from_disk = std::ranges::any_of(modules_to_read_from_disk,
[&](const auto& i) { return m_pszModule == i; });
const bool should_read_from_disk = std::any_of(modules_to_read_from_disk.begin(), modules_to_read_from_disk.end(), [&](const auto& i) {
return m_pszModule == i;
});
std::vector<std::uint8_t> disk_data{};
if (should_read_from_disk) {
if (should_read_from_disk)
{
std::ifstream stream(m_pszPath, std::ios::in | std::ios::binary);
if (!stream.good()) {
if (!stream.good())
{
CSSHARP_CORE_ERROR("Cannot open file {}", m_pszPath);
return;
}
@@ -212,25 +214,25 @@ CModule::CModule(std::string_view path, dl_phdr_info* info)
disk_data.assign((std::istreambuf_iterator(stream)), std::istreambuf_iterator<char>());
}
for (auto i = 0; i < info->dlpi_phnum; i++) {
for (auto i = 0; i < info->dlpi_phnum; i++)
{
auto address = m_baseAddress + info->dlpi_phdr[i].p_paddr;
auto type = info->dlpi_phdr[i].p_type;
auto is_dynamic_section = type == PT_DYNAMIC;
if (is_dynamic_section) {
if (is_dynamic_section)
{
DumpSymbols(reinterpret_cast<ElfW(Dyn)*>(address));
continue;
}
if (type != PT_LOAD)
continue;
if (type != PT_LOAD) continue;
auto flags = info->dlpi_phdr[i].p_flags;
auto is_executable = (flags & PF_X) != 0;
auto is_readable = (flags & PF_R) != 0;
if (!is_executable || !is_readable)
continue;
if (!is_executable || !is_readable) continue;
auto size = info->dlpi_phdr[i].p_filesz;
auto* data = reinterpret_cast<std::uint8_t*>(address);
@@ -240,8 +242,10 @@ CModule::CModule(std::string_view path, dl_phdr_info* info)
segment.address = address;
segment.bytes.reserve(size);
if (should_read_from_disk) {
if (auto bytes = GetOriginalBytes(disk_data, address - m_baseAddress, size)) {
if (should_read_from_disk)
{
if (auto bytes = GetOriginalBytes(disk_data, address - m_baseAddress, size))
{
CSSHARP_CORE_INFO("Copying bytes from disk for {}", m_pszPath);
segment.bytes = bytes.value();
continue;
@@ -253,8 +257,7 @@ CModule::CModule(std::string_view path, dl_phdr_info* info)
segment.bytes.assign(&data[0], &data[size]);
}
if (m_fnCreateInterface == nullptr)
return;
if (m_fnCreateInterface == nullptr) return;
m_bInitialized = true;
}
@@ -265,24 +268,18 @@ void CModule::DumpSymbols()
{
const auto dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(m_baseAddress);
const auto nt_header = reinterpret_cast<PIMAGE_NT_HEADERS>(
reinterpret_cast<std::uint8_t*>(m_baseAddress) + dos_header->e_lfanew);
const auto nt_header = reinterpret_cast<PIMAGE_NT_HEADERS>(reinterpret_cast<std::uint8_t*>(m_baseAddress) + dos_header->e_lfanew);
const auto [export_address_rva, export_size] =
nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (export_size == 0 || export_address_rva == 0)
return;
const auto [export_address_rva, export_size] = nt_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (export_size == 0 || export_address_rva == 0) return;
auto export_directory =
reinterpret_cast<IMAGE_EXPORT_DIRECTORY*>(m_baseAddress + export_address_rva);
const auto names =
reinterpret_cast<uint32_t*>(m_baseAddress + export_directory->AddressOfNames);
const auto addresses =
reinterpret_cast<uint32_t*>(m_baseAddress + export_directory->AddressOfFunctions);
const auto ordinals =
reinterpret_cast<std::uint16_t*>(m_baseAddress + export_directory->AddressOfNameOrdinals);
auto export_directory = reinterpret_cast<IMAGE_EXPORT_DIRECTORY*>(m_baseAddress + export_address_rva);
const auto names = reinterpret_cast<uint32_t*>(m_baseAddress + export_directory->AddressOfNames);
const auto addresses = reinterpret_cast<uint32_t*>(m_baseAddress + export_directory->AddressOfFunctions);
const auto ordinals = reinterpret_cast<std::uint16_t*>(m_baseAddress + export_directory->AddressOfNameOrdinals);
for (auto i = 0ull; i < export_directory->NumberOfNames; i++) {
for (auto i = 0ull; i < export_directory->NumberOfNames; i++)
{
const auto export_name = reinterpret_cast<const char*>(m_baseAddress + names[i]);
const auto address = m_baseAddress + addresses[ordinals[i]];
@@ -290,7 +287,8 @@ void CModule::DumpSymbols()
address < reinterpret_cast<uintptr_t>(export_directory) + export_size)
continue;
if (std::string_view(export_name) == "CreateInterface") {
if (std::string_view(export_name) == "CreateInterface")
{
m_fnCreateInterface = reinterpret_cast<fnCreateInterface>(address);
}
@@ -313,33 +311,36 @@ void CModule::DumpSymbols(ElfW(Dyn) * dyn)
};
auto header = (Header*)gnuHashAddress;
const auto bucketsAddress =
gnuHashAddress + sizeof(Header) + (sizeof(std::uintptr_t) * header->bloom_size);
const auto bucketsAddress = gnuHashAddress + sizeof(Header) + (sizeof(std::uintptr_t) * header->bloom_size);
// Locate the chain that handles the largest index bucket.
uint32_t lastSymbol = 0;
auto bucketAddress = (uint32_t*)bucketsAddress;
for (uint32_t i = 0; i < header->nbuckets; ++i) {
for (uint32_t i = 0; i < header->nbuckets; ++i)
{
uint32_t bucket = *bucketAddress;
if (lastSymbol < bucket) {
if (lastSymbol < bucket)
{
lastSymbol = bucket;
}
bucketAddress++;
}
if (lastSymbol < header->symoffset) {
if (lastSymbol < header->symoffset)
{
return header->symoffset;
}
// Walk the bucket's chain to add the chain length to the total.
const auto chainBaseAddress = bucketsAddress + (sizeof(uint32_t) * header->nbuckets);
for (;;) {
auto chainEntry =
(uint32_t*)(chainBaseAddress + (lastSymbol - header->symoffset) * sizeof(uint32_t));
for (;;)
{
auto chainEntry = (uint32_t*)(chainBaseAddress + (lastSymbol - header->symoffset) * sizeof(uint32_t));
lastSymbol++;
// If the low bit is set, this entry is the end of the chain.
if (*chainEntry & 1) {
if (*chainEntry & 1)
{
break;
}
}
@@ -353,34 +354,46 @@ void CModule::DumpSymbols(ElfW(Dyn) * dyn)
char* string_table{};
std::size_t symbol_count{};
while (dyn->d_tag != DT_NULL) {
if (dyn->d_tag == DT_HASH) {
while (dyn->d_tag != DT_NULL)
{
if (dyn->d_tag == DT_HASH)
{
hash_ptr = reinterpret_cast<ElfW(Word)*>(dyn->d_un.d_ptr);
symbol_count = hash_ptr[1];
} else if (dyn->d_tag == DT_STRTAB) {
}
else if (dyn->d_tag == DT_STRTAB)
{
string_table = reinterpret_cast<char*>(dyn->d_un.d_ptr);
} else if (!symbol_count && dyn->d_tag == DT_GNU_HASH) {
}
else if (!symbol_count && dyn->d_tag == DT_GNU_HASH)
{
symbol_count = GetNumberOfSymbolsFromGnuHash(dyn->d_un.d_ptr);
} else if (dyn->d_tag == DT_SYMTAB) {
}
else if (dyn->d_tag == DT_SYMTAB)
{
symbols = reinterpret_cast<ElfW(Sym)*>(dyn->d_un.d_ptr);
for (auto i = 0; i < symbol_count; i++) {
if (!symbols[i].st_name) {
for (auto i = 0; i < symbol_count; i++)
{
if (!symbols[i].st_name)
{
continue;
}
if (symbols[i].st_other != 0) {
if (symbols[i].st_other != 0)
{
continue;
}
auto address = symbols[i].st_value + m_baseAddress;
std::string_view name = &string_table[symbols[i].st_name];
if (name == "CreateInterface") {
if (name == "CreateInterface")
{
m_fnCreateInterface = reinterpret_cast<fnCreateInterface>(address);
}
_symbols.insert({name.data(), address});
_symbols.insert({ name.data(), address });
}
}
@@ -390,11 +403,9 @@ void CModule::DumpSymbols(ElfW(Dyn) * dyn)
#endif
std::optional<std::vector<std::uint8_t>>
CModule::GetOriginalBytes(const std::vector<std::uint8_t>& disk_data, std::uintptr_t rva,
std::size_t size)
CModule::GetOriginalBytes(const std::vector<std::uint8_t>& disk_data, std::uintptr_t rva, std::size_t size)
{
auto get_file_ptr_from_rva = [](std::uint8_t* data,
std::uintptr_t address) -> std::optional<std::uintptr_t> {
auto get_file_ptr_from_rva = [](std::uint8_t* data, std::uintptr_t address) -> std::optional<std::uintptr_t> {
#ifdef _WIN32
// thank you praydog
// https://github.com/cursey/kananlib/blob/b0323a0b005fc9e3944e0ea36dcc98eda4b84eea/src/Module.cpp#L176
@@ -402,14 +413,16 @@ CModule::GetOriginalBytes(const std::vector<std::uint8_t>& disk_data, std::uintp
const auto dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(data);
const auto nt_header = reinterpret_cast<PIMAGE_NT_HEADERS>(&data[dos_header->e_lfanew]);
auto section = IMAGE_FIRST_SECTION(nt_header);
for (auto i = 0; i < nt_header->FileHeader.NumberOfSections; i++, section++) {
for (auto i = 0; i < nt_header->FileHeader.NumberOfSections; i++, section++)
{
auto section_size = section->Misc.VirtualSize;
if (section_size == 0) {
if (section_size == 0)
{
section_size = section->SizeOfRawData;
}
if (address >= section->VirtualAddress &&
address < static_cast<uintptr_t>(section->VirtualAddress) + section_size) {
if (address >= section->VirtualAddress && address < static_cast<uintptr_t>(section->VirtualAddress) + section_size)
{
const auto delta = section->VirtualAddress - section->PointerToRawData;
return reinterpret_cast<std::uintptr_t>(data + (address - delta));
@@ -423,23 +436,24 @@ CModule::GetOriginalBytes(const std::vector<std::uint8_t>& disk_data, std::uintp
};
const auto disk_ptr = get_file_ptr_from_rva(const_cast<std::uint8_t*>(disk_data.data()), rva);
if (!disk_ptr)
return std::nullopt;
if (!disk_ptr) return std::nullopt;
const auto disk_bytes = reinterpret_cast<std::uint8_t*>(*disk_ptr);
std::vector<std::uint8_t> result{&disk_bytes[0], &disk_bytes[size]};
std::vector<std::uint8_t> result{ &disk_bytes[0], &disk_bytes[size] };
return result;
}
void* CModule::FindSignature(const char* signature)
{
if (signature == nullptr || strlen(signature) == 0) {
if (signature == nullptr || strlen(signature) == 0)
{
return nullptr;
}
auto pData = CGameConfig::HexToByte(signature);
if (pData.empty()) [[unlikely]] {
if (pData.empty()) [[unlikely]]
{
CSSHARP_CORE_ERROR("Cannot convert signture \"{}\" to bytes", signature);
return nullptr;
}
@@ -449,25 +463,27 @@ void* CModule::FindSignature(const char* signature)
void* CModule::FindSignature(const std::vector<int16_t>& sigBytes)
{
for (auto&& segment : m_vecSegments) {
for (auto&& segment : m_vecSegments)
{
const auto size = segment.bytes.size();
auto* data = segment.bytes.data();
auto first_byte = sigBytes[0];
std::uint8_t* end = data + size - sigBytes.size();
for (std::uint8_t* current = data; current <= end; ++current) {
if (first_byte != -1)
current = std::find(current, end, first_byte);
for (std::uint8_t* current = data; current <= end; ++current)
{
if (first_byte != -1) current = std::find(current, end, first_byte);
if (current == end) {
if (current == end)
{
break;
}
if (std::equal(sigBytes.begin() + 1, sigBytes.end(), current + 1,
[](auto opt, auto byte) {
return opt == -1 || opt == byte;
})) {
if (std::equal(sigBytes.begin() + 1, sigBytes.end(), current + 1, [](auto opt, auto byte) {
return opt == -1 || opt == byte;
}))
{
return reinterpret_cast<void*>(current - data + segment.address);
}
}
@@ -478,7 +494,8 @@ void* CModule::FindSignature(const std::vector<int16_t>& sigBytes)
void* CModule::FindInterface(std::string_view name)
{
if (_interfaces.empty()) {
if (_interfaces.empty())
{
auto RelToAbs = [](std::uintptr_t address, int offset) {
const auto displacement = *reinterpret_cast<int32_t*>(address + offset);
return address + offset + displacement + sizeof(int32_t);
@@ -506,15 +523,16 @@ void* CModule::FindInterface(std::string_view name)
void* ret_interface{};
const auto interface_reg = *reinterpret_cast<CInterfaceRegister**>(RelToAbs(address, 3));
for (auto list = interface_reg; list != nullptr; list = list->pNext) {
for (auto list = interface_reg; list != nullptr; list = list->pNext)
{
auto interface_addrss = list->fnCreate();
if (const std::string_view interface_name = list->szName; interface_name == name)
ret_interface = interface_addrss;
if (const std::string_view interface_name = list->szName; interface_name == name) ret_interface = interface_addrss;
_interfaces.insert({list->szName, reinterpret_cast<uintptr_t>(interface_addrss)});
_interfaces.insert({ list->szName, reinterpret_cast<uintptr_t>(interface_addrss) });
}
if (ret_interface == nullptr) {
if (ret_interface == nullptr)
{
// Replace Error() from hl2sdk-cs2, it essentially calls Plat_ExitProcess
CSSHARP_CORE_ERROR("Could not find interface {} in {}", name, m_pszModule);
Plat_ExitProcess(1);
@@ -522,10 +540,11 @@ void* CModule::FindInterface(std::string_view name)
return ret_interface;
}
const auto it = _interfaces.find(name.data());
if (it == _interfaces.end()) {
if (it == _interfaces.end())
{
CSSHARP_CORE_ERROR("Could not find interface {} in {}", name, m_pszModule);
Plat_ExitProcess(1);
}
@@ -535,7 +554,8 @@ void* CModule::FindInterface(std::string_view name)
void* CModule::FindSymbol(const std::string& name)
{
if (const auto it = _symbols.find(name); it != _symbols.end()) {
if (const auto it = _symbols.find(name); it != _symbols.end())
{
return reinterpret_cast<void*>(it->second);
}