
使用的软件:
- Cheat Engine
- Visual Studio
使用的编程语言:
- C#
前言
这一文章将向你展示从基址寻找到内存读取,再通过矩阵算法绘制方框。从0开始制作一款CSGO透视辅助软件。
在其中我会提供我寻找到的内存基址和寻找方法,由于游戏版本更新迭代,很有可能部分基址已经无法使用,所以我将着重强调这一方法,而非基址信息。
注:本教程纯属学习用途,制作全过程将会在线下游戏进行,不可在线上游戏中使用!
1.外挂等辅助软件的基本原理
如果稍微了解一点计算机编程开发的都应该知道,我们在对对象或者数据进行初始化个调用的过程中,计算机需要先在内存区域中新增加一个区域用来存放这个数据,或者从磁盘中获取数据存放到内存中的某一个位置。
理解了这个,接下来就好说很多了,在游戏运行中,人物的状态、位置等信息均存在内存中。我们只需要读取并获取这些信息,甚至在有必要的时候我们还可以修改这些信息,从而达到作弊的目的。
举个例子,游戏在开局运行当中,人物的状态、位置等信息也被存在内存中,我们只需要知道人物的位置信息被存在内存当中的哪一个地方,就能顺理成章的获取到人物的位置从而达到透视的效果。
2.人物部分重要基址的获取
首先,需要实现透视功能,当然需要获取到人物的位置信息。在CSGO中,人物数据是用一种链表结构进行存储的,链表的大概格式如下:
Player{
数据地址
ID
上一个结构体地址
下一个结构体地址
}
通过链表就可以依次偏移出所有玩家的人物的信息。
进入游戏,按“~”键打开控制台,键入指令方便我们的后续操作
sv_cheats 1 //开启作弊
mp_roundtime_defuse 60 //设置回合时间60分钟
bot_stop 1 //禁止bot移动
mp_restartgame 1 //1秒后重新开始
我们可以通过已知的数据,比如角色血量的方式来索引链表(血量更加直观,通过链表中其他已知的角色数据都可以尝试搜索链表)

当然我的任务血量为“97”,使用CE搜索“97”,出现八千多个结果,回到游戏使用“hurtme <血量>”指令自扣1点血,再次搜索“96”

循环上述操作,我们最终得到了13个结果

一般由ECX的偏移得到数据,举个例子,ECX+血量的偏移量得到血量。所以我们只需要寻找ECX的数据即可。
先看一下第一个结果,右键点击“找出是什么访问了这个地址”

结果如下,有一条move指令,将ecx+18的值存储进ecx,我们可以试试这不是我们需要的链表地址,此时ecx为0000005F,通过十六进制搜索0000005F。
直接寻找绿色的静态地址即可,右键“浏览相关内存区域”查看这块内存中是否符合我们之前提到的链表特征,如果不符合就继续查看下一条静态地址。

如果这些静态地址中都不符合我们的链表特诊,大概率说明,这个ECX地址并不是我们想要的,寻找下一个ECX的地址即可,比如下面这个EXC地址为“656B5840”。偏移为0x100

我们试试这个,重复上述步骤,十六进制搜索这个值,发现四个绿色静态地址,第一个经过还不符合链表特诊,我们再打开第二个试试,右键浏览相关内存区域。如果是首次打开CE,你可能需要调整一下显示类型为“4字节(HEX)”


我们发现这块内存非常符合链表特诊,且也符合这局10名玩家的条件,上下均为空值。那么基本这个可以判断“client.dll+4E021E4”是在指向这个链表的一个基址。

我们可以来验证一下,之前血量的偏移是0x100,我们可以在CE中手动添加地址和再加上偏移试试能不能得到血量的值

ok,我们继续向下索引,我们转到这个链表地址

因为人物位置坐标数据类型为单浮点,我们把显示类型改为单浮点

尝试在游戏中移动,观察内存区域中的变动情况,得到存放玩家坐标的地址分别是“656B58E0”、“656B58E4”、“656B58E8”

我们得到的这个地址是动态地址,我们需要做一个简单的十六进制加减来得到它对基址的偏移量,方便我们之后在编程环节中得到这个值。用“656B58E0-656B5840(链表地址)”得到偏移量“A0”,然后通过横向加上0x4就可以得到另外两个方向的值了。
接下来我们还需要确定一下角色阵营的数据,将显示类型调整为4字节(DEC)方便查看。不断切换阵营,观察变化的数值

观察得出,在CSGO中,2代表T阵营,3代表CT阵营。
重复上述计算偏移的操作,就可以得到玩家阵营的偏移了。
3.视角矩阵基址的获取
CSGO中的矩阵是4X4的矩阵,特诊主要如下图

有两个我们可以确定的数据,就是这个1和-1,当指向天空时为1,当指向地面时为-1,通过单浮点,反复搜索这两个值,并在绿色的静态地址中就能够找到矩阵

1.代码
我们已经详细讲解过如何通过CE寻找CSGO的内存基址了,在这一篇中,我将直接展示代码。
github: https://github.com/Mangofang/CSGOCheat
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Drawing;
namespace CSGOCheat3._0
{
class Program
{
struct Player //人物struct
{
public static int[] Address = new int[20];
public static float[] LocatX = new float[20];
public static float[] LocatY = new float[20];
public static float[] LocatZ = new float[20];
public static float[] Head = new float[20];
public static int[] Heal = new int[20];
public static int[] Camp = new int[20];
}
public static float clipCoords_x;
public static float clipCoords_y;
public static float clipCoords_z;
public static float clipCoords_w;
public static float clipCoords_x_head;
public static float clipCoords_y_head;
public static float clipCoords_z_head;
public static float clipCoords_w_head;
public static float Rectangle_wight;
public static float Rectangle_hight;
public static float NDC_x;
public static float NDC_y;
public static float NDC_x_head;
public static float NDC_y_head;
public static float[] Matrix = new float[16];
public static float[] screen_x = new float[20];
public static float[] screen_y = new float[20];
public static float[] screen_x_head = new float[20];
public static float[] screen_y_head = new float[20];
public static string gamename = "csgo";
public static int windows_x = 1920;
public static int windows_y = 1080;
public static IntPtr hAddress = OpenProcess(0x1F0FFF, false, GetPidByProcessName(gamename));//获取游戏进程
public static IntPtr csgo_handle = Process.GetProcessById(GetPidByProcessName(gamename)).MainWindowHandle;//获取游戏句柄
static void Main(string[] args)
{
int Player_Num;
int dev = 0x0;
GetWindowsXandY();
//如果无法显示方框只需要修改下面的两个地址即可
IntPtr MainAddress = (IntPtr)(GetDllAddress("client.dll") + 0x4DFFF14);//人物链表地址
IntPtr MatrixAddress = new IntPtr(GetDllAddress("client.dll") + 0x4DF0D6C - 0x28);//视角矩阵
for (Player_Num = 0; Player_Num <= 20; Player_Num++)//计算链表内角色数量
{
int tem_Address = 0;
ReadProcessMemory((int)hAddress, (IntPtr)MainAddress + dev, out tem_Address, 4, 0);
Player.Address[Player_Num] = tem_Address;
if (tem_Address == 0)
{
break;
}
dev = dev + 0x10;
}
ThreadPool.QueueUserWorkItem(item =>
{
Pen pen_DR = new Pen(Color.FromArgb(0x66ff0000), 2);
Pen pen_DY = new Pen(Color.Green, 2);
Graphics e = Graphics.FromHwnd(csgo_handle);
e.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
e.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
byte x = 0;
byte y = 0;
byte z = 0;
byte[] x_ = new byte[4];
byte[] y_ = new byte[4];
byte[] z_ = new byte[4];
while (true)
{
for (int i = 0; i <= Player_Num - 1; i++)
{
for (int a = 0; a < 4; a++)
{
ReadProcessMemory((int)hAddress, (IntPtr)Player.Address[i] + 0xA0 + a, out x, 4, 0);
x_[a] = x;
ReadProcessMemory((int)hAddress, (IntPtr)Player.Address[i] + 0xA4 + a, out y, 4, 0);
y_[a] = y;
ReadProcessMemory((int)hAddress, (IntPtr)Player.Address[i] + 0xA8 + a, out z, 4, 0);
z_[a] = z;
}
ReadProcessMemory((int)hAddress, (IntPtr)Player.Address[i] + 0xF4, out Player.Camp[i], 4, 0);
ReadProcessMemory((int)hAddress, (IntPtr)Player.Address[i] + 0x100, out Player.Heal[i], 4, 0);
Player.Head[i] = Player.LocatZ[i] + 70f;
Player.LocatX[i] = ToFloat(x_);
Player.LocatY[i] = ToFloat(y_);
Player.LocatZ[i] = ToFloat(z_);
Matrix[0] = MatrixToFloat(MatrixAddress);
Matrix[1] = MatrixToFloat(MatrixAddress + 0x4);
Matrix[2] = MatrixToFloat(MatrixAddress + 0x8);
Matrix[3] = MatrixToFloat(MatrixAddress + 0x8 + 0x4);
Matrix[4] = MatrixToFloat(MatrixAddress + 0x10);
Matrix[5] = MatrixToFloat(MatrixAddress + 0x14);
Matrix[6] = MatrixToFloat(MatrixAddress + 0x18);
Matrix[7] = MatrixToFloat(MatrixAddress + 0x18 + 0x4);
Matrix[8] = MatrixToFloat(MatrixAddress + 0x20);
Matrix[9] = MatrixToFloat(MatrixAddress + 0x24);
Matrix[10] = MatrixToFloat(MatrixAddress + 0x28);
Matrix[11] = MatrixToFloat(MatrixAddress + 0x28 + 0x4);
Matrix[12] = MatrixToFloat(MatrixAddress + 0x30);
Matrix[13] = MatrixToFloat(MatrixAddress + 0x34);
Matrix[14] = MatrixToFloat(MatrixAddress + 0x38);
Matrix[15] = MatrixToFloat(MatrixAddress + 0x38 + 0x4);
GetclipCoord(Player.LocatX[i], Player.LocatY[i], Player.LocatZ[i], Player.Head[i], i);
Rectangle_hight = (screen_y_head[i] - screen_y[i]) * -1;
Rectangle_wight = 0.5f * Rectangle_hight;
if (Player.Address[i] != Player.Address[0] && Player.Heal[i] != 0)
{
if (Player.Camp[i] != Player.Camp[0])
{
e.DrawLine(pen_DR, windows_x / 2, windows_y, screen_x[i], screen_y[i]);
e.DrawRectangle(pen_DR, screen_x[i] - Rectangle_wight / 2, screen_y[i] - Rectangle_hight, Rectangle_wight, Rectangle_hight);
}
else
{
e.DrawLine(pen_DY, windows_x / 2, windows_y, screen_x[i], screen_y[i]);
e.DrawRectangle(pen_DY, screen_x[i] - Rectangle_wight / 2, screen_y[i] - Rectangle_hight, Rectangle_wight, Rectangle_hight);
}
}
}
}
});
//Console.WriteLine(Player_Num);
Console.ReadKey();
}
public static float MatrixToFloat(IntPtr MatrixAddress)
{
byte data;
byte[] C = new byte[4];
for (int i = 0; i < 4; i++)
{
ReadProcessMemory((int)hAddress, MatrixAddress + i, out data, 4, 0);
C[i] = data;
}
return ToFloat(C);
}
public static bool GetclipCoord(float pos_x, float pos_y, float pos_z, float head, int i)
{
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];
clipCoords_x_head = pos_x * Matrix[0] + pos_y * Matrix[1] + head * Matrix[2] + Matrix[3];
clipCoords_y_head = pos_x * Matrix[4] + pos_y * Matrix[5] + head * Matrix[6] + Matrix[7];
clipCoords_z_head = pos_x * Matrix[8] + pos_y * Matrix[9] + head * Matrix[10] + Matrix[11];
clipCoords_w_head = pos_x * Matrix[12] + pos_y * Matrix[13] + head * Matrix[14] + Matrix[15];
if (clipCoords_w < 0.1f || clipCoords_w_head < 0.1f)
{
return false;
}
NDC_x = clipCoords_x / clipCoords_w;
NDC_y = clipCoords_y / clipCoords_w;
NDC_x_head = clipCoords_x_head / clipCoords_w_head;
NDC_y_head = clipCoords_y_head / clipCoords_w_head;
screen_x[i] = (windows_x / 2 * NDC_x) + (NDC_x + windows_x / 2);
screen_y[i] = -(windows_y / 2 * NDC_y) + (NDC_y + windows_y / 2);
screen_x_head[i] = (windows_x / 2 * NDC_x_head) + (NDC_x_head + windows_x / 2);
screen_y_head[i] = -(windows_y / 2 * NDC_y_head) + (NDC_y_head + windows_y / 2);
return true;
}
public static float ToFloat(byte[] data)
{
float a = 0;
byte i;
byte[] x = data;
unsafe
{
void* pf;
fixed (byte* px = x)
{
pf = &a;
for (i = 0; i < data.Length; i++)
{
*((byte*)pf + i) = *(px + i);
}
}
}
return a;
}
public static int GetDllAddress(string DllName)
{
int address = 0;
foreach (Process p in Process.GetProcessesByName(gamename))
{
foreach (ProcessModule m in p.Modules)
{
if (m.ModuleName == DllName)
{
address = m.BaseAddress.ToInt32();
}
}
}
return address;
}
public static int GetPidByProcessName(string processName)
{
Process[] arrayProcess = Process.GetProcessesByName(processName);
foreach (Process p in arrayProcess)
{
return p.Id;
}
return 0;
}
[DllImportAttribute("kernel32.dll", EntryPoint = "ReadProcessMemory")]
static extern bool ReadProcessMemory(int hProcess, IntPtr lpBaseAddress, out int lpBuffer, int nSize, int lpNumberOfBytesRead);
[DllImportAttribute("kernel32.dll", EntryPoint = "ReadProcessMemory")]
static extern bool ReadProcessMemory(int hProcess, IntPtr lpBaseAddress, out byte lpBuffer, int nSize, int lpNumberOfBytesRead);
[DllImportAttribute("kernel32.dll", EntryPoint = "OpenProcess")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;//最左坐标
public int Top;//最上坐标
public int Right;//最右坐标
public int Bottom;//最下坐标
}
public static void GetWindowsXandY()
{
RECT rect = new RECT();
GetWindowRect(csgo_handle, ref rect);
windows_x = rect.Right - rect.Left;
windows_y = rect.Bottom - rect.Top;
}
}
}
Comments NOTHING