方框透视原理

原理非常简单,找到人物世界坐标地址(包括自身) -> 世界坐标转屏幕坐标 -> GDI绘制。
原理简单,但实践起来却没有想象中那么简单。这第一步都需要花费不少时间。

如果以下代码编译运行后不显示方框,应该是游戏更新导致一下三个地址变化,自己用ce慢慢找吧

dwLocalPlayer, dwEntityList, dwViewMatrix

代码


#include <Windows.h>
#include <sstream>
#include <math.h>
#include <iostream>
#include <TlHelp32.h>
#include <tchar.h>

#include <string>
#include "csgo.h"

using namespace std;


DWORD dwLocalPlayer = 0xD8B2BC; //玩家本人基址
DWORD dwEntityList = 0x4DA2F44; //实体列表
DWORD dwViewMatrix = 0x4D94844; //视图矩阵
DWORD m_dwBoneMatrix = 0x26A8; //敌人视图矩阵


DWORD zPosOffset = 0x140; // z 坐标偏移
DWORD yPosOffset = 0x13C; // y 坐标偏移
DWORD xPosOffset = 0x138; // x 坐标偏移
DWORD hPosOffset = 0x110; // 高度偏移
DWORD hpOffset = 0x100; // 血量偏移地址
DWORD m_iTeamNum = 0xF4; //阵营标志
DWORD m_bDormant = 0xED; //实体是否移除偏移
DWORD m_vecOrigin = 0x138; //实体列表 x,y,z偏移地址



HWND hwndCSGO;
//client.dll
DWORD gameModule;
//需要设置的初始化变量
HDC HDC_Desktop;

//windef库定义的笔刷对象,用于绘图
HBRUSH EnemyBrush;
//windef库定义的文字对象,用于绘画文字
HFONT Font;
//目标窗口的矩形对象
RECT m_Rect;
//目标窗口
HWND TargetWnd;
//目标窗口代理
HWND Handle;

//目标窗口矩形大小
RECT windowRect;

HANDLE handle;
DWORD pid;


//线的颜色
COLORREF SnapLineCOLOR;
//文本的颜色
COLORREF TextCOLOR;

float Matrix[16];//视图矩阵


//defining our vectors
struct Vec3
{
float x, y, z;
};

struct Vec4
{
float x, y, z, w;
};

struct Vec2
{
float x, y;
};



//设置绘画
void SetupDrawing(HDC hDesktop, HWND handle)
{
//目标桌面
HDC_Desktop = hDesktop;
//代理
Handle = handle;
//创建一个正方体笔刷,颜色为红色
EnemyBrush = CreateSolidBrush(RGB(255, 0, 0));
//设置线的颜色为蓝色
SnapLineCOLOR = RGB(0, 0, 255);
//设置文本的颜色为绿色
TextCOLOR = RGB(0, 255, 0);
}

//填充矩形
void DrawFilledRect(int x, int y, int w, int h)
{
RECT rect = { x, y, x + w, y + h };
FillRect(HDC_Desktop, &rect, EnemyBrush);
}

//绘画目标的边框
void DrawBorderBox(int x, int y, int w, int h, int thickness)
{

DrawFilledRect(x, y, w, thickness);

DrawFilledRect(x, y, thickness, h);

DrawFilledRect((x + w), y, thickness, h);

DrawFilledRect(x, y + h, w + thickness, thickness);
}

//绘画直线
void DrawLine(float StartX, float StartY, float EndX, float EndY, COLORREF Pen)
{
//设置绘线的样式
HPEN hNPen = CreatePen(PS_SOLID, 2, Pen);
//配置到画笔对象
HPEN hOPen = (HPEN)SelectObject(HDC_Desktop, hNPen);
//设置起始点
MoveToEx(HDC_Desktop, StartX, StartY, NULL); //start
//设置画笔终点
LineTo(HDC_Desktop, EndX, EndY); //end
//删除对象
DeleteObject(SelectObject(HDC_Desktop, hOPen));
}

//绘画距离字符串
void DrawString(int x, int y, COLORREF color, const char* text)
{
//设置文本对齐方式
SetTextAlign(HDC_Desktop, TA_CENTER | TA_NOUPDATECP);
//设置背景颜色
SetBkColor(HDC_Desktop, RGB(0, 0, 0));
//设置背景模式,透明
SetBkMode(HDC_Desktop, TRANSPARENT);
//设置文字颜色
SetTextColor(HDC_Desktop, color);

//选择对象
SelectObject(HDC_Desktop, Font);
//输出文本
TextOutA(HDC_Desktop, x, y, text, strlen(text));
//删除对象
DeleteObject(Font);

}





//世界坐标 到 屏幕坐标
bool WorldToScreen(Vec3 pos, Vec2& screen, float matrix[16], int windowWidth, int windowHeight)
{
Vec4 clipCoords;
clipCoords.x = pos.x * matrix[0] + pos.y * matrix[1] + pos.z * matrix[2] + matrix[3];
clipCoords.y = pos.x * matrix[4] + pos.y * matrix[5] + pos.z * matrix[6] + matrix[7];
clipCoords.z = pos.x * matrix[8] + pos.y * matrix[9] + pos.z * matrix[10] + matrix[11];
clipCoords.w = pos.x * matrix[12] + pos.y * matrix[13] + pos.z * matrix[14] + matrix[15];

if (clipCoords.w < 0.1f)
return false;

Vec3 NDC;
NDC.x = clipCoords.x / clipCoords.w;
NDC.y = clipCoords.y / clipCoords.w;
NDC.z = clipCoords.z / clipCoords.w;

screen.x = (windowWidth / 2 * NDC.x) + (NDC.x + windowWidth / 2);
screen.y = -(windowHeight / 2 * NDC.y) + (NDC.y + windowHeight / 2);
return true;
}


DWORD GetProcId(const char* procName)
{
DWORD procId = 0;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32 procEntry;
procEntry.dwSize = sizeof(procEntry);

if (Process32First(hSnap, &procEntry))
{
do
{
//if (!_wcsicmp((const wchar_t*)procEntry.szExeFile, procName))
if (strcmp(procName, procEntry.szExeFile) == 0)
{
procId = procEntry.th32ProcessID;
break;
}
} while (Process32Next(hSnap, &procEntry));

}
}
CloseHandle(hSnap);
return procId;
}

uintptr_t GetModBase(DWORD ProcID, const char* ModuleName)
{
uintptr_t modbaseAddr = 0;
HANDLE hsnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, ProcID);
if (hsnap != INVALID_HANDLE_VALUE)
{
MODULEENTRY32 modEntry;
modEntry.dwSize = sizeof(modEntry);
if (Module32First(hsnap, &modEntry))
{
do
{
//if (!_wcsicmp((const wchar_t*)modEntry.szModule, ModuleName))
if (strcmp(ModuleName, modEntry.szModule) == 0)
{
modbaseAddr = (uintptr_t)modEntry.modBaseAddr;
break;
}
} while (Module32Next(hsnap, &modEntry));
}
}
CloseHandle(hsnap);
return modbaseAddr;
}

void MemStart()
{
//为目标窗体赋值
TargetWnd = FindWindow(0, "Counter-Strike: Global Offensive");
//获得目标桌面
HDC HDC_Desktop = GetDC(TargetWnd);
SetupDrawing(HDC_Desktop, TargetWnd);
GetWindowThreadProcessId(TargetWnd, &pid);
handle = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
GetClientRect(TargetWnd, &windowRect);

windowRect.bottom *= 1.25;
windowRect.right *= 1.25;


}

int main()
{
MemStart();
DWORD processID = GetProcId("csgo.exe");
HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
uintptr_t dllBaseAddress = 0;
dllBaseAddress = GetModBase(processID, "client.dll");
if (processHandle != 0)
{
cout << "ProcessID: " << "0x" << hex << processID << endl;;
cout << "Process access granted\n";
cout << "dllBaseAddress: " << "0x" << hex << dllBaseAddress <<endl;
}
else
{
cout << "Process access not granted " << endl;
Sleep(5000);
exit(1);
}
//cout << pid << endl;
//HMODULE gg = GetProcessModuleHandle(pid, "client.dll");
gameModule = dllBaseAddress;
cout << "\n" << endl;
cout << gameModule + dwLocalPlayer + zPosOffset << endl;
cout << windowRect.left << endl;

//LPVOID bb = LPVOID(DWORD(gameModule) + DWORD(dwLocalPlayer) + DWORD(zPosOffset));
//system("tasklist -m >d:12343.txt");
//调用绘线函数
//绘画直线
DWORD ClocalPlayer;
int m_fFlags = 0;


float x, y, z, hp;


char buf[100];
while (1) {
ReadProcessMemory(processHandle, (PBYTE*)(dllBaseAddress + dwLocalPlayer), &ClocalPlayer, sizeof(DWORD), NULL);
//Base
Vec2 vScreen = { 0,0 };
Vec2 vScreen_head = { 0,0 };
//Head
Vec2 vHead = { 0,0 };

Vec3 vecMine;
//DrawLine(0, 0, 1920.0, 1080.0, SnapLineCOLOR);
ReadProcessMemory(processHandle, (BYTE*)(ClocalPlayer + xPosOffset), &vecMine.x, sizeof(float), NULL);
ReadProcessMemory(processHandle, (BYTE*)(ClocalPlayer + yPosOffset), &vecMine.y, sizeof(float), NULL);
ReadProcessMemory(processHandle, (BYTE*)(ClocalPlayer + zPosOffset), &vecMine.z, sizeof(float), NULL);

ReadProcessMemory(processHandle, (PBYTE*)(dllBaseAddress + dwViewMatrix), Matrix, sizeof(Matrix), NULL);

DrawString(100, 100, TextCOLOR, ("x: " + to_string(int(vecMine.x))).data());
DrawString(100, 120, TextCOLOR, ("y: " + to_string(int(vecMine.y))).data());
DrawString(100, 140, TextCOLOR, ("z: " + to_string(int(vecMine.z))).data());
//窗口大小
//DrawString(100, 180, TextCOLOR, ("l: " + to_string(windowRect.left)).data());
DrawString(100, 200, TextCOLOR, ("r: " + to_string(windowRect.right)).data());
//DrawString(100, 220, TextCOLOR, ("t: " + to_string(windowRect.top)).data());
DrawString(100, 240, TextCOLOR, ("b: " + to_string(windowRect.bottom)).data());

//自己 阵营
int myTeam = 0;
ReadProcessMemory(processHandle, (BYTE*)(ClocalPlayer + m_iTeamNum), &myTeam, sizeof(int), NULL);
/*
for (int i = 0; i < 16;i++) {
DrawString(200, 200 + i * 20, TextCOLOR, ("Matrix: " + to_string(Matrix[i])).data());
}
*/
DrawString(200, 200 + 16 * 20, TextCOLOR, ("阵营: " + to_string(int(myTeam))).data());


//loop
for (short int i = 0; i < 64; i++) {
//DWORD entity = *(DWORD*)(gameModule + dwEntityList + i * 0x10);
DWORD entity = NULL;
ReadProcessMemory(processHandle, (DWORD*)(gameModule + dwEntityList + i * 0x10), &entity, sizeof(DWORD), NULL);

if (entity != NULL) {
if (entity != ClocalPlayer) {
int entityTeam = 0; //阵营
ReadProcessMemory(processHandle, (int*)(entity + m_iTeamNum), &entityTeam, sizeof(int), NULL);
Vec3 entityLocation = { 0,0,0, }; //坐标
ReadProcessMemory(processHandle, (Vec3*)(entity + m_vecOrigin), &entityLocation, sizeof(Vec3), NULL);
DWORD dwBoneMatrix = 0;
ReadProcessMemory(processHandle, (DWORD*)(entity + m_dwBoneMatrix), &dwBoneMatrix, sizeof(DWORD), NULL);

DWORD health = 0;//血量
ReadProcessMemory(processHandle, (DWORD*)(entity + hpOffset), &health, sizeof(DWORD), NULL);
int isDormat = 1;//是否加载了
ReadProcessMemory(processHandle, (int*)(entity + m_bDormant), &isDormat, sizeof(int), NULL);
float head = 0;//高度
ReadProcessMemory(processHandle, (float*)(entity + hPosOffset), &head, sizeof(float), NULL);
Vec3 head_dwBoneMatrix = { entityLocation.x, entityLocation.y, entityLocation.z+head };
if (isDormat == 0) {//检查实体是否被剔除
if (health > 0) {
if (WorldToScreen(entityLocation, vScreen, Matrix, windowRect.right, windowRect.bottom)) {
//得到了头部的x,y,z
float enemyHeadX = 0;
ReadProcessMemory(processHandle, (float*)(dwBoneMatrix + 0x30 * 9 + 0x0C), &isDormat, sizeof(float), NULL);
float enemyHeadY = 0;
ReadProcessMemory(processHandle, (float*)(dwBoneMatrix + 0x30 * 9 + 0x1C), &isDormat, sizeof(float), NULL);
float enemyHeadZ = 0;
ReadProcessMemory(processHandle, (float*)(dwBoneMatrix + 0x30 * 9 + 0x2C), &isDormat, sizeof(float), NULL);
//实体的x,y,z转成 Vec3
Vec3 enemyHeadPos = { enemyHeadX, enemyHeadY, enemyHeadZ };


if (WorldToScreen(head_dwBoneMatrix, vHead, Matrix, windowRect.right, windowRect.bottom)) {
float head = vHead.y - vScreen.y;
float width = head / 1.8;
float center = width / 2;
if (myTeam == entityTeam)
{
EnemyBrush = CreateSolidBrush(RGB(000, 000, 255));
//身体
DrawBorderBox(vScreen.x - center, vScreen.y, width, head, 2);
//头部
DrawBorderBox(vScreen.x - center, vScreen.y + head, width, head * 0.125, 2);
DeleteObject(EnemyBrush);
}
else
{
EnemyBrush = CreateSolidBrush(RGB(0, 255, 67));
//身体
DrawBorderBox(vScreen.x - center, vScreen.y , width, head, 2);
DeleteObject(EnemyBrush);
//头部
EnemyBrush = CreateSolidBrush(RGB(255, 000, 000));
DrawBorderBox(vScreen.x - head * 0.125, vScreen.y+head, head * 0.125*2, head*0.125, 1);
DrawLine(vHead.x, vHead.y, windowRect.right / 2, 0, RGB(255, 000, 000));
// hp
if (width) {
DrawString(vScreen.x, vScreen.y + head / 2, RGB(245, 166, 35), (to_string(health)).data());
}
/*
DrawString(vScreen.x, vScreen.y, RGB(0, 255, 68), ("*"));
DrawString(vHead.x, vHead.y, RGB(0, 255, 68), ("#"));
*/
DeleteObject(EnemyBrush);
}


}
}
}
}

}

}
}

}
return 0;
}

效果


这是最简单的一个方框透视,虽然代码不是最简洁。