
1. 从“黑盒”到“白盒”重新理解手游漏洞挖掘的本质很多人一听到“游戏漏洞挖掘”脑海里浮现的可能是电影里黑客敲击键盘、屏幕滚动着绿色代码的炫酷场景觉得这门槛高不可攀是顶级安全专家的专属领域。其实不然对于手游而言尤其是市面上大量的中轻度游戏其漏洞挖掘的入门门槛远比想象中要低。这背后的核心在于理解一个关键转变现代手游漏洞挖掘很大程度上已经从“破解加密”转向了“逻辑与数据流分析”。早年的单机游戏或早期网游安全重点在于防止客户端被逆向、被脱壳所以加壳、混淆、加密是主流。但现在手游作为强联网、高频更新的服务型产品其核心逻辑和资产校验早已转移到服务端。客户端的很多数据本质上只是一个“视图”或“缓存”。厂商的防护思路也变了与其耗费巨资做一个密不透风的客户端“铁桶”这几乎不可能且影响性能体验不如承认客户端“不可信”转而加固服务端的校验逻辑。这就给我们这些入门者打开了一扇窗我们不需要去攻克那些复杂的VMProtect、Themida等强壳我们的目标变成了——找出客户端与服务端在数据认知上不一致的“缝隙”。举个例子你玩一个卡牌游戏抽卡动画金光闪闪出了一张SSR。这个“出SSR”的结果是客户端先告诉你的还是服务端先确定的显然是服务端先随机确定了结果再把结果卡牌ID下发给客户端客户端只是负责把这个ID对应的炫酷动画播放给你看。漏洞就可能出现在这里如果客户端在请求抽卡时自己能决定或影响这个“卡牌ID”参数呢这就是一个典型的“客户端可控关键参数”漏洞。再比如购买道具时客户端发送了“道具ID1001数量1价格100钻石”的请求包如果服务端只校验了总价100钻石是否足够但没有严格校验“数量”参数是否为正整数那么修改数量为-1就可能触发“购买-1个道具获得100钻石”的异常逻辑。所以入门手游漏洞挖掘你首先要建立的思维是把自己从玩家视角切换为“数据包视角”。你看到的画面、点击的按钮、触发的特效背后都是一次次HTTP/HTTPS或TCP协议的通信。你的工作就是像侦探一样截获、分析、篡改这些通信数据包观察服务端的反应从中找出逻辑悖论。这听起来是不是和Web渗透测试里的抓包改包非常像没错其内核原理是相通的这也是为什么很多Web安全研究员转型做游戏安全上手很快的原因。工具链上也早已平民化。你不需要多么高深的内核驱动开发知识对于入门级挖掘一套“安卓模拟器 抓包工具 内存修改工具”的组合拳就足以应对大多数场景。模拟器推荐夜神或雷电它们对调试支持较好抓包工具首推Burp Suite或Fiddler/Charles用于拦截和修改网络封包内存工具则绕不开经典的Cheat Engine (CE)用于在游戏运行时扫描和修改内存中的数据。别被“内存修改”吓到在入门阶段我们更多的是用它作为“数据探针”定位那些在界面上显示的数字如金币、体力、道具数量在内存中的地址进而理解游戏的数据存储格式而非直接制作外挂。接下来我将通过几个具体、简单且具有代表性的案例带你走完从环境搭建、数据定位、漏洞分析到报告撰写的完整闭环。我们聚焦的是逻辑漏洞而非破解游戏客户端本身。请务必牢记所有测试应在自己拥有合法权限的游戏如公司内部测试服、已获得授权的SRC项目上进行严格遵守法律法规这是安全研究的底线。2. 工欲善其事环境搭建与核心工具初探在开始真正的挖掘之前一个稳定、可控的测试环境至关重要。这个环境需要能让我们方便地运行游戏、拦截流量、查看和修改内存。2.1 安卓模拟器的选择与配置为什么用模拟器而不是真机因为模拟器提供了无与伦比的便利性快照Snapshot功能可以瞬间保存和恢复游戏状态方便反复测试可以轻松地以root权限运行方便安装证书、调试工具屏幕录制和操作脚本也更方便。我个人的选择是雷电模拟器9。它的兼容性和性能比较均衡默认就是root模式。安装后有几项关键配置需要做开启Root权限在模拟器设置中确认“Root权限”已开启。安装证书为抓HTTPS包做准备这是抓包能否成功的关键。以Burp Suite为例启动Burp后在Proxy - Options - Proxy Listeners 中导出其CA证书Der格式。将证书文件拖入模拟器在模拟器的“文件管理器”中找到它点击安装。安装时会要求你命名证书并选择用途务必选择“VPN和应用”或“WLAN”然后完成安装。你可以在“设置 - 安全 - 加密与凭据 - 信任的凭据 - 用户”中看到已安装的Burp证书。配置代理在模拟器的WLAN设置中长按当前连接的Wi-Fi选择“修改网络”。展开高级选项代理选择“手动”代理服务器主机名填写你电脑的IP地址在cmd中输入ipconfig查看通常是192.168.x.x端口填写Burp监听的端口默认8080。保存后模拟器的所有网络流量就会经过你的Burp。注意有些游戏会进行SSL Pinning证书绑定即只信任自己内置的证书不信任用户安装的根证书。这会导致即使安装了Burp证书HTTPS流量也无法解密。遇到这种情况就需要使用更进阶的工具如Frida、Objection来绕过证书绑定这属于中级技巧入门阶段如果遇到可以先记录暂时跳过或寻找其他测试点。2.2 抓包利器Burp Suite的基础使用Burp Suite是Web安全测试的瑞士军刀在游戏测试中同样核心。我们主要用到它的**Proxy代理和Repeater重放器**模块。启动Burp后确保Proxy - Intercept是“Intercept is on”状态。这时模拟器中打开游戏的所有HTTP/HTTPS请求都会被Burp截获。你可以点击“Forward”放行或“Drop”丢弃也可以在此时修改请求内容再放行。但对于漏洞挖掘更常用的模式是“拦截记录事后分析”。我会先把Intercept关掉Intercept is off让流量正常通过所有请求和响应都会记录在Proxy - HTTP history中。然后我去游戏里进行一个关键操作比如购买一个道具。操作完成后回到Burp的History页面通过过滤Filter功能比如按路径/buy、按状态码200等快速定位到刚才的购买请求。找到疑似关键的请求后右键发送到Repeater。Repeater是一个可以手动修改并重复发送单个请求的工具是测试漏洞的“主战场”。你可以在这里修改参数值比如把amount1改成amount-1或amount99999然后点击“Send”观察服务端返回的响应。如果返回了成功购买99999个道具的消息而你的钻石只扣了1个的钱那漏洞就出现了。2.3 内存扫描仪Cheat Engine的定位艺术Cheat Engine常被误解为“外挂制作器”但在安全测试中它是一个强大的动态分析辅助工具。它的核心功能是通过不断变化的值在进程的内存空间中定位存储该值的地址。它的工作原理基于一个简单事实游戏中显示的数字金币、血量、经验值必然在内存的某个地方以某种格式整数、浮点数存储着。CE能帮我们找到它。基本操作流程在CE中点击左上角电脑图标选择游戏进程通常是模拟器进程下的某个子进程如xxx.exe。在游戏中假设你有1000金币。在CE的“数值”框输入1000扫描类型选择“精确数值”数值类型根据情况选“4字节”整数常用或“所有类型”首次模糊搜索时用点击“首次扫描”。扫描结果会列出成千上万个地址这很正常。回到游戏通过任何方式让金币数量发生变化比如花费50金币。现在金币变成950。回到CE在数值框输入变化后的值950点击“再次扫描”。如此反复几次增加、减少结果列表会迅速减少。最终通常会剩下几个甚至一个地址。这个地址很可能就是存储你金币数值的地址。为什么这么做因为内存中虽然有很多值是1000但当你金币变化时只有真正存储你金币的那个地址的值会同步变化。CE通过“变化筛选”法帮我们定位。找到地址后你可以双击它移到下方然后修改它的值。但这不是我们的最终目的。我们的目的是理解这个值在通信中的作用。比如你发现修改这个内存地址的值游戏界面显示的金币立刻变了但重新登录后恢复原样。这说明这个值只是“本地显示”服务端不认可。你需要观察当你进行“购买”操作时客户端是否将这个内存地址的值或由其计算出的值发送给了服务端这时就需要结合Burp抓包来看了。更高级的用法是查找“未知数值”。比如你想找一个隐藏的人物ID。你可以选择“未知的初始值”扫描然后在游戏里切换人物选择“变动的数值”再次扫描再切回来选择“未变动的数值”扫描……通过一系列操作筛选。这需要耐心但原理相通通过内存值的变化规律来定位关键数据。实操心得CE扫描时数值类型的选择非常关键。很多游戏为了反作弊会使用“加密值”或“浮点数”存储。比如界面上显示1000内存里可能是1000*22000或者是一个浮点数1000.0。如果按“4字节”搜1000搜不到可以尝试“浮点数”搜1000.0或者尝试“所有类型”进行模糊搜索。有时甚至需要用到“大于”、“小于”等范围搜索。这是一个试错的过程也是经验积累的地方。3. 案例拆解一道具数量篡改与负数溢出漏洞这是手游中最经典、也最容易被忽视的漏洞类型之一。我们通过一个模拟的“购买道具”场景来还原整个挖掘过程。测试目标某手游商城使用游戏内货币“钻石”购买道具“强化石”单价10钻石/个。测试步骤正常流程抓包开启Burp代理清空历史记录。在游戏中购买1个强化石。确认花费10钻石获得1个强化石。在Burp的HTTP history中过滤寻找包含“buy”、“purchase”、“shop”等关键词的POST请求。很快找到一个POST /api/v1/shop/buy HTTP/1.1 Host: game.example.com Content-Type: application/json ... {item_id: 105, amount: 1, price: 10, currency: diamond, client_time: 1646123456}响应为{code:0, msg:success, data:{new_diamond:990, item_count:5}}(假设原来有1000钻5个石头)。参数分析与篡改测试将这个请求右键发送到Repeater。我们重点关注amount数量和price单价参数。首先测试数量边界修改amount: 0发送。响应可能是{code:1001, msg:invalid amount}。正常服务端做了校验。修改amount: -1发送。关键来了响应可能是{code:0, msg:success, data:{new_diamond:1010, item_count:4}}。 钻石增加了10道具数量减少了1漏洞原理分析服务端的购买逻辑伪代码可能是这样的def handle_buy(user, item_id, amount): item get_item(item_id) # 获取道具信息包括单价 total_cost item.price * amount # 计算总价 if user.currency total_cost: return error(货币不足) if amount 0: # 可能漏了负数校验或者校验不完整 return error(数量无效) user.currency - total_cost # 扣除货币 user.add_item(item_id, amount) # 增加道具 return success()问题出在user.currency - total_cost这一行。当amount -1total_cost 10 * (-1) -10。那么user.currency - (-10)等价于user.currency 10。于是玩家钻石增加了。同时user.add_item(item_id, -1)会导致道具数量减少。这就是典型的“负数溢出”逻辑漏洞。服务端只校验了amount非正数时提示错误但没有考虑到负数的乘法会带来负总价进而导致资产增加的致命问题。深入利用与影响评估如果漏洞存在攻击者可以反复购买amount: -99999瞬间刷取大量游戏货币。更隐蔽的变种是修改price参数为负数。如果服务端信任客户端传来的price那么即使amount1total_cost也可能为负。还需要测试item_id。如果item_id可以被篡改为其他更贵重道具的ID而price保持不变就可能实现“低价购买高价道具”。挖掘技巧与注意事项不要只测-1尝试-999999999大负数、0.5小数、1.0浮点数字符串、1字符串数字、null、true等。服务端对参数类型的解析差异可能导致意外行为。关注响应中的完整状态成功的响应里不仅要看code和msg更要仔细比对data里的所有字段如new_diamond新钻石、item_count新道具数看它们的变化是否符合预期逻辑。结合内存修改辅助定位有时你无法直接通过抓包猜到参数名。比如游戏可能将购买数量放在一个结构体里参数名是data: 加密字符串。这时你可以用CE定位到游戏界面中“购买数量”输入框背后的内存值然后尝试修改这个值比如改成999再进行购买操作。同时抓包观察发送出去的加密数据是否变化。如果变化了说明这个加密数据包含了数量信息。虽然我们无法解密但我们可以用Burp的Repeater将正常购买1个的请求中的加密数据块替换成修改数量为999后抓到的加密数据块直接重放看服务端是否认可。这叫做“盲打”是测试加密接口的常用方法。4. 案例拆解二客户端可控的资源ID与越权访问这类漏洞的根源在于服务端过度信任客户端传来的标识符ID没有在服务端严格校验该ID是否与当前玩家状态、上下文环境匹配。测试场景一个包含多个副本关卡的手游每个副本有唯一ID。玩家通关副本AID101后可以领取通关奖励。测试步骤正常流程进入副本101战斗胜利点击“领取奖励”。抓包看到请求POST /api/v1/dungeon/reward HTTP/1.1 {dungeon_id: 101}响应成功获得奖励。漏洞探测在Repeater中将dungeon_id修改为102一个你未解锁或未通关的副本ID。发送请求。可能出现几种情况最严重响应成功获得了副本102的奖励。这说明服务端只检查了“这个ID的副本是否存在”没检查“玩家是否有权领取这个ID的奖励”。中等响应失败但错误信息暴露了副本状态如{code:1002, msg:dungeon not passed}。这属于信息泄露结合其他漏洞可能有利用空间。安全响应通用错误如{code:1001, msg:invalid request}。漏洞原理与扩展原理服务端代码可能如下def get_reward(user_id, dungeon_id): reward_config get_reward_by_dungeon_id(dungeon_id) # 从配置表读奖励 # 缺失了关键一步检查 user_id 是否已通关 dungeon_id user.add_reward(reward_config) return success()扩展利用遍历ID使用IntruderBurp的爆破模块对dungeon_id进行从1到10000的遍历可能刷出大量隐藏副本或测试服的奖励。关联其他功能同样的思路可以测试“兑换礼包码”修改code参数为其他玩家的有效码、“使用道具”修改item_id为稀有道具ID、“穿戴装备”修改equip_id为高阶装备ID。结合时间戳有些领取每日奖励的接口会校验date参数。尝试修改为明天或昨天的日期看是否能重复领取或预领取。实操心得ID的规律与信息搜集ID不是随机的游戏中的物品ID、副本ID、角色ID通常是连续或按范围划分的。例如普通道具ID范围1000-1999稀有道具2000-2999。通过正常游戏获取几个ID你就能大致猜出整个编码规律。利用游戏内信息很多漏洞来源于“客户端拥有本不该知道的信息”。例如在PVP对战列表里虽然对手的角色形象是客户端绘制的但对手的完整角色数据包括未公开的皮肤、装备可能已经随着列表一起下发到了客户端只是被UI隐藏了。通过抓包或内存分析找到这些数据就可能发现未公开内容泄露的漏洞。关注“状态标志位”很多操作需要前置条件如“任务已完成”、“副本已通关”、“活动已开启”。这些状态位flag很可能以布尔值0/1的形式存储在客户端。用CE搜索这些状态位通过变化筛选完成任务前后搜索值从0变1如果发现能直接修改这些标志位为1再触发领取操作就可能绕过服务端校验。这比直接修改ID更隐蔽。5. 案例拆解三本地校验绕过与时间同步攻击这类漏洞源于游戏将部分关键逻辑的校验放在了客户端服务端要么完全信任客户端的结果要么校验不严。典型场景一抽卡/随机奖励客户端有一个炫酷的抽卡动画然后向服务端报告“我抽到了SSRID是1005”。服务端可能只记录“用户进行了一次抽卡获得了ID为1005的道具”而没有用服务端的随机算法重新计算一次。那么通过抓包拦截这个报告请求直接修改其中的道具ID就能“指哪抽哪”。测试方法正常抽卡一次抓包。找到上报结果的请求可能叫/gacha/result。分析请求体找到疑似奖励ID的字段如reward_id,card_id。在Repeater中修改该ID为一个更稀有的道具ID重放请求。检查游戏内邮箱或背包看是否收到了修改后的道具。典型场景二战斗结果上报在一些弱联网或对实时性要求不高的游戏中战斗可能在本地计算结束后将结果胜负、得分、伤害、获得物品上报给服务端。服务端直接信任并更新数据库。测试方法进行一场战斗无论胜负抓取战斗结束后的上报请求。请求中可能包含win: true/false,score: 1500,drops: [{id:101, count:2}]等字段。尝试修改win: false为truescore: 1500为999999drops数组里增加物品或修改数量。重放请求查看游戏内排行榜、战绩、物品是否异常更新。典型场景三时间戳校验绕过很多操作有冷却时间CD或每日次数限制如“每日可领取一次体力”、“技能冷却5分钟”。客户端通常会本地记录上次操作的时间并显示倒计时。服务端也会校验时间。攻击方式客户端时间篡改这是最简单粗暴的。修改手机或模拟器的系统时间快进到未来。有些游戏的本倒计时是基于本地时间的修改后倒计时立即结束允许你再次操作。此时抓包看服务端是否接受请求。如果服务端只用了客户端上传的时间戳client_time来做时间差计算那么这个漏洞就存在。时间戳参数篡改抓取请求包里面常带有一个timestamp或client_time字段。尝试将这个值改为很久以前的时间比如昨天或者改为未来的时间。服务端如果只用这个时间戳和服务器时间做简单比较可能会被绕过。更安全的做法是服务端只信任自己的服务器时间client_time仅用于日志和粗略的防重放防止同一个包重复发送而不用于核心业务逻辑判断。注意事项时间同步攻击的测试需要谨慎因为频繁修改系统时间或发送异常时间戳的请求很容易触发游戏服务器的风控警报导致临时封禁。建议在测试服或获得明确授权的情况下进行。6. 漏洞挖掘的进阶思路与防御视角当你掌握了基础的数据包篡改和内存定位后可以尝试一些更系统的挖掘思路。同时了解防御方如何思考能帮你更好地发现漏洞。6.1 状态机混乱业务流程的漏洞游戏本质是一个复杂的状态机。漏洞常出现在状态转换不被允许却被客户端强行触发的时候。顺序 bypass一个任务链需要按A-B-C的顺序完成。尝试在完成A后直接抓取完成C的请求包并发送看是否能跳过B。重复提交找到一个会产生收益的请求如领取登录奖励用Burp的Intruder模块连续重放几百次观察奖励是否被重复发放。服务端必须要有强大的幂等性校验如使用唯一token和频率限制。并发竞争这是高阶漏洞。例如购买物品时客户端先发送“扣款”请求收到成功响应后再发送“增加物品”请求。如果两个请求分开攻击者可以利用网络延迟在“扣款”成功后疯狂并发发送“增加物品”请求可能造成物品刷取。这需要服务端将扣款和发货做成一个原子操作。6.2 信息泄露客户端的数据宝藏客户端里往往藏着不该给玩家看的东西这本身就是漏洞。配置文件解包游戏APK/IPA查看assets,res目录下的配置文件json, xml, plist里面可能有未上线活动的配置、内部调试开关、甚至后台管理地址和密码明文。日志输出游戏在开发阶段会留有调试日志发布时可能未完全关闭。通过logcat安卓或连接Xcode查看控制台输出iOS可能会打印出网络请求的URL、参数、服务器返回的错误详情这些是极佳的漏洞挖掘入口。隐藏界面/功能通过修改客户端内存中的某个标志位或者发送特定的封包有可能激活游戏内隐藏的调试菜单、GM命令界面或未完成的功能模块。6.3 从防御者角度思考要挖得深不妨想想开发者会怎么防。签名校验请求参数加上一个由客户端密钥和参数计算出的sign签名。服务器用同样算法验签不一致则拒绝。绕过思路如果这个密钥硬编码在客户端那么通过逆向分析可以提取出来自己构造签名。如果算法太弱如简单的MD5(参数拼接)可能构造哈希冲突。序列号/Token防重放每次请求带一个递增的序列号或服务器下发的临时Token服务器校验其唯一性。绕过思路如果序列号生成规则可预测或Token泄露则可能被利用。关键逻辑服务端化这是治本之策。所有产生资源变动的逻辑、随机数生成、胜负判定必须在服务端完成。客户端只做表现。作为测试者你要做的就是验证服务端是否真的做到了100%校验。你的每一次篡改都是对服务端校验完备性的测试。7. 从测试到报告负责任披露与心得分享发现漏洞不是终点如何清晰、专业、安全地报告给厂商并最终推动修复才是安全研究的价值闭环。一份合格的安全报告应包含漏洞标题简洁明了如“【手游XXX】商城购买接口存在负数数量溢出导致资产无限刷取漏洞”。风险等级通常参考CVSS标准或厂商自定标准根据漏洞利用难度、影响范围全服/单个用户、危害程度破坏经济系统/获取非公开信息来评定如高危、中危、低危。漏洞详情测试环境游戏名称、版本号、测试账号、测试时间。复现步骤按1、2、3...列出详细操作步骤包括使用的工具、篡改的具体参数和值。做到让一个不熟悉该游戏的安全工程师也能按步骤复现。请求与响应附上关键的原始HTTP请求和响应数据可脱敏部分参数用代码块形式展示。漏洞原理简要分析服务端可能的问题代码逻辑。漏洞证明提供截图或短视频展示漏洞利用前后的效果对比如钻石数量变化、获得异常道具。修复建议给出具体的修复方案。例如“服务端应在扣除货币前严格校验amount参数为正整数且结合单价计算出的总价total_cost必须为正数。建议使用服务端配置的单价而非客户端上传的price字段。”其他信息你的联系方式希望如何被致谢等。关于SRC安全应急响应中心国内很多互联网公司都设立了SRC接收外部安全研究员提交的漏洞并给予奖励。对于游戏漏洞陌陌SRC、腾讯安全应急响应中心TSRC、网易安全中心NSC等都是不错的选择它们对游戏业务漏洞的奖励机制相对明确。提交前务必仔细阅读该SRC的漏洞范围规定确保你的测试行为在允许范围内。最后的心得体会手游漏洞挖掘是一条需要耐心、细心和发散思维的路。它不像传统渗透测试有那么多自动化工具可以依赖更多时候靠的是你对游戏业务逻辑的理解和“猜”的能力。刚开始可能一无所获这很正常。每一个抓到的异常包每一个CE扫描出来的奇怪数值都值得深究一下。我个人的习惯是遇到一个游戏先当普通玩家玩上半天熟悉它的核心系统养成、战斗、社交、经济在脑子里画出它的数据流图哪些操作会消耗资源哪些操作会产生资源资源在哪里交换这些交换点就是潜在的漏洞点。然后再拿起工具有的放矢地去测试。记住工具Burp, CE只是你的手和眼真正的大脑是你对逻辑的理解。不要沉迷于寻找“一键秒杀”的漏洞那些高危的、影响面广的漏洞往往就藏在最普通的业务交互背后等待着那个问出“如果…会怎样”的人。保持好奇心保持严谨保持对规则的敬畏你就能在这条路上找到属于自己的乐趣和价值。