From e7ee41ca2352ebb856301e7623936d120b272292 Mon Sep 17 00:00:00 2001 From: linuxversion <1660477+SomeCrazyGuy@users.noreply.github.com> Date: Wed, 22 May 2024 21:33:11 -0400 Subject: [PATCH 1/3] Revert "version 1.2.20" This reverts commit 3b9932068ae36e987e4b2584fa409af536daca18. --- betterapi.h | 3 --- src/hook_api.cpp | 61 ++++++++---------------------------------------- src/main.h | 5 ++-- 3 files changed, 12 insertions(+), 57 deletions(-) diff --git a/betterapi.h b/betterapi.h index e057822..6fe0ec1 100644 --- a/betterapi.h +++ b/betterapi.h @@ -198,9 +198,6 @@ struct hook_api_t { // !EXPERIMENTAL API! AOB scan the exe memory and return the first match void* (*AOBScanEXE)(const char* signature); - - // !EXPERIMENTAL API! get exe version as unsigned int or 0 on failure - unsigned (*GetExeVersion)(); }; // This API allows you to create a basic mod menu without linking to imgui diff --git a/src/hook_api.cpp b/src/hook_api.cpp index bd16dfe..e98fed5 100644 --- a/src/hook_api.cpp +++ b/src/hook_api.cpp @@ -163,7 +163,7 @@ static FUNC_PTR HookFunctionIAT(const char* dll_name, const char* func_name, con } -static void* AOBScanBuffer(const unsigned char* buffer, unsigned buffer_size, const char* signature) { +static void* AOBScanEXE(const char* signature) { constexpr uint16_t sig_end = 0xFFFF; const auto siglen = strlen(signature); @@ -224,10 +224,16 @@ static void* AOBScanBuffer(const unsigned char* buffer, unsigned buffer_size, co sig[matchcount] = sig_end; - for (unsigned i = 0; i < buffer_size; ++i) { + const auto hdr1 = (const IMAGE_DOS_HEADER*)Relocate(0); + const auto hdr2 = (const IMAGE_NT_HEADERS64*)Relocate(hdr1->e_lfanew); + const unsigned char* haystack = (const unsigned char*)Relocate(0); + const unsigned count = hdr2->OptionalHeader.SizeOfImage; + + + for (unsigned i = 0; i < count; ++i) { auto match = 0; - while ((buffer[i] & (sig[match] >> 8)) == (sig[match] & 0xFF)) { + while ((haystack[i] & (sig[match] >> 8)) == (sig[match] & 0xFF)) { ++i; ++match; if (sig[match] == sig_end) { @@ -237,56 +243,10 @@ static void* AOBScanBuffer(const unsigned char* buffer, unsigned buffer_size, co } } - free(sig); return NULL; } -static void* AOBScanEXE(const char* signature) { - const auto hdr1 = (const IMAGE_DOS_HEADER*)Relocate(0); - const auto hdr2 = (const IMAGE_NT_HEADERS64*)Relocate(hdr1->e_lfanew); - const unsigned char* haystack = (const unsigned char*)hdr1; - const unsigned count = hdr2->OptionalHeader.SizeOfImage; - return AOBScanBuffer(haystack, count, signature); -} - - -static unsigned GetExeVersion() { - const auto hdr1 = (const IMAGE_DOS_HEADER*)Relocate(0); - const auto hdr2 = (const IMAGE_NT_HEADERS64*)Relocate(hdr1->e_lfanew); - const auto sections = (const IMAGE_SECTION_HEADER*)Relocate(hdr1->e_lfanew + sizeof(IMAGE_NT_HEADERS64)); - const auto num_sections = hdr2->FileHeader.NumberOfSections; - - const unsigned char* rsrc = nullptr; - uint32_t rsrc_size = 0; - for (auto i = 0; i < num_sections; ++i) { - if (*(const uint32_t*)sections[i].Name == *(const uint32_t*)".rsrc") { - rsrc = (const unsigned char*)Relocate(sections[i].VirtualAddress); - rsrc_size = sections[i].SizeOfRawData; - } - } - - if (!rsrc_size) { - return 0; - } - - auto ver = (const unsigned char*)AOBScanBuffer(rsrc, rsrc_size, "50 00 72 00 6f 00 64 00 75 00 63 00 74 00 56 00 65 00 72 00 73 00 69 00 6f 00 6e 00"); - - if (!ver) { - return 0; - } - - ver += 30; - - unsigned major; - unsigned minor; - unsigned patch; - unsigned revision; - swscanf_s((const wchar_t*)ver, L"%u.%u.%u.%u", &major, &minor, &patch, &revision); - - return MAKE_VERSION(major, minor, patch); -} - static constexpr struct hook_api_t HookAPI { &HookFunction, @@ -295,8 +255,7 @@ static constexpr struct hook_api_t HookAPI { &SafeWriteMemory, &GetProcAddressFromIAT, &HookFunctionIAT, - &AOBScanEXE, - &GetExeVersion + &AOBScanEXE }; extern constexpr const struct hook_api_t* GetHookAPI() { diff --git a/src/main.h b/src/main.h index 6ac0258..2bacb0b 100644 --- a/src/main.h +++ b/src/main.h @@ -48,7 +48,7 @@ inline constexpr auto file_name_only(const char* const in) noexcept -> const cha #include "../imgui/imgui.h" -#define BETTERCONSOLE_VERSION "1.2.20" +#define BETTERCONSOLE_VERSION "1.2.19" // -------------------------------------------------------------------- @@ -64,8 +64,7 @@ constexpr uint32_t GAME_VERSION = MAKE_VERSION(1, 11, 36); struct ModMenuSettings { int HotkeyModifier = 0; int ConsoleHotkey = 112; //VK_F1 - int FontScaleOverride = 100; - int IgnoreGameVersion = 0; + int FontScaleOverride = 0; }; extern const ModMenuSettings* GetSettings(); From c8d2a44098f9102c4a59ef187c1fa761c5e73b7c Mon Sep 17 00:00:00 2001 From: linuxversion <1660477+SomeCrazyGuy@users.noreply.github.com> Date: Tue, 4 Jun 2024 01:30:19 -0400 Subject: [PATCH 2/3] fix compatibility with other overlays --- Starfield Console Replacer.vcxproj | 11 +- Starfield Console Replacer.vcxproj.filters | 9 + VersionInfo.rc | Bin 4738 -> 4730 bytes imgui/imgui_impl_dx11.cpp | 594 ++++++++++++++++ imgui/imgui_impl_dx11.h | 26 + imgui/imgui_impl_dx12.cpp | 758 --------------------- imgui/imgui_impl_dx12.h | 41 -- src/d3d11on12ui.cpp | 136 ++++ src/d3d11on12ui.h | 5 + src/fake_vcruntime140_1.cpp | 11 +- src/gui.cpp | 23 +- src/main.cpp | 328 ++++----- src/main.h | 23 +- src/settings.cpp | 5 +- 14 files changed, 943 insertions(+), 1027 deletions(-) create mode 100644 imgui/imgui_impl_dx11.cpp create mode 100644 imgui/imgui_impl_dx11.h delete mode 100644 imgui/imgui_impl_dx12.cpp delete mode 100644 imgui/imgui_impl_dx12.h create mode 100644 src/d3d11on12ui.cpp create mode 100644 src/d3d11on12ui.h diff --git a/Starfield Console Replacer.vcxproj b/Starfield Console Replacer.vcxproj index eb572a1..b5504e3 100644 --- a/Starfield Console Replacer.vcxproj +++ b/Starfield Console Replacer.vcxproj @@ -97,7 +97,7 @@ Console DebugFull - $(MSBuildProjectDirectory)\minhook\libMinHook.x64.lib;dxgi.lib;d3d12.lib;%(AdditionalDependencies) + $(MSBuildProjectDirectory)\minhook\libMinHook.x64.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies) true @@ -130,7 +130,7 @@ true true DebugFull - $(MSBuildProjectDirectory)\minhook\libMinHook.x64.lib;dxgi.lib;d3d12.lib;%(AdditionalDependencies) + $(MSBuildProjectDirectory)\minhook\libMinHook.x64.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies) true @@ -154,7 +154,7 @@ copy /Y "$(OutDir)$(TargetName).dll" "$(OutDir)BetterConsole.asi" Console DebugFull - $(MSBuildProjectDirectory)\minhook\libMinHook.x64.lib;dxgi.lib;d3d12.lib;%(AdditionalDependencies) + $(MSBuildProjectDirectory)\minhook\libMinHook.x64.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies) true @@ -188,7 +188,7 @@ copy /Y "$(OutDir)$(TargetName).dll" "$(OutDir)BetterConsole.asi" true true DebugFull - $(MSBuildProjectDirectory)\minhook\libMinHook.x64.lib;dxgi.lib;d3d12.lib;%(AdditionalDependencies) + $(MSBuildProjectDirectory)\minhook\libMinHook.x64.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies) true @@ -203,12 +203,14 @@ copy /Y "$(OutDir)$(TargetName).dll" "$(OutDir)BetterConsole.asi" + + @@ -233,6 +235,7 @@ copy /Y "$(OutDir)$(TargetName).dll" "$(OutDir)BetterConsole.asi" + diff --git a/Starfield Console Replacer.vcxproj.filters b/Starfield Console Replacer.vcxproj.filters index 543a43d..118fa87 100644 --- a/Starfield Console Replacer.vcxproj.filters +++ b/Starfield Console Replacer.vcxproj.filters @@ -74,6 +74,12 @@ vcruntime140_1 interface + + imgui + + + Source Files + @@ -139,6 +145,9 @@ Header Files + + Header Files + diff --git a/VersionInfo.rc b/VersionInfo.rc index 75f87972015f33de9e9f10aaae1579e1e386a3d1..5b71a43de47967025dd60254f7435667450ec7be 100644 GIT binary patch delta 60 zcmZot{iU*@kAvBmL1*%04t)^4`8Y>5Gp8|w9)kf0PyWcIyE%*J2_rjHXmUQ6XXXX). +// 2021-05-19: DirectX11: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) +// 2021-02-18: DirectX11: Change blending equation to preserve alpha in output buffer. +// 2019-08-01: DirectX11: Fixed code querying the Geometry Shader state (would generally error with Debug layer enabled). +// 2019-07-21: DirectX11: Backup, clear and restore Geometry Shader is any is bound when calling ImGui_ImplDX10_RenderDrawData. Clearing Hull/Domain/Compute shaders without backup/restore. +// 2019-05-29: DirectX11: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. +// 2019-04-30: DirectX11: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. +// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile(). +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. +// 2018-08-01: DirectX11: Querying for IDXGIFactory instead of IDXGIFactory1 to increase compatibility. +// 2018-07-13: DirectX11: Fixed unreleased resources in Init and Shutdown functions. +// 2018-06-08: Misc: Extracted imgui_impl_dx11.cpp/.h away from the old combined DX11+Win32 example. +// 2018-06-08: DirectX11: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. +// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX11_RenderDrawData() in the .h file so you can call it yourself. +// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. +// 2016-05-07: DirectX11: Disabling depth-write. + +#include "imgui.h" +#include "imgui_impl_dx11.h" + +// DirectX +#include +#include +#include +#ifdef _MSC_VER +#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. +#endif + +// DirectX11 data +struct ImGui_ImplDX11_Data +{ + ID3D11Device* pd3dDevice; + ID3D11DeviceContext* pd3dDeviceContext; + IDXGIFactory* pFactory; + ID3D11Buffer* pVB; + ID3D11Buffer* pIB; + ID3D11VertexShader* pVertexShader; + ID3D11InputLayout* pInputLayout; + ID3D11Buffer* pVertexConstantBuffer; + ID3D11PixelShader* pPixelShader; + ID3D11SamplerState* pFontSampler; + ID3D11ShaderResourceView* pFontTextureView; + ID3D11RasterizerState* pRasterizerState; + ID3D11BlendState* pBlendState; + ID3D11DepthStencilState* pDepthStencilState; + int VertexBufferSize; + int IndexBufferSize; + + ImGui_ImplDX11_Data() { memset(this, 0, sizeof(*this)); VertexBufferSize = 5000; IndexBufferSize = 10000; } +}; + +struct VERTEX_CONSTANT_BUFFER +{ + float mvp[4][4]; +}; + +// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +static ImGui_ImplDX11_Data* ImGui_ImplDX11_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplDX11_Data*)ImGui::GetIO().BackendRendererUserData : NULL; +} + +// Functions +static void ImGui_ImplDX11_SetupRenderState(ImDrawData* draw_data, ID3D11DeviceContext* ctx) +{ + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + + // Setup viewport + D3D11_VIEWPORT vp; + memset(&vp, 0, sizeof(D3D11_VIEWPORT)); + vp.Width = draw_data->DisplaySize.x; + vp.Height = draw_data->DisplaySize.y; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + vp.TopLeftX = vp.TopLeftY = 0; + ctx->RSSetViewports(1, &vp); + + // Setup shader and vertex buffers + unsigned int stride = sizeof(ImDrawVert); + unsigned int offset = 0; + ctx->IASetInputLayout(bd->pInputLayout); + ctx->IASetVertexBuffers(0, 1, &bd->pVB, &stride, &offset); + ctx->IASetIndexBuffer(bd->pIB, sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT, 0); + ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + ctx->VSSetShader(bd->pVertexShader, NULL, 0); + ctx->VSSetConstantBuffers(0, 1, &bd->pVertexConstantBuffer); + ctx->PSSetShader(bd->pPixelShader, NULL, 0); + ctx->PSSetSamplers(0, 1, &bd->pFontSampler); + ctx->GSSetShader(NULL, NULL, 0); + ctx->HSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. + ctx->DSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. + ctx->CSSetShader(NULL, NULL, 0); // In theory we should backup and restore this as well.. very infrequently used.. + + // Setup blend state + const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; + ctx->OMSetBlendState(bd->pBlendState, blend_factor, 0xffffffff); + ctx->OMSetDepthStencilState(bd->pDepthStencilState, 0); + ctx->RSSetState(bd->pRasterizerState); +} + +// Render function +void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data) +{ + // Avoid rendering when minimized + if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) + return; + + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + ID3D11DeviceContext* ctx = bd->pd3dDeviceContext; + + // Create and grow vertex/index buffers if needed + if (!bd->pVB || bd->VertexBufferSize < draw_data->TotalVtxCount) + { + if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; } + bd->VertexBufferSize = draw_data->TotalVtxCount + 5000; + D3D11_BUFFER_DESC desc; + memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.ByteWidth = bd->VertexBufferSize * sizeof(ImDrawVert); + desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.MiscFlags = 0; + if (bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVB) < 0) + return; + } + if (!bd->pIB || bd->IndexBufferSize < draw_data->TotalIdxCount) + { + if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; } + bd->IndexBufferSize = draw_data->TotalIdxCount + 10000; + D3D11_BUFFER_DESC desc; + memset(&desc, 0, sizeof(D3D11_BUFFER_DESC)); + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.ByteWidth = bd->IndexBufferSize * sizeof(ImDrawIdx); + desc.BindFlags = D3D11_BIND_INDEX_BUFFER; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + if (bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pIB) < 0) + return; + } + + // Upload vertex/index data into a single contiguous GPU buffer + D3D11_MAPPED_SUBRESOURCE vtx_resource, idx_resource; + if (ctx->Map(bd->pVB, 0, D3D11_MAP_WRITE_DISCARD, 0, &vtx_resource) != S_OK) + return; + if (ctx->Map(bd->pIB, 0, D3D11_MAP_WRITE_DISCARD, 0, &idx_resource) != S_OK) + return; + ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource.pData; + ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource.pData; + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); + memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); + vtx_dst += cmd_list->VtxBuffer.Size; + idx_dst += cmd_list->IdxBuffer.Size; + } + ctx->Unmap(bd->pVB, 0); + ctx->Unmap(bd->pIB, 0); + + // Setup orthographic projection matrix into our constant buffer + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. + { + D3D11_MAPPED_SUBRESOURCE mapped_resource; + if (ctx->Map(bd->pVertexConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource) != S_OK) + return; + VERTEX_CONSTANT_BUFFER* constant_buffer = (VERTEX_CONSTANT_BUFFER*)mapped_resource.pData; + float L = draw_data->DisplayPos.x; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; + float T = draw_data->DisplayPos.y; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; + float mvp[4][4] = + { + { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, + { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.5f, 0.0f }, + { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, + }; + memcpy(&constant_buffer->mvp, mvp, sizeof(mvp)); + ctx->Unmap(bd->pVertexConstantBuffer, 0); + } + + // Backup DX state that will be modified to restore it afterwards (unfortunately this is very ugly looking and verbose. Close your eyes!) + struct BACKUP_DX11_STATE + { + UINT ScissorRectsCount, ViewportsCount; + D3D11_RECT ScissorRects[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; + D3D11_VIEWPORT Viewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; + ID3D11RasterizerState* RS; + ID3D11BlendState* BlendState; + FLOAT BlendFactor[4]; + UINT SampleMask; + UINT StencilRef; + ID3D11DepthStencilState* DepthStencilState; + ID3D11ShaderResourceView* PSShaderResource; + ID3D11SamplerState* PSSampler; + ID3D11PixelShader* PS; + ID3D11VertexShader* VS; + ID3D11GeometryShader* GS; + UINT PSInstancesCount, VSInstancesCount, GSInstancesCount; + ID3D11ClassInstance *PSInstances[256], *VSInstances[256], *GSInstances[256]; // 256 is max according to PSSetShader documentation + D3D11_PRIMITIVE_TOPOLOGY PrimitiveTopology; + ID3D11Buffer* IndexBuffer, *VertexBuffer, *VSConstantBuffer; + UINT IndexBufferOffset, VertexBufferStride, VertexBufferOffset; + DXGI_FORMAT IndexBufferFormat; + ID3D11InputLayout* InputLayout; + }; + BACKUP_DX11_STATE old = {}; + old.ScissorRectsCount = old.ViewportsCount = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; + ctx->RSGetScissorRects(&old.ScissorRectsCount, old.ScissorRects); + ctx->RSGetViewports(&old.ViewportsCount, old.Viewports); + ctx->RSGetState(&old.RS); + ctx->OMGetBlendState(&old.BlendState, old.BlendFactor, &old.SampleMask); + ctx->OMGetDepthStencilState(&old.DepthStencilState, &old.StencilRef); + ctx->PSGetShaderResources(0, 1, &old.PSShaderResource); + ctx->PSGetSamplers(0, 1, &old.PSSampler); + old.PSInstancesCount = old.VSInstancesCount = old.GSInstancesCount = 256; + ctx->PSGetShader(&old.PS, old.PSInstances, &old.PSInstancesCount); + ctx->VSGetShader(&old.VS, old.VSInstances, &old.VSInstancesCount); + ctx->VSGetConstantBuffers(0, 1, &old.VSConstantBuffer); + ctx->GSGetShader(&old.GS, old.GSInstances, &old.GSInstancesCount); + + ctx->IAGetPrimitiveTopology(&old.PrimitiveTopology); + ctx->IAGetIndexBuffer(&old.IndexBuffer, &old.IndexBufferFormat, &old.IndexBufferOffset); + ctx->IAGetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); + ctx->IAGetInputLayout(&old.InputLayout); + + // Setup desired DX state + ImGui_ImplDX11_SetupRenderState(draw_data, ctx); + + // Render command lists + // (Because we merged all buffers into a single one, we maintain our own offset into them) + int global_idx_offset = 0; + int global_vtx_offset = 0; + ImVec2 clip_off = draw_data->DisplayPos; + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback != NULL) + { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + ImGui_ImplDX11_SetupRenderState(draw_data, ctx); + else + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); + ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) + continue; + + // Apply scissor/clipping rectangle + const D3D11_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y }; + ctx->RSSetScissorRects(1, &r); + + // Bind texture, Draw + ID3D11ShaderResourceView* texture_srv = (ID3D11ShaderResourceView*)pcmd->GetTexID(); + ctx->PSSetShaderResources(0, 1, &texture_srv); + ctx->DrawIndexed(pcmd->ElemCount, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset); + } + } + global_idx_offset += cmd_list->IdxBuffer.Size; + global_vtx_offset += cmd_list->VtxBuffer.Size; + } + + // Restore modified DX state + ctx->RSSetScissorRects(old.ScissorRectsCount, old.ScissorRects); + ctx->RSSetViewports(old.ViewportsCount, old.Viewports); + ctx->RSSetState(old.RS); if (old.RS) old.RS->Release(); + ctx->OMSetBlendState(old.BlendState, old.BlendFactor, old.SampleMask); if (old.BlendState) old.BlendState->Release(); + ctx->OMSetDepthStencilState(old.DepthStencilState, old.StencilRef); if (old.DepthStencilState) old.DepthStencilState->Release(); + ctx->PSSetShaderResources(0, 1, &old.PSShaderResource); if (old.PSShaderResource) old.PSShaderResource->Release(); + ctx->PSSetSamplers(0, 1, &old.PSSampler); if (old.PSSampler) old.PSSampler->Release(); + ctx->PSSetShader(old.PS, old.PSInstances, old.PSInstancesCount); if (old.PS) old.PS->Release(); + for (UINT i = 0; i < old.PSInstancesCount; i++) if (old.PSInstances[i]) old.PSInstances[i]->Release(); + ctx->VSSetShader(old.VS, old.VSInstances, old.VSInstancesCount); if (old.VS) old.VS->Release(); + ctx->VSSetConstantBuffers(0, 1, &old.VSConstantBuffer); if (old.VSConstantBuffer) old.VSConstantBuffer->Release(); + ctx->GSSetShader(old.GS, old.GSInstances, old.GSInstancesCount); if (old.GS) old.GS->Release(); + for (UINT i = 0; i < old.VSInstancesCount; i++) if (old.VSInstances[i]) old.VSInstances[i]->Release(); + ctx->IASetPrimitiveTopology(old.PrimitiveTopology); + ctx->IASetIndexBuffer(old.IndexBuffer, old.IndexBufferFormat, old.IndexBufferOffset); if (old.IndexBuffer) old.IndexBuffer->Release(); + ctx->IASetVertexBuffers(0, 1, &old.VertexBuffer, &old.VertexBufferStride, &old.VertexBufferOffset); if (old.VertexBuffer) old.VertexBuffer->Release(); + ctx->IASetInputLayout(old.InputLayout); if (old.InputLayout) old.InputLayout->Release(); +} + +static void ImGui_ImplDX11_CreateFontsTexture() +{ + // Build texture atlas + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + + // Upload texture to graphics system + { + D3D11_TEXTURE2D_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Width = width; + desc.Height = height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + + ID3D11Texture2D* pTexture = NULL; + D3D11_SUBRESOURCE_DATA subResource; + subResource.pSysMem = pixels; + subResource.SysMemPitch = desc.Width * 4; + subResource.SysMemSlicePitch = 0; + bd->pd3dDevice->CreateTexture2D(&desc, &subResource, &pTexture); + IM_ASSERT(pTexture != NULL); + + // Create texture view + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc; + ZeroMemory(&srvDesc, sizeof(srvDesc)); + srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MipLevels = desc.MipLevels; + srvDesc.Texture2D.MostDetailedMip = 0; + bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, &bd->pFontTextureView); + pTexture->Release(); + } + + // Store our identifier + io.Fonts->SetTexID((ImTextureID)bd->pFontTextureView); + + // Create texture sampler + { + D3D11_SAMPLER_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + desc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP; + desc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP; + desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + desc.MipLODBias = 0.f; + desc.ComparisonFunc = D3D11_COMPARISON_ALWAYS; + desc.MinLOD = 0.f; + desc.MaxLOD = 0.f; + bd->pd3dDevice->CreateSamplerState(&desc, &bd->pFontSampler); + } +} + +bool ImGui_ImplDX11_CreateDeviceObjects() +{ + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + if (!bd->pd3dDevice) + return false; + if (bd->pFontSampler) + ImGui_ImplDX11_InvalidateDeviceObjects(); + + // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) + // If you would like to use this DX11 sample code but remove this dependency you can: + // 1) compile once, save the compiled shader blobs into a file or source code and pass them to CreateVertexShader()/CreatePixelShader() [preferred solution] + // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL. + // See https://github.com/ocornut/imgui/pull/638 for sources and details. + + // Create the vertex shader + { + static const char* vertexShader = + "cbuffer vertexBuffer : register(b0) \ + {\ + float4x4 ProjectionMatrix; \ + };\ + struct VS_INPUT\ + {\ + float2 pos : POSITION;\ + float4 col : COLOR0;\ + float2 uv : TEXCOORD0;\ + };\ + \ + struct PS_INPUT\ + {\ + float4 pos : SV_POSITION;\ + float4 col : COLOR0;\ + float2 uv : TEXCOORD0;\ + };\ + \ + PS_INPUT main(VS_INPUT input)\ + {\ + PS_INPUT output;\ + output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\ + output.col = input.col;\ + output.uv = input.uv;\ + return output;\ + }"; + + ID3DBlob* vertexShaderBlob; + if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), NULL, NULL, NULL, "main", "vs_4_0", 0, 0, &vertexShaderBlob, NULL))) + return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! + if (bd->pd3dDevice->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), NULL, &bd->pVertexShader) != S_OK) + { + vertexShaderBlob->Release(); + return false; + } + + // Create the input layout + D3D11_INPUT_ELEMENT_DESC local_layout[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D11_INPUT_PER_VERTEX_DATA, 0 }, + { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + if (bd->pd3dDevice->CreateInputLayout(local_layout, 3, vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), &bd->pInputLayout) != S_OK) + { + vertexShaderBlob->Release(); + return false; + } + vertexShaderBlob->Release(); + + // Create the constant buffer + { + D3D11_BUFFER_DESC desc; + desc.ByteWidth = sizeof(VERTEX_CONSTANT_BUFFER); + desc.Usage = D3D11_USAGE_DYNAMIC; + desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + desc.MiscFlags = 0; + bd->pd3dDevice->CreateBuffer(&desc, NULL, &bd->pVertexConstantBuffer); + } + } + + // Create the pixel shader + { + static const char* pixelShader = + "struct PS_INPUT\ + {\ + float4 pos : SV_POSITION;\ + float4 col : COLOR0;\ + float2 uv : TEXCOORD0;\ + };\ + sampler sampler0;\ + Texture2D texture0;\ + \ + float4 main(PS_INPUT input) : SV_Target\ + {\ + float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \ + return out_col; \ + }"; + + ID3DBlob* pixelShaderBlob; + if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), NULL, NULL, NULL, "main", "ps_4_0", 0, 0, &pixelShaderBlob, NULL))) + return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! + if (bd->pd3dDevice->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), NULL, &bd->pPixelShader) != S_OK) + { + pixelShaderBlob->Release(); + return false; + } + pixelShaderBlob->Release(); + } + + // Create the blending setup + { + D3D11_BLEND_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.AlphaToCoverageEnable = false; + desc.RenderTarget[0].BlendEnable = true; + desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; + desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; + desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + bd->pd3dDevice->CreateBlendState(&desc, &bd->pBlendState); + } + + // Create the rasterizer state + { + D3D11_RASTERIZER_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.FillMode = D3D11_FILL_SOLID; + desc.CullMode = D3D11_CULL_NONE; + desc.ScissorEnable = true; + desc.DepthClipEnable = true; + bd->pd3dDevice->CreateRasterizerState(&desc, &bd->pRasterizerState); + } + + // Create depth-stencil State + { + D3D11_DEPTH_STENCIL_DESC desc; + ZeroMemory(&desc, sizeof(desc)); + desc.DepthEnable = false; + desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + desc.DepthFunc = D3D11_COMPARISON_ALWAYS; + desc.StencilEnable = false; + desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + desc.BackFace = desc.FrontFace; + bd->pd3dDevice->CreateDepthStencilState(&desc, &bd->pDepthStencilState); + } + + ImGui_ImplDX11_CreateFontsTexture(); + + return true; +} + +void ImGui_ImplDX11_InvalidateDeviceObjects() +{ + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + if (!bd->pd3dDevice) + return; + + if (bd->pFontSampler) { bd->pFontSampler->Release(); bd->pFontSampler = NULL; } + if (bd->pFontTextureView) { bd->pFontTextureView->Release(); bd->pFontTextureView = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied data->pFontTextureView to io.Fonts->TexID so let's clear that as well. + if (bd->pIB) { bd->pIB->Release(); bd->pIB = NULL; } + if (bd->pVB) { bd->pVB->Release(); bd->pVB = NULL; } + if (bd->pBlendState) { bd->pBlendState->Release(); bd->pBlendState = NULL; } + if (bd->pDepthStencilState) { bd->pDepthStencilState->Release(); bd->pDepthStencilState = NULL; } + if (bd->pRasterizerState) { bd->pRasterizerState->Release(); bd->pRasterizerState = NULL; } + if (bd->pPixelShader) { bd->pPixelShader->Release(); bd->pPixelShader = NULL; } + if (bd->pVertexConstantBuffer) { bd->pVertexConstantBuffer->Release(); bd->pVertexConstantBuffer = NULL; } + if (bd->pInputLayout) { bd->pInputLayout->Release(); bd->pInputLayout = NULL; } + if (bd->pVertexShader) { bd->pVertexShader->Release(); bd->pVertexShader = NULL; } +} + +bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context) +{ + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + ImGui_ImplDX11_Data* bd = IM_NEW(ImGui_ImplDX11_Data)(); + io.BackendRendererUserData = (void*)bd; + io.BackendRendererName = "imgui_impl_dx11"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + + // Get factory from device + IDXGIDevice* pDXGIDevice = NULL; + IDXGIAdapter* pDXGIAdapter = NULL; + IDXGIFactory* pFactory = NULL; + + if (device->QueryInterface(IID_PPV_ARGS(&pDXGIDevice)) == S_OK) + if (pDXGIDevice->GetParent(IID_PPV_ARGS(&pDXGIAdapter)) == S_OK) + if (pDXGIAdapter->GetParent(IID_PPV_ARGS(&pFactory)) == S_OK) + { + bd->pd3dDevice = device; + bd->pd3dDeviceContext = device_context; + bd->pFactory = pFactory; + } + if (pDXGIDevice) pDXGIDevice->Release(); + if (pDXGIAdapter) pDXGIAdapter->Release(); + bd->pd3dDevice->AddRef(); + bd->pd3dDeviceContext->AddRef(); + + return true; +} + +void ImGui_ImplDX11_Shutdown() +{ + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + ImGui_ImplDX11_InvalidateDeviceObjects(); + if (bd->pFactory) { bd->pFactory->Release(); } + if (bd->pd3dDevice) { bd->pd3dDevice->Release(); } + if (bd->pd3dDeviceContext) { bd->pd3dDeviceContext->Release(); } + io.BackendRendererName = NULL; + io.BackendRendererUserData = NULL; + IM_DELETE(bd); +} + +void ImGui_ImplDX11_NewFrame() +{ + ImGui_ImplDX11_Data* bd = ImGui_ImplDX11_GetBackendData(); + IM_ASSERT(bd != NULL && "Did you call ImGui_ImplDX11_Init()?"); + + if (!bd->pFontSampler) + ImGui_ImplDX11_CreateDeviceObjects(); +} diff --git a/imgui/imgui_impl_dx11.h b/imgui/imgui_impl_dx11.h new file mode 100644 index 0000000..a83bce1 --- /dev/null +++ b/imgui/imgui_impl_dx11.h @@ -0,0 +1,26 @@ +// dear imgui: Renderer Backend for DirectX11 +// This needs to be used along with a Platform Backend (e.g. Win32) + +// Implemented features: +// [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. +// Read online: https://github.com/ocornut/imgui/tree/master/docs + +#pragma once +#include "imgui.h" // IMGUI_IMPL_API + +struct ID3D11Device; +struct ID3D11DeviceContext; + +IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context); +IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplDX11_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data); + +// Use if you want to reset your rendering device without losing Dear ImGui state. +IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects(); +IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects(); diff --git a/imgui/imgui_impl_dx12.cpp b/imgui/imgui_impl_dx12.cpp deleted file mode 100644 index cf9afd6..0000000 --- a/imgui/imgui_impl_dx12.cpp +++ /dev/null @@ -1,758 +0,0 @@ -// dear imgui: Renderer Backend for DirectX12 -// This needs to be used along with a Platform Backend (e.g. Win32) - -// Implemented features: -// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. - -// Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. -// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. -// To build this on 32-bit systems: -// - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in the 'example_win32_direct12/example_win32_direct12.vcxproj' project file) -// - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like. -// - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!) -// - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in the example_win32_direct12/build_win32.bat file) - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. -// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). -// 2021-05-19: DirectX12: Replaced direct access to ImDrawCmd::TextureId with a call to ImDrawCmd::GetTexID(). (will become a requirement) -// 2021-02-18: DirectX12: Change blending equation to preserve alpha in output buffer. -// 2021-01-11: DirectX12: Improve Windows 7 compatibility (for D3D12On7) by loading d3d12.dll dynamically. -// 2020-09-16: DirectX12: Avoid rendering calls with zero-sized scissor rectangle since it generates a validation layer warning. -// 2020-09-08: DirectX12: Clarified support for building on 32-bit systems by redefining ImTextureID. -// 2019-10-18: DirectX12: *BREAKING CHANGE* Added extra ID3D12DescriptorHeap parameter to ImGui_ImplDX12_Init() function. -// 2019-05-29: DirectX12: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. -// 2019-04-30: DirectX12: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. -// 2019-03-29: Misc: Various minor tidying up. -// 2018-12-03: Misc: Added #pragma comment statement to automatically link with d3dcompiler.lib when using D3DCompile(). -// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. -// 2018-06-12: DirectX12: Moved the ID3D12GraphicsCommandList* parameter from NewFrame() to RenderDrawData(). -// 2018-06-08: Misc: Extracted imgui_impl_dx12.cpp/.h away from the old combined DX12+Win32 example. -// 2018-06-08: DirectX12: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle (to ease support for future multi-viewport). -// 2018-02-22: Merged into master with all Win32 code synchronized to other examples. - -#include "imgui.h" -#ifndef IMGUI_DISABLE -#include "imgui_impl_dx12.h" - -// DirectX -#include -#include -#include -#ifdef _MSC_VER -#pragma comment(lib, "d3dcompiler") // Automatically link with d3dcompiler.lib as we are using D3DCompile() below. -#endif - -// DirectX data -struct ImGui_ImplDX12_RenderBuffers; -struct ImGui_ImplDX12_Data -{ - ID3D12Device* pd3dDevice; - ID3D12RootSignature* pRootSignature; - ID3D12PipelineState* pPipelineState; - DXGI_FORMAT RTVFormat; - ID3D12Resource* pFontTextureResource; - D3D12_CPU_DESCRIPTOR_HANDLE hFontSrvCpuDescHandle; - D3D12_GPU_DESCRIPTOR_HANDLE hFontSrvGpuDescHandle; - ID3D12DescriptorHeap* pd3dSrvDescHeap; - UINT numFramesInFlight; - - ImGui_ImplDX12_RenderBuffers* pFrameResources; - UINT frameIndex; - - ImGui_ImplDX12_Data() { memset((void*)this, 0, sizeof(*this)); frameIndex = UINT_MAX; } -}; - -// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts -// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. -static ImGui_ImplDX12_Data* ImGui_ImplDX12_GetBackendData() -{ - return ImGui::GetCurrentContext() ? (ImGui_ImplDX12_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; -} - -// Buffers used during the rendering of a frame -struct ImGui_ImplDX12_RenderBuffers -{ - ID3D12Resource* IndexBuffer; - ID3D12Resource* VertexBuffer; - int IndexBufferSize; - int VertexBufferSize; -}; - -struct VERTEX_CONSTANT_BUFFER_DX12 -{ - float mvp[4][4]; -}; - -// Functions -static void ImGui_ImplDX12_SetupRenderState(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx, ImGui_ImplDX12_RenderBuffers* fr) -{ - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - - // Setup orthographic projection matrix into our constant buffer - // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). - VERTEX_CONSTANT_BUFFER_DX12 vertex_constant_buffer; - { - float L = draw_data->DisplayPos.x; - float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; - float T = draw_data->DisplayPos.y; - float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; - float mvp[4][4] = - { - { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, - { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, - { 0.0f, 0.0f, 0.5f, 0.0f }, - { (R+L)/(L-R), (T+B)/(B-T), 0.5f, 1.0f }, - }; - memcpy(&vertex_constant_buffer.mvp, mvp, sizeof(mvp)); - } - - // Setup viewport - D3D12_VIEWPORT vp; - memset(&vp, 0, sizeof(D3D12_VIEWPORT)); - vp.Width = draw_data->DisplaySize.x; - vp.Height = draw_data->DisplaySize.y; - vp.MinDepth = 0.0f; - vp.MaxDepth = 1.0f; - vp.TopLeftX = vp.TopLeftY = 0.0f; - ctx->RSSetViewports(1, &vp); - - // Bind shader and vertex buffers - unsigned int stride = sizeof(ImDrawVert); - unsigned int offset = 0; - D3D12_VERTEX_BUFFER_VIEW vbv; - memset(&vbv, 0, sizeof(D3D12_VERTEX_BUFFER_VIEW)); - vbv.BufferLocation = fr->VertexBuffer->GetGPUVirtualAddress() + offset; - vbv.SizeInBytes = fr->VertexBufferSize * stride; - vbv.StrideInBytes = stride; - ctx->IASetVertexBuffers(0, 1, &vbv); - D3D12_INDEX_BUFFER_VIEW ibv; - memset(&ibv, 0, sizeof(D3D12_INDEX_BUFFER_VIEW)); - ibv.BufferLocation = fr->IndexBuffer->GetGPUVirtualAddress(); - ibv.SizeInBytes = fr->IndexBufferSize * sizeof(ImDrawIdx); - ibv.Format = sizeof(ImDrawIdx) == 2 ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT; - ctx->IASetIndexBuffer(&ibv); - ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - ctx->SetPipelineState(bd->pPipelineState); - ctx->SetGraphicsRootSignature(bd->pRootSignature); - ctx->SetGraphicsRoot32BitConstants(0, 16, &vertex_constant_buffer, 0); - - // Setup blend factor - const float blend_factor[4] = { 0.f, 0.f, 0.f, 0.f }; - ctx->OMSetBlendFactor(blend_factor); -} - -template -static inline void SafeRelease(T*& res) -{ - if (res) - res->Release(); - res = nullptr; -} - -// Render function -void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* ctx) -{ - // Avoid rendering when minimized - if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) - return; - - // FIXME: I'm assuming that this only gets called once per frame! - // If not, we can't just re-allocate the IB or VB, we'll have to do a proper allocator. - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - bd->frameIndex = bd->frameIndex + 1; - ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[bd->frameIndex % bd->numFramesInFlight]; - - // Create and grow vertex/index buffers if needed - if (fr->VertexBuffer == nullptr || fr->VertexBufferSize < draw_data->TotalVtxCount) - { - SafeRelease(fr->VertexBuffer); - fr->VertexBufferSize = draw_data->TotalVtxCount + 5000; - D3D12_HEAP_PROPERTIES props; - memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); - props.Type = D3D12_HEAP_TYPE_UPLOAD; - props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - D3D12_RESOURCE_DESC desc; - memset(&desc, 0, sizeof(D3D12_RESOURCE_DESC)); - desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - desc.Width = fr->VertexBufferSize * sizeof(ImDrawVert); - desc.Height = 1; - desc.DepthOrArraySize = 1; - desc.MipLevels = 1; - desc.Format = DXGI_FORMAT_UNKNOWN; - desc.SampleDesc.Count = 1; - desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - desc.Flags = D3D12_RESOURCE_FLAG_NONE; - if (bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&fr->VertexBuffer)) < 0) - return; - } - if (fr->IndexBuffer == nullptr || fr->IndexBufferSize < draw_data->TotalIdxCount) - { - SafeRelease(fr->IndexBuffer); - fr->IndexBufferSize = draw_data->TotalIdxCount + 10000; - D3D12_HEAP_PROPERTIES props; - memset(&props, 0, sizeof(D3D12_HEAP_PROPERTIES)); - props.Type = D3D12_HEAP_TYPE_UPLOAD; - props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; - props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; - D3D12_RESOURCE_DESC desc; - memset(&desc, 0, sizeof(D3D12_RESOURCE_DESC)); - desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; - desc.Width = fr->IndexBufferSize * sizeof(ImDrawIdx); - desc.Height = 1; - desc.DepthOrArraySize = 1; - desc.MipLevels = 1; - desc.Format = DXGI_FORMAT_UNKNOWN; - desc.SampleDesc.Count = 1; - desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - desc.Flags = D3D12_RESOURCE_FLAG_NONE; - if (bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&fr->IndexBuffer)) < 0) - return; - } - - // Upload vertex/index data into a single contiguous GPU buffer - void* vtx_resource, *idx_resource; - D3D12_RANGE range; - memset(&range, 0, sizeof(D3D12_RANGE)); - if (fr->VertexBuffer->Map(0, &range, &vtx_resource) != S_OK) - return; - if (fr->IndexBuffer->Map(0, &range, &idx_resource) != S_OK) - return; - ImDrawVert* vtx_dst = (ImDrawVert*)vtx_resource; - ImDrawIdx* idx_dst = (ImDrawIdx*)idx_resource; - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); - memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); - vtx_dst += cmd_list->VtxBuffer.Size; - idx_dst += cmd_list->IdxBuffer.Size; - } - fr->VertexBuffer->Unmap(0, &range); - fr->IndexBuffer->Unmap(0, &range); - - // Setup desired DX state - ImGui_ImplDX12_SetupRenderState(draw_data, ctx, fr); - - // Render command lists - // (Because we merged all buffers into a single one, we maintain our own offset into them) - int global_vtx_offset = 0; - int global_idx_offset = 0; - ImVec2 clip_off = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback != nullptr) - { - // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) - ImGui_ImplDX12_SetupRenderState(draw_data, ctx, fr); - else - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - // Project scissor/clipping rectangles into framebuffer space - ImVec2 clip_min(pcmd->ClipRect.x - clip_off.x, pcmd->ClipRect.y - clip_off.y); - ImVec2 clip_max(pcmd->ClipRect.z - clip_off.x, pcmd->ClipRect.w - clip_off.y); - if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) - continue; - - // Apply Scissor/clipping rectangle, Bind texture, Draw - const D3D12_RECT r = { (LONG)clip_min.x, (LONG)clip_min.y, (LONG)clip_max.x, (LONG)clip_max.y }; - D3D12_GPU_DESCRIPTOR_HANDLE texture_handle = {}; - texture_handle.ptr = (UINT64)pcmd->GetTexID(); - ctx->SetGraphicsRootDescriptorTable(1, texture_handle); - ctx->RSSetScissorRects(1, &r); - ctx->DrawIndexedInstanced(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); - } - } - global_idx_offset += cmd_list->IdxBuffer.Size; - global_vtx_offset += cmd_list->VtxBuffer.Size; - } -} - -static void ImGui_ImplDX12_CreateFontsTexture() -{ - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - - // Upload texture to graphics system - { - 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 = width; - desc.Height = 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 = nullptr; - bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, - D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&pTexture)); - - UINT uploadPitch = (width * 4 + D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u) & ~(D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1u); - UINT uploadSize = 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 = nullptr; - HRESULT hr = bd->pd3dDevice->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE, &desc, - D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&uploadBuffer)); - IM_ASSERT(SUCCEEDED(hr)); - - void* mapped = nullptr; - D3D12_RANGE range = { 0, uploadSize }; - hr = uploadBuffer->Map(0, &range, &mapped); - IM_ASSERT(SUCCEEDED(hr)); - for (int y = 0; y < height; y++) - memcpy((void*) ((uintptr_t) mapped + y * uploadPitch), pixels + y * width * 4, width * 4); - uploadBuffer->Unmap(0, &range); - - 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 = width; - srcLocation.PlacedFootprint.Footprint.Height = 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; - - ID3D12Fence* fence = nullptr; - hr = bd->pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&fence)); - IM_ASSERT(SUCCEEDED(hr)); - - HANDLE event = CreateEvent(0, 0, 0, 0); - IM_ASSERT(event != nullptr); - - D3D12_COMMAND_QUEUE_DESC queueDesc = {}; - queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; - queueDesc.NodeMask = 1; - - ID3D12CommandQueue* cmdQueue = nullptr; - hr = bd->pd3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&cmdQueue)); - IM_ASSERT(SUCCEEDED(hr)); - - ID3D12CommandAllocator* cmdAlloc = nullptr; - hr = bd->pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&cmdAlloc)); - IM_ASSERT(SUCCEEDED(hr)); - - ID3D12GraphicsCommandList* cmdList = nullptr; - hr = bd->pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, cmdAlloc, nullptr, IID_PPV_ARGS(&cmdList)); - IM_ASSERT(SUCCEEDED(hr)); - - cmdList->CopyTextureRegion(&dstLocation, 0, 0, 0, &srcLocation, nullptr); - cmdList->ResourceBarrier(1, &barrier); - - hr = cmdList->Close(); - IM_ASSERT(SUCCEEDED(hr)); - - cmdQueue->ExecuteCommandLists(1, (ID3D12CommandList* const*)&cmdList); - hr = cmdQueue->Signal(fence, 1); - IM_ASSERT(SUCCEEDED(hr)); - - fence->SetEventOnCompletion(1, event); - WaitForSingleObject(event, INFINITE); - - cmdList->Release(); - cmdAlloc->Release(); - cmdQueue->Release(); - CloseHandle(event); - fence->Release(); - uploadBuffer->Release(); - - // Create texture view - 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; - bd->pd3dDevice->CreateShaderResourceView(pTexture, &srvDesc, bd->hFontSrvCpuDescHandle); - SafeRelease(bd->pFontTextureResource); - bd->pFontTextureResource = pTexture; - } - - // Store our identifier - // READ THIS IF THE STATIC_ASSERT() TRIGGERS: - // - Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. - // - This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. - // [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in the 'example_win32_direct12/example_win32_direct12.vcxproj' project file) - // [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like. - // [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!) - // [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in the example_win32_direct12/build_win32.bat file) - static_assert(sizeof(ImTextureID) >= sizeof(bd->hFontSrvGpuDescHandle.ptr), "Can't pack descriptor handle into TexID, 32-bit not supported yet."); - io.Fonts->SetTexID((ImTextureID)bd->hFontSrvGpuDescHandle.ptr); -} - -bool ImGui_ImplDX12_CreateDeviceObjects() -{ - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - if (!bd || !bd->pd3dDevice) - return false; - if (bd->pPipelineState) - ImGui_ImplDX12_InvalidateDeviceObjects(); - - // Create the root signature - { - D3D12_DESCRIPTOR_RANGE descRange = {}; - descRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - descRange.NumDescriptors = 1; - descRange.BaseShaderRegister = 0; - descRange.RegisterSpace = 0; - descRange.OffsetInDescriptorsFromTableStart = 0; - - D3D12_ROOT_PARAMETER param[2] = {}; - - param[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; - param[0].Constants.ShaderRegister = 0; - param[0].Constants.RegisterSpace = 0; - param[0].Constants.Num32BitValues = 16; - param[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX; - - param[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - param[1].DescriptorTable.NumDescriptorRanges = 1; - param[1].DescriptorTable.pDescriptorRanges = &descRange; - param[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; - - // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. - D3D12_STATIC_SAMPLER_DESC staticSampler = {}; - staticSampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; - staticSampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - staticSampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - staticSampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - staticSampler.MipLODBias = 0.f; - staticSampler.MaxAnisotropy = 0; - staticSampler.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; - staticSampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK; - staticSampler.MinLOD = 0.f; - staticSampler.MaxLOD = 0.f; - staticSampler.ShaderRegister = 0; - staticSampler.RegisterSpace = 0; - staticSampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; - - D3D12_ROOT_SIGNATURE_DESC desc = {}; - desc.NumParameters = _countof(param); - desc.pParameters = param; - desc.NumStaticSamplers = 1; - desc.pStaticSamplers = &staticSampler; - desc.Flags = - D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | - D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | - D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS | - D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS; - - // Load d3d12.dll and D3D12SerializeRootSignature() function address dynamically to facilitate using with D3D12On7. - // See if any version of d3d12.dll is already loaded in the process. If so, give preference to that. - static HINSTANCE d3d12_dll = ::GetModuleHandleA("d3d12.dll"); - if (d3d12_dll == nullptr) - { - // Attempt to load d3d12.dll from local directories. This will only succeed if - // (1) the current OS is Windows 7, and - // (2) there exists a version of d3d12.dll for Windows 7 (D3D12On7) in one of the following directories. - // See https://github.com/ocornut/imgui/pull/3696 for details. - const char* localD3d12Paths[] = { ".\\d3d12.dll", ".\\d3d12on7\\d3d12.dll", ".\\12on7\\d3d12.dll" }; // A. current directory, B. used by some games, C. used in Microsoft D3D12On7 sample - for (int i = 0; i < IM_ARRAYSIZE(localD3d12Paths); i++) - if ((d3d12_dll = ::LoadLibraryA(localD3d12Paths[i])) != nullptr) - break; - - // If failed, we are on Windows >= 10. - if (d3d12_dll == nullptr) - d3d12_dll = ::LoadLibraryA("d3d12.dll"); - - if (d3d12_dll == nullptr) - return false; - } - - PFN_D3D12_SERIALIZE_ROOT_SIGNATURE D3D12SerializeRootSignatureFn = (PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)::GetProcAddress(d3d12_dll, "D3D12SerializeRootSignature"); - if (D3D12SerializeRootSignatureFn == nullptr) - return false; - - ID3DBlob* blob = nullptr; - if (D3D12SerializeRootSignatureFn(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, nullptr) != S_OK) - return false; - - bd->pd3dDevice->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(), IID_PPV_ARGS(&bd->pRootSignature)); - blob->Release(); - } - - // By using D3DCompile() from / d3dcompiler.lib, we introduce a dependency to a given version of d3dcompiler_XX.dll (see D3DCOMPILER_DLL_A) - // If you would like to use this DX12 sample code but remove this dependency you can: - // 1) compile once, save the compiled shader blobs into a file or source code and assign them to psoDesc.VS/PS [preferred solution] - // 2) use code to detect any version of the DLL and grab a pointer to D3DCompile from the DLL. - // See https://github.com/ocornut/imgui/pull/638 for sources and details. - - D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc; - memset(&psoDesc, 0, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC)); - psoDesc.NodeMask = 1; - psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; - psoDesc.pRootSignature = bd->pRootSignature; - psoDesc.SampleMask = UINT_MAX; - psoDesc.NumRenderTargets = 1; - psoDesc.RTVFormats[0] = bd->RTVFormat; - psoDesc.SampleDesc.Count = 1; - psoDesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE; - - ID3DBlob* vertexShaderBlob; - ID3DBlob* pixelShaderBlob; - - // Create the vertex shader - { - static const char* vertexShader = - "cbuffer vertexBuffer : register(b0) \ - {\ - float4x4 ProjectionMatrix; \ - };\ - struct VS_INPUT\ - {\ - float2 pos : POSITION;\ - float4 col : COLOR0;\ - float2 uv : TEXCOORD0;\ - };\ - \ - struct PS_INPUT\ - {\ - float4 pos : SV_POSITION;\ - float4 col : COLOR0;\ - float2 uv : TEXCOORD0;\ - };\ - \ - PS_INPUT main(VS_INPUT input)\ - {\ - PS_INPUT output;\ - output.pos = mul( ProjectionMatrix, float4(input.pos.xy, 0.f, 1.f));\ - output.col = input.col;\ - output.uv = input.uv;\ - return output;\ - }"; - - if (FAILED(D3DCompile(vertexShader, strlen(vertexShader), nullptr, nullptr, nullptr, "main", "vs_5_0", 0, 0, &vertexShaderBlob, nullptr))) - return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! - psoDesc.VS = { vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize() }; - - // Create the input layout - static D3D12_INPUT_ELEMENT_DESC local_layout[] = - { - { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, pos), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, - { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, (UINT)IM_OFFSETOF(ImDrawVert, uv), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, - { "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, (UINT)IM_OFFSETOF(ImDrawVert, col), D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }, - }; - psoDesc.InputLayout = { local_layout, 3 }; - } - - // Create the pixel shader - { - static const char* pixelShader = - "struct PS_INPUT\ - {\ - float4 pos : SV_POSITION;\ - float4 col : COLOR0;\ - float2 uv : TEXCOORD0;\ - };\ - SamplerState sampler0 : register(s0);\ - Texture2D texture0 : register(t0);\ - \ - float4 main(PS_INPUT input) : SV_Target\ - {\ - float4 out_col = input.col * texture0.Sample(sampler0, input.uv); \ - return out_col; \ - }"; - - if (FAILED(D3DCompile(pixelShader, strlen(pixelShader), nullptr, nullptr, nullptr, "main", "ps_5_0", 0, 0, &pixelShaderBlob, nullptr))) - { - vertexShaderBlob->Release(); - return false; // NB: Pass ID3DBlob* pErrorBlob to D3DCompile() to get error showing in (const char*)pErrorBlob->GetBufferPointer(). Make sure to Release() the blob! - } - psoDesc.PS = { pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize() }; - } - - // Create the blending setup - { - D3D12_BLEND_DESC& desc = psoDesc.BlendState; - desc.AlphaToCoverageEnable = false; - desc.RenderTarget[0].BlendEnable = true; - desc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA; - desc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA; - desc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD; - desc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE; - desc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; - desc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD; - desc.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; - } - - // Create the rasterizer state - { - D3D12_RASTERIZER_DESC& desc = psoDesc.RasterizerState; - desc.FillMode = D3D12_FILL_MODE_SOLID; - desc.CullMode = D3D12_CULL_MODE_NONE; - desc.FrontCounterClockwise = FALSE; - desc.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; - desc.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; - desc.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS; - desc.DepthClipEnable = true; - desc.MultisampleEnable = FALSE; - desc.AntialiasedLineEnable = FALSE; - desc.ForcedSampleCount = 0; - desc.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF; - } - - // Create depth-stencil State - { - D3D12_DEPTH_STENCIL_DESC& desc = psoDesc.DepthStencilState; - desc.DepthEnable = false; - desc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; - desc.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS; - desc.StencilEnable = false; - desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; - desc.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; - desc.BackFace = desc.FrontFace; - } - - HRESULT result_pipeline_state = bd->pd3dDevice->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&bd->pPipelineState)); - vertexShaderBlob->Release(); - pixelShaderBlob->Release(); - if (result_pipeline_state != S_OK) - return false; - - ImGui_ImplDX12_CreateFontsTexture(); - - return true; -} - -void ImGui_ImplDX12_InvalidateDeviceObjects() -{ - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - if (!bd || !bd->pd3dDevice) - return; - - ImGuiIO& io = ImGui::GetIO(); - SafeRelease(bd->pRootSignature); - SafeRelease(bd->pPipelineState); - SafeRelease(bd->pFontTextureResource); - io.Fonts->SetTexID(0); // We copied bd->pFontTextureView to io.Fonts->TexID so let's clear that as well. - - for (UINT i = 0; i < bd->numFramesInFlight; i++) - { - ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i]; - SafeRelease(fr->IndexBuffer); - SafeRelease(fr->VertexBuffer); - } -} - -bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap, - D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle) -{ - ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); - - // Setup backend capabilities flags - ImGui_ImplDX12_Data* bd = IM_NEW(ImGui_ImplDX12_Data)(); - io.BackendRendererUserData = (void*)bd; - io.BackendRendererName = "imgui_impl_dx12"; - io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. - - bd->pd3dDevice = device; - bd->RTVFormat = rtv_format; - bd->hFontSrvCpuDescHandle = font_srv_cpu_desc_handle; - bd->hFontSrvGpuDescHandle = font_srv_gpu_desc_handle; - bd->pFrameResources = new ImGui_ImplDX12_RenderBuffers[num_frames_in_flight]; - bd->numFramesInFlight = num_frames_in_flight; - bd->pd3dSrvDescHeap = cbv_srv_heap; - bd->frameIndex = UINT_MAX; - - // Create buffers with a default size (they will later be grown as needed) - for (int i = 0; i < num_frames_in_flight; i++) - { - ImGui_ImplDX12_RenderBuffers* fr = &bd->pFrameResources[i]; - fr->IndexBuffer = nullptr; - fr->VertexBuffer = nullptr; - fr->IndexBufferSize = 10000; - fr->VertexBufferSize = 5000; - } - - return true; -} - -void ImGui_ImplDX12_Shutdown() -{ - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); - ImGuiIO& io = ImGui::GetIO(); - - // Clean up windows and device objects - ImGui_ImplDX12_InvalidateDeviceObjects(); - delete[] bd->pFrameResources; - io.BackendRendererName = nullptr; - io.BackendRendererUserData = nullptr; - io.BackendFlags &= ~ImGuiBackendFlags_RendererHasVtxOffset; - IM_DELETE(bd); -} - -void ImGui_ImplDX12_NewFrame() -{ - ImGui_ImplDX12_Data* bd = ImGui_ImplDX12_GetBackendData(); - IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplDX12_Init()?"); - - if (!bd->pPipelineState) - ImGui_ImplDX12_CreateDeviceObjects(); -} - -//----------------------------------------------------------------------------- - -#endif // #ifndef IMGUI_DISABLE diff --git a/imgui/imgui_impl_dx12.h b/imgui/imgui_impl_dx12.h deleted file mode 100644 index 8b8d9b4..0000000 --- a/imgui/imgui_impl_dx12.h +++ /dev/null @@ -1,41 +0,0 @@ -// dear imgui: Renderer Backend for DirectX12 -// This needs to be used along with a Platform Backend (e.g. Win32) - -// Implemented features: -// [X] Renderer: User texture binding. Use 'D3D12_GPU_DESCRIPTOR_HANDLE' as ImTextureID. Read the FAQ about ImTextureID! -// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. - -// Important: to compile on 32-bit systems, this backend requires code to be compiled with '#define ImTextureID ImU64'. -// See imgui_impl_dx12.cpp file for details. - -// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. -// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. -// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. -// Read online: https://github.com/ocornut/imgui/tree/master/docs - -#pragma once -#include "imgui.h" // IMGUI_IMPL_API -#ifndef IMGUI_DISABLE -#include // DXGI_FORMAT - -struct ID3D12Device; -struct ID3D12DescriptorHeap; -struct ID3D12GraphicsCommandList; -struct D3D12_CPU_DESCRIPTOR_HANDLE; -struct D3D12_GPU_DESCRIPTOR_HANDLE; - -// cmd_list is the command list that the implementation will use to render imgui draw lists. -// Before calling the render function, caller must prepare cmd_list by resetting it and setting the appropriate -// render target and descriptor heap that contains font_srv_cpu_desc_handle/font_srv_gpu_desc_handle. -// font_srv_cpu_desc_handle and font_srv_gpu_desc_handle are handles to a single SRV descriptor to use for the internal font texture. -IMGUI_IMPL_API bool ImGui_ImplDX12_Init(ID3D12Device* device, int num_frames_in_flight, DXGI_FORMAT rtv_format, ID3D12DescriptorHeap* cbv_srv_heap, - D3D12_CPU_DESCRIPTOR_HANDLE font_srv_cpu_desc_handle, D3D12_GPU_DESCRIPTOR_HANDLE font_srv_gpu_desc_handle); -IMGUI_IMPL_API void ImGui_ImplDX12_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplDX12_NewFrame(); -IMGUI_IMPL_API void ImGui_ImplDX12_RenderDrawData(ImDrawData* draw_data, ID3D12GraphicsCommandList* graphics_command_list); - -// Use if you want to reset your rendering device without losing Dear ImGui state. -IMGUI_IMPL_API void ImGui_ImplDX12_InvalidateDeviceObjects(); -IMGUI_IMPL_API bool ImGui_ImplDX12_CreateDeviceObjects(); - -#endif // #ifndef IMGUI_DISABLE diff --git a/src/d3d11on12ui.cpp b/src/d3d11on12ui.cpp new file mode 100644 index 0000000..58ef019 --- /dev/null +++ b/src/d3d11on12ui.cpp @@ -0,0 +1,136 @@ +#include +#include +#include +#include + +#include "main.h" +#include "gui.h" + +#include "../imgui/imgui_impl_dx11.h" +#include "../imgui/imgui_impl_win32.h" + +struct D3DBuffer { + ID3D12Resource* d3d12RenderTarget; + ID3D11Resource* d3d11WrappedBackBuffer; + ID3D11RenderTargetView* d3d11RenderTargetView; +}; + +static D3DBuffer* Buffers{ nullptr }; +static IDXGISwapChain3* g_d3d12swapchain3{ nullptr }; + + +static ID3D11Device* g_d3d11device{ nullptr }; +static ID3D11DeviceContext* g_d3d11context{ nullptr }; +static ID3D11On12Device* g_d3d11on12device{ nullptr }; + + +static unsigned g_buffercount{ 0 }; +static bool g_initialized = false; + + +// NOTE: the other code doesnt abort on hr != S_OK +extern void UI_Initialize(void* dx12_swapchain, void* dx12_commandqueue) { + ASSERT(g_initialized == false); + const auto swapchain = (IDXGISwapChain*)dx12_swapchain; + const auto dx12queue = (ID3D12CommandQueue*)dx12_commandqueue; + + // Get the dx12 device from the swapchain + HRESULT hr; + ID3D12Device* dx12device; + hr = swapchain->GetDevice(IID_PPV_ARGS(&dx12device)); + ASSERT(hr == S_OK); + + // Get the info from the swapchain to get the number of frame buffers + DXGI_SWAP_CHAIN_DESC desc; + hr = swapchain->GetDesc(&desc); + ASSERT(hr == S_OK); + + g_buffercount = desc.BufferCount; + + // Create the dx11 device from the dx12 device and command queue + const auto feature_level = D3D_FEATURE_LEVEL_11_0; + hr = D3D11On12CreateDevice(dx12device, 0, &feature_level, 1, (IUnknown* const*)&dx12queue, 1, 0, &g_d3d11device, &g_d3d11context, nullptr); + ASSERT(hr == S_OK); + + // verify the device + hr = g_d3d11device->QueryInterface(IID_PPV_ARGS(&g_d3d11on12device)); + ASSERT(hr == S_OK); + + // get the complete swapchain interface + hr = swapchain->QueryInterface(IID_PPV_ARGS(&g_d3d12swapchain3)); + ASSERT(hr == S_OK); + + // Create the dx12 buffers + Buffers = (decltype(Buffers))calloc(g_buffercount, sizeof(*Buffers)); + ASSERT(Buffers != NULL); + + ID3D12DescriptorHeap* heap; + D3D12_DESCRIPTOR_HEAP_DESC rtvdesc{}; + rtvdesc.NumDescriptors = g_buffercount; + rtvdesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + rtvdesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; + hr = dx12device->CreateDescriptorHeap(&rtvdesc, IID_PPV_ARGS(&heap)); + ASSERT(hr == S_OK); + + D3D12_CPU_DESCRIPTOR_HANDLE rtvhandle = heap->GetCPUDescriptorHandleForHeapStart(); + auto rtvsize = dx12device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + + for (unsigned i = 0; i < g_buffercount; ++i) { + hr = swapchain->GetBuffer(i, IID_PPV_ARGS(&Buffers[i].d3d12RenderTarget)); + ASSERT(hr == S_OK); + + dx12device->CreateRenderTargetView(Buffers[i].d3d12RenderTarget, nullptr, rtvhandle); + + D3D11_RESOURCE_FLAGS flags = { D3D11_BIND_RENDER_TARGET }; + hr = g_d3d11on12device->CreateWrappedResource(Buffers[i].d3d12RenderTarget, &flags, D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_PRESENT, IID_PPV_ARGS(&Buffers[i].d3d11WrappedBackBuffer)); + ASSERT(hr == S_OK); + + hr = g_d3d11device->CreateRenderTargetView(Buffers[i].d3d11WrappedBackBuffer, nullptr, &Buffers[i].d3d11RenderTargetView); + ASSERT(hr == S_OK); + + rtvhandle.ptr += rtvsize; + } + + ImGui_ImplDX11_Init(g_d3d11device, g_d3d11context); + g_initialized = true; +} + + +extern void UI_Render() { + const auto index = g_d3d12swapchain3->GetCurrentBackBufferIndex(); + const auto b = &Buffers[index]; + //const float fill_color[] = { 0.f, 0.f, 0.f, 0.f }; + + ImGui_ImplDX11_NewFrame(); + ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); + + draw_gui(); + + ImGui::Render(); + g_d3d11on12device->AcquireWrappedResources(&b->d3d11WrappedBackBuffer, 1); + g_d3d11context->OMSetRenderTargets(1, &b->d3d11RenderTargetView, nullptr); + //g_d3d11context->ClearRenderTargetView(b->d3d11RenderTargetView, fill_color); + ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + g_d3d11on12device->ReleaseWrappedResources(&b->d3d11WrappedBackBuffer, 1); //NOTE: this was array and size, but i made a struct of thses! + g_d3d11context->Flush(); +} + + +extern void UI_Release() { + if (g_initialized == false) return; + + ImGui_ImplDX11_Shutdown(); + + for (unsigned i = 0; i < g_buffercount; ++i) { + if (Buffers[i].d3d11RenderTargetView) Buffers[i].d3d11RenderTargetView->Release(); + if (Buffers[i].d3d11WrappedBackBuffer) Buffers[i].d3d11WrappedBackBuffer->Release(); + if (Buffers[i].d3d12RenderTarget) Buffers[i].d3d12RenderTarget->Release(); + } + + g_d3d11context->Flush(); //necessary?? + g_d3d11context->Release(); + g_d3d11device->Release(); + g_d3d11on12device->Release(); + g_initialized = false; +} \ No newline at end of file diff --git a/src/d3d11on12ui.h b/src/d3d11on12ui.h new file mode 100644 index 0000000..114a094 --- /dev/null +++ b/src/d3d11on12ui.h @@ -0,0 +1,5 @@ +#pragma once + +void UI_Initialize(void* dx12_swapchain, void* dx12_commandqueue); +extern void UI_Render(); +extern void UI_Release(); \ No newline at end of file diff --git a/src/fake_vcruntime140_1.cpp b/src/fake_vcruntime140_1.cpp index c874b75..d721b8d 100644 --- a/src/fake_vcruntime140_1.cpp +++ b/src/fake_vcruntime140_1.cpp @@ -3,15 +3,8 @@ #include #include - -[[noreturn]] static inline void fatal_error(const char* const message) noexcept { - MessageBoxA(NULL, message, "Fatal Error", 0); - std::abort(); - _STL_UNREACHABLE; -} - - extern "C" EXCEPTION_DISPOSITION __declspec(dllexport) __CxxFrameHandler4(void*, void*, void*, void*) { //todo: forward this to the real one? - fatal_error(__func__); + MessageBoxA(NULL, __func__, "Betterconsole Crashed!", 0); + std::abort(); } diff --git a/src/gui.cpp b/src/gui.cpp index 0b1ab8e..6c2ac07 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -14,22 +14,17 @@ static const struct simple_draw_t* const SimpleDraw{GetSimpleDrawAPI()}; extern void draw_gui() { static ImGuiTabItemFlags ConsoleDefaultFlags = ImGuiTabItemFlags_SetSelected; - static bool initpos = false; - - if (!initpos) { - initpos = true; - - const auto size = ImGui::GetIO().DisplaySize; - const float width = size.x / 2; - const float height = size.y / 2; - - //put the window in the middle of the screen at 50% game width and height - ImGui::SetNextWindowPos(ImVec2{ width / 2, height / 2 }); - ImGui::SetNextWindowSize(ImVec2{ width, height }); + + static bool once = false; + if (!once) { + const auto screen = ImGui::GetIO().DisplaySize; + ImGui::SetNextWindowPos(ImVec2{ screen.x / 4, screen.y / 4 }); + ImGui::SetNextWindowSize(ImVec2{ screen.x / 2 , screen.y / 2 }); + once = true; } auto imgui_context = ImGui::GetCurrentContext(); - ImGui::Begin(BetterAPIName " Mod Menu"); + ImGui::Begin("BetterConsole"); ImGui::BeginTabBar("mod tabs"); size_t infos_count = 0; @@ -81,7 +76,7 @@ extern void draw_gui() { } if (ImGui::Button("Open Config File")) { char path[260]; - ShellExecuteA(NULL, "open", GetPathInDllDir(path, "MiniModMenuRegistry.txt"), NULL, NULL, 1); + ShellExecuteA(NULL, "open", GetPathInDllDir(path, "BetterConsoleConfig.txt"), NULL, NULL, 1); } ImGui::EndPopup(); } diff --git a/src/main.cpp b/src/main.cpp index a4ca411..a1dfbec 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,6 @@ #include "main.h" #include "../imgui/imgui_impl_win32.h" -#include "../imgui/imgui_impl_dx12.h" #include #include @@ -13,6 +12,8 @@ #include "internal_plugin.h" +#include "d3d11on12ui.h" + extern "C" __declspec(dllexport) SFSEPluginVersionData SFSEPlugin_Version = { 1, // SFSE api version @@ -61,6 +62,15 @@ extern char* GetPathInDllDir(char* path_max_buffer, const char* filename) { } +const char* const filename_only(const char* path) { + const char* p = path; + while (*path) { + if (*path == '\\') p = path; + ++path; + } + return ++p; +} + #ifdef MODMENU_DEBUG #include @@ -85,7 +95,7 @@ static void write_log(const char* const str) noexcept { logging_mutex.unlock(); } extern void DebugImpl(const char* const filename, const char* const func, int line, const char* const fmt, ...) noexcept { - auto bytes = snprintf(format_buffer, buffer_size, "%s:%s:%d>", filename, func, line); + auto bytes = snprintf(format_buffer, buffer_size, "%s:%s:%d>", filename_only(filename), func, line); ASSERT(bytes > 0); ASSERT(bytes < buffer_size); @@ -111,7 +121,7 @@ extern void AssertImpl [[noreturn]] (const char* const filename, const char* con "In function '%s'\n" "On line '%d'\n" "Message: '%s'", - filename, + filename_only(filename), func, line, text @@ -121,6 +131,29 @@ extern void AssertImpl [[noreturn]] (const char* const filename, const char* con MessageBoxA(NULL, format_buffer, "BetterConsole Crashed!", 0); abort(); } + +extern void TraceImpl(const char* const filename, const char* const func, int line, const char* const fmt, ...) noexcept { + static bool init = false; + static char tracebuff[2048]; + const auto bytes = snprintf(tracebuff, sizeof(tracebuff), "%s:%s:%d> ", filename_only(filename), func, line); + ASSERT(bytes > 0 && "trace buffer too small ?"); + va_list args; + va_start(args, fmt); + const auto bytes2 = vsnprintf(tracebuff + bytes, sizeof(tracebuff) - bytes, fmt, args); + ASSERT(bytes2 > 0 && "trace buffer too small ?"); + tracebuff[bytes + bytes2] = '\r'; + tracebuff[bytes + bytes2 + 1] = '\n'; + tracebuff[bytes + bytes2 + 2] = '\0'; + va_end(args); + if (!init) { + AllocConsole(); + FILE* file = nullptr; + freopen_s(&file, "CONIN$", "rb", stdin); + freopen_s(&file, "CONOUT$", "wb", stdout); + freopen_s(&file, "CONOUT$", "wb", stderr); + } + fputs(tracebuff, stdout); +} #endif @@ -145,7 +178,7 @@ extern const ModMenuSettings* GetSettings() { } -#define MAX_QUEUES 16 +#define MAX_QUEUES 4 struct SwapChainQueue { ID3D12CommandQueue* Queue; IDXGISwapChain* SwapChain; @@ -220,6 +253,17 @@ static HRESULT FAKE_CreateSwapChainForHwnd( DEBUG("Queues[%u] was overwritten", highest); } + + auto proc = (decltype(OLD_Wndproc))GetWindowLongPtrW(hWnd, GWLP_WNDPROC); + if ((uint64_t)FAKE_Wndproc != (uint64_t)proc) { + OLD_Wndproc = proc; + SetWindowLongPtrW(hWnd, GWLP_WNDPROC, (LONG_PTR)FAKE_Wndproc); + DEBUG("Input Hook: OLD_Wndproc: %p, Current_Wndproc: %p, NEW_Wndproc: %p", OLD_Wndproc, proc, FAKE_Wndproc); + } + else { + DEBUG("WndProc already hooked"); + } + enum : unsigned { QueryInterface, AddRef, @@ -232,17 +276,33 @@ static HRESULT FAKE_CreateSwapChainForHwnd( Present, }; + /* static bool once = 1; if (once) { once = 0; // needed to use a stronger hook here, the steam overlay uses vmt hooks to draw // if the window is resized and this function is called again, we end up in a // recursive loop with the steam overlay + // Also, if rivatuner is installed we will need to hook the hook after present, yay! FUNC_PTR* fp = *(FUNC_PTR**)*ppSwapChain; DEBUG("HookFunction: IDXGISwapChain::Present"); + present_address = (intptr_t)fp[Present]; OLD_Present = (decltype(OLD_Present))API.Hook->HookFunction(fp[Present], (FUNC_PTR)FAKE_Present); } - + */ + + + static bool once = false; + if (!once) { + OLD_Present = (decltype(OLD_Present))API.Hook->HookVirtualTable( + *ppSwapChain, + Present, + (FUNC_PTR)FAKE_Present + ); + DEBUG("Hooked Present, OLD_Present: %p, NEW_Present: %p", OLD_Present, FAKE_Present); + once = true; + } + return ret; } @@ -275,12 +335,23 @@ static HRESULT FAKE_CreateDXGIFactory2(UINT Flags, REFIID RefID, void **ppFactor CreateSwapChainForHwnd }; - OLD_CreateSwapChainForHwnd = (decltype(OLD_CreateSwapChainForHwnd))API.Hook->HookVirtualTable( - *ppFactory, - CreateSwapChainForHwnd, - (FUNC_PTR) FAKE_CreateSwapChainForHwnd - ); - DEBUG("Hooked CreateSwapChainForHwnd"); + // Using a stronger hook here because otherwise the steam overlay will not function + // not sure why a vmt hook doesnt work here + FUNC_PTR* fp = *(FUNC_PTR**)*ppFactory; + DEBUG("HookFunction: CreateSwapChainForHwnd"); + OLD_CreateSwapChainForHwnd = (decltype(OLD_CreateSwapChainForHwnd))API.Hook->HookFunction(fp[CreateSwapChainForHwnd], (FUNC_PTR)FAKE_CreateSwapChainForHwnd); + + /* + bool hook = false; // for debugger + if (hook) { + OLD_CreateSwapChainForHwnd = (decltype(OLD_CreateSwapChainForHwnd))API.Hook->HookVirtualTable( + *ppFactory, + CreateSwapChainForHwnd, + (FUNC_PTR)FAKE_CreateSwapChainForHwnd + ); + DEBUG("Hooked CreateSwapChainForHwnd"); + } + */ } return ret; @@ -335,11 +406,13 @@ static void SetupModMenu() { OLD_ClipCursor = (decltype(OLD_ClipCursor)) API.Hook->HookFunctionIAT("user32.dll", "ClipCursor", (FUNC_PTR)FAKE_ClipCursor); DEBUG("Hook ClipCursor: %p", OLD_ClipCursor); + OLD_CreateDXGIFactory2 = (decltype(OLD_CreateDXGIFactory2)) API.Hook->HookFunctionIAT("sl.interposer.dll", "CreateDXGIFactory2", (FUNC_PTR)FAKE_CreateDXGIFactory2); if (!OLD_CreateDXGIFactory2) { OLD_CreateDXGIFactory2 = (decltype(OLD_CreateDXGIFactory2))API.Hook->HookFunctionIAT("dxgi.dll", "CreateDXGIFactory2", (FUNC_PTR)FAKE_CreateDXGIFactory2); } DEBUG("Hook CreateDXGIFactory2: %p", OLD_CreateDXGIFactory2); + IMGUI_CHECKVERSION(); ImGui::CreateContext(); @@ -359,10 +432,6 @@ static void SetupModMenu() { } extern "C" __declspec(dllexport) void SFSEPlugin_Load(const SFSEInterface * sfse) { -#ifdef _DEBUG - //while (!IsDebuggerPresent()) Sleep(100); -#endif // _DEBUG - static PluginHandle MyPluginHandle; static SFSEMessagingInterface* MessageInterface; @@ -388,7 +457,11 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE self, DWORD fdwReason, LPVOID) { /* lock the linker/dll loader until hooks are installed, TODO: make sure this code path is fast */ static bool RunHooksOnlyOnce = true; ASSERT(RunHooksOnlyOnce == true); //i want to know if this assert ever gets triggered - + +#ifdef _DEBUG + while (!IsDebuggerPresent()) Sleep(100); +#endif // _DEBUG + //use the directory of the betterconsole dll as the place to put other files GetModuleFileNameA(self, DLL_DIR, MAX_PATH); char* n = DLL_DIR; @@ -403,177 +476,11 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE self, DWORD fdwReason, LPVOID) { return TRUE; } -struct FrameContext { - ID3D12CommandAllocator* commandAllocator{ nullptr }; - ID3D12Resource* mainRenderTargetResource{ nullptr }; - D3D12_CPU_DESCRIPTOR_HANDLE mainRenderTargetDescriptor; - UINT64 fenceValue{ 0 }; //TODO: use this -}; - -struct RenderState { - ID3D12Device* device{ nullptr }; - //IDXGISwapChain* swapchain{ nullptr }; - ID3D12CommandQueue* commandQueue{ nullptr }; - ID3D12DescriptorHeap* descriptorHeapBackBuffers{ nullptr }; - ID3D12DescriptorHeap* descriptorHeapImGuiRender{ nullptr }; - ID3D12GraphicsCommandList* commandList{ nullptr }; - FrameContext* frameContext{ nullptr }; - UINT bufferCount{ 0 }; -}; - - -static void SetupRenderState(RenderState* state, IDXGISwapChain3* Swapchain) { - Swapchain->GetDevice(IID_PPV_ARGS(&state->device)); - ASSERT(state->device != NULL); - - DXGI_SWAP_CHAIN_DESC sdesc; - Swapchain->GetDesc(&sdesc); - - state->bufferCount = sdesc.BufferCount; - - free(state->frameContext); - state->frameContext = (FrameContext*)calloc(state->bufferCount, sizeof(FrameContext)); - - D3D12_DESCRIPTOR_HEAP_DESC descriptorImGuiRender = {}; - descriptorImGuiRender.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; - descriptorImGuiRender.NumDescriptors = state->bufferCount; - descriptorImGuiRender.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; - - state->device->CreateDescriptorHeap(&descriptorImGuiRender, IID_PPV_ARGS(&state->descriptorHeapImGuiRender)); - - ID3D12CommandAllocator* allocator = nullptr; - state->device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&allocator)); - ASSERT(allocator != NULL); - for (size_t i = 0; i < state->bufferCount; i++) { - state->frameContext[i].commandAllocator = allocator; - } - - state->device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, allocator, NULL, IID_PPV_ARGS(&state->commandList)); - - D3D12_DESCRIPTOR_HEAP_DESC descriptorBackBuffers{}; - descriptorBackBuffers.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; - descriptorBackBuffers.NumDescriptors = state->bufferCount; - descriptorBackBuffers.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; - descriptorBackBuffers.NodeMask = 1; - - state->device->CreateDescriptorHeap(&descriptorBackBuffers, IID_PPV_ARGS(&state->descriptorHeapBackBuffers)); - - const auto rtvDescriptorSize = state->device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); - D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = state->descriptorHeapBackBuffers->GetCPUDescriptorHandleForHeapStart(); - - for (UINT i = 0; i < state->bufferCount; i++) { - ID3D12Resource* pBackBuffer = nullptr; - state->frameContext[i].mainRenderTargetDescriptor = rtvHandle; - Swapchain->GetBuffer(i, IID_PPV_ARGS(&pBackBuffer)); - state->device->CreateRenderTargetView(pBackBuffer, nullptr, rtvHandle); - state->frameContext[i].mainRenderTargetResource = pBackBuffer; - rtvHandle.ptr += rtvDescriptorSize; - } - - ImGui_ImplDX12_Init(state->device, state->bufferCount, - DXGI_FORMAT_R8G8B8A8_UNORM, state->descriptorHeapImGuiRender, - state->descriptorHeapImGuiRender->GetCPUDescriptorHandleForHeapStart(), - state->descriptorHeapImGuiRender->GetGPUDescriptorHandleForHeapStart()); - - ImGui_ImplDX12_CreateDeviceObjects(); -} - - -static void InjectImGui(IDXGISwapChain3* Swapchain) { - static RenderState state; - - static IDXGISwapChain* last_swapchain = nullptr; - if (last_swapchain != Swapchain) { - last_swapchain = Swapchain; - - DEBUG("Injecting imgui renderer into swapchain: %p", Swapchain); - - //find the command queue for this state - ID3D12CommandQueue* tmp = nullptr; - for (const auto& q : Queues) { - if (!state.commandQueue) { - DEBUG("Queue %p, Swapchain: %p", q.Queue, q.SwapChain); - } - if (q.SwapChain == Swapchain) { - tmp = q.Queue; - break; - } - } - if (tmp != state.commandQueue) { - DEBUG("Selecting command queue: %p", tmp); - state.commandQueue = tmp; - } - ASSERT(state.commandQueue != NULL); - - DEBUG("RenderPresent init Swapchain: %p", Swapchain); - - DXGI_SWAP_CHAIN_DESC sdesc; - Swapchain->GetDesc(&sdesc); - HWND window_handle = sdesc.OutputWindow; - - auto proc = (decltype(OLD_Wndproc))GetWindowLongPtrW(window_handle, GWLP_WNDPROC); - DEBUG("Input Hook: OLD_OLD_Wndproc: %p, NEW_OLD_Wndproc: %p, FAKE_Wndproc: %p", OLD_Wndproc, proc, FAKE_Wndproc); - if ((uint64_t)FAKE_Wndproc != (uint64_t)proc) { - OLD_Wndproc = proc; - SetWindowLongPtrW(window_handle, GWLP_WNDPROC, (LONG_PTR)FAKE_Wndproc); - } - else { - DEBUG("Wndproc already hooked"); - } - - //TODO: actually release and recapture graphics system - state.device = nullptr; - } - - if (should_show_ui) { - if (!state.device) { - SetupRenderState(&state, Swapchain); - return; - } - - ImGui_ImplDX12_NewFrame(); - ImGui_ImplWin32_NewFrame(); - ImGui::NewFrame(); - - draw_gui(); - - ImGui::EndFrame(); - ImGui::Render(); - - FrameContext& currentFrameContext = state.frameContext[Swapchain->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.mainRenderTargetResource; - barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; - barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; - - state.commandList->Reset(currentFrameContext.commandAllocator, nullptr); - state.commandList->ResourceBarrier(1, &barrier); - state.commandList->OMSetRenderTargets(1, ¤tFrameContext.mainRenderTargetDescriptor, FALSE, nullptr); - state.commandList->SetDescriptorHeaps(1, &state.descriptorHeapImGuiRender); - - ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), state.commandList); - - barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; - barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; - - state.commandList->ResourceBarrier(1, &barrier); - state.commandList->Close(); - - state.commandQueue->ExecuteCommandLists(1, reinterpret_cast(&state.commandList)); - } -} - static HRESULT FAKE_Present(IDXGISwapChain3* This, UINT SyncInterval, UINT PresentFlags) { // just when you though we were done loading down the main render thread... // now we have the periodic callback. these callbacks are spread out over seveal frames // each mod can register one callback and that callback will be called every "1 / mod count" frames - size_t infos_count = 0; auto infos = GetModInfo(&infos_count); @@ -585,7 +492,38 @@ static HRESULT FAKE_Present(IDXGISwapChain3* This, UINT SyncInterval, UINT Prese if (callback) callback(); } - InjectImGui(This); + + if (EveryNFrames(240)) { + DEBUG("render heartbeat, showing ui: %s", (should_show_ui)? "true" : "false"); + } + + static bool hooked = false; + static IDXGISwapChain3* last_swapchain = nullptr; + + if (last_swapchain != This) { + if (hooked) { + UI_Release(); + hooked = false; + } + last_swapchain = This; + } + + if (should_show_ui) { + if (!hooked) { + void* queue = nullptr; + for (auto i : Queues) { + DEBUG("Searching for CommandQueue for swapchain %p: [ chain: %p, queue: %p, age: %u ]", This, i.SwapChain, i.Queue, i.Age); + if (i.SwapChain == This) { + queue = i.Queue; + DEBUG("Commandqueue found!"); + break; + } + } + UI_Initialize(This, queue); + hooked = true; + } + UI_Render(); + } // one common cause of crashes is steam overlay hooking ::Present() and getting us into a stack overflow // keep this detection code in place to detect breaking changes to the imgui hook @@ -594,6 +532,28 @@ static HRESULT FAKE_Present(IDXGISwapChain3* This, UINT SyncInterval, UINT Prese ++loop_check; auto ret = OLD_Present(This, SyncInterval, PresentFlags); --loop_check; + +#if 0 + // Check if rivatuner broke the present hook and play an uno reverse card on rivatuner + static bool riva_check = false; + if (!riva_check) { + const auto hook = (const unsigned char*)present_address; + int32_t target = 0; + const intptr_t address = (intptr_t)hook; + FUNC_PTR me = (FUNC_PTR)FAKE_Present; + ASSERT(hook[0] == 0xE9); // JMP <32 bit address> + memcpy(&target, &hook[1], sizeof(target)); + auto hook_address = (FUNC_PTR)(address + target + 5 /* size of JMP */); + + // test + if (hook_address != me) { + DEBUG("Playing UNO reverse card on rivatuner! OLD_Present: %p, hook_address: %p, me: %p", OLD_Present, hook_address, me); + OLD_Present = (decltype(OLD_Present))API.Hook->HookFunction(hook_address, me); + } + riva_check = true; + } +#endif + return ret; } @@ -602,7 +562,7 @@ static LRESULT FAKE_Wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) static HWND last_window_handle = nullptr; if (EveryNFrames(600)) { - DEBUG("Heartbeat"); + DEBUG("input heartbeat"); } if (hWnd && (last_window_handle != hWnd)) { diff --git a/src/main.h b/src/main.h index 2bacb0b..d1f2241 100644 --- a/src/main.h +++ b/src/main.h @@ -10,28 +10,21 @@ #ifdef MODMENU_DEBUG extern void DebugImpl(const char* const filename, const char* const func, int line, const char* const fmt, ...) noexcept; -extern void AssertImpl [[noreturn]] (const char* const filename, const char* const func, int line, const char* const text) noexcept; -inline constexpr auto file_name_only(const char* const in) noexcept -> const char* const { - auto i = in; - auto ret = in; - while (*i) { - if ((*i == '\\') || (*i =='/')) ret = i; - ++i; - } - return ++ret; -} -#define DEBUG(...) do { DebugImpl(file_name_only(__FILE__), __func__, __LINE__, " " __VA_ARGS__); } while(0) -#define ASSERT(CONDITION) do { if (!(CONDITION)) { AssertImpl(file_name_only(__FILE__), __func__, __LINE__, " " #CONDITION); } } while(0) +extern void AssertImpl(const char* const filename, const char* const func, int line, const char* const text) noexcept; +extern void TraceImpl(const char* const filename, const char* const func, int line, const char* const fmt, ...) noexcept; +#define DEBUG(...) do { DebugImpl(__FILE__, __func__, __LINE__, " " __VA_ARGS__); } while(0) +#define ASSERT(CONDITION) do { if (!(CONDITION)) { AssertImpl(__FILE__, __func__, __LINE__, " " #CONDITION); } } while(0) +#define TRACE(...) do { TraceImpl(__FILE__, __func__, __LINE__, " " __VA_ARGS__); } while(0) #define IMGUI_DEBUG_PARANOID #else #define DEBUG(...) do { } while(0) #define ASSERT(...) do { } while(0) +#defince TRACE(...) do { } while(0) #endif // MODMENU_DEBUG #define WIN32_LEAN_AND_MEAN #define VC_EXTRA_LEAN -#define STRICT #define BETTERAPI_ENABLE_STD @@ -48,7 +41,7 @@ inline constexpr auto file_name_only(const char* const in) noexcept -> const cha #include "../imgui/imgui.h" -#define BETTERCONSOLE_VERSION "1.2.19" +#define BETTERCONSOLE_VERSION "1.3.0" // -------------------------------------------------------------------- @@ -69,4 +62,4 @@ struct ModMenuSettings { extern const ModMenuSettings* GetSettings(); extern ModMenuSettings* GetSettingsMutable(); -extern char* GetPathInDllDir(char* path_max_buffer, const char* filename); +extern char* GetPathInDllDir(char* path_max_buffer, const char* filename); \ No newline at end of file diff --git a/src/settings.cpp b/src/settings.cpp index c1ccc18..d6102ab 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -8,7 +8,7 @@ #include "simpledraw.h" #include "internal_plugin.h" -#define SETTINGS_REGISTRY_PATH "MiniModMenuRegistry.txt" +#define SETTINGS_REGISTRY_PATH "BetterConsoleConfig.txt" union SettingValue { void* as_data; @@ -406,7 +406,8 @@ static bool TurboSettingsParser() { extern void SaveSettingsRegistry() { FILE* f = NULL; - fopen_s(&f, SETTINGS_REGISTRY_PATH, "wb"); + char path[MAX_PATH]; + fopen_s(&f, GetPathInDllDir(path, SETTINGS_REGISTRY_PATH), "wb"); if (f == NULL) return; for (const auto& s : Registry) { From dcab0a1ea66349741a4a04f65e48d45c5b04ea7e Mon Sep 17 00:00:00 2001 From: linuxversion <1660477+SomeCrazyGuy@users.noreply.github.com> Date: Tue, 4 Jun 2024 22:28:11 -0400 Subject: [PATCH 3/3] check if signature scan succeeds, compile minhook libary into project --- Starfield Console Replacer.vcxproj | 6 +- Starfield Console Replacer.vcxproj.filters | 12 +- minhook/buffer.c | 312 +++++++ minhook/buffer.h | 42 + minhook/hde/hde32.c | 324 ++++++++ minhook/hde/hde32.h | 105 +++ minhook/hde/hde64.c | 335 ++++++++ minhook/hde/hde64.h | 112 +++ minhook/hde/pstdint.h | 39 + minhook/hde/table32.h | 73 ++ minhook/hde/table64.h | 74 ++ minhook/hook.c | 924 +++++++++++++++++++++ minhook/libMinHook.x64.lib | Bin 30014 -> 0 bytes minhook/trampoline.c | 320 +++++++ minhook/trampoline.h | 105 +++ src/console.cpp | 51 +- src/hook_api.cpp | 27 +- src/minhook_bridge.c | 30 + src/minhook_bridge.h | 13 + 19 files changed, 2837 insertions(+), 67 deletions(-) create mode 100644 minhook/buffer.c create mode 100644 minhook/buffer.h create mode 100644 minhook/hde/hde32.c create mode 100644 minhook/hde/hde32.h create mode 100644 minhook/hde/hde64.c create mode 100644 minhook/hde/hde64.h create mode 100644 minhook/hde/pstdint.h create mode 100644 minhook/hde/table32.h create mode 100644 minhook/hde/table64.h create mode 100644 minhook/hook.c delete mode 100644 minhook/libMinHook.x64.lib create mode 100644 minhook/trampoline.c create mode 100644 minhook/trampoline.h create mode 100644 src/minhook_bridge.c create mode 100644 src/minhook_bridge.h diff --git a/Starfield Console Replacer.vcxproj b/Starfield Console Replacer.vcxproj index b5504e3..a0c343c 100644 --- a/Starfield Console Replacer.vcxproj +++ b/Starfield Console Replacer.vcxproj @@ -188,7 +188,7 @@ copy /Y "$(OutDir)$(TargetName).dll" "$(OutDir)BetterConsole.asi" true true DebugFull - $(MSBuildProjectDirectory)\minhook\libMinHook.x64.lib;d3d11.lib;dxgi.lib;%(AdditionalDependencies) + d3d11.lib;dxgi.lib;%(AdditionalDependencies) true @@ -204,7 +204,6 @@ copy /Y "$(OutDir)$(TargetName).dll" "$(OutDir)BetterConsole.asi" - @@ -217,6 +216,7 @@ copy /Y "$(OutDir)$(TargetName).dll" "$(OutDir)BetterConsole.asi" + @@ -225,7 +225,6 @@ copy /Y "$(OutDir)$(TargetName).dll" "$(OutDir)BetterConsole.asi" - @@ -241,6 +240,7 @@ copy /Y "$(OutDir)$(TargetName).dll" "$(OutDir)BetterConsole.asi" + diff --git a/Starfield Console Replacer.vcxproj.filters b/Starfield Console Replacer.vcxproj.filters index 118fa87..e1357aa 100644 --- a/Starfield Console Replacer.vcxproj.filters +++ b/Starfield Console Replacer.vcxproj.filters @@ -32,9 +32,6 @@ imgui - - imgui - imgui @@ -80,6 +77,9 @@ Source Files + + Source Files + @@ -88,9 +88,6 @@ imgui - - imgui - imgui @@ -148,6 +145,9 @@ Header Files + + Header Files + diff --git a/minhook/buffer.c b/minhook/buffer.c new file mode 100644 index 0000000..55412b0 --- /dev/null +++ b/minhook/buffer.c @@ -0,0 +1,312 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "buffer.h" + +// Size of each memory block. (= page size of VirtualAlloc) +#define MEMORY_BLOCK_SIZE 0x1000 + +// Max range for seeking a memory block. (= 1024MB) +#define MAX_MEMORY_RANGE 0x40000000 + +// Memory protection flags to check the executable address. +#define PAGE_EXECUTE_FLAGS \ + (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) + +// Memory slot. +typedef struct _MEMORY_SLOT +{ + union + { + struct _MEMORY_SLOT *pNext; + UINT8 buffer[MEMORY_SLOT_SIZE]; + }; +} MEMORY_SLOT, *PMEMORY_SLOT; + +// Memory block info. Placed at the head of each block. +typedef struct _MEMORY_BLOCK +{ + struct _MEMORY_BLOCK *pNext; + PMEMORY_SLOT pFree; // First element of the free slot list. + UINT usedCount; +} MEMORY_BLOCK, *PMEMORY_BLOCK; + +//------------------------------------------------------------------------- +// Global Variables: +//------------------------------------------------------------------------- + +// First element of the memory block list. +PMEMORY_BLOCK g_pMemoryBlocks; + +//------------------------------------------------------------------------- +VOID InitializeBuffer(VOID) +{ + // Nothing to do for now. +} + +//------------------------------------------------------------------------- +VOID UninitializeBuffer(VOID) +{ + PMEMORY_BLOCK pBlock = g_pMemoryBlocks; + g_pMemoryBlocks = NULL; + + while (pBlock) + { + PMEMORY_BLOCK pNext = pBlock->pNext; + VirtualFree(pBlock, 0, MEM_RELEASE); + pBlock = pNext; + } +} + +//------------------------------------------------------------------------- +#if defined(_M_X64) || defined(__x86_64__) +static LPVOID FindPrevFreeRegion(LPVOID pAddress, LPVOID pMinAddr, DWORD dwAllocationGranularity) +{ + ULONG_PTR tryAddr = (ULONG_PTR)pAddress; + + // Round down to the allocation granularity. + tryAddr -= tryAddr % dwAllocationGranularity; + + // Start from the previous allocation granularity multiply. + tryAddr -= dwAllocationGranularity; + + while (tryAddr >= (ULONG_PTR)pMinAddr) + { + MEMORY_BASIC_INFORMATION mbi; + if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0) + break; + + if (mbi.State == MEM_FREE) + return (LPVOID)tryAddr; + + if ((ULONG_PTR)mbi.AllocationBase < dwAllocationGranularity) + break; + + tryAddr = (ULONG_PTR)mbi.AllocationBase - dwAllocationGranularity; + } + + return NULL; +} +#endif + +//------------------------------------------------------------------------- +#if defined(_M_X64) || defined(__x86_64__) +static LPVOID FindNextFreeRegion(LPVOID pAddress, LPVOID pMaxAddr, DWORD dwAllocationGranularity) +{ + ULONG_PTR tryAddr = (ULONG_PTR)pAddress; + + // Round down to the allocation granularity. + tryAddr -= tryAddr % dwAllocationGranularity; + + // Start from the next allocation granularity multiply. + tryAddr += dwAllocationGranularity; + + while (tryAddr <= (ULONG_PTR)pMaxAddr) + { + MEMORY_BASIC_INFORMATION mbi; + if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0) + break; + + if (mbi.State == MEM_FREE) + return (LPVOID)tryAddr; + + tryAddr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize; + + // Round up to the next allocation granularity. + tryAddr += dwAllocationGranularity - 1; + tryAddr -= tryAddr % dwAllocationGranularity; + } + + return NULL; +} +#endif + +//------------------------------------------------------------------------- +static PMEMORY_BLOCK GetMemoryBlock(LPVOID pOrigin) +{ + PMEMORY_BLOCK pBlock; +#if defined(_M_X64) || defined(__x86_64__) + ULONG_PTR minAddr; + ULONG_PTR maxAddr; + + SYSTEM_INFO si; + GetSystemInfo(&si); + minAddr = (ULONG_PTR)si.lpMinimumApplicationAddress; + maxAddr = (ULONG_PTR)si.lpMaximumApplicationAddress; + + // pOrigin ± 512MB + if ((ULONG_PTR)pOrigin > MAX_MEMORY_RANGE && minAddr < (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE) + minAddr = (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE; + + if (maxAddr > (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE) + maxAddr = (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE; + + // Make room for MEMORY_BLOCK_SIZE bytes. + maxAddr -= MEMORY_BLOCK_SIZE - 1; +#endif + + // Look the registered blocks for a reachable one. + for (pBlock = g_pMemoryBlocks; pBlock != NULL; pBlock = pBlock->pNext) + { +#if defined(_M_X64) || defined(__x86_64__) + // Ignore the blocks too far. + if ((ULONG_PTR)pBlock < minAddr || (ULONG_PTR)pBlock >= maxAddr) + continue; +#endif + // The block has at least one unused slot. + if (pBlock->pFree != NULL) + return pBlock; + } + +#if defined(_M_X64) || defined(__x86_64__) + // Alloc a new block above if not found. + { + LPVOID pAlloc = pOrigin; + while ((ULONG_PTR)pAlloc >= minAddr) + { + pAlloc = FindPrevFreeRegion(pAlloc, (LPVOID)minAddr, si.dwAllocationGranularity); + if (pAlloc == NULL) + break; + + pBlock = (PMEMORY_BLOCK)VirtualAlloc( + pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (pBlock != NULL) + break; + } + } + + // Alloc a new block below if not found. + if (pBlock == NULL) + { + LPVOID pAlloc = pOrigin; + while ((ULONG_PTR)pAlloc <= maxAddr) + { + pAlloc = FindNextFreeRegion(pAlloc, (LPVOID)maxAddr, si.dwAllocationGranularity); + if (pAlloc == NULL) + break; + + pBlock = (PMEMORY_BLOCK)VirtualAlloc( + pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (pBlock != NULL) + break; + } + } +#else + // In x86 mode, a memory block can be placed anywhere. + pBlock = (PMEMORY_BLOCK)VirtualAlloc( + NULL, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); +#endif + + if (pBlock != NULL) + { + // Build a linked list of all the slots. + PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBlock + 1; + pBlock->pFree = NULL; + pBlock->usedCount = 0; + do + { + pSlot->pNext = pBlock->pFree; + pBlock->pFree = pSlot; + pSlot++; + } while ((ULONG_PTR)pSlot - (ULONG_PTR)pBlock <= MEMORY_BLOCK_SIZE - MEMORY_SLOT_SIZE); + + pBlock->pNext = g_pMemoryBlocks; + g_pMemoryBlocks = pBlock; + } + + return pBlock; +} + +//------------------------------------------------------------------------- +LPVOID AllocateBuffer(LPVOID pOrigin) +{ + PMEMORY_SLOT pSlot; + PMEMORY_BLOCK pBlock = GetMemoryBlock(pOrigin); + if (pBlock == NULL) + return NULL; + + // Remove an unused slot from the list. + pSlot = pBlock->pFree; + pBlock->pFree = pSlot->pNext; + pBlock->usedCount++; +#ifdef _DEBUG + // Fill the slot with INT3 for debugging. + memset(pSlot, 0xCC, sizeof(MEMORY_SLOT)); +#endif + return pSlot; +} + +//------------------------------------------------------------------------- +VOID FreeBuffer(LPVOID pBuffer) +{ + PMEMORY_BLOCK pBlock = g_pMemoryBlocks; + PMEMORY_BLOCK pPrev = NULL; + ULONG_PTR pTargetBlock = ((ULONG_PTR)pBuffer / MEMORY_BLOCK_SIZE) * MEMORY_BLOCK_SIZE; + + while (pBlock != NULL) + { + if ((ULONG_PTR)pBlock == pTargetBlock) + { + PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBuffer; +#ifdef _DEBUG + // Clear the released slot for debugging. + memset(pSlot, 0x00, sizeof(MEMORY_SLOT)); +#endif + // Restore the released slot to the list. + pSlot->pNext = pBlock->pFree; + pBlock->pFree = pSlot; + pBlock->usedCount--; + + // Free if unused. + if (pBlock->usedCount == 0) + { + if (pPrev) + pPrev->pNext = pBlock->pNext; + else + g_pMemoryBlocks = pBlock->pNext; + + VirtualFree(pBlock, 0, MEM_RELEASE); + } + + break; + } + + pPrev = pBlock; + pBlock = pBlock->pNext; + } +} + +//------------------------------------------------------------------------- +BOOL IsExecutableAddress(LPVOID pAddress) +{ + MEMORY_BASIC_INFORMATION mi; + VirtualQuery(pAddress, &mi, sizeof(mi)); + + return (mi.State == MEM_COMMIT && (mi.Protect & PAGE_EXECUTE_FLAGS)); +} diff --git a/minhook/buffer.h b/minhook/buffer.h new file mode 100644 index 0000000..204d551 --- /dev/null +++ b/minhook/buffer.h @@ -0,0 +1,42 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +// Size of each memory slot. +#if defined(_M_X64) || defined(__x86_64__) + #define MEMORY_SLOT_SIZE 64 +#else + #define MEMORY_SLOT_SIZE 32 +#endif + +VOID InitializeBuffer(VOID); +VOID UninitializeBuffer(VOID); +LPVOID AllocateBuffer(LPVOID pOrigin); +VOID FreeBuffer(LPVOID pBuffer); +BOOL IsExecutableAddress(LPVOID pAddress); diff --git a/minhook/hde/hde32.c b/minhook/hde/hde32.c new file mode 100644 index 0000000..eb6af9b --- /dev/null +++ b/minhook/hde/hde32.c @@ -0,0 +1,324 @@ +/* + * Hacker Disassembler Engine 32 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#if defined(_M_IX86) || defined(__i386__) + +#include +#include "hde32.h" +#include "table32.h" + +unsigned int hde32_disasm(const void *code, hde32s *hs) +{ + uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; + uint8_t *ht = hde32_table, m_mod, m_reg, m_rm, disp_size = 0; + + memset(hs, 0, sizeof(hde32s)); + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde32_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { + ht = hde32_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde32_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde32_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde32_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde32_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde32_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde32_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if ((*ht++ & pref) && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + break; + } + + if (m_mod != 3 && m_rm == 4 && !(pref & PRE_67)) { + hs->flags |= F_SIB; + p++; + hs->sib = c; + hs->sib_scale = c >> 6; + hs->sib_index = (c & 0x3f) >> 3; + if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) + disp_size = 4; + } + + p--; + switch (disp_size) { + case 1: + hs->flags |= F_DISP8; + hs->disp.disp8 = *p; + break; + case 2: + hs->flags |= F_DISP16; + hs->disp.disp16 = *(uint16_t *)p; + break; + case 4: + hs->flags |= F_DISP32; + hs->disp.disp32 = *(uint32_t *)p; + break; + } + p += disp_size; + } else if (pref & PRE_LOCK) + hs->flags |= F_ERROR | F_ERROR_LOCK; + + if (cflags & C_IMM_P66) { + if (cflags & C_REL32) { + if (pref & PRE_66) { + hs->flags |= F_IMM16 | F_RELATIVE; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + goto disasm_done; + } + goto rel32_ok; + } + if (pref & PRE_66) { + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + } else { + hs->flags |= F_IMM32; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } + } + + if (cflags & C_IMM16) { + if (hs->flags & F_IMM32) { + hs->flags |= F_IMM16; + hs->disp.disp16 = *(uint16_t *)p; + } else if (hs->flags & F_IMM16) { + hs->flags |= F_2IMM16; + hs->disp.disp16 = *(uint16_t *)p; + } else { + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + } + p += 2; + } + if (cflags & C_IMM8) { + hs->flags |= F_IMM8; + hs->imm.imm8 = *p++; + } + + if (cflags & C_REL32) { + rel32_ok: + hs->flags |= F_IMM32 | F_RELATIVE; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else if (cflags & C_REL8) { + hs->flags |= F_IMM8 | F_RELATIVE; + hs->imm.imm8 = *p++; + } + + disasm_done: + + if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { + hs->flags |= F_ERROR | F_ERROR_LENGTH; + hs->len = 15; + } + + return (unsigned int)hs->len; +} + +#endif // defined(_M_IX86) || defined(__i386__) diff --git a/minhook/hde/hde32.h b/minhook/hde/hde32.h new file mode 100644 index 0000000..1112450 --- /dev/null +++ b/minhook/hde/hde32.h @@ -0,0 +1,105 @@ +/* + * Hacker Disassembler Engine 32 + * Copyright (c) 2006-2009, Vyacheslav Patkov. + * All rights reserved. + * + * hde32.h: C/C++ header file + * + */ + +#ifndef _HDE32_H_ +#define _HDE32_H_ + +/* stdint.h - C99 standard header + * http://en.wikipedia.org/wiki/stdint.h + * + * if your compiler doesn't contain "stdint.h" header (for + * example, Microsoft Visual C++), you can download file: + * http://www.azillionmonkeys.com/qed/pstdint.h + * and change next line to: + * #include "pstdint.h" + */ +#include "pstdint.h" + +#define F_MODRM 0x00000001 +#define F_SIB 0x00000002 +#define F_IMM8 0x00000004 +#define F_IMM16 0x00000008 +#define F_IMM32 0x00000010 +#define F_DISP8 0x00000020 +#define F_DISP16 0x00000040 +#define F_DISP32 0x00000080 +#define F_RELATIVE 0x00000100 +#define F_2IMM16 0x00000800 +#define F_ERROR 0x00001000 +#define F_ERROR_OPCODE 0x00002000 +#define F_ERROR_LENGTH 0x00004000 +#define F_ERROR_LOCK 0x00008000 +#define F_ERROR_OPERAND 0x00010000 +#define F_PREFIX_REPNZ 0x01000000 +#define F_PREFIX_REPX 0x02000000 +#define F_PREFIX_REP 0x03000000 +#define F_PREFIX_66 0x04000000 +#define F_PREFIX_67 0x08000000 +#define F_PREFIX_LOCK 0x10000000 +#define F_PREFIX_SEG 0x20000000 +#define F_PREFIX_ANY 0x3f000000 + +#define PREFIX_SEGMENT_CS 0x2e +#define PREFIX_SEGMENT_SS 0x36 +#define PREFIX_SEGMENT_DS 0x3e +#define PREFIX_SEGMENT_ES 0x26 +#define PREFIX_SEGMENT_FS 0x64 +#define PREFIX_SEGMENT_GS 0x65 +#define PREFIX_LOCK 0xf0 +#define PREFIX_REPNZ 0xf2 +#define PREFIX_REPX 0xf3 +#define PREFIX_OPERAND_SIZE 0x66 +#define PREFIX_ADDRESS_SIZE 0x67 + +#pragma pack(push,1) + +typedef struct { + uint8_t len; + uint8_t p_rep; + uint8_t p_lock; + uint8_t p_seg; + uint8_t p_66; + uint8_t p_67; + uint8_t opcode; + uint8_t opcode2; + uint8_t modrm; + uint8_t modrm_mod; + uint8_t modrm_reg; + uint8_t modrm_rm; + uint8_t sib; + uint8_t sib_scale; + uint8_t sib_index; + uint8_t sib_base; + union { + uint8_t imm8; + uint16_t imm16; + uint32_t imm32; + } imm; + union { + uint8_t disp8; + uint16_t disp16; + uint32_t disp32; + } disp; + uint32_t flags; +} hde32s; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +/* __cdecl */ +unsigned int hde32_disasm(const void *code, hde32s *hs); + +#ifdef __cplusplus +} +#endif + +#endif /* _HDE32_H_ */ diff --git a/minhook/hde/hde64.c b/minhook/hde/hde64.c new file mode 100644 index 0000000..ce773b1 --- /dev/null +++ b/minhook/hde/hde64.c @@ -0,0 +1,335 @@ +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#if defined(_M_X64) || defined(__x86_64__) + +#include +#include "hde64.h" +#include "table64.h" + +unsigned int hde64_disasm(const void *code, hde64s *hs) +{ + uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0; + uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0; + uint8_t op64 = 0; + + memset(hs, 0, sizeof(hde64s)); + + for (x = 16; x; x--) + switch (c = *p++) { + case 0xf3: + hs->p_rep = c; + pref |= PRE_F3; + break; + case 0xf2: + hs->p_rep = c; + pref |= PRE_F2; + break; + case 0xf0: + hs->p_lock = c; + pref |= PRE_LOCK; + break; + case 0x26: case 0x2e: case 0x36: + case 0x3e: case 0x64: case 0x65: + hs->p_seg = c; + pref |= PRE_SEG; + break; + case 0x66: + hs->p_66 = c; + pref |= PRE_66; + break; + case 0x67: + hs->p_67 = c; + pref |= PRE_67; + break; + default: + goto pref_done; + } + pref_done: + + hs->flags = (uint32_t)pref << 23; + + if (!pref) + pref |= PRE_NONE; + + if ((c & 0xf0) == 0x40) { + hs->flags |= F_PREFIX_REX; + if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8) + op64++; + hs->rex_r = (c & 7) >> 2; + hs->rex_x = (c & 3) >> 1; + hs->rex_b = c & 1; + if (((c = *p++) & 0xf0) == 0x40) { + opcode = c; + goto error_opcode; + } + } + + if ((hs->opcode = c) == 0x0f) { + hs->opcode2 = c = *p++; + ht += DELTA_OPCODES; + } else if (c >= 0xa0 && c <= 0xa3) { + op64++; + if (pref & PRE_67) + pref |= PRE_66; + else + pref &= ~PRE_66; + } + + opcode = c; + cflags = ht[ht[opcode / 4] + (opcode % 4)]; + + if (cflags == C_ERROR) { + error_opcode: + hs->flags |= F_ERROR | F_ERROR_OPCODE; + cflags = 0; + if ((opcode & -3) == 0x24) + cflags++; + } + + x = 0; + if (cflags & C_GROUP) { + uint16_t t; + t = *(uint16_t *)(ht + (cflags & 0x7f)); + cflags = (uint8_t)t; + x = (uint8_t)(t >> 8); + } + + if (hs->opcode2) { + ht = hde64_table + DELTA_PREFIXES; + if (ht[ht[opcode / 4] + (opcode % 4)] & pref) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (cflags & C_MODRM) { + hs->flags |= F_MODRM; + hs->modrm = c = *p++; + hs->modrm_mod = m_mod = c >> 6; + hs->modrm_rm = m_rm = c & 7; + hs->modrm_reg = m_reg = (c & 0x3f) >> 3; + + if (x && ((x << m_reg) & 0x80)) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + + if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) { + uint8_t t = opcode - 0xd9; + if (m_mod == 3) { + ht = hde64_table + DELTA_FPU_MODRM + t*8; + t = ht[m_reg] << m_rm; + } else { + ht = hde64_table + DELTA_FPU_REG; + t = ht[t] << m_reg; + } + if (t & 0x80) + hs->flags |= F_ERROR | F_ERROR_OPCODE; + } + + if (pref & PRE_LOCK) { + if (m_mod == 3) { + hs->flags |= F_ERROR | F_ERROR_LOCK; + } else { + uint8_t *table_end, op = opcode; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_LOCK_OK; + table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK; + } else { + ht = hde64_table + DELTA_OP_LOCK_OK; + table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK; + op &= -2; + } + for (; ht != table_end; ht++) + if (*ht++ == op) { + if (!((*ht << m_reg) & 0x80)) + goto no_lock_error; + else + break; + } + hs->flags |= F_ERROR | F_ERROR_LOCK; + no_lock_error: + ; + } + } + + if (hs->opcode2) { + switch (opcode) { + case 0x20: case 0x22: + m_mod = 3; + if (m_reg > 4 || m_reg == 1) + goto error_operand; + else + goto no_error_operand; + case 0x21: case 0x23: + m_mod = 3; + if (m_reg == 4 || m_reg == 5) + goto error_operand; + else + goto no_error_operand; + } + } else { + switch (opcode) { + case 0x8c: + if (m_reg > 5) + goto error_operand; + else + goto no_error_operand; + case 0x8e: + if (m_reg == 1 || m_reg > 5) + goto error_operand; + else + goto no_error_operand; + } + } + + if (m_mod == 3) { + uint8_t *table_end; + if (hs->opcode2) { + ht = hde64_table + DELTA_OP2_ONLY_MEM; + table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM; + } else { + ht = hde64_table + DELTA_OP_ONLY_MEM; + table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM; + } + for (; ht != table_end; ht += 2) + if (*ht++ == opcode) { + if ((*ht++ & pref) && !((*ht << m_reg) & 0x80)) + goto error_operand; + else + break; + } + goto no_error_operand; + } else if (hs->opcode2) { + switch (opcode) { + case 0x50: case 0xd7: case 0xf7: + if (pref & (PRE_NONE | PRE_66)) + goto error_operand; + break; + case 0xd6: + if (pref & (PRE_F2 | PRE_F3)) + goto error_operand; + break; + case 0xc5: + goto error_operand; + } + goto no_error_operand; + } else + goto no_error_operand; + + error_operand: + hs->flags |= F_ERROR | F_ERROR_OPERAND; + no_error_operand: + + c = *p++; + if (m_reg <= 1) { + if (opcode == 0xf6) + cflags |= C_IMM8; + else if (opcode == 0xf7) + cflags |= C_IMM_P66; + } + + switch (m_mod) { + case 0: + if (pref & PRE_67) { + if (m_rm == 6) + disp_size = 2; + } else + if (m_rm == 5) + disp_size = 4; + break; + case 1: + disp_size = 1; + break; + case 2: + disp_size = 2; + if (!(pref & PRE_67)) + disp_size <<= 1; + break; + } + + if (m_mod != 3 && m_rm == 4) { + hs->flags |= F_SIB; + p++; + hs->sib = c; + hs->sib_scale = c >> 6; + hs->sib_index = (c & 0x3f) >> 3; + if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1)) + disp_size = 4; + } + + p--; + switch (disp_size) { + case 1: + hs->flags |= F_DISP8; + hs->disp.disp8 = *p; + break; + case 2: + hs->flags |= F_DISP16; + hs->disp.disp16 = *(uint16_t *)p; + break; + case 4: + hs->flags |= F_DISP32; + hs->disp.disp32 = *(uint32_t *)p; + break; + } + p += disp_size; + } else if (pref & PRE_LOCK) + hs->flags |= F_ERROR | F_ERROR_LOCK; + + if (cflags & C_IMM_P66) { + if (cflags & C_REL32) { + if (pref & PRE_66) { + hs->flags |= F_IMM16 | F_RELATIVE; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + goto disasm_done; + } + goto rel32_ok; + } + if (op64) { + hs->flags |= F_IMM64; + hs->imm.imm64 = *(uint64_t *)p; + p += 8; + } else if (!(pref & PRE_66)) { + hs->flags |= F_IMM32; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else + goto imm16_ok; + } + + + if (cflags & C_IMM16) { + imm16_ok: + hs->flags |= F_IMM16; + hs->imm.imm16 = *(uint16_t *)p; + p += 2; + } + if (cflags & C_IMM8) { + hs->flags |= F_IMM8; + hs->imm.imm8 = *p++; + } + + if (cflags & C_REL32) { + rel32_ok: + hs->flags |= F_IMM32 | F_RELATIVE; + hs->imm.imm32 = *(uint32_t *)p; + p += 4; + } else if (cflags & C_REL8) { + hs->flags |= F_IMM8 | F_RELATIVE; + hs->imm.imm8 = *p++; + } + + disasm_done: + + if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) { + hs->flags |= F_ERROR | F_ERROR_LENGTH; + hs->len = 15; + } + + return (unsigned int)hs->len; +} + +#endif // defined(_M_X64) || defined(__x86_64__) diff --git a/minhook/hde/hde64.h b/minhook/hde/hde64.h new file mode 100644 index 0000000..ecbf4df --- /dev/null +++ b/minhook/hde/hde64.h @@ -0,0 +1,112 @@ +/* + * Hacker Disassembler Engine 64 + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + * hde64.h: C/C++ header file + * + */ + +#ifndef _HDE64_H_ +#define _HDE64_H_ + +/* stdint.h - C99 standard header + * http://en.wikipedia.org/wiki/stdint.h + * + * if your compiler doesn't contain "stdint.h" header (for + * example, Microsoft Visual C++), you can download file: + * http://www.azillionmonkeys.com/qed/pstdint.h + * and change next line to: + * #include "pstdint.h" + */ +#include "pstdint.h" + +#define F_MODRM 0x00000001 +#define F_SIB 0x00000002 +#define F_IMM8 0x00000004 +#define F_IMM16 0x00000008 +#define F_IMM32 0x00000010 +#define F_IMM64 0x00000020 +#define F_DISP8 0x00000040 +#define F_DISP16 0x00000080 +#define F_DISP32 0x00000100 +#define F_RELATIVE 0x00000200 +#define F_ERROR 0x00001000 +#define F_ERROR_OPCODE 0x00002000 +#define F_ERROR_LENGTH 0x00004000 +#define F_ERROR_LOCK 0x00008000 +#define F_ERROR_OPERAND 0x00010000 +#define F_PREFIX_REPNZ 0x01000000 +#define F_PREFIX_REPX 0x02000000 +#define F_PREFIX_REP 0x03000000 +#define F_PREFIX_66 0x04000000 +#define F_PREFIX_67 0x08000000 +#define F_PREFIX_LOCK 0x10000000 +#define F_PREFIX_SEG 0x20000000 +#define F_PREFIX_REX 0x40000000 +#define F_PREFIX_ANY 0x7f000000 + +#define PREFIX_SEGMENT_CS 0x2e +#define PREFIX_SEGMENT_SS 0x36 +#define PREFIX_SEGMENT_DS 0x3e +#define PREFIX_SEGMENT_ES 0x26 +#define PREFIX_SEGMENT_FS 0x64 +#define PREFIX_SEGMENT_GS 0x65 +#define PREFIX_LOCK 0xf0 +#define PREFIX_REPNZ 0xf2 +#define PREFIX_REPX 0xf3 +#define PREFIX_OPERAND_SIZE 0x66 +#define PREFIX_ADDRESS_SIZE 0x67 + +#pragma pack(push,1) + +typedef struct { + uint8_t len; + uint8_t p_rep; + uint8_t p_lock; + uint8_t p_seg; + uint8_t p_66; + uint8_t p_67; + uint8_t rex; + uint8_t rex_w; + uint8_t rex_r; + uint8_t rex_x; + uint8_t rex_b; + uint8_t opcode; + uint8_t opcode2; + uint8_t modrm; + uint8_t modrm_mod; + uint8_t modrm_reg; + uint8_t modrm_rm; + uint8_t sib; + uint8_t sib_scale; + uint8_t sib_index; + uint8_t sib_base; + union { + uint8_t imm8; + uint16_t imm16; + uint32_t imm32; + uint64_t imm64; + } imm; + union { + uint8_t disp8; + uint16_t disp16; + uint32_t disp32; + } disp; + uint32_t flags; +} hde64s; + +#pragma pack(pop) + +#ifdef __cplusplus +extern "C" { +#endif + +/* __cdecl */ +unsigned int hde64_disasm(const void *code, hde64s *hs); + +#ifdef __cplusplus +} +#endif + +#endif /* _HDE64_H_ */ diff --git a/minhook/hde/pstdint.h b/minhook/hde/pstdint.h new file mode 100644 index 0000000..84d82a0 --- /dev/null +++ b/minhook/hde/pstdint.h @@ -0,0 +1,39 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +// Integer types for HDE. +typedef INT8 int8_t; +typedef INT16 int16_t; +typedef INT32 int32_t; +typedef INT64 int64_t; +typedef UINT8 uint8_t; +typedef UINT16 uint16_t; +typedef UINT32 uint32_t; +typedef UINT64 uint64_t; diff --git a/minhook/hde/table32.h b/minhook/hde/table32.h new file mode 100644 index 0000000..7b3e12e --- /dev/null +++ b/minhook/hde/table32.h @@ -0,0 +1,73 @@ +/* + * Hacker Disassembler Engine 32 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#define C_NONE 0x00 +#define C_MODRM 0x01 +#define C_IMM8 0x02 +#define C_IMM16 0x04 +#define C_IMM_P66 0x10 +#define C_REL8 0x20 +#define C_REL32 0x40 +#define C_GROUP 0x80 +#define C_ERROR 0xff + +#define PRE_ANY 0x00 +#define PRE_NONE 0x01 +#define PRE_F2 0x02 +#define PRE_F3 0x04 +#define PRE_66 0x08 +#define PRE_67 0x10 +#define PRE_LOCK 0x20 +#define PRE_SEG 0x40 +#define PRE_ALL 0xff + +#define DELTA_OPCODES 0x4a +#define DELTA_FPU_REG 0xf1 +#define DELTA_FPU_MODRM 0xf8 +#define DELTA_PREFIXES 0x130 +#define DELTA_OP_LOCK_OK 0x1a1 +#define DELTA_OP2_LOCK_OK 0x1b9 +#define DELTA_OP_ONLY_MEM 0x1cb +#define DELTA_OP2_ONLY_MEM 0x1da + +unsigned char hde32_table[] = { + 0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3, + 0xa8,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xac,0xaa,0xb2,0xaa,0x9f,0x9f, + 0x9f,0x9f,0xb5,0xa3,0xa3,0xa4,0xaa,0xaa,0xba,0xaa,0x96,0xaa,0xa8,0xaa,0xc3, + 0xc3,0x96,0x96,0xb7,0xae,0xd6,0xbd,0xa3,0xc5,0xa3,0xa3,0x9f,0xc3,0x9c,0xaa, + 0xaa,0xac,0xaa,0xbf,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0x90, + 0x82,0x7d,0x97,0x59,0x59,0x59,0x59,0x59,0x7f,0x59,0x59,0x60,0x7d,0x7f,0x7f, + 0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x9a,0x88,0x7d, + 0x59,0x50,0x50,0x50,0x50,0x59,0x59,0x59,0x59,0x61,0x94,0x61,0x9e,0x59,0x59, + 0x85,0x59,0x92,0xa3,0x60,0x60,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59, + 0x59,0x59,0x9f,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xcc,0x01,0xbc,0x03,0xf0, + 0x10,0x10,0x10,0x10,0x50,0x50,0x50,0x50,0x14,0x20,0x20,0x20,0x20,0x01,0x01, + 0x01,0x01,0xc4,0x02,0x10,0x00,0x00,0x00,0x00,0x01,0x01,0xc0,0xc2,0x10,0x11, + 0x02,0x03,0x11,0x03,0x03,0x04,0x00,0x00,0x14,0x00,0x02,0x00,0x00,0xc6,0xc8, + 0x02,0x02,0x02,0x02,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xca, + 0x01,0x01,0x01,0x00,0x06,0x00,0x04,0x00,0xc0,0xc2,0x01,0x01,0x03,0x01,0xff, + 0xff,0x01,0x00,0x03,0xc4,0xc4,0xc6,0x03,0x01,0x01,0x01,0xff,0x03,0x03,0x03, + 0xc8,0x40,0x00,0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00, + 0x00,0x00,0x00,0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00, + 0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0xff,0xff,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x7f,0x00,0x00,0xff,0x4a,0x4a,0x4a,0x4a,0x4b,0x52,0x4a,0x4a,0x4a,0x4a,0x4f, + 0x4c,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x55,0x45,0x40,0x4a,0x4a,0x4a, + 0x45,0x59,0x4d,0x46,0x4a,0x5d,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a, + 0x4a,0x4a,0x4a,0x4a,0x4a,0x61,0x63,0x67,0x4e,0x4a,0x4a,0x6b,0x6d,0x4a,0x4a, + 0x45,0x6d,0x4a,0x4a,0x44,0x45,0x4a,0x4a,0x00,0x00,0x00,0x02,0x0d,0x06,0x06, + 0x06,0x06,0x0e,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x00,0x06,0x06,0x02,0x06, + 0x00,0x0a,0x0a,0x07,0x07,0x06,0x02,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, + 0x04,0x04,0x00,0x00,0x00,0x0e,0x05,0x06,0x06,0x06,0x01,0x06,0x00,0x00,0x08, + 0x00,0x10,0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01, + 0x86,0x00,0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba, + 0xf8,0xbb,0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00, + 0xc4,0xff,0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00, + 0x13,0x09,0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07, + 0xb2,0xff,0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf, + 0xe7,0x08,0x00,0xf0,0x02,0x00 +}; diff --git a/minhook/hde/table64.h b/minhook/hde/table64.h new file mode 100644 index 0000000..01d4541 --- /dev/null +++ b/minhook/hde/table64.h @@ -0,0 +1,74 @@ +/* + * Hacker Disassembler Engine 64 C + * Copyright (c) 2008-2009, Vyacheslav Patkov. + * All rights reserved. + * + */ + +#define C_NONE 0x00 +#define C_MODRM 0x01 +#define C_IMM8 0x02 +#define C_IMM16 0x04 +#define C_IMM_P66 0x10 +#define C_REL8 0x20 +#define C_REL32 0x40 +#define C_GROUP 0x80 +#define C_ERROR 0xff + +#define PRE_ANY 0x00 +#define PRE_NONE 0x01 +#define PRE_F2 0x02 +#define PRE_F3 0x04 +#define PRE_66 0x08 +#define PRE_67 0x10 +#define PRE_LOCK 0x20 +#define PRE_SEG 0x40 +#define PRE_ALL 0xff + +#define DELTA_OPCODES 0x4a +#define DELTA_FPU_REG 0xfd +#define DELTA_FPU_MODRM 0x104 +#define DELTA_PREFIXES 0x13c +#define DELTA_OP_LOCK_OK 0x1ae +#define DELTA_OP2_LOCK_OK 0x1c6 +#define DELTA_OP_ONLY_MEM 0x1d8 +#define DELTA_OP2_ONLY_MEM 0x1e7 + +unsigned char hde64_table[] = { + 0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5, + 0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1, + 0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea, + 0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0, + 0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab, + 0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92, + 0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90, + 0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b, + 0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b, + 0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc, + 0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20, + 0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff, + 0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00, + 0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01, + 0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10, + 0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00, + 0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00, + 0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00, + 0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff, + 0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00, + 0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40, + 0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43, + 0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40, + 0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06, + 0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07, + 0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04, + 0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10, + 0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00, + 0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb, + 0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff, + 0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09, + 0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff, + 0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08, + 0x00,0xf0,0x02,0x00 +}; diff --git a/minhook/hook.c b/minhook/hook.c new file mode 100644 index 0000000..d0368c4 --- /dev/null +++ b/minhook/hook.c @@ -0,0 +1,924 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include "MinHook.h" +#include "buffer.h" +#include "trampoline.h" + +#ifndef ARRAYSIZE + #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +// Initial capacity of the HOOK_ENTRY buffer. +#define INITIAL_HOOK_CAPACITY 32 + +// Initial capacity of the thread IDs buffer. +#define INITIAL_THREAD_CAPACITY 128 + +// Special hook position values. +#define INVALID_HOOK_POS UINT_MAX +#define ALL_HOOKS_POS UINT_MAX + +// Freeze() action argument defines. +#define ACTION_DISABLE 0 +#define ACTION_ENABLE 1 +#define ACTION_APPLY_QUEUED 2 + +// Thread access rights for suspending/resuming threads. +#define THREAD_ACCESS \ + (THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SET_CONTEXT) + +// Hook information. +typedef struct _HOOK_ENTRY +{ + LPVOID pTarget; // Address of the target function. + LPVOID pDetour; // Address of the detour or relay function. + LPVOID pTrampoline; // Address of the trampoline function. + UINT8 backup[8]; // Original prologue of the target function. + + UINT8 patchAbove : 1; // Uses the hot patch area. + UINT8 isEnabled : 1; // Enabled. + UINT8 queueEnable : 1; // Queued for enabling/disabling when != isEnabled. + + UINT nIP : 4; // Count of the instruction boundaries. + UINT8 oldIPs[8]; // Instruction boundaries of the target function. + UINT8 newIPs[8]; // Instruction boundaries of the trampoline function. +} HOOK_ENTRY, *PHOOK_ENTRY; + +// Suspended threads for Freeze()/Unfreeze(). +typedef struct _FROZEN_THREADS +{ + LPDWORD pItems; // Data heap + UINT capacity; // Size of allocated data heap, items + UINT size; // Actual number of data items +} FROZEN_THREADS, *PFROZEN_THREADS; + +//------------------------------------------------------------------------- +// Global Variables: +//------------------------------------------------------------------------- + +// Spin lock flag for EnterSpinLock()/LeaveSpinLock(). +volatile LONG g_isLocked = FALSE; + +// Private heap handle. If not NULL, this library is initialized. +HANDLE g_hHeap = NULL; + +// Hook entries. +struct +{ + PHOOK_ENTRY pItems; // Data heap + UINT capacity; // Size of allocated data heap, items + UINT size; // Actual number of data items +} g_hooks; + +//------------------------------------------------------------------------- +// Returns INVALID_HOOK_POS if not found. +static UINT FindHookEntry(LPVOID pTarget) +{ + UINT i; + for (i = 0; i < g_hooks.size; ++i) + { + if ((ULONG_PTR)pTarget == (ULONG_PTR)g_hooks.pItems[i].pTarget) + return i; + } + + return INVALID_HOOK_POS; +} + +//------------------------------------------------------------------------- +static PHOOK_ENTRY AddHookEntry() +{ + if (g_hooks.pItems == NULL) + { + g_hooks.capacity = INITIAL_HOOK_CAPACITY; + g_hooks.pItems = (PHOOK_ENTRY)HeapAlloc( + g_hHeap, 0, g_hooks.capacity * sizeof(HOOK_ENTRY)); + if (g_hooks.pItems == NULL) + return NULL; + } + else if (g_hooks.size >= g_hooks.capacity) + { + PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc( + g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity * 2) * sizeof(HOOK_ENTRY)); + if (p == NULL) + return NULL; + + g_hooks.capacity *= 2; + g_hooks.pItems = p; + } + + return &g_hooks.pItems[g_hooks.size++]; +} + +//------------------------------------------------------------------------- +static VOID DeleteHookEntry(UINT pos) +{ + if (pos < g_hooks.size - 1) + g_hooks.pItems[pos] = g_hooks.pItems[g_hooks.size - 1]; + + g_hooks.size--; + + if (g_hooks.capacity / 2 >= INITIAL_HOOK_CAPACITY && g_hooks.capacity / 2 >= g_hooks.size) + { + PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc( + g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity / 2) * sizeof(HOOK_ENTRY)); + if (p == NULL) + return; + + g_hooks.capacity /= 2; + g_hooks.pItems = p; + } +} + +//------------------------------------------------------------------------- +static DWORD_PTR FindOldIP(PHOOK_ENTRY pHook, DWORD_PTR ip) +{ + UINT i; + + if (pHook->patchAbove && ip == ((DWORD_PTR)pHook->pTarget - sizeof(JMP_REL))) + return (DWORD_PTR)pHook->pTarget; + + for (i = 0; i < pHook->nIP; ++i) + { + if (ip == ((DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i])) + return (DWORD_PTR)pHook->pTarget + pHook->oldIPs[i]; + } + +#if defined(_M_X64) || defined(__x86_64__) + // Check relay function. + if (ip == (DWORD_PTR)pHook->pDetour) + return (DWORD_PTR)pHook->pTarget; +#endif + + return 0; +} + +//------------------------------------------------------------------------- +static DWORD_PTR FindNewIP(PHOOK_ENTRY pHook, DWORD_PTR ip) +{ + UINT i; + for (i = 0; i < pHook->nIP; ++i) + { + if (ip == ((DWORD_PTR)pHook->pTarget + pHook->oldIPs[i])) + return (DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i]; + } + + return 0; +} + +//------------------------------------------------------------------------- +static VOID ProcessThreadIPs(HANDLE hThread, UINT pos, UINT action) +{ + // If the thread suspended in the overwritten area, + // move IP to the proper address. + + CONTEXT c; +#if defined(_M_X64) || defined(__x86_64__) + DWORD64 *pIP = &c.Rip; +#else + DWORD *pIP = &c.Eip; +#endif + UINT count; + + c.ContextFlags = CONTEXT_CONTROL; + if (!GetThreadContext(hThread, &c)) + return; + + if (pos == ALL_HOOKS_POS) + { + pos = 0; + count = g_hooks.size; + } + else + { + count = pos + 1; + } + + for (; pos < count; ++pos) + { + PHOOK_ENTRY pHook = &g_hooks.pItems[pos]; + BOOL enable; + DWORD_PTR ip; + + switch (action) + { + case ACTION_DISABLE: + enable = FALSE; + break; + + case ACTION_ENABLE: + enable = TRUE; + break; + + default: // ACTION_APPLY_QUEUED + enable = pHook->queueEnable; + break; + } + if (pHook->isEnabled == enable) + continue; + + if (enable) + ip = FindNewIP(pHook, *pIP); + else + ip = FindOldIP(pHook, *pIP); + + if (ip != 0) + { + *pIP = ip; + SetThreadContext(hThread, &c); + } + } +} + +//------------------------------------------------------------------------- +static BOOL EnumerateThreads(PFROZEN_THREADS pThreads) +{ + LPDWORD p; + BOOL succeeded = FALSE; + + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (hSnapshot != INVALID_HANDLE_VALUE) + { + THREADENTRY32 te; + te.dwSize = sizeof(THREADENTRY32); + if (Thread32First(hSnapshot, &te)) + { + succeeded = TRUE; + do + { + if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(DWORD)) + && te.th32OwnerProcessID == GetCurrentProcessId() + && te.th32ThreadID != GetCurrentThreadId()) + { + if (pThreads->pItems == NULL) + { + pThreads->capacity = INITIAL_THREAD_CAPACITY; + pThreads->pItems + = (LPDWORD)HeapAlloc(g_hHeap, 0, pThreads->capacity * sizeof(DWORD)); + if (pThreads->pItems == NULL) + { + succeeded = FALSE; + break; + } + } + else if (pThreads->size >= pThreads->capacity) + { + pThreads->capacity *= 2; + p = (LPDWORD)HeapReAlloc( + g_hHeap, 0, pThreads->pItems, pThreads->capacity * sizeof(DWORD)); + if (p == NULL) + { + succeeded = FALSE; + break; + } + + pThreads->pItems = p; + } + pThreads->pItems[pThreads->size++] = te.th32ThreadID; + } + + te.dwSize = sizeof(THREADENTRY32); + } while (Thread32Next(hSnapshot, &te)); + + if (succeeded && GetLastError() != ERROR_NO_MORE_FILES) + succeeded = FALSE; + + if (!succeeded && pThreads->pItems != NULL) + { + HeapFree(g_hHeap, 0, pThreads->pItems); + pThreads->pItems = NULL; + } + } + CloseHandle(hSnapshot); + } + + return succeeded; +} + +//------------------------------------------------------------------------- +static MH_STATUS Freeze(PFROZEN_THREADS pThreads, UINT pos, UINT action) +{ + MH_STATUS status = MH_OK; + + pThreads->pItems = NULL; + pThreads->capacity = 0; + pThreads->size = 0; + if (!EnumerateThreads(pThreads)) + { + status = MH_ERROR_MEMORY_ALLOC; + } + else if (pThreads->pItems != NULL) + { + UINT i; + for (i = 0; i < pThreads->size; ++i) + { + HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]); + if (hThread != NULL) + { + SuspendThread(hThread); + ProcessThreadIPs(hThread, pos, action); + CloseHandle(hThread); + } + } + } + + return status; +} + +//------------------------------------------------------------------------- +static VOID Unfreeze(PFROZEN_THREADS pThreads) +{ + if (pThreads->pItems != NULL) + { + UINT i; + for (i = 0; i < pThreads->size; ++i) + { + HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]); + if (hThread != NULL) + { + ResumeThread(hThread); + CloseHandle(hThread); + } + } + + HeapFree(g_hHeap, 0, pThreads->pItems); + } +} + +//------------------------------------------------------------------------- +static MH_STATUS EnableHookLL(UINT pos, BOOL enable) +{ + PHOOK_ENTRY pHook = &g_hooks.pItems[pos]; + DWORD oldProtect; + SIZE_T patchSize = sizeof(JMP_REL); + LPBYTE pPatchTarget = (LPBYTE)pHook->pTarget; + + if (pHook->patchAbove) + { + pPatchTarget -= sizeof(JMP_REL); + patchSize += sizeof(JMP_REL_SHORT); + } + + if (!VirtualProtect(pPatchTarget, patchSize, PAGE_EXECUTE_READWRITE, &oldProtect)) + return MH_ERROR_MEMORY_PROTECT; + + if (enable) + { + PJMP_REL pJmp = (PJMP_REL)pPatchTarget; + pJmp->opcode = 0xE9; + pJmp->operand = (UINT32)((LPBYTE)pHook->pDetour - (pPatchTarget + sizeof(JMP_REL))); + + if (pHook->patchAbove) + { + PJMP_REL_SHORT pShortJmp = (PJMP_REL_SHORT)pHook->pTarget; + pShortJmp->opcode = 0xEB; + pShortJmp->operand = (UINT8)(0 - (sizeof(JMP_REL_SHORT) + sizeof(JMP_REL))); + } + } + else + { + if (pHook->patchAbove) + memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); + else + memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL)); + } + + VirtualProtect(pPatchTarget, patchSize, oldProtect, &oldProtect); + + // Just-in-case measure. + FlushInstructionCache(GetCurrentProcess(), pPatchTarget, patchSize); + + pHook->isEnabled = !!enable; + pHook->queueEnable = !!enable; + + return MH_OK; +} + +//------------------------------------------------------------------------- +static MH_STATUS EnableAllHooksLL(BOOL enable) +{ + MH_STATUS status = MH_OK; + UINT i, first = INVALID_HOOK_POS; + + for (i = 0; i < g_hooks.size; ++i) + { + if (g_hooks.pItems[i].isEnabled != enable) + { + first = i; + break; + } + } + + if (first != INVALID_HOOK_POS) + { + FROZEN_THREADS threads; + status = Freeze(&threads, ALL_HOOKS_POS, enable ? ACTION_ENABLE : ACTION_DISABLE); + if (status == MH_OK) + { + for (i = first; i < g_hooks.size; ++i) + { + if (g_hooks.pItems[i].isEnabled != enable) + { + status = EnableHookLL(i, enable); + if (status != MH_OK) + break; + } + } + + Unfreeze(&threads); + } + } + + return status; +} + +//------------------------------------------------------------------------- +static VOID EnterSpinLock(VOID) +{ + SIZE_T spinCount = 0; + + // Wait until the flag is FALSE. + while (InterlockedCompareExchange(&g_isLocked, TRUE, FALSE) != FALSE) + { + // No need to generate a memory barrier here, since InterlockedCompareExchange() + // generates a full memory barrier itself. + + // Prevent the loop from being too busy. + if (spinCount < 32) + Sleep(0); + else + Sleep(1); + + spinCount++; + } +} + +//------------------------------------------------------------------------- +static VOID LeaveSpinLock(VOID) +{ + // No need to generate a memory barrier here, since InterlockedExchange() + // generates a full memory barrier itself. + + InterlockedExchange(&g_isLocked, FALSE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_Initialize(VOID) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap == NULL) + { + g_hHeap = HeapCreate(0, 0, 0); + if (g_hHeap != NULL) + { + // Initialize the internal function buffer. + InitializeBuffer(); + } + else + { + status = MH_ERROR_MEMORY_ALLOC; + } + } + else + { + status = MH_ERROR_ALREADY_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_Uninitialize(VOID) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + status = EnableAllHooksLL(FALSE); + if (status == MH_OK) + { + // Free the internal function buffer. + + // HeapFree is actually not required, but some tools detect a false + // memory leak without HeapFree. + + UninitializeBuffer(); + + HeapFree(g_hHeap, 0, g_hooks.pItems); + HeapDestroy(g_hHeap); + + g_hHeap = NULL; + + g_hooks.pItems = NULL; + g_hooks.capacity = 0; + g_hooks.size = 0; + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + if (IsExecutableAddress(pTarget) && IsExecutableAddress(pDetour)) + { + UINT pos = FindHookEntry(pTarget); + if (pos == INVALID_HOOK_POS) + { + LPVOID pBuffer = AllocateBuffer(pTarget); + if (pBuffer != NULL) + { + TRAMPOLINE ct; + + ct.pTarget = pTarget; + ct.pDetour = pDetour; + ct.pTrampoline = pBuffer; + if (CreateTrampolineFunction(&ct)) + { + PHOOK_ENTRY pHook = AddHookEntry(); + if (pHook != NULL) + { + pHook->pTarget = ct.pTarget; +#if defined(_M_X64) || defined(__x86_64__) + pHook->pDetour = ct.pRelay; +#else + pHook->pDetour = ct.pDetour; +#endif + pHook->pTrampoline = ct.pTrampoline; + pHook->patchAbove = !!ct.patchAbove; + pHook->isEnabled = FALSE; + pHook->queueEnable = FALSE; + pHook->nIP = ct.nIP; + memcpy(pHook->oldIPs, ct.oldIPs, ARRAYSIZE(ct.oldIPs)); + memcpy(pHook->newIPs, ct.newIPs, ARRAYSIZE(ct.newIPs)); + + // Back up the target function. + + if (ct.patchAbove) + { + memcpy( + pHook->backup, + (LPBYTE)pTarget - sizeof(JMP_REL), + sizeof(JMP_REL) + sizeof(JMP_REL_SHORT)); + } + else + { + memcpy(pHook->backup, pTarget, sizeof(JMP_REL)); + } + + if (ppOriginal != NULL) + *ppOriginal = pHook->pTrampoline; + } + else + { + status = MH_ERROR_MEMORY_ALLOC; + } + } + else + { + status = MH_ERROR_UNSUPPORTED_FUNCTION; + } + + if (status != MH_OK) + { + FreeBuffer(pBuffer); + } + } + else + { + status = MH_ERROR_MEMORY_ALLOC; + } + } + else + { + status = MH_ERROR_ALREADY_CREATED; + } + } + else + { + status = MH_ERROR_NOT_EXECUTABLE; + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + UINT pos = FindHookEntry(pTarget); + if (pos != INVALID_HOOK_POS) + { + if (g_hooks.pItems[pos].isEnabled) + { + FROZEN_THREADS threads; + status = Freeze(&threads, pos, ACTION_DISABLE); + if (status == MH_OK) + { + status = EnableHookLL(pos, FALSE); + + Unfreeze(&threads); + } + } + + if (status == MH_OK) + { + FreeBuffer(g_hooks.pItems[pos].pTrampoline); + DeleteHookEntry(pos); + } + } + else + { + status = MH_ERROR_NOT_CREATED; + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +static MH_STATUS EnableHook(LPVOID pTarget, BOOL enable) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + if (pTarget == MH_ALL_HOOKS) + { + status = EnableAllHooksLL(enable); + } + else + { + UINT pos = FindHookEntry(pTarget); + if (pos != INVALID_HOOK_POS) + { + if (g_hooks.pItems[pos].isEnabled != enable) + { + FROZEN_THREADS threads; + status = Freeze(&threads, pos, ACTION_ENABLE); + if (status == MH_OK) + { + status = EnableHookLL(pos, enable); + + Unfreeze(&threads); + } + } + else + { + status = enable ? MH_ERROR_ENABLED : MH_ERROR_DISABLED; + } + } + else + { + status = MH_ERROR_NOT_CREATED; + } + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget) +{ + return EnableHook(pTarget, TRUE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget) +{ + return EnableHook(pTarget, FALSE); +} + +//------------------------------------------------------------------------- +static MH_STATUS QueueHook(LPVOID pTarget, BOOL queueEnable) +{ + MH_STATUS status = MH_OK; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + if (pTarget == MH_ALL_HOOKS) + { + UINT i; + for (i = 0; i < g_hooks.size; ++i) + g_hooks.pItems[i].queueEnable = !!queueEnable; + } + else + { + UINT pos = FindHookEntry(pTarget); + if (pos != INVALID_HOOK_POS) + { + g_hooks.pItems[pos].queueEnable = !!queueEnable; + } + else + { + status = MH_ERROR_NOT_CREATED; + } + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget) +{ + return QueueHook(pTarget, TRUE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget) +{ + return QueueHook(pTarget, FALSE); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_ApplyQueued(VOID) +{ + MH_STATUS status = MH_OK; + UINT i, first = INVALID_HOOK_POS; + + EnterSpinLock(); + + if (g_hHeap != NULL) + { + for (i = 0; i < g_hooks.size; ++i) + { + if (g_hooks.pItems[i].isEnabled != g_hooks.pItems[i].queueEnable) + { + first = i; + break; + } + } + + if (first != INVALID_HOOK_POS) + { + FROZEN_THREADS threads; + status = Freeze(&threads, ALL_HOOKS_POS, ACTION_APPLY_QUEUED); + if (status == MH_OK) + { + for (i = first; i < g_hooks.size; ++i) + { + PHOOK_ENTRY pHook = &g_hooks.pItems[i]; + if (pHook->isEnabled != pHook->queueEnable) + { + status = EnableHookLL(i, pHook->queueEnable); + if (status != MH_OK) + break; + } + } + + Unfreeze(&threads); + } + } + } + else + { + status = MH_ERROR_NOT_INITIALIZED; + } + + LeaveSpinLock(); + + return status; +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_CreateHookApiEx( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, + LPVOID *ppOriginal, LPVOID *ppTarget) +{ + HMODULE hModule; + LPVOID pTarget; + + hModule = GetModuleHandleW(pszModule); + if (hModule == NULL) + return MH_ERROR_MODULE_NOT_FOUND; + + pTarget = (LPVOID)GetProcAddress(hModule, pszProcName); + if (pTarget == NULL) + return MH_ERROR_FUNCTION_NOT_FOUND; + + if (ppTarget != NULL) + *ppTarget = pTarget; + + return MH_CreateHook(pTarget, pDetour, ppOriginal); +} + +//------------------------------------------------------------------------- +MH_STATUS WINAPI MH_CreateHookApi( + LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal) +{ + return MH_CreateHookApiEx(pszModule, pszProcName, pDetour, ppOriginal, NULL); +} + +//------------------------------------------------------------------------- +const char *WINAPI MH_StatusToString(MH_STATUS status) +{ +#define MH_ST2STR(x) \ + case x: \ + return #x; + + switch (status) { + MH_ST2STR(MH_UNKNOWN) + MH_ST2STR(MH_OK) + MH_ST2STR(MH_ERROR_ALREADY_INITIALIZED) + MH_ST2STR(MH_ERROR_NOT_INITIALIZED) + MH_ST2STR(MH_ERROR_ALREADY_CREATED) + MH_ST2STR(MH_ERROR_NOT_CREATED) + MH_ST2STR(MH_ERROR_ENABLED) + MH_ST2STR(MH_ERROR_DISABLED) + MH_ST2STR(MH_ERROR_NOT_EXECUTABLE) + MH_ST2STR(MH_ERROR_UNSUPPORTED_FUNCTION) + MH_ST2STR(MH_ERROR_MEMORY_ALLOC) + MH_ST2STR(MH_ERROR_MEMORY_PROTECT) + MH_ST2STR(MH_ERROR_MODULE_NOT_FOUND) + MH_ST2STR(MH_ERROR_FUNCTION_NOT_FOUND) + } + +#undef MH_ST2STR + + return "(unknown)"; +} diff --git a/minhook/libMinHook.x64.lib b/minhook/libMinHook.x64.lib deleted file mode 100644 index 36bb8e717f9e1e59a0e4893c52fe9a033bfa7e45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30014 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 diff --git a/minhook/trampoline.c b/minhook/trampoline.c new file mode 100644 index 0000000..8d06d43 --- /dev/null +++ b/minhook/trampoline.c @@ -0,0 +1,320 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#if defined(_MSC_VER) && !defined(MINHOOK_DISABLE_INTRINSICS) + #define ALLOW_INTRINSICS + #include +#endif + +#ifndef ARRAYSIZE + #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) +#endif + +#if defined(_M_X64) || defined(__x86_64__) + #include "./hde/hde64.h" + typedef hde64s HDE; + #define HDE_DISASM(code, hs) hde64_disasm(code, hs) +#else + #include "./hde/hde32.h" + typedef hde32s HDE; + #define HDE_DISASM(code, hs) hde32_disasm(code, hs) +#endif + +#include "trampoline.h" +#include "buffer.h" + +// Maximum size of a trampoline function. +#if defined(_M_X64) || defined(__x86_64__) + #define TRAMPOLINE_MAX_SIZE (MEMORY_SLOT_SIZE - sizeof(JMP_ABS)) +#else + #define TRAMPOLINE_MAX_SIZE MEMORY_SLOT_SIZE +#endif + +//------------------------------------------------------------------------- +static BOOL IsCodePadding(LPBYTE pInst, UINT size) +{ + UINT i; + + if (pInst[0] != 0x00 && pInst[0] != 0x90 && pInst[0] != 0xCC) + return FALSE; + + for (i = 1; i < size; ++i) + { + if (pInst[i] != pInst[0]) + return FALSE; + } + return TRUE; +} + +//------------------------------------------------------------------------- +BOOL CreateTrampolineFunction(PTRAMPOLINE ct) +{ +#if defined(_M_X64) || defined(__x86_64__) + CALL_ABS call = { + 0xFF, 0x15, 0x00000002, // FF15 00000002: CALL [RIP+8] + 0xEB, 0x08, // EB 08: JMP +10 + 0x0000000000000000ULL // Absolute destination address + }; + JMP_ABS jmp = { + 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6] + 0x0000000000000000ULL // Absolute destination address + }; + JCC_ABS jcc = { + 0x70, 0x0E, // 7* 0E: J** +16 + 0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6] + 0x0000000000000000ULL // Absolute destination address + }; +#else + CALL_REL call = { + 0xE8, // E8 xxxxxxxx: CALL +5+xxxxxxxx + 0x00000000 // Relative destination address + }; + JMP_REL jmp = { + 0xE9, // E9 xxxxxxxx: JMP +5+xxxxxxxx + 0x00000000 // Relative destination address + }; + JCC_REL jcc = { + 0x0F, 0x80, // 0F8* xxxxxxxx: J** +6+xxxxxxxx + 0x00000000 // Relative destination address + }; +#endif + + UINT8 oldPos = 0; + UINT8 newPos = 0; + ULONG_PTR jmpDest = 0; // Destination address of an internal jump. + BOOL finished = FALSE; // Is the function completed? +#if defined(_M_X64) || defined(__x86_64__) + UINT8 instBuf[16]; +#endif + + ct->patchAbove = FALSE; + ct->nIP = 0; + + do + { + HDE hs; + UINT copySize; + LPVOID pCopySrc; + ULONG_PTR pOldInst = (ULONG_PTR)ct->pTarget + oldPos; + ULONG_PTR pNewInst = (ULONG_PTR)ct->pTrampoline + newPos; + + copySize = HDE_DISASM((LPVOID)pOldInst, &hs); + if (hs.flags & F_ERROR) + return FALSE; + + pCopySrc = (LPVOID)pOldInst; + if (oldPos >= sizeof(JMP_REL)) + { + // The trampoline function is long enough. + // Complete the function with the jump to the target function. +#if defined(_M_X64) || defined(__x86_64__) + jmp.address = pOldInst; +#else + jmp.operand = (UINT32)(pOldInst - (pNewInst + sizeof(jmp))); +#endif + pCopySrc = &jmp; + copySize = sizeof(jmp); + + finished = TRUE; + } +#if defined(_M_X64) || defined(__x86_64__) + else if ((hs.modrm & 0xC7) == 0x05) + { + // Instructions using RIP relative addressing. (ModR/M = 00???101B) + + // Modify the RIP relative address. + PUINT32 pRelAddr; + + // Avoid using memcpy to reduce the footprint. +#ifndef ALLOW_INTRINSICS + memcpy(instBuf, (LPBYTE)pOldInst, copySize); +#else + __movsb(instBuf, (LPBYTE)pOldInst, copySize); +#endif + pCopySrc = instBuf; + + // Relative address is stored at (instruction length - immediate value length - 4). + pRelAddr = (PUINT32)(instBuf + hs.len - ((hs.flags & 0x3C) >> 2) - 4); + *pRelAddr + = (UINT32)((pOldInst + hs.len + (INT32)hs.disp.disp32) - (pNewInst + hs.len)); + + // Complete the function if JMP (FF /4). + if (hs.opcode == 0xFF && hs.modrm_reg == 4) + finished = TRUE; + } +#endif + else if (hs.opcode == 0xE8) + { + // Direct relative CALL + ULONG_PTR dest = pOldInst + hs.len + (INT32)hs.imm.imm32; +#if defined(_M_X64) || defined(__x86_64__) + call.address = dest; +#else + call.operand = (UINT32)(dest - (pNewInst + sizeof(call))); +#endif + pCopySrc = &call; + copySize = sizeof(call); + } + else if ((hs.opcode & 0xFD) == 0xE9) + { + // Direct relative JMP (EB or E9) + ULONG_PTR dest = pOldInst + hs.len; + + if (hs.opcode == 0xEB) // isShort jmp + dest += (INT8)hs.imm.imm8; + else + dest += (INT32)hs.imm.imm32; + + // Simply copy an internal jump. + if ((ULONG_PTR)ct->pTarget <= dest + && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) + { + if (jmpDest < dest) + jmpDest = dest; + } + else + { +#if defined(_M_X64) || defined(__x86_64__) + jmp.address = dest; +#else + jmp.operand = (UINT32)(dest - (pNewInst + sizeof(jmp))); +#endif + pCopySrc = &jmp; + copySize = sizeof(jmp); + + // Exit the function if it is not in the branch. + finished = (pOldInst >= jmpDest); + } + } + else if ((hs.opcode & 0xF0) == 0x70 + || (hs.opcode & 0xFC) == 0xE0 + || (hs.opcode2 & 0xF0) == 0x80) + { + // Direct relative Jcc + ULONG_PTR dest = pOldInst + hs.len; + + if ((hs.opcode & 0xF0) == 0x70 // Jcc + || (hs.opcode & 0xFC) == 0xE0) // LOOPNZ/LOOPZ/LOOP/JECXZ + dest += (INT8)hs.imm.imm8; + else + dest += (INT32)hs.imm.imm32; + + // Simply copy an internal jump. + if ((ULONG_PTR)ct->pTarget <= dest + && dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL))) + { + if (jmpDest < dest) + jmpDest = dest; + } + else if ((hs.opcode & 0xFC) == 0xE0) + { + // LOOPNZ/LOOPZ/LOOP/JCXZ/JECXZ to the outside are not supported. + return FALSE; + } + else + { + UINT8 cond = ((hs.opcode != 0x0F ? hs.opcode : hs.opcode2) & 0x0F); +#if defined(_M_X64) || defined(__x86_64__) + // Invert the condition in x64 mode to simplify the conditional jump logic. + jcc.opcode = 0x71 ^ cond; + jcc.address = dest; +#else + jcc.opcode1 = 0x80 | cond; + jcc.operand = (UINT32)(dest - (pNewInst + sizeof(jcc))); +#endif + pCopySrc = &jcc; + copySize = sizeof(jcc); + } + } + else if ((hs.opcode & 0xFE) == 0xC2) + { + // RET (C2 or C3) + + // Complete the function if not in a branch. + finished = (pOldInst >= jmpDest); + } + + // Can't alter the instruction length in a branch. + if (pOldInst < jmpDest && copySize != hs.len) + return FALSE; + + // Trampoline function is too large. + if ((newPos + copySize) > TRAMPOLINE_MAX_SIZE) + return FALSE; + + // Trampoline function has too many instructions. + if (ct->nIP >= ARRAYSIZE(ct->oldIPs)) + return FALSE; + + ct->oldIPs[ct->nIP] = oldPos; + ct->newIPs[ct->nIP] = newPos; + ct->nIP++; + + // Avoid using memcpy to reduce the footprint. +#ifndef ALLOW_INTRINSICS + memcpy((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize); +#else + __movsb((LPBYTE)ct->pTrampoline + newPos, (LPBYTE)pCopySrc, copySize); +#endif + newPos += (UINT8)copySize; + oldPos += hs.len; + } while (!finished); + + // Is there enough place for a long jump? + if (oldPos < sizeof(JMP_REL) + && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL) - oldPos)) + { + // Is there enough place for a short jump? + if (oldPos < sizeof(JMP_REL_SHORT) + && !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL_SHORT) - oldPos)) + { + return FALSE; + } + + // Can we place the long jump above the function? + if (!IsExecutableAddress((LPBYTE)ct->pTarget - sizeof(JMP_REL))) + return FALSE; + + if (!IsCodePadding((LPBYTE)ct->pTarget - sizeof(JMP_REL), sizeof(JMP_REL))) + return FALSE; + + ct->patchAbove = TRUE; + } + +#if defined(_M_X64) || defined(__x86_64__) + // Create a relay function. + jmp.address = (ULONG_PTR)ct->pDetour; + + ct->pRelay = (LPBYTE)ct->pTrampoline + newPos; + memcpy(ct->pRelay, &jmp, sizeof(jmp)); +#endif + + return TRUE; +} diff --git a/minhook/trampoline.h b/minhook/trampoline.h new file mode 100644 index 0000000..bdffdac --- /dev/null +++ b/minhook/trampoline.h @@ -0,0 +1,105 @@ +/* + * MinHook - The Minimalistic API Hooking Library for x64/x86 + * Copyright (C) 2009-2017 Tsuda Kageyu. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#pragma pack(push, 1) + +// Structs for writing x86/x64 instructions. + +// 8-bit relative jump. +typedef struct _JMP_REL_SHORT +{ + UINT8 opcode; // EB xx: JMP +2+xx + UINT8 operand; +} JMP_REL_SHORT, *PJMP_REL_SHORT; + +// 32-bit direct relative jump/call. +typedef struct _JMP_REL +{ + UINT8 opcode; // E9/E8 xxxxxxxx: JMP/CALL +5+xxxxxxxx + UINT32 operand; // Relative destination address +} JMP_REL, *PJMP_REL, CALL_REL; + +// 64-bit indirect absolute jump. +typedef struct _JMP_ABS +{ + UINT8 opcode0; // FF25 00000000: JMP [+6] + UINT8 opcode1; + UINT32 dummy; + UINT64 address; // Absolute destination address +} JMP_ABS, *PJMP_ABS; + +// 64-bit indirect absolute call. +typedef struct _CALL_ABS +{ + UINT8 opcode0; // FF15 00000002: CALL [+6] + UINT8 opcode1; + UINT32 dummy0; + UINT8 dummy1; // EB 08: JMP +10 + UINT8 dummy2; + UINT64 address; // Absolute destination address +} CALL_ABS; + +// 32-bit direct relative conditional jumps. +typedef struct _JCC_REL +{ + UINT8 opcode0; // 0F8* xxxxxxxx: J** +6+xxxxxxxx + UINT8 opcode1; + UINT32 operand; // Relative destination address +} JCC_REL; + +// 64bit indirect absolute conditional jumps that x64 lacks. +typedef struct _JCC_ABS +{ + UINT8 opcode; // 7* 0E: J** +16 + UINT8 dummy0; + UINT8 dummy1; // FF25 00000000: JMP [+6] + UINT8 dummy2; + UINT32 dummy3; + UINT64 address; // Absolute destination address +} JCC_ABS; + +#pragma pack(pop) + +typedef struct _TRAMPOLINE +{ + LPVOID pTarget; // [In] Address of the target function. + LPVOID pDetour; // [In] Address of the detour function. + LPVOID pTrampoline; // [In] Buffer address for the trampoline and relay function. + +#if defined(_M_X64) || defined(__x86_64__) + LPVOID pRelay; // [Out] Address of the relay function. +#endif + BOOL patchAbove; // [Out] Should use the hot patch area? + UINT nIP; // [Out] Number of the instruction boundaries. + UINT8 oldIPs[8]; // [Out] Instruction boundaries of the target function. + UINT8 newIPs[8]; // [Out] Instruction boundaries of the trampoline function. +} TRAMPOLINE, *PTRAMPOLINE; + +BOOL CreateTrampolineFunction(PTRAMPOLINE ct); diff --git a/src/console.cpp b/src/console.cpp index 957d805..262311b 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -4,6 +4,7 @@ #include #include +// Console print interface: //48 89 5c 24 ?? 48 89 6c 24 ?? 48 89 74 24 ?? 57 b8 30 10 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 49 - new sig with 1 match //48 89 5c 24 ?? 48 89 6c 24 ?? 48 89 74 24 ?? 57 b8 30 10 -- the 0x1030 stack size might be too unique /* @@ -27,11 +28,7 @@ return; */ - -constexpr uint64_t OFFSET_console_vprint = 0x29177f8; //stolen from sfse, void ConsolePrintV(ConsoleMgr*, const char* fmt, va_list args) - - - +// Console run command interface: //48 8b c4 48 89 50 ?? 4c 89 40 ?? 4c 89 48 ?? 55 53 56 57 41 55 41 56 41 57 48 8d /* check for this in ghidra: @@ -42,10 +39,7 @@ memset(local_c38,0,0x400); pcVar15 = "float fresult\nref refr\nset refr to GetSelectedRef\nset fresult to "; */ -constexpr uint64_t OFFSET_console_run = 0x2911d84; //void ConsoleRun(NULL, char* cmd) - -//TODO: get the location of betterconsole and spawn the files there? #define OUTPUT_FILE_PATH "BetterConsoleOutput.txt" #define HISTORY_FILE_PATH "BetterConsoleHistory.txt" @@ -254,37 +248,20 @@ extern void setup_console(const BetterAPI* api) { HistoryHandle = LogBuffer->Restore("Command History", HISTORY_FILE_PATH); const auto hook_print_aob = HookAPI->AOBScanEXE("48 89 5c 24 ?? 48 89 6c 24 ?? 48 89 74 24 ?? 57 b8 30 10 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 49"); - - if (hook_print_aob) { - DEBUG("Hooking print function using AOB method"); - OLD_ConsolePrintV = (decltype(OLD_ConsolePrintV))HookAPI->HookFunction( - (FUNC_PTR)hook_print_aob, - (FUNC_PTR)console_print - ); - } - else { - DEBUG("Hooking print function using offset method"); - OLD_ConsolePrintV = (decltype(OLD_ConsolePrintV))HookAPI->HookFunction( - (FUNC_PTR)HookAPI->Relocate(OFFSET_console_vprint), - (FUNC_PTR)console_print - ); - } + ASSERT(hook_print_aob != NULL && "Could not hook console_print function (game version incompatible?)"); + DEBUG("Hooking print function using AOB method"); + OLD_ConsolePrintV = (decltype(OLD_ConsolePrintV))HookAPI->HookFunction( + (FUNC_PTR)hook_print_aob, + (FUNC_PTR)console_print + ); const auto hook_run_aob = HookAPI->AOBScanEXE("48 8b c4 48 89 50 ?? 4c 89 40 ?? 4c 89 48 ?? 55 53 56 57 41 55 41 56 41 57 48 8d"); - if (hook_run_aob) { - DEBUG("Hooking run function using AOB method"); - OLD_ConsoleRun = (decltype(OLD_ConsoleRun))HookAPI->HookFunction( - (FUNC_PTR)hook_run_aob, - (FUNC_PTR)console_run - ); - } - else { - DEBUG("Hooking run function using offset method"); - OLD_ConsoleRun = (decltype(OLD_ConsoleRun))HookAPI->HookFunction( - (FUNC_PTR)HookAPI->Relocate(OFFSET_console_run), - (FUNC_PTR)console_run - ); - } + ASSERT(hook_run_aob != NULL && "Could not hook console_run function (game version incompatible?)"); + DEBUG("Hooking run function using AOB method"); + OLD_ConsoleRun = (decltype(OLD_ConsoleRun))HookAPI->HookFunction( + (FUNC_PTR)hook_run_aob, + (FUNC_PTR)console_run + ); IOBuffer[0] = 0; } \ No newline at end of file diff --git a/src/hook_api.cpp b/src/hook_api.cpp index e98fed5..1371429 100644 --- a/src/hook_api.cpp +++ b/src/hook_api.cpp @@ -1,28 +1,13 @@ #include "main.h" -#include "../minhook/MinHook.h" +#include "minhook_bridge.h" + +#include static FUNC_PTR HookFunction(FUNC_PTR old, FUNC_PTR new_func) { - ASSERT(old != NULL); - ASSERT(new_func != NULL); - DEBUG("OLD: '%p', NEW: '%p'", old, new_func); - static bool init = false; - if (!init) { - if (MH_Initialize() != MH_OK) { - ASSERT(false && "minhook failed to initialize"); - } - init = true; - } - FUNC_PTR ret = nullptr; - if (MH_CreateHook(old, new_func, (LPVOID*)&ret) != MH_OK) { - ASSERT(false && "minhook failed to hook function"); - return NULL; - } - if (MH_EnableHook(old) != MH_OK) { - ASSERT(false && "minhook failed to enable hook"); - return NULL; - }; - ASSERT(ret != NULL); + auto ret = minhook_hook_function(old, new_func); + DEBUG("Hook Function: old: %p, new: %p, trampoline: %p", old, new_func, ret); + ASSERT(ret != NULL && "MinHook failed to hook function!"); return ret; } diff --git a/src/minhook_bridge.c b/src/minhook_bridge.c new file mode 100644 index 0000000..6cdf345 --- /dev/null +++ b/src/minhook_bridge.c @@ -0,0 +1,30 @@ +#include "../minhook/MinHook.h" +#include "../minhook/trampoline.h" +#include "../minhook/buffer.h" + +#include "../minhook/hde/pstdint.h" +#include "../minhook/hde/hde64.h" + +#include "../minhook/hde/hde64.c" +#include "../minhook/buffer.c" +#include "../minhook/trampoline.c" +#include "../minhook/hook.c" + + +#include "minhook_bridge.h" + +CEXPORT FUNC_PTR minhook_hook_function(FUNC_PTR old_func, FUNC_PTR new_func) { + static unsigned init = 0; + if (!init) { + MH_Initialize(); + init = 1; + } + FUNC_PTR ret = NULL; + if (MH_CreateHook((LPVOID)old_func, (LPVOID)new_func, (LPVOID*)&ret) != MH_OK) { + return NULL; + } + if (MH_EnableHook((LPVOID)old_func) != MH_OK) { + return NULL; + }; + return ret; +} \ No newline at end of file diff --git a/src/minhook_bridge.h b/src/minhook_bridge.h new file mode 100644 index 0000000..31a395a --- /dev/null +++ b/src/minhook_bridge.h @@ -0,0 +1,13 @@ +#pragma once + +#include "../betterapi.h" + +#ifdef __cplusplus +#define CEXPORT extern "C" +#else +#define CEXPORT extern +#endif + +// use minhook to hook old_func and redirect to new_func, return a pointer to call old_func +// returns null on error +CEXPORT FUNC_PTR minhook_hook_function(FUNC_PTR old_func, FUNC_PTR new_func); \ No newline at end of file