写在前面

逆向方面我是自娱自乐的半吊子水平,文章中如果有一些理解上的错误,希望各位大佬不吝赐教~

目标

破解扫雷小游戏,一键通关

思路

1. 算法识别雷区

文字/图像识别扫雷界面,根据扫雷玩法自动计算雷区分布。如云顶之弈自动下棋外挂

优点

  • 不用注入内存,不容易被反外挂检测到
  • 不受游戏数据结构变动影响

缺点

  • 考验开发+算法能力,需要反复调试才能实现完美的扫雷效果
  • 过度依赖识别效果,并且有的时候一些雷没法通过推理判断出位置,因此准确率上可能存在一定偏差

2. 读内存识别雷区

通过分析内存数据,直接将雷区信息对应的数组信息读取出来。如LOL透视外挂

优点

  • 准确率高
  • 只需要简单分析内存数据即可,难度相对较低

缺点

  • 需要注入进程读内存,可能会被反外挂程序检测
  • 需要基于内存数据实现自动点击
  • 游戏数据结构变动会导致数据读取异常
  • 有时候需要计算指针找出基地址

3. 改游戏数据

绕过游戏本身的限制,直接修改雷区内存或外部数据文件,将雷的数量改为1,从而降低游戏难度。如吃鸡爆头挂、除草挂

优点

  • 过程简单粗暴,可以使用CE傻瓜式修改

缺点

  • 只能基于数据做修改,自由度较低
  • 需要注入进程写内存,容易被反外挂+反作弊软件检测
  • 数据修改不当可能会引起冲突,导致游戏崩溃

4. 调用关键API

省略中间过程,直接调用关键API。如原神瞬移挂,DNF全屏秒杀挂、人偶挂

优点

  • 自由度高,可以任意组合游戏API实现各种效果
  • 分析的过程中可以更透彻的了解游戏底层逻辑

缺点

  • 需要注入进程远程调用函数,容易被反外挂软件检测
  • 需要掌握逆向分析能力
  • 调用不当可能会引起冲突,导致游戏崩溃

实战

WX20210308-234429.png

找雷区

基于第一个方块的值,使用模糊查找雷区

WX20210308-234451.png

小技巧:根据临近变量的内存地址找到雷区内存地址

WX20210308-234501.png

解析数据

WX20210308-234509.png

自动点击

使用spy++记录消息事件

代码实现

'''
Author      : LzSkyline
Date        : 2021-02-23
Description : 读内存自动扫雷
'''


from ctypes import *
import win32process
import win32api
import win32gui
import win32con


# 访问权限,权限越高越容易被检测
PROCESS_ALL_ACCESS = 0X1F0FFF # 最高权限
kernel32 = windll.LoadLibrary("kernel32.dll")


def wg():
  # 根据标题获取窗口句柄
  window_handle = win32gui.FindWindow(None, "扫雷")
  if not window_handle:
    return

  # 根据窗口获取进程信息
  _, pid = win32process.GetWindowThreadProcessId(window_handle)
  if not pid:
    return

  # 获取进程操作权限
  phand = win32api.OpenProcess(PROCESS_ALL_ACCESS, False, pid)
  if not phand:
    print("进程打开失败!")
    return

  mem_width = 32
  # 创建一个有符号int类型
  game_width = c_int(0)
  # 读进程内存,将值写入指定地址
  kernel32.ReadProcessMemory(int(phand), 0x01005334, byref(game_width), 2, None)
  game_height = c_int(0)
  kernel32.ReadProcessMemory(int(phand), 0x01005338, byref(game_height), 2, None)

  # 创建一个指定大小的字节数组
  addr = create_string_buffer(mem_width * game_height.value)
  kernel32.ReadProcessMemory(int(phand), 0x01005361, byref(addr), mem_width * game_height.value, None)

  for i in range(game_height.value):
    for j in range(game_width.value):
      current = hex(addr.value[i * mem_width + j])
      print(current, end=" ")
      if current == "0xf":
        # 发送Windows消息,模拟点击事件
        win32api.PostMessage(window_handle, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, win32api.MAKELONG(19 + j * 16, 63 + i * 16))
        win32api.PostMessage(window_handle, win32con.WM_LBUTTONUP, 0, 0)
    print()
  # while True:
  #   kernel32.WriteProcessMemory(int(phand), 0x03128064, byref(c_int(sun)), 4, bytes(c_int(0)))


wg()

扩展1:修改手机单机游戏

1. Root后直接改内存

  • Game Guardian
  • Xposed框架 + 插件注入

2. 在虚拟空间中Root后直接改内存

  • VirtualExposed:轻量(启动快占用低)、无广告、不稳定(闪退)
  • 平行空间:稳定、有广告
  • 虚拟大师(VMOS):手机上的VMware、重量级(启动慢占用高)、稳定、扩展性强

3. 逆向分析APP,修改关键代码

  • smali文件(大多数APP应用)
  • so文件(cocos2dx引擎开发的游戏)
  • Assembly-CSharp.dll文件(Unity3D引擎开发的游戏)
  • config json文件(大多数单机游戏)

扩展2:网络游戏破解思路

1. request请求并发

涉及到数据库操作可尝试

优点

  • 因为并未修改数据所以不会被签名校验拦截,不需要考虑加密算法
  • 可用来刷物品、刷点券

缺点

  • 现在游戏关键操作加锁已经是基本功了,很少会有并发漏洞
  • 行为非常明显,容易被风控系统拦截

2. request请求修改

当request请求未做签名校验时可尝试(还可以试一下SQL注入,可能有惊喜)

优点

  • response数据被加密管我什么事
  • 修改成功后数据会存储到服务器上,删除本地客户端也不会丢失
  • 可以用来改物品使用数量、改充值金额

缺点

  • 特征非常明显
  • 如果request请求有签名校验,需要逆向分析加密算法

3. response数据修改

当response请求没有被加密时可尝试

优点

  • request签名校验失败关我什么事
  • 不用分析&修改客户端代码就可以解锁更多功能(本地VIP,回合制战斗加速、免广告特权)
  • 可以用来刷物品数量、刷点券余额
  • 与网络不好时的特征很相似,不容易被发现

缺点

  • 每次客户端重新访问服务端接口更新数据时都需要修改,否则数据会还原
  • 如果response数据被加密,需要逆向分析加密算法
如果觉得我的文章对你有用,请随意赞赏