Initial commit

This commit is contained in:
Andreas Maerten
2023-08-11 22:51:23 +02:00
committed by GitHub
commit 84f912cb34
58 changed files with 2583 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
BasedOnStyle: Microsoft
AccessModifierOffset: '-4'
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: 'true'
AlignEscapedNewlines: Left
AlignOperands: 'false'
AlignTrailingComments: 'true'
AllowAllArgumentsOnNextLine: 'false'
AllowAllConstructorInitializersOnNextLine: 'false'
AllowAllParametersOfDeclarationOnNextLine: 'false'
AllowShortBlocksOnASingleLine: 'false'
AllowShortCaseLabelsOnASingleLine: 'true'
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: 'false'
AlwaysBreakTemplateDeclarations: 'Yes'
BinPackArguments: 'false'
BinPackParameters: 'true'
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Allman
BreakBeforeTernaryOperators: 'false'
BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterColon
BreakStringLiterals: 'false'
CompactNamespaces: 'false'
ConstructorInitializerAllOnOneLineOrOnePerLine: 'false'
ConstructorInitializerIndentWidth: '4'
Cpp11BracedListStyle: 'true'
DerivePointerAlignment: 'false'
FixNamespaceComments: 'false'
IncludeBlocks: Regroup
IndentCaseLabels: 'false'
IndentPPDirectives: BeforeHash
IndentWidth: '4'
TabWidth: 4
IndentWrappedFunctionNames: 'true'
KeepEmptyLinesAtTheStartOfBlocks: 'false'
Language: Cpp
MacroBlockBegin: '0'
MaxEmptyLinesToKeep: '2'
NamespaceIndentation: All
PenaltyExcessCharacter: '0'
PenaltyReturnTypeOnItsOwnLine: '0'
PointerAlignment: Left
ReflowComments: 'false'
SortIncludes: 'true'
SortUsingDeclarations: 'true'
SpaceAfterCStyleCast: 'false'
SpaceAfterLogicalNot: 'false'
SpaceAfterTemplateKeyword: 'false'
SpaceBeforeAssignmentOperators: 'true'
SpaceBeforeCpp11BracedList: 'false'
SpaceBeforeCtorInitializerColon: 'true'
SpaceBeforeInheritanceColon: 'true'
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: 'true'
SpaceInEmptyParentheses: 'false'
SpacesBeforeTrailingComments: '1'
SpacesInAngles: 'false'
SpacesInCStyleCastParentheses: 'false'
SpacesInContainerLiterals: 'false'
SpacesInParentheses: 'false'
SpacesInSquareBrackets: 'false'
Standard: Auto
UseTab: ForIndentation
BreakBeforeBraces: Custom
BraceWrapping:
BeforeLambdaBody: 'false'
AfterCaseLabel: 'true'
PackConstructorInitializers: Never
+1
View File
@@ -0,0 +1 @@
build/
+14
View File
@@ -0,0 +1,14 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "GTA5.exe Attach",
"type": "cppvsdbg",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}
+81
View File
@@ -0,0 +1,81 @@
{
"files.associations": {
"*.c": "c",
"string_view": "cpp",
"algorithm": "cpp",
"atomic": "cpp",
"bit": "cpp",
"cctype": "cpp",
"charconv": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"exception": "cpp",
"format": "cpp",
"forward_list": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"istream": "cpp",
"iterator": "cpp",
"limits": "cpp",
"locale": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"optional": "cpp",
"ostream": "cpp",
"ratio": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"string": "cpp",
"system_error": "cpp",
"thread": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"utility": "cpp",
"vector": "cpp",
"xfacet": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocbuf": "cpp",
"xlocinfo": "cpp",
"xlocmes": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xstddef": "cpp",
"xstring": "cpp",
"xtr1common": "cpp",
"xutility": "cpp",
"array": "cpp",
"condition_variable": "cpp",
"deque": "cpp",
"fstream": "cpp",
"functional": "cpp",
"iostream": "cpp",
"list": "cpp",
"queue": "cpp",
"ranges": "cpp",
"source_location": "cpp",
"span": "cpp",
"unordered_map": "cpp",
"xhash": "cpp",
"filesystem": "cpp"
},
"C_Cpp.default.configurationProvider": "maxmitti.cmake-tools-fork"
}
+31
View File
@@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.20.x)
project(NewBase VERSION 0.0.1 DESCRIPTION "A new base using new C++ features optimised for speed and ease of use")
# libs
include(cmake/async-logger.cmake)
include(cmake/imgui.cmake)
include(cmake/json.cmake)
include(cmake/minhook.cmake)
# source
set(SRC_DIR "${PROJECT_SOURCE_DIR}/src")
file(GLOB_RECURSE SRC_FILES
"${SRC_DIR}/**.hpp"
"${SRC_DIR}/**.cpp"
)
add_library(${PROJECT_NAME} MODULE ${SRC_FILES})
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 23)
target_precompile_headers(${PROJECT_NAME} PUBLIC "${SRC_DIR}/common.hpp")
message(STATUS "Setting up include directories.")
target_include_directories(${PROJECT_NAME} PRIVATE
"${SRC_DIR}"
"${imgui_SOURCE_DIR}"
"${minhook_SOURCE_DIR}/include"
)
message(STATUS "Setting up linked libraries")
target_link_libraries(${PROJECT_NAME} PRIVATE AsyncLogger imgui minhook nlohmann_json::nlohmann_json)
+9
View File
@@ -0,0 +1,9 @@
# Template Hell Base
This base is honestly a joke and a learning opporunity for myself to learn some more stuff about C++20. I went through the basics of what makes a mod menu base and tried to template them to hell and back.
## Structure
- `core/` : Essential general features for the base
- `game/` : Game specific implemented things
- `util/` : Loose functions that aren't game specific
+12
View File
@@ -0,0 +1,12 @@
include(FetchContent)
message(STATUS "Setting up AsyncLogger")
FetchContent_Declare(
AsyncLogger
GIT_REPOSITORY https://github.com/Yimura/AsyncLogger.git
GIT_TAG v0.0.6
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(AsyncLogger)
set_property(TARGET AsyncLogger PROPERTY CXX_STANDARD 23)
+33
View File
@@ -0,0 +1,33 @@
include(FetchContent)
set(LIB_NAME "imgui")
message(STATUS "Setting up ${LIB_NAME}")
FetchContent_Declare(
${LIB_NAME}
GIT_REPOSITORY https://github.com/Spyral-Org/imgui.git
GIT_TAG 44d98bfd6d12caee43bea49fda71c8313c3e57e7
GIT_PROGRESS TRUE
)
FetchContent_GetProperties(${LIB_NAME})
if(NOT imgui_POPULATED)
FetchContent_Populate(${LIB_NAME})
file(GLOB SRC_IMGUI
"${imgui_SOURCE_DIR}/*.cpp"
"${imgui_SOURCE_DIR}/*.h"
"${imgui_SOURCE_DIR}/backends/imgui_impl_win32.*"
"${imgui_SOURCE_DIR}/backends/imgui_impl_dx11.*"
"${imgui_SOURCE_DIR}/misc/cpp/imgui_stdlib.*"
)
add_library(imgui STATIC ${SRC_IMGUI})
source_group(TREE ${imgui_SOURCE_DIR} PREFIX "imgui" FILES ${SRC_IMGUI})
target_include_directories(imgui PRIVATE
"${imgui_SOURCE_DIR}"
"${imgui_SOURCE_DIR}/backends"
"${imgui_SOURCE_DIR}/misc/cpp"
)
endif()
+6
View File
@@ -0,0 +1,6 @@
include(FetchContent)
message(STATUS "Setting up nlohmann::json")
FetchContent_Declare(json GIT_REPOSITORY "https://github.com/nlohmann/json/" GIT_TAG v3.11.2 GIT_SHALLOW TRUE)
option(JSON_MultipleHeaders "Disable nlohmann JSON multi header default." OFF)
FetchContent_MakeAvailable(json)
+12
View File
@@ -0,0 +1,12 @@
include(FetchContent)
set(LIB_NAME "minhook")
message(STATUS "Setting up ${LIB_NAME}")
FetchContent_Declare(
${LIB_NAME}
GIT_REPOSITORY https://github.com/TsudaKageyu/minhook.git
GIT_TAG 98b74f1fc12d00313d91f10450e5b3e0036175e3
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(${LIB_NAME})
+9
View File
@@ -0,0 +1,9 @@
#include "common.hpp"
namespace NewBase
{
std::atomic<bool> g_Running{true};
HINSTANCE g_DllInstance{nullptr};
HANDLE g_MainThread{INVALID_HANDLE_VALUE};
DWORD g_MainThreadId = -1;
}
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <AsyncLogger/Logger.hpp>
#include <MinHook.h>
#include <Windows.h>
#include <array>
#include <atomic>
#include <chrono>
#include <cstddef>
#include <filesystem>
#include <fstream>
#include <functional>
#include <future>
#include <imgui.h>
#include <iostream>
#include <map>
#include <memory>
#include <string_view>
#include <thread>
#include <vector>
#include <nlohmann/json.hpp>
using namespace al;
#include "core/logger/LogHelper.hpp"
namespace NewBase
{
using namespace std::chrono_literals;
extern std::atomic<bool> g_Running;
extern HINSTANCE g_DllInstance;
extern HANDLE g_MainThread;
extern DWORD g_MainThreadId;
}
+20
View File
@@ -0,0 +1,20 @@
#include "BaseObj.hpp"
namespace NewBase
{
BaseObj::BaseObj(const std::filesystem::path& path) :
m_Path(path)
{
}
[[nodiscard]] bool BaseObj::Exists() const
{
return exists(m_Path);
}
const std::filesystem::path& BaseObj::Path() const
{
return m_Path;
}
}
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include <filesystem>
namespace NewBase
{
class BaseObj
{
public:
BaseObj(const std::filesystem::path& path);
[[nodiscard]] bool Exists() const;
const std::filesystem::path& Path() const;
protected:
const std::filesystem::path m_Path;
};
}
+24
View File
@@ -0,0 +1,24 @@
#include "File.hpp"
#include "FileMgr.hpp"
namespace NewBase
{
File::File(const std::filesystem::path& path) :
BaseObj(path)
{
}
File File::Move(std::filesystem::path newPath)
{
if (newPath.is_relative())
newPath = m_Path.parent_path() / newPath;
FileMgr::EnsureFileCanBeCreated(newPath);
if (std::filesystem::exists(m_Path))
std::filesystem::rename(m_Path, newPath);
return { newPath };
}
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include "BaseObj.hpp"
namespace NewBase
{
class File final : public BaseObj
{
public:
File(const std::filesystem::path& path);
File Move(std::filesystem::path newPath);
};
}
+57
View File
@@ -0,0 +1,57 @@
#include "FileMgr.hpp"
#include "File.hpp"
#include "Folder.hpp"
namespace NewBase
{
void FileMgr::Init(const std::filesystem::path& rootFolder)
{
GetInstance().InitImpl(rootFolder);
}
void FileMgr::InitImpl(const std::filesystem::path& rootFolder)
{
m_RootFolder = rootFolder;
CreateFolderIfNotExists(m_RootFolder);
}
const std::filesystem::path& FileMgr::CreateFolderIfNotExists(const std::filesystem::path &folder)
{
bool create_path = !exists(folder);
if (!create_path && !is_directory(folder))
{
std::filesystem::remove(folder);
create_path = true;
}
if (create_path)
create_directory(folder);
return folder;
}
const std::filesystem::path& FileMgr::EnsureFileCanBeCreated(const std::filesystem::path &file)
{
return FileMgr::CreateFolderIfNotExists(file.parent_path());
}
File FileMgr::GetProjectFileImpl(const std::filesystem::path& file) const
{
if (file.is_absolute())
throw std::invalid_argument("FileMgr::GetProjectFile expects a relative path.");
auto projFile = File(m_RootFolder / file);
EnsureFileCanBeCreated(file);
return projFile;
}
Folder FileMgr::GetProjectFolderImpl(const std::filesystem::path &folder) const
{
if (folder.is_absolute())
throw std::invalid_argument("FileMgr::GetProjectFolder expects a relative path.");
return { m_RootFolder / folder };
}
}
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include <filesystem>
#include "File.hpp"
#include "Folder.hpp"
namespace NewBase
{
class FileMgr final
{
private:
FileMgr() = default;
public:
virtual ~FileMgr() = default;
FileMgr(const FileMgr&) = delete;
FileMgr(FileMgr&&) noexcept = delete;
FileMgr& operator=(const FileMgr&) = delete;
FileMgr& operator=(FileMgr&&) noexcept = delete;
static void Init(const std::filesystem::path& rootFolder);
[[nodiscard]] static File GetProjectFile(const std::filesystem::path& file)
{ return GetInstance().GetProjectFileImpl(file); }
[[nodiscard]] static Folder GetProjectFolder(const std::filesystem::path& folder)
{ return GetInstance().GetProjectFolderImpl(folder); }
static const std::filesystem::path& CreateFolderIfNotExists(const std::filesystem::path& folder);
static const std::filesystem::path& EnsureFileCanBeCreated(const std::filesystem::path& file);
private:
void InitImpl(const std::filesystem::path& rootFolder);
File GetProjectFileImpl(const std::filesystem::path& file) const;
Folder GetProjectFolderImpl(const std::filesystem::path& folder) const;
static FileMgr& GetInstance()
{
static FileMgr i {};
return i;
}
private:
std::filesystem::path m_RootFolder;
};
}
+11
View File
@@ -0,0 +1,11 @@
#include "Folder.hpp"
#include "FileMgr.hpp"
namespace NewBase
{
Folder::Folder(const std::filesystem::path& path) :
BaseObj(path)
{
}
}
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include "BaseObj.hpp"
namespace NewBase
{
class Folder final : public BaseObj
{
public:
Folder(const std::filesystem::path& folder);
};
}
+34
View File
@@ -0,0 +1,34 @@
#include "BaseHook.hpp"
#include <MinHook.h>
namespace NewBase
{
BaseHook::BaseHook(const std::string_view name) :
m_Name(name),
m_Enabled(false)
{
m_Hooks.emplace_back(this);
}
std::vector<BaseHook*>& BaseHook::Hooks()
{
return m_Hooks;
}
void BaseHook::EnableAll()
{
for (auto hook : m_Hooks)
{
hook->Enable();
}
}
void BaseHook::DisableAll()
{
for (auto hook : m_Hooks)
{
hook->Disable();
}
}
}
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#include <string_view>
namespace NewBase
{
class BaseHook
{
private:
const std::string_view m_Name;
protected:
bool m_Enabled;
public:
BaseHook(const std::string_view name);
virtual ~BaseHook() = default;
BaseHook(const BaseHook&) = delete;
BaseHook(BaseHook&&) noexcept = delete;
BaseHook& operator=(const BaseHook&) = delete;
BaseHook& operator=(BaseHook&&) noexcept = delete;
const std::string_view Name() const
{ return m_Name; }
inline bool IsEnabled() const
{ return m_Enabled; }
virtual bool Enable() = 0;
virtual bool Disable() = 0;
public:
template<auto HookFunc>
struct HookHelper
{
inline static BaseHook* m_Hook;
};
template<auto HookFunc>
inline static void Add(BaseHook* hook);
template<auto HookFunc, typename T>
inline static T* Get();
static std::vector<BaseHook*>& Hooks();
static void EnableAll();
static void DisableAll();
private:
inline static std::vector<BaseHook*> m_Hooks;
};
template<auto HookFunc>
inline void BaseHook::Add(BaseHook* hook)
{
HookHelper<HookFunc>::m_Hook = hook;
}
template<auto HookFunc, typename T>
inline T* BaseHook::Get()
{
return reinterpret_cast<T*>(HookHelper<HookFunc>::m_Hook);
}
}
+131
View File
@@ -0,0 +1,131 @@
#pragma once
#include "BaseHook.hpp"
#include "core/memory/PointerCalculator.hpp"
#include <MinHook.h>
#include <string_view>
namespace NewBase
{
template<typename T = int*>
class DetourHook : public BaseHook
{
private:
void* m_TargetFunc;
void* m_DetourFunc;
void* m_OriginalFunc;
public:
DetourHook(const std::string_view name, void* target, T detour);
virtual ~DetourHook();
bool Enable();
bool Disable();
bool EnableNow();
bool DisableNow();
T Original() const;
private:
void OptimizeHook();
};
template<typename T>
inline DetourHook<T>::DetourHook(const std::string_view name, void* target, T detour) :
BaseHook(name),
m_TargetFunc(target),
m_DetourFunc(detour),
m_OriginalFunc(nullptr)
{
OptimizeHook();
if (const auto result = MH_CreateHook(m_TargetFunc, m_DetourFunc, &m_OriginalFunc); result != MH_OK)
{
throw std::runtime_error("Failed to create hook!");
}
}
template<typename T>
inline DetourHook<T>::~DetourHook()
{
DisableNow();
}
template<typename T>
inline bool DetourHook<T>::Enable()
{
if (m_Enabled)
return false;
if (const auto result = MH_QueueEnableHook(m_TargetFunc); result != MH_OK)
{
throw std::runtime_error("Failed to queue hook to be enabled.");
return false;
}
return true;
}
template<typename T>
inline bool DetourHook<T>::Disable()
{
if (!m_Enabled)
return false;
if (const auto result = MH_QueueDisableHook(m_TargetFunc); result != MH_OK)
{
throw std::runtime_error("Failed to queue hook to be disable.");
return false;
}
return true;
}
template<typename T>
inline bool DetourHook<T>::EnableNow()
{
if (m_Enabled)
return false;
if (const auto result = MH_EnableHook(m_TargetFunc); result != MH_OK)
{
throw std::runtime_error("Failed to enable hook right now.");
return false;
}
return true;
}
template<typename T>
inline bool DetourHook<T>::DisableNow()
{
if (!m_Enabled)
return false;
if (const auto result = MH_DisableHook(m_TargetFunc); result != MH_OK)
{
throw std::runtime_error("Failed to disable hook right now.");
return false;
}
return true;
}
template<typename T>
inline T DetourHook<T>::Original() const
{
return reinterpret_cast<T>(m_OriginalFunc);
}
template<typename T>
inline void DetourHook<T>::OptimizeHook()
{
auto ptr = PointerCalculator(m_TargetFunc);
while (ptr.As<std::uint8_t&>() == 0xE9)
{
ptr = ptr.Add(1).Rip();
}
m_TargetFunc = ptr.As<void*>();
}
}
+55
View File
@@ -0,0 +1,55 @@
#include "Hooking.hpp"
#include "BaseHook.hpp"
#include "DetourHook.hpp"
#include "VMTHook.hpp"
#include "game/hooks/Hooks.hpp"
#include "game/pointers/Pointers.hpp"
namespace NewBase
{
Hooking::Hooking()
{
BaseHook::Add<Window::WndProc>(new DetourHook("WndProc", Pointers.WndProc, Window::WndProc));
auto swapchain = new VMTHook<SwapChain::VMTSize>("SwapChain", *Pointers.SwapChain);
swapchain->Hook(SwapChain::VMTPresentIdx, SwapChain::Present);
swapchain->Hook(SwapChain::VMTResizeBuffersIdx, SwapChain::ResizeBuffers);
BaseHook::Add<SwapChain::Present>(swapchain);
}
Hooking::~Hooking()
{
DestroyImpl();
}
bool Hooking::Init()
{
return GetInstance().InitImpl();
}
void Hooking::Destroy()
{
GetInstance().DestroyImpl();
}
bool Hooking::InitImpl()
{
BaseHook::EnableAll();
m_MinHook.ApplyQueued();
return true;
}
void Hooking::DestroyImpl()
{
BaseHook::DisableAll();
m_MinHook.ApplyQueued();
for (auto it : BaseHook::Hooks())
{
delete it;
}
BaseHook::Hooks().clear();
}
}
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include "MinHook.hpp"
namespace NewBase
{
class Hooking
{
private:
Hooking();
MinHook m_MinHook;
public:
virtual ~Hooking();
Hooking(const Hooking&) = delete;
Hooking(Hooking&&) noexcept = delete;
Hooking& operator=(const Hooking&) = delete;
Hooking& operator=(Hooking&&) noexcept = delete;
static bool Init();
static void Destroy();
private:
bool InitImpl();
void DestroyImpl();
private:
static Hooking& GetInstance()
{
static Hooking i{};
return i;
}
};
}
+60
View File
@@ -0,0 +1,60 @@
#pragma once
#include "BaseHook.hpp"
#include "memory/Module.hpp"
#include "memory/PointerCalculator.hpp"
namespace NewBase
{
template<typename T = int*>
class IATHook : public BaseHook
{
private:
void** m_HookLocation;
void* m_OriginalFunc;
void* m_HookFunc;
public:
IATHook(const std::string_view name, Module* module, const std::string_view library, const std::string_view import, T detour);
virtual ~IATHook();
bool Enable();
bool Disable();
T Original() const;
};
template<typename T>
inline IATHook<T>::IATHook(const std::string_view name, Module* module, const std::string_view library, const std::string_view import, T detour) :
BaseHook(name)
{
m_HookLocation = module->GetImport(library, import);
m_OriginalFunc = *m_HookLocation;
m_HookFunc = (void*)(detour);
}
template<typename T>
inline IATHook<T>::~IATHook()
{
Disable();
}
template<typename T>
inline bool IATHook<T>::Enable()
{
*m_HookLocation = m_HookFunc;
return true;
}
template<typename T>
inline bool IATHook<T>::Disable()
{
*m_HookLocation = m_OriginalFunc;
return true;
}
template<typename T>
inline T IATHook<T>::Original() const
{
return reinterpret_cast<T>(m_OriginalFunc);
}
}
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include <MinHook.h>
namespace NewBase
{
class MinHook
{
public:
MinHook()
{
MH_Initialize();
}
~MinHook()
{
MH_Uninitialize();
}
MH_STATUS ApplyQueued()
{
return MH_ApplyQueued();
}
};
}
+102
View File
@@ -0,0 +1,102 @@
#pragma once
#include "BaseHook.hpp"
#include <array>
#include <cstddef>
#include <string_view>
namespace NewBase
{
template<std::size_t N>
class VMTHook : public BaseHook
{
private:
std::string_view m_Name;
bool m_Enabled;
std::array<void*, N> m_NewVMT = {nullptr};
void** m_OriginalVMT;
void*** m_VMTAddress;
public:
template<typename T>
VMTHook(const std::string_view name, T* vmtAddress);
~VMTHook();
virtual bool Enable() override;
virtual bool Disable() override;
template<typename T>
inline T Original(const std::uint32_t idx) const;
template<typename T>
void Hook(const std::uint32_t idx, T& detour);
void UnHook(const std::uint32_t idx);
inline constexpr std::size_t VMTSize() const
{
return N;
}
};
template<std::size_t N>
template<typename T>
inline VMTHook<N>::VMTHook(const std::string_view name, T* vmtAddress) :
BaseHook(name),
m_VMTAddress(reinterpret_cast<void***>(vmtAddress))
{
m_OriginalVMT = *m_VMTAddress;
std::copy_n(m_OriginalVMT, N, m_NewVMT.begin());
}
template<std::size_t N>
inline VMTHook<N>::~VMTHook()
{
Disable();
}
template<std::size_t N>
inline bool VMTHook<N>::Enable()
{
if (m_Enabled)
return false;
m_Enabled = true;
*m_VMTAddress = m_NewVMT.data();
return true;
}
template<std::size_t N>
inline bool VMTHook<N>::Disable()
{
if (!m_Enabled)
return false;
*m_VMTAddress = m_OriginalVMT;
m_Enabled = false;
return true;
}
template<std::size_t N>
template<typename T>
inline void VMTHook<N>::Hook(const std::uint32_t idx, T& detour)
{
if (std::is_pointer<T>())
m_NewVMT[idx] = reinterpret_cast<void*>(detour);
else
m_NewVMT[idx] = reinterpret_cast<void*>(&detour);
}
template<std::size_t N>
inline void VMTHook<N>::UnHook(const std::uint32_t idx)
{
m_NewVMT[idx] = m_OriginalVMT[idx];
}
template<std::size_t N>
template<typename T>
inline T VMTHook<N>::Original(const std::uint32_t idx) const
{
return reinterpret_cast<T>(m_OriginalVMT[idx]);
}
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once
namespace NewBase
{
enum class LogColor
{
RESET,
WHITE = 97,
CYAN = 36,
MAGENTA = 35,
BLUE = 34,
GREEN = 32,
YELLOW = 33,
RED = 31,
BLACK = 30
};
}
+111
View File
@@ -0,0 +1,111 @@
#include "LogHelper.hpp"
#include "LogSink.hpp"
namespace NewBase
{
void LogHelper::Destroy()
{
GetInstance().DestroyImpl();
}
bool LogHelper::Init(const std::string_view consoleName, const std::filesystem::path& file, const bool attachConsole)
{
return GetInstance().InitImpl(consoleName, file, attachConsole);
}
void LogHelper::ToggleConsole(bool toggle)
{
GetInstance().ToggleConsoleImpl(toggle);
}
void LogHelper::DestroyImpl()
{
Logger::Destroy();
CloseOutputStreams();
if (m_DidConsoleExist)
SetConsoleMode(m_ConsoleHandle, m_OriginalConsoleMode);
if (!m_DidConsoleExist && m_AttachConsole)
FreeConsole();
}
bool LogHelper::InitImpl(const std::string_view consoleName, const std::filesystem::path& file, const bool attachConsole)
{
m_ConsoleTitle = consoleName;
m_File = file;
m_AttachConsole = attachConsole;
if (m_AttachConsole)
{
if (m_DidConsoleExist = ::AttachConsole(GetCurrentProcessId()); !m_DidConsoleExist)
AllocConsole();
if (m_ConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); m_ConsoleHandle != nullptr)
{
SetConsoleTitleA(m_ConsoleTitle.data());
SetConsoleOutputCP(CP_UTF8);
DWORD consoleMode;
GetConsoleMode(m_ConsoleHandle, &consoleMode);
m_OriginalConsoleMode = consoleMode;
// terminal like behaviour enable full color support
consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
consoleMode &= ~(ENABLE_QUICK_EDIT_MODE);
SetConsoleMode(m_ConsoleHandle, consoleMode);
}
}
OpenOutputStreams();
Logger::Init();
Logger::AddSink([this](LogMessagePtr msg) {
m_ConsoleOut << LogSink::FormatConsole(msg);
m_ConsoleOut.flush();
});
Logger::AddSink([this](LogMessagePtr msg) {
m_FileOut << LogSink::FormatFile(msg);
m_FileOut.flush();
});
return true;
}
void LogHelper::ToggleConsoleImpl(bool toggle)
{
if (m_DidConsoleExist)
return;
if (m_AttachConsole)
{
m_ConsoleOut.close();
FreeConsole();
}
else
{
m_ConsoleOut.open("CONOUT$", std::ios_base::out | std::ios_base::app);
AllocConsole();
}
m_AttachConsole = !m_AttachConsole;
}
void LogHelper::CloseOutputStreams()
{
if (m_AttachConsole)
m_ConsoleOut.close();
m_FileOut.close();
}
void LogHelper::OpenOutputStreams()
{
if (m_AttachConsole)
m_ConsoleOut.open("CONOUT$", std::ios_base::out | std::ios_base::app);
m_FileOut.open(m_File, std::ios::out | std::ios::trunc);
}
}
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include <filesystem>
#include <fstream>
#include <iostream>
namespace NewBase
{
#define ADD_COLOR_TO_STREAM(color) "\x1b[" << int(color) << "m"
#define RESET_STREAM_COLOR "\x1b[0m"
#define HEX(value) "0x" << std::hex << std::uppercase << DWORD64(value) << std::dec << std::nouppercase
class LogHelper final
{
public:
~LogHelper() = default;
LogHelper(const LogHelper&) = delete;
LogHelper(LogHelper&&) = delete;
LogHelper& operator=(const LogHelper&) = delete;
LogHelper& operator=(LogHelper&&) = delete;
static void Destroy();
static bool Init(const std::string_view consoleName, const std::filesystem::path& file, const bool attachConsole = true);
static void ToggleConsole(bool toggle);
private:
LogHelper(){};
static LogHelper& GetInstance()
{
static LogHelper i{};
return i;
}
void DestroyImpl();
bool InitImpl(const std::string_view consoleName, const std::filesystem::path& file, const bool attachConsole);
void ToggleConsoleImpl(bool toggle);
void CloseOutputStreams();
void OpenOutputStreams();
private:
bool m_AttachConsole;
bool m_DidConsoleExist;
std::string_view m_ConsoleTitle;
DWORD m_OriginalConsoleMode;
HANDLE m_ConsoleHandle;
std::ofstream m_ConsoleOut;
std::filesystem::path m_File;
std::ofstream m_FileOut;
};
}
+57
View File
@@ -0,0 +1,57 @@
#include "LogSink.hpp"
#include "LogColor.hpp"
namespace NewBase
{
LogColor LogSink::GetColor(const eLogLevel level)
{
switch (level)
{
case eLogLevel::VERBOSE: return LogColor::BLUE;
case eLogLevel::INFO: return LogColor::GREEN;
case eLogLevel::WARNING: return LogColor::YELLOW;
case eLogLevel::FATAL: return LogColor::RED;
}
return LogColor::WHITE;
}
const char* LogSink::GetLevelStr(const eLogLevel level)
{
constexpr std::array<const char*, 4> levelStrings = {{{"DEBUG"}, {"INFO"}, {"WARN"}, {"ERROR"}}};
return levelStrings[level];
}
std::string LogSink::FormatConsole(const LogMessagePtr msg)
{
std::stringstream out;
const auto timestamp = std::format("{0:%H:%M:%S}", msg->Timestamp());
const auto& location = msg->Location();
const auto level = msg->Level();
const auto color = LogSink::GetColor(level);
const auto file = std::filesystem::path(location.file_name()).filename().string();
out << "[" << timestamp << "]" << ADD_COLOR_TO_STREAM(color) << "[" << GetLevelStr(level) << "/" << file << ":"
<< location.line() << "] " << RESET_STREAM_COLOR << msg->Message();
return out.str();
}
std::string LogSink::FormatFile(const LogMessagePtr msg)
{
std::stringstream out;
const auto timestamp = std::format("{0:%H:%M:%S}", msg->Timestamp());
const auto& location = msg->Location();
const auto level = msg->Level();
const auto file = std::filesystem::path(location.file_name()).filename().string();
out << "[" << timestamp << "]"
<< "[" << GetLevelStr(level) << "/" << file << ":" << location.line() << "] " << msg->Message();
return out.str();
}
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <AsyncLogger/Logger.hpp>
#include <memory>
#include <string>
namespace NewBase
{
using namespace al;
enum class LogColor;
class LogSink
{
private:
static LogColor GetColor(const eLogLevel level);
static const char* GetLevelStr(const eLogLevel level);
public:
static std::string FormatConsole(const LogMessagePtr msg);
static std::string FormatFile(const LogMessagePtr msg);
};
}
+37
View File
@@ -0,0 +1,37 @@
#include "BytePatch.hpp"
namespace NewBase
{
BytePatch::~BytePatch()
{
Restore();
}
void BytePatch::Apply() const
{
std::copy_n(m_Patch.get(), m_Size, reinterpret_cast<byte*>(m_Address));
}
void BytePatch::Restore() const
{
std::copy_n(m_Original.get(), m_Size, reinterpret_cast<byte*>(m_Address));
}
void BytePatch::Remove() const
{
if (const auto it = std::find(m_Patches.begin(), m_Patches.end(), this); it != m_Patches.end())
{
m_Patches.erase(it);
}
}
void BytePatch::RestoreAll()
{
m_Patches.clear();
}
bool operator==(const std::unique_ptr<BytePatch>& a, const BytePatch* b)
{
return a->m_Address == b->m_Address;
}
}
+90
View File
@@ -0,0 +1,90 @@
#pragma once
#include <memory>
#include <span>
#include <vector>
namespace NewBase
{
template<typename T>
concept SpanCompatibleType = requires(T a)
{
std::span{a};
};
class BytePatch
{
private:
byte* m_Address;
std::unique_ptr<byte[]> m_Patch;
std::unique_ptr<byte[]> m_Original;
std::size_t m_Size;
protected:
static inline std::vector<std::unique_ptr<BytePatch>> m_Patches;
public:
virtual ~BytePatch();
void Apply() const;
void Restore() const;
void Remove() const;
static void RestoreAll();
template<typename TAddr>
static const std::unique_ptr<BytePatch>& Make(TAddr addr, std::remove_pointer_t<std::remove_reference_t<TAddr>> value);
template<typename TAddr, typename T>
requires SpanCompatibleType<T>
static const std::unique_ptr<BytePatch>& Make(TAddr addr, T spanCompatible);
private:
template<typename TAddr>
BytePatch(TAddr addr, std::remove_pointer_t<std::remove_reference_t<TAddr>> value);
template<typename TAddr, typename T, std::size_t N>
BytePatch(TAddr addr, std::span<T, N> span);
friend bool operator==(const std::unique_ptr<BytePatch>& a, const BytePatch* b);
};
template<typename TAddr>
inline const std::unique_ptr<BytePatch>& BytePatch::Make(TAddr addr, std::remove_pointer_t<std::remove_reference_t<TAddr>> value)
{
return m_Patches.emplace_back(std::unique_ptr<BytePatch>(new BytePatch(addr, value)));
}
template<typename TAddr, typename T>
requires SpanCompatibleType<T>
inline const std::unique_ptr<BytePatch>& BytePatch::Make(TAddr addr, T spanCompatible)
{
return m_Patches.emplace_back(std::unique_ptr<BytePatch>(new BytePatch(addr, std::span{spanCompatible})));
}
template<typename TAddr>
inline BytePatch::BytePatch(TAddr addr, std::remove_pointer_t<std::remove_reference_t<TAddr>> value) :
m_Address(addr)
{
m_Size = sizeof(std::remove_pointer_t<std::remove_reference_t<TAddr>>);
m_Original = std::make_unique<byte[]>(m_Size);
std::copy_n(m_Address, m_Size, m_Original.get());
m_Patch = std::make_unique<byte[]>(m_Size);
std::copy_n(&value, m_Size, m_Patch.get());
}
template<typename TAddr, typename T, std::size_t N>
inline BytePatch::BytePatch(TAddr addr, std::span<T, N> span) :
m_Address((byte*)addr)
{
m_Size = span.size();
m_Original = std::make_unique<byte[]>(m_Size);
std::copy_n(m_Address, m_Size, m_Original.get());
m_Patch = std::make_unique<byte[]>(m_Size);
std::copy(span.begin(), span.end(), m_Patch.get());
}
}
+63
View File
@@ -0,0 +1,63 @@
#include "Module.hpp"
namespace NewBase
{
Module::Module(LDR_DATA_TABLE_ENTRY* dllEntry) :
m_Path(dllEntry->FullDllName.Buffer),
m_Name(m_Path.filename().string()),
m_Base(dllEntry->DllBase),
m_Size(0)
{
const auto ntHeader = GetNtHeader();
if (ntHeader)
{
m_Size = ntHeader->OptionalHeader.SizeOfCode;
}
}
const std::string_view Module::Name() const
{
return m_Name;
}
void** Module::GetImport(const std::string_view moduleName, const std::string_view symbolName) const
{
const auto ntHeader = GetNtHeader();
if (!ntHeader)
return nullptr;
const auto imageDataDirectory = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
auto importDescriptor = m_Base.Add(imageDataDirectory.VirtualAddress).As<IMAGE_IMPORT_DESCRIPTOR*>();
for (; importDescriptor->Name; importDescriptor++)
{
if (const auto modName = m_Base.Add(importDescriptor->Name).As<const char*>(); strcmp(modName, moduleName.data()))
continue;
auto firstThunk = m_Base.Add(importDescriptor->FirstThunk).As<IMAGE_THUNK_DATA*>();
auto origThunk = m_Base.Add(importDescriptor->OriginalFirstThunk).As<IMAGE_THUNK_DATA*>();
for (; origThunk->u1.AddressOfData != 0; firstThunk++, origThunk++)
{
if (const auto importData = m_Base.Add(origThunk->u1.AddressOfData).As<IMAGE_IMPORT_BY_NAME*>();
strcmp(importData->Name, symbolName.data()))
continue;
return reinterpret_cast<void**>(&firstThunk->u1.Function);
}
}
return nullptr;
}
bool Module::Valid() const
{
return m_Size;
}
IMAGE_NT_HEADERS* Module::GetNtHeader() const
{
if (!m_Base)
return nullptr;
const auto offset = m_Base.As<IMAGE_DOS_HEADER*>()->e_lfanew;
return m_Base.Add(offset).As<IMAGE_NT_HEADERS*>();
}
}
+88
View File
@@ -0,0 +1,88 @@
#pragma once
#include "PointerCalculator.hpp"
#include "common.hpp"
#include <winternl.h>
namespace NewBase
{
class Module
{
public:
Module(LDR_DATA_TABLE_ENTRY* dllEntry);
~Module() = default;
const std::string_view Name() const;
inline const std::uintptr_t Base() const;
inline const std::uintptr_t Size() const;
inline const std::uintptr_t End() const;
/**
* @brief Parses the EAT of the module for the requested symbolname
*
* @param symbolName
* @return void* Function address of the exported function
*/
template<typename T = void*>
T GetExport(const std::string_view symbolName) const;
/**
* @brief Gets the address of the import function
*
* @param moduleName The module to get the import from
* @param symbolName The function name
* @return void**
*/
void** GetImport(const std::string_view moduleName, const std::string_view symbolName) const;
bool Valid() const;
private:
IMAGE_NT_HEADERS* GetNtHeader() const;
private:
const std::filesystem::path m_Path;
const std::string m_Name;
PointerCalculator m_Base;
std::uintptr_t m_Size;
};
inline const std::uintptr_t Module::Base() const
{
return m_Base.As<std::uintptr_t>();
}
inline const std::uintptr_t Module::Size() const
{
return m_Size;
}
inline const std::uintptr_t Module::End() const
{
return Base() + m_Size;
}
template<typename T>
inline T Module::GetExport(const std::string_view symbolName) const
{
const auto ntHeader = GetNtHeader();
if (!ntHeader)
return nullptr;
const auto imageDataDirectory = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
const auto exportDirectory = m_Base.Add(imageDataDirectory.VirtualAddress).As<IMAGE_EXPORT_DIRECTORY*>();
const auto namesOffsets = m_Base.Add(exportDirectory->AddressOfNames).As<DWORD*>();
const auto funcOffsets = m_Base.Add(exportDirectory->AddressOfFunctions).As<DWORD*>();
for (std::size_t i = 0; i < exportDirectory->NumberOfFunctions; i++)
{
const auto functionName = m_Base.Add(namesOffsets[i]).As<const char*>();
if (strcmp(functionName, symbolName.data()))
continue;
return m_Base.Add(funcOffsets[i]).As<T>();
}
LOG(FATAL) << "Cannot find export: " << symbolName;
return nullptr;
}
}
+49
View File
@@ -0,0 +1,49 @@
#include "ModuleMgr.hpp"
#include "util/Joaat.hpp"
namespace NewBase
{
Module* ModuleMgr::Get(const std::string_view name)
{
return Get(Joaat(name));
}
Module* ModuleMgr::Get(joaat_t hash)
{
if (const auto& it = m_CachedModules.find(hash); it != m_CachedModules.end())
{
return it->second.get();
}
return nullptr;
}
bool ModuleMgr::LoadModules()
{
const auto peb = reinterpret_cast<PPEB>(NtCurrentTeb()->ProcessEnvironmentBlock);
if (!peb)
return false;
const auto ldrData = peb->Ldr;
if (!ldrData)
return false;
const auto moduleList = &ldrData->InMemoryOrderModuleList;
auto moduleEntry = moduleList->Flink;
for (; moduleList != moduleEntry; moduleEntry = moduleEntry->Flink)
{
const auto tableEntry = CONTAINING_RECORD(moduleEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
if (!tableEntry)
continue;
if (tableEntry->FullDllName.Buffer)
{
auto module = std::make_unique<Module>(tableEntry);
m_CachedModules.insert({Joaat(module->Name()), std::move(module)});
}
}
return true;
}
}
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include "Module.hpp"
#include "common.hpp"
namespace NewBase
{
using joaat_t = std::uint32_t;
class ModuleMgr
{
public:
ModuleMgr() = default;
virtual ~ModuleMgr() = default;
ModuleMgr(const ModuleMgr&) = delete;
ModuleMgr(ModuleMgr&&) noexcept = delete;
ModuleMgr& operator=(const ModuleMgr&) = delete;
ModuleMgr& operator=(ModuleMgr&&) noexcept = delete;
Module* Get(const std::string_view name);
Module* Get(joaat_t hash);
/**
* @brief Loads the modules from PEB and caches them.
*
* @return true If the peb is found and modules have been cached.
* @return false If the peb or peb->Ldr pointer were invalid.
*/
bool LoadModules();
private:
std::unordered_map<std::uint32_t, std::unique_ptr<Module>> m_CachedModules;
};
inline NewBase::ModuleMgr ModuleMgr;
}
+131
View File
@@ -0,0 +1,131 @@
#pragma once
#include "util/StrToHex.hpp"
#include <string_view>
namespace NewBase
{
template<std::size_t N>
struct Signature
{
char m_Signature[N]{};
std::size_t m_SignatureByteLength;
constexpr Signature(char const (&signature)[N]) :
m_SignatureByteLength(0)
{
std::ranges::copy(signature, m_Signature);
for (std::size_t i = 0; i < N; i++)
{
if (m_Signature[i] == ' ' || m_Signature[i] == '\0')
m_SignatureByteLength++;
}
}
/**
* @brief Signature string
*
* @return constexpr const decltype(m_Signature)&
*/
inline constexpr const decltype(m_Signature)& Get() const
{
return m_Signature;
}
/**
* @brief Returns the Length of the original signature string
*
* @return constexpr std::size_t
*/
constexpr std::size_t Length() const
{
return N;
}
/**
* @brief Returns the actual length in bytes for the signature
*
* @return constexpr std::size_t
*/
constexpr std::size_t ByteLength() const
{
return m_SignatureByteLength;
}
};
class IPattern
{
public:
constexpr IPattern() = default;
virtual ~IPattern() = default;
virtual const std::string_view Name() const = 0;
virtual constexpr std::span<const std::optional<std::uint8_t>> Signature() const = 0;
};
template<Signature S>
class Pattern final : public IPattern
{
private:
const std::string_view m_Name;
std::array<std::optional<std::uint8_t>, S.ByteLength()> m_Signature;
public:
constexpr Pattern(const std::string_view name);
inline virtual const std::string_view Name() const override
{
return m_Name;
}
inline virtual constexpr std::span<const std::optional<std::uint8_t>> Signature() const override
{
return m_Signature;
}
friend std::ostream& operator<< <>(std::ostream& os, const Pattern<S>& signature);
};
template<Signature S>
inline constexpr Pattern<S>::Pattern(const std::string_view name) :
IPattern(),
m_Name(name)
{
for (size_t i = 0, pos = 0; i < S.Length(); i++)
{
const auto c = S.Get()[i];
if (c == ' ')
continue;
if (c == '\0')
break;
if (S.Get()[i] == '?')
{
if (S.Get()[i + 1] == '?')
i++;
m_Signature[pos++] = std::nullopt;
continue;
}
m_Signature[pos++] = static_cast<std::uint8_t>(StrToHex(S.Get()[i]) * 0x10 + StrToHex(S.Get()[++i]));
}
}
template<Signature S>
inline std::ostream& operator<<(std::ostream& os, const Pattern<S>& pattern)
{
os << pattern.Name() << ": { ";
for (const auto byte : pattern.Signature())
{
if (byte == std::nullopt)
{
os << "?? ";
continue;
}
os << std::hex << std::uppercase << DWORD64(byte.value()) << std::nouppercase << std::dec << ' ';
}
os << "}";
return os;
}
}
+73
View File
@@ -0,0 +1,73 @@
#include "PatternScanner.hpp"
#include "Module.hpp"
#include <future>
namespace NewBase
{
PatternScanner::PatternScanner(const Module* module) :
m_Module(module),
m_Patterns()
{
}
bool PatternScanner::Scan()
{
if (!m_Module || !m_Module->Valid())
return false;
std::vector<std::future<bool>> jobs;
for (const auto& [pattern, func] : m_Patterns)
{
jobs.emplace_back(std::async(&PatternScanner::ScanInternal, this, pattern, func));
}
bool scanSuccess = true;
for (auto& job : jobs)
{
job.wait();
if (scanSuccess)
scanSuccess = job.get();
}
if (!scanSuccess)
{
LOG(FATAL) << "Some patterns have not been found, continuing would be foolish.";
}
return scanSuccess;
}
bool PatternScanner::ScanInternal(const IPattern* pattern, PatternFunc func) const
{
const auto signature = pattern->Signature();
for (auto i = m_Module->Base(); i < m_Module->End(); ++i)
{
if (signature.size() + i > m_Module->End())
break;
const auto instruction = reinterpret_cast<std::uint8_t*>(i);
bool found = true;
for (std::size_t instructionIdx = 0; instructionIdx < signature.size(); ++instructionIdx)
{
if (signature[instructionIdx] && signature[instructionIdx].value() != instruction[instructionIdx])
{
found = false;
}
}
if (found)
{
LOG(INFO) << "Found pattern [" << pattern->Name() << "] : [" << HEX(i) << "]";
std::invoke(func, i);
return true;
}
}
LOG(WARNING) << "Failed to find pattern [" << pattern->Name() << "]";
return false;
}
}
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include "Pattern.hpp"
#include "PointerCalculator.hpp"
#include <functional>
#include <vector>
namespace NewBase
{
class Module;
using PatternFunc = std::function<void(PointerCalculator)>;
class PatternScanner
{
private:
const Module* m_Module;
std::vector<std::pair<const IPattern*, PatternFunc>> m_Patterns;
public:
PatternScanner(const Module* module);
template<Signature S>
void Add(const Pattern<S>& pattern, const PatternFunc& func);
bool Scan();
private:
bool ScanInternal(const IPattern* pattern, PatternFunc func) const;
};
template<Signature S>
inline void PatternScanner::Add(const Pattern<S>& pattern, const PatternFunc& func)
{
m_Patterns.push_back(std::move(std::make_pair(&pattern, func)));
}
}
+106
View File
@@ -0,0 +1,106 @@
#pragma once
#include <cstdint>
#include <type_traits>
namespace NewBase
{
class PointerCalculator final
{
private:
std::uintptr_t m_InternalPtr;
public:
inline PointerCalculator(void* ptr = nullptr);
inline PointerCalculator(std::uintptr_t ptr);
template<typename T>
inline PointerCalculator(T* ptr);
virtual ~PointerCalculator() = default;
template<typename T>
std::enable_if_t<std::is_pointer_v<T>, T> As() const;
template<typename T>
std::enable_if_t<std::is_lvalue_reference_v<T>, T> As() const;
template<typename T>
std::enable_if_t<std::is_same_v<T, std::uintptr_t>, T> As() const;
template<typename T>
PointerCalculator Add(T offset) const;
template<typename T>
PointerCalculator Sub(T offset) const;
PointerCalculator Rip() const;
explicit operator bool() const;
friend bool operator==(PointerCalculator& a, PointerCalculator& b);
friend bool operator!=(PointerCalculator& a, PointerCalculator& b);
};
inline PointerCalculator::PointerCalculator(void* ptr) :
m_InternalPtr(reinterpret_cast<std::uintptr_t>(ptr))
{
}
inline PointerCalculator::PointerCalculator(std::uintptr_t ptr) :
m_InternalPtr(ptr)
{
}
template<typename T>
inline PointerCalculator::PointerCalculator(T* ptr) :
m_InternalPtr(reinterpret_cast<std::uintptr_t>(ptr))
{
}
template<typename T>
inline std::enable_if_t<std::is_pointer_v<T>, T> PointerCalculator::As() const
{
return reinterpret_cast<T>(m_InternalPtr);
}
template<typename T>
inline std::enable_if_t<std::is_lvalue_reference_v<T>, T> PointerCalculator::As() const
{
return *reinterpret_cast<std::add_pointer_t<std::remove_reference_t<T>>>(m_InternalPtr);
}
template<typename T>
inline std::enable_if_t<std::is_same_v<T, std::uintptr_t>, T> PointerCalculator::As() const
{
return m_InternalPtr;
}
template<typename T>
inline PointerCalculator PointerCalculator::Add(T offset) const
{
return {m_InternalPtr + offset};
}
template<typename T>
inline PointerCalculator PointerCalculator::Sub(T offset) const
{
return {m_InternalPtr - offset};
}
inline PointerCalculator PointerCalculator::Rip() const
{
return Add(As<std::int32_t&>()).Add(4);
}
inline PointerCalculator::operator bool() const
{
return m_InternalPtr != 0;
}
inline bool operator==(PointerCalculator& a, PointerCalculator& b)
{
return a.m_InternalPtr == b.m_InternalPtr;
}
inline bool operator!=(PointerCalculator& a, PointerCalculator& b)
{
return a.m_InternalPtr != b.m_InternalPtr;
}
}
+112
View File
@@ -0,0 +1,112 @@
#include "Renderer.hpp"
#include "game/pointers/Pointers.hpp"
#include <backends/imgui_impl_dx11.h>
#include <backends/imgui_impl_win32.h>
#include <imgui.h>
namespace NewBase
{
Renderer::Renderer()
{
}
Renderer::~Renderer()
{
}
void Renderer::DestroyImpl()
{
if (m_SwapChain && m_Device && m_DeviceContext)
{
ImGui_ImplDX11_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
}
}
bool Renderer::InitImpl()
{
if (!Pointers.SwapChain)
{
LOG(WARNING) << "SwapChain pointer is invalid!";
return false;
}
if (m_SwapChain = *Pointers.SwapChain; !m_SwapChain)
{
LOG(WARNING) << "Dereferenced SwapChain pointer is invalid!";
return false;
}
if (const auto result = m_SwapChain->GetDevice(__uuidof(ID3D11Device), reinterpret_cast<void**>(&m_Device)); result < 0)
{
LOG(WARNING) << "Failed to get D3D Device with result: [" << result << "]";
return false;
}
m_Device->GetImmediateContext(&m_DeviceContext);
ImGui::CreateContext();
ImGui::StyleColorsDark();
// never returns false, useless to check return
ImGui_ImplDX11_Init(m_Device, m_DeviceContext);
ImGui_ImplWin32_Init(Pointers.Hwnd);
LOG(INFO) << "Renderer has finished initializing.";
return true;
}
bool Renderer::AddDXCallbackImpl(DXCallback&& callback, std::uint32_t priority)
{
return m_DXCallbacks.insert({priority, callback}).second;
}
void Renderer::AddWindowProcedureCallbackImpl(WindowProcedureCallback&& callback)
{
return m_WindowProcedureCallbacks.push_back(callback);
}
void Renderer::OnPresentImpl()
{
Renderer::NewFrame();
for (const auto& callback : m_DXCallbacks | std::views::values)
callback();
Renderer::EndFrame();
}
LRESULT Renderer::WndProcImpl(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
for (const auto& callback : m_WindowProcedureCallbacks)
callback(hwnd, msg, wparam, lparam);
return ImGui_ImplWin32_WndProcHandler(hwnd, msg, wparam, lparam);
}
void Renderer::PreResize()
{
ImGui_ImplDX11_InvalidateDeviceObjects();
}
void Renderer::PostResize()
{
ImGui_ImplDX11_CreateDeviceObjects();
}
void Renderer::NewFrame()
{
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
}
void Renderer::EndFrame()
{
ImGui::Render();
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
}
}
+102
View File
@@ -0,0 +1,102 @@
#pragma once
#include "util/Joaat.hpp"
#include <d3d11.h>
#include <functional>
#include <map>
#include <windows.h>
namespace NewBase
{
using DXCallback = std::function<void()>;
using WindowProcedureCallback = std::function<void(HWND, UINT, WPARAM, LPARAM)>;
class Renderer final
{
private:
Renderer();
public:
~Renderer();
Renderer(const Renderer&) = delete;
Renderer(Renderer&&) noexcept = delete;
Renderer& operator=(const Renderer&) = delete;
Renderer& operator=(Renderer&&) noexcept = delete;
static void Destroy()
{
GetInstance().DestroyImpl();
}
static bool Init()
{
return GetInstance().InitImpl();
}
/**
* @brief Add a callback function to draw using ImGui
*
* @param callback Callback function
* @param priority Low values will be drawn before higher values.
* @return true Successfully added callback.
* @return false Duplicate render priority was given.
*/
static bool AddDXCallback(DXCallback&& callback, std::uint32_t priority)
{
return GetInstance().AddDXCallbackImpl(std::move(callback), priority);
}
/**
* @brief Add a callback function to handle Windows WindowProcedure
*
* @param callback Callback function
*/
static void AddWindowProcedureCallback(WindowProcedureCallback&& callback)
{
GetInstance().AddWindowProcedureCallbackImpl(std::move(callback));
}
static void OnPresent()
{
GetInstance().OnPresentImpl();
}
static LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
return GetInstance().WndProcImpl(hwnd, msg, wparam, lparam);
}
static void PreResize();
static void PostResize();
private:
static void NewFrame();
static void EndFrame();
private:
void DestroyImpl();
bool InitImpl();
bool AddDXCallbackImpl(DXCallback&& callback, std::uint32_t priority);
void AddWindowProcedureCallbackImpl(WindowProcedureCallback&& callback);
void OnPresentImpl();
LRESULT WndProcImpl(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
static Renderer& GetInstance()
{
static Renderer i{};
return i;
}
private:
ID3D11Device* m_Device;
ID3D11DeviceContext* m_DeviceContext;
IDXGISwapChain* m_SwapChain;
std::map<joaat_t, DXCallback> m_DXCallbacks;
std::vector<WindowProcedureCallback> m_WindowProcedureCallbacks;
};
}
// Make our linker aware of the ImGui WndProcHandler
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+44
View File
@@ -0,0 +1,44 @@
#include "GUI.hpp"
#include "menu/Menu.hpp"
#include "core/renderer/Renderer.hpp"
namespace NewBase
{
GUI::GUI() :
m_IsOpen(false)
{
Renderer::AddDXCallback(Menu::Main, -1);
Renderer::AddWindowProcedureCallback([this](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
GUI::WndProc(hwnd, msg, wparam, lparam);
});
}
GUI::~GUI()
{
}
void GUI::ToggleMouse()
{
auto& io = ImGui::GetIO();
if (GUI::IsOpen())
{
io.MouseDrawCursor = true;
io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse;
}
else
{
io.MouseDrawCursor = false;
io.ConfigFlags |= ImGuiConfigFlags_NoMouse;
}
}
void GUI::WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
if (msg == WM_KEYUP && wparam == VK_INSERT)
{
m_IsOpen = !m_IsOpen;
ToggleMouse();
}
}
}
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include <windows.h>
namespace NewBase
{
class GUI final
{
private:
GUI();
public:
~GUI();
GUI(const GUI&) = delete;
GUI(GUI&&) noexcept = delete;
GUI& operator=(const GUI&) = delete;
GUI& operator=(GUI&&) noexcept = delete;
static void Init()
{
GetInstance();
}
static bool IsOpen()
{
return GetInstance().m_IsOpen;
}
private:
void WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
static void ToggleMouse();
static GUI& GetInstance()
{
static GUI i{};
return i;
}
private:
bool m_IsOpen;
};
}
+17
View File
@@ -0,0 +1,17 @@
#include "Menu.hpp"
namespace NewBase
{
void Menu::Main()
{
if (!GUI::IsOpen())
return;
if (ImGui::Begin("Test"))
{
if (ImGui::Button("Unload"))
g_Running = false;
}
ImGui::End();
}
}
+7
View File
@@ -0,0 +1,7 @@
#pragma once
#include "game/frontend/GUI.hpp"
namespace NewBase::Menu
{
extern void Main();
}
+27
View File
@@ -0,0 +1,27 @@
#include "core/hooking/VMTHook.hpp"
#include "core/renderer/Renderer.hpp"
#include "game/hooks/Hooks.hpp"
namespace NewBase
{
HRESULT SwapChain::Present(IDXGISwapChain* that, UINT syncInterval, UINT flags)
{
if (g_Running)
{
Renderer::OnPresent();
}
return BaseHook::Get<SwapChain::Present, VMTHook<SwapChain::VMTSize>>()->Original<decltype(&Present)>(SwapChain::VMTPresentIdx)(that, syncInterval, flags);
}
HRESULT SwapChain::ResizeBuffers(IDXGISwapChain* that, UINT bufferCount, UINT width, UINT height, DXGI_FORMAT newFormat, UINT swapChainFlags)
{
if (g_Running)
{
Renderer::PreResize();
const auto result = BaseHook::Get<SwapChain::Present, VMTHook<SwapChain::VMTSize>>()->Original<decltype(&ResizeBuffers)>(SwapChain::VMTResizeBuffersIdx)(that, bufferCount, width, height, newFormat, swapChainFlags);
Renderer::PostResize();
return result;
}
return BaseHook::Get<SwapChain::Present, VMTHook<SwapChain::VMTSize>>()->Original<decltype(&ResizeBuffers)>(SwapChain::VMTResizeBuffersIdx)(that, bufferCount, width, height, newFormat, swapChainFlags);
}
}
+18
View File
@@ -0,0 +1,18 @@
#include "core/hooking/DetourHook.hpp"
#include "core/renderer/Renderer.hpp"
#include "game/hooks/Hooks.hpp"
namespace NewBase
{
LRESULT Window::WndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
if (g_Running)
Renderer::WndProc(hwnd, umsg, wparam, lparam);
if (umsg == WM_KEYUP && wparam == VK_DELETE) // fallback to unload
g_Running = false;
return BaseHook::Get<Window::WndProc, DetourHook<WNDPROC>>()->Original()(hwnd, umsg, wparam, lparam);
}
}
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <d3d11.h>
namespace NewBase
{
namespace SwapChain
{
constexpr auto VMTPresentIdx = 8;
constexpr auto VMTResizeBuffersIdx = 13;
constexpr auto VMTSize = 19;
extern HRESULT Present(IDXGISwapChain* that, UINT syncInterval, UINT flags);
extern HRESULT ResizeBuffers(IDXGISwapChain* that, UINT bufferCount, UINT width, UINT height, DXGI_FORMAT newFormat, UINT swapChainFlags);
}
namespace Window
{
extern LRESULT WndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam);
}
}
+55
View File
@@ -0,0 +1,55 @@
#include "Pointers.hpp"
#include "core/memory/BytePatch.hpp"
#include "core/memory/ModuleMgr.hpp"
#include "core/memory/PatternScanner.hpp"
#include "util/Joaat.hpp"
namespace NewBase
{
bool Pointers::Init()
{
const auto gta5 = ModuleMgr.Get("GTA5.exe"_J);
if (!gta5)
{
LOG(FATAL) << "Could not find " << gta5->Name() << ", is this GTA5?";
return false;
}
auto scanner = PatternScanner(gta5);
constexpr auto swapchainPtrn = Pattern<"48 8B 0D ? ? ? ? 48 8B 01 44 8D 43 01 33 D2 FF 50 40 8B C8">("IDXGISwapChain");
scanner.Add(swapchainPtrn, [this](PointerCalculator ptr) {
SwapChain = ptr.Add(3).Rip().As<IDXGISwapChain**>();
});
constexpr auto wndProcPtrn = Pattern<"48 8B C4 48 89 58 08 4C 89 48 20 55 56 57 41 54 41 55 41 56 41 57 48 8D 68 A1 48 81 EC F0">("WNDPROC");
scanner.Add(wndProcPtrn, [this](PointerCalculator ptr) {
WndProc = ptr.As<WNDPROC>();
});
constexpr auto debugTrap = Pattern<"48 83 EC 28 33 C9 FF 15 ? ? ? ? 45 33 C9">("DebugTrap");
scanner.Add(debugTrap, [this](PointerCalculator ptr) {
BytePatch::Make(ptr.As<void*>(), std::to_array({ 0xC3, 0x90, 0x90, 0x90 }))->Apply();
UnhookWindowsHookEx(*ptr.Add(45).Rip().As<HHOOK*>());
});
if (!scanner.Scan())
{
LOG(FATAL) << "Some patterns could not be found, unloading.";
return false;
}
if (Hwnd = FindWindow("grcWindow", nullptr); !Hwnd)
{
LOG(FATAL) << "Failed to grab game window, unloading.";
return false;
}
return true;
}
}
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <d3d11.h>
#include <windows.h>
namespace NewBase
{
struct PointerData
{
IDXGISwapChain** SwapChain;
HWND Hwnd;
WNDPROC WndProc;
};
struct Pointers : PointerData
{
bool Init();
};
inline NewBase::Pointers Pointers;
}
+58
View File
@@ -0,0 +1,58 @@
#include "common.hpp"
#include "core/filemgr/FileMgr.hpp"
#include "core/hooking/Hooking.hpp"
#include "core/memory/ModuleMgr.hpp"
#include "core/renderer/Renderer.hpp"
#include "game/frontend/GUI.hpp"
#include "game/pointers/Pointers.hpp"
namespace NewBase
{
DWORD Main(void*)
{
const auto documents = std::filesystem::path(std::getenv("USERPROFILE")) / "Documents";
FileMgr::Init(documents / "HellBase");
LogHelper::Init("henlo", FileMgr::GetProjectFile("./cout.log").Path());
if (!ModuleMgr.LoadModules())
goto unload;
if (!Pointers.Init())
goto unload;
if (!Renderer::Init())
goto unload;
GUI::Init();
Hooking::Init();
while (g_Running)
{
std::this_thread::sleep_for(100ms);
}
unload:
Hooking::Destroy();
Renderer::Destroy();
LogHelper::Destroy();
CloseHandle(g_MainThread);
FreeLibraryAndExitThread(g_DllInstance, EXIT_SUCCESS);
return EXIT_SUCCESS;
}
}
BOOL WINAPI DllMain(HINSTANCE dllInstance, DWORD reason, void*)
{
using namespace NewBase;
DisableThreadLibraryCalls(dllInstance);
if (reason == DLL_PROCESS_ATTACH)
{
g_DllInstance = dllInstance;
g_MainThread = CreateThread(nullptr, 0, Main, nullptr, 0, &g_MainThreadId);
}
return true;
}
+19
View File
@@ -0,0 +1,19 @@
#include "Joaat.hpp"
namespace NewBase
{
constexpr joaat_t Joaat(const std::string_view str)
{
joaat_t hash = 0;
for (auto c : str)
{
hash += ToLower(c);
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}
}
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include <cstdint>
#include <string_view>
namespace NewBase
{
using joaat_t = std::uint32_t;
inline constexpr char ToLower(char c)
{
return c >= 'A' && c <= 'Z' ? c | 1 << 5 : c;
}
extern constexpr joaat_t Joaat(const std::string_view str);
inline consteval joaat_t operator""_J(const char* s, std::size_t n)
{
joaat_t result = 0;
for (std::size_t i = 0; i < n; i++)
{
result += ToLower(s[i]);
result += (result << 10);
result ^= (result >> 6);
}
result += (result << 3);
result ^= (result >> 11);
result += (result << 15);
return result;
}
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <cstdint>
namespace NewBase
{
inline constexpr std::uint8_t StrToHex(const char& ch) noexcept
{
if (ch >= '0' && ch <= '9')
return ch - '0';
if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
return ch - 'a' + 10;
}
}