广告位 · 页头(后台-模块管理-新建模块后替换此区域)

Android工厂安全测试如何落地?核心检查项与实现代码解析

工厂安全测试(Factory Security)并不是在产线跑一遍杀毒软件,而是围绕Android平台的安全启动链和硬件信任根做一系列确定性校验。产线设备需要保证从Bootloader到系统分区再到应用运行环境,每一层都处于可信状态。如果某个环节被绕过,后续使用时可能出现系统被root、支付环境异常或企业数据泄露等问题。因此,工厂安全测试的每一项结果都应当是可量化、可复现的,不能只凭人工观察。

Android工厂安全测试如何落地?核心检查项与实现代码解析

工厂安全测试的范围与目标

工厂安全测试覆盖启动链、系统完整性、密钥管理和DRM等几个维度。启动链验证主要看Bootloader是否处于锁定状态,以及AVB(Android Verified Boot)对boot、system、vendor等分区的哈希校验是否通过。系统完整性则涉及SELinux是否处于Enforcing模式、dm-verity是否真正启用。密钥管理主要检查Keymaster HAL是否正常,设备是否有硬件级密钥存储,比如StrongBox或TEE。DRM部分常见的是Widevine安全等级,L1表示内容解密在TEE中完成,L3则表示纯软件解密,很多视频应用会限制L3设备的清晰度。

和实验室阶段的安全测试不同,工厂安全测试必须考虑效率与稳定性。产线每台设备停留时间有限,测试项不能依赖网络请求或云端证书验证,因为工厂网络环境可能受限。通常采用离线判断方式,通过读取系统属性、调用系统API或检查证书链来完成。这就要求测试代码轻量、快速,并且能明确返回通过或失败的状态码,方便MES系统记录。

此外,工程模式与用户模式的安全策略可能存在差异。部分ODM设备在工程ROM中会临时解锁Bootloader或关闭SELinux方便调试,但在量产版本中必须恢复。工厂安全测试要能识别当前系统是否为正式用户构建,避免把工程机误判为安全设备,或者把用户机误判为不安全。

核心安全属性读取与验证方法

Android系统在启动过程中会把许多安全状态写入只读系统属性,这是工厂测试最直接的检查入口。常用的属性包括ro.boot.verifiedbootstate,它反映AVB校验结果,正常值应为green;ro.boot.flash.locked表示Bootloader锁定状态,量产设备应为1;ro.boot.vbmeta.device_state显示vbmeta分区的设备状态,locked为正常。此外,ro.build.version.security_patch可以读取安全补丁级别,用于确认系统是否包含最新的漏洞修复。

在产线测试机上,可以直接通过adb命令批量读取这些属性。下面是几个核心命令示例,可以集成到Shell脚本中,或者由测试APK通过Runtime.getRuntime().exec()调用。

# 检查AVB启动状态
adb shell getprop ro.boot.verifiedbootstate
# 检查Bootloader锁定状态
adb shell getprop ro.boot.flash.locked
# 检查vbmeta设备状态
adb shell getprop ro.boot.vbmeta.device_state
# 读取安全补丁级别
adb shell getprop ro.build.version.security_patch
# 检查SELinux模式
adb shell getenforce

需要注意的是,getenforce返回Enforcing才表示SELinux策略真正生效。如果返回Permissive,即使系统属性显示AVB为green,也不能判定安全测试通过。因为Permissive模式下大量策略违规只记录不拦截,攻击面会显著扩大。测试脚本应当同时检查SELinux状态,并且与前述属性做联合判断。

工厂测试APK中的自动化检查实现

如果产线没有配备adb环境,更常见的做法是把安全测试集成到一个专用的工厂测试APK中。APK可以调用Android SDK接口读取部分属性,并通过反射读取隐藏属性。以下代码演示了一个SecurityTestUtil类,它检查Bootloader锁定、AVB状态、SELinux模式和Keymaster硬件支持情况。

import android.os.Build;
import android.os.SystemProperties;
import java.lang.reflect.Method;
public class SecurityTestUtil {
    public static final int RESULT_PASS = 0;
    public static final int RESULT_FAIL_BOOTLOADER = 1;
    public static final int RESULT_FAIL_AVB = 2;
    public static final int RESULT_FAIL_SELINUX = 3;
    public static final int RESULT_FAIL_KEYMASTER = 4;
    public static int runAllTests() {
        if (!checkBootloaderLocked()) return RESULT_FAIL_BOOTLOADER;
        if (!checkAvbState()) return RESULT_FAIL_AVB;
        if (!checkSelinuxEnforcing()) return RESULT_FAIL_SELINUX;
        if (!checkKeymasterSupport()) return RESULT_FAIL_KEYMASTER;
        return RESULT_PASS;
    }
    private static boolean checkBootloaderLocked() {
        String locked = getSystemProperty("ro.boot.flash.locked");
        return "1".equals(locked) || "true".equalsIgnoreCase(locked);
    }
    private static boolean checkAvbState() {
        String state = getSystemProperty("ro.boot.verifiedbootstate");
        String deviceState = getSystemProperty("ro.boot.vbmeta.device_state");
        return "green".equals(state) && "locked".equals(deviceState);
    }
    private static boolean checkSelinuxEnforcing() {
        String enforcing = getSystemProperty("ro.build.selinux");
        if ("1".equals(enforcing)) return true;
        try {
            Class<?> c = Class.forName("android.os.SELinux");
            Method m = c.getMethod("isSELinuxEnforced");
            return (Boolean) m.invoke(null);
        } catch (Exception e) {
            return false;
        }
    }
    private static boolean checkKeymasterSupport() {
        // 检查Keymaster HAL版本,实际项目可通过KeyChain或Keystore接口验证
        String km = getSystemProperty("ro.hardware.keystore");
        return km != null && !km.isEmpty();
    }
    private static String getSystemProperty(String key) {
        try {
            Class<?> sp = Class.forName("android.os.SystemProperties");
            Method get = sp.getMethod("get", String.class);
            return (String) get.invoke(null, key);
        } catch (Exception e) {
            return null;
        }
    }
}

这段代码中checkSelinuxEnforcing优先读取ro.build.selinux属性,如果不可用则通过反射调用android.os.SELinux.isSELinuxEnforced()。需要注意的是,Android 8.0之后部分系统属性对普通应用不可见,测试APK需要签名成系统应用或使用android:sharedUserId="android.uid.system",并且运行在系统进程。否则SystemProperties.get可能返回空值。此外,Keymaster检查只是简单判断属性是否存在,实际工厂测试中应通过KeyStore.getInstance("AndroidKeyStore")生成密钥并验证其是否存储在TEE中,这需要额外的证书链解析和Attestation验证。

常见失败场景与排查建议

工厂安全测试失败时,产线人员往往会直接判定主板问题,但实际上多数失败与软件配置相关。最常见的是Bootloader锁定状态为0。这通常是因为烧录的工程版本带有解锁标志,或者前一个工位执行了fastboot oem unlock。此时需要检查版本包是否为量产签名版本,以及烧录后是否执行了锁定命令fastboot oem lock或fastboot flashing lock。

另一种高频失败是AVB状态为yellow或orange。yellow表示使用了非官方的签名密钥,比如开发阶段的自签名vbmeta;orange则说明设备允许解锁启动,通常与Bootloader锁定状态有关。遇到这种情况,要核对vbmeta.img是否由原厂的release key签名,并且boot.img、system.img的哈希是否包含在vbmeta的校验描述中。如果修改了系统分区但未重新生成vbmeta,也会导致校验失败。

Widevine等级异常也是工厂测试中容易漏掉的问题。有些设备在工厂测试APK中只检查了getprop ro.boot.verifiedbootstate,却没有验证DRM信息。实际上,即使启动链正常,如果TEE中的Widevine keybox未写入或过期,DRM等级会降为L3。此时可以通过dumpsys media.drm命令查看Widevine安全等级,若为L3则需要检查keybox烧录工位是否成功,以及设备时间是否同步。DRM证书验证依赖时间戳,如果产线设备时间错误,也可能导致判定失败。

排查时建议开启adb logcat -s keystore avb libc抓取相关日志,同时记录每次测试时的属性快照。通过对比通过设备和失败设备的属性值,可以快速缩小问题范围。工厂测试代码中应当输出完整的失败码和属性值,而不是只返回一个布尔值,这样便于MES系统追溯。

广告位 · 文章底部(后台-模块管理替换此区域)