minor changes

- cheat-base:
add support dx12
add several utility modifications
- cheat-library:
refactored sniffer. Check: https://github.com/Akebi-Group/Akebi-PacketSniffer
This commit is contained in:
Callow 2022-07-07 02:44:47 +03:00
parent 90fca5d656
commit e40232f757
44 changed files with 1298 additions and 612 deletions

View File

@ -125,7 +125,7 @@
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir)framework\;$(ProjectDir)src\;$(ProjectDir)vendor\detours\;$(ProjectDir)vendor\fmt\include\;$(ProjectDir)vendor\imgui\;$(ProjectDir)vendor\magic_enum\include\;$(ProjectDir)vendor\simpleIni\;$(ProjectDir)vendor\json\single_include\;$(ProjectDir)vendor\imgui-notify-v2\;$(ProjectDir)vendor\stb\</AdditionalIncludeDirectories>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<LanguageStandard_C>Default</LanguageStandard_C>
</ClCompile>
<Link>
<SubSystem>
@ -158,7 +158,7 @@
<Optimization>MaxSpeed</Optimization>
<WholeProgramOptimization>true</WholeProgramOptimization>
<OmitFramePointers>false</OmitFramePointers>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<LanguageStandard_C>Default</LanguageStandard_C>
</ClCompile>
<Link>
<SubSystem>
@ -193,7 +193,7 @@
<Optimization>MaxSpeed</Optimization>
<WholeProgramOptimization>true</WholeProgramOptimization>
<OmitFramePointers>false</OmitFramePointers>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<LanguageStandard_C>Default</LanguageStandard_C>
</ClCompile>
<Link>
<SubSystem>
@ -222,7 +222,7 @@
<PreprocessorDefinitions>_DEBUG;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS;_CRT_SECURE_NO_WARNINGS;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<LanguageStandard_C>Default</LanguageStandard_C>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<SupportJustMyCode>false</SupportJustMyCode>
<BufferSecurityCheck>false</BufferSecurityCheck>
@ -242,7 +242,7 @@
<PreprocessorDefinitions>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS;_CRT_SECURE_NO_WARNINGS;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<LanguageStandard_C>Default</LanguageStandard_C>
</ClCompile>
<Lib>
<AdditionalDependencies>detours-$(PlatformShortName).lib</AdditionalDependencies>
@ -259,7 +259,7 @@
<PreprocessorDefinitions>_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS;_CRT_SECURE_NO_WARNINGS;_MBCS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard_C>stdc17</LanguageStandard_C>
<LanguageStandard_C>Default</LanguageStandard_C>
</ClCompile>
<Lib>
<AdditionalDependencies>detours-$(PlatformShortName).lib</AdditionalDependencies>
@ -298,6 +298,8 @@
<ClInclude Include="src\cheat-base\events\joins\handlereventjoin.hpp" />
<ClInclude Include="src\cheat-base\inject\load-library.h" />
<ClInclude Include="src\cheat-base\inject\manual-map.h" />
<ClInclude Include="src\cheat-base\ISerializable.h" />
<ClInclude Include="src\cheat-base\render\backend\dx12-hook.h" />
<ClInclude Include="src\cheat-base\ResourceLoader.h" />
<ClInclude Include="src\cheat-base\Hotkey.h" />
<ClInclude Include="src\cheat-base\render\ImageLoader.h" />
@ -335,6 +337,7 @@
<ClInclude Include="vendor\imgui-notify-v2\font_awesome_5.h" />
<ClInclude Include="vendor\imgui-notify-v2\imgui_notify.h" />
<ClInclude Include="vendor\imgui\backends\imgui_impl_dx11.h" />
<ClInclude Include="vendor\imgui\backends\imgui_impl_dx12.h" />
<ClInclude Include="vendor\imgui\backends\imgui_impl_win32.h" />
<ClInclude Include="vendor\imgui\imconfig.h" />
<ClInclude Include="vendor\imgui\imgui.h" />
@ -365,6 +368,7 @@
</ClCompile>
<ClCompile Include="src\cheat-base\inject\load-library.cpp" />
<ClCompile Include="src\cheat-base\inject\manual-map.cpp" />
<ClCompile Include="src\cheat-base\render\backend\dx12-hook.cpp" />
<ClCompile Include="src\cheat-base\ResourceLoader.cpp" />
<ClCompile Include="src\cheat-base\Hotkey.cpp" />
<ClCompile Include="src\cheat-base\render\ImageLoader.cpp" />
@ -410,6 +414,14 @@
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release_WS|Win32'">pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="vendor\imgui\backends\imgui_impl_dx12.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release_WS|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="vendor\imgui\backends\imgui_impl_win32.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">NotUsing</PrecompiledHeader>

View File

@ -243,6 +243,15 @@
<ClInclude Include="src\cheat-base\inject\load-library.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\ISerializable.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cheat-base\render\backend\dx12-hook.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="vendor\imgui\backends\imgui_impl_dx12.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\cheat-base\util.cpp">
@ -335,5 +344,11 @@
<ClCompile Include="src\cheat-base\inject\load-library.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cheat-base\render\backend\dx12-hook.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="vendor\imgui\backends\imgui_impl_dx12.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -17,7 +17,7 @@ public:
}
template <typename Fn>
[[nodiscard]] static Fn getOrigin(Fn handler, const char* callerName = nullptr) noexcept
static Fn getOrigin(Fn handler, const char* callerName = nullptr) noexcept
{
if (holderMap.count(reinterpret_cast<void*>(handler)) == 0) {
LOG_WARNING("Origin not found for handler: %s. Maybe racing bug.", callerName == nullptr ? "<Unknown>" : callerName);
@ -27,14 +27,17 @@ public:
}
template <typename Fn>
[[nodiscard]] static void detach(Fn handler) noexcept
static void detach(Fn handler) noexcept
{
disable(handler);
holderMap.erase(reinterpret_cast<void*>(handler));
}
// I don't know why
#ifdef _WIN64
template <typename RType, typename... Params>
[[nodiscard]] static RType call(RType(*handler)(Params...), const char* callerName = nullptr, Params... params)
static RType call(RType(*handler)(Params...), const char* callerName = nullptr, Params... params)
{
auto origin = getOrigin(handler, callerName);
if (origin != nullptr)
@ -43,6 +46,30 @@ public:
return RType();
}
#else
template <typename RType, typename... Params>
static RType call(RType(__cdecl *handler)(Params...), const char* callerName = nullptr, Params... params)
{
auto origin = getOrigin(handler, callerName);
if (origin != nullptr)
return origin(params...);
return RType();
}
template <typename RType, typename... Params>
static RType call(RType(__stdcall *handler)(Params...), const char* callerName = nullptr, Params... params)
{
auto origin = getOrigin(handler, callerName);
if (origin != nullptr)
return origin(params...);
return RType();
}
#endif
static void detachAll() noexcept
{
for (const auto &[key, value] : holderMap)

View File

@ -0,0 +1,20 @@
#pragma once
#include <nlohmann/json.hpp>
class ISerializable
{
public:
virtual void to_json(nlohmann::json& j) const = 0;
virtual void from_json(const nlohmann::json& j) = 0;
};
inline void to_json(nlohmann::json& j, const ISerializable& ser)
{
ser.to_json(j);
}
inline void from_json(const nlohmann::json& j, ISerializable& ser)
{
ser.from_json(j);
}

View File

@ -16,18 +16,22 @@ PipeTransfer::PipeTransfer(const std::string& name)
PipeTransfer::~PipeTransfer()
{
if (m_Pipe)
CloseHandle(m_Pipe);
Close();
}
bool PipeTransfer::Create()
{
if (m_Pipe)
CloseHandle(m_Pipe);
Close();
m_Pipe = CreateNamedPipe(m_Name.c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 256 * 1024, 16, INFINITE, NULL);
return IsPipeOpened();
}
void PipeTransfer::Close()
{
if (m_Pipe)
CloseHandle(m_Pipe);
}
bool PipeTransfer::IsPipeOpened()
{
return m_Pipe && m_Pipe != INVALID_HANDLE_VALUE;

View File

@ -20,6 +20,7 @@ public:
~PipeTransfer();
bool Create();
void Close();
bool Connect();
bool WaitForConnection();
bool IsPipeOpened();

View File

@ -10,9 +10,9 @@
namespace cheat
{
void CheatManagerBase::Init(LPBYTE pFontData, DWORD dFontDataSize)
void CheatManagerBase::Init(LPBYTE pFontData, DWORD dFontDataSize, renderer::DXVersion dxVersion)
{
renderer::Init(pFontData, dFontDataSize);
renderer::Init(pFontData, dFontDataSize, dxVersion);
events::RenderEvent += MY_METHOD_HANDLER(CheatManagerBase::OnRender);
events::KeyUpEvent += MY_METHOD_HANDLER(CheatManagerBase::OnKeyUp);

View File

@ -2,6 +2,7 @@
#include <cheat-base/cheat/Feature.h>
#include <cheat-base/config/Config.h>
#include <cheat-base/events/event.hpp>
#include <cheat-base/render/renderer.h>
#include <vector>
#include <map>
@ -30,7 +31,7 @@ namespace cheat
void OnWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool& cancelled);
void OnRender();
void Init(LPBYTE pFontData, DWORD dFontDataSize);
void Init(LPBYTE pFontData, DWORD dFontDataSize, renderer::DXVersion dxVersion = renderer::DXVersion::D3D11);
virtual void CursorSetVisibility(bool visibility) = 0;
virtual bool CursorGetVisibility() = 0;

View File

@ -6,7 +6,11 @@
#include "fields/Toggle.h"
#include "fields/Enum.h"
#define NFEX(field, friendName, name, section, defaultValue, shared) field##(config::CreateField<decltype(##field##)::_ValueType>(friendName, name, section, shared, defaultValue))
#define SNFEX(field, friendName, name, section, defaultValue, shared) config::CreateField<decltype(##field##)::_ValueType>(friendName, name, section, shared, defaultValue)
#define SNFB(field, name, section, defaultValue, shared) SNFEX(field, name, config::internal::FixFieldName(#field), section, defaultValue, shared)
#define SNF(field, name, section, defaultValue) SNFB(field, name, section, defaultValue, false)
#define NFEX(field, friendName, name, section, defaultValue, shared) field##(SNFEX(field, friendName, name, section, defaultValue, shared))
#define NFEXUP(field, friendName, name, section, shared, ...) field##(config::CreateField<decltype(##field##)::_ValueType>(friendName, name, section, shared, __VA_ARGS__))
#define NFB(field, name, section, defaultValue, shared) NFEX(field, name, config::internal::FixFieldName(#field), section, defaultValue, shared)

View File

@ -112,6 +112,13 @@ namespace config::internal
return *this;
}
FieldBase<T>& operator=(T&& other)
{
p_Container->m_Value = std::move(other);
p_Container->FireChanged();
return *this;
}
FieldBase<T>& operator=(std::shared_ptr<FieldSerialize<T>>& other)
{
p_Container->ChangedEvent -= MY_METHOD_HANDLER(FieldBase<T>::OnFieldChanged);

View File

@ -5,6 +5,18 @@
namespace config::internal
{
namespace CHECK
{
struct No {};
template<typename T, typename Arg> No operator== (const T&, const Arg&);
template<typename T, typename Arg = T>
struct EqualExists
{
enum { value = !std::is_same<decltype(std::declval<T>() == std::declval<Arg>()), No>::value };
};
}
template<typename T>
class FieldSerialize : public FieldEntry
{
@ -14,8 +26,12 @@ namespace config::internal
nlohmann::json ToJson() override
{
if (m_Value == m_DefaultValue)
return {};
if constexpr (CHECK::EqualExists<T>::value)
{
if (m_Value == m_DefaultValue)
return {};
}
return converters::ToJson(m_Value);
}

View File

@ -4,7 +4,6 @@
#include <cstdio>
#include <cheat-base/HookManager.h>
#pragma comment(lib, "D3dcompiler.lib")
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "winmm.lib")
@ -18,37 +17,55 @@ extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam
static IDXGISwapChainPresent fnIDXGISwapChainPresent;
static ID3D11Device* pDevice = nullptr;
static HRESULT __stdcall Present(IDXGISwapChain* pChain, const UINT SyncInterval, const UINT Flags)
static HRESULT __stdcall Present_Hook(IDXGISwapChain* pChain, const UINT SyncInterval, const UINT Flags)
{
static BOOL g_bInitialised = false;
// Main D3D11 Objects
static ID3D11DeviceContext* pContext = nullptr;
if (!g_bInitialised) {
pChain->GetDevice(__uuidof(pDevice), reinterpret_cast<void**>(&pDevice));
pDevice->GetImmediateContext(&pContext);
if (!g_bInitialised)
{
auto result = (HRESULT)pChain->GetDevice(__uuidof(pDevice), reinterpret_cast<void**>(&pDevice));
DXGI_SWAP_CHAIN_DESC sd;
pChain->GetDesc(&sd);
if (SUCCEEDED(result))
{
pDevice->GetImmediateContext(&pContext);
backend::DX11Events::InitializeEvent(sd.OutputWindow, pDevice, pContext, pChain);
DXGI_SWAP_CHAIN_DESC sd;
pChain->GetDesc(&sd);
g_bInitialised = true;
backend::DX11Events::InitializeEvent(sd.OutputWindow, pDevice, pContext, pChain);
g_bInitialised = true;
}
}
// render function
backend::DX11Events::RenderEvent(pContext);
if (g_bInitialised)
backend::DX11Events::RenderEvent(pContext);
return CALL_ORIGIN(Present, pChain, SyncInterval, Flags);
return CALL_ORIGIN(Present_Hook, pChain, SyncInterval, Flags);
}
static IDXGISwapChainPresent findDirect11Present()
{
const HWND hWnd = GetForegroundWindow();
WNDCLASSEX wc{ 0 };
wc.cbSize = sizeof(wc);
wc.lpfnWndProc = DefWindowProc;
wc.lpszClassName = TEXT("Class");
if (!RegisterClassEx(&wc))
{
return nullptr;
}
HWND hWnd = CreateWindow(wc.lpszClassName, TEXT(""), WS_DISABLED, 0, 0, 0, 0, NULL, NULL, NULL, nullptr);
IDXGISwapChain* pSwapChain;
constexpr D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0;
D3D_FEATURE_LEVEL featureLevel;
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));
swapChainDesc.BufferCount = 1;
@ -64,11 +81,15 @@ static IDXGISwapChainPresent findDirect11Present()
// Main D3D11 Objects
ID3D11DeviceContext* pContext = nullptr;
ID3D11Device* pDevice = nullptr;
if (FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_WARP, NULL, NULL, &featureLevel, 1,
D3D11_SDK_VERSION, &swapChainDesc, &pSwapChain, &pDevice, NULL, &pContext)) &&
FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, &featureLevel, 1,
D3D11_SDK_VERSION, &swapChainDesc, &pSwapChain, &pDevice, NULL, &pContext)))
if (/*FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_WARP, NULL, NULL, &featureLevel, 1,
D3D11_SDK_VERSION, &swapChainDesc, &pSwapChain, &pDevice, NULL, &pContext)) &&*/
FAILED(D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0, D3D11_SDK_VERSION,
&swapChainDesc, &pSwapChain, &pDevice, &featureLevel, nullptr)))
{
DestroyWindow(swapChainDesc.OutputWindow);
UnregisterClass(wc.lpszClassName, GetModuleHandle(nullptr));
return nullptr;
}
@ -78,9 +99,12 @@ static IDXGISwapChainPresent findDirect11Present()
auto swapChainPresent = reinterpret_cast<IDXGISwapChainPresent>(pSwapChainVtable[8]);
pDevice->Release();
pContext->Release();
//pContext->Release();
pSwapChain->Release();
DestroyWindow(swapChainDesc.OutputWindow);
UnregisterClass(wc.lpszClassName, GetModuleHandle(nullptr));
return swapChainPresent;
}
@ -95,7 +119,7 @@ void backend::InitializeDX11Hooks()
}
LOG_DEBUG("SwapChain Present: %p", fnIDXGISwapChainPresent);
HookManager::install(fnIDXGISwapChainPresent, Present);
HookManager::install(fnIDXGISwapChainPresent, Present_Hook);
LOG_DEBUG("Initializing D3D11 hook: done.");
}

View File

@ -2,6 +2,8 @@
#include <Windows.h>
#include <d3d11.h>
#include <d3d12.h>
#include <d3dcompiler.h>
#include <cheat-base/events/event.hpp>
@ -18,5 +20,6 @@ namespace backend
public:
inline static TEvent<ID3D11DeviceContext*> RenderEvent{};
inline static TEvent<HWND, ID3D11Device*, ID3D11DeviceContext*, IDXGISwapChain*> InitializeEvent{};
inline static TEvent<> FailedEvent{};
};
}

View File

@ -0,0 +1,515 @@
#include <pch.h>
#include "dx12-hook.h"
#include <cstdio>
#include <cstdint>
#include <cheat-base/HookManager.h>
#include <dxgi1_4.h>
#pragma comment(lib, "D3dcompiler.lib")
#pragma comment(lib, "d3d12.lib")
#pragma comment(lib, "winmm.lib")
// D3X HOOK DEFINITIONS
typedef HRESULT(APIENTRY* IDXGISwapChainPresent)(IDXGISwapChain3* pSwapChain, UINT SyncInterval, UINT Flags);
typedef void(APIENTRY* DrawInstanced)(ID3D12GraphicsCommandList* dCommandList, UINT VertexCountPerInstance, UINT InstanceCount, UINT StartVertexLocation, UINT StartInstanceLocation);
typedef void(APIENTRY* DrawIndexedInstanced)(ID3D12GraphicsCommandList* dCommandList, UINT IndexCountPerInstance, UINT InstanceCount, UINT StartIndexLocation, INT BaseVertexLocation, UINT StartInstanceLocation);
typedef void(APIENTRY* ExecuteCommandLists)(ID3D12CommandQueue* queue, UINT NumCommandLists, ID3D12CommandList* ppCommandLists);
struct DirectX12MainFunctions
{
IDXGISwapChainPresent presentFunc;
DrawInstanced drawInstancedFunc;
DrawIndexedInstanced drawIndexedInstancedFunc;
ExecuteCommandLists executeCommandListFunc;
};
// Definition of WndProc Hook. Its here to avoid dragging dependencies on <windows.h> types.
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
namespace DirectX12Interface {
ID3D12Device* Device = nullptr;
ID3D12DescriptorHeap* DescriptorHeapBackBuffers;
ID3D12DescriptorHeap* DescriptorHeapImGuiRender;
ID3D12GraphicsCommandList* CommandList;
ID3D12CommandQueue* CommandQueue;
struct _FrameContext {
ID3D12CommandAllocator* CommandAllocator;
ID3D12Resource* Resource;
D3D12_CPU_DESCRIPTOR_HANDLE DescriptorHandle;
};
UINT BuffersCounts = -1;
_FrameContext* FrameContext;
}
static HRESULT __stdcall Present_Hook(IDXGISwapChain3* pChain, const UINT SyncInterval, const UINT Flags)
{
static BOOL g_bInitialised = false;
if (!g_bInitialised)
{
auto result = (HRESULT)pChain->GetDevice(__uuidof(DirectX12Interface::Device), reinterpret_cast<void**>(&DirectX12Interface::Device));
LOG_DEBUG("Result of GetDevice: %x", HRESULT_CODE(result));
if (SUCCEEDED(result))
{
DXGI_SWAP_CHAIN_DESC Desc;
pChain->GetDesc(&Desc);
Desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
Desc.Windowed = ((GetWindowLongPtr(Desc.OutputWindow, GWL_STYLE) & WS_POPUP) != 0) ? false : true;
DirectX12Interface::BuffersCounts = Desc.BufferCount;
DirectX12Interface::FrameContext = new DirectX12Interface::_FrameContext[DirectX12Interface::BuffersCounts];
D3D12_DESCRIPTOR_HEAP_DESC DescriptorImGuiRender = {};
DescriptorImGuiRender.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
DescriptorImGuiRender.NumDescriptors = DirectX12Interface::BuffersCounts;
DescriptorImGuiRender.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
if (DirectX12Interface::Device->CreateDescriptorHeap(&DescriptorImGuiRender, IID_PPV_ARGS(&DirectX12Interface::DescriptorHeapImGuiRender)) != S_OK)
return CALL_ORIGIN(Present_Hook, pChain, SyncInterval, Flags);
ID3D12CommandAllocator* Allocator;
if (DirectX12Interface::Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&Allocator)) != S_OK)
return CALL_ORIGIN(Present_Hook, pChain, SyncInterval, Flags);
for (size_t i = 0; i < DirectX12Interface::BuffersCounts; i++) {
DirectX12Interface::FrameContext[i].CommandAllocator = Allocator;
}
if (DirectX12Interface::Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, Allocator, NULL, IID_PPV_ARGS(&DirectX12Interface::CommandList)) != S_OK ||
DirectX12Interface::CommandList->Close() != S_OK)
return CALL_ORIGIN(Present_Hook, pChain, SyncInterval, Flags);
D3D12_DESCRIPTOR_HEAP_DESC DescriptorBackBuffers;
DescriptorBackBuffers.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
DescriptorBackBuffers.NumDescriptors = DirectX12Interface::BuffersCounts;
DescriptorBackBuffers.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
DescriptorBackBuffers.NodeMask = 1;
if (DirectX12Interface::Device->CreateDescriptorHeap(&DescriptorBackBuffers, IID_PPV_ARGS(&DirectX12Interface::DescriptorHeapBackBuffers)) != S_OK)
return CALL_ORIGIN(Present_Hook, pChain, SyncInterval, Flags);
const auto RTVDescriptorSize = DirectX12Interface::Device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12_CPU_DESCRIPTOR_HANDLE RTVHandle = DirectX12Interface::DescriptorHeapBackBuffers->GetCPUDescriptorHandleForHeapStart();
for (size_t i = 0; i < DirectX12Interface::BuffersCounts; i++) {
ID3D12Resource* pBackBuffer = nullptr;
DirectX12Interface::FrameContext[i].DescriptorHandle = RTVHandle;
pChain->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer));
DirectX12Interface::Device->CreateRenderTargetView(pBackBuffer, nullptr, RTVHandle);
DirectX12Interface::FrameContext[i].Resource = pBackBuffer;
RTVHandle.ptr += RTVDescriptorSize;
}
backend::DX12Events::InitializeEvent(Desc.OutputWindow, DirectX12Interface::Device, DirectX12Interface::BuffersCounts, DirectX12Interface::DescriptorHeapImGuiRender);
g_bInitialised = true;
}
}
// render function
if (!g_bInitialised || DirectX12Interface::CommandQueue == nullptr)
return CALL_ORIGIN(Present_Hook, pChain, SyncInterval, Flags);
backend::DX12Events::PreRenderEvent();
DirectX12Interface::_FrameContext& CurrentFrameContext = DirectX12Interface::FrameContext[pChain->GetCurrentBackBufferIndex()];
CurrentFrameContext.CommandAllocator->Reset();
D3D12_RESOURCE_BARRIER Barrier;
Barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
Barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
Barrier.Transition.pResource = CurrentFrameContext.Resource;
Barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
Barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
Barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
DirectX12Interface::CommandList->Reset(CurrentFrameContext.CommandAllocator, nullptr);
DirectX12Interface::CommandList->ResourceBarrier(1, &Barrier);
DirectX12Interface::CommandList->OMSetRenderTargets(1, &CurrentFrameContext.DescriptorHandle, FALSE, nullptr);
DirectX12Interface::CommandList->SetDescriptorHeaps(1, &DirectX12Interface::DescriptorHeapImGuiRender);
backend::DX12Events::PostRenderEvent(DirectX12Interface::CommandList);
Barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
Barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
DirectX12Interface::CommandList->ResourceBarrier(1, &Barrier);
DirectX12Interface::CommandList->Close();
DirectX12Interface::CommandQueue->ExecuteCommandLists(1, reinterpret_cast<ID3D12CommandList* const*>(&DirectX12Interface::CommandList));
return CALL_ORIGIN(Present_Hook, pChain, SyncInterval, Flags);
}
void ExecuteCommandLists_Hook(ID3D12CommandQueue* queue, UINT NumCommandLists, ID3D12CommandList* ppCommandLists) {
if (!DirectX12Interface::CommandQueue)
DirectX12Interface::CommandQueue = queue;
CALL_ORIGIN(ExecuteCommandLists_Hook, queue, NumCommandLists, ppCommandLists);
}
static void GetHardwareAdapter(IDXGIFactory4* pFactory, IDXGIAdapter1** ppAdapter)
{
*ppAdapter = nullptr;
for (UINT adapterIndex = 0; ; ++adapterIndex)
{
IDXGIAdapter1* pAdapter = nullptr;
if (DXGI_ERROR_NOT_FOUND == pFactory->EnumAdapters1(adapterIndex, &pAdapter))
{
// No more adapters to enumerate.
break;
}
// Check to see if the adapter supports Direct3D 12, but don't create the
// actual device yet.
if (SUCCEEDED(D3D12CreateDevice(pAdapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr)))
{
*ppAdapter = pAdapter;
return;
}
pAdapter->Release();
}
}
WNDCLASSEX WindowClass;
HWND WindowHwnd;
bool InitWindow() {
WindowClass.cbSize = sizeof(WNDCLASSEX);
WindowClass.style = CS_HREDRAW | CS_VREDRAW;
WindowClass.lpfnWndProc = DefWindowProc;
WindowClass.cbClsExtra = 0;
WindowClass.cbWndExtra = 0;
WindowClass.hInstance = GetModuleHandle(NULL);
WindowClass.hIcon = NULL;
WindowClass.hCursor = NULL;
WindowClass.hbrBackground = NULL;
WindowClass.lpszMenuName = NULL;
WindowClass.lpszClassName = "MJ";
WindowClass.hIconSm = NULL;
RegisterClassEx(&WindowClass);
WindowHwnd = CreateWindow(WindowClass.lpszClassName, "DirectX Window", WS_OVERLAPPEDWINDOW, 0, 0, 100, 100, NULL, NULL, WindowClass.hInstance, NULL);
if (WindowHwnd == NULL) {
return false;
}
return true;
}
bool DeleteWindow() {
DestroyWindow(WindowHwnd);
UnregisterClass(WindowClass.lpszClassName, WindowClass.hInstance);
if (WindowHwnd != NULL) {
return false;
}
return true;
}
bool FindFunctions(DirectX12MainFunctions& mainFunctions) {
if (InitWindow() == false) {
return false;
}
HMODULE D3D12Module = GetModuleHandle("d3d12.dll");
HMODULE DXGIModule = GetModuleHandle("dxgi.dll");
if (D3D12Module == NULL || DXGIModule == NULL) {
DeleteWindow();
return false;
}
void* CreateDXGIFactory = GetProcAddress(DXGIModule, "CreateDXGIFactory");
if (CreateDXGIFactory == NULL) {
DeleteWindow();
return false;
}
IDXGIFactory* Factory;
if (((long(__stdcall*)(const IID&, void**))(CreateDXGIFactory))(__uuidof(IDXGIFactory), (void**)&Factory) < 0) {
DeleteWindow();
return false;
}
IDXGIAdapter* Adapter;
if (Factory->EnumAdapters(0, &Adapter) == DXGI_ERROR_NOT_FOUND) {
DeleteWindow();
return false;
}
void* D3D12CreateDevice = GetProcAddress(D3D12Module, "D3D12CreateDevice");
if (D3D12CreateDevice == NULL) {
DeleteWindow();
return false;
}
ID3D12Device* Device;
if (((long(__stdcall*)(IUnknown*, D3D_FEATURE_LEVEL, const IID&, void**))(D3D12CreateDevice))(Adapter, D3D_FEATURE_LEVEL_11_0, __uuidof(ID3D12Device), (void**)&Device) < 0) {
DeleteWindow();
return false;
}
D3D12_COMMAND_QUEUE_DESC QueueDesc;
QueueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
QueueDesc.Priority = 0;
QueueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
QueueDesc.NodeMask = 0;
ID3D12CommandQueue* CommandQueue;
if (Device->CreateCommandQueue(&QueueDesc, __uuidof(ID3D12CommandQueue), (void**)&CommandQueue) < 0) {
DeleteWindow();
return false;
}
ID3D12CommandAllocator* CommandAllocator;
if (Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, __uuidof(ID3D12CommandAllocator), (void**)&CommandAllocator) < 0) {
DeleteWindow();
return false;
}
ID3D12GraphicsCommandList* CommandList;
if (Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, CommandAllocator, NULL, __uuidof(ID3D12GraphicsCommandList), (void**)&CommandList) < 0) {
DeleteWindow();
return false;
}
DXGI_RATIONAL RefreshRate;
RefreshRate.Numerator = 60;
RefreshRate.Denominator = 1;
DXGI_MODE_DESC BufferDesc;
BufferDesc.Width = 100;
BufferDesc.Height = 100;
BufferDesc.RefreshRate = RefreshRate;
BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
DXGI_SAMPLE_DESC SampleDesc;
SampleDesc.Count = 1;
SampleDesc.Quality = 0;
DXGI_SWAP_CHAIN_DESC SwapChainDesc = {};
SwapChainDesc.BufferDesc = BufferDesc;
SwapChainDesc.SampleDesc = SampleDesc;
SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
SwapChainDesc.BufferCount = 2;
SwapChainDesc.OutputWindow = WindowHwnd;
SwapChainDesc.Windowed = 1;
SwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
SwapChainDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
IDXGISwapChain* SwapChain;
if (Factory->CreateSwapChain(CommandQueue, &SwapChainDesc, &SwapChain) < 0) {
DeleteWindow();
return false;
}
typedef uint64_t uintx_t;
auto MethodsTable = (uintx_t*)::calloc(150, sizeof(uintx_t));
memcpy(MethodsTable, *(uintx_t**)Device, 44 * sizeof(uintx_t));
memcpy(MethodsTable + 44, *(uintx_t**)CommandQueue, 19 * sizeof(uintx_t));
memcpy(MethodsTable + 44 + 19, *(uintx_t**)CommandAllocator, 9 * sizeof(uintx_t));
memcpy(MethodsTable + 44 + 19 + 9, *(uintx_t**)CommandList, 60 * sizeof(uintx_t));
memcpy(MethodsTable + 44 + 19 + 9 + 60, *(uintx_t**)SwapChain, 18 * sizeof(uintx_t));
mainFunctions.presentFunc = reinterpret_cast<decltype(mainFunctions.presentFunc)>(MethodsTable[140]);
mainFunctions.drawInstancedFunc = reinterpret_cast<decltype(mainFunctions.drawInstancedFunc)>(MethodsTable[84]);
mainFunctions.drawIndexedInstancedFunc = reinterpret_cast<decltype(mainFunctions.drawIndexedInstancedFunc)>(MethodsTable[85]);
mainFunctions.executeCommandListFunc = reinterpret_cast<decltype(mainFunctions.executeCommandListFunc)>(MethodsTable[54]);
free(MethodsTable);
Device->Release();
Device = NULL;
CommandQueue->Release();
CommandQueue = NULL;
CommandAllocator->Release();
CommandAllocator = NULL;
CommandList->Release();
CommandList = NULL;
SwapChain->Release();
SwapChain = NULL;
DeleteWindow();
return true;
}
void backend::InitializeDX12Hooks()
{
LOG_DEBUG("Initializing D3D12 hook: started.");
DirectX12MainFunctions mainFuncs;
if (!FindFunctions(mainFuncs))
{
LOG_ERROR("Failed to find 'Present' function for D3D12.");
return;
}
LOG_DEBUG("IDXGISwapChainPresent Present: %p", mainFuncs.presentFunc);
LOG_DEBUG("DrawInstanced Present: %p", mainFuncs.drawInstancedFunc);
LOG_DEBUG("DrawIndexedInstanced Present: %p", mainFuncs.drawIndexedInstancedFunc);
LOG_DEBUG("ExecuteCommandLists Present: %p", mainFuncs.executeCommandListFunc);
HookManager::install(mainFuncs.presentFunc, Present_Hook);
HookManager::install(mainFuncs.executeCommandListFunc, ExecuteCommandLists_Hook);
LOG_DEBUG("Initializing D3D12 hook: done.");
}
bool backend::LoadTextureFromMemory(LPBYTE image_data, int image_width, int image_height, D3D12_CPU_DESCRIPTOR_HANDLE srv_cpu_handle,
ID3D12Resource** out_tex_resource, int* out_width, int* out_height)
{
if (DirectX12Interface::Device == nullptr)
return false;
// Create texture resource
D3D12_HEAP_PROPERTIES props;
memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES));
props.Type = D3D12_HEAP_TYPE_DEFAULT;
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
D3D12_RESOURCE_DESC desc;
ZeroMemory(&desc, sizeof(desc));
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
desc.Alignment = 0;
desc.Width = image_width;
desc.Height = image_height;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
ID3D12Resource* pTexture = NULL;
DirectX12Interface::Device->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
D3D12_RESOURCE_STATE_COPY_DEST, NULL, IID_PPV_ARGS(&pTexture));
// Create a temporary upload resource to move the data in
UINT uploadPitch = (image_width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u);
UINT uploadSize = image_height * uploadPitch;
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Alignment = 0;
desc.Width = uploadSize;
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
props.Type = D3D12_HEAP_TYPE_UPLOAD;
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
ID3D12Resource* uploadBuffer = NULL;
HRESULT hr = DirectX12Interface::Device->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc,
D3D12_RESOURCE_STATE_GENERIC_READ, NULL, IID_PPV_ARGS(&uploadBuffer));
IM_ASSERT(SUCCEEDED(hr));
// Write pixels into the upload resource
void* mapped = NULL;
D3D12_RANGE range = { 0, uploadSize };
hr = uploadBuffer->Map(0, &range, &mapped);
IM_ASSERT(SUCCEEDED(hr));
for (int y = 0; y < image_height; y++)
memcpy((void*)((uintptr_t)mapped + y * uploadPitch), image_data + y * image_width * 4, image_width * 4);
uploadBuffer->Unmap(0, &range);
// Copy the upload resource content into the real resource
D3D12_TEXTURE_COPY_LOCATION srcLocation = {};
srcLocation.pResource = uploadBuffer;
srcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
srcLocation.PlacedFootprint.Footprint.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srcLocation.PlacedFootprint.Footprint.Width = image_width;
srcLocation.PlacedFootprint.Footprint.Height = image_height;
srcLocation.PlacedFootprint.Footprint.Depth = 1;
srcLocation.PlacedFootprint.Footprint.RowPitch = uploadPitch;
D3D12_TEXTURE_COPY_LOCATION dstLocation = {};
dstLocation.pResource = pTexture;
dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dstLocation.SubresourceIndex = 0;
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = pTexture;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
// Create a temporary command queue to do the copy with
ID3D12Fence* fence = NULL;
hr = DirectX12Interface::Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence));
IM_ASSERT(SUCCEEDED(hr));
HANDLE event = CreateEvent(0, 0, 0, 0);
IM_ASSERT(event != NULL);
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.NodeMask = 1;
ID3D12CommandQueue* cmdQueue = NULL;
hr = DirectX12Interface::Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&cmdQueue));
IM_ASSERT(SUCCEEDED(hr));
ID3D12CommandAllocator* cmdAlloc = NULL;
hr = DirectX12Interface::Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc));
IM_ASSERT(SUCCEEDED(hr));
ID3D12GraphicsCommandList* cmdList = NULL;
hr = DirectX12Interface::Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, NULL, IID_PPV_ARGS(&cmdList));
IM_ASSERT(SUCCEEDED(hr));
cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, NULL);
cmdList->ResourceBarrier(1, &barrier);
hr = cmdList->Close();
IM_ASSERT(SUCCEEDED(hr));
// Execute the copy
cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList);
hr = cmdQueue->Signal(fence, 1);
IM_ASSERT(SUCCEEDED(hr));
// Wait for everything to complete
fence->SetEventOnCompletion(1, event);
WaitForSingleObject(event, INFINITE);
// Tear down our temporary command queue and release the upload resource
cmdList->Release();
cmdAlloc->Release();
cmdQueue->Release();
CloseHandle(event);
fence->Release();
uploadBuffer->Release();
// Create a shader resource view for the texture
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc;
ZeroMemory(&srvDesc, sizeof(srvDesc));
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = desc.MipLevels;
srvDesc.Texture2D.MostDetailedMip = 0;
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
DirectX12Interface::Device->CreateShaderResourceView(pTexture, &srvDesc, srv_cpu_handle);
// Return results
*out_tex_resource = pTexture;
*out_width = image_width;
*out_height = image_height;
return true;
}

View File

@ -0,0 +1,27 @@
#pragma once
#include <Windows.h>
#include <d3d12.h>
#include <d3dcompiler.h>
#include <cheat-base/events/event.hpp>
namespace backend
{
void InitializeDX12Hooks();
// Thanks to https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples#example-for-directx11-users
bool LoadTextureFromMemory(LPBYTE image_data, int image_width, int image_height, D3D12_CPU_DESCRIPTOR_HANDLE srv_cpu_handle,
ID3D12Resource** out_tex_resource, int* out_width, int* out_height);
class DX12Events
{
public:
inline static TEvent<> PreRenderEvent{};
inline static TEvent<ID3D12GraphicsCommandList*> PostRenderEvent{};
inline static TEvent<HWND, ID3D12Device*, UINT, ID3D12DescriptorHeap*> InitializeEvent{};
};
}

View File

@ -2,10 +2,15 @@
#include "renderer.h"
#include <backends/imgui_impl_dx11.h>
#pragma comment(lib, "dxgi")
#include <backends/imgui_impl_dx12.h>
#include <backends/imgui_impl_win32.h>
#include <cheat-base/util.h>
#include <cheat-base/render/backend/dx11-hook.h>
#include <cheat-base/render/backend/dx12-hook.h>
#include <cheat-base/ResourceLoader.h>
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
@ -33,19 +38,39 @@ namespace renderer
static WNDPROC OriginalWndProcHandler;
static ID3D11RenderTargetView* mainRenderTargetView;
static void OnRender(ID3D11DeviceContext* pContext);
static void OnDX11Initialize(HWND window, ID3D11Device* pDevice, ID3D11DeviceContext* pContext, IDXGISwapChain* pChain);
static void OnRenderDX11(ID3D11DeviceContext* pContext);
static void OnInitializeDX11(HWND window, ID3D11Device* pDevice, ID3D11DeviceContext* pContext, IDXGISwapChain* pChain);
static void OnPreRenderDX12();
static void OnPostRenderDX12(ID3D12GraphicsCommandList* commandList);
static void OnInitializeDX12(HWND window, ID3D12Device* pDevice, UINT buffersCounts, ID3D12DescriptorHeap* pDescriptorHeapImGuiRender);
void Init(LPBYTE fontData, DWORD fontDataSize)
void Init(LPBYTE fontData, DWORD fontDataSize, DXVersion version)
{
_customFontData = { fontData, fontDataSize };
LOG_DEBUG("Initialize IMGui...");
backend::DX11Events::RenderEvent += FUNCTION_HANDLER(OnRender);
backend::DX11Events::InitializeEvent += FUNCTION_HANDLER(OnDX11Initialize);
backend::InitializeDX11Hooks();
switch (version)
{
case renderer::DXVersion::D3D11:
backend::DX11Events::RenderEvent += FUNCTION_HANDLER(OnRenderDX11);
backend::DX11Events::InitializeEvent += FUNCTION_HANDLER(OnInitializeDX11);
backend::InitializeDX11Hooks();
break;
case renderer::DXVersion::D3D12:
backend::DX12Events::InitializeEvent += FUNCTION_HANDLER(OnInitializeDX12);
backend::DX12Events::PreRenderEvent += FUNCTION_HANDLER(OnPreRenderDX12);
backend::DX12Events::PostRenderEvent += FUNCTION_HANDLER(OnPostRenderDX12);
backend::InitializeDX12Hooks();
break;
case renderer::DXVersion::D3D9:
case renderer::DXVersion::D3D10:
default:
LOG_ERROR("Used unsupported version of DX.");
}
}
void SetInputLock(void* id, bool value)
@ -134,7 +159,53 @@ namespace renderer
static void SetupImGuiStyle();
static LRESULT CALLBACK hWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static void OnDX11Initialize(HWND window, ID3D11Device* pDevice, ID3D11DeviceContext* pContext, IDXGISwapChain* pChain)
void OnPreRenderDX12()
{
ImGui_ImplDX12_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.FontDefault = GetFontBySize(_globalFontSize);
ImGui::NewFrame();
events::RenderEvent();
ImGui::EndFrame();
}
void OnPostRenderDX12(ID3D12GraphicsCommandList* commandList)
{
ImGui::Render();
ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), commandList);
}
void OnInitializeDX12(HWND window, ID3D12Device* pDevice, UINT buffersCounts, ID3D12DescriptorHeap* pDescriptorHeapImGuiRender)
{
LOG_DEBUG("ImGUI: DirectX12 backend initialized successfully.");
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
LoadCustomFont();
SetupImGuiStyle();
//Set OriginalWndProcHandler to the Address of the Original WndProc function
OriginalWndProcHandler = reinterpret_cast<WNDPROC>(SetWindowLongPtr(window, GWLP_WNDPROC,
reinterpret_cast<LONG_PTR>(hWndProc)));
ImGui_ImplWin32_Init(window);
ImGui_ImplDX12_Init(pDevice, buffersCounts, DXGI_FORMAT_R8G8B8A8_UNORM,
pDescriptorHeapImGuiRender,
pDescriptorHeapImGuiRender->GetCPUDescriptorHandleForHeapStart(),
pDescriptorHeapImGuiRender->GetGPUDescriptorHandleForHeapStart());
ImGui_ImplDX12_CreateDeviceObjects();
ImGui::GetIO().ImeWindowHandle = window;
io.SetPlatformImeDataFn = nullptr; // F**king bug take 4 hours of my life
}
static void OnInitializeDX11(HWND window, ID3D11Device* pDevice, ID3D11DeviceContext* pContext, IDXGISwapChain* pChain)
{
LOG_DEBUG("ImGUI: DirectX11 backend initialized successfully.");
@ -161,7 +232,7 @@ namespace renderer
io.SetPlatformImeDataFn = nullptr; // F**king bug take 4 hours of my life
}
static void OnRender(ID3D11DeviceContext* pContext)
static void OnRenderDX11(ID3D11DeviceContext* pContext)
{
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();

View File

@ -5,7 +5,15 @@
namespace renderer
{
void Init(LPBYTE pFontData, DWORD dFontDataSize);
enum class DXVersion
{
D3D9,
D3D10,
D3D11,
D3D12
};
void Init(LPBYTE pFontData, DWORD dFontDataSize, DXVersion version = DXVersion::D3D11);
// Font sizing
ImFont* GetFontBySize(float fontSize);

View File

@ -272,6 +272,18 @@ namespace util
return static_cast<int64_t>(timezoneInfo.Bias) * 60;
}
void OpenConsole()
{
AllocConsole();
freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
freopen_s((FILE**)stderr, "CONOUT$", "w", stderr);
auto consoleWindow = GetConsoleWindow();
SetForegroundWindow(consoleWindow);
ShowWindow(consoleWindow, SW_RESTORE);
ShowWindow(consoleWindow, SW_SHOW);
}
void OpenURL(const char* url)
{
ShellExecute(nullptr, nullptr, url, nullptr, nullptr, SW_SHOW);

View File

@ -45,6 +45,7 @@ namespace util
int64_t GetTimezoneBias();
void OpenConsole();
template<typename ... Args>
std::string string_format(const std::string& format, Args ... args)

View File

@ -15,6 +15,11 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\user\cheat\misc\sniffer\pipe\messages\PipeMessage.h" />
<ClInclude Include="src\user\cheat\misc\sniffer\pipe\messages\PipeModifyData.h" />
<ClInclude Include="src\user\cheat\misc\sniffer\pipe\messages\PipePacketData.h" />
<ClInclude Include="src\user\cheat\misc\sniffer\pipe\PipeClient.h" />
<ClInclude Include="src\user\cheat\misc\sniffer\pipe\PipeIO.h" />
<ClInclude Include="src\user\cheat\visuals\TextureChanger.h" />
<ClInclude Include="src\user\cheat\world\FakeTime.h" />
<ClInclude Include="src\user\cheat\debugger.h">
@ -22,26 +27,6 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="src\user\cheat\misc\sniffer\MessageManager.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="src\user\cheat\misc\sniffer\messages\MessageBase.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="src\user\cheat\misc\sniffer\messages\MessageHeader.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="src\user\cheat\misc\sniffer\messages\ModifyData.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="src\user\cheat\misc\sniffer\messages\PacketData.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">true</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="src\user\cheat\GenshinCM.h" />
<ClInclude Include="src\user\cheat\teleport\CustomTeleports.h" />
<ClInclude Include="src\user\cheat\visuals\Browser.h" />
@ -117,6 +102,11 @@
<Font Include="res\Ruda-ExtraBold.ttf" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\user\cheat\misc\sniffer\pipe\messages\PipeMessage.cpp" />
<ClCompile Include="src\user\cheat\misc\sniffer\pipe\messages\PipeModifyData.cpp" />
<ClCompile Include="src\user\cheat\misc\sniffer\pipe\messages\PipePacketData.cpp" />
<ClCompile Include="src\user\cheat\misc\sniffer\pipe\PipeClient.cpp" />
<ClCompile Include="src\user\cheat\misc\sniffer\pipe\PipeIO.cpp" />
<ClCompile Include="src\user\cheat\visuals\TextureChanger.cpp" />
<ClCompile Include="src\user\cheat\world\FakeTime.cpp" />
<ClCompile Include="src\user\cheat\debugger.cpp">
@ -124,26 +114,6 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\user\cheat\misc\sniffer\MessageManager.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\user\cheat\misc\sniffer\messages\MessageBase.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\user\cheat\misc\sniffer\messages\MessageHeader.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\user\cheat\misc\sniffer\messages\ModifyData.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\user\cheat\misc\sniffer\messages\PacketData.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WS|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="src\user\cheat\teleport\CustomTeleports.cpp" />
<ClCompile Include="src\user\cheat\GenshinCM.cpp" />
<ClCompile Include="src\user\cheat\visuals\Browser.cpp" />
@ -849,9 +819,11 @@
<TargetName>CLibrary</TargetName>
<OutDir>$(SolutionDir)bin\$(Configuration)-$(PlatformShortName)\</OutDir>
<IntDir>$(SolutionDir)bin\$(Configuration)-$(PlatformShortName)\obj\$(ProjectName)\</IntDir>
<CustomBuildBeforeTargets>Run</CustomBuildBeforeTargets>
<CustomBuildBeforeTargets>
</CustomBuildBeforeTargets>
<PostBuildEventUseInBuild>false</PostBuildEventUseInBuild>
<CustomBuildAfterTargets>FinalizeBuildStatus</CustomBuildAfterTargets>
<CustomBuildAfterTargets>
</CustomBuildAfterTargets>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>

View File

@ -189,21 +189,6 @@
<ClInclude Include="src\user\cheat\visuals\CameraZoom.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\user\cheat\misc\sniffer\messages\MessageBase.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\user\cheat\misc\sniffer\messages\MessageHeader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\user\cheat\misc\sniffer\messages\ModifyData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\user\cheat\misc\sniffer\messages\PacketData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\user\cheat\misc\sniffer\MessageManager.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\user\cheat\debugger.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -237,6 +222,21 @@
<ClInclude Include="src\user\cheat\visuals\TextureChanger.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\user\cheat\misc\sniffer\pipe\messages\PipeMessage.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\user\cheat\misc\sniffer\pipe\messages\PipeModifyData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\user\cheat\misc\sniffer\pipe\messages\PipePacketData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\user\cheat\misc\sniffer\pipe\PipeClient.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\user\cheat\misc\sniffer\pipe\PipeIO.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Font Include="res\Ruda-Bold.ttf" />
@ -384,21 +384,6 @@
<ClCompile Include="src\user\cheat\visuals\CameraZoom.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\user\cheat\misc\sniffer\messages\MessageBase.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\user\cheat\misc\sniffer\messages\MessageHeader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\user\cheat\misc\sniffer\messages\ModifyData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\user\cheat\misc\sniffer\messages\PacketData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\user\cheat\misc\sniffer\MessageManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\user\cheat\visuals\ShowChestIndicator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -432,6 +417,21 @@
<ClCompile Include="src\user\cheat\visuals\TextureChanger.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\user\cheat\misc\sniffer\pipe\messages\PipeMessage.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\user\cheat\misc\sniffer\pipe\messages\PipeModifyData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\user\cheat\misc\sniffer\pipe\messages\PipePacketData.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\user\cheat\misc\sniffer\pipe\PipeClient.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\user\cheat\misc\sniffer\pipe\PipeIO.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="res\res.rc">

View File

@ -111,7 +111,7 @@ namespace cheat
FEAT_INST(HideUI),
FEAT_INST(Browser),
FEAT_INST(EnablePeaking),
FEAT_INST(TextureChanger),
FEAT_INST(TextureChanger)
});
#undef FEAT_INST

View File

@ -1,85 +0,0 @@
#include "pch-il2cpp.h"
#include "MessageManager.h"
namespace sniffer
{
void MessageManager::Send(MessageBase& data)
{
if (!IsConnected())
return;
s_Pipe->WriteObject(data.header());
s_Pipe->WriteObject(data);
}
MessageBase* MessageManager::ReceiveMessage()
{
if (!IsConnected())
return nullptr;
MessageHeader header;
s_Pipe->ReadObject(header);
MessageIDs messageID = static_cast<MessageIDs>(header.messageID());
#define MESSAGE_CASE(mid, type)\
case mid: \
{ \
auto data = new type(header); \
s_Pipe->ReadObject(*data); \
CallHandlers(*data); \
return reinterpret_cast<MessageBase*>(data);\
}
switch (messageID)
{
MESSAGE_CASE(MessageIDs::PACKET_DATA, PacketData);
MESSAGE_CASE(MessageIDs::MODIFY_DATA, ModifyData);
default:
break;
}
return nullptr;
}
void MessageManager::ProcessMessage()
{
delete ReceiveMessage();
}
bool MessageManager::IsConnected()
{
if (s_Pipe == nullptr)
return false;
return s_Pipe->IsPipeOpened() || TryConnectToPipe();
}
void MessageManager::SetConnectionDelay(uint32_t delay)
{
s_ConnectionDelay = delay;
}
uint32_t MessageManager::GetConnectionDelay()
{
return s_ConnectionDelay;
}
void MessageManager::Connect(const std::string& pipeName)
{
if (s_Pipe != nullptr)
delete s_Pipe;
s_Pipe = new PipeTransfer(pipeName);
TryConnectToPipe();
}
void MessageManager::Disconnect()
{
if (s_Pipe == nullptr)
return;
delete s_Pipe;
}
}

View File

@ -1,119 +0,0 @@
#pragma once
#include "messages/MessageHeader.h"
#include "messages/ModifyData.h"
#include "messages/PacketData.h"
#include <vector>
namespace sniffer
{
enum MessageIDs : uint32_t
{
NONE = 0,
PACKET_DATA = 1,
MODIFY_DATA = 2
};
class MessageManager
{
public:
template<class T>
using CallbackFunction = void(*)(const T& packetData);
template<class T>
inline static void AddHandler(CallbackFunction<T> callback)
{
s_Handlers.push_back(callback);
}
template<class TMessage>
inline static TMessage CreateMessage(uint32_t reqID = 0)
{
static_assert(std::is_base_of<MessageBase, TMessage>::value, "Should be derived of MessageBase.");
return TMessage(MessageHeader{ GetMessageIDByType<TMessage>(), reqID });
}
template<class TMessage>
inline static std::optional<TMessage> WaitFor()
{
static_assert(std::is_base_of<MessageBase, TMessage>::value, "Should be derived of MessageBase.");
if (!IsConnected())
return {};
while (true)
{
auto messagePtr = ReceiveMessage();
if (messagePtr == nullptr)
return {};
if (GetMessageIDByType<TMessage>() != messagePtr->messageID())
{
delete messagePtr;
continue;
}
// Copying message
TMessage message = *reinterpret_cast<TMessage*>(messagePtr);
delete messagePtr;
return message;
}
}
static void Send(MessageBase& data);
static MessageBase* ReceiveMessage();
static void ProcessMessage();
static void SetConnectionDelay(uint32_t delay);
static uint32_t GetConnectionDelay();
static bool IsConnected();
static void Connect(const std::string& pipeName);
static void Disconnect();
private:
inline static PipeTransfer* s_Pipe = nullptr;
inline static uint32_t s_ConnectionDelay = 5;
inline static std::time_t s_NextTimeToConnect = 0;
template<class T>
static std::vector<CallbackFunction<T>> s_Handlers;
static bool TryConnectToPipe()
{
if (s_Pipe == nullptr)
return false;
std::time_t currTime = std::time(0);
if (s_NextTimeToConnect > currTime)
return false;
bool result = s_Pipe->Connect();
if (result)
LOG_INFO("Connected to pipe successfully.");
else
s_NextTimeToConnect = currTime + 5; // delay in 5 sec
return result;
}
template<class TMessage> static uint32_t GetMessageIDByType();
template<> static uint32_t GetMessageIDByType<PacketData>() { return MessageIDs::PACKET_DATA; }
template<> static uint32_t GetMessageIDByType<ModifyData>() { return MessageIDs::MODIFY_DATA; }
template<class T>
static void CallHandlers(const T& message)
{
for (auto& handler : s_Handlers<T>)
handler(message);
}
};
}

View File

@ -16,7 +16,7 @@ namespace cheat::feature
NF(f_PipeName, "Pipe name", "PacketSniffer", "genshin_packet_pipe")
{
sniffer::MessageManager::Connect(f_PipeName.value());
client.Connect(f_PipeName.value());
HookManager::install(app::Kcp_KcpNative_kcp_client_send_packet, KcpNative_kcp_client_send_packet_Hook);
HookManager::install(app::MoleMole_KcpClient_TryDequeueEvent, KcpClient_TryDequeueEvent_Hook);
@ -42,36 +42,36 @@ namespace cheat::feature
return instance;
}
bool PacketSniffer::ProcessModifiedData(app::KcpPacket_1* packet)
bool PacketSniffer::ProcessModifiedData(app::KcpPacket_1*& packet)
{
auto modify_data = sniffer::MessageManager::WaitFor<ModifyData>();
auto modify_data = client.WaitFor<PipeModifyData>();
if (!modify_data)
return false;
switch (modify_data->modify_type)
switch (modify_data->modifyType)
{
case PacketModifyType::Blocked:
case ModifyType::Blocked:
return true;
case PacketModifyType::Modified:
case ModifyType::Modified:
{
auto data_size = modify_data->modified_head.size() + modify_data->modified_message.size() + 12;
auto data_size = modify_data->head.size() + modify_data->content.size() + 12;
char* data = new char[data_size];
auto head_size = static_cast<uint16_t>(modify_data->modified_head.size());
auto message_size = static_cast<uint32_t>(modify_data->modified_message.size());
auto head_size = static_cast<uint16_t>(modify_data->head.size());
auto message_size = static_cast<uint32_t>(modify_data->content.size());
util::WriteMapped(data, 0, static_cast<uint16_t>(0x4567)); // Magic number
util::WriteMapped(data, 2, modify_data->message_id); // Message id
util::WriteMapped(data, 2, modify_data->messageID); // Message id
util::WriteMapped(data, 4, head_size); // Head size
util::WriteMapped(data, 6, message_size); // Message size
// Fill content
char* ptr_head_content = data + 10;
memcpy_s(ptr_head_content, head_size, modify_data->modified_head.data(), head_size);
memcpy_s(ptr_head_content, head_size, modify_data->head.data(), head_size);
char* ptr_message_content = ptr_head_content + modify_data->modified_head.size();
memcpy_s(ptr_message_content, message_size, modify_data->modified_message.data(), message_size);
char* ptr_message_content = ptr_head_content + modify_data->head.size();
memcpy_s(ptr_message_content, message_size, modify_data->content.data(), message_size);
util::WriteMapped(ptr_message_content, message_size, static_cast<uint16_t>(0x89AB));
@ -86,28 +86,29 @@ namespace cheat::feature
packet = new_packet;
}
break;
case PacketModifyType::Unchanged:
case ModifyType::Unchanged:
default:
break;
}
return false;
}
bool PacketSniffer::OnPacketIO(app::KcpPacket_1* packet, PacketIOType type)
bool PacketSniffer::OnPacketIO(app::KcpPacket_1*& packet, NetIODirection direction)
{
if (!sniffer::MessageManager::IsConnected())
if (!client.IsConnected())
return true;
if (!f_CaptureEnabled)
return true;
PacketData packetData = ParseRawPacketData((char*)packet->data, packet->dataLen);
if (!packetData.valid)
auto pipeData = client.CreateMessage<PipePacketData>();
bool parsed = ParseRawPacketData((char*)packet->data, packet->dataLen, pipeData);
if (!parsed)
return true;
packetData.ioType = type;
packetData.blockModeEnabled = f_ManipulationEnabled;
sniffer::MessageManager::Send(packetData);
pipeData.direction = direction;
pipeData.manipulationEnabled = f_ManipulationEnabled;
client.Send(pipeData);
bool canceled = f_ManipulationEnabled && ProcessModifiedData(packet);
@ -126,7 +127,7 @@ namespace cheat::feature
delete[] byteArray;
}
PacketData PacketSniffer::ParseRawPacketData(char* encryptedData, uint32_t length)
bool PacketSniffer::ParseRawPacketData(char* encryptedData, uint32_t length, PipePacketData& dataOut)
{
// Packet structure
// * Magic word (0x4567) [2 bytes]
@ -139,7 +140,6 @@ namespace cheat::feature
// Header size - 12 bytes
PacketData packetData = sniffer::MessageManager::CreateMessage<PacketData>();
// Decrypting packetData
auto data = new char[length];
memcpy_s(data, length, encryptedData, length);
@ -150,14 +150,14 @@ namespace cheat::feature
if (magicHead != 0x4567)
{
LOG_ERROR("Head magic value for packet is not valid.");
return packetData;
return false;
}
uint16_t magicEnd = util::ReadMapped<uint16_t>(data, length - 2);
if (magicEnd != 0x89AB)
{
LOG_ERROR("End magic value for packet is not valid.");
return packetData;
return false;
}
uint16_t messageId = util::ReadMapped<uint16_t>(data, 2);
@ -167,21 +167,20 @@ namespace cheat::feature
if (length < headSize + contenSize + 12)
{
LOG_ERROR("Packet size is not valid.");
return packetData;
return false;
}
packetData.valid = true;
packetData.messageID = messageId;
dataOut.messageID = messageId;
packetData.headRawData = std::vector<byte>((size_t)headSize, 0);
memcpy_s(packetData.headRawData.data(), headSize, data + 10, headSize);
dataOut.head = std::vector<byte>((size_t)headSize, 0);
memcpy_s(dataOut.head.data(), headSize, data + 10, headSize);
packetData.messageRawData = std::vector<byte>((size_t)contenSize, 0);
memcpy_s(packetData.messageRawData.data(), contenSize, data + 10 + headSize, contenSize);
dataOut.content = std::vector<byte>((size_t)contenSize, 0);
memcpy_s(dataOut.content.data(), contenSize, data + 10 + headSize, contenSize);
delete[] data;
return packetData;
return true;
}
bool PacketSniffer::KcpClient_TryDequeueEvent_Hook(void* __this, app::ClientKcpEvent* evt, MethodInfo* method)
@ -193,13 +192,13 @@ namespace cheat::feature
return result;
auto& sniffer = GetInstance();
return sniffer.OnPacketIO(evt->_evt.packet, PacketIOType::Receive);
return sniffer.OnPacketIO(evt->_evt.packet, NetIODirection::Receive);
}
int32_t PacketSniffer::KcpNative_kcp_client_send_packet_Hook(void* kcp_client, app::KcpPacket_1* packet, MethodInfo* method)
{
auto& sniffer = GetInstance();
if (!sniffer.OnPacketIO(packet, PacketIOType::Send))
if (!sniffer.OnPacketIO(packet, NetIODirection::Send))
return 0;
return CALL_ORIGIN(KcpNative_kcp_client_send_packet_Hook, kcp_client, packet, method);

View File

@ -7,10 +7,9 @@
#include <cheat-base/config/config.h>
#include <cheat-base/PipeTransfer.h>
#include "messages/PacketData.h"
#include "messages/ModifyData.h"
#include "MessageManager.h"
#include "pipe/messages/PipeModifyData.h"
#include "pipe/messages/PipePacketData.h"
#include "pipe/PipeClient.h"
namespace cheat::feature
{
@ -22,6 +21,8 @@ namespace cheat::feature
config::Field<bool> f_ManipulationEnabled;
config::Field<std::string> f_PipeName;
PipeClient client;
static PacketSniffer& GetInstance();
const FeatureGUIInfo& GetGUIInfo() const override;
@ -30,13 +31,13 @@ namespace cheat::feature
private:
PacketSniffer();
bool ProcessModifiedData(app::KcpPacket_1* packet);
PacketData ParseRawPacketData(char* encryptedData, uint32_t length);
bool ProcessModifiedData(app::KcpPacket_1*& packet);
bool ParseRawPacketData(char* encryptedData, uint32_t length, PipePacketData& dataOut);
static void EncryptXor(void* content, uint32_t length);
static bool KcpClient_TryDequeueEvent_Hook(void* __this, app::ClientKcpEvent* evt, MethodInfo* method);
static int32_t KcpNative_kcp_client_send_packet_Hook(void* kcp_client, app::KcpPacket_1* packet, MethodInfo* method);
bool OnPacketIO(app::KcpPacket_1* packet, PacketIOType type);
bool OnPacketIO(app::KcpPacket_1*& packet, NetIODirection direction);
};
}

View File

@ -1,32 +0,0 @@
#include "pch-il2cpp.h"
#include "MessageBase.h"
MessageBase::MessageBase(const MessageHeader& header) : m_Header(header)
{
}
uint32_t MessageBase::requestID() const
{
return m_Header.requestID();
}
uint32_t MessageBase::messageID() const
{
return m_Header.messageID();
}
int64_t MessageBase::timestamp() const
{
return m_Header.timestamp();
}
uint64_t MessageBase::sequenceID() const
{
return m_Header.sequenceID();
}
MessageHeader& MessageBase::header()
{
return m_Header;
}

View File

@ -1,17 +0,0 @@
#pragma once
#include "MessageHeader.h"
class MessageBase : public PipeSerializedObject
{
public:
explicit MessageBase(const MessageHeader& header);
uint32_t requestID() const;
uint32_t messageID() const;
int64_t timestamp() const;
uint64_t sequenceID() const;
MessageHeader& header();
private:
MessageHeader m_Header;
};

View File

@ -1,53 +0,0 @@
#include <pch-il2cpp.h>
#include "MessageHeader.h"
MessageHeader::MessageHeader(uint32_t messageId, uint32_t requestID /*= 0*/) :
m_MessageID(messageId), m_RequestID(requestID),
m_SequenceID(GenerateSequenceID()), m_Timestamp(util::GetCurrentTimeMillisec())
{ }
MessageHeader::MessageHeader() : m_MessageID(0), m_RequestID(0), m_SequenceID(0), m_Timestamp(0)
{ }
uint32_t MessageHeader::requestID() const
{
return m_RequestID;
}
uint32_t MessageHeader::messageID() const
{
return m_MessageID;
}
int64_t MessageHeader::timestamp() const
{
return m_Timestamp;
}
uint64_t MessageHeader::sequenceID() const
{
return m_SequenceID;
}
void MessageHeader::Write(PipeTransfer* transfer)
{
transfer->Write(m_MessageID);
transfer->Write(m_RequestID);
transfer->Write(util::GetCurrentTimeMillisec());
transfer->Write(m_SequenceID);
}
void MessageHeader::Read(PipeTransfer* transfer)
{
transfer->Read(m_MessageID);
transfer->Read(m_RequestID);
transfer->Read(m_Timestamp);
transfer->Read(m_SequenceID);
}
uint64_t MessageHeader::GenerateSequenceID()
{
s_SequenceGlobalID += 1;
return s_SequenceGlobalID;
}

View File

@ -1,25 +0,0 @@
#pragma once
#include <cheat-base/PipeTransfer.h>
class MessageHeader : public PipeSerializedObject
{
public:
MessageHeader();
MessageHeader(uint32_t messageId, uint32_t requestID = 0);
uint32_t requestID() const;
uint32_t messageID() const;
int64_t timestamp() const;
uint64_t sequenceID() const;
void Write(PipeTransfer* transfer) final;
void Read(PipeTransfer* transfer) final;
private:
inline static std::atomic<uint64_t> s_SequenceGlobalID {};
static uint64_t GenerateSequenceID();
uint32_t m_MessageID;
uint32_t m_RequestID;
int64_t m_Timestamp;
int64_t m_SequenceID;
};

View File

@ -1,28 +0,0 @@
#include <pch-il2cpp.h>
#include "ModifyData.h"
ModifyData::ModifyData(const MessageHeader& header) : MessageBase(header),
modify_type(PacketModifyType::Unchanged)
{ }
void ModifyData::Write(PipeTransfer* transfer)
{
transfer->Write<PacketModifyType>(modify_type);
if (modify_type == PacketModifyType::Modified)
{
transfer->Write(message_id);
transfer->Write(modified_head);
transfer->Write(modified_message);
}
}
void ModifyData::Read(PipeTransfer* transfer)
{
transfer->Read<PacketModifyType>(modify_type);
if (modify_type == PacketModifyType::Modified)
{
transfer->Read(message_id);
transfer->Read(modified_head);
transfer->Read(modified_message);
}
}

View File

@ -1,28 +0,0 @@
#pragma once
#include <cheat-base/PipeTransfer.h>
#include "MessageBase.h"
enum class PacketModifyType
{
Unchanged,
Modified,
Blocked
};
class ModifyData : public MessageBase
{
public:
PacketModifyType modify_type;
uint16_t message_id;
std::string modified_head;
std::string modified_message;
ModifyData(const MessageHeader& header);
~ModifyData() {}
// Inherited via PipeSerializedObject
void Write(PipeTransfer* transfer) override;
void Read(PipeTransfer* transfer) override;
};

View File

@ -1,20 +0,0 @@
#include <pch-il2cpp.h>
#include "PacketData.h"
void PacketData::Write(PipeTransfer* transfer)
{
transfer->Write(blockModeEnabled);
transfer->Write(ioType);
transfer->Write(messageID);
transfer->Write(headRawData);
transfer->Write(messageRawData);
}
void PacketData::Read(PipeTransfer* transfer)
{
transfer->Read(blockModeEnabled);
transfer->Read(ioType);
transfer->Read(messageID);
transfer->Read(headRawData);
transfer->Read(messageRawData);
}

View File

@ -1,42 +0,0 @@
#pragma once
#include <cheat-base/PipeTransfer.h>
#include "MessageBase.h"
enum class PacketIOType
{
Receive,
Send
};
enum class PacketDataType
{
Raw,
Parsed
};
class PacketData : public MessageBase
{
public:
using MessageBase::MessageBase;
bool blockModeEnabled;
PacketIOType ioType;
PacketDataType dataType;
bool valid;
uint64_t parentPacketID;
int16_t messageID;
std::vector<byte> headRawData;
std::vector<byte> messageRawData;
std::string name;
std::string headJson;
std::string messageJson;
// Inherited via PipeSerializedObject
virtual void Write(PipeTransfer* transfer) override;
virtual void Read(PipeTransfer* transfer) override;
};

View File

@ -0,0 +1,54 @@
#include "pch-il2cpp.h"
#include "PipeClient.h"
bool PipeClient::IsConnected()
{
if (m_Pipe == nullptr)
return false;
return m_Pipe->IsPipeOpened() || TryConnectToPipe();
}
void PipeClient::SetConnectionDelay(uint32_t delay)
{
m_ConnectionDelay = delay;
}
uint32_t PipeClient::GetConnectionDelay()
{
return m_ConnectionDelay;
}
void PipeClient::Connect(const std::string& pipeName)
{
if (m_Pipe != nullptr)
delete m_Pipe;
m_Pipe = new PipeTransfer(pipeName);
TryConnectToPipe();
}
void PipeClient::Disconnect()
{
if (m_Pipe == nullptr)
return;
delete m_Pipe;
}
bool PipeClient::TryConnectToPipe()
{
if (m_Pipe == nullptr)
return false;
std::time_t currTime = std::time(0);
if (m_NextTimeToConnect > currTime)
return false;
bool result = m_Pipe->Connect();
if (result)
LOG_INFO("Connected to pipe successfully.");
else
m_NextTimeToConnect = currTime + 5; // delay in 5 sec
return result;
}

View File

@ -0,0 +1,23 @@
#pragma once
#include <vector>
#include "PipeIO.h"
class PipeClient : public PipeIO
{
public:
PipeClient() : PipeIO(), m_ConnectionDelay(5), m_NextTimeToConnect(0) {}
void SetConnectionDelay(uint32_t delay);
uint32_t GetConnectionDelay();
bool IsConnected() final;
void Connect(const std::string& pipeName);
void Disconnect();
private:
uint32_t m_ConnectionDelay;
std::time_t m_NextTimeToConnect;
bool TryConnectToPipe();
};

View File

@ -0,0 +1,46 @@
#include "pch-il2cpp.h"
#include "PipeIO.h"
void PipeIO::Send(PipeMessage& data)
{
if (!IsConnected())
return;
m_Pipe->WriteObject(data);
}
PipeMessage* PipeIO::ReceiveMessage()
{
if (!IsConnected())
return nullptr;
PipeMessage header;
m_Pipe->ReadObject(header);
MessageIDs messageID = static_cast<MessageIDs>(header.packetID());
#define MESSAGE_CASE(mid, type)\
case mid: \
{ \
auto data = new type(); \
m_Pipe->ReadObject(*data); \
data->SetMessage(header); \
CallHandlers(*data); \
return reinterpret_cast<PipeMessage*>(data); \
}
switch (messageID)
{
MESSAGE_CASE(MessageIDs::PACKET_DATA, PipePacketData);
MESSAGE_CASE(MessageIDs::MODIFY_DATA, PipeModifyData);
default:
break;
}
return nullptr;
}
void PipeIO::ProcessMessage()
{
auto message = ReceiveMessage();
delete message;
}

View File

@ -0,0 +1,110 @@
#pragma once
#include "messages/PipeMessage.h"
#include "messages/PipeModifyData.h"
#include "messages/PipePacketData.h"
#include <vector>
#include <optional>
enum class MessageIDs : uint32_t
{
NONE = 0,
PACKET_DATA = 1,
MODIFY_DATA = 2
};
namespace internal
{
template<class TMessage> struct message_to_id;
template<> struct message_to_id<PipePacketData> : std::integral_constant<uint32_t, static_cast<uint32_t>(MessageIDs::PACKET_DATA)> {};
template<> struct message_to_id<PipeModifyData> : std::integral_constant<uint32_t, static_cast<uint32_t>(MessageIDs::MODIFY_DATA)> {};
}
class PipeIO
{
public:
template<class T>
using CallbackFunction = void(*)(const T& packetData);
template<class T>
void AddHandler(CallbackFunction<T> callback)
{
static_assert(std::is_base_of<PipeMessage, T>::value, "Should be derived of MessageBase.");
constexpr auto messageID = internal::message_to_id<T>::value;
m_Handlers.push_back({
messageID,
reinterpret_cast<UniqueCallbackFunction>(callback)
});
}
template<class TMessage>
TMessage CreateMessage(uint32_t reqID = 0, uint32_t seqID = 0)
{
static_assert(std::is_base_of<PipeMessage, TMessage>::value, "Should be derived of MessageBase.");
return TMessage(internal::message_to_id<TMessage>::value);
}
template<class TMessage>
std::optional<TMessage> WaitFor()
{
static_assert(std::is_base_of<PipeMessage, TMessage>::value, "Should be derived of MessageBase.");
if (!IsConnected())
return {};
while (true)
{
auto messagePtr = ReceiveMessage();
if (messagePtr == nullptr)
return {};
if (internal::message_to_id<TMessage>::value != messagePtr->packetID())
{
delete messagePtr;
continue;
}
// Copying message
TMessage message = *reinterpret_cast<TMessage*>(messagePtr);
delete messagePtr;
return message;
}
}
void Send(PipeMessage& data);
PipeMessage* ReceiveMessage();
void ProcessMessage();
virtual bool IsConnected() = 0;
protected:
PipeIO() : m_Pipe(nullptr) {};
PipeTransfer* m_Pipe;
private:
using UniqueCallbackFunction = void(*)(const PipeMessage& pipeMessage);
std::vector<std::pair<uint32_t, UniqueCallbackFunction>> m_Handlers;
template<class T>
void CallHandlers(const T& message)
{
for (auto& [id, handler] : m_Handlers)
{
if (id == internal::message_to_id<T>::value)
handler(message);
}
}
public:
PipeIO(PipeIO& other) = delete;
PipeIO(PipeIO&& other) = delete;
PipeIO& operator=(PipeIO& other) = delete;
PipeIO& operator=(PipeIO&& other) = delete;
};

View File

@ -0,0 +1,37 @@
#include "pch-il2cpp.h"
#include "PipeMessage.h"
PipeMessage::PipeMessage(uint32_t packetID) :
m_PipePacketID(packetID), m_Timestamp(util::GetCurrentTimeMillisec())
{ }
PipeMessage::PipeMessage() : m_PipePacketID(0), m_Timestamp(0)
{ }
uint32_t PipeMessage::packetID() const
{
return m_PipePacketID;
}
int64_t PipeMessage::timestamp() const
{
return m_Timestamp;
}
void PipeMessage::Write(PipeTransfer* transfer)
{
transfer->Write(m_PipePacketID);
transfer->Write(util::GetCurrentTimeMillisec());
}
void PipeMessage::Read(PipeTransfer* transfer)
{
transfer->Read(m_PipePacketID);
transfer->Read(m_Timestamp);
}
void PipeMessage::SetMessage(const PipeMessage& other)
{
m_PipePacketID = other.m_PipePacketID;
m_Timestamp = other.m_Timestamp;
}

View File

@ -0,0 +1,21 @@
#pragma once
#include <atomic>
#include <cheat-base/PipeTransfer.h>
class PipeMessage : public PipeSerializedObject
{
public:
PipeMessage();
PipeMessage(uint32_t packetID);
uint32_t packetID() const;
int64_t timestamp() const;
void SetMessage(const PipeMessage& other);
void Write(PipeTransfer* transfer) override;
void Read(PipeTransfer* transfer) override;
private:
uint32_t m_PipePacketID;
int64_t m_Timestamp;
};

View File

@ -0,0 +1,32 @@
#include "pch-il2cpp.h"
#include "PipeModifyData.h"
PipeModifyData::PipeModifyData() :
modifyType(ModifyType::Unchanged), head({}), content({}), messageID(0)
{ }
void PipeModifyData::Write(PipeTransfer* transfer)
{
PipeMessage::Write(transfer);
transfer->Write(modifyType);
if (modifyType == ModifyType::Modified)
{
transfer->Write(messageID);
transfer->Write(content);
transfer->Write(head);
}
}
void PipeModifyData::Read(PipeTransfer* transfer)
{
transfer->Read(modifyType);
if (modifyType == ModifyType::Modified)
{
transfer->Read(messageID);
transfer->Read(content);
transfer->Read(head);
}
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <cheat-base/PipeTransfer.h>
#include "PipeMessage.h"
enum class ModifyType
{
Unchanged, Blocked, Modified
};
class PipeModifyData : public PipeMessage
{
public:
using PipeMessage::PipeMessage;
ModifyType modifyType;
uint32_t messageID;
std::vector<uint8_t> head;
std::vector<uint8_t> content;
PipeModifyData();
~PipeModifyData() {}
// Inherited via PipeSerializedObject
virtual void Write(PipeTransfer* transfer) final;
virtual void Read(PipeTransfer* transfer) final;
};

View File

@ -0,0 +1,22 @@
#include "pch-il2cpp.h"
#include "PipePacketData.h"
void PipePacketData::Write(PipeTransfer* transfer)
{
PipeMessage::Write(transfer);
transfer->Write(manipulationEnabled);
transfer->Write(direction);
transfer->Write(messageID);
transfer->Write(head);
transfer->Write(content);
}
void PipePacketData::Read(PipeTransfer* transfer)
{
transfer->Read(manipulationEnabled);
transfer->Read(direction);
transfer->Read(messageID);
transfer->Read(head);
transfer->Read(content);
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "PipeMessage.h"
enum class NetIODirection
{
Send, Receive
};
class PipePacketData : public PipeMessage
{
public:
using PipeMessage::PipeMessage;
bool manipulationEnabled;
NetIODirection direction;
int16_t messageID;
std::vector<byte> head;
std::vector<byte> content;
// Inherited via PipeSerializedObject
void Write(PipeTransfer* transfer) final;
void Read(PipeTransfer* transfer) final;
};