【C++】從零開始的CS:GO逆向分析3——寫出一個透視

2022-10-13 21:04:28

【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<class T>
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 << "請先啟動遊戲"<< std::endl;

    DWORD process_id = get_process_id(process_name);
    HANDLE process_handle = get_process_handle(process_id);
    g_process_id = process_id; //將pid儲存到全域性變數
    g_process_handle = process_handle;//將process_handle儲存到全域性變數
    //獲取模組資訊
    get_moduel_info(process_id, "engine.dll", engine_module);
    get_moduel_info(process_id, "client.dll", client_module);
    get_moduel_info(process_id, "server.dll", server_module);

    UINT_PTR temp_address;
    float Matrix[16];
    UINT_PTR matrix_address = client_module.module_address + dwViewMatrix; //獲取視角矩陣地址
    g_matrix_address = matrix_address; //將視角矩陣地址儲存到全域性變數

    //獲取人物視角地址
    ReadProcessMemory(g_process_handle, (LPVOID)(engine_module.module_address + 0x58CFDC), &temp_address, 4, NULL);//[engine.dll + 58CFDC]+00004D90
    g_angle_address = temp_address + dwClientState_ViewAngles;

    //獲取本地人物地址 [client.dll+0xDC04CC]+100 = 生命值
    ReadProcessMemory(g_process_handle, (LPVOID)(client_module.module_address + dwLocalPlayer), &temp_address, 4, NULL);
    g_local_player = temp_address; //[g_local_player+100] = 生命值

    //獲得ENtitylist地址  [client.dll+0x4DDC90C + i *0x10]+100 = 敵人生命值
     g_player_list_address = client_module.module_address + dwEntityList;
}

 


 先說一下整體的思路:

 

 通過程序名(csgo.exe)獲取程序ID

  ↓

 通過程序ID獲取程序控制程式碼、client.dll模組的資訊

  ↓

 通過程序控制程式碼讀取人物視角矩陣地址、本地人物物件地址、敵人物件地址 並儲存到全域性變數(初始化完成)

  ↓

 獲得螢幕大小儲存在全域性變數、建立透明視窗

  ↓

 迴圈遍歷敵人物件,通過地址讀取到人物的視角矩陣、敵人的位置

  ↓

 在迴圈中將敵人的位置結合矩陣,轉換成2D座標

  ↓

 再回圈中在透明視窗上把算出來的座標畫出來

 


 

再寫一段虛擬碼出來幫助理解,程式碼貼在後面

int main
{
    獲取視角矩陣地址、獲取本地人物地址、獲取敵人物件地址
    獲取螢幕解析度
    根據螢幕解析度建立視窗
    while1)訊息迴圈
    {
    清除畫的線 獲得視角矩陣,因為會變,所以需要不停的獲取
for(int i=0;i<64;i++)因為遊戲人數最大為64 { 獲得自己的陣營 獲取當前敵人物件 根據物件獲取人物血量、陣營、生存狀態、敵人是否有效 如果敵人血量<=0 或者 敵人陣營=自己陣營 或者 無效 或者 敵人物件為空 或者 敵人生存狀態
        則遍歷下一個物件 獲得敵人的位置 將敵人的座標轉換為2D座標 畫線 } } }

 

 

程式碼部分

GetImformation.h

#pragma once
struct Vec2
{public:
    float x, y;

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

};
bool is_error();
void error(const char*text);
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;
    }
};
void init_address(const char*process_name);
DWORD get_process_id(const char*process_name);

HANDLE get_process_handle(DWORD process_id);

void ConvertToRange(Vec2 &Point);
bool WorldToScreen(const Vec3& VecOrgin, Vec2& VecScreen, float* Matrix);
void get_moduel_info(DWORD process_id, const char *name, OUT module_information&info);
DWORD read_memory(HANDLE process, DWORD address, void *recv, int size);

template<class T>
T ReadMem(HANDLE ProcessHandle, UINT_PTR Address, int size)
{
    T Reader;
    ReadProcessMemory(ProcessHandle, (LPVOID)Address, &Reader, size, NULL);
    return Reader;
}

 

GetImformation.cpp

#include<Windows.h>
#include<TlHelp32.h>
#include"GetIMformation.h"


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
//獲取模組資訊
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);//TODO
            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;
}
void error(const char*text)
{
    MessageBoxA(nullptr, text, nullptr, MB_OK);
    exit(-1);
}
bool is_error()
{
    return GetLastError() != 0;
}
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("讀取記憶體失敗");
}

HANDLE get_process_handle(DWORD process_id)
{
    HANDLE process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_id);
    if (is_error())
        error("get_process_handle失敗");
    std::cout << "程序控制程式碼為:" << std::hex << process_handle << std::endl;
    return process_handle;
}
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)
        {

            CloseHandle(snap);
            return process_info.th32ProcessID;
        }
        state = Process32Next(snap, &process_info);
    }
    CloseHandle(snap);
    MessageBoxA(NULL, "查詢程序id失敗", "提示", MB_OK);
    return 0;
}
void GetWindowSize()
{
    HDC hdc = GetDC(nullptr);
    g_client_width = GetDeviceCaps(hdc, DESKTOPHORZRES);
    g_client_height = GetDeviceCaps(hdc, DESKTOPVERTRES);
    ReleaseDC(nullptr, hdc);
}

void init_address(const char*process_name)
{
    std::cout << "請先啟動遊戲"<< std::endl;

    DWORD process_id = get_process_id(process_name);
    HANDLE process_handle = get_process_handle(process_id);
    g_process_id = process_id;

    //獲取模組資訊
    g_process_handle = process_handle;

    get_moduel_info(process_id, "engine.dll", engine_module);
    get_moduel_info(process_id, "client.dll", client_module);
    get_moduel_info(process_id, "server.dll", server_module);


    //TODO 要寫一個特徵碼定址,獲取視角矩陣資訊
    UINT_PTR temp_address;
    float Matrix[16];
    UINT_PTR matrix_address = client_module.module_address + dwViewMatrix;
    g_matrix_address = matrix_address;

    //獲取 人物視角地址
    ReadProcessMemory(g_process_handle, (LPVOID)(engine_module.module_address + 0x58CFDC), &temp_address, 4, NULL);//[engine.dll + 58CFDC]+00004D90
    g_angle_address = temp_address + 0x00004D90;

    //獲取本地人物地址 [client.dll+0xDC04CC]+100 = 生命值
    ReadProcessMemory(g_process_handle, (LPVOID)(client_module.module_address + dwLocalPlayer), &temp_address, 4, NULL);
    g_local_player = temp_address; //[g_local_player+100] = 生命值
    temp_address = 0;

    //獲得ENtitylist地址  [client.dll+0x4DDC90C + i *0x10]+100 = 敵人生命值
     g_player_list_address = client_module.module_address + dwEntityList;
}

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;
}

 

main.cpp

#include <stdio.h>
#include<cstdlib>
#include<Windows.h>
#include<iostream>
#include<cmath>

#include <GLFW/glfw3.h>
#include "imgui/imgui.h"
#include "imgui/imgui_impl_glfw.h"
#include "imgui/imgui_impl_opengl3.h"

#include "GetIMformation/GetIMformation.h"


//宣告外部變數
extern DWORD g_process_id;
extern HANDLE g_process_handle;
extern UINT_PTR g_local_player;
extern UINT_PTR g_player_list_address;
extern UINT_PTR g_matrix_address;
extern UINT_PTR g_angle_address;
extern HWND g_game_hwnd;
extern module_information engine_module;
extern module_information client_module;
extern module_information server_module;
extern float g_client_width;
extern float g_client_height;

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();
}
void ShowMenu(GLFWwindow* Window)
{
    glfwSetWindowAttrib(Window, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE);
}

void HideMenu(GLFWwindow* Window)
{
    glfwSetWindowAttrib(Window, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE);
}

static void glfw_error_callback(int error, const char* description)
{
    fprintf(stderr, "Glfw Error %d: %s\n", error, description);
}

void GetWindowSize()
{
    HDC hdc = GetDC(nullptr);
    g_client_width = GetDeviceCaps(hdc, DESKTOPHORZRES);
    g_client_height = GetDeviceCaps(hdc, DESKTOPVERTRES);
    ReleaseDC(nullptr, hdc);
}


int main(int, char**)
{
    /////////////////////////功能性程式碼////////////////////////////////////////////////////////////////////////////////////////////
    GetWindowSize();
    init_address("csgo.exe");
    UINT_PTR temp_address;

    /////////////////////////功能性程式碼////////////////////////////////////////////////////////////////////////////////////////////


    // Setup window
    glfwSetErrorCallback(glfw_error_callback);
    if (!glfwInit())
        return 1;
    GLFWmonitor* monitor = glfwGetPrimaryMonitor();


    //###########################設定視窗###########################
    auto glsl_version = "#version 130";
    int Height = glfwGetVideoMode(monitor)->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<UINT_PTR>(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 <= 0) ||
                    lifestate || Dormant)
                    continue;

                ReadProcessMemory(g_process_handle, (LPVOID)(Entity + m_vecOrigin), &temp_pos, 12, nullptr);
                EntityLocation.x = temp_pos[0], EntityLocation.y = temp_pos[1], EntityLocation.z = temp_pos[2];

                if (!WorldToScreen(EntityLocation, ScreenCoord, Matrix))
                    continue;

                if (true)
                {
                    DrawLine(LineOrigin, ScreenCoord);
                }
            }
        }
        // Rendering
        ImGui::Render();
        int display_w, display_h;
        glfwGetFramebufferSize(window, &display_w, &display_h);
        glViewport(0, 0, display_w, display_h);
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
        glfwSwapBuffers(window);
    }

    // Cleanup
    ImGui_ImplOpenGL3_Shutdown();
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
    glfwDestroyWindow(window);
    glfwTerminate();

    return 0;
}

至此一個簡單的透視就寫完了,本系列也完結力....

 

後記:

  感覺有些地方邏輯講的不是很清晰,有問題可以在評論區裡提

  裡面寫的偏移全都是寫死的,等CSGO一更新就不能用了,解決這個問題的方法,就是寫一個特徵碼查詢,但不能保證所有人都是來學思路和方法的,對於直接copy程式碼的,起碼偏移要自己找一下。

  方框、自瞄、防閃其實也寫了,但感覺不是很適合在這裡發,如果後面想發了,再寫一個補充篇吧

  因為是從一堆功能中抽出一個小透,所以可能報錯,少點什麼東西,能自己補的自己補一下,不能的評論區說一下,我來補