移动应用加固 西安守界御盾信息安全技术有限责任公司 145 views

SO 文件名不等于 SO 载体可读:御盾 S21-R43 native 载体静态与动态烟测

SO 文件名不等于 SO 载体可读:御盾 S21 R43 native 载体静态与动态烟测 结论:本轮测评目标是验证“SO/native 载体是否能被低成本静态读懂”。最新 release 候选呈现 native、assets 与入口代理链分层承载;同批 debug 候选完成安装和入口拉起。可以公开的判断是:SO 表面经过符号收敛,核心载体不等同于普通 EL

从阅读进入评估 如果你正在评估 App 加固方案,可以先看官网能力边界,再提交一个真实包做 PoC。
查看御盾官网 提交内测评估

结论:本轮测评目标是验证“SO/native 载体是否能被低成本静态读懂”。最新 release 候选呈现 native、assets 与入口代理链分层承载;同批 debug 候选完成安装和入口拉起。可以公开的判断是:SO 表面经过符号收敛,核心载体不等同于普通 ELF 可读文件。

摘要

上一轮文章回答的是“JADX 能不能直接还原原始算法”。这轮不能再重复同一个角度,所以把目标换成 SO/native 载体专项:攻击者看到包里有 .so 外观的载体时,能不能把它当成普通 ELF 直接读懂;native 层是否保留大量符号;入口启动是否仍然停留在普通 Application/Activity 流程;同批可安装候选能否至少完成安装和入口烟测。

本轮采用同批次分工:最新 release 输出用于静态结构评估,因为它代表当前加固产物的发布面;同批 debug 输出用于动态烟测,因为它签名有效,可以在测试设备上安装并拉起入口。这个分工很重要:release 候选不满足安装前置条件,所以不能拿它写动态通过;debug 候选完成安装和入口拉起,也不能被扩大成完整业务链路通过。

公开结论保持克制:一是 native 载体均为 arm64 方向,主要 ELF 载体为 stripped,未保留普通 .symtab;二是 assets 中的大体量核心载体虽然带有 SO 外观,但静态识别不是普通 ELF,这意味着“看到 SO 文件名”不等于“能按普通 SO 直接分析”;三是 Manifest 与反编译面显示启动链有代理层和组件工厂介入;四是同批 debug 候选安装成功、入口可拉起,并在短时间窗口内保留进程状态。以上足够支撑一篇 SO/native 载体专项文章,但不足以宣称二次打包、危险环境、完整业务链路、动态脱壳全部通过。

读者对象

本文适合三类读者。第一类是正在做 App 加固 PoC 的安全负责人,他们需要知道 SO 保护不能只看“包里有没有 .so”,还要看 ELF 是否可读、符号是否收敛、native 与 assets 是否分工。第二类是移动端研发负责人,他们关心加固后启动链、组件实例化和 native 装载是否会改变调试与排障方式。第三类是逆向测评人员,他们需要一份不会泄露样本细节、但能说明测评过程和证据边界的公开记录。

如果你的验收口径仍停留在“JADX 打不开就是好”“有 SO 就是 SO 加固”“安装成功就是完整通过”,这篇文章可以作为修正模板。它把 release 静态、debug 动态、native 表面、assets 载体和公开边界拆开处理,避免把不同层级的结论混成一句空话。

核心结论

  • 本轮主目标是 SO/native 载体静态可读性与同批动态烟测,不重复上一轮 DEX/JADX 还原目标。
  • 最新 release 候选是静态主对象,同批 debug 候选是动态烟测对象,二者不能混用结论。
  • release 候选呈现单 DEX、多个 arm64 native 载体、assets 核心载体和入口代理链。
  • debug 候选签名有效,安装成功,入口可拉起,并在观测窗口内保持进程状态。
  • 主要 ELF 载体为 stripped,普通符号表未保留;动态符号表仅保留运行链接所需表面。
  • assets 中的大体量核心载体带 SO 外观,但静态识别不按普通 ELF 展开,不能把文件名当成可读性判断。
  • native runtime 中出现非语义化 section 命名,降低了按 section 名直接理解业务语义的价值。
  • 明文面没有形成可公开的 IP 字面量泄露证据;少量敏感词模式只保留内部复核,不写成公开泄露。
  • 本轮不声明完整业务通过、不声明动态脱壳通过、不声明二次打包闭合、不声明危险环境通过。
  • 后续应拆成四个专项:SO 载体深度静态、二次打包重签闭合、危险环境矩阵、DEX dump 与运行态窗口。

测评目标与非目标

目标 本轮状态 已做动作 可公开结论 非目标与边界
SO/native 载体可读性 主目标,已执行 对 release 与 debug 候选做容器、ELF、字符串和 section 表面检查。 普通文件名不能代表普通 ELF 可读性,主要 native 表面经过符号收敛。 不公开 SO 名、BuildID、符号、偏移、section 原始名称。
同批动态烟测 已执行 使用签名有效的 debug 候选安装并拉起入口。 安装和入口拉起成立,进程在观测窗口内存在。 不公开设备、包名、命令、Activity 和日志。
原始算法还原 复用上一轮边界 只观察 Java 面与 native/asset 分工,不重复写 DEX 还原结论。 本文不把算法还原作为新结论。 不公开源码、类名、调用链。
DEX dump/脱壳 未执行 仅列入后续专项。 本轮不声明 dump 成功或失败。 不公开 dump、注入、attach 或内存步骤。
二次打包与重签 未执行 仅把 sealed/签名/顺序绑定作为后续目标。 本轮不声明二次打包闭合。 不公开改包、重签、回包流程。
危险环境矩阵 未执行 只完成普通测试环境安装和入口烟测。 本轮不声明 root、Hook、模拟器对抗结论。 后续需要单独设备矩阵。

事实依据与脱敏证据

# 证据来源类型 脱敏后的观察事实 支撑的工程判断 公开化边界
1 APK 候选记录 同批次 release 与 debug 输出都在本轮更新,release 约 78 MB,debug 体量更大。 本轮不是复写旧文章,而是针对新构建做 SO/native 专项。 不公开路径、文件名、hash、签名细节。
2 ZIP 容器枚举 release 呈现单 DEX、3 个 native 载体、2 个 assets 文件;debug 呈现单 DEX、4 个 native 载体、2 个 assets 文件。 native 与 assets 是本轮保护面核心,Java 面不是唯一入口。 不公开真实文件清单。
3 签名校验 release 不作为安装对象;debug 签名有效,可进入安装烟测。 静态候选和动态候选分工明确,避免把不可安装 release 写成动态结论。 不公开证书、摘要、签名原文。
4 Manifest 结构 两个候选均观察到组件工厂介入,且 native 不以普通解压路径展开。 启动链和组件实例化阶段存在保护介入。 不公开真实组件、类名、authority。
5 JADX 恢复面 release 反编译得到 47 个 Java 文件,debug 得到 98 个 Java 文件,工具均报告少量错误。 Java 面能看到壳层与代理结构,但不能直接代表 native/asset 核心。 不公开源码、包名、类名。
6 native ELF 表面 主要 ELF 为 arm64,.symtab 不存在,动态符号表只保留链接表面。 低成本按符号导航 native 逻辑的路径被压缩。 不公开 SO 名、BuildID、符号、偏移。
7 native section 表面 runtime 载体除常规 section 外,还出现非语义化 section 命名。 不能依赖 section 名直接推断业务语义。 不公开原始 section 名和数量细节。
8 assets 载体 最大 assets 载体体量巨大,且静态识别不是普通 ELF。 “SO 外观”不等于“普通 SO 可读”,assets 载体需要独立专项。 不公开 assets 名、格式、内容。
9 字符串面 IP 字面量未形成公开泄露证据;少量敏感模式仅作为内部复核项。 发布面可写“未形成可公开明文泄露证据”,不能写绝对无敏感词。 不公开 strings 输出。
10 动态安装 同批 debug 候选安装成功。 动态烟测不是空白,具备后续设备矩阵基础。 不公开设备、包名、安装命令。
11 入口拉起 入口可拉起,观测窗口内进程存在,未见 ANR。 可以写入口烟测成立,但不能写完整业务通过。 不公开 Activity、窗口、进程、日志。
12 流程边界 本轮没有执行二次打包、危险环境、运行态 dump 和 Hook。 公开文章只写已验证正向事实,不把后续专项提前写成结论。 不公开任何可复现攻击步骤。

原始报告事实映射

原始工具事实 公开转述 支撑判断 未公开边界
同批 release/debug 输出同时更新。 本轮使用新构建,不重复上一轮 DEX 文章。 分析对象新鲜。 路径、hash、文件名。
release 不通过安装签名前置。 release 只用于静态评估。 不伪造动态结论。 原始签名错误。
debug 通过签名校验。 同批 debug 可进入安装烟测。 动态前置成立。 证书摘要。
release/debug 均有 native 与 assets 载体。 native/assets 是本轮主分析面。 SO/native 专项成立。 文件清单。
大体量 assets 载体不是普通 ELF。 SO 外观不等于普通 ELF 可读。 需要 assets 载体专项。 assets 名和内容。
主要 ELF stripped 且无普通符号表。 native 符号导航线索收敛。 SO 表面保护成立。 符号、偏移、BuildID。
debug 安装成功。 同批可安装候选通过安装烟测。 动态基础成立。 设备、命令、包名。
入口可拉起并有进程状态。 入口烟测成立。 可进入后续兼容矩阵。 Activity、日志、进程。

动态时间线

阶段 动作 脱敏观察 判断 边界
1 候选检索 找到同批次新 release 和 debug 输出。 本轮有新输入。 不公开路径。
2 签名判断 release 不作安装对象,debug 可安装。 动静候选分工明确。 不公开证书。
3 静态容器枚举 release/debug 均呈现 DEX、native、assets 分层。 分析目标转向 SO/native 载体。 不公开清单。
4 Manifest 观察 组件工厂与入口代理链存在。 启动阶段有保护介入。 不公开组件。
5 native 表面 ELF stripped,无普通符号表。 符号导航成本提高。 不公开符号和偏移。
6 assets 表面 大体量载体不按普通 ELF 展开。 文件名不是可读性证据。 不公开名称。
7 安装烟测 debug 候选安装成功。 可安装性成立。 不公开设备和命令。
8 入口烟测 入口可拉起,观测窗口内进程存在。 入口链路可进入后续矩阵。 不公开包名、Activity、日志。
9 结论收束 本轮不写完整业务通过。 防止过度营销化。 后续专项另做。

攻击工具矩阵

攻击阶段 常见工具/方案类别 攻击者目标 本轮纳入方式 本轮结论 后续专项
Java 反编译 JADX/JEB 类工具 从 Java 面找原始业务算法。 已观察 release/debug 反编译面。 Java 面主要体现壳层与代理结构。 不重复上一轮 DEX 文章。
smali/资源解包 apktool/baksmali 看 Manifest、资源、native 和 assets 分布。 已做 nosrc 解包。 native/assets 是核心分析面。 后续可做回包对比。
SO 静态分析 ELF 工具、strings、section/dynsym 观察 找符号、section、依赖和 JNI 入口。 已做 file、字符串、ELF 元数据归纳。 stripped 与非语义化 section 降低直读价值。 后续可做 IDA/Ghidra 深度。
DEX dump/脱壳 frida-dexdump、内存提取 拿运行态 DEX。 本轮未执行。 不写 dump 结论。 单独专项。
注入/Hook Frida、Xposed、inline hook 挂 native/Java 关键点。 本轮未执行。 不写 Hook 阻断结论。 单独 observer。
调试/跟踪 ptrace、gdb/lldb、IDA 动调 跟踪 native 路径。 本轮只做静态调试面线索。 不写调试已阻断。 单独调试抗性。
二次打包 解包、改包、重签、回包 验证完整性闭合。 本轮未执行。 不写二次打包闭合。 单独 fail-closed。
危险环境 root、模拟器、Hook 框架 扩大动态观测窗口。 本轮未执行。 不写危险环境结论。 设备矩阵。

现象复核链

首次观察 复核动作 复核后的判断 仍然不能证明
release 与 debug 同批更新。 对候选时间、体量、签名状态分开记录。 release 适合静态,debug 适合动态。 不能把二者结论混用。
包里出现多个 SO 外观载体。 用文件识别和 ELF 解析复核。 有的是真 ELF,有的是 assets 载体,不应只看后缀。 不能公开 assets 内部格式。
native ELF stripped。 检查普通符号表和动态符号表。 普通符号导航线索收敛。 不能写 native 不可分析。
runtime 出现非语义化 section。 对 section 表面做归类。 section 名不能直接解释业务语义。 不能公开 section 原名。
debug 安装成功。 继续拉起入口并观察进程。 安装和入口烟测成立。 不能写完整业务通过。
入口拉起后进程存在。 查看窗口/进程状态与 ANR 观察。 初步启动链可进入后续矩阵。 不能写长期稳定或全机型兼容。
字符串面少量敏感模式命中。 与 IP、URL、secret 类模式分开处理。 未形成可公开明文泄露证据。 不能公开原始字符串。

主因链

本轮最核心的原因链是:攻击者不能只根据文件名判断 SO/native 载体可读性。release/debug 的容器都显示 native 与 assets 分层;大体量 assets 载体虽然带 SO 外观,但静态识别并不是普通 ELF;真正的 ELF 载体又呈现 stripped、无普通符号表、动态符号表收敛、runtime section 非语义化等特征。再结合 Manifest 中的组件工厂与入口代理链,可以得出一个克制但有价值的结论:低成本静态分析不能直接把这些载体当成普通 SO 源码替代品来理解,后续若要继续推进,必须进入 SO 深度静态、运行态加载点、调试抗性和二次打包闭合专项。

动态侧的原因链同样要收住:debug 候选安装成功、入口可拉起、观测窗口内有进程状态,这只证明同批可安装候选具备动态烟测基础;它不能替代 release 签名状态,不能证明完整业务长期稳定,也不能证明危险环境和 Hook 场景通过。把这些边界写清楚,文章才像测评记录,而不是宣传页。

技术拆解

1. 为什么不能只看 SO 文件名

Android 包里出现 .so 后缀,只说明它位于 native 或伪 native 的承载位置,不代表它一定是普通 ELF,也不代表 IDA/Ghidra 能按常规路径直接展开业务语义。本轮最典型的观察是:assets 中的大体量核心载体具备 SO 外观,但文件识别并不把它当成普通 ELF;相反,真正的 ELF 载体位于 native 目录,体量、动态依赖、section、动态符号和字符串面各自承担不同角色。

这对防守侧是有价值的:核心材料可以不直接暴露在 Java 源码面,也不直接以普通 ELF 的方式在 assets 中展开。攻击者如果只做“后缀名枚举 + strings + 符号表”三步,很容易把载体分层理解错。

2. stripped 与动态符号表收敛

本轮主要 ELF 均未保留普通符号表。动态符号表仍然存在,这是 Android 动态链接需要的常规表面,但它并不等同于完整业务符号。公开表达时应写成“普通符号导航线索收敛”,不能写成“没有任何符号”或“完全不可逆”。

更细一点看,runtime 载体中存在非语义化 section 命名。section 仍然存在,程序仍然需要加载和执行;但 section 名不再提供直观业务语义。这种处理会降低低成本静态阅读效率,尤其是对依赖 section 名、字符串和导出符号快速定位的人来说。

3. 入口代理链与 native 装载的关系

Manifest 与反编译面显示,启动阶段不是简单进入原始业务 Activity。组件工厂、代理入口和 native 装载线索共同表明:保护逻辑在组件实例化和入口准备阶段就介入。这种介入不是为了“隐藏一个类名”这么简单,而是为了把后续类加载、native 准备、真实入口恢复和完整性校验放在一个受控链路中。

公开稿不能写真实组件名,也不能放调用链,但可以把工程意义说清楚:入口代理链让攻击者无法只从 Manifest 入口直接推导真实业务入口;native 装载线索说明真实逻辑恢复依赖运行态准备,而不是静态文件展开。

4. debug 动态烟测为什么仍然有价值

release 候选不满足安装前置,因此动态烟测使用同批 debug 候选。这个选择不是偷换概念,而是分工:release 用来看发布面结构,debug 用来看同批逻辑在测试环境能否安装和拉起入口。烟测结果显示安装成功、入口可拉起、观测窗口内进程存在。这足以说明“同批候选具备进入动态矩阵的基础”,但不能说明完整业务通过。

合格文章必须保留这条边界。如果把 debug 安装成功写成 release 完整通过,就是错误;如果因为 release 未签名就放弃所有动态证据,也是错误。正确做法是分开说。

工程落地

PoC 验收建议

验收项 做法 合格口径 不合格写法
SO/native 表面 统计 ELF、assets、section、dynsym、strings。 说明符号、载体和语义线索是否收敛。 只说“有 SO 保护”。
入口链 看 Manifest 与组件实例化线索。 说明入口是否有代理层介入。 公开真实组件名。
动态烟测 用可安装同批候选安装和拉起入口。 写安装、入口和观测窗口。 写完整业务通过。
明文面 做敏感模式统计。 写“未形成可公开泄露证据”。 贴完整 strings。
后续专项 拆成二次打包、危险环境、dump、Hook。 每个专项单独目标和证据。 一篇文章里全部宣称通过。

防御侧伪代码

public_evidence:
  measurement_goal: "SO/native carrier readability and same-build dynamic smoke"
  release_static:
    dex_count: 1
    native_carriers: "multiple arm64 carriers"
    asset_carriers: "large protected carrier plus auxiliary assets"
    manifest: "component factory and proxy entry observed"
    elf_surface: "stripped, no ordinary symbol table"
  debug_dynamic:
    signature_state: "installable test candidate"
    install: "success"
    launch: "entry can be started"
    process_window: "process observed during smoke window"
  boundaries:
    not_claimed:
      - "full business flow"
      - "dangerous environment pass"
      - "runtime unpacking pass"
      - "repackaging closure"
    private:
      - "paths, package names, component names, commands, logs, symbols, offsets"
assessment_flow:
  1. choose latest release candidate for static surface
  2. verify whether release candidate is installable
  3. choose same-build installable debug candidate for smoke test
  4. inspect DEX/native/assets/manifest separately
  5. classify SO-looking assets and real ELF carriers separately
  6. verify install and entry launch without publishing identifiers
  7. publish only supported positive observations
  8. move dump, hook, repackaging and hostile environment to separate gates

攻防视角

攻击者面对这类包,通常会按四步走:先看 Java 面,找不到就看 Manifest 入口;再看 native 目录,找 JNI 和导出符号;然后看 assets,判断是否有壳载体、加密材料或压缩包;最后才进入运行态调试、dump、Hook 和二次打包。本文覆盖的是前三步加一个安装烟测,不覆盖后四步的完整结论。

防守侧要做的是让每一步都没有“低成本直达”。Java 面不能直接给原始算法;Manifest 不能直接给真实业务入口;native 不能保留大量语义符号;assets 不能按普通文件直读;安装烟测不能被夸大成所有设备通过。这样写出来的公开文章才有可信度。

风险边界

本轮没有公开任何路径、包名、组件、命令、日志、符号、偏移、hash、签名证书或可复现攻击步骤。所有真实细节只进入私有证据包。

本轮没有执行以下结论:完整业务通过、二次打包闭合、危险环境通过、Frida/Hook 阻断、DEX dump 失败或成功、SO 脱壳失败或成功。这些都需要独立专项。本文只声明已经验证的正向事实:静态 native/SO 表面具备符号收敛与载体分层特征,同批可安装候选完成安装和入口烟测。

常见误区

误区一:包里有 SO 就等于 SO 已经安全

不对。SO 只是承载形态,安全性要看符号、section、依赖、字符串、加载时机和完整性链。本文的重点也不是“有 SO”,而是“SO 外观、真实 ELF、assets 载体和 stripped 表面必须分开评估”。

误区二:release 不能安装就说明加固失败

不对。当前 release 候选不作为安装对象,说明它不满足动态安装前置。动态烟测应选择同批可安装候选,而不是把签名状态误解成防护失败。

误区三:debug 能安装就说明 release 完整通过

也不对。debug 安装成功只能说明同批逻辑具备动态烟测基础,不能替代 release 发布验证,更不能替代多设备兼容、业务链路、危险环境和二次打包专项。

误区四:stripped 就等于无法逆向

不对。stripped 只说明普通符号导航线索减少。专业逆向仍可能通过控制流、字符串、动态调试和行为观察推进。公开文章应写“提高低成本分析门槛”,不能写“绝对不可逆”。

内链

FAQ

这篇文章能证明 SO 脱壳失败吗?

不能。本轮没有执行 SO 脱壳专项,只能证明普通 native 表面存在符号收敛、载体分层和 assets 非普通 ELF 载体等静态特征。SO 脱壳需要单独目标和工具链。

为什么 release 和 debug 要分开写?

因为 release 是当前发布面静态对象,debug 是同批可安装动态对象。把 release 的静态结构和 debug 的安装烟测混成一个结论,会制造不准确的公开材料。

安装成功是否代表兼容性通过?

不代表。安装成功和入口拉起只是动态烟测的第一步。兼容性需要设备矩阵、系统版本、CPU 架构、业务路径和异常闭合记录。

SO 文件名为什么不能作为可读性判断?

因为文件名只是容器标签。assets 里可以存在 SO 外观但非普通 ELF 的载体,native 目录里也可能有 stripped ELF。要判断可读性,必须看文件魔数、ELF 元数据、符号、section、字符串和加载路径。

下一步最值得测什么?

建议优先做四个专项:SO 深度静态与调试抗性、二次打包重签闭合、危险环境矩阵、DEX dump 与运行态窗口。每个专项都应单独成文,避免一篇文章承载过多结论。

御盾内测申请

需要针对自己的 App 验证加固策略?

提交项目平台和当前攻防问题,安全工程师会按业务复杂度安排人工审核。完整技术档案可在申请后补充。

Android加固 SO保护 native保护 VMP AppComponentFactory assets载体 动态烟测 御盾
相关阅读