以CSGO透视为例研究,实现FPS游戏的方框透视!
001卡盟·2020-03-05 14:31:29浏览次数: 1209
以CSGO透视为例研究,实现FPS游戏的方框透视
FPS游戏一直全是游戏中的受欢迎,在诸多稀奇古怪的外挂软件中,透视能够 说成最基础的作用,这在其中,V社受欢迎的CS可以说被苦不堪言,从初期CS1.6的侧门穿烟到如今的CSGO的小陀螺大陀螺图片,殊不知其透视的基本原理是一脉相承的,说简易些,每一游戏手机客户端都务必了解对手的部位,仅仅 在具体游戏中依照游戏体制对游戏玩家给予显示信息,可是,这种对手的部位信息内容,终究是储放在手机客户端—游戏玩家的电脑.
虽然各种游戏拥有自身的反挂对策,可是终究是在游戏玩家的客场上,欠佳游戏玩家只必须寻找储放的内存中的对手的座标,根据GDI绘图方框,便能够 保持最基础的透视,由于这一全过程只能载入内存,而沒有载入,因此透视不易被杜绝.
针对CSGO,由于V社游戏中广泛常有人物高亮的控制面板命令,只必须在手机客户端上将操纵高亮的内存标值改动,乃至都省掉了绘图方框的全过程(以下图)—实际上,这类舞弊方法在18年很广泛.
CSGO人物高亮輔助在这一产业链中,习惯性把对内存开展载入和改动的挂称为外挂软件,把仅限载入内存的称为輔助(脚本制作),实际上业界早就达成协议:輔助就是说外挂软件,套入B站UP主右小死得话:
内存挂奸污游戏,非内存挂性侵游戏,不论是外挂软件和輔助,都对游戏导致了挥之不去的损害.解析巨头都用C++,像我这类菜鸡只适用C#,在上一篇文章中根据读写能力内存的实际操作,保持了一个最基础的植物大战僵尸的修改器,本期以CSGO的方框透视为例,只涉及读内存,不涉及到写.
在本文最终我能把一个双板透视的demo源代码放出去,可是我毫无疑问并不是让大家拿来当仙人的,这也只是是一个最基础的demo罢了,我所有的实践活动所有是在线下bot局里边,都没有对多的人pk开展过影响.因此即使你拿来多的人,毫无疑问都是会被ban掉的.
最先导入我写的一个内存读写能力的类.上一期做植物大战僵尸修改器的情况下的作法是每载入/改动一次内存就获得一次程序流程句柄,实际操作出来这模样极为消耗内存.在遍历CSGO的目标链表的情况下必须消耗1.5秒上下的時间,因此我将好多个方式改成必须实例化再应用,也就是说全部案例目标直至最终销户都只能一个句柄的存有.
struct Person_Struct { public int Health; //CT(警)3,T(匪)2 public int army; public float X, Y, Z; }CSGO中目标的储放是一个链表的结构,在大一所学的数据信息结构中,人们了解只必须寻找随意的一个链表节点,就能够 遍历出全部链表.因此如今人们早已找到存储自身人物角色的节点,根据CE的结构分析,人们能够 获得这模样的一个链表结构
struct Obj_Struct{ public int Self_ADDR;//当前对象实例存放地址 public int ID;//节点ID public int Pre, Next;//上一个节点/下一个节点 }
实现
有了这些,可以遍历出所有节点的对象.
先得到链表头,当然,你也可以找链尾或者同时找.只是这样子比较麻烦.
Obj_Struct Get_First_Node(Obj_Struct Any_Node) { var First_Node = Any_Node; while (First_Node.Pre != 0) { First_Node = Init_Struct(First_Node.Pre); } return First_Node; }
得到链头之后定义一个定时器,每10毫秒轮询一遍,获取最新信息.
void Timer_Call(object state) { if (!Timer_State) { Timer_State = true; var Rs_List = new List<Person_Struct>(); var Index_Node = (Obj_Struct)state; var self = Init_Person(Init_Struct(DllPtr + self_offset).Self_ADDR); while (Index_Node.Next != 0) { Index_Node = Init_Struct(Index_Node.Next); var To_Person = Init_Person(Index_Node.Self_ADDR); // && if (To_Person.Health > 0 && !new float[] { To_Person.X, To_Person.Y, To_Person.Z }.Contains(0)) { if (To_Person.X != self.X) { Rs_List.Add(To_Person); } //Console.WriteLine(To_Person.Health); } } Draw_Rectangle(self,Rs_List); Person_List = Rs_List; Timer_State = false; } }这在其中,每一对象试着去获取它的血量,由于链表中不仅储放着人物,很将会也有一些无关紧要的物品,比如说跑来跑去的雏鸡,枪支这些.一些对象乃至有血量,可是却不一定有人物的坐标,因此假如出現血量不以0且坐标不以(0,0,0)的,那麼就能够 评定它是一个生存人物(死尸血量也为0,就无需显示信息了),评定后,获取其坐标,随后利用WorldToScreen涵数变换为显示屏坐标.WorldToScreen说白了,获取总体目标对象当今的全球坐标系部位,并将其变换为显示屏坐标系的点.
ViewScreen WorldToScreen(Person_Struct Ps,float[,] ViewMatrix) { var Sigma_Matrix = new float[4]; for (int i = 0; i < Sigma_Matrix.Length; i++) { Sigma_Matrix[i] = ViewMatrix[i, 0] * Ps.X + ViewMatrix[i, 1] * Ps.Y + ViewMatrix[i, 2] * Ps.Z + ViewMatrix[i, 3]; } if (Sigma_Matrix[3] < 0.01) { return new ViewScreen() {X = 0,Y=0}; } float X = Sigma_Matrix[0] / Sigma_Matrix[3]; float Y = Sigma_Matrix[1] / Sigma_Matrix[3]; float Z = Sigma_Matrix[2] / Sigma_Matrix[3]; float Window_H = 1080 / 2;//屏幕高度 float Window_W = 1920 / 2;//屏幕宽度 ViewScreen Rs; Rs.X = X * Window_W + Window_W + X; Rs.Y = -(Y * Window_H) + Window_H + Y; return Rs; }
得到人物在屏幕上的坐标就简单的了.最后就是使用GDI+绘制方框了,这里一定要记得绘制出所有人物的坐标后再进行下一轮,而非绘制一个人即refresh,否则即使开启了双缓冲,闪烁也会很严重.使用GDI+绘制的函数:
void Draw_Rectangle(Person_Struct self,List<Person_Struct> Other_list) { //求距离 try { var Rectangles = new List<(Rectangle,Color)>(); foreach (var Other in Other_list) { var VM = WorldToScreen(Other, Get_ViewMatrix(DllPtr + ViewMatrix_offset)); double M = Math.Sqrt(Math.Pow((double)(self.X - Other.X), 2) + Math.Pow((double)(self.Y - Other.Y), 2) + Math.Pow((double)(self.Z - Other.Z), 2)) / 30; int H = Convert.ToInt32(950 / M * 2); int W = Convert.ToInt32(400 / M * 2); if (Other.army == self.army) { Rectangles.Add((new Rectangle((int)VM.X - W / 2, (int)VM.Y - H, W, H), Color.Coral)); } else { Rectangles.Add((new Rectangle((int)VM.X - W / 2, (int)VM.Y - H, W, H), Color.Red)); } } Form1.bitmap_form.Drawing(Rectangles); } catch { } } public void Drawing(List<(Rectangle rectangle,Color line_color)> Rectangles) { try { var MyBuff = new BufferedGraphicsContext().Allocate(CreateGraphics(), new Rectangle(Location.X, Location.Y, Width, Height)); var e = MyBuff.Graphics; e.Clear(BackColor); foreach (var rectangles in Rectangles) { var pen = new Pen(rectangles.line_color, 3); e.DrawRectangle(pen, rectangles.rectangle); } MyBuff.Render(); MyBuff.Dispose(); } catch { } }
这2个涵数的效果是,同势力人物应用橙色描绘,对手应用红色描绘,自然,你可以绘图上人物血条护甲和外挂软件中普遍的放射线这些.因为GDI+的高效率较低,因此一定要打开双缓存,那样才不容易有闪动的状况出現.
假如上边流程进行圆满得话,大约能够 做到以下效果:
因为CSGO只能对手暴露位置,或是间距对手足够近的情况下,才会将其目标载入,且当其身亡或是再度掩藏位置一段时间,他的信息会保存直到下一次暴露位置或是更新情况.因此必须做足够的判断,不可以做到其他手机游戏那类全局性透視的实际效果.
接下去,你必须做进一步的判断与调节,例如判断开倍镜尺寸来调节框架尺寸(由于间距位置不变可是角度引流矩阵发生变化),随后来判断滞留好久没有情况更新的人物(自然要区别老六).
因为我不是玩CSGO的,因此即便有透視打电脑上都是很菜hhh.