【C++】从零开始的CSGO逆向分析3

您所在的位置:网站首页 人物的透视 【C++】从零开始的CSGO逆向分析3

【C++】从零开始的CSGO逆向分析3

2024-04-19 09:31| 来源: 网络整理| 查看: 265

【C++】从零开始的CS:GO逆向分析3——写出一个透视

 

本篇内容包括:

  1. 透视实现的方法介绍

  2. 通过进程名获取进程id和进程句柄

  3. 通过进程id获取进程中的模块信息(模块大小,模块地址,模块句柄)

  4. 读取游戏内存(人物ViewMatrix,敌人坐标,敌人生命值,敌人阵营)

  5. 三维坐标转二维坐标(游戏内人物坐标转换成屏幕上的坐标)

  6. glfw+imgui 在屏幕上的绘制直线

请先依据前两篇,对偏移、基址有基本了解,并且配置好了glfw+imgui的环境,在上篇创建好的工程中创建CPP文件和同名.h文件

 实现效果:

 

透视实现的方法介绍

  一般有两种方式,一种是外挂,一种是内挂,外挂是在创建一个透明窗口,在透明窗口上画线,让鼠标事件透过窗口,透明窗口覆盖在游戏窗口上。内挂是通过DLL注入,HOOK游戏中的绘制函数,在游戏绘制人物的时候绘制自己的线。还剩一种比较少用,但也可以实现,找到人物模型ID,在渲染到人物模型的时候关掉渲染缓冲(应该是叫这个?),使人物模型在墙模型前面渲染,导致可以直接看到人物。本篇文章采用的是外挂的形式,根据上篇文章已经可以创建出一个覆盖在屏幕上的透明窗口。

先把需要用到的全局变量声明一下(GetImformation.cpp)

变量名起的挺明白的,就不写注释了

DWORD g_process_id = NULL; HANDLE g_process_handle = NULL; UINT_PTR g_local_player = NULL; UINT_PTR g_player_list_address = NULL; UINT_PTR g_matrix_address = NULL; UINT_PTR g_angle_address = NULL; HWND g_game_hwnd = NULL; module_information engine_module; module_information client_module; module_information server_module; float g_client_width; float g_client_height; 把需要用到的偏移也声明一下 #define dwViewMatrix 0x4DCF254 #define dwLocalPlayer 0xDC14CC #define dwClientState 0x58CFDC #define dwEntityList 0x4DDD93C #define dwClientState_ViewAngles 0x4D90 #define m_vecOrigin 0x138 #define m_bDormant 0xED #define m_lifeState 0x25F #define m_iHealth 0x100 #define m_iTeamNum 0xF4 再把需要使用到的函数先声明和实现(GetImformation.cpp),实现思路写在后面 获取屏幕大小,保存到全局变量 void GetWindowSize() { HDC hdc = GetDC(nullptr); g_client_width = GetDeviceCaps(hdc, DESKTOPHORZRES); g_client_height = GetDeviceCaps(hdc, DESKTOPVERTRES); ReleaseDC(nullptr, hdc); }

 

 

先写一个错误获取函数,以方便获取出错的信息 void error(const char*text) { MessageBoxA(nullptr, text, nullptr, MB_OK); exit(-1); } bool is_error() { return GetLastError() != 0; }     通过进程名获取进程id和进程句柄

使用CreateToolhelp32Snapshot函数,创建进程快照,遍历系统快照中的进程名,遍历到process_name,则返回该进程的进程ID

DWORD get_process_id(const char*process_name) { HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (is_error()) error("CreateToolhelp32Snapshot失败"); PROCESSENTRY32 process_info; ZeroMemory(&process_info, sizeof(process_info)); process_info.dwSize = sizeof(process_info); char target[1024]; ZeroMemory(target, 1024); strncpy_s(target, process_name, strlen(process_name)); _strupr(target); bool state = Process32First(snap, &process_info); while (state) { if (strncmp(_strupr(process_info.szExeFile), target, strlen(target)) == 0) { return process_info.th32ProcessID; } state = Process32Next(snap, &process_info); } CloseHandle(snap); return 0; }

通过进程ID获取进程句柄

HANDLE get_process_handle(DWORD process_id) { HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_id); if (is_error()) error("get_process_handle失败"); return process_handle; }

 

 

通过进程id获取进程中的模块信息(模块大小,模块地址,模块句柄)

可以发现偏移都是由 client.dll+xxxxx 此种形式构成,所以需要获取模块的地址

先创建一个模块结构体,需要获取模块的模块大小,模块地址,模块句柄

class module_information { public: HANDLE module_handle; char module_name[1024]; char *module_data; UINT_PTR module_address; int module_size; void alloc(int size) { module_size = size; module_data = (char *)VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (is_error())error("申请内存失败"); } void release() { if (module_data)VirtualFree(module_data, 0, MEM_RELEASE); module_data = nullptr; } };

传入进程ID和需要获取的模块名,CreateToolhelp32Snapshot创建模块快照,遍历快照,比对模块名,获取模块信息

void get_moduel_info(DWORD process_id, const char *name, OUT module_information&info) { HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, process_id); if (is_error())error("创建快照错误"); MODULEENTRY32 module_info; ZeroMemory(&module_info, sizeof(module_info)); module_info.dwSize = sizeof(module_info); char target[1024]; ZeroMemory(target, 1024); strncpy(target, name, strlen(name)); _strupr(target); bool status = Module32First(snap, &module_info); while (status) { if (strncmp(_strupr(module_info.szModule), target, sizeof(target)) == 0) { info.module_address = (UINT_PTR)module_info.modBaseAddr; info.module_handle = module_info.hModule; info.alloc(module_info.modBaseSize); DWORD size = read_memory(g_process_handle, info.module_address, info.module_data, info.module_size);//TODO CloseHandle(snap); return; } status = Module32Next(snap, &module_info); } error("未找到模块"); return; }     读取游戏内存函数

例如之前得到 上下角度 = [[engine.dll+58CFDC]+00004D90] ,则可以 

ReadProcessMemory(g_process_handle, (LPVOID)(engine.dll+58CFDC), recv, size, &readsize);

ReadProcessMemory(g_process_handle, (LPVOID)recv, recv, size, &readsize);

函数的使用方法:ReadProcessMemory(句柄,地址,读到哪里,读多少,具体读了多少);

则可以读到上下角度

通过ReadProcessMemory函数读取内存,对这个函数进行打包,方便使用(好吧,我承认这个打包的很烂,几乎没有方便使用)

DWORD read_memory(HANDLE process, DWORD address, void *recv, int size) { DWORD readsize; ReadProcessMemory(process, (LPVOID)address, recv, size, &readsize); return readsize; if (is_error())error("读取内存失败"); }

重写了一个我觉得比较好用的,各位可以酌情对其进行改写

template T ReadMem(HANDLE ProcessHandle, UINT_PTR Address, int size) { T Reader; ReadProcessMemory(ProcessHandle, (LPVOID)Address, &Reader, size, NULL); return Reader; }   三维坐标转二维坐标

创建两个结构体来储存二维坐标,一个用来储存三维坐标

struct Vec2 {public: float x, y; }; struct Vec3 { public: float x, y, z; };

传入一个三维坐标和视角矩阵,算出人物在屏幕上的坐标 VecScreen

bool WorldToScreen(const Vec3& VecOrgin, Vec2& VecScreen, float* Matrix) { VecScreen.x = VecOrgin.x *Matrix[0] + VecOrgin.y*Matrix[1] + VecOrgin.z*Matrix[2] + Matrix[3]; VecScreen.y = VecOrgin.x *Matrix[4] + VecOrgin.y*Matrix[5] + VecOrgin.z*Matrix[6] + Matrix[7]; float w = VecOrgin.x*Matrix[12] + VecOrgin.y*Matrix[13] + VecOrgin.z*Matrix[14] + Matrix[15]; if (w < 0.01f) { return false; } Vec2 NDC; NDC.x = VecScreen.x / w; NDC.y = VecScreen.y / w; VecScreen.x = (g_client_width / 2 * NDC.x) + (NDC.x + g_client_width / 2); VecScreen.y = (g_client_height / 2 * NDC.y) + (NDC.y + g_client_height / 2); ConvertToRange(VecScreen); return true; } void ConvertToRange(Vec2 &Point) { Point.x /= g_client_width; Point.x *= 2.0f; Point.x -= 1.0f; Point.y /= g_client_height; Point.y *= 2.0f; Point.y -= 1.0f; }  GLFW画线

使用glVertex2f函数,第一个glVertex2f是开始的位置,第二个glVertex2f是结束的位置

void DrawLine(Vec2& start, Vec2& end) { glLineWidth(1.2); glBegin(GL_LINES); glColor4f(255, 255, 255, 100); glVertex2f(start.x, start.y); glVertex2f(end.x, end.y); glEnd(); }

 

写一个init函数,实现初始化 void init_address(const char*process_name) { std::cout height; int Width = glfwGetVideoMode(monitor)->width; glfwWindowHint(GLFW_FLOATING, true); glfwWindowHint(GLFW_RESIZABLE, false); glfwWindowHint(GLFW_MAXIMIZED, true); glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, true); //###########################设置窗口########################### GLFWwindow* window = glfwCreateWindow(Width, Height, "titile", nullptr, nullptr); if (window == nullptr) return 1; glfwSetWindowAttrib(window, GLFW_DECORATED, false); //设置没有标题栏 ShowWindow(GetConsoleWindow(), SW_HIDE); glfwMakeContextCurrent(window); glfwSwapInterval(1); IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui::StyleColorsDark(); ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init(glsl_version); bool bMenuVisible = true; bool Dormant; int EntityTeamNum; int lifestate; int blood; int iTeamNum; float temp_pos[3]; float Matrix[16]; Vec2 LineOrigin; Vec2 ScreenCoord; Vec3 EntityLocation; LineOrigin.x = 0.0f; LineOrigin.y = -1.0f; UINT_PTR Entity; while (!glfwWindowShouldClose(window)) { glfwPollEvents(); glClear(GL_COLOR_BUFFER_BIT); ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); if (GetAsyncKeyState(VK_F11) & 1) { bMenuVisible = !bMenuVisible; if (bMenuVisible) ShowMenu(window); else HideMenu(window); } //界面设计 if (bMenuVisible) { ImGui::Text("USE F11 TO Hiden/Show"); ImGui::Text(""); if (ImGui::Button("exit")) return 0; } ReadProcessMemory(g_process_handle, (LPVOID)(client_module.module_address + dwLocalPlayer), &g_local_player, 4, nullptr); if(g_local_player!=0) { ScreenCoord.x = 0.0f; ScreenCoord.y = -1.0f; g_angle_address = ReadMem(g_process_handle, (engine_module.module_address + dwClientState), 4)+ dwClientState_ViewAngles; ReadProcessMemory(g_process_handle, (LPCVOID)(client_module.module_address + dwViewMatrix), Matrix, sizeof(float) * 16, nullptr); for (short int i = 0; i < 64; ++i) { ReadProcessMemory(g_process_handle, (LPVOID)(client_module.module_address + dwLocalPlayer), &g_local_player, 4, nullptr); ReadProcessMemory(g_process_handle, (LPCVOID)(g_local_player + m_iTeamNum), &iTeamNum, 4, nullptr); //获取敌人实体 ReadProcessMemory(g_process_handle, (LPCVOID)(client_module.module_address + dwEntityList + i * 0x10), &Entity, sizeof(float), nullptr); ReadProcessMemory(g_process_handle, (LPVOID)(Entity + m_bDormant), &Dormant, sizeof(bool), nullptr); ReadProcessMemory(g_process_handle, (LPVOID)(Entity + m_lifeState), &lifestate, 4, nullptr); ReadProcessMemory(g_process_handle, (LPCVOID)(Entity + m_iTeamNum), &EntityTeamNum, 4, nullptr); ReadProcessMemory(g_process_handle, (LPCVOID)(Entity + m_iHealth), &blood, 4, nullptr); if ((Entity == NULL) || (Entity == g_local_player) || (EntityTeamNum == iTeamNum) || (blood


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3