写点什么

揭秘手游外挂:基于内存蜜罐的内存修改挂分析技术

2021 年 4 月 02 日

揭秘手游外挂:基于内存蜜罐的内存修改挂分析技术

经过近几年游戏市场的变迁,手游市场也在飞速发展。同时手游本身的安全风险也逐渐暴露出来。无恒实验室也在承担着手游安全评审的相关工作,上期我们分享了游戏安全评审的技术进阶历程。2020 年市场上重度手游的不断推出,游戏外挂的风险更是与日俱增,无恒实验室也加入到反外挂的战场。外挂分析作为反外挂的第一步,分析的深度、质量和时效,又往往对外挂打击起着决定性的作用。


本文从外挂分类讲起,给大家一个初步感性认知,之后对占比高达 90%以上的内存修改挂的快速分析技巧进行详细介绍。

一、外挂分类

2020 年伊始,外挂情报同学收集了不同游戏大量的外挂样本,从技术实现上大概分为以下几类


  • 定制挂:针对特定游戏逻辑或数据特征,通过直接修改客户端逻辑、数据或读取游戏核心数据并展示,以实现游戏作弊功能,常见的有以下几类

  • root、越狱类注入型外挂

  • 基于应用多开形式的外挂

  • 基于 vmos、光速虚拟机等虚拟机挂

  • 基于 windows+模拟器类型的外挂

  • 通用修改器:具备内存查找修改功能的通用或者自定义作弊工具,比如 gameguardian、igg、ce、葫芦侠等

  • 脚本辅助类:通过录制玩家操作反复重放,或通过取色点识图等方式进行自动操作的辅助程序。比如按键精灵、叉叉助手等

  • 破解版:修改游戏客户端逻辑、数据、资源,重打包形成具备一定作弊功能的非法客户端,常见于单机休闲类游戏。


尽管技术表现形式多种多样,但从原理上无外乎内存修改、函数调用、模拟点击、协议模拟,其中尤以内存修改类外挂占比居多,不完全统计内存修改类可占到 90% 以上的比例。

二、内存修改挂分析思路

内存修改主要包括代码、数据、资源、显存修改外挂,分析主要有三步骤


  1. 确定被修改内存的类型、修改前后的数据,可能存在多处修改。如果直接命中修改代码段则大概率即是外挂功能与此代码实现有关,可省略以下步骤。

  2. 过滤筛选有效内存修改:通过还原内存修改位置,逐步排除无效的内存修改点。

  3. 确认外挂原理:根据不同的游戏引擎不同的实现方式,实现方法不同,不过思想是一致的,即通过监控游戏内存对象的分配释放,搜索第二步得到的内存地址来精确匹配修改的内存对象即可。


高质量的外挂分析,既需要知道外挂做了什么,同时也应该分析清楚外挂为什么这么做,搞清楚外挂功能的内在原理,对游戏引擎、OpenGL、脚本等的理解提出了比较高的要求。限于篇幅,本文仅针对内存修改挂第一步提出了不同情景下的快速分析方法。

2.1 场景 1 跨进程修改手游内存

此类场景相信大家并不陌生,主要是通用修改器和定制挂,定位方法也较为简单。

2.1.1 通用修改器

先来看一段 GG 外挂脚本,如下所示,清楚写明了外挂搜索替换流程,想象下如果分析外挂时能够获取到 GG 脚本,那么外挂分析定位将极大的简化。



然而,现实是残酷的,实际上外挂制作者为了防止外挂脚本外泄,一般都会自定义 lua 解释器并对 lua 脚本进行加密处理,如下图所示,反编译难度和时间成本大大增加。



其实没必要硬碰硬,试想如果我们能够对 GG api 挂钩子,然后将 API 调用序列和参数都打印出来,不就变相的实现了脚本反编译,在此仅提示思路,具体实现有兴趣的同学可以动手尝试。

2.1.2 通用的跨进程监控分析

顺着刚才的思路继续思考,既然是跨进程的内存读写,必然要调用系统 api,如果我们在系统 api 上做文章,不就可以得到通用的内存修改挂的分析定位方法吗?经过实践,大致总结以下四种跨进程读写方式,感兴趣的同学可以动手实战锻炼下,细节不再赘述。


  • process_vm_readv、process_vm_writev

  • /proc/pid/mem

  • /dev/mem(涉及整个物理内存的读写,外挂用的比较少)

  • Ptrace PTRACE_PEEKDATA/PTRACE_PEEKTEXT、PTRACE_POKETEXT/POKEDATA

2.2 场景 2 类似注入修改类(虚拟机、多开、Window+模拟器类)

如果说场景 1 是定点 API 突破,那场景 2 就比较复杂了,常规思路只能通过定位外挂模块,脱壳反编译分析+动态调试定位,对于未加固的外挂程序还相对可接受,但如果外挂模块保护比较强,在短短的一天左右时间内分析清楚外挂原理,堪称地狱难度,对人的精力、技巧考验极大,这也是本文重点要讲述的问题。


不止一次的问自己,有没有更好更有效的方法,好在懒人有懒福,经过一段时间摸索思考,终于总结出一套较为实际可行的方案。内存蜜罐分析方案作为通用的分析方案,可有效解决注入类外挂的内存修改定位难题,对跨进程修改内存也有效,可以说统一内存修改类外挂的分析方法。

三、内存蜜罐原理简介

讲原理之前,我们先回顾下内存修改挂的第一步搜索定位指定数据,可能涉及偏移和多级指针,第二步才是修改。而我们的目标是定位修改的位置和长度,如果我们直接 dump 外挂修改前后的进程内存进行对比,则修改的位置必然在其中。但是面对茫茫多的修改位置,如何确定外挂究竟修改的哪一处呢?因此问题转换为修改后的内存精确定位问题,这也是内存蜜罐名称的由来。


内存蜜罐方案的核心就是监控对比外挂功能修改后和修改前的内存变化,精心构造具有指定关系的内存布局,模拟修改前的内存状态,诱导外挂功能关闭开启后再次修改蜜罐内存,通过蜜罐前后的内存对比,即可定位外挂被修改的所有内存位置和修改前后数据,解决分析思路第一步的问题。


针对第二步的问题,通过逐步还原外挂修改的内存并进行测试,即可定位有效内存位置及修改前后数据。

3.1 概念介绍

3.1.1 结构体范围

针对每一处内存修改,外挂一般通过特征搜索定位内存地址+偏移,中间可能涉及多级指针问题。因此每一步内存修改需要确定结构体范围。假设地址 0x1000 中的数据被修改,则构造 0x900 中-0x1100 中的数据,其中,0x100 为结构范围配置项,可考虑 4 字节对齐。

3.1.2 指针级别

默认 1-3 级指针,最多支持 5 级指针,指针级别越高,所需内存越大。

针对结构体中的地址地址范围,进行全局搜索。

3.2 蜜罐实现步骤

3.2.1 DUMP

枚举游戏进程所有内存模块,将关注的内存 dump 到磁盘中,作为原始内存。由于进程运行中,各种内存时刻变化,为了缩小蜜罐监控范围,可以考虑冻结部分线程,并根据游戏类型情况可有选择的去除部分内存


  • 非游戏逻辑相关的内存,比如安卓中/dev、apk、dex、jar、dalvik、zygote 进程空间内存的其他内存

  • 可以考虑去除系统模块内存

  • 只监控游戏引擎核心模块内存及其分配的内存

3.2.2 蜜罐构造

做完第一步,即可开启开挂功能,待外挂修改内存完毕,即可构造蜜罐。蜜罐构造期间、可尝试冻结游戏进程,减少无效修改项的干扰。根据构造方式的不同,又分为内存安全型蜜罐和内存破坏型蜜罐。

3.2.2.1 内存安全蜜罐
  • 原理

以指针级别 2,结构体范围为举例:


实现流程

以指针级别 2,结构体范围为举例

  1. 外挂功能开启前,dump maps 文件中所有内存镜像 imag0;

  2. 根据级别筛选需要监控的内存范围列表;

  3. 外挂功能开启后,对比监控的内存哪些位置发生改变,形成 modify1(地址、原始值、修改后的值)列表,若修改代码段则仅报告修改内容,不存放到 modify1 中;

  4. 指针级别 1,申请内存,直接存放 modify1 列表相关的结构体内存范围;

  5. 指针级别 2,在 imag0 镜像中,搜索 modify1 结构体范围的指针,形成 modify2(地址、原始值)列表,申请内存,直接存放 modify2 列表相关的结构体内存范围,并修正指针;

  6. 指针级别 3,在 imag2 镜像中,搜索 modify1 结构体范围的指针,形成 modify3(地址、原始值)列表,申请内存,直接存放 modify3 列表相关的结构体内存范围,并修正指针;

  7. 将以上自己构造的多个内存蜜罐保存为 image1,释放 modify1、modify2、modify3;

  8. 关闭外挂功能并重新开启,对比监控的内存蜜罐中哪些位置发生改变,此处即为外挂实际修改的内存。

3.2.2.2 内存破坏性蜜罐

原理

该方式不存在多级指针问题,直接将所有指向一级指针的数据,改为构造的内存蜜罐中的地址劣势:可能会造成游戏 crash 或者功能异常。

实现流程

以指针级别 2,结构体范围为举例,相比内存安全蜜罐,流程大大简化。


  1. 外挂功能开启前,dump maps 文件中所有内存镜像 imag0;

  2. 根据级别筛选需要监控的内存范围列表;

  3. 外挂功能开启后,对比监控的内存哪些位置发生改变,形成 modify1(地址、原始值、修改后的值)列表;

  4. 指针级别 1,申请内存,直接存放 modify1 列表相关的结构体内存范围;

  5. 在进程内存空间中搜索 modify1 结构体地址范围,只要命中,则替换为内存蜜罐中的地址。;

  6. 将以上自己构造的多个内存蜜罐保存为 image1;

  7. 关闭外挂功能并重新开启,对比监控的内存蜜罐中哪些位置发生改变,此处即为外挂实际修改的内存。


3.2.3 计算差异

待内存蜜罐构造完成,重新关闭、打开外挂功能。由于上一步内存蜜罐已经按照外挂功能开启前后的内存变化构造了所有被新修改内存的多级内存镜像,因此重新打开外挂功能时内存蜜罐也会一并被搜索到进而修改。


通过 dump 的镜像内存和内存蜜罐现有内存的比对,即可定位出所有被外挂修改的蜜罐内存位置,进而映射出原始游戏进程中被蜜罐修改的内存起始位置,修改前后的数据。

3.2.4 筛选有效内存

将第三步中定位出的所有原始内存修改位置,逐项还原测试外挂功能是否生效,即可精准定位有效内存的修改位置。

四、结束语

整个蜜罐原理和实现并不复杂,难点在于控制蜜罐内存占用量,实际使用中需要控制好结构体范围、多级指针深度和性能优化,由于时间仓促和保密问题,难以将整个方案详尽的展示给大家,未尽之处望大家体谅,欢迎大家拍砖讨论。


本文转载自:字节跳动技术团队(ID:toutiaotechblog)

原文链接:揭秘手游外挂:基于内存蜜罐的内存修改挂分析技术

2021 年 4 月 02 日 07:002155

评论

发布
暂无评论
发现更多内容

秒杀系统的难点在哪?如何突破?

跳蚤

区块链一文知

秋呈

区块链+

递归的人生哲学

Nick

数据结构 算法 递归

区块链与数字货币的发展到底有什么意义

CECBC区块链专委会

数字货币

产品经理训练营知识汇总

SilentMacUser

产品经理 产品经理训练营 邱岳

博文推荐 | Apache Pulsar 延迟消息投递解析

Apache Pulsar

kafka 开源 RocketMQ pulsar Apache Pulsar

【管理笔记16】行销的获利性分析

俊毅

28天写作

浅谈JVM 垃圾回收原理

跳蚤

优化JAVA代码总结

跳蚤

第十三周作业&总结

胡益

架构师训练营第八周作业 - 命题作业

阿德儿

【答疑点评必看】如何从「数据范围」中找到解题「突破口」...

宫水三叶的刷题日记

LeetCode 数据结构与算法 面试数据结构与算法

不写代码可以写爬虫程序吗?老师说可以,无编码学爬虫之一。

梦想橡皮擦

Python 28天写作 2月春节不断更

数据库规范设计说明书 整理

edd

翻译:《实用的Python编程》03_01_Script

codists

Python

join为啥会阻塞主线程?

叫练

join

「架构师训练营 4 期」 第八周 - 001&2

凯迪

架构师训练营 4 期

Elasticsearch Query Phase

escray

elastic 七日更 28天写作 死磕Elasticsearch 60天通过Elastic认证考试 2月春节不断更

《函数式编程精粹》(1) 函数式思考

陈皓07

《函数式编程精粹》(2) 热身:A STACK BASED CALCULATOR

陈皓07

网络故障的排错思路指南

微服务架构设计与最佳实践

Kevin Wan

go 微服务 微服务拆分 微服务治理 go-zero

译文 | 深度剖析 Pulsar Functions

Apache Pulsar

大数据 kafka 开源 pulsar Apache Pulsar

《函数式编程精粹》(3) Functional Design

陈皓07

第9周作业

cafebaby

MySQL连接超时关闭问题解决

flyer0126

MySQL MySQL优化

APP启动流程图

林亚超

第8周课后练习-性能优化二

潘涛

架构师训练营 4 期

Java 并发系列(一):多线程三大特性

TroyLiu

Java 多线程 原子性 可见性 有序性

为何要构建团队契约

Bruce Talk

敏捷 Agile

并发与并行

ES_her0

28天写作

4月17日 HarmonyOS 开发者日·上海站

4月17日 HarmonyOS 开发者日·上海站

揭秘手游外挂:基于内存蜜罐的内存修改挂分析技术-InfoQ