尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

WeChatPad源码剖析(三):从BaseDexClassLoader提取DEX镜像的完整C++实现解析

发布时间:2026/9/25 1:38:45

资讯中心
01
ARTICLE

WeChatPad源码剖析(三):从BaseDexClassLoader提取DEX镜像的完整C++实现解析

WeChatPad源码剖析(三):从BaseDexClassLoader提取DEX镜像的完整C++实现解析
WeChatPad源码剖析三从BaseDexClassLoader提取DEX镜像的完整C实现解析【免费下载链接】WeChatPad强制使用微信平板模式项目地址: https://gitcode.com/gh_mirrors/we/WeChatPadWeChatPad是一个让微信强制以平板模式运行的开源 LSPosed 模块。本文作为源码剖析系列第三篇聚焦一个核心问题如何从 BaseDexClassLoader 提取 DEX 镜像。项目作者用一套纯 C 的 JNI 实现绕开 Android 官方的解析工具链直接从 ClassLoader 内部取出 DEX 内存映像含 ZIP 回退解压为全量方法搜索提供高速数据源。下面带你完整读懂这条提取链路。为什么要先提取 DEX 镜像WeChatPad 的核心玩法不是写死的 Hook而是在运行时动态搜索目标方法例如通过字符串特征定位判断设备是否为平板的方法再对其打 Hook。搜索的前提是先拿到 APK 里所有的 DEX 数据。Android 应用启动时DEX 已被BaseDexClassLoader加载进内存。直接复用这份内存映像比重新读文件解压快得多——这正是 dex_helper.cc 要解决的事。DEX 镜像提取成功后会被送入 DexHelper 类做预处理与缓存。值得注意的是它的缓存结构大量使用了项目内置的并行哈希表库 parallel_hashmap见 dex_helper.h 缓存区 中的method_cache_、field_cache_这也是仓库中携带 parallel_hashmap 源码的原因。五级反射链路从 ClassLoader 一路挖到 DEX提取 DEX 镜像依赖一条固定的字段链路全部在 JNI_OnLoad 中一次性缓存字段地址避免每次反射BaseDexClassLoader └─ pathList (DexPathList) └─ dexElements (Element[]) └─ dexFile (DexFile) ├─ mCookie → 原生 DEX 解析对象指针数组 └─ mFileName → APK/DEX 文件路径回退用字段类型签名作用pathListLdalvik/system/DexPathList;类路径列表入口dexElements[Ldalvik/system/DexPathList$Element;每个 DEX 元素dexFileLdalvik/system/DexFile;单个 DEX 文件对象mCookieLjava/lang/Object;ART 内部的 DEX 解析上下文mFileNameLjava/lang/String;原始文件路径 这些是 Android 框架的私有字段名不同 ROM/版本可能有差异这也是此类工具对系统版本敏感的根源。load() 函数DEX 镜像提取的完整流程入口是 Kotlin 侧 DexHelper.kt 声明的external fun load(classLoader: ClassLoader)其 C 实现在 Java_com_rarnu_dex_DexHelper_load。整体分四步。第一步沿反射链路取出 DEX 元素数组函数先读取pathList→dexElements然后逐个遍历数组元素取出每个dexFile及其mCookie一个jlongArray。任何一环取不到就直接return 0安全退出防御非常克制。第二步mCookie 转指针直接读内存中的 DEX 映像mCookie里的每个 long 实际上是 ART 内部的DexFile原生对象地址。作者定义了一个与原生布局对齐的桩结构 MyDexFile仅begin_起始地址 size_长度两个字段将 jlongArray 强制转型为const MyDexFile **就能零拷贝拿到每个 DEX 在内存中的起止位置。随后从后向前遍历多 DEX 场景下越靠后的越重要对每个映像调用 dex::Reader::IsCompact 检查魔数魔数为dex\n→ 标准 DEX直接记录(begin_, size_)使用魔数为cdexCompact DEX新系统常见的压缩格式→ 内存映像无法直接解析清空结果并进入文件回退。第三步Compact DEX 的文件回退策略一旦内存映像不可用就取mFileName打开原始文件 dex_helper.cc#L494-L528若是普通.dex文件 → 整个文件 mmap 后直接作为镜像若是 APKZIP 包→ 进入第四步手动解包。第四步手写 ZIP 解析 zlib 解压这是整个实现里最硬核的部分。项目没有使用 AOSP 的ZipArchive而是自己写了一个极简 ZIP 解析器 ZipLocalFile定位文件头以本地文件头魔数0x04034b50校验起点顺着next()遍历条目字节级对齐校验static_assert 序列 逐一断言各字段偏移防止编译器对齐导致解析错位解压uncompress()支持两种模式——zlib 压缩compress 0x8用inflate流式解压到匿名内存和存储模式直接memcpy解压完成后mprotect降权为只读。随后按classes.dex、classes2.dex… 的顺序逐个查找条目把解压出的 DEX 镜像收集进images列表。两个支撑组件MemMap 与 HandlerMemMapRAII 内存映射MemMap 统一了文件 mmap和匿名 mmap两种来源支持移动语义、禁止拷贝析构自动munmap。所有解压产物都通过它管理杜绝泄漏。Handler生命周期载体using Handler std::tuplestd::unique_ptrDexHelper, std::listMemMap;load()成功后 new 一个Handler把DexHelper解析索引和maps保持所有 mmap 内存有效打包指针以 jlong 存入 Kotlin 对象的token字段。查找类接口每次都通过token反查回这个Handler最后由 close() 统一释放——DEX 镜像的生命周期管理干净利落。parallel_hashmap 之所以能支撑起百万级方法的高速缓存查询关键在于它的索引计算与锁分片策略下图展示了其索引计算原理本项目 dex_helper.cc 的缓存结构正依赖该库其多线程扩展的基准测试对比结果左单线程右多线程也可作为参考说明了在大量 DEX 方法缓存场景下引入并行哈希表的收益提取结果如何被消费回到 Kotlin 层 XposedInit.kt先沿 parent 链向上找到BaseDexClassLoader构造DexHelper内部即触发load()完成 DEX 镜像提取然后调用findMethodUsingString(Lenovo TB-9707F, ...)以设备型号字符串为特征搜索目标方法decodeMethodIndex把镜像索引还原成 JavaMember对象最后交给XposedBridge.hookMethod完成 Hook——平板模式开关就此被接管。小结这套实现的三个设计亮点内存优先、文件兜底优先零拷贝读mCookie指向的内存 DEXCompact DEX 场景自动回退到文件解压兼容性最大化零依赖的极简 ZIP 解析仅靠魔数 偏移量 static_assert对齐断言比引入整个 ZipArchive 更轻量且 slicer/reader.h 提供了完整的 DEX 格式解析层RAII 贯穿全程MemMap、Handler、Kotlin 侧的AutoCloseable三层生命周期管理DEX 镜像的提取与释放闭环清晰。至此WeChatPad「DEX 镜像提取 → 全量索引 → 动态搜索 → Hook」的第一环就解析完毕。下一篇我们将深入DexHelper的方法搜索算法看看它如何在多 DEX 上做到接近 O(1) 的特征查找。【免费下载链接】WeChatPad强制使用微信平板模式项目地址: https://gitcode.com/gh_mirrors/we/WeChatPad创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。