三虫科技
三虫科技
  • 发布:2026-10-30 19:03
  • 更新:2026-10-30 20:21
  • 阅读:151

【报Bug】uni-app x 蒸汽 v2:libuniappx.so 依赖 libc++_shared.so,加固后 dlopen 失败启动崩溃

分类:uni-app

产品分类: uniapp/App

PC开发环境操作系统: Windows

PC开发环境操作系统版本号: Windows 10 19045

HBuilderX类型: 正式

HBuilderX版本号: 5.33

手机系统: Android

手机系统版本号: Android 13

手机厂商: 小米

手机机型: Xiaomi Pad 6 (23046RP50C) / Android 13

页面类型: vue

vue版本: vue3

打包方式: 云端

项目创建方式: HBuilderX

App下载地址或H5⽹址: https://cocos.quwanci.com/apk-releases/1.0.66-hardened-20261028224029.apk

示例代码:

解析 so 依赖与符号(Python3,无需第三方库)

用法: python analyze.py <apk路径>

import struct, zipfile, sys

def dyn(data):
e_shoff = struct.unpack_from('<Q', data, 0x28)[0]
esz = struct.unpack_from('<H', data, 0x3A)[0]
enum = struct.unpack_from('<H', data, 0x3C)[0]
secs = []
for i in range(enum):
off = e_shoff + i esz
t = struct.unpack_from('<I', data, off + 4)[0]
o = struct.unpack_from('<Q', data, off + 0x18)[0]
sz = struct.unpack_from('<Q', data, off + 0x20)[0]
lk = struct.unpack_from('<I', data, off + 0x28)[0]
ent = struct.unpack_from('<Q', data, off + 0x38)[0]
secs.append(dict(t=t, o=o, sz=sz, lk=lk, ent=ent))
needed, dynstr = [], None
for s in secs:
if s['t'] == 6:
dynstr = secs[s['lk']]
for j in range(s['sz'] // s['ent']):
tag, val = struct.unpack_from('<qQ', data, s['o'] + j
s['ent'])
if tag == 0: break
if tag == 1: needed.append(val)
break
rd = lambda b, o: data[b+o:data.index(b'\x00', b+o)].decode('utf-8', 'replace')
return [rd(dynstr['o'], x) for x in needed] if dynstr else []

z = zipfile.ZipFile(sys.argv[1])
for n in z.namelist():
if n.endswith('libuniappx.so'):
print('DT_NEEDED:', dyn(z.read(n)))

本机实测输出:

DT_NEEDED: ['libc.so','liblog.so','libandroid.so','libjnigraphics.so',

'libdl.so','libm.so','libc++_shared.so']

操作步骤:
  1. 用 HBuilderX 5.33 云打包一个 uni-app x 蒸汽 v2 的 Android 包(本项目 vapor-render-target=bytecode)
  2. 用 XopProtector 0.6.29 加壳:
    java -jar protector-packer.jar app.apk -o out.apk --shell-dir shell-files --protect-so-max-file-mb 40
  3. zipalign + apksigner 重签
  4. 安装到小米平板 23046RP50C(Android 13 / HyperOS),点击图标启动

补充:不加壳的原包在同一台设备上启动完全正常(可直接对比验证)。
不加 --protect-so-max-file-mb 时壳会因 libuniappx.so 26MB > 默认 8MB 上限而跳过该库,
此时报 No implementation found ... Bridge.initBridge(so_plain 里根本没有这个库)。

预期结果:
  1. 加壳后启动正常,Bridge.initBridge 能完成 JNI 注册;
  2. 或从根上:libuniappx.so 不再与 libc++_shared.so 动态耦合,在 nativeLibraryDir 被重定向时仍可正常加载。

实际结果:

启动即崩(界面闪现后回桌面,无崩溃弹窗):

  • 小米平板 23046RP50C / Android 13:100% 复现
  • iQOO 13 / vivo Android 16:不复现(linker 更宽容,属侥幸)
  • 未加固原包:全部设备正常
protector.SoLoad: L2 fail .../libuniappx.so err=dlopen failed:  
cannot locate symbol "_ZTTNSt6__ndk119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE"  
Zygote: Process 9974 exited due to signal 11 (Segmentation fault)

bug描述:

结论

建议 libuniappx.so 改为静态链接 libc++,消除对 libc++_shared.so 的动态依赖。

现象

uni-app x 蒸汽 v2 的包经过第三方加固壳(XopProtector 0.6.29)处理后,启动崩溃:

E Bridge  : Failed to load native library 'uniappx': dlopen failed:  
cannot locate symbol "_ZTTNSt6__ndk119basic_ostringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE"  
referenced by ".../code_cache/protector/so_plain/libuniappx.so"  

E AndroidRuntime: java.lang.UnsatisfiedLinkError: No implementation found for  
  void io.dcloud.uniappxv.bridge.Bridge.initBridge(float, android.content.res.AssetManager,  
  java.lang.String, boolean, boolean)  
  at io.dcloud.uniappxv.bridge.Bridge.initBridge(Native Method)

同类缺失符号还有 _ZTVNSt6__ndk13pmr25monotonic_buffer_resourceE、_ZTVNSt6__ndk117bad_function_callE。

成因

libuniappx.so 的 DT_NEEDED 含 libc++_shared.so(完全合法的 ELF 依赖)。加固壳会把
nativeLibraryDir 指向自己的解密目录 so_plain 并置于库搜索路径首位;而壳自身
libprotector.so 也依赖 libc++_shared.so,因此它必须把 libc++ 留在原始 lib 目录、
不复制进 so_plain。结果位于 so_plain 的 libuniappx.so 找不到 libc++ 符号。

这是符合 Android 规范的行为链,但客观上让 uni-app x 产物与多数加壳方案不兼容。

关键数据

libuniappx.so                     26.36 MB  
libc++_shared.so                   1.31 MB  
libuniappx 自身已导出的 __ndk1 符号   2710 个   ← 已大量静态实例化  
libuniappx 仍动态导入的 C++ 符号         226 个  
其中 libc++_shared 未能解析的              0 个   ← 可 100% 静态化

它现在是半静态半动态——模板在 so 内实例化,只有非模板实现(std::terminate、
异常类型信息、__shared_weak_count 等)走动态导入。改全静态几乎不增体积,
且可从 APK 移除 libc++_shared.so(1.31 MB),净体积基本持平。

建议

set(ANDROID_STL c++_static)                                 # 方案 A  
target_link_libraries(uniappx PRIVATE -static-libstdc++)     # 方案 B

收益

  1. 解锁第三方加固/加壳兼容性(本文实际触发场景)
  2. 消除一类崩溃:任何重定向 nativeLibraryDir 的环境(加固壳、插件化、部分 ROM 库隔离策略)
  3. 减小安装包:可移除 libc++_shared.so 1.31 MB
  4. 减少启动期动态链接开销

补充:该缺陷在部分机型上被掩盖

同一份产物在 iQOO 13 / vivo(Android 16)可正常启动,在小米平板 23046RP50C
(Android 13 / HyperOS)必崩。各厂商 linker 对「库搜索路径缺少依赖库」的容忍度不同,
所以这类问题极易漏到线上,用户侧表现为「只有某个牌子的手机闪退」,极难归因。

2026-10-30 19:03 负责人:无 分享
已邀请:
DCloud_App_Array

DCloud_App_Array

这个错误是加固工具破坏了动态库相互依赖加载导致的问题,可以尝试更新XopProtector到最新版本。
将 libc++_shared.so 强制配置为加固或同步至壳的解密目录。
libc++_shared.so是很常见的共用动态库,理论上加固工具应该支持的。

要回复问题请先登录或注册