MAX 如何帮助 Roskomnadzor 构建铁幕:VPN 检测、IP 收集和对Gosuslugi的检查 (1)

来源: https://pikabu.ru/story/kak_max_pomogaet_rkn_stroit_zheleznyiy_zanaves_vpndetekt_sbor_ip_i_proverki_na_gosuslugi_13874594

研究的第二部分已经发布:《MAX 的隐藏功能 #2:这款即时通讯软件内置了用于通话的关键词识别系统》。

在第三部分,我计划解答评论和论坛中最常被问到的问题。如果您有任何疑问,请告诉我;我会拿着代码一步步为您解答。

  1. MAX 是否在后台监听麦克风?

  2. MAX 是否像 Telegram 一样具有端到端加密 (E2E)?

  3. 未经授权是否可以获取聊天记录中的照片?

  4. MAX 在安全性方面与 Telegram 有何不同?

这些问题以及其他问题的答案将在第三部分中给出。

该研究的第三部分已经发表:

MAX #3 隐藏的秘密:MAX 通信是一本公开的书

TL;DR(太长不看版)

如果您感兴趣,下面有详细的分析。如果您想查看代码本身,请向下滚动到“第二部分”。

这是该系列文章的第一篇。

我正在对 MAX 即时通讯软件进行全面的安全审计。本文主要介绍其 IP 地址收集系统和 VPN 检测功能。如果大家对这个话题感兴趣,后续还会推出更多文章。

  • 静默呼叫 :服务器如何在不响铃或显示用户界面的情况下发起呼叫
  • 通过麦克风输入关键词 :MAX 内置了 ASR(语音识别)、语音识别和关键词检测功能,其模型由服务器提供。
  • 端到端加密?不,你肯定没听说过 :消息、联系人和令牌都是以明文形式存储的。

背景

我从事移动应用安全审计工作。最近我接手了 MAX 的调查,这是一款来自 VK 的即时通讯应用,它被大力宣传为“Telegram 的替代品”。我从 Google Play 和 RuStore 下载了 APK 文件,使用 JADX 对其进行了反编译,然后开始深入研究代码。

在分析网络流量时,我注意到一个奇怪的现象:每次启动应用时,它都会访问checkip.amazonaws.com。这是亚马逊的一项服务,用于获取你的外部 IP 地址。为什么这款即时通讯应用需要通过亚马逊获取我的 IP 地址呢?

我顺着线索找到了一个完整的子系统,而不是一个漏洞。它经过精心设计、调试,具备防检测保护和保证数据传输到服务器的功能。这不是简单的分析,而是网络监控基础设施。

第一部分:我的发现

不包含代码,以下是概要:该应用程序的功能、其运行原因以及存在的风险。

  1. 六种查找您IP地址的服务

每次打开 MAX 时,该应用都会秘密检测您的外部 IP 地址。它使用六项服务来实现这一点:

六个服务——只为完成一项任务。为什么要这么多?为了可靠性:如果一个服务没有响应,另一个服务会接替。每次的顺序都是随机打乱的——这使得基于网络模式检测请求变得更加困难。

但最有趣的是:这些服务的地址隐藏 在代码中。它们不是像“ https://ip.mail.ru/ ”这样的常规字符串,而是数字数组:

{104, 116, 116, 112, 115, 58, 47, 47, 105, 112, 46, 109, 97, 105, 108, 46, 114, 117, 47}

每个数字都是一个字母的 ASCII 码。104 代表“h”,116 代表“t”,以此类推。这样就得到 了https://ip.mail.ru/这样的网址。标准的 ASCII 码搜索(例如 grep、strings)永远找不到这些网址。这不是漏洞或优化问题,而是有意隐藏

还有一点:该应用程序使用底层原始套接字(java.net.Socket)发送请求,而不是标准的 OkHttp 库(后者很容易被拦截)。这绕过了网络拦截器、代理和流量监控。

简单来说: 一个典型的应用程序如果出于合法目的(例如,为了提供地理位置信息)需要你的 IP 地址,它会通过标准库发出一个请求。而 MAX 则通过隐藏的 URL,经由原始套接字发出六个备份请求。这可是恶意软件级别的技术。

2. 五项辅助功能检查:该应用程序会确定您是否身处俄罗斯

在收集 IP 地址的同时,该应用会检查五个主机的可用性,以确定您的手机是否可以连接到它们:

Gosuslugi 出现在这份列表中并非偶然——MAX 集成了通过统一身份识别和认证系统 (ESIA) 进行身份验证的功能(更多内容将在后续文章中介绍)。然而,此次检查并非针对身份验证,而是针对基本的网络访问性 :您的设备是否可以访问gosuslugi.ru?检查结果(简单的“是”或“否”)将与 VPN 标志和 IP 地址一起发送到服务器。

为什么?综合所有答案,VK才能了解全貌:

  • 公共服务可用 + 无需 VPN + 俄罗斯运营商 = 俄罗斯普通用户
  • 政府服务不可用 + 使用 VPN + 俄罗斯运营商 =用户绕过封锁
  • 公共服务不可用 ,但其他国外服务可用 + 无 VPN + 未知运营商 =用户绕过封锁 + 用户绕过 VPN 验证

3. VPN检测:MAX能够识别并报告

MAX 通过标准的 Android API 检测 VPN。它适用于任何 VPN——NordVPN、WireGuard、OpenVPN Enterprise 以及内置 VPN。

接下来会发生什么: 每次您打开应用时,您的 VPN 状态、IP 地址和其他数据都会被发送到 VK 服务器。这一点已通过代码分析和流量监控得到证实。VK 可以准确地知道您是否正在使用 VPN、何时使用以及使用的 IP 地址。

此外,代码中还发现了一个名为“无需 VPN 通话更佳”的小部件——该面板应在启用 VPN 的通话期间显示。该应用程序的资源包含俄语和英语字符串。我尚未在真机上动态复现此问题,但代码和资源确实存在。

4. 每次开业时填写完整的档案

每次打开 MAX(或应用程序在后台启动)时,它都会收集以下数据并将其发送到 VK 服务器:

  • 您的 MAX 账户 IDOK.ru ID) - 来自您的账户
  • 会话 ID - 来自应用程序
  • 授权密钥 - 来自 HTTP 请求
  • 外部 IP 地址 - 通过 6 种 IP 服务之一
  • 移动运营商 - 来自 SIM 卡(例如“MTS”)
  • 连接类型 :WiFi / 4G / 5G
  • VPN 是否已启用? — 通过系统 API
  • 5 项服务的可用性 - 通过网络检查
  • 时间 - 系统时钟

这是发送到服务器的数据包的样子(根据代码重建):

用户: #123456789(账户 MAX)

时间: 2026年4月12日 14:30:00

IP地址: 185.xxx.xxx.xxx

VPN:

运营商: MTS

连接方式: WiFi

服务: gosuslugi.ru — 是,Google — 是,API MAX — 是,CDN 调用 — 是,推送 — 是

这不是匿名分析。每条帖子都与您的帐户唯一关联 。由于 VK 集团拥有OK.ruVK.comMail.ru,您的 MAX 帐户与您的 VKontakte 个人资料、电子邮件和其他生态系统服务相关联。

规模

MAX拥有1.07亿注册用户7700万日活跃用户 (VK数据,2026年3月)。从2025年9月起,该应用将预装 在俄罗斯和白俄罗斯销售的所有安卓设备上。

想象一下:每天有7700万人打开MAX浏览器。每次打开时,他们都会收到一个包含其IP地址、VPN状态和运营商信息的数据包。这相当于每天数千万个数据点 :谁在使用VPN,谁没有使用,使用哪个IP地址,通过哪个运营商,以及被屏蔽的服务是否可用。所有信息都是实时生成的。

这不仅仅是“单一应用分析”,而是一个全国性的网络环境监控基础设施。随着俄罗斯联邦通信、信息技术和大众传媒监管局(Roskomnadzor)实施新的网络封锁,HOST_REACHABILITY 数据变得更有价值——它能显示 封锁是否有效 以及哪些攻击者绕过了封锁。

5. 代码中的 Telegram 和 WhatsApp 域名

存储活跃服务地址的同一个文件也包含竞争对手的域名:main.telegram.orgmmg.whatsapp.net

当前版本中未使用它们。

我并不是说 Telegram 的可用性检查器“可以立即启用”。但竞争对手的域名与正在运行的数据收集系统存储在同一个文件中,这一事实值得注意。

此外,MAX 会检查您的设备上是否安装了 Telegram,方法是搜索三个应用版本:主应用、测试版应用和网页版。官方说法是,这是为了检测“分享”按钮(Telegram 排在第一位)。

详细信息请见下方技术部分。

6. 服务器控制一切

整个追踪系统通过 VK 的推送管理系统 (PMS)由一个服务器标志 控制。这类似于远程配置,但功能更强大:PMS 值通过持久的 TCP 连接传输并立即生效。

实际意义如下:

  • VK 可以通过单个交换机为所有用户 启用IP 地址收集功能。
  • 可以针对特定用户 启用(定向跟踪)
  • 针对特定群体 启用(A/B 测试、特定操作员、特定地区)
  • 可以在检查之前禁用 (例如,在 Google Play 审核之前)
  • 这一切都无需更新应用程序。

该功能甚至还有一个专用的线程池 ,名为“主机可达性”。这不是附加的分析功能——它是一个独立的子系统,被设计成一个单独的组件。

还有一点:数据永不丢失 。如果发送失败,请求会重试三次。如果三次尝试后仍然失败,数据会保存到本地存储,稍后再发送。当应用最小化时,所有累积的事件都会被强制清除。VK 设计此功能是为了确保100% 收集到的数据 都能被检索到。

7. 法律现实

我不会猜测VK是否与情报机构共享这些数据。相反,以下是事实。

VK/MAX 是根据联邦第 149 号法律注册的“信息传播组织者”。根据《亚罗瓦亚法》(联邦第 374 号法律),VK/MAX 必须存储通信元数据一年,并应要求提供。必须存储的元数据包括 IP 地址、用户 ID、时间戳和 VPN 状态。联邦第 276 号法律(2024 年修订版)加大了对使用 VPN 绕过封锁的处罚力度,而 HOST_REACHABILITY 可以提供 VPN 使用者、使用时间和使用地点的精确证据。

但是,根据联邦法律第152号,IP地址被视为个人数据。收集此类信息无需征得 用户的同意。

重要提示: 我并非指责 VK 滥用这些数据。我只是指出,其代码会收集这些数据,法律要求必须存储和共享这些数据,而用户却无法选择退出。最终结论由您自行判断。


第二部分:证据(供想要核实的人参考)

以下是反编译后的 APK 的具体代码。所有文件和字符串引用均来自 JADX 反汇编器,APK 版本为 26.12.1。

2.1 URL 的隐藏方式:通过 int 进行混淆

文件: defpackage/ f28.java:43

以下是地址https://ip.mail.ru/在 MAX 代码中的显示方式:

// f28.java:53 x = new int {104, 116, 116, 112, 115, 58, 47, 47, 105, 112, 46, 109, 97, 105, 108, 46, 114, 117, 47};

手动解码:

104 → h 116 → t 116 → t 112 → p 115 → s 58 → : 47 → / 47 → / 105 → i 112 → p 46 → . 109 → m 97 → a 105 → i 108 → l 46 → . 114 → r 117 → u 47 → /

结果:https://ip.mail.ru/

f28.java中的所有 16 个 URL都采用这种编码方式。解码由rd4.java延迟执行(IP 服务使用第 17-22 个用例,检查使用第 12-16 个用例)。

这并非代码压缩或优化。ProGuard 会混淆类名和方法名,但不会将字符串字面量转换为整数数组。这是开发者手动完成的工作 ,旨在使 URL 对静态分析工具(例如 grep、strings、jadx string search)不可见。

2.2 IP 地址收集:w18.java

文件: defpackage/w18.java:139-140

// 将所有 6 个 IP 服务收集到一个列表中

列表 o1 = rz3.o1(sz3.U(

f28.o.getValue(), // Yandex IPv4

f28.q.getValue(), // Yandex IPv6

f28.s.getValue(), // ifconfig.me

f28.u.getValue(), // api.ipify.org

f28.w.getValue(), // checkip.amazonaws.com

f28.y.getValue())); // ip.mail.ru

// 洗牌 - 每次都随机排序

Collections.shuffle(o1);

接下来,执行顺序搜索:第一个返回 IP 地址不等于 127.0.0.1 的服务获胜。其余服务不予查询。

关键点: 请求是通过 java.net.Socket + ByteArrayOutputStream(原始 TCP)发送的,而不是 OkHttp。OkHttp 是 Android 的标准 HTTP 库,很容易被代理(例如 Burp Suite、Charles、mitmproxy)拦截。原始套接字可以绕过所有这些限制。在设备上配置了代理的研究人员将无法 在标准工具中看到这些请求。

2.3 组装数据包:c28.java

文件: defpackage/c28.java:100-116

ro9 ro9Var = new ro9 ();

// 主机可用性检查结果:{hostname: true/false}

ro9Var.put(“hosts”, vibVar);

// 移动运营商:“25001:MTS”

ro9Var.put(“运算符”, str);

// 连接类型:WiFi (0) 或移动网络 (1)

ro9Var.put(“connection_type”, new Integer ( nc4Var.g() ? nc4Var.b().a : 1));

// 外部 IP 地址

如果 (str2 != null) {

ro9Var.put(“ip”, str2); }

// VPN标志

if (((nc4) b19Var.getValue()).e()) {

ro9Var.put(“vpn”, new Integer (1)); }

// 发送:类型 HOST_REACHABILITY,事件 GET_HOST_REACHABILITY

lg9.h(lg9Var, “HOST_REACHABILITY”, “GET_HOST_REACHABILITY”, ro9Var.b(), 8);

发送到服务器的重构 JSON:

{

“type”: “主机可达性”,

“事件”: “GET_HOST_REACHABILITY”,

“userId”: 123456789,

“sessionId”: 987654321,

“时间”:1712847600000,

“params”: {

“hosts”: {

" api.oneme.ru ": true,

" gstatic.com ":true,

" calls.okcdn.ru ": true,

" gosuslugi.ru ":true,

" mtalk.google.com ":真

},

“操作员”: “25001:MTS”,

“connection_type”: 0,

“ip”: " 185.xxx.xxx.xxx ",

“vpn”:1

}

}

目标地址: api.ok.ru/api/log/externalLog(POST,gzip,通过 session_key 进行身份验证)。

2.4 保证送达

文件: defpackage/lg9.java:49-103

发送 → 出错? → 重试 1 → 出错? → 重试 2 → 出错? → 重试 3 → 出错? → 保存到本地存储 (vgh) → 连接后稍后发送

最多重试 3 次(第 66 行:intValue() > 3)。3 次失败后,数据将保存到本地 vgh 存储(第 68-77 行)以便稍后发送。当应用程序最小化时,将强制刷新(r87.java:68)。

结果:无论网络 连接如何, 您的数据都会发送到 VK 服务器。没有网络?它会等待。服务器崩溃?它会重试。您关闭了应用?下次启动应用时,数据将会发送。系统设计确保不会丢失任何事件

2.5 “使用 VPN 更佳”小部件和 VPN 事件分析

文件: one/me/calls/ui/ui/call/panels/ VpnPanelWidget.java,defpackage/ dy1.java:46

// dy1.java:46 — 通话期间检测到 VPN

VpnPanelWidget widget = new VpnPanelWidget ();

// 已使用“call_vpn_panel_widget_tag”标签添加到用户界面

VPN 通知状态

文件: defpackage/ l4j.java

l4j.a // 已启用 — VPN 已启用

l4j.b // 已禁用 — VPN 已禁用

l4j.c // USER_IGNORED — 用户关闭了警告

l4j.d // 未知 — 无法确定

显示横幅时的分析事件:类型为 VPN 的 BAD_CONNECTION_ALERT(m82.java:43

2.6 非活跃 Telegram 和 WhatsApp 可用性检查

文件: defpackage/ rd4.java:87

case 15: return hb0.a(f28.l); // main.telegram.org

案例 16:返回 hb0.a(f28.m); // mmg.whatsapp.net

文件: defpackage/ c28.java:150 —活动 检查列表:

列表 U = sz3.U(

" api.oneme.ru ",

(String) f28.b.getValue(), // gstatic.com

(String) f28.f.getValue(), // calls.okcdn.ru

(字符串)f28.h.getValue(),// gosuslugi.ru

(String) f28.d.getValue()); // mtalk.google.com

f28.l(Telegram)和 f28.m(WhatsApp)存在于rd4.java解码器中,但不在c28.java 的活动列表中。此外,在 smali 字节码层面,这些字段没有对应的 move-result-object / sput-object——解码结果会被丢弃。这部分代码是无效的,或者说是一个存根,不提供任何实际功能

f28 中也存在不活跃的华为推送域名:

2.7 当它有效时

主要触发条件: 每次应用切换到前台时(您打开或返回 MAX 时):

r87.java:82 → 应用程序前台回调

→ 检查 PMS 标志(e28.java:142-146)

→ 如果已启用且没有活动检查

→ 启动 c28(完整流程)

此外: 登录时(AccountInitializer),后台唤醒时(one.me.background.wake.HostReachabilityChecker)。

甚至在授权之前: y18.java:66-100 中的代码表明,网络检查会根据会话状态(未连接/已连接/已登录)以不同的模式运行。网络指纹识别在用户登录之前 就开始了。

结论

MAX 是一款预装在超过 1 亿台设备上的应用程序,它具有一个系统,每次打开它时都会执行以下操作:

  • 通过 6 项隐藏服务查找您的 IP 地址
  • 确定 VPN 是否已启用
  • 检查您的网络是否提供俄罗斯和国际服务。
  • 将所有这些都链接到您的真实账户
  • 保证数据传输到 VK 服务器——无损且可重复传输

该系统由服务器管理,无需更新应用程序即可为所有人、用户组或特定用户启用。服务地址经过特殊隐藏,请求路由绕过标准网络库。

我区分代码中的功能已确认的使用情况 。服务器端标志可能并非对所有人启用。但代码就在这里,请查看。


接下来会发生什么?

IP地址收集和VPN检测只是冰山一角。我在MAX代码中还发现了许多其他有趣的东西,计划在以后的文章中分享。

请在评论区写下您希望首先分析的内容。


技术参考

APK 版本: 26.12.1 (6679)开发者: OOO “COMMUNICATION PLATFORM” (VK Group, Moscow)方法: 反编译 APK 的静态分析 (JADX) SHA256: 47420c41b742e67daae85ee75e0aa76935736c7491bab9da942c012e2115ffa1

关于文件名的说明: APK 中的大多数类都使用 ProGuard 进行了混淆。您不会看到像HostReachabilityManager.java这样有意义的文件名,而是会看到f28.javac28.javaw18.java等文件名。在 JADX 中,这些文件名会被放入 defpackage/ 目录(默认存放没有指定包名的类的目录)。这对于混淆后的代码来说是正常的。具有正常文件名的类(例如 VpnPanelWidget.java)会保留其原始路径。我在这里会指定它们的完整路径。

如果发现错误,请告诉我,我会改正。我注重的是准确性,而不是夸大其词。