📦WebToApp 源码深度解析
把"开源神器 WebToApp 一键网站转 APK"这个标题党,用 522 个 Kotlin 文件的字节级证据,拆给你看
起因:营销号推"开源神器 WebToApp,秒杀打包工具",怀疑是又一个 WebView 套壳。
第一轮误判:看标题以为就是 50 行 WebView template,等级对齐到 GitHub 满大街的 wrap 壳。
翻源码后:打脸。core/apkbuilder/ 是国内罕见的字节级 APK 操作实现——自写 AXML / ARSC 编辑器,v1/v2/v3 签名,流式 ZIP + ZipAligner。但也发现它是商业产品的引流版。
§0概述与真相
| 指标 | 值 |
|---|---|
| 仓库 | https://github.com/shiahonb777/web-to-app |
| 创建 / 最后推送 | 2025-11-26 / 2026-04-12(活跃) |
| Stars / Forks | 2465 / 353 |
| 仓库大小 / 源码量 | 19.6 MB(仓库)/ 33 MB · 522 个 .kt · ~10 万行 |
| 提交数 | 仅 14 次——典型代码 dump,不是社区开发 |
| 版本 | v1.9.5(versionCode 32) |
| 商业背景 | 硬编码后端 https://api.shiaho.sbs · Google Play 6 档订阅 · 本地激活码 |
§1技术全景
core/ 下 43+ 个子模块,按体积排序 Top 10:
§2★ APK 构建引擎(全项目最高价值)
这是 WebToApp 真正与众不同的地方。别的"套壳工具"都是调 Android Gradle Plugin + AAPT,打出一个跟开发者模板没区别的 APK。WebToApp 完全不依赖 Android SDK 的构建链,而是在运行时用纯 Kotlin 代码直接操作 APK 字节——这意味着可以在手机上给手机打包 APK。
流水线总览
AXML 编辑器 · 自实现 Binary XML 解析
core/apkbuilder/AxmlEditor.kt:26-248
- AndroidManifest.xml 在 APK 里是二进制 XML(AXML),格式没有公开文档,只能逆 Android 源码
frameworks/base/tools/aapt/ - 支持 UTF-8 和 UTF-16LE 双编码(旧 APK 常为 UTF-16,新 APK 多为 UTF-8)
- 修改字符串必须保持字节长度——因为字符串池偏移表不可改,否则 AXML 解析会崩溃。作者在注释里明确写了这点
- 支持清除
android:testOnly="true"标志(让 Play Protect 不弹"应用未完全发布"警告) - 支持权限修复、组件
android:name相对路径恢复为绝对类名
ARSC 字符串池编辑
core/apkbuilder/ArscEditor.kt:32-79(完整注释在 10-128 行)
resources.arsc是 Android 的编译后资源表,包含所有字符串、数值、布局引用- 作者在前 100 多行注释里详细写了 ARSC 二进制格式(Type Chunk / String Pool / Package Chunk / Config Chunk)——说明是真的啃过
frameworks/base/libs/androidfw/ResourceTypes.cpp - 实现"保长度替换":修改字符串时对齐到原字节长度,不动 offset 表
- 这样才能在不重算整个资源表的前提下修改应用名称、默认文案
流式 ZIP 写入 + 4 字节对齐
core/apkbuilder/ZipUtils.kt:92-122 · core/apkbuilder/ZipAligner.kt:23-156
两个核心问题:
- 大文件不能一次加载到内存——主 APK 里可能带 Node.js/Python 二进制 40MB+,全读进
ByteArray会 OOM(Android 单进程堆上限 192-512MB) - APK 要求 4 字节对齐——但 Java 的
ZipOutputStream不暴露内部流位置,无法在线对齐
方案:
- ZipUtils.kt:92-122:10MB 以上的 entry 强制走
STORED(无压缩)模式,先扫一遍文件算 CRC32,再流式写入——避免把整个文件读到内存。ApkBuilder.kt:1022 注释明确:"Node binary ~40MB, use streaming write" - ZipAligner.kt:23-156:自己实现一个"后对齐"写入器——边写边手动维护字节游标,在每个 entry 头前补
Extra Field填充到 4 字节边界。这是绕 Java API 限制的正确姿势
apksig 集成 · 正道签名
app/build.gradle.kts:210:com.android.tools.build:apksig:8.3.0
core/apkbuilder/JarSigner.kt:8:import com.android.apksig.ApkSigner
- 用的是Google 官方 apksig 库——不是自己手搓的 RSA-SHA256(那种山寨实现很容易被 Play 拒绝)
- 支持 v1 + v2 + v3 三种签名方案(Android 7+ 强制 v2,Android 11+ 用 v3)
- 支持 Android KeyStore 和 PKCS12 证书两种密钥源切换,不强制用户把私钥明文放本地
§3浏览器双核引擎
core/engine/BrowserEngine.kt:11-122 定义统一接口,loadUrl() / evaluateJavascript() / canGoBack() / getShields()。
GeckoView · 真的把 Firefox 塞进 APK
core/engine/GeckoViewEngine.kt:30-80
import org.mozilla.geckoview.GeckoRuntime
import org.mozilla.geckoview.GeckoSession
// ...
private val runtime = GeckoRuntime.create(context, runtimeSettings) // line 63
- 真·依赖
org.mozilla.geckoview包,不是字符串常量 - 配置了反追踪 / SafeBrowsing / Cookie 隔离(第 52-61 行)
- 但
libxul.so不内置——build.gradle.kts:137-142 明确exclude,由GeckoEngineDownloader.kt首次启动按需下载(~150MB,太大不能内置)
System WebView · 备选路径
core/engine/SystemWebViewEngine.kt 提供完整 fallback,走 Android 自带 WebView(Chromium 内核)。
§4多运行时集成
这是最反直觉的部分——一个"套壳工具"竟然真的打包了 Node.js / PHP / Python / Go 运行时。
| 运行时 | 实现 | 评价 |
|---|---|---|
| Node.js | core/nodejs/NodeRuntime.kt + JNI 桥 NodeBridge.kt调用 node::Start() 进程内跑 |
真 · 但单进程仅能启动一次(nodejs-mobile 限制,18-26 行注释写明) |
| PHP | core/php/PhpAppRuntime.kt:70-72 从 nativeLibraryDir 加载build.gradle.kts:250-284 downloadPhpBinary task 从 pmmp/PHP-Binaries 下载 PHP 8.4 |
真 · 绕 SELinux execute_no_trans 正确姿势 |
| Python | PythonRuntime.kt + ProcessBuilder下载 CPython,支持 Flask / Django / FastAPI |
真 |
| Go | GoRuntime.kt + ProcessBuilder |
轻度 · 只支持预编译的 Go 程序 |
| "Linux" | core/linux/PerformanceOptimizer.kt(2809 行) | 名不副实 · 其实是纯 Kotlin 写的图片/JS/CSS minify 工具,跟 Linux 没关系 |
nativeLibraryDir 起? —— Android 的 SELinux 策略 untrusted_app_* 域对 /data/data/*/files 下的可执行文件施加 execute_no_trans,直接 exec 会被 deny。但 nativeLibraryDir(即 /data/app/~~xxx/lib/arm64)是可执行的。所以把 PHP 二进制当作 libphp.so 放 jniLibs/,运行时换名执行,绕过限制。这是 Termux 之外极少数在普通用户应用里跑原生可执行文件的正确做法。
§5加密与加固
crypto/ · 教科书级 AES-GCM + PBKDF2
AesCryptoEngine.kt:AES-256-GCM + PBKDF2 派生密钥- core/crypto/KeyManager.kt:48-95:按包名 + 签名证书派生,每个打出去的 APK 拿到的密钥都不一样——防止一个 APK 的密钥被泄露后影响其他用户
AssetEncryptor.kt+SecureAssetLoader.kt:资源文件 AES 加密存储,加载时透明解密
disguise/ · 浏览器指纹三层伪装
core/disguise/BrowserDisguiseEngine.kt:39-80
- Level 1:
BrowserKernel——改 HTTP headers + 注入基础 JS - Level 2-5:
BrowserDisguise——22 个向量指纹伪装:Canvas / WebGL / Audio / Screen / Timezone / Fonts / Battery / Plugins... - OAuth 专用层:
OAuthCompatEngine针对 Google / Facebook / Microsoft 的登录反检测 - 动态 JS 生成:
BrowserDisguiseJsGenerator.kt每次启动生成新的伪装脚本,防指纹收敛
blacktech/ · 这块是 PPT 功能
core/blacktech/BlackTechConfig.kt(203 行)
- "核弹模式""隐身模式""摩斯电码 SOS"——听起来很酷
- 实际字段
forceMaxPerformance/forceBlockPowerKey/forceAirplaneMode都需要系统级权限,用户态 APK 根本拿不到 - 判断:这部分是给商业版 Pro/Ultra 宣传页用的噱头配置,真实效果不及一半
§6扩展系统 + AI 集成
Chrome 扩展兼容层(31 文件 · 22661 行,单模块最大)
- core/extension/ChromeExtensionParser.kt:77-243:真的解析
manifest.json,支持content_scripts/permissions/background标准字段 - core/extension/ChromeExtensionPolyfill.kt:1409:实现
chrome.runtime.sendMessage()等核心 API DeclarativeNetRequestEngine.kt:Manifest V3 的声明式网络请求引擎- core/extension/ExtensionFileManager.kt:399-407:扫描嵌套目录查找 manifest.json
AI 多模型适配
core/ai/AiApiClient.kt:30——支持 Anthropic Claude / Google Gemini / OpenAI / OpenRouter 四家:
- Anthropic header:
anthropic-version: 2023-06-01(正确的最新版本) - 资源文件
litellm_model_prices.json(39KB)包含 100+ 模型的即时价格——说明作者考虑了成本 - 无硬编码 API Key:
grep -r "sk-\|claude-\|AIza"零命中,用户自带凭证。正确的设计
§7商业化阉割证据链
前面几节都在夸,这节是批判。三条硬证据指向"这是商业产品的引流版":
证据 1:硬编码云服务端
core/cloud/CloudApiClient.kt:30
const val BASE_URL = "https://api.shiaho.sbs"
// 第 50 行: POST /api/v1/activation/redeem 激活码兑换
// 第 80 行: POST /api/v1/activation/preview 激活码预览
// 还有云备份 / 通知 / 分析完整后端
.sbs 是典型的低价短期域名(Side Business),常用于灰色项目——但本项目看起来更像是作者给商业版选的廉价域名而已。真正的问题是这个 URL 是硬编码的,上游随时改协议你就挂。
证据 2:本地激活码系统
core/activation/ActivationManager.kt:20-507
- 支持 4 种类型:
PERMANENT/TIME_LIMITED/USAGE_LIMITED/DEVICE_BOUND - 第 441-448 行用常量时间比较防时序攻击——写得很认真,说明作者是真的想把激活系统做稳
- 时限/用途限制持久化到 DataStore(第 281-305 行)
- 但开源代码里看不到"如果未激活则禁用 Pro 功能"的判断——那部分可能在私有仓库,或者用代码混淆隐藏了
证据 3:Google Play 订阅
core/billing/BillingManager.kt:15-25
enum class SubscriptionSku(val id: String) {
PRO_MONTHLY("pro_monthly"),
PRO_QUARTERLY("pro_quarterly"),
PRO_YEARLY("pro_yearly"),
ULTRA_MONTHLY("ultra_monthly"),
ULTRA_QUARTERLY("ultra_quarterly"),
ULTRA_YEARLY("ultra_yearly"),
}
- 6 档订阅 SKU,Pro / Ultra 两个级别 × 月/季/年
- 依赖
com.android.billingclient:billing-ktx:7.0.0 - 订阅状态检查:第 300-328 行
§8技术评分矩阵
| 模块 | 评分 | 类型 | 是否值得抄 |
|---|---|---|---|
| APK 构建引擎 | 9/10 | 真功夫 | ★★★★★ 全项目最高价值 |
| 加密体系 | 8/10 | 真功夫 | ★★★★ KeyManager.kt 按包名派生思路 |
| Chrome 扩展 Polyfill | 8/10 | 真功夫 | ★★★★ 移动端 Manifest V3 稀缺参考 |
| GeckoView 引擎 | 8/10 | 真集成 | ★★★ Firefox 内核接入套路 |
| AI 多模型适配 | 7/10 | 真功夫 | ★★★ 无硬编码 Key,干净 |
| 多运行时 | 7/10 | 真集成 | ★★★ PHP/Node SELinux 绕行技巧 |
| 指纹伪装 disguise/ | 7/10 | 部分真 | ★★ 伦理有疑问,慎用 |
| 激活 + 订阅 | 6/10 | 调包 | ★ 商业化模板参考 |
| i18n 翻译 | 3/10 | 水分 | ✗ 机器翻译体积膨胀 |
| BlackTech "黑科技" | 3/10 | PPT 功能 | ✗ 权限拿不到 |
§9最终定论
三问三答
- 是不是噱头? 不是。APK 构建引擎是真家伙,单拎出来都够发一篇技术文章。
- 是不是纯开源神器? 不是。是商业产品的引流版,有闭源后端 + 订阅 + 激活码。
- 值不值得看? 非常值得。作为 Android 底层学习材料,是近年来质量最高的开源项目之一。
抄代码优先级
- core/apkbuilder/ 整块 —— 字节级 APK 操作,无替代品
- core/crypto/KeyManager.kt:48-95 —— 按包名派生密钥的设计
- core/engine/BrowserEngine.kt + GeckoViewEngine.kt —— GeckoView 集成套路
- core/extension/ChromeExtensionParser.kt —— Manifest V3 解析 + Polyfill
- core/php/PhpAppRuntime.kt:70-72 —— SELinux
execute_no_trans绕行技巧 - core/ai/AiApiClient.kt —— 多 LLM 适配干净实现
谁适合用它
- ✅ Android 开发者想学 APK 字节结构和打包原理
- ✅ Web 开发者想给自己的站点快速套壳自用(别上架商业应用)
- ✅ 研究 Android 加密 / 反爬 / 扩展系统的人
- ❌ 追求严肃商业落地的团队 —— 用 Capacitor / Tauri Mobile
- ❌ 想要活跃社区生态的 —— 14 次提交 + 单人维护,作者跑路就死
一句话总结
1) 删除
core/cloud/ 里所有对 api.shiaho.sbs 的调用;2) 禁用
core/activation/ 的激活校验(或改为永久版);3) 把
core/billing/ 整块删掉,避免无意中触发 Google Play 的订阅校验;4) 自己的签名证书,自己的密钥管理,不要信它的云备份。
本文所有论断均基于 git clone --depth=1 https://github.com/shiahonb777/web-to-app(2026-04-13 快照)的源码,采样 + 精读 + grep 验证。若后续提交改变了实现,请以上游为准。