1. 项目概述:为什么2024年还在谈360加固保?
如果你是一名Android开发者,尤其是身处国内应用市场的开发者,那么“加固”这个词对你来说绝对不陌生。它就像给自家房子装防盗门,是应用发布前一道几乎绕不开的工序。而在众多加固方案中,360加固保凭借其免费、易用以及与国内各大应用商店的深度集成,成为了许多个人开发者和中小团队的首选,甚至是“默认选项”。
但问题恰恰出在这里。正因为用的人多,默认配置的“坑”也就显得格外普遍。我见过太多开发者,包括曾经的我自己,在项目临近上线时,匆匆忙忙打开360加固保的客户端,直接“一键加固”,然后就把APK扔给测试或上传市场。结果呢?应用在特定机型上闪退、某些功能(比如热更新、推送、插件化)莫名其妙失效、甚至加固后的包体积暴增,导致用户下载安装失败。这些问题往往在测试阶段难以复现,一旦线上爆发,就是一场手忙脚乱的灾难。
所以,这篇指南的目的,不是教你如何使用360加固保——它的官方文档已经足够简单。我要分享的,是经过无数次“踩坑”和“填坑”后,总结出的那份针对2024年最新版本(通常指其命令行工具、插件及云端策略)的深度配置心得与避坑清单。这些内容,你在官方文档里很难找到,它们源于真实的线上事故复盘、与技术支持的技术拉锯,以及我们团队内部的血泪教训。无论你是刚接触加固的新手,还是已经用过多次的老手,相信都能从中找到让你“恍然大悟”或“心头一紧”的要点。
2. 核心思路:从“黑盒加固”到“白盒配置”
过去,我们常把加固视为一个“黑盒”过程:输入一个APK,输出一个加固后的APK,中间发生了什么并不关心。这种思路在早期简单应用上或许可行,但在如今动辄集成十几个SDK、采用混合开发框架(如Flutter、React Native)、或使用了高级特性(如Kotlin协程、R8全模式混淆)的现代Android应用上,必定会碰得头破血流。
正确的思路,是将加固视为一个需要精细调控的**“白盒”构建环节**。你需要清楚地知道:
- 加固在做什么:它不仅仅是加壳,还包括代码混淆、资源加密、反调试、签名校验等多重保护。
- 你的应用“怕”什么:你的应用有哪些特性(如JNI调用、反射、动态加载)是与加固的默认行为冲突的?
- 如何与加固“对话”:通过配置文件、命令行参数、Gradle插件选项,告诉加固工具哪些地方需要“特殊照顾”。
基于这个思路,我们的配置指南将围绕三个核心维度展开:兼容性配置、安全性平衡与流程自动化。目标是得到一个既安全可靠,又稳定兼容的加固产出物。
2.1 理解360加固保的核心处理流程
在深入配置之前,有必要快速了解一下360加固保(以其命令行工具jiagu为例)的大致工作流程,这有助于理解后续每个配置项的意义:
- 解包与分析:工具会解压你的APK,分析其DEX文件、资源文件、清单文件等结构。
- DEX保护:这是核心。会对classes.dex进行加壳、混淆、虚拟化等处理,生成新的DEX或so库。这里最容易引发兼容性问题。
- 资源与文件保护:对assets、res目录下的特定资源进行加密或混淆。
- 运行时库注入:将加固的运行时库(so文件)注入到APK中,这些库负责在应用启动时解密和加载被保护的内容。
- 重打包与重签名:将处理后的所有文件重新打包成APK,并使用你提供的签名文件进行重新签名。
整个过程,尤其是第2和第4步,是“坑”的高发区。我们的配置,本质上就是在引导工具,安全地绕过我们应用的“雷区”。
3. 环境准备与工具选择
工欲善其事,必先利其器。首先确保你使用的是最新的工具,旧版本的bug和限制可能让你事倍功半。
3.1 获取最新版加固工具
不要使用网页版上传加固,那不适合自动化,也无法进行精细配置。前往360加固保官网,下载最新版本的命令行加固工具。通常是一个ZIP包,如jiagu_linux.zip(Linux/Mac) 或jiagu_windows.zip。
注意:务必从官网下载,网络上流传的破解版或旧版可能携带恶意代码或存在未知漏洞,直接威胁你的源码和证书安全。
解压后,目录结构通常包含:
jiagu或jiagu.jar(主程序)jiagu.ini或config.ini(基础配置文件)/libs目录 (依赖库)
3.2 Java环境确认
360加固保命令行工具基于Java开发,需要本地安装JRE(Java Runtime Environment)或JDK。建议使用JDK 8或JDK 11这两个长期支持版本,兼容性最广。避免使用过新(如JDK 17+)或过旧的版本。
在终端中检查:
java -version确保输出类似java version "1.8.0_301"。
3.3 准备签名文件
加固过程会破坏原始签名,因此必须在加固后重新签名。你需要准备好你的应用签名文件(.keystore或.jks)以及对应的别名、密钥密码、存储密码。
安全建议:
- 永远不要将签名密码硬编码在脚本或配置文件中。
- 使用环境变量或构建系统的安全存储(如Android Studio的
signingConfigs,或CI/CD系统的Secret管理)来传递密码。 - 准备一个专门的“加固用”签名文件,与正式发布签名区分开,用于测试加固流程。
4. 关键配置解析与避坑实战
这是本文的核心。我们将通过一个典型的config.xml(或通过命令行参数)来逐一拆解关键配置项。以下配置基于我近期项目的实践,并附上了每个选项的“潜台词”和“坑点”。
4.1 基础配置与登录
首先,你需要登录你的360开发者账号。通过命令行初始化:
java -jar jiagu.jar -login 你的账号 你的密码登录信息会保存在本地,后续操作无需重复登录。
踩坑实录1:如果账号开启了二次验证(如手机令牌),命令行登录可能会失败。解决方案是先在网页端登录一次,或者联系客服暂时关闭二次验证(不推荐)。更稳定的做法是使用**“授权码”**方式。在官网控制台生成一个长期有效的授权码,然后使用
-import参数导入,可以避免密码泄露和二次验证问题。java -jar jiagu.jar -import your_auth_code_file.txt
4.2 配置模板生成与结构
使用以下命令生成一个默认的配置文件模板:
java -jar jiagu.jar -config -show这会输出一个XML格式的配置示例。将其保存为jiagu_config.xml,我们将在此基础上修改。
一个强化版的配置文件骨架如下:
<?xml version="1.0" encoding="UTF-8"?> <tasks> <task type="reinforce"> <!-- 输入输出路径 --> <option name="in" value="/path/to/your/app-release.apk"/> <option name="out" value="/path/to/output/dir"/> <!-- ############### 核心保护配置 ############### --> <option name="multi" value="1"/> <!-- 多DEX处理模式 --> <option name="crash" value="0"/> <!-- 崩溃日志抓取,0关闭,1开启 --> <option name="x86" value="1"/> <!-- 是否支持x86架构,按需开启 --> <option name="arm64" value="1"/> <!-- 是否支持arm64-v8a,必须为1 --> <!-- 签名配置 --> <sign> <option name="keystore" value="/path/to/your.keystore"/> <option name="alias" value="your_alias"/> <option name="pswd" value="${KEYSTORE_PASSWORD}"/> <!-- 建议用变量 --> <option name="aliaspswd" value="${KEY_PASSWORD}"/> </sign> <!-- ############### 高级白名单配置(避坑关键)############### --> <white-list> <!-- 关键:不混淆/不加密的类或包 --> <package name="com.xxx.yourmodel.entity.*"/> <!-- 实体类,JSON序列化需要 --> <package name="com.xxx.yourmodel.BuildConfig"/> <!-- 构建配置类 --> <class name="androidx.startup.Initializer"/> <!-- Jetpack Startup组件 --> <!-- JNI方法所在的类,必须加入! --> <class name="com.xxx.jni.NativeHelper"/> <!-- 反射调用的类,如一些工厂类、路由表 --> <class name="com.xxx.router.RouterTable"/> <!-- 第三方SDK要求不混淆的类,参考其文档 --> <package name="com.google.android.gms.ads.*"/> <package name="com.tencent.mm.opensdk.**"/> </white-list> <!-- 资源文件保护配置 --> <resource> <option name="assets" value="0"/> <!-- assets目录保护,0关闭 --> <option name="res" value="0"/> <!-- res目录保护,0关闭 --> <option name="filter" value="*.png, *.jpg, *.so"/> <!-- 不处理的文件 --> </resource> <!-- ############### 特定功能配置 ############### --> <option name="webview" value="1"/> <!-- 如果用了系统WebView,建议开启 --> <option name="debuggable" value="false"/> <!-- 加固后是否可调试,必须false --> </task> </tasks>4.3 多DEX处理 (multi) 与64位支持 (arm64)
<option name="multi" value="1"/>- 是什么:指定对多DEX应用的处理模式。
value=1通常表示“自动处理多DEX”。 - 为什么:现代Android应用方法数超限,普遍使用MultiDex。如果这里配置不当,加固后可能导致
NoClassDefFoundError。 - 避坑指南:永远设置为1。如果你遇到加固后启动崩溃,提示主DEX找不到某个类,可以尝试结合下面的白名单,将
Application类及其直接引用的类加入白名单。
- 是什么:指定对多DEX应用的处理模式。
<option name="arm64" value="1"/>- 是什么:是否生成支持arm64-v8a架构的加固库。
- 为什么:Google Play从2019年就要求上架应用必须支持64位。国内主流应用商店也已跟进。必须设为1。
- 联动坑:开启
arm64=1后,请务必检查你项目中jniLibs目录或第三方SDK是否提供了对应的64位so库。如果只有32位的so (armeabi-v7a),加固包在64位设备上运行可能会找不到原生库而崩溃。解决方案是要求SDK提供商提供64位库,或过滤掉64位架构(不推荐,可能被应用市场拒绝)。
4.4 白名单配置 (white-list):最大的坑王
这是整个配置文件的灵魂所在,90%的兼容性问题都源于此配置不全或不准。加固工具的混淆和加密功能非常激进,会破坏那些依赖“字符串匹配”或“运行时结构稳定”的代码。
白名单配置原则:
- 反射相关:任何通过
Class.forName(),getMethod()等方式动态调用的类、方法、字段,其名称必须保持原样。 - JNI相关:Java层的Native方法声明类 (
native关键字修饰的方法所在的类),必须加入白名单。因为C/C++代码通过JNIEnv->GetMethodID查找方法时,依赖的是混淆前的名称。 - 序列化/反序列化:所有用于JSON(如Gson、Jackson)、Parcelable、Serializable的实体类(
Data Class),其字段名通常需要保持稳定。至少要把这些类本身加入白名单,防止类名被混淆。字段名是否混淆取决于序列化库的配置(如Gson的@SerializedName)。 - 动态加载:如果你使用了插件化、热修复框架(如Tinker、Sophix),那些需要从APK外部加载的类,其类名和包路径必须保持不变。
- 第三方SDK要求:几乎所有第三方SDK的集成文档里都会有一节“Proguard/R8 Rules”,里面列出的
-keep规则,同样适用于360加固保的白名单。你需要将这些类或包名,翻译成<package name="..."/>或<class name="..."/>的格式,加入到配置中。
实操心得:如何快速确定白名单?
- 从崩溃日志反推:加固包测试时出现
ClassNotFoundException,NoSuchMethodError,日志里会给出缺失的类名或方法名,直接加入白名单。 - 扫描代码:在项目中全局搜索
Class.forName、getDeclaredMethod、getField等关键字,找出所有硬编码的字符串类名/方法名。 - 依赖分析:检查
build.gradle中所有第三方库的官方文档,收集它们的混淆保持规则。 - 最笨但最有效的方法:在测试阶段,将白名单范围放得很宽(如
com.yourcompany.*),让加固生效但混淆减弱。然后逐步缩小范围,直到找到引发问题的那个最小类集合。
4.5 资源文件保护 (resource)
<option name="assets" value="0"/>与<option name="res" value="0"/>- 是什么:是否对
assets和res目录下的文件进行加密。 - 为什么默认建议关闭(设为0):
- 性能开销:运行时解密资源会有轻微性能损耗。
- 兼容性风险:某些第三方库或框架(如一些游戏引擎、WebView加载本地HTML)可能直接通过文件路径访问资源,加密会导致访问失败。
- 必要性低:APK中的资源文件(图片、布局XML)本身是编译后的二进制格式,可读性已不高,加密的边际收益有限。
- 何时开启:如果你的APK包含非常关键的、不希望被直接提取的配置文件、脚本或密钥文件(但请注意,密钥硬编码在APK中本身就不安全),可以单独开启
assets保护,并通过<filter>排除其他文件。
- 是什么:是否对
4.6 签名配置 (sign)
这里有一个巨坑:加固工具的重签名操作,可能会破坏Android V2/V3签名方案(APK Signature Scheme v2/v3)。
- 现象:加固后的APK,在Android 7.0(Nougat)及以上设备安装时,可能会提示“安装包解析错误”或“安装包已损坏”。
- 原因:V2/V3签名是对整个APK文件(ZIP结构)进行签名。加固工具修改了APK内部文件后,如果没有正确处理签名块,就会导致签名失效。
- 解决方案:
- 确保使用最新版加固工具:新版本通常对V2/V3签名支持更好。
- 在加固命令中显式指定签名方案(如果工具支持)。查看帮助文档:
寻找是否有java -jar jiagu.jar -help-v2sign或-signscheme相关参数。 - 终极方案:先加固,后手动签名。这是最可靠的方法。
- 步骤一:在加固配置中,不配置
<sign>部分,或者使用一个临时测试签名。 - 步骤二:对加固输出的、未签名的APK(或使用测试签名的APK),使用Android SDK的
apksigner工具进行正式的V2/V3签名。
# 使用 apksigner 进行签名 (Android SDK Build-Tools 中) apksigner sign --ks your.keystore --ks-key-alias your_alias --out signed.apk unsigned.apk - 步骤一:在加固配置中,不配置
- 验证签名:签名后,使用
apksigner verify -v your_signed.apk检查是否包含V2/V3签名。
重要提示:绝对不要用
jarsigner工具对加固后的APK进行签名,因为它只提供旧的V1签名,无法生成V2/V3签名,在高版本系统上一定会出问题。
5. 集成到自动化构建流程(CI/CD)
手动操作容易出错,将加固集成到CI/CD(如Jenkins、GitLab CI、GitHub Actions)中是专业团队的标配。这里给出一个基于Shell脚本和Gradle的集成思路。
5.1 基于Gradle Task的自动化
在项目的app模块的build.gradle中,添加一个自定义Task:
android { // ... 你的其他配置 signingConfigs { release { storeFile file("path/to/your.keystore") storePassword System.getenv("STORE_PASSWORD") keyAlias "your_alias" keyPassword System.getenv("KEY_PASSWORD") } } buildTypes { release { signingConfig signingConfigs.release // ... 其他配置 } } } task reinforce360(type: Exec) { dependsOn 'assembleRelease' // 依赖Release构建 group = 'build' description = '使用360加固保加固Release APK' // 定义路径变量 def jiaguDir = file("/opt/tools/360jiagu") // 加固工具目录 def jiaguJar = new File(jiaguDir, "jiagu.jar") def configFile = new File(jiaguDir, "jiagu_config.xml") def inputApk = file("${buildDir}/outputs/apk/release/app-release.apk") def outputDir = file("${buildDir}/outputs/jiagu/") // 确保输出目录存在 outputs.dir outputDir commandLine 'java', '-jar', jiaguJar.toString(), '-config', configFile.toString(), '-in', inputApk.toString(), '-out', outputDir.toString() // 可以添加更多参数,如 -config -show 来指定配置 // 环境变量传递密码(示例,更安全的方式是用CI系统变量) environment "KEYSTORE_PASSWORD", System.getenv("KEYSTORE_PASSWORD") environment "KEY_PASSWORD", System.getenv("KEY_PASSWORD") doFirst { if (!inputApk.exists()) { throw new GradleException("Input APK not found: ${inputApk}") } println "开始加固: ${inputApk.name}" } doLast { println "加固完成,输出目录: ${outputDir}" // 这里可以添加自动签名步骤(调用apksigner) } }然后,你可以在终端运行./gradlew reinforce360来完成构建和加固。
5.2 CI/CD管道示例(GitHub Actions)
name: Build and Reinforce on: push: tags: - 'v*' jobs: build-and-reinforce: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Set up JDK 11 uses: actions/setup-java@v3 with: java-version: '11' distribution: 'temurin' - name: Setup Android SDK uses: android-actions/setup-android@v2 - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build Release APK run: ./gradlew assembleRelease env: STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }} KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} - name: Download 360 Jiagu CLI run: | wget -O jiagu.zip "https://官方下载链接/jiagu_linux.zip" # 请替换为真实链接 unzip jiagu.zip -d /opt/360jiagu chmod +x /opt/360jiagu/jiagu.jar - name: Configure 360 Jiagu run: | cd /opt/360jiagu # 使用授权码登录(更安全) echo "${{ secrets.JIAGU_AUTH_CODE }}" > authcode.txt java -jar jiagu.jar -import authcode.txt # 复制预定义好的配置文件 cp $GITHUB_WORKSPACE/jiagu_config.xml . - name: Reinforce APK run: | cd /opt/360jiagu java -jar jiagu.jar -config jiagu_config.xml \ -in $GITHUB_WORKSPACE/app/build/outputs/apk/release/app-release.apk \ -out $GITHUB_WORKSPACE/app/build/outputs/jiagu/ env: KEYSTORE_PASSWORD: ${{ secrets.STORE_PASSWORD }} KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} - name: Re-sign with APKSigner (V2/V3) run: | cd $GITHUB_WORKSPACE # 找到加固输出的APK,通常名字会变 REINFORCED_APK=$(find app/build/outputs/jiagu -name "*jiagu*.apk" | head -1) # 使用apksigner重新签名 $ANDROID_HOME/build-tools/30.0.3/apksigner sign \ --ks keystore.jks \ --ks-pass pass:${{ secrets.STORE_PASSWORD }} \ --ks-key-alias your_alias \ --key-pass pass:${{ secrets.KEY_PASSWORD }} \ --out app-release-reinforced-signed.apk \ "$REINFORCED_APK" - name: Upload Artifact uses: actions/upload-artifact@v3 with: name: reinforced-apk path: app-release-reinforced-signed.apk这个流程实现了从代码到加固签名APK的全自动化,确保了流程的一致性和可重复性。
6. 加固后的测试与验证清单
加固完成并不意味着万事大吉,必须进行严格的测试。以下是一份必须执行的检查清单:
| 测试类别 | 测试项目 | 预期结果与排查方法 |
|---|---|---|
| 安装与启动 | 在Android 5.0, 7.0, 9.0, 11.0, 13+ 等多个版本的真机/模拟器上安装。 | 能正常安装、启动,无“解析包错误”。重点测Android 7.0+,验证V2/V3签名。 |
| 基础功能 | 遍历所有主要Activity、Fragment。测试网络请求、数据加载、UI交互。 | 功能正常,无闪退。关注白名单遗漏导致的ClassNotFoundException。 |
| JNI/NDK | 测试所有依赖原生库的功能(如音视频处理、图像识别)。 | 功能正常。如果崩溃,检查对应Java类是否在白名单中,以及so库架构是否齐全。 |
| 反射与动态代理 | 测试使用了反射、动态代理(如Retrofit接口)、AspectJ等技术的功能。 | 功能正常。此类问题最隐蔽,需结合运行时日志和白名单配置仔细排查。 |
| 第三方SDK | 测试登录、支付、推送、分享、地图、统计等所有集成的SDK。 | SDK功能全部正常。将各SDK官方要求的-keep规则全部加入白名单。 |
| 性能与体积 | 对比加固前后APK体积。测试启动速度、页面跳转流畅度。 | 体积增长在合理范围(通常增加2-5MB)。启动时间无明显劣化(<200ms)。 |
| 安全扫描 | 使用其他安全扫描工具(如腾讯御安全、爱加密在线检测)对加固包进行扫描。 | 无“未加固”或“保护强度弱”的告警。确保加固确实生效。 |
一个必备的调试技巧:开启加固日志在加固配置中,可以尝试寻找开启调试日志的选项(有时在config.ini中,或通过-debug命令行参数)。加固工具运行时输出的详细日志,能帮你看清它具体处理了哪些类、哪些方法,对于定位白名单问题有奇效。
7. 常见问题与排查技巧实录
这里记录了几个最让人头疼的“坑”及其解决方案。
问题一:加固后App启动立刻闪退,日志显示java.lang.NoClassDefFoundError或java.lang.NoSuchMethodError。
- 原因:这是最典型的白名单配置不全。加固工具混淆或加密了某个类/方法,但运行时(可能是通过反射、JNI或框架初始化)需要按原名称查找,找不到就崩溃。
- 排查:
- 查看崩溃堆栈,找到缺失的类名或方法名。
- 如果堆栈信息被混淆,尝试在测试手机连接电脑,使用
adb logcat | grep -i "classnotfound\|nosuchmethod"过滤更底层的错误信息。 - 将缺失的类或它的父类、接口加入白名单。如果是第三方库,去查该库的官方混淆规则。
问题二:在Android 7.0及以上系统安装失败,提示“安装包解析错误”。
- 原因:V2/V3签名被破坏。
- 解决:
- 使用
apksigner verify -v your.apk检查加固后APK的签名方案。如果只有v1 scheme,说明有问题。 - 采用“先加固,后手动用apksigner签名”的流程。
- 确保构建环境中的
apksigner版本与build-tools版本匹配。
- 使用
问题三:集成热更新(如Tinker)后,加固包无法应用补丁。
- 原因:热更新框架通常需要对比基线包和补丁包的类与方法差异。加固工具的深度混淆和加密改变了类与方法的特征,导致差异计算失败。
- 解决:
- 严格遵循热更新框架的“加固配置指南”。以Tinker为例,它要求将
tinkerId、Application及其入口类、AssetManager等加入白名单。 - 将热更新框架自身的所有类(如
com.tencent.tinker.**)加入白名单。 - 与热更新框架的打包流程顺序很重要。标准流程是:打基线包 -> 加固基线包 -> 发布基线包 -> 基于源码打补丁包 -> 加固补丁包。切记,补丁包也需要用完全相同的加固配置进行处理!
- 严格遵循热更新框架的“加固配置指南”。以Tinker为例,它要求将
问题四:加固后APK体积增加过多(如超过10MB)。
- 原因:主要来自加固运行时库(so文件)的注入。360加固保会为每个支持的CPU架构注入一个so库。
- 优化:
- 检查
<option name="x86" value="1"/>。如果你的用户几乎没有x86架构的设备(如Intel芯片的平板),可以将其设为0,减少一个架构的so库。 - 检查资源文件保护是否不必要地开启了。关闭
assets和res保护能减少一些体积和运行时开销。 - 在
resource的filter中,排除掉已经压缩过的文件,如*.so,*.zip,避免重复压缩。
- 检查
问题五:使用Flutter或React Native等跨平台框架开发的应用,加固后白屏或JS加载失败。
- 原因:这些框架的JavaScript/Flutter引擎可能需要从assets目录加载特定的脚本或资源文件(如
flutter_assets、index.android.bundle)。加固工具对assets的加密可能破坏了文件的读取。 - 解决:
- 在配置文件中,将资源保护关闭:
<option name="assets" value="0"/>。 - 如果出于安全考虑必须保护assets,则使用
<filter>精确排除框架所需的文件,例如:<option name="filter" value="flutter_assets/**, jsbundles/**"/>。
- 在配置文件中,将资源保护关闭:
最后,记住一个核心原则:加固配置是一个与你的应用特性深度绑定的过程,没有放之四海而皆准的“最佳配置”。本文提供的指南和避坑点,是你构建自己稳定加固流程的起点。最好的方法,是为你的项目建立一个专门的、版本化的加固配置文件,并随着每次引入新的第三方库或使用新的技术特性,不断更新和完善你的白名单。把这个过程也纳入你的开发流程,加固这件事,才能真正从“玄学”变成“科学”。