ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Ghost Downloader Android 原生二进制执行方案解析:linker64 绕过 W^X 限制与 FFmpeg 预打包约束

Ghost Downloader Android 原生二进制执行方案解析:linker64 绕过 W^X 限制与 FFmpeg 预打包约束 Ghost Downloader Android 原生二进制执行方案解析linker64 绕过 W^X 限制与 FFmpeg 预打包约束【免费下载链接】Ghost-Downloader-3The only downloader you need. 下载器的集大成者。项目地址: https://gitcode.com/GitHub_Trending/gh/Ghost-Downloader-3本篇文章以 Ghost Downloader 架构决策记录ADRdocs/adr/0005-android-native-binary-exec-via-linker64.md 为核心系统讲解 Android 10 平台下 W^X 内存保护与 SELinux 对可执行文件的限制以及项目如何通过/system/bin/linker64间接执行运行时热安装的 N_m3u8DL-RE 二进制同时解释为何 FFmpeg 必须预打包在nativeLibraryDir中。读完本文你将理解 Android 平台上下载型应用分发原生可执行文件的底层约束掌握 linker64 调用技巧、app_process方案的取舍理由以及该方案对 APK 体积约 20 MB和更新策略的实际影响。一、问题背景Android 10 的 W^X 与 SELinux 双重限制Android 从版本 10API 29开始强制执行W^XWrite XOR Execute安全策略位于 APK 的nativeLibraryDir目录之外的文件一律不能被当作可执行文件直接运行。同时SELinux 的 exec 权限检查也进一步收紧了对应用私有目录中二进制文件的执行限制。这对 Ghost Downloader 这类下载器的集大成者提出了一个现实问题项目在 Android 端依赖若干第三方原生工具链如 HLS/DASH 流媒体下载工具 N_m3u8DL-RE、视频混流工具 FFmpeg这些二进制要么随 APK 预打包要么在运行时从网络获取。如果采用运行时下载到应用私有目录再直接执行的思路在 Android 10 上会直接触碰 W^X 与 SELinux 红线而失败。从仓库源码可以印证这一平台差异的存在app/platform/android.py 中通过hasattr(sys, getandroidapilevel)判定IS_ANDROID并通过jnius反射ApplicationInfo.nativeLibraryDir获取原生库目录app/platform/android.py。该目录正是 Android 允许执行二进制的唯一位置。二、核心方案通过/system/bin/linker64 path间接执行ADR 给出的解决方案是下载得到的原生二进制N_m3u8DL-RE必须通过/system/bin/linker64 path方式调用以绕过 SELinux 的 exec 限制。其原理是linker64是 Android 系统自带的动态链接器由系统域system domain授权位于受信任的系统路径。将可执行文件作为linker64的参数传入后二进制本身不再以被 exec 的对象身份接受 SELinux 检查而是作为链接器加载的动态对象被解析执行从而规避了对应用私有目录文件的 exec 拒绝。ADR 明确记录该方案已在真机on-device上验证通过。这一方案意味着 Ghost Downloader 可以安全地把 N_m3u8DL-RE 的下载产物放在 APK 之外的目录如应用数据目录运行时通过类似subprocess的方式以linker64 path形式拉起而不是把二进制预打包进 APK。为什么不能直接用app_processADR 同时记录了一个被否决的候选方案使用app_process替代linker64。否决理由是app_process面向 Java 类加载场景设计需要完整的 Java 类加载环境并不适合加载纯原生plain native可执行文件。linker64作为纯原生二进制的系统链接器才是与 N_m3u8DL-RE 这类 C/C 编译产物匹配的入口。三、FFmpeg 必须预打包N_m3u8DL-RE 内部 exec 的隐蔽陷阱ADR 强调了另一个关键约束FFmpeg 必须保持预打包在nativeLibraryDir中通过gd3ffmpeg这个 python-for-androidp4arecipe 实现。原因在于 N_m3u8DL-RE 的内部实现它下载完 HLS/DASH 分片后会以子进程方式内部 exec 调用 ffmpeg来完成音视频混流muxing与 AES-128 解密这一点也在 docs/adr/0002-minimal-lgpl-self-built-ffmpeg.md 中有所呼应。关键在于N_m3u8DL-RE 的内部 exec 调用不会经过我们的 linker64 包装如果 ffmpeg 位于nativeLibraryDir之外N_m3u8DL-RE 的内部 exec 会静默失败fails silently用户只会得到没有混流结果的残缺输出而不会有任何明显报错。因此用 linker64 包装解决所有二进制执行问题的通用思路在此不成立linker64 方案只适用于由 Ghost Downloader 自己发起的子进程调用对于 N_m3u8DL-RE 内部再派生的 ffmpeg 子进程唯一可靠的路径就是把 ffmpeg 放进系统允许执行的nativeLibraryDir。四、收益与边界只有 N_m3u8DL-RE 可以运行时热安装综合以上分析ADR 得出的最终边界是二进制分发方式原因FFmpeg / ffprobe预打包进nativeLibraryDirp4a recipegd3ffmpegN_m3u8DL-RE 内部 exec 不经过 linker64 包装必须保证可执行N_m3u8DL-RE运行时热安装linker64 间接执行体积约 20 MB且独立于应用发版更新这样做的直接收益是APK 体积节省约 20 MB——N_m3u8DL-RE 不再随 APK 分发用户需要时才下载。同时 N_m3u8DL-RE 的版本更新节奏与 Ghost Downloader 主应用解耦无需为了升级下载器而整体更新 APK。ADR 同时明确记录该热安装实现当前处于延后deferred状态。从仓库当前源码也可以看到这一状态的印证features/m3u8_pack/config.py 中M3U8Runtime.path()在 Android 平台返回nativeLibraryDir()/libnm3u8dlre.so即当前实现仍从nativeLibraryDir读取 N_m3u8DL-RE同一文件中canInstall not IS_ANDROIDfeatures/m3u8_pack/config.py说明 Android 端尚不支持运行时一键安装compose/build-scripts/fetch_android_libs.sh 构建脚本仍将 N_m3u8DL-RE 的 android-bionic-arm64 产物下载后重命名为libnm3u8dlre.so放入jniLibs/arm64-v8a随 APK 预打包。可见 ADR 描述的 linker64 热安装是既定目标方向而当前仓库的 Android 构建链路仍采用全部预打包的过渡实现。五、被否决方案对比为什么不全量打包ADR 记录了两种被考虑过的替代方案其取舍逻辑值得借鉴方案一将所有二进制打包进nativeLibraryDir——被否决。虽然这是最直接、最符合 Android 平台惯例的做法但它不符合长期目标N_m3u8DL-RE 体积约 20 MB且更新独立于应用全量打包既让 APK 膨胀又迫使任何下载器版本升级都必须走完整的应用更新流程。这正是预打包 ffmpeg 热安装 N_m3u8DL-RE混合策略的动机来源。方案二使用app_process替代linker64——被否决。app_process需要 Java 类加载机制适用于执行 Java/Android 框架代码对纯原生可执行文件并不合适因此选定linker64。六、仓库实现印证Android 平台二进制的实际解析路径ADR 决策在仓库多个模块中得到落实可以从源码层面完整还原 Android 端二进制解析链路1. 统一获取nativeLibraryDirapp/platform/android.py 通过jnius反射org.kivy.android.PythonActivity的mActivity调用getApplicationInfo().nativeLibraryDir获取该目录并用lru_cache缓存结果。2. M3U8 下载引擎N_m3u8DL-REfeatures/m3u8_pack/config.py 在 Android 上解析nativeLibraryDir()/libnm3u8dlre.so作为可执行路径features/m3u8_pack/task.py 在任务运行时通过asyncio.create_subprocess_exec(execPath, *command, ...)拉起该二进制若路径不存在则抛出N_m3u8DL-RE 未安装的TaskError。3. FFmpeg / ffprobefeatures/ffmpeg_pack/config.py 同样在 Android 上从nativeLibraryDir读取libffmpeg.so与libffprobe.so与 ADRFFmpeg 必须预打包的结论一致。4. 同类处理的 QuickJSAndroid 端 JavaScript 引擎同样遵循该模式features/yt_dlp_pack/config.py 从nativeLibraryDir读取libqjs.so。5. 构建侧的一体化打包compose/build-scripts/fetch_android_libs.sh 统一从上游拉取 Android arm64 预编译产物FFmpeg/ffprobe、N_m3u8DL-RE、QuickJS-NG并全部重命名为lib*.so落入jniLibs/arm64-v8a——这正体现了当前以nativeLibraryDir为唯一可执行边界的构建约束也为未来 N_m3u8DL-RE 切出预打包、改走 linker64 热安装预留了清晰的改造点。七、小结这条 ADR 浓缩了 Android 平台原生二进制分发的核心矛盾W^X 与 SELinux 只认nativeLibraryDir而热安装与体积优化又希望把大体积工具移出 APK。Ghost Downloader 给出的答案是一套混合策略可被自己包装调用的 N_m3u8DL-RE 走linker64间接执行以换取约 20 MB 的 APK 节省而会被子进程内部 exec 的 FFmpeg 则坚守nativeLibraryDir预打包以保证混流链路可靠。该决策对任何在 Android 上分发原生工具的下载类应用都具有直接参考价值。【免费下载链接】Ghost-Downloader-3The only downloader you need. 下载器的集大成者。项目地址: https://gitcode.com/GitHub_Trending/gh/Ghost-Downloader-3创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表