Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/release-notes/versions/1.8.5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
### PVM2 组件方法兼容修复

- 修复 Activity、Application、Service 等组件中的普通方法因包含字段、调用、数组元素或同步块而被叶子白名单整批排除的问题,统一进入共用编译器逐指令校验。
- 修复分支和异常处理区中的可空接收者被静态拒绝的问题;方法长度上限从 512 扩大到 4096 个 DEX 代码单元,同时严格检查展开后的代码长度、寄存器、跳转和异常表边界。
- 修复字符串常量的内嵌空字符、补充平面字符和驻留身份;修复祖先类引用、派生对象及接口默认实现中的父类调用分派。
- PVM2 镜像升级至 v9,打包器、运行时和覆盖报告同步适配。初始化器、生成桥、特殊对象契约及其他未支持方法仍明确保留;零转换及必保方法未覆盖仍失败,不把跳过项计为已保护。

### 验证与使用说明

- 自建 Java/D8 样本中 46 个方法通过转换和最终产物验证;在 Android 9、15、16 模拟器及 Android 15 真机上完成 20 次启动、10,500 项执行断言,覆盖 16 KB 内存页、无资源表和原生接口默认方法。
- 本地实际 APK 的限定选择范围为 21 个方法,18 个普通方法转换成功,3 个初始化器保留;完成五种安装/启动场景及生命周期、通知服务检查。该结果不代表全部业务流程或全部方法分支已验证。
- 升级后需重新加固,使用配套打包器与运行时;已有 APK 不会自动获得修复。PVM2 需要 JDK 17 或更高版本,具体覆盖范围以本次产物逐方法报告为准,分享前请手动脱敏。
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/shield-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "shield-cli"
version = "1.8.4"
version = "1.8.5"
edition = "2021"
authors = ["MocikaDev"]
license = "MIT OR Apache-2.0"
Expand Down
4 changes: 2 additions & 2 deletions apps/shield-gui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/shield-gui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mocika-shield-gui",
"version": "1.8.4",
"version": "1.8.5",
"license": "MIT OR Apache-2.0",
"private": true,
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion apps/shield-gui/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mocika-shield"
version = "1.8.4"
version = "1.8.5"
edition = "2021"
license = "MIT OR Apache-2.0"
publish = false
Expand Down
2 changes: 1 addition & 1 deletion apps/shield-gui/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Shellsmith",
"version": "1.8.4",
"version": "1.8.5",
"identifier": "dev.mocika.shield-gui",
"build": {
"beforeDevCommand": "npm run dev -- --host 127.0.0.1",
Expand Down
2 changes: 1 addition & 1 deletion apps/shield-gui/src/hooks/use-about-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type AppInfo = {
};

const defaultAppInfo: AppInfo = {
version: "1.8.4",
version: "1.8.5",
git_hash: "dev",
build_date: "unknown",
};
Expand Down
2 changes: 1 addition & 1 deletion crates/shield-core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "shield-core"
version = "1.8.4"
version = "1.8.5"
edition = "2021"
authors = ["MocikaDev"]
license = "MIT OR Apache-2.0"
Expand Down
14 changes: 10 additions & 4 deletions crates/shield-core/src/protection_report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ impl Pvm2Failure {
ensure!(
report.schema_version == 2
&& report.code_version == 4
&& matches!(report.image_version, 7 | 8),
&& matches!(report.image_version, 7..=9),
"方法失败报告协议无效"
);
let mut classes = BTreeSet::new();
Expand Down Expand Up @@ -453,7 +453,7 @@ impl MethodReport {
ensure!(
self.schema_version == 2
&& self.code_version == 4
&& matches!(self.image_version, 7 | 8),
&& matches!(self.image_version, 7..=9),
"方法报告协议版本不受支持"
);
ensure!(
Expand Down Expand Up @@ -795,6 +795,12 @@ mod tests {
"reason": reason, "artifact_verified": false, "plaintext_policy": null
})).unwrap());
}
for image in [7, 8, 9, 10] {
detail.image_version = image;
detail.write(&path).unwrap();
assert_eq!(Pvm2Failure::zero_methods(&path).is_ok(), image <= 9);
}
detail.image_version = 9;
detail.write(&path).unwrap();
let failure = Pvm2Failure::zero_methods(&path).unwrap();
assert_eq!(failure.summary.selected_classes, 1);
Expand Down Expand Up @@ -995,14 +1001,14 @@ mod tests {
);
}
}
for image in [7, 8] {
for image in [7, 8, 9] {
let mut candidate: MethodReport = serde_json::from_value(data.clone()).unwrap();
candidate.image_version = image;
assert!(candidate
.validate(dir.path(), &code, &modules, &BTreeSet::new(), 1, &inputs)
.is_ok());
}
for (schema, image) in [(1, 7), (2, 6), (2, 9)] {
for (schema, image) in [(1, 7), (2, 6), (2, 10)] {
let mut candidate: MethodReport = serde_json::from_value(data.clone()).unwrap();
candidate.schema_version = schema;
candidate.image_version = image;
Expand Down
8 changes: 5 additions & 3 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -543,11 +543,13 @@ shield protect --input input.apk --output protected.apk \

启用 PVM2 的成功输出旁会生成 `protected.apk.protection.json` 或 `protected.aab.protection.json`。统计分别展示转换成功率、选中方法覆盖率、原始指令字节覆盖率和必保方法覆盖率;未声明必保方法时该指标为 `null`,不记为 100%。这些百分比仅针对选择范围,不代表整个应用。报告包含逐方法原因、最终索引与产物摘要,不包含密钥或明文镜像,也不进入安装包。

选择范围在兼容性过滤**之前**统计。UI/导航、数据/资源辅助和媒体命名不再直接排除整类:没有对象状态访问、外部调用或同步的独立方法,可以继续进入 PVM2 指令与产物检查。支持基本类型计算,以及对象/数组参数未使用、引用透传、空值分支和引用身份比较;引用参数不会再仅因其类型被排除,但数组元素和字段访问仍不在这一兼容放行范围内。私有、静态和实例方法适用相同的方法体检查;继承父类或实现接口本身不再排除普通实例方法。跳板保留原声明、访问标志、参数与接收者,虚方法分派仍由 ART 完成;`hashCode/finalize` 等特殊对象契约仍保守保留。分支、循环和数值运算仍须通过现有编译器检查,不能只凭命名认定可转换。
选择范围在兼容性过滤**之前**统计。UI、导航和 Android 组件中的普通方法不再因包含字段、方法调用、数组元素或同步块而被叶子方法白名单整批排除;它们与普通业务类共用编译器,逐指令校验后再决定是否转换。跳板保留原声明、访问标志、参数与接收者,虚方法分派仍由 ART 完成。父类调用保留声明类的直接父类信息,接口默认实现单独按原接口调用,不能由接收者的动态类型推断。

独立方法中的异常处理不再仅因存在异常表而被排除。捕获、异常透传、重新抛出和不访问外部状态的清理逻辑继续经过异常表映射、控制流及指令检查;`throw null` 产生可被捕获的 `NullPointerException`。这不意味着任意 `try/catch/finally` 都受支持:异常区中的外部调用、字段、数组元素或同步仍受上述限制。
这一路径支持经回归验证的字段读写、对象创建、静态/虚拟/接口/直接/父类调用、数组元素、空值调用、异常捕获与 finally、同步块。空值本身不再作为静态拒绝条件,运行时在原调用位置抛出并派发 `NullPointerException`。方法仍受支持指令、32 个寄存器(含编译临时寄存器)、4096 个 DEX 代码单元、编译后代码 65535 字节、分支偏移和异常表映射等边界约束;不是任意方法都能转换。标准 UTF-8 字符串保留内嵌空字符、补充平面字符及驻留身份;孤立 UTF-16 代理项明确拒绝,不能静默替换。

构造器、静态初始化器、生成桥接/合成方法和仍涉及框架状态的候选保持原兼容边界;放行继承类的独立方法不等于放行包含 `invoke-super` 等外部调用的方法体。跳过项仍计入覆盖率分母,并保留 `ui_runtime_glue`、`compiler_generated_bridge`、`data_resource_runtime_glue` 或 `playback_compatibility` 等实际分类。精确必保清单同样受方法兼容检查约束,必保项被排除时任务失败,不会自动降级。所有候选都不可转换时仍返回 `PVM2_ZERO_METHODS`,不会把零转换记为成功。这些规则不代表已识别应用全部关键业务;完整覆盖率需用配套的新打包器和运行时重新生成。
数据、资源和媒体框架的专用兼容规则仍仅放行不访问外部状态、不调用其他方法的独立叶子方法。构造器、静态初始化器、生成桥接/合成方法、组件中的声明同步方法及 `hashCode/finalize` 等特殊对象契约仍保留。同步块已支持不代表所有声明同步方法都已开放。跳过项始终计入覆盖率分母,并保留实际分类。

精确必保清单同样受方法兼容检查约束,必保项被排除时任务失败,不会自动降级。所有候选都不可转换时仍返回 `PVM2_ZERO_METHODS`。本轮镜像协议为 v9,需要使用配套打包器和运行时重新加固;旧运行时拒绝新镜像,新运行时继续读取旧协议。覆盖报告只证明选择范围内的产物转换,不代表全部关键业务或全部运行路径已验收。

兼容排除的本地原因带有首个阻断条件,例如 `ui_runtime_glue:virtual_contract`;脱敏摘要分别统计特殊对象方法或虚方法契约未验证、同步方法及叶子方法白名单之外的指令,并兼容旧打包器返回的异常区排除原因。它们表示准入限制,不能据此认定方法一定是生命周期回调。旧报告没有细分信息时仍按原分类展示,不能反推具体方法体。基础预检通过也不代表 PVM2 方法覆盖已经通过。

Expand Down
2 changes: 1 addition & 1 deletion shield-stub/compat/api19-rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion shield-stub/compat/api19-rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mocikashield-api19"
version = "1.8.4"
version = "1.8.5"
edition = "2021"
license = "MIT OR Apache-2.0"
build = "../../src/main/rust/build.rs"
Expand Down
2 changes: 1 addition & 1 deletion shield-stub/src/main/rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mocikashield"
version = "1.8.4"
version = "1.8.5"
edition = "2021"
license = "MIT OR Apache-2.0"

Expand Down
8 changes: 5 additions & 3 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ Windows classpath 分隔符改为 `;`。输出目录必须不存在。检查恢

## PVM2 必保方法清理与 v8 声明参数

`make test-pvm2` 验证 schema 2 报告及认证策略,`make test-pvm2-runtime` 用实际 Java 编译器/加密器生成三种 ISA 的普通、必保和异常镜像,再由生产 C++ 在宿主 JVM 中执行。断言覆盖普通缓存保留、必保方法正常/异常退出清理、递归与跨线程最后引用释放、8 线程竞争,以及旧 v6 解析和未知/截断策略拒绝。可用 `PVM2_SANITIZER=thread python3 tests/scripts/check-pvm2-runtime.py` 检查竞争;默认使用 ASan/UBSan。宿主结果不代替 Android 设备或性能验收。
`make test-pvm2` 验证 schema 2 报告及认证策略,`make test-pvm2-runtime` 用实际 Java 编译器/加密器生成三种 ISA 的普通、必保和异常镜像,再由生产 C++ 在宿主 JVM 中执行。断言覆盖普通缓存保留、必保方法正常及异常返回后的清理、递归和并发、认证失败不使用明文及阻断时的清理。

同一检查包含有限的结构变换试验:六种整数比较、条件反转加跳转、三种 ISA,在生产解释器中执行 900 次边界对照;恢复检查对 180 个随机镜像再次规范化比较关系。该规则仍可跨构建复用,因此没有将这种简单变换启用为生产抗还原策略。接收者准入另覆盖等值边、未知值、字段/数组访问的正常边、对象与结果共用寄存器、绕过访问的分支汇合、宽值双寄存器覆盖、lit8 高位目标和异常入口覆盖 `this` 的正反例。写寄存器范围复用已锁定 dexlib2 的指令元数据。 新增异常边只传播实际可抛异常指令执行前的状态,汇合所有前驱;`instanceof` 成功边可传播源对象非空,任何源/结果写入及不一致汇合都会使关系失效。生产 JNI 对照另覆盖两种语义、三种 ISA 的 900 次类型检查和 catch 路径。
可空接收者不再因分支或异常区被静态拒绝。生产 JNI 对照覆盖两种接收者语义、三种 ISA 的 900 次正常与异常路径;结构检查仍拒绝损坏指令、非指令边界的跳转、错误载荷、越界调用寄存器及异常处理表。代码长度检查覆盖 4096/4097 个代码单元边界、数组载荷展开超过 65535 字节的拒绝,以及不丢失字符的 UTF-8 编码边界。

v8 在寄存器映射后追加参数数量及规范化类型表,并与镜像一起认证。对象包装类按声明保留为引用,`long`/`double` 仅在声明为基本类型时占两格。检查覆盖五类包装引用、null、混合宽参数、错误数量/类型拒绝,以及 v6/v7 解析兼容。新产物必须重建标准资源和 Packer;旧资源在打包前被版本校验拒绝。
v8 追加经认证的参数类型表,包装对象按声明保留引用。v9 的 `invoke-super` 方法引用记录 `直接父类|原引用类型->方法签名`,类调用从声明父类分派,接口调用保留原接口;旧运行时因协议版本不匹配拒绝新镜像。新运行时仍能解析 v6/v7/v8,未知未来版本必须拒绝。必须一起重建打包器和标准壳资源。

`ANDROID_SDK_ROOT=/path/to/sdk python3 tests/scripts/check-pvm2-art.py --output target/pvm2-art --serial 明确设备序列号` 生成自有 Java/D8 整包,实际验证未加固基线、加固首次启动、再次启动、覆盖安装及装回基线。默认 46 个方法通过最终产物验证,每次执行 525 项断言,覆盖字段/数组/对象状态、调用、空值和异常、锁竞争、较大方法、字符串空字符/补充平面字符/驻留身份,以及祖先引用和派生接收者下的父类调用。`--min-api 26` 额外保留原生接口默认方法,默认 23 验证 D8 脱糖;`--without-resource-table` 验证无资源表。省略 `--serial` 只构建检查,不能当作执行通过。同一设备重复安装必须复用输出目录内的测试证书。

加载器复制优化的检查为 `shield-stub` 的 `testReleaseUnitTest` 与 `cargo test --manifest-path shield-stub/src/main/rust/Cargo.toml bin_loader`:前者检查完整 ZIP 字节及伪造长度拒绝,后者检查借用密文原地址、v5/v6 全部截断位置和原认证往返。`MOCIKA_RUNTIME_PROFILE=1` 的诊断构建可输出冷解密和缓存命中阶段的 Java/Native 堆分配观测;发布性能对照使用未启用诊断的资源,堆分配数也不等于 PSS 或泄漏结论。

Expand Down
Loading
Loading