复古决战快速施法穿墙秒怪分析流程及安全防护

news2024/12/26 11:18:52

《决战》是一款非常古老的RPG游戏,作为热血传奇同期的热门游戏也深受7080后的喜爱。

在十几年前玩这个游戏时,我也使用过瞬移穿墙,快速施法秒怪等功能的辅助。

下面我们就用一个自己架设的单机版来回顾一下当年辅助开发的流程,并从安全角度来对这些功能进行防护分析。

一、找到明文发包函数

首先我们对功能的数据需求进行分析。

瞬移穿墙是法师专利,常规情况下法师的瞬移是不可以穿墙的,为了实现穿墙功能,我们一定要从发包入手,所以首要目标是找到明文发包函数。

然后是快速施法,既然有这个功能说明这个功能也很可能是通过快速的协议重发实现的,所以我们也要从明文发包函数入手。

xdbg32附加游戏,在send下断,走路游戏断下

重复下断,做吃药或者瞬移动作,观察堆栈发现堆栈返回是不同的,说明游戏没有线程发包

多次执行到返回,找到公共CALL和功能CALL临近的位置,为明文发包函数头部

以上都是新手的基础操作,没啥难点。

二、瞬移穿墙分析

在技能CALL上下断,进行瞬移,我们来观察下参数

明文函数有2个参数,第一个是0,第二个是结构体

结构体中+8又是一个结构体,作为明文封包结构体,而+C则是包长,其他的都写固定值即可

分析明文封包结构体

+0的6D表示技能包头,BYTE型

+1是2712,应该是角色ID,DWORD型

+5的07是瞬移技能ID,这个通过和其他技能的对比能够观察出来

然后+6和+A则是目的地的X和Y坐标,DWORD型

我们自己申请内存构建结构体,用代码注入器进行测试,发现这个封包是可以穿墙的,但是正常游戏是会撞墙的

这说明在上面有某些代码对这个坐标进行了校验和修正

我们向上分析,首先找到了组包函数

这个函数的参数就是XY坐标,并且是修正过的

那么我们去追这两个坐标的来源

修正的X和Y分别来源于ecx+8和ecx+C,而最终来源则是上面的call 0x004A2C20的返回

我们在看下call 0x004A2C20的参数

发现这个CALL的4个参数分别是当前的XY和目的地的XY,而在经过这个CALL之后,目的地的XY会被修正为不可穿墙的坐标

如果我们想手动游戏实现穿墙,可以HOOK下面的4EFA9D来将目的地的坐标传递给修正后的坐标

如果觉得HOOK代码比较麻烦的话,也可以用其他的办法,就是讲004EFA9D处的机器码改写为8B 49 90

因为我们在这个位置下断后发现,ecx-70里面存放的恰好是修正前目的地坐标的结构,而[ecx-70]+8和+C指向的就是目的地坐标

这样我们只需要写内存就可以。

这样处理以后,我们就可以瞬移穿墙了,这里并没有用到其他的数据,接下来我们分析快速施法。

三、快速施法的实现

我们回到技能CALL的位置,对一个怪物释放技能,并观察参数

封包结构体和瞬移很像,其中的包长变为了6

+0同样是包头0x6D

+1是怪物ID,DWORD型

+5是04,代表技能“冰魄”,BYTE型

我们用代码注入器对这个技能进行快速释放,发现技能的释放频率明显提升,以下是普通情况和直接发包的对比

在这个明文封包里面,我们的技能ID可以固定,其实需要传递的只是一个最近的怪物ID

所以我们接下来要对角色和周围对象的数据进行分析

四、角色对象分析

通常情况下角色对象我们要从角色的血量入手,但是当你去扫描角色血量时,会发现直接扫到一个非游戏模块的基地址

无论我们切换装备还是吃药,得到血量都是基地址,此时可能有些人认为自己已经找到了角色对象,就凑合用了。

其实这个数据并不是我们想要的答案,因为这个游戏的对象下血量只有在被近战攻击时才会被正确的写入,其他时候都是不变的或者有错误的。不止是角色,怪物也是如此。

所以我们想找角色对象,需要让怪物攻击掉血后进行扫描。

重新扫描可以得到一个非基地址的结果

对这个结果下访问断点,可以得到+58偏移

ce扫描rbx可以得到两个基地址,其中的一个基地址的结果可能会变化,所以我们选择另一个

对005780C4进行访问,可以得到1ACC偏移和基地址0x5765F8

血量公式[0x5765F8+1ACC]+58

角色对象搞定了,接下来分析周围遍历

五、周围对象遍历

周围对象遍历还是从血量入手,既然前面说到了远程攻击无法改变怪物对象下的血量,那我们就找个怪物去砍他

用CE扫描到怪物血量

用xdbg观察地址,发现怪物对象和角色对象的结构基本是一样的,也就是说第一层偏移也是+58

在血量上下访问断点

向上分析eax,得到来源[esp+10],继续分析得到来源于CALL的返回

在这个CALL里面我们可以得到一个二叉树的遍历,过程就不详细分析了,最终的公式如下

[[0x5765F8+1B0C]+4] 根节点 节点==[0x00590790]结束遍历

+0 左子树

+8 右子树

+C ID

+10 对象

对象+58 当前血量(没啥用的数据)

[对象+2C]+0 名字ASCII

对象+224 X

对象+228 Y

这些数据都是最基本的,没有什么难度,通过观察对比可以得到+158是死亡标志,怪物死亡后

六、周围对象分类

对象类型的重要性往往会被忽略,但是当你用到它时又会发现这个数据无从下手。

如果不对类型进行判断,我们就会对NPC,建筑物,甚至自己发送技能包,所以这个数据是不可或缺的。

最常见的入手点是通过和对象的交互进行分析。比如打开NPC,攻击怪物等等。

我们找到一个NPC,通过明文发包函数断到打开NPCCALL

再次返回一层

接下来找一个怪,用左键攻击他,得到普通攻击CALL

再次返回后发现和打开NPC的外层是一样的

这说明普通攻击和打开NPC是在同一个函数下的,我们需要找到区分这两个功能的跳转。

在头部下断,立刻断下,单步执行找到跳出到返回的跳转,然后绕过这个跳转,在下面下断做打开NPC动作

单步执行到一个dec代码下面时发现打开NPC不跳,攻击怪物跳

到CALL里分析eax的来源

这里跳转的ecx决定返回的eax值,所以我们只要分析ecx的来源即可,向上分析,经过一个inc后来源于+248偏移

这个偏移恰好在对象下,经过多个对象的观察,我们得出了以下结论

这里的建筑指的是传送门之类的,未知是一些看不到的对象

分类解决了,接下来就是怪物的死亡标志,这个数据是通过对对象的属性进行观察得到的,因为怪物死亡后0.5秒左右才会改变,所以如果用CE搜索的过快可能会把这个标志过滤掉

死亡标志的偏移是+158

七、封装数据,编写逻辑,实现功能

搞定了数据以后,我们就可以实现这个自动释放技能打怪的功能了,

首先我们需要通过二叉树读取到周围对象的信息

    void objTree(DWORD node)//遍历周围对象二叉树
{
     try
     {
            DWORD left = (DWORD)(node + 0);
            DWORD right = (DWORD)(node + 8);
            DWORD ObjID = (DWORD)(node + 0xC);
            DWORD dObj = (DWORD)(node + 0x10);
            objarr[i].setID(ObjID);
        objarr[i].setDeath(*(BYTE*)(dObj + 0x158));
        objarr[i].setType(*(DWORD*)(dObj + 0x248));
        objarr[i].setCoord(*(DWORD*)(dObj + 0x228), *(DWORD*)(dObj + 0x22C));
        char* name = (char*)(*(DWORD*)(dObj + 0x2C));
        objarr[i].setName(name);

        char* isdeath = "";
        if (objarr[i].getDeath())
        isdeath = "死亡";
        else
        isdeath = "活着";
        DbgPrintf_Mine("%X ,%s ,%X ,%s ,%d ,%d ,%s", dObj, objarr[i].getName(), objarr[i].getID(), objarr[i].getType(), objarr[i].getCoord().X, objarr[i].getCoord().Y, isdeath);
        i++;
            if (left != *(DWORD*)Base_End)
            {
            objTree(left);
            }
            if (right != *(DWORD*)Base_End)
            {
            objTree(right);
            }
     }
     catch (...)
     {
           DbgPrintf_Mine("二叉树读取异常" );
     }
}

然后我们要在这些对象中筛选出最近的、活着的、在技能射程范围内的怪物

Object getNearest()//取最近距离的,活着的怪物
{
        getObj();
        DWORD a = 1000;
        DWORD b = 1000;
        Object cNest = Object();
        for (int j = 0; j < i; j++)
        {
        b = getdistance(objarr[j]);

        if ((a > b)  && (objarr[j].getDeath() == 0) && (objarr[j].getTypeid() == 0))
        {
            a = b;        
            cNest = objarr[j];
        }
        }
        return cNest;
}

在发送封包的代码中我们加入射程判断,并对射程在15以内的怪物释放技能

void sendPacket()    //发送封包
{
        Object obj = getNearest();
        if (getdistance(obj) < 15)
        {
        static char buf[0x500] = { 0 };
        static char buf1[0x500] = { 0 };
        memset(buf, 0, 0x500);
        memset(buf1, 0, 0x500);
        packet1* pkt1 = (packet1*)buf;
        packet2* pkt2 = (packet2*)buf1;
        //组包过程
        pkt2->packHead = 0x6D;
        pkt2->id = obj.getID();
        if (pkt2->id == 0)
            return;

        pkt2->num = 2;

        pkt1->base = 0x517788;
        pkt1->a1 = 1;
        pkt1->pack2 = pkt2;
        pkt1->lenth = 6;
        pkt1->a2 = 1;
        pkt1->a3 = 0x400;
        pkt1->skillId = 0x6A5A;
      //内联汇编调用明文发包函数
        DWORD call1 = call_mingwen;
        DWORD b = (DWORD)&buf;
        __asm
        {
            pushad
            mov eax, b
            push eax
            push 0
            mov eax, call1
            call eax
            popad
        }
    }
}

最后我们只要开一根线程去不断的调用技能函数即可

int ii = 0;
void myThread()
{    
        ii = 0;
        while(ii == 0)
        {
        Sleep(100);
        sendPacket();
        }
}

void endThread()
{
        ii = 1;
}

以上就是这个功能实现的全过程,非常的简单,除了数据分析的过程稍长一些,逻辑代码非常少

后面我们还可以加入一些远程传送,自动打怪的功能

八、游戏安全角度的对抗

以上的功能虽然很暴力,但是从游戏安全角度去做检测也是非常简单的

1、首先是穿墙瞬移的部分,我们可以通过CRC去进行校验,保证代码不被普通改写。

2、其次是快速施法,我们可以在封包中加入时间戳,或者在服务器对封包间隔做校验,这个处理起来其实是很容易的

3、如果后面用到了远程传送,我们也可以做传送距离的校验,一般来说,封包传送的最大距离是能够遍历到的最远距离,

而玩家能够实现的距离则是一个屏幕内能看到的距离。

其实所有的功能只要在服务器做好严格的校验,就很难出现类似的BUG,本地的检测在协议面前很难发挥作用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/477160.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【三十天精通Vue 3】 第二十二天 Vue 3的UI框架详解

✅创作者&#xff1a;陈书予 &#x1f389;个人主页&#xff1a;陈书予的个人主页 &#x1f341;陈书予的个人社区&#xff0c;欢迎你的加入: 陈书予的社区 &#x1f31f;专栏地址: 三十天精通 Vue 3 文章目录 引言一、常用的Vue 3 UI框架概览1. 常用的Vue 3 UI框架有哪些&…

不同主题增删改查系统【控制台+MySQL】(Java课设)

有很多顾客都是只要实现各种各样的增删改查系统即可&#xff0c;只是主题和数据库表不一样&#xff0c;功能都是增删改查这四个功能&#xff0c;做出来的效果和下面的截图是一样的&#xff0c;后续这样的增删改查系统的运行效果请参考下面的截图&#xff0c;我就不一一演示了&a…

OSPF-MGRE综合实验

拓扑结构&#xff1a; 要求&#xff1a; 1、R6为ISP只能配置IP地址&#xff0c;R1~R5的环回为私有网段 2、R1/4/5为全连的MGRE结构&#xff0c;R1/2/3为星型的拓扑结构&#xff0c;R1为中心站点 3、所有私有网段可以互相通讯&#xff0c;私有网段使用OSPF协议完成 使用的设备…

【Java笔试强训 13】

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 一、选择题 二、编程题 &#x1f525;参数解析…

忆暖行动|“ 还可以留一点做成柿饼,做法也很简单,就是挑硬柿子把皮削掉,用开水烫个几秒”

追忆过往 感恩现在 我们知道&#xff0c;现在的生活与之前相比发生了翻天覆地的变化&#xff0c;您觉得有什么变化呢&#xff1f; 现在的生活好啊&#xff0c;家家房子都盖起来了&#xff0c;你瞅我这房子&#xff0c;是我子女们大前年给我盖的&#xff0c;我原来都是住的土房…

【Unity-UGUI控件全面解析】| Image 图片组件详解

🎬【Unity-UGUI控件全面解析】| Image 图片组件详解一、组件介绍二、组件属性面板2.1 Image Type三、代码操作组件四、组件常用方法示例4.1 简易血条制作4.2 简易技能冷却条制作五、组件相关扩展使用5.1 Mask 遮罩💯总结🎬 博客主页:https://xiaoy.blog.csdn.net 🎥 本…

Java每日一练(20230430)

目录 1. 文本左右对齐 &#x1f31f;&#x1f31f;&#x1f31f; 2. 求素数和 &#x1f31f; 3. 整数转换英文表示 &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专…

mycat的基本介绍及安装

海量数据存储解决方案之分库分表原理解析及mycat安装及使用_已经存在大量数据 可以使用mycat分表吗_踩踩踩从踩的博客-CSDN博客 Mycat核心概念工作原理及高级特性分析_mycat-mini-monitor_踩踩踩从踩的博客-CSDN博客 前言 在之前的文章中&#xff0c;介绍了一部分的mycat&am…

Web App Manager - 将任何网站转换为应用程序

Web App Manager - 将任何网站转换为应用程序 WebApp Manager 是一款实用程序&#xff0c;由 Linux Mint 和 Peppermint 基于 Peppermint 的 ICE 合作创建——用户可以使用该应用程序将他们喜欢的应用程序转换为独立的 Web 应用程序&#xff0c;它最早于 2010 年首次发布&…

CKA/CKS/CKAD认证考试攻略

什么是CKA考试&#xff1f; CKA认证考试是由Linux基金会和云原生计算基金会(CNCF)创建的&#xff0c;以促进Kubernetes生态系统的持续发展。该考试是一种远程在线、有监考、基于实操的认证考试&#xff0c;需要在运行Kubernetes的命令行中解决多个任务。CKA认证考试是专为Kube…

vue---组件基本知识

目录 一、组件基础 二、Props组件交互 三、自定义组件交互 一、组件基础 对于组件&#xff0c;我个人的理解是每个网页其实都是由一个个组件组成的&#xff0c;它可以理解成网页元素的组成单位&#xff0c;下面我们来看下如何将组件加载到页面中。 &#xff08;1&#xff09…

120 · 单词接龙

链接&#xff1a;LintCode 炼码 class Solution { public:/*** param start: a string* param end: a string* param dict: a set of string* return: An integer*/int ladderLength(string &start, string &end, unordered_set<string> &dict) {// write y…

基于springcloud微服务的java课程资源在线学习考试系统

在我国&#xff0c;由于计算机与网络技术的不断发展&#xff0c;信息化建设的不断深入&#xff0c;不管是企业、学校或个人都在结合计算机网络技术队现有的管理或生活中的一些环节进行开发研究&#xff0c;运用计算机进行一些必要的数据信息管理&#xff0c;分析及发布&#xf…

故事连载:AION之殆之永恒的深渊

AION深渊 魔族长老齐西尔内召集了魔族两大司令官&#xff0c;由于天族无法进入伏魔殿&#xff0c;魔族无法进入极乐世界&#xff1b;亚特雷亚事件关系到天魔两族的存亡&#xff0c;天族大神宫尤克类阿斯也意识到事情的严重性&#xff0c;带领天族众将与齐西尔内相约永恒之塔&am…

Centos8手动设置时区、日期、时间,且将时间设置为24小时格式

一、快速设置Centos时间为24小时显示步骤 1.1、查看当前的系统显示时间格式 查看当前Centos系统的时间命令: date 查看后发现Centos系统确实是只显示12小时格式的时间,且使用的不是北京时区时间,因此我们在需要对系统的时区和时间格式需要进行设置。 1.2、快速设…

HTB靶机05-Nibbles-WP

Nibbles 2023-04-04 16:38:48 Scan ┌──(xavier㉿kali)-[~] └─$ sudo nmap -sSV -T4 10.10.10.75 Starting Nmap 7.92 ( https://nmap.org ) at 2023-04-04 16:39 CST Nmap scan report for 10.10.10.75 Host is up (0.43s latency). Not shown: 998 closed tcp ports (…

关于《浏览器如何工作》---塔利加西尔 博客的疑惑与解答

疑惑与解答 1.浏览器组件和浏览器管理的线程是一个概念吗&#xff1f;2.浏览器中的各个线程又是由哪些组件创建与管理的呢&#xff1f;3.为什么使用CDN可以加速JavaScript文件的加载? 1.浏览器组件和浏览器管理的线程是一个概念吗&#xff1f; 浏览器组件和浏览器管理的线程是…

PE系统与U盘启动工具的推荐

PE系统与U盘启动工具 PE系统微PE(快速装系统)优启通(面对各种新旧设备)HotPE(可玩性)FirPE(适合年轻人折腾)Edgeless(随身U盘电脑)WinpeMaker(自定义自己的PE神器)其他PE U盘启动工具Ventoy&#xff08;强烈推荐&#xff09;Rufus&#xff08;推荐&#xff09;Windows USB/DVDE…

IPsec中IKE与ISAKMP过程分析(主模式-消息2)

IPsec中IKE与ISAKMP过程分析&#xff08;主模式-消息1&#xff09;_搞搞搞高傲的博客-CSDN博客 IPsec协议族中IKE&#xff08;Internet Key Exchange&#xff09;是一种基于ISAKMP的协议&#xff0c;它为建立IPSec安全通信隧道提供了一种无痕密钥交换的机制。简单来说&#xff…

用python绘制RC低通滤波器bode图

用python绘制RC低通滤波器bode图 Bode图 Bode图(国内有译作“伯德图”&#xff0c;也有译作“波特图”)是一种用于描述线性系统的频率响应的图形工具。频率响应是指系统对不同频率的输入信号的响应程度&#xff0c;通常用幅度和相位来表示。Bode图以对数坐标轴的形式显示系统…