阶段七:图形渲染
📅 创建日期: 2025-01-XX
⏱️ 预计学时: 6-8 周
🎯 学习目标: 掌握 ImGui 界面库、DirectX Hook、游戏 Overlay 开发
📋 前置要求: [[阶段五:核心逆向技术]]
🔗 返回: [[逆向与驱动开发学习路径(详细版)]]
📚 本阶段内容概览
- [[#7.1 ImGui入门|7.1 ImGui入门]]
- [[#7.1.1 ImGui基础|ImGui基础]]
- [[#7.1.2 常用控件|常用控件]]
- [[#7.1.3 自定义样式|自定义样式]]
- [[#7.2 DirectX基础|7.2 DirectX基础]]
- [[#7.2.1 DirectX 9|DirectX 9]]
- [[#7.2.2 DirectX 11|DirectX 11]]
- [[#7.3 Hook渲染|7.3 Hook渲染]]
- [[#7.3.1 D3D9 Hook|D3D9 Hook]]
- [[#7.3.2 D3D11 Hook|D3D11 Hook]]
- [[#7.4 Overlay技术|7.4 Overlay技术]]
7.1 ImGui入门
7.1.1 ImGui基础
📝 学习笔记
什么是 ImGui
ImGui (Immediate Mode GUI) 是一个轻量级的 C++ GUI 库,特点:
- 无状态,每帧重新构建UI
- 无需复杂的继承体系
- 适合游戏内嵌界面、调试工具
基本框架
#include "imgui.h"
#include "imgui_impl_win32.h"
#include "imgui_impl_dx11.h"
// 初始化
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
// 设置风格
ImGui::StyleColorsDark();
// 初始化后端
ImGui_ImplWin32_Init(hWnd);
ImGui_ImplDX11_Init(device, deviceContext);
// 渲染循环
while (running) {
// 开始新帧
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
// 构建UI
ImGui::Begin("My Window");
ImGui::Text("Hello, ImGui!");
ImGui::End();
// 渲染
ImGui::Render();
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
}
// 清理
ImGui_ImplDX11_Shutdown();
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
7.1.2 常用控件
📝 学习笔记
void RenderUI() {
ImGui::Begin("Control Panel");
// 文本
ImGui::Text("Static text");
ImGui::TextColored(ImVec4(1,1,0,1), "Yellow text");
// 按钮
if (ImGui::Button("Click Me")) {
// 按钮点击处理
}
// 复选框
static bool enabled = false;
ImGui::Checkbox("Enable Feature", &enabled);
// 滑块
static float value = 0.5f;
ImGui::SliderFloat("Value", &value, 0.0f, 1.0f);
static int intValue = 50;
ImGui::SliderInt("Int Value", &intValue, 0, 100);
// 输入框
static char text[256] = "";
ImGui::InputText("Name", text, sizeof(text));
static float floatInput = 0.0f;
ImGui::InputFloat("Float", &floatInput);
// 颜色选择器
static float color[4] = {1,0,0,1};
ImGui::ColorEdit4("Color", color);
// 下拉框
static int current = 0;
const char* items[] = {"Option 1", "Option 2", "Option 3"};
ImGui::Combo("Select", ¤t, items, IM_ARRAYSIZE(items));
// 列表框
ImGui::ListBox("List", ¤t, items, IM_ARRAYSIZE(items));
// 树节点
if (ImGui::TreeNode("Advanced")) {
ImGui::Text("Advanced options here");
ImGui::TreePop();
}
// 标签页
if (ImGui::BeginTabBar("Tabs")) {
if (ImGui::BeginTabItem("Tab 1")) {
ImGui::Text("Tab 1 content");
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Tab 2")) {
ImGui::Text("Tab 2 content");
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
ImGui::End();
}
常用控件速查
| 控件 | 函数 | 说明 |
|---|---|---|
| 文本 | Text(), TextColored() | 静态文本 |
| 按钮 | Button(), SmallButton() | 可点击按钮 |
| 复选框 | Checkbox() | 布尔值切换 |
| 单选按钮 | RadioButton() | 单选选项 |
| 滑块 | SliderFloat/Int() | 数值滑动条 |
| 输入框 | InputText/Float/Int() | 输入字段 |
| 颜色 | ColorEdit3/4() | 颜色选择器 |
| 下拉框 | Combo() | 下拉选择 |
| 列表 | ListBox() | 列表选择 |
| 表格 | BeginTable()/EndTable() | 数据表格 |
7.1.3 自定义样式
📝 学习笔记
void SetCustomStyle() {
ImGuiStyle& style = ImGui::GetStyle();
// 窗口
style.WindowRounding = 5.0f;
style.WindowBorderSize = 1.0f;
style.WindowPadding = ImVec2(10, 10);
// 控件
style.FrameRounding = 3.0f;
style.FramePadding = ImVec2(5, 3);
style.ItemSpacing = ImVec2(8, 4);
// 颜色
ImVec4* colors = style.Colors;
colors[ImGuiCol_WindowBg] = ImVec4(0.1f, 0.1f, 0.1f, 0.9f);
colors[ImGuiCol_TitleBg] = ImVec4(0.2f, 0.2f, 0.2f, 1.0f);
colors[ImGuiCol_TitleBgActive] = ImVec4(0.3f, 0.3f, 0.3f, 1.0f);
colors[ImGuiCol_Button] = ImVec4(0.3f, 0.3f, 0.3f, 1.0f);
colors[ImGuiCol_ButtonHovered] = ImVec4(0.4f, 0.4f, 0.4f, 1.0f);
colors[ImGuiCol_ButtonActive] = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
colors[ImGuiCol_CheckMark] = ImVec4(0.0f, 1.0f, 0.0f, 1.0f);
colors[ImGuiCol_SliderGrab] = ImVec4(0.0f, 0.8f, 0.0f, 1.0f);
}
// 自定义字体
void LoadCustomFont() {
ImGuiIO& io = ImGui::GetIO();
// 加载字体
io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\msyh.ttc", 16.0f,
NULL, io.Fonts->GetGlyphRangesChineseFull());
// 或从内存加载
// io.Fonts->AddFontFromMemoryTTF(fontData, fontSize, 16.0f);
}
7.2 DirectX基础
7.2.1 DirectX 9
📝 学习笔记
D3D9 初始化
#include <d3d9.h>
#pragma comment(lib, "d3d9.lib")
IDirect3D9* d3d = NULL;
IDirect3DDevice9* device = NULL;
BOOL InitD3D9(HWND hWnd) {
// 创建D3D对象
d3d = Direct3DCreate9(D3D_SDK_VERSION);
if (!d3d) return FALSE;
// 设置呈现参数
D3DPRESENT_PARAMETERS d3dpp = {};
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; // VSync
// 创建设备
HRESULT hr = d3d->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_HARDWARE_VERTEXPROCESSING,
&d3dpp,
&device
);
return SUCCEEDED(hr);
}
void RenderFrame() {
device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(0, 40, 100), 1.0f, 0);
if (SUCCEEDED(device->BeginScene())) {
// 渲染内容...
device->EndScene();
}
device->Present(NULL, NULL, NULL, NULL);
}
void CleanupD3D9() {
if (device) device->Release();
if (d3d) d3d->Release();
}
D3D9 绘图函数
// 绘制线段
void DrawLine(float x1, float y1, float x2, float y2, D3DCOLOR color) {
struct Vertex {
float x, y, z, rhw;
D3DCOLOR color;
};
Vertex vertices[] = {
{x1, y1, 0, 1, color},
{x2, y2, 0, 1, color}
};
device->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE);
device->DrawPrimitiveUP(D3DPT_LINELIST, 1, vertices, sizeof(Vertex));
}
// 绘制矩形
void DrawRect(float x, float y, float w, float h, D3DCOLOR color) {
DrawLine(x, y, x + w, y, color);
DrawLine(x + w, y, x + w, y + h, color);
DrawLine(x + w, y + h, x, y + h, color);
DrawLine(x, y + h, x, y, color);
}
// 绘制填充矩形
void DrawFilledRect(float x, float y, float w, float h, D3DCOLOR color) {
struct Vertex {
float x, y, z, rhw;
D3DCOLOR color;
};
Vertex vertices[] = {
{x, y, 0, 1, color},
{x + w, y, 0, 1, color},
{x, y + h, 0, 1, color},
{x + w, y + h, 0, 1, color}
};
device->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE);
device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, vertices, sizeof(Vertex));
}
7.2.2 DirectX 11
📝 学习笔记
D3D11 初始化
#include <d3d11.h>
#pragma comment(lib, "d3d11.lib")
ID3D11Device* device = NULL;
ID3D11DeviceContext* context = NULL;
IDXGISwapChain* swapChain = NULL;
ID3D11RenderTargetView* renderTargetView = NULL;
BOOL InitD3D11(HWND hWnd) {
// 交换链描述
DXGI_SWAP_CHAIN_DESC scd = {};
scd.BufferCount = 1;
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
scd.OutputWindow = hWnd;
scd.SampleDesc.Count = 1;
scd.Windowed = TRUE;
scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
// 创建设备和交换链
D3D_FEATURE_LEVEL featureLevel;
HRESULT hr = D3D11CreateDeviceAndSwapChain(
NULL, D3D_DRIVER_TYPE_HARDWARE, NULL,
0, NULL, 0,
D3D11_SDK_VERSION,
&scd,
&swapChain,
&device,
&featureLevel,
&context
);
if (FAILED(hr)) return FALSE;
// 创建渲染目标视图
ID3D11Texture2D* backBuffer;
swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
device->CreateRenderTargetView(backBuffer, NULL, &renderTargetView);
backBuffer->Release();
context->OMSetRenderTargets(1, &renderTargetView, NULL);
// 设置视口
D3D11_VIEWPORT vp = {};
vp.Width = 800;
vp.Height = 600;
vp.MaxDepth = 1.0f;
context->RSSetViewports(1, &vp);
return TRUE;
}
void RenderFrame() {
float clearColor[4] = {0.0f, 0.2f, 0.4f, 1.0f};
context->ClearRenderTargetView(renderTargetView, clearColor);
// 渲染内容...
swapChain->Present(1, 0); // VSync
}
7.3 Hook渲染
7.3.1 D3D9 Hook
📝 学习笔记
获取 D3D9 设备虚表
// 方法1: 创建临时设备获取虚表
PVOID* GetD3D9VTable() {
// 创建临时窗口
HWND hWnd = CreateWindowEx(0, L"STATIC", L"", WS_POPUP, 0, 0, 1, 1,
NULL, NULL, NULL, NULL);
IDirect3D9* d3d = Direct3DCreate9(D3D_SDK_VERSION);
if (!d3d) return NULL;
D3DPRESENT_PARAMETERS d3dpp = {};
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hWnd;
IDirect3DDevice9* device;
d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &device);
// 获取虚表
PVOID* vtable = *(PVOID**)device;
// 复制需要的函数地址
PVOID endScene = vtable[42]; // EndScene
PVOID reset = vtable[16]; // Reset
PVOID present = vtable[17]; // Present
device->Release();
d3d->Release();
DestroyWindow(hWnd);
return vtable;
}
Hook EndScene
typedef HRESULT(WINAPI* EndScene_t)(IDirect3DDevice9*);
EndScene_t oEndScene = NULL;
HRESULT WINAPI hkEndScene(IDirect3DDevice9* device) {
// 在这里绘制自定义内容
static bool init = false;
if (!init) {
// 初始化ImGui
ImGui_ImplDX9_Init(device);
init = true;
}
// ImGui渲染
ImGui_ImplDX9_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
ImGui::Begin("D3D9 Overlay");
ImGui::Text("Hello from hooked EndScene!");
ImGui::End();
ImGui::Render();
ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
return oEndScene(device);
}
// 安装Hook
void InstallD3D9Hook() {
PVOID* vtable = GetD3D9VTable();
// 使用MinHook或直接修改虚表
MH_CreateHook(vtable[42], hkEndScene, (PVOID*)&oEndScene);
MH_EnableHook(vtable[42]);
}
D3D9 关键虚表索引
| 索引 | 函数 | 用途 |
|---|---|---|
| 16 | Reset | 设备重置时需要重建资源 |
| 17 | Present | 每帧呈现 |
| 42 | EndScene | 渲染结束,常用Hook点 |
7.3.2 D3D11 Hook
📝 学习笔记
Hook Present (DXGI)
typedef HRESULT(WINAPI* Present_t)(IDXGISwapChain*, UINT, UINT);
Present_t oPresent = NULL;
ID3D11Device* g_device = NULL;
ID3D11DeviceContext* g_context = NULL;
ID3D11RenderTargetView* g_rtv = NULL;
HRESULT WINAPI hkPresent(IDXGISwapChain* swapChain, UINT syncInterval, UINT flags) {
static bool init = false;
if (!init) {
// 获取设备和上下文
swapChain->GetDevice(__uuidof(ID3D11Device), (void**)&g_device);
g_device->GetImmediateContext(&g_context);
// 创建渲染目标视图
ID3D11Texture2D* backBuffer;
swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
g_device->CreateRenderTargetView(backBuffer, NULL, &g_rtv);
backBuffer->Release();
// 初始化ImGui
ImGui_ImplDX11_Init(g_device, g_context);
init = true;
}
// 设置渲染目标
g_context->OMSetRenderTargets(1, &g_rtv, NULL);
// ImGui渲染
ImGui_ImplDX11_NewFrame();
ImGui_ImplWin32_NewFrame();
ImGui::NewFrame();
ImGui::Begin("D3D11 Overlay");
ImGui::Text("Hello from hooked Present!");
ImGui::End();
ImGui::Render();
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
return oPresent(swapChain, syncInterval, flags);
}
// 获取SwapChain虚表
PVOID* GetDXGIVTable() {
// 创建临时设备获取虚表
HWND hWnd = CreateWindowEx(0, L"STATIC", L"", WS_POPUP, 0, 0, 1, 1,
NULL, NULL, NULL, NULL);
DXGI_SWAP_CHAIN_DESC scd = {};
scd.BufferCount = 1;
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
scd.OutputWindow = hWnd;
scd.SampleDesc.Count = 1;
scd.Windowed = TRUE;
IDXGISwapChain* swapChain;
ID3D11Device* device;
ID3D11DeviceContext* context;
D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0,
NULL, 0, D3D11_SDK_VERSION, &scd,
&swapChain, &device, NULL, &context);
PVOID* vtable = *(PVOID**)swapChain;
swapChain->Release();
device->Release();
context->Release();
DestroyWindow(hWnd);
return vtable;
}
DXGI SwapChain 虚表索引
| 索引 | 函数 | 用途 |
|---|---|---|
| 8 | Present | 每帧呈现 |
| 13 | ResizeBuffers | 窗口大小改变 |
7.4 Overlay技术
📝 学习笔记
外部 Overlay 窗口
// 创建透明窗口
HWND CreateOverlayWindow() {
WNDCLASSEX wc = {};
wc.cbSize = sizeof(wc);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = OverlayWndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = L"OverlayClass";
RegisterClassEx(&wc);
// 获取目标窗口位置
HWND targetWnd = FindWindow(NULL, L"Game Window");
RECT rect;
GetWindowRect(targetWnd, &rect);
// 创建分层窗口
HWND overlay = CreateWindowEx(
WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOOLWINDOW,
L"OverlayClass",
L"Overlay",
WS_POPUP,
rect.left, rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
NULL, NULL, GetModuleHandle(NULL), NULL
);
// 设置透明色
SetLayeredWindowAttributes(overlay, RGB(0, 0, 0), 0, LWA_COLORKEY);
ShowWindow(overlay, SW_SHOW);
return overlay;
}
// 跟随目标窗口
void UpdateOverlayPosition(HWND overlay, HWND target) {
RECT rect;
if (GetWindowRect(target, &rect)) {
SetWindowPos(overlay, HWND_TOPMOST,
rect.left, rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
SWP_NOACTIVATE);
}
}
GDI 绘图
// 在Overlay上绘制
void DrawOnOverlay(HDC hdc, int width, int height) {
// 清除背景(透明色)
HBRUSH brush = CreateSolidBrush(RGB(0, 0, 0));
RECT rect = {0, 0, width, height};
FillRect(hdc, &rect, brush);
DeleteObject(brush);
// 绘制矩形框
HPEN pen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
SelectObject(hdc, pen);
SelectObject(hdc, GetStockObject(NULL_BRUSH));
Rectangle(hdc, 100, 100, 200, 200);
DeleteObject(pen);
// 绘制文字
SetTextColor(hdc, RGB(0, 255, 0));
SetBkMode(hdc, TRANSPARENT);
TextOut(hdc, 100, 80, L"ESP Box", 7);
}
[!tip] Overlay vs 内部 Hook
方式 优点 缺点 外部Overlay 不修改目标进程 可能被遮挡,需要同步位置 内部Hook 与游戏渲染同步 需要注入,可能被检测
✅ 阶段检查点
ImGui检查
- [ ] 搭建ImGui开发环境
- [ ] 掌握常用控件使用
- [ ] 能自定义样式和字体
DirectX检查
- [ ] 理解D3D9/D3D11初始化流程
- [ ] 能进行基本的图形绘制
- [ ] 了解渲染管线基础
Hook渲染检查
- [ ] 实现D3D9 EndScene Hook
- [ ] 实现D3D11 Present Hook
- [ ] 在Hook中集成ImGui
Overlay检查
- [ ] 创建透明Overlay窗口
- [ ] 实现窗口位置跟随
- [ ] 使用GDI或D3D在Overlay上绘制
📖 学习资料
| 资源类型 | 名称 | 说明 |
|---|---|---|
| 📚 文档 | ImGui Wiki | 官方文档 |
| 📚 文档 | DirectX文档 | 微软官方 |
| 🔧 示例 | ImGui Demo | 完整示例 |
| 📹 视频 | YouTube D3D教程 | DirectX入门 |
🔗 导航
← [[阶段六:驱动开发]] | [[逆向与驱动开发学习路径(详细版)| 返回主目录]] | [[阶段八:脚本框架]] →