二三事

“みんなみんな大好き!”

0%

PJSK 证书抓包教程

文前提示:如果移动端访问时未显示侧栏,可点击左侧按钮以查看侧栏目录。

安卓模拟器证书抓包

前言

考虑到台服、韩服不支持引继,加之不少实体安卓设备 root 困难(无论是否经过自定义证书,至少需要 root 才能解密 SSL 流量),因此台服、韩服的安卓设备用户抓包需要借助模拟器。在 root 的模拟器中可以使用证书解密流量,也可以考虑 eCapture(但依赖内核版本),本部分将一步一步介绍在模拟器上证书抓包的详细流程。

  • 国服安卓版本存在虚拟机检测和代理检测(通过 SSL Pinning),非 root 安卓设备 / 模拟器基本无解;
  • 日服、国际服安卓用户可以考虑引继抓包,但也可以模拟器抓包;
  • 对于苹果用户,建议模块抓包,更加便捷。

注:截止至 2025 年 7 月,主流安卓模拟器内核版本均低于 5.5,eCapture 在这些设备上无法正常工作。经研究,可能是在低于 5.5 版本的内核上 bpf_probe_read_user 无法读取用户态数据导致的,因此本文主要介绍证书抓包的方法。持有实体安卓设备的用户可以考虑 eCapture。

准备工作

软件:

  1. 任意可 root 的安卓模拟器,以 MuMu模拟器 为例
  2. 任意抓包软件,以 Charles 为例
  3. Android SDK Platform-Tools
  4. OpenSSL for Windows(OpenSSL 可以仅下载但暂不安装,稍后按照教程安装)

硬件上只需要一台电脑。以下演示以 Windows 电脑的环境为例,Mac 电脑同理。

读者至少应该有知道如何解压压缩文件的常识,否则还是建议借一台苹果设备或找持有苹果设备的朋友帮忙用模块抓包。

如果不是第一次按本文方法流程抓包,则直接移步最后一步 开始抓包 即可!

Charles 设置

  1. ProxyProxy settings 中勾选 Enable transplant HTTP proxying,端口 Port 保持默认的 8888 不变,点击 Done 保存改动;
  2. ProxySSL Proxying Settings 中确认 Enable SSL proxying 为默认启用状态,随后在 Include 一栏(左栏)点击 +Host 填入 *Prot 填入 *,随后点击两次 Done 保存改动;
  3. HelpSSL ProxyingSave Charles Root Certificate...,选择一个路径保存证书,例如 C:\Users\你的用户名\Desktop\abc.pem,但建议将证书保存至 Android SDK Platform-Tools 路径下。

模拟器设置

  1. 从右上角设置处进入 设置中心

    设置中心

  2. 点击 其他 选项卡,找到 Root 权限,勾选 开启手机 Root 权限,并点击 保存设置

  3. 设置中心磁盘磁盘共享 → 勾选 可写系统盘,模拟器将要求需要重启以应用更改,选择允许模拟器重启;

  4. 重启完毕后,在模拟器内进入 设置(包含在桌面的 系统应用 中)→ 关于手机 → 连续多次点击 版本号,直到提示已进入开发者模式;

  5. 在模拟器内进入 设置系统开发者选项 → 启用 USB 调试

OpenSSL 安装

下载 OpenSSL for Windows 然后执行安装即可,基本上一直点击确定 / 继续即可,只有在下面这一步需要特别注意:

OpenSSL 安装

这里需要手动勾选 The OpenSSL binaries (/bin) directory

最后让我们捐赠的选项可以不选,把默认已选择的 One-time $10 donation to Windows OpenSSL 的选项取消掉,然后点击 finish 即可。

adb 操作(证书安装)

注:MuMu 模拟器默认启用了本地 adb 调试,因此无需设置。

  1. 在这一步需要使用到此前安装的 OpenSSL。

    • 对于 Windows11 用户,按住 WIn + S 搜索 “openssl” 将能够检索到此前安装的 Win64 OpenSSL Command Prompt,选中或回车;
    • 对于其他 Windows 系统版本或检索不到程序的用户,如果此前没有手动修改安装地址,则可在默认安装路径 C:\Program Files\OpenSSL-Win64 下找到 start.bat 批处理脚本文件,双击启动脚本。

    如果顺利,将启动一个终端。在终端中首先执行命令 cd SDK路径,其中 SDK路径 是 Android SDK Platform-Tools 所在的路径,这也是此前推荐的存放证书文件的路径——如果此前将证书存放到了其他位置,则应当 cd 到相应路径。

    接着,执行 openssl x509 -subject_hash_old -in 证书文件名 以获得证书的哈希值,其中 证书文件名 为你此前保存证书时输入的文件名。如果顺利,将得到下面的输出:

    获取证书哈希值

    其中第一行的 c866bff9 即为证书的哈希值(不同的版本、不同的设备上得到的证书哈希值可能不一样)。

    如果你此前将证书存放到了其他位置,在继续下一步前应首先执行命令 cd SDK路径。如果你此前按照推荐将证书文件放在了 SDK 路径下,则直接进行下一步。

  2. 接下来在终端中继续输入命令 adb connect 127.0.0.1:7555 以连接至 adb 服务端口,其中 7555 是 MuMu 模拟器的默认端口,对于别的常见模拟器:

    • 雷电模拟器:adb connect 127.0.0.1:5555
    • 夜神模拟器:adb connect 127.0.0.1:62001
    • 蓝叠模拟器:adb connect 127.0.0.1:5555

    如果顺利,应该能看到返回 connected to 127.0.0.1:7555 字样,然后输入命令 adb devices 再次确认,如果顺利,应该能看到返回结果:

    1
    2
    List of devices attached
    127.0.0.1:7555 device

    ⚠ 注意:如果此处列出了多个结果,例如

    1
    2
    3
    List of devices attached
    127.0.0.1:7555 device
    emulator-5554 device

    则下文中执行的 adb 命令需要通过 -s 参数指定设备!

  3. 将证书文件重命名为 哈希值.0,例如演示中应当将证书重命名为 c866bff9.0,如果重命名是在资源管理器中操作的,则务必注意确保操作时未隐藏后缀名,如果不确定请百度 “Windows 资源管理器如何显示后缀”;

  4. 回到终端,在终端中输入命令 adb root 并回车执行。如果是第一次执行该命令,此时查看模拟器,模拟器内应当弹出询问是否同意操作的弹窗,选择允许,然后再次回到终端;

    ⚠ 注意:如果此前 adb devices 返回了多个结果则命令可能执行失败,此时应改为执行命令 adb -s 目标设备 root,对于 MuMu 模拟器而言通常是 adb -s 127.0.0.1:7555 root,对于其他模拟器则自行根据 adb devices 的结果在 -s 参数后指定目标设备。

    当返回结果 restarting adbd as root 时表示操作成功。

  5. 执行命令 adb push 证书新文件名 /system/etc/security/cacerts;

    ⚠ 注意:如果此前 adb devices 返回了多个结果则命令可能执行失败,此时应改为执行命令 adb -s 目标设备 push 证书新文件名 /system/etc/security/cacerts,对于 MuMu 模拟器而言通常是 adb -s 127.0.0.1:7555 push 证书新文件名 /system/etc/security/cacerts,对于其他模拟器则自行根据 adb devices 的结果在 -s 参数后指定目标设备。

  6. 执行命令 adb shell "chmod 664 /system/etc/security/cacerts/证书新文件名"

    ⚠ 注意:如果此前 adb devices 返回了多个结果则命令可能执行失败,此时应改为执行命令 adb -s 目标设备 shell "chmod 664 /system/etc/security/cacerts/证书新文件名",对于 MuMu 模拟器而言通常是 adb -s 127.0.0.1:7555 shell "chmod 664 /system/etc/security/cacerts/证书新文件名",对于其他模拟器则自行根据 adb devices 的结果在 -s 参数后指定目标设备。

开始抓包

首先获取电脑的本地 IP。在 Charles 上依次按 HelpLocal IP addresses,查看电脑的本地 IP。

  • 例如,我的电脑使用 Realtek 网卡并通过有线以太网接入网络,因此 Realtek PCIe GbE Family Controller 对应的 IP 便是本机当前的 IP,如下图所示:

    以太网网卡本地 IP

  • 或者,若电脑使用无线网卡接入网络,例如我的电脑使用的是 Intel AX200 无线网卡,则 Intel(R) Wi-Fi 6 AX200 160MHz 对应的 IP 便是本机当前的 IP,如下图所示:

    无线网卡本地 IP

回到模拟器的安卓系统内,依次按 设置网络和互联网互联网齿轮图标,然后点击右上角的笔图标、再单击 高级选项 右侧的展开按钮以配置代理。

选择手动代理,在 代理主机名 填入 电脑本地 IP 地址,在 代理端口 填入 8888(若此前在 Charles 中更改了默认代理端口,则填入相应的端口而不是 8888),然后保存。

回到 Charles,如果是初次代理,则 Charles 会询问是否允许连接,选择允许(如果是初次代理但未询问是否允许连接,则在模拟器内进行一次任意网络活动即可触发,例如在浏览器中随便访问一个网站)。现在我们可以解密 SSL 流量了,确保 Charles 处于 recording 与 SSL proxying 状态,即右上角的录制图标和解锁图标应当处于选中、启用的状态:

确保 Charles 正在工作

现在再回到模拟器内,启动游戏并登录即可抓包。

  • 玩家数据抓包 发生在每次从标题界面进入游戏的时候
  • 烤森数据抓包 如果使用普通版抓包,则每次退出到标题界面重新进入烤森才会抓取 如果使用(强制刷新)版抓包,则每次进入烤森都会强制让游戏刷新数据,每次都能够抓取

如果是国服以外的游戏(日服、台服、国际服及韩服),一般此时直接登录游戏即可抓包。

⚠ 注意:国服嵌入了字节跳动 SDK,除检测虚拟机外还会检测代理(SSL Pinning)。如果在开启代理后登录被提示 网络连接异常,请稍后再试,大概率是代理被检测。可以考虑 Frida Gadget 注入后动态 hook 以绕开 SSL Pinning。

以台服为例,登录游戏后回到 Charles,可以看到如下内容:

抓取 Suite

对于台服而言,其中域名 mk-zian-obt-cdn.bytedgame.com 下的 Suite 就是我们的游戏数据(不包含 MySekai),如果是其他服务器则自行寻找相应的 Suite 即可。我们对 Suite 右键保存便大功告成:

保存 Suite

如果使用 HarukiBot / Lunabot,在网站 上传Suite数据 | Haruki 工具箱 上传我们抓取的 Suite 便可以使用组卡功能了!

MySekai 的数据按相同流程操作即可获取,仅仅是 URL 存在区别。

实体安卓设备证书抓包

前言

由于谷歌安全策略的限制,非 root 安卓用户已不能自行安装根证书,因此必须有已 root 的安卓设备才能通过证书抓包。

这里以通过 Magisk 获得 root 权限的小米 8 为例。至于如何获取 root 权限,本文不再赘述,因为不同品牌的设备、不同的 root 方式差别很大,这可能需要读者自行解决。例如,华为手机和澎湃系统的小米手机由于厂商的限制,普通用户基本上无法获取 root 权限;即使是演示所使用的这部小米 8,也因为骁龙 845 的 Fastboot 驱动问题在折腾了数个小时后才成功 root。

鉴于目标读者已经在实体安卓设备上取得 root 权限,因此本部分仅概述关键流程,不再像上文 安卓模拟器证书抓包 那样逐步演示。

⚠ 注意:国服嵌入了字节跳动 SDK,除检测虚拟机外还会检测代理(SSL Pinning)。如果在开启代理后登录被提示 网络连接异常,请稍后再试,大概率是代理被检测。可以考虑 Frida Gadget 注入后动态 hook 以绕开 SSL Pinning。

准备工作

硬件:

  1. 一台电脑,以 Windows 电脑为例
  2. 一部已 root 的实体安卓设备,以通过 Magisk 获取 root 权限的小米 8 为例(Android 10)
  3. 一根用于连接电脑与安卓设备的数据线

软件(不包括 hook 绕过 SSL Pinning 所需的软件,这部分内容请见 ):

  1. 任意抓包软件,以 Charles 为例
  2. Android SDK Platform-Tools
  3. OpenSSL for Windows(OpenSSL 请按上文教程安装)

Charles 的设置、证书文件的更名、OpenSSL 的安装与上文相同。请分别参考上文中的 Charles 设置adb 操作(证书安装) 以及 OpenSSL 安装,同时确保实体安卓设备已激活开发者选项且启用 USB 调试功能。

在实体设备上安装证书

  1. 完成准备工作并将安卓设备与电脑连接后,使用 Android SDK Platform-Tools 在终端中执行 adb devices,如果顺利应当得到以下结果:

    1
    2b74da54        device

    ⚠ 注意:若提示设备 unauthorized,例如 adb devices 返回结果

    1
    2b74da54        unauthorized

    这可能是设备首次与电脑连接而未授权导致的,此时安卓设备上应弹出是否允许当前计算机对设备 USB 调试的弹窗,选择允许 / 确定后再次执行 adb devices,应该就可以得到正确的结果。

  2. 为 Shell 获取权限。执行 adb shell 进入 Android 设备的 Linux Shell 环境,然后执行 su 获取超级用户权限。对于 Magisk,此时安卓设备上可能会弹出是否允许 Shell 获取 root 权限,应迅速点击 “允许”,否则倒计时结束后将默认拒绝提权,提示 Permission denied

    如果未及时同意授予权限,对于 Magisk,可在 Magisk超级用户 中手动允许 Shell 超级用户权限,再回到终端中执行 su

  3. 挂载系统分区为可写。对于 Android 10+ 常见的 system-as-root 方案,system 分区被挂载在根目录 / 下,因此只需要挂载根目录可写,即执行 mount -o rw, remount /;对于 Android 10 以下的版本,可能应改为执行 mount -o rw, remount /system 以挂载 system 分区为可写。

  4. 推送证书。对于已更名为 哈希值.0 的证书文件,依次执行

    • 退出 Linux Shell 环境,执行 exit
    • 复制证书至某个不需要权限的目录,例如 /data/local/tmp,执行 adb push 已更名的证书文件 无需权限的证书存放目录
    • 回到超级用户的 Linux Shell 环境,执行 adb shell,再执行 su
    • 将证书推送至目标位置,执行 cp 存放证书的目录/证书文件名 /system/etc/security/cacerts/
    • chmod 644 /system/etc/security/cacerts/已更名的证书文件
    • chown root:root /system/etc/security/cacerts/已更名的证书文件
    • 重启以应用更改,执行 reboot

开始抓包

剩余步骤与安卓模拟器情况下的操作相同,配置代理再开始抓包即可,因此不再赘述。

⚠ 注意:国服嵌入了字节跳动 SDK,除检测虚拟机外还会检测代理(SSL Pinning)。如果在开启代理后登录被提示 网络连接异常,请稍后再试,大概率是代理被检测。可以考虑 Frida Gadget 注入后动态 hook 以绕开 SSL Pinning。

苹果设备证书抓包

苹果设备的证书抓包与上文安卓设备证书抓包没有任何本质区别,只不过在流程上较之安卓设备简化许多,因为苹果允许用户直接自行安装并信任证书。

因此,苹果如何证书抓包就不过多介绍了,因为没有任何复杂的操作,在按提示安装并信任证书以后,与安卓设备证书抓包流程是一模一样的——配置代理,然后使用代理软件解密 SSL 流量。

苹果设备模块转发抓包

这一方法适用于任何服务器,即使是国服。缺点亦显而易见,你需要一台苹果设备。

我们的目的是抓取来自服务器的原始账户资产数据,例如 Suite 与 MySekai 数据。

本部分主要介绍模块抓包,因为这一方法更不易被检测,全服务器适用,包括限制最多的国服。

准备工作

硬件:

  1. 一部苹果设备
  2. 一部能运行 Python 的设备,最好是搭载任何一个操作系统的电脑(实际上,即使是安卓手机也可以通过 Termux 运行 Python,所以移动设备也可以做到,但可能操作不便)

软件:

  1. Shadowrocket(苹果设备上的其他代理软件也可以实现类似功能,本部分以 Shadowrocket 模块做演示,其他软件灵活调整即可)
  2. Python3

世界计划抓包教程 · 模块抓包 | ルナ茶 完成 Shadowrocket 模块抓包的前置准备工作。

模块配置

魔改模块配置基于 Lunabot烤抓包模块 | ルナ茶 的劳动成果,十分感谢!

只需要把 Lunabot烤抓包模块 | ルナ茶 提供的模块配置的 script-path 字段修改为指向稍后将运行 Python 设备的 URL 即可,譬如我的设备在内网中的 IP 是 192.168.1.12,那么就可以将 script-path 修改为 http://192.168.1.12:8000/upload.js

例如,要抓取国服 Suite 数据,对于 Lunabot烤抓包模块 | ルナ茶 提供的原始 Shadowrocket 模块配置,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!url=https://bot.teaphenby.com/public/modules/cn_suite.sgmodule
#!name=LunaBot世界计划抓包模块-国服(Suite)
#!desc=抓取世界计划国服游戏的Suite数据并上传到bot服务器
#!homepage=https://bot.teaphenby.com/public/tutorial/tutorial.html
#!author=NeuraXmy
#!redirect=1
#!mitm=2
#!total=3

[URL Rewrite]
^https:\/\/submit\.backtrace\.io\/ reject

[Script]
SCRIPT_cn_suite_1 = type=http-response, requires-body=1, binary-body-mode=1, max-size=100000000, timeout=60, pattern=^https:\/\/mkcn-prod-public-60001-1\.dailygn\.com\/api\/suite\/user\/(\d+)$, script-path=https://bot.teaphenby.com/public/scripts/upload.js

[Mitm]
hostname=%APPEND% mkcn-prod-public-60001-1.dailygn.com, submit.backtrace.io

我们将其中的 script-path 字段修改为指向本地服务器的 URL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!name=国服游戏数据本地转发
#!desc=抓取游戏数据并转发到本地计算机
#!author=魔改自NeuraXmy
#!mitm=2
#!total=3

[URL Rewrite]
^https:\/\/submit\.backtrace\.io\/ reject

[Script]
SCRIPT_cn_suite = type=http-response, requires-body=1, binary-body-mode=1, max-size=100000000, timeout=60, pattern=^https:\/\/mkcn-prod-public-60001-1\.dailygn\.com\/api\/suite\/user\/(\d+)$, script-path=http://192.168.1.12:8000/upload.js

[Mitm]
hostname=%APPEND% mkcn-prod-public-60001-1.dailygn.com, submit.backtrace.io

模块的使用方法请移步 世界计划抓包教程 · 模块抓包 | ルナ茶

Python 脚本

编写代码,为模块生成远程 JavaScript 脚本,同时监听来自模块转发的数据。

在使用脚本之前,记得修改 LOCAL_IP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import http.server
import socketserver
import os
import time
import re
from urllib.parse import urlparse
from datetime import datetime

PORT = 8000
LOCAL_IP = '192.168.1.12'

def extract_api_type(url):
match = re.search(r'/api/([^/]+)', url)
if match:
api_type = match.group(1)
if 'mysekai' in api_type.lower():
return 'mysekai'
return api_type.lower()
return 'unknown'

def generate_filename(api_type, original_url):
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
user_id = re.search(r'/user/(\d+)', original_url)
user_str = f"_user{user_id.group(1)}" if user_id else ""
return f"{api_type}{user_str}_{timestamp}_{os.getpid()}.bin"

class RequestHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
if self.path == '/upload.js':
js_content = """
const upload = () => {
$httpClient.post({
url: "http://%s:%d/upload",
headers: {
"X-Original-Url": $request.url,
"X-Request-Path": $request.path
},
body: $response.body
}, (error) => $done({}));
};
upload();
""" % (LOCAL_IP, PORT)

js_content = js_content.strip()
self.send_response(200)
self.send_header('Content-Type', 'application/javascript; charset=utf-8')
self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate')
self.send_header('Pragma', 'no-cache')
self.send_header('Expires', '0')
self.send_header('Content-Length', str(len(js_content.encode('utf-8'))))
self.end_headers()
self.wfile.write(js_content.encode('utf-8'))
return

self.send_response(404)
self.end_headers()

def do_POST(self):
original_url = self.headers.get('X-Original-Url', '')
api_type = extract_api_type(original_url)
filename = generate_filename(api_type, original_url)
content_length = int(self.headers['Content-Length'])
received_data = self.rfile.read(content_length)

with open(filename, 'wb') as f:
f.write(received_data)

print(f"Saved [{api_type.upper()}]: {filename}")
print(f"Source URL: {original_url[:100]}{'...' if len(original_url) > 100 else ''}")
print(f"File Size: {len(received_data)/1024:.2f} KB\n")

self.send_response(200)
self.send_header('Content-Type', 'text/plain; charset=utf-8')
self.end_headers()

if __name__ == "__main__":
print(f"Universal Data Receiver running at http://0.0.0.0:{PORT}")
print("File naming format: [api_type]_[user]_[timestamp]_[pid].bin\n")
try:
with socketserver.TCPServer(("", PORT), RequestHandler) as httpd:
httpd.serve_forever()
except KeyboardInterrupt:
print("\nServer stopped by user")

开始抓包

启动 Python 脚本,启用模块并登录游戏,抓取的数据将保存在 Python 脚本当前的工作路径。

API 解密

首先安装由 mos9527 编写的工具库 sssekaipip install -U sssekai)。

然后以 Suite 为例,执行命令 sssekai apidecrypt mysuite.bin suite.json 即可解密并得到明文数据。

逆向工程请见 Project SEKAI 逆向 - 笔记归档 | mos9527

后记

参考文档 1:MuMu模拟器如何将系统磁盘更改为可读写? | MuMu模拟器

参考文档 2:如何安装证书&抓包? | MuMu模拟器

参考文档 3:世界计划抓包教程 | ルナ茶

参考文档 4:Project SEKAI 逆向 - 笔记归档 | mos9527

作者:等待雨晴,作于 2025-7-23,更新于 2025-7-27