学习是对技术的祛魅
公众号:
 
或许我们的公众号会有更多你感兴趣的内容
  Direct3D11 注入 
在此之前,公众号已经简述了MinHook的原理,那么利用这种原理我们就可以通过hook在d3d编写的游戏中实现窗口
相关代码:https://github.com/Joe1sn/dx11-hook-example 
  Direct3D简述 
这里使用ImGui的默认dx11版本示例来讲解,首先d3d的绘制依靠的是windows的窗口(window),接着是D3D设备、上下文和交换链以及各种绘制方法的d3dAPI传入操作系统和GPU,最后传输到显示器显示,大致如下
 
那么对于d3d API来说是这样的:
  如何显示 
那么最后如何得到渲染好的最终帧呢?答案是交换链的 IDXGISwapChain::Present 方法用于呈现最终渲染的帧
https://learn.microsoft.com/zh-cn/windows/win32/api/dxgi/nn-dxgi-idxgiswapchain 
具体参数
https://learn.microsoft.com/zh-cn/windows/win32/api/dxgi/nf-dxgi-idxgiswapchain-present 
那么我们可以hook这个函数,然后提交我们要现实的内容,最后再一同显示出来
  编写DLL 
这里我就是用visual studio 2022 默认的DLL项目
导入imgui和minhook
本篇文章将处理的是dx11版本的d3d,那么如何判断游戏是否使用了该dll呢?一般来说是凭借经验,不过也可以使用CE遍历一下DLL即可
如果发现调用了多种图形API,例如OpenGL,Vulkan等,可查看使用调用对应的呈现最终帧的渲染函数,也可以参考kiero的一些做法,链接:https://github.com/Rebzzel/kiero 
那么如何找到该函数呢?别忘了最后的dll是注入到程序中的,那么我们的dll也可以使用d3d11.dll,而且前文提到过IDXGISwapChain::Present是IDXGISwapChain下的方法,那么我们如果也要使用该方法,也是会跳转到同一个位置(因为注入后就是同一个程序了,那么对应的dll的内存也是同一块)
而且Present是虚函数,位置在vtable[8],
那么可以得到定位函数,这里抄了下imgui例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 typedef  long (__stdcall* DIGX_Present) (IDXGISwapChain*, UINT, UINT)  ;DIGX_Present originPresent; DIGX_Present old_present; bool  getPresentPtr ()  {         DXGI_SWAP_CHAIN_DESC sd;     ZeroMemory (&sd, sizeof (sd));     sd.BufferCount = 2 ;     sd.BufferDesc.Width = 0 ;     sd.BufferDesc.Height = 0 ;     sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;               sd.OutputWindow = GetForegroundWindow ();             sd.SampleDesc.Count = 1 ;     sd.Windowed = TRUE;     sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;     UINT createDeviceFlags = 0 ;     IDXGISwapChain* swap_chain;     ID3D11Device* device;     D3D_FEATURE_LEVEL featureLevel;     const  D3D_FEATURE_LEVEL featureLevelArray[2 ] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, };     if  (D3D11CreateDeviceAndSwapChain (         nullptr ,          D3D_DRIVER_TYPE_HARDWARE,          nullptr ,          createDeviceFlags,          featureLevelArray,          2 ,          D3D11_SDK_VERSION,          &sd,          &swap_chain,         &device,         &featureLevel,          nullptr ) == S_OK) {         void ** p_vtable = *reinterpret_cast <void ***>(swap_chain);            swap_chain->Release ();               device->Release ();                   old_present = (DIGX_Present)p_vtable[8 ];             return  true ;     }     return  false ; } 
 
确定下主线程的主要结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 int  WINAPI main (HMODULE hModule)   {         if  (!getPresentPtr ())         return  1 ;               if  (MH_Initialize () != MH_OK)         return  1 ;          if  (MH_CreateHook (reinterpret_cast <void **>(old_present), &myPresent, reinterpret_cast <void **>(&originPresent)) != MH_OK)         return  1 ;          if  (MH_EnableHook (old_present) != MH_OK)         return  1 ;          while  (true ) {         Sleep (50 );         if  (GetAsyncKeyState (VK_F1)) {             break ;         }     }                   if  (MH_DisableHook (MH_ALL_HOOKS) != MH_OK)         return  1 ;     if  (MH_Uninitialize () != MH_OK)         return  1 ;     FreeLibraryAndExitThread (hModule, 0 );     return  0 ; } BOOL APIENTRY DllMain ( HMODULE hModule,                         DWORD  ul_reason_for_call,                        LPVOID lpReserved                      )  {    switch  (ul_reason_for_call)     {     case  DLL_PROCESS_ATTACH: {         CreateThread (NULL , 0 , (LPTHREAD_START_ROUTINE)main, hModule, 0 , NULL );     }     case  DLL_THREAD_ATTACH:     case  DLL_THREAD_DETACH:     case  DLL_PROCESS_DETACH:         break ;     }     return  TRUE; } 
 
完成myPresent来替换旧的Present,这里依旧是抄了写imgui的示例代码,主要还是参考示例代码中的初始化和绘制的步骤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 WNDPROC oWndProc; extern  IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)  ;LRESULT WINAPI WndProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)   {    if  (ImGui_ImplWin32_WndProcHandler (hWnd, msg, wParam, lParam))         return  true ;     return  ::DefWindowProcW (hWnd, msg, wParam, lParam); } bool  init = false ;HWND window = NULL ; static  ID3D11Device* g_pd3dDevice = nullptr ;static  ID3D11DeviceContext* g_pd3dDeviceContext = nullptr ;static  ID3D11RenderTargetView* g_mainRenderTargetView = nullptr ;static  long  __stdcall myPresent (IDXGISwapChain* p_swap_chain, UINT sync_interval, UINT flags)   {    if  (!init) {                  if  (SUCCEEDED (p_swap_chain->GetDevice (__uuidof(ID3D11Device), (void **)&g_pd3dDevice)))         {                          g_pd3dDevice->GetImmediateContext (&g_pd3dDeviceContext);                                       DXGI_SWAP_CHAIN_DESC sd;             p_swap_chain->GetDesc (&sd);             window = sd.OutputWindow;             ID3D11Texture2D* pBackBuffer;             p_swap_chain->GetBuffer (0 , __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);             g_pd3dDevice->CreateRenderTargetView (pBackBuffer, NULL , &g_mainRenderTargetView);             pBackBuffer->Release ();             oWndProc = (WNDPROC)SetWindowLongPtr (window, GWLP_WNDPROC, (LONG_PTR)WndProc);                          ImGui::CreateContext ();             ImGuiIO& io = ImGui::GetIO (); (void )io;             io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;                  io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;                   ImGui::StyleColorsDark ();             ImGuiStyle& style = ImGui::GetStyle ();             if  (io.ConfigFlags)             {                 style.WindowRounding = 0.0f ;                 style.Colors[ImGuiCol_WindowBg].w = 1.0f ;             }             ImGui_ImplWin32_Init (window);             ImGui_ImplDX11_Init (g_pd3dDevice, g_pd3dDeviceContext);             init = true ;         }         else              return  originPresent (p_swap_chain, sync_interval, flags);     }     ImGui_ImplDX11_NewFrame ();     ImGui_ImplWin32_NewFrame ();     ImGui::NewFrame ();     ImGui::ShowDemoWindow ();     ImGui::EndFrame ();     ImGui::Render ();     g_pd3dDeviceContext->OMSetRenderTargets (1 , &g_mainRenderTargetView, nullptr );     ImGui_ImplDX11_RenderDrawData (ImGui::GetDrawData ());                                                       return  originPresent (p_swap_chain, sync_interval, flags); }