一般有两种方式,一种是外挂,一种是内挂,外挂是在创建一个透明窗口,在透明窗口上画线,让鼠标事件透过窗口,透明窗口覆盖在游戏窗口上。内挂是通过DLL注入,HOOK游戏中的绘制函数,在游戏绘制人物的时候绘制自己的线。还剩一种比较少用,但也可以实现,找到人物模型ID,在渲染到人物模型的时候关掉渲染缓冲(应该是叫这个?),使人物模型在墙模型前面渲染,导致可以直接看到人物。本篇文章采用的是外挂的形式,根据上篇文章已经可以创建出一个覆盖在屏幕上的透明窗口。
变量名起的挺明白的,就不写注释了
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 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; }
使用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; }
可以发现偏移都是由 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; }
标签:c++,操作,代码,函数,地址 来源:
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。