从 Apk 提取代码到单独 dex

在这里插入图片描述

从 Apk 提取代码到单独 dex

Android 中动态加载是指应用程序在运行时加载和执行 Dex 文件的过程,可以在运行时加载不同的代码或功能,而无需重新编译整个应用程序,动态加载 Dex 文件通常涉及以下步骤:

  • 创建 Dex 文件
    我们接触到的通常是 Android studio 等 IDE 工具将 Java 或 Kotlin 代码编译成 Dex 格式的字节码文件。
  • 将 Dex 文件打包到 APK 中
    Apk 就是一个压缩包,通常也是 IDE 将编译好的 Dex 文件打包到应用程序的 APK 文件中。
  • 运行时加载 Dex 文件
    当应用程序启动时,Android 系统会加载应用程序的代码。如果应用程序中包含 Dex 文件,系统会自动将其加载到内存中,以便应用程序可以执行其中的代码。
  • 执行 Dex 文件中的代码:
    一旦 Dex 文件被加载到内存中,应用程序就可以执行其中的代码。

通过动态加载 Dex 文件,可以实现更加灵活和可扩展的功能,可以编写插件或模块化的代码,并在运行时根据需要加载它们。DexClassLoader 是 Android 中的一个类加载器,用于动态加载包含 Dex 文件的 jar 或 apk 文件,它的工作原理大概是:加载 Dex 文件,DexClassLoader 会从指定的路径中读取 Dex 文件,并将其加载到内存中… …无序过多描述,我们知道它可以加载 dex 文件即可。

本文讲的如下图所示:

如何从 apk 中获取特定包名下的代码并保存到 dex 文件中,然后再把此 dex 放到 assets 目录下,最后重新打包成 APK。

在这里插入图片描述

本次通过 Java 实现,最终产物是一个可执行的 Jar,使用方式如下所示:

java -jar xpluginJs.jar -mapping mapping.txt -apk 1.apk -dexname hello -pkg com.primer.pay.manager,com.primer.login.manager

意思是:
根据 mapping.txt 映射文件从 1.apk 安装包中提取所有 com.primer.pay.manager,com.primer.login.manager 包下的代码,将所提取代码写入 hello.dex 文件,hello.dex 文件将被放到包体内 assets 目录下。

/*** * 1、必须参数*      -mapping:安装包的映射文件*      -apk:待处理 apk 文件*      -dexname:处理后存放在 assets 目录下的 dex 文件名*      -pkg:需要提取的包名(混淆前的包名),多个包名用英文逗号分隔* * * 2、当前目录运行环境目录结构*      tools:内置工具(apktool、smali)*      xpluginJs.jar:主程序*      mapping.txt:映射文件*      1.apk:apk 文件*      output-dex2jar:中间产物输出目录*      output-dex2jar\apk-decode\dist\***.apk:最终产物*/

在这里插入图片描述

0、线性执行

一图概述~
在这里插入图片描述

    public void run() {System.err.println("=================== 1、Initializes the output directory");initOutPutDir();mDexConfig.setspecifyPkgSmailOutputPath(OUTPUT_PATH_SMAIL);System.err.println("=================== 2、Load the local mapping configuration");mObsMapping = loadMappingConfig(mDexConfig.getMappingFilePath());System.err.println("=================== 3、Gets a list of mapping names for the specified package name");List<String> specifyMappingPkgList = ObsMappingUtils.collectionSpecifyPkgMapping(mObsMapping,mDexConfig.getSpecifiyPkgList());System.err.println("=================== 4、Decompile Apk using apktool");String decodeOutPath = decodeApk(mDexConfig.getApkFilePath(), mDexConfig.getApkDecodeOutputDirname());mDexConfig.setApkDecodeOutputDirpath(decodeOutPath);System.err.println("=================== 5、Collects and moves the smail file under the specified package name");collectionAndMoveSpecifyPkgSmail(decodeOutPath, specifyMappingPkgList);System.err.println("=================== 6、Package the smali file as dex");String smailToDexPath = encodeSmailToDex(mDexConfig.getSpecifyPkgSmailOutputPath());if (CommUtil.isEmptyOrNoExists(smailToDexPath)) {throw new IllegalArgumentException("smailToDexPath is null");}System.err.println("\tsmailToDexPath: " + smailToDexPath);System.err.println("=================== 7、dex encryption");String encodeDexPath = encodeDex(mDexConfig.getEncodeDexFilename(), smailToDexPath);System.err.println("\tencodeDexPath: " + encodeDexPath);String decodeDexPath = decodeDex(mDexConfig.getEncodeDexFilename() + "-decode", encodeDexPath);System.err.println("\tencodeDexPath: " + decodeDexPath);System.err.println("=================== 8、copy dex encryption");boolean isCopy = copyDexToAssets(encodeDexPath, decodeOutPath);if (!isCopy) {throw new IllegalArgumentException("copy dex to assets error");}System.err.println("=================== 9、Apk compile back");String unsignApkFilePath = encodeApk(decodeOutPath);System.err.println("\t [Successfully~] unsignApkFilePath: " + unsignApkFilePath);}

1、参数解析

按格式解析参数,收集必要的信息,这里参数解析或许有漏洞,但你可以根据自己的想法写出更好的参数解析,避免因外部使用的多样化导致内部解析异常。

package com.primer;import java.util.Arrays;
import com.primer.bean.CmdArgs;public class Main {private static DexToolManager mDexToolManager;private static CmdArgs mCmdArgs;public static void main(String[] args) {mCmdArgs = parserArgs(args);initDexToolManager();}//解析 java -jar 传入的参数private static CmdArgs parserArgs(String[] args) {if (args == null || args.length == 0) {throw new IllegalArgumentException(" argument not be empty");}CmdArgs cmdArgs = new CmdArgs();String current;for (int i = 0; i < args.length; i++) {current = args[i];if (current.equals("-mapping")) {if (i + 1 >= args.length) {throw new IllegalArgumentException("-mapping args error");}cmdArgs.mappingPath = args[i + 1];} else if (current.equals("-apk")) {if (i + 1 >= args.length) {throw new IllegalArgumentException("-apk args error");}cmdArgs.apkPath = args[i + 1];} else if (current.equals("-dexname")) {if (i + 1 >= args.length) {throw new IllegalArgumentException("-dexname args error");}cmdArgs.encodeDexFilename = args[i + 1];} else if (current.equals("-pkg")) {if (i + 1 >= args.length) {throw new IllegalArgumentException("-pkg args error");}String str = args[i + 1];String[] pkgList = str.split(",");if (pkgList == null || pkgList.length == 0) {throw new IllegalArgumentException("args error");}cmdArgs.specifiyPkgList = Arrays.asList(pkgList);}}cmdArgs.checkArgument();return cmdArgs;}private static void initDexToolManager() {DexConfig dexConfig = new DexConfig.Builder().setApkFilePath(mCmdArgs.apkPath).setMappingFilePath(mCmdArgs.mappingPath).setSpecifyPkgList(mCmdArgs.specifiyPkgList).setEncodeDexFilename(mCmdArgs.encodeDexFilename).setApkDecodeOutputDirname("apk-decode").build();//简单得对参数是否有效做检查dexConfig.checkConfigIllegal();mDexToolManager = new DexToolManager();mDexToolManager.setDexConfig(dexConfig);mDexToolManager.run();}
}

2、准备工作目录

  • initOutPutDir:创建输出目录、清空输出目录残留的文件

如果让自己写删除目录及目录下的所有文件,很容易让我们想起了递归遍历,可以递归删除文件。

public void traversalFile(File dirFile, FileTraversal traversal) {if (dirFile == null) {return;}for (File file : dirFile.listFiles()) {if (file.isDirectory()) {traversalFile(file, traversal);traversal.processDir(file);} else {traversal.processFile(file);}}}//使用
traversalFile(outFile, new FileTraversal() {@Overridepublic void processFile(File file) {if (file.exists()) {file.delete();}}@Overridepublic void processDir(File file) {if (file.exists()) {file.delete();}}});

3、加载映射

  • loadMappingConfig:加载 mapping.txt 映射文件

我们知道 build/output/**/ release/mapping.txt 就是 Android 开启混淆打包生成的映射文件,就是根据该文件的格式进行解析,可以从中解析获取混淆前类的全限定名(包名+类名),当然也可以拿到混淆前后的方法名等信息。

com.opos.mobad.service.tasks.a -> com.opos.mobad.service.tasks.a:java.lang.String a -> ajava.io.FileFilter b -> bjava.util.HashMap getPayMap(android.content.Context,boolean,int) -> aboolean d(android.content.Context) -> djava.lang.String j(android.content.Context) -> j

根据映射文件我们可以这样简单写出解析存储数据的 bean 类及关系。

//一个映射文件包含很多类的映射
public class ObsMapping {private LinkedList<ClassObsMapping> obsMapping;//略
}//一个类包含类名映射、多个成员变量映射、多个方法的映射(我们没有使用到成员变量,所以可以不要)
public class ClassObsMapping {//类名映射private MappingItem classMapping;//方法映射private LinkedList<MappingItem> methodsMapping;//略
}//映射的基本元素是混淆前后的名称
public class MappingItem {//混淆前名称private String originalName;//混淆后名称private String mappingName;//略
}

4、确认提取目标

  • collectionSpecifyPkgMapping:根据 -pkg 参数列表指定的包名,从映射文件中收集混淆后的包名。

如 com.primer.manager.A -> com.android.manager.AA,那么收集的是 com.android.manager,最终返回的是混淆后的类的包名。

5、apk 反编译

  • decodeApk:利用 apktool 工具反编译

简单地封装 Runtime.getRuntime(),通过调用 runtime.exec(cmdline) 执行控制台命令,这里是通过控制台执行 bat 脚本,再由脚本执行 java 命令执行 apktool。

APKTOOL_BAT_PATH 指向的一个 bat 脚本路径:

:: 参数1-apktool  参数2-apk路径  参数3-输出路径  -f:强制覆盖
java -jar %1 d %2 -o %3 -f
/*** java -jar apktool_2.7.0.jar d [apk file] -o [out name]** @param inputApkPath* @param outDirName* @return*/private String decodeApk(String inputApkPath, String outDirName) {if (inputApkPath == null || inputApkPath.isEmpty()) {throw new IllegalArgumentException("inputApkPath is null");}String cmdline;String outApkPath = OUTPUT_PATH + File.separator + outDirName;StringBuilder sb = new StringBuilder();sb.append(APKTOOL_BAT_PATH).append(" ").append(APKTOOL_JAR_PATH).append(" ").append(inputApkPath).append(" ").append(outApkPath);if (sb.toString().contains("\\") && !sb.toString().contains("\\\\")) {cmdline = sb.toString().replace("\\", "\\\\");} else {cmdline = sb.toString();}System.out.println("decodeApk cmd: " + cmdline);CommUtil.executeCmdline(cmdline, false);return outApkPath;}

6、smali 收集

  • collectionAndMoveSpecifyPkgSmail:根据上述收集到的映射,在反编译目录下查找文件并存放到额外目录下。

通过递归遍历过了 smali 找到映射文件,再把目标文件已到指定目录待下一步处理。

    private void collectionAndMoveSpecifyPkgSmail(String path, List<String> specifyPkgList) {if (CommUtil.isEmptyOrNoExists(specifyPkgList)) {return;}traversalFile(new File(path), new FileTraversal() {@Overridepublic void processFile(File file) {if (file.getName().endsWith(".smali")) {SmailFile smailFile = splitClassPkgname(file.getAbsolutePath());if (!CommUtil.isEmptyOrNoExists(smailFile.pakcgeName)) {for (String pkg : specifyPkgList) {if (pkg.equals(smailFile.pakcgeName)) {moveAndDeleteTargetFile(file, smailFile);break;}}}}}@Overridepublic void processDir(File file) {}});}
public class SmailFile {//映射后的文件名(类名)public String filename;//映射后的包名,如 com.primer.managerpublic String pakcgeName;//映射后的包路径,如 com/primer/manager//因为提取到外部目录下也应该创建相同包名的目录,再不 smali 文件存放到该目录下,确保前后一致public String pakagePath;@Overridepublic String toString() {return "SmailFile: " + filename + ", " + pakcgeName + ", " + pakagePath;}
}

7、smali 打包

  • encodeSmailToDex:使用 smali.jar 把一组 smali 文件打包成 dex

这里有一点需要注意的是,最新版 smali.jar 打包参数是 assemble ,好像以前的包版本打包参数是 b。

:: java -jar smali.jar b out_directory -o output.dex
:: 参数1:smali.jar
:: 参数2:out_directory smali 文件目录
java -jar %1 assemble %2

8、dex 加密

  • encodeDex:对 dex 文件应用自己的加密算法

dex 文件本质上是一个二进制文件,二进制文件读取出来就是一组字节数组 byte[],简单的对字节数组进行特殊操作(插入偏移量等)就是对文件的加密。

9、dex 加密文件放入包体

  • copyDexToAssets:如果你想把提取部分的代码 dex 后续通过动态加载方式执行,你可以重新把它打入包体的其他地方存储备用,也可以后续通过远程请求获取再加载等。

10、apk 打包

  • encodeApk:同样的,也是使用 apktool 工具

这里还是先调用 bat 脚本,再由 bat 执行 java 命令执行 apktool,你也可以以自己的方式处理。

java -jar apktool_2.7.0.jar b [apk decode file path]

:: 参数1-apktool  参数2-apk路径
java -jar %1 b %2

TODO:
当然,有了想法你可以做很多诸如此类的事情!

  • 你可以在 apk 打包完成功之后,完成重新签名
  • 等等等

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/647021.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Redis的五种常用数据类型详解及相关面试问题

目录 Redis的五种常用数据类型详解 简述 Redis五种基本数据类型 String字符串 常用命令 应用场景 Hash散列表 常用命令 使用场景 List链表 常用命令 应用场景 Set( 集合) 常用命令 应用场景 SortedSet( 有序集合) zset 常用命令介绍 应用场景 面试题常问的数…

退货通知单下推销售退货单,无法下推问题排查

文章目录 退货通知单下推销售退货单&#xff0c;无法下推问题排查报错界面排查原因 退货通知单下推销售退货单&#xff0c;无法下推问题排查 报错界面 排查 检验单已做。 原因 合格未勾选判退。

按键协管指南针加速计陀螺仪GPS等原理图纸2

1.imu电路。 加速计包含重力感应。 到传感器芯片u8, U16, U18的信息都是用的spi接口&#xff0c;如下图所示。OSCAR_TO_IMU_SPI_SCLK_FL, IMU_TO_OSCAR_SPI_MISO_FL, OSCAR_TO_IMU_SPI_MOSI_FL接了u8, u16, u18,通过片选信号cs选择哪个芯片接收。 加速计&#xff0c;陀螺仪&…

Redash 默认key漏洞(CVE-2021-41192)复现

Redash是以色列Redash公司的一套数据整合分析解决方案。该产品支持数据整合、数据可视化、查询编辑和数据共享等。 Redash 10.0.0及之前版本存在安全漏洞&#xff0c;攻击者可利用该漏洞来使用已知的默认值伪造会话。 1.漏洞级别 中危 2.漏洞搜索 fofa "redash"…

289. 生命游戏

根据 百度百科 &#xff0c; 生命游戏 &#xff0c;简称为 生命 &#xff0c;是英国数学家约翰何顿康威在 1970 年发明的细胞自动机。 给定一个包含 m n 个格子的面板&#xff0c;每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态&#xff1a; 1 即为 活细胞 &am…

主播考核体系相关基础

1.主播薪资类型 2.主播考核体系 1.分为日常考核、月度考核 日常考核分为三部曲&#xff1a;播前、播中、播后 &#xff08;1&#xff09;播前 &#xff08;2&#xff09;播中 &#xff08;3&#xff09;播后 月度考核 月度考核表列举 主播等级划分要素 主播晋升考核方…

JVM篇----第六篇

系列文章目录 文章目录 系列文章目录前言一、堆(Heap-线程共享) -运行时数据区二、方法区/永久代(线程共享)三、JVM 运行时内存四、新生代前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看…

Qt6入门教程 11:父子对象关系

在上一篇中的纯手写部分&#xff0c;不管是创建菜单、工具栏还是状态栏&#xff0c;我们new完之后都未显式的调用delete进行销毁&#xff0c;这样难道不会有内存泄漏么&#xff1f; QMenuBar *menuBar new QMenuBar(this); QToolBar *toolBar new QToolBar(this); QStatusBa…

web前端之不一样的居中方式、解决tabBar选项卡居中问题、css支持嵌套、auto

MENU 前言htmlstyle效果 前言 这里不能使用justify-content: center;&#xff0c;因为在小屏幕上&#xff0c;这种方式无法显示最前面的两个tabBar。 html <div id"box" class"d_f o_a mt_50 mb_50 ml_20 mr_20"><div class"ws_n">…

【MySQL】如何通过DDL去创建和修改员工信息表

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-fmKISDBsFq74ab2Z {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

【vue】Vue2和Vue3中的代码逻辑复用对比(mixins、自定义hook):

文章目录 一、前言&#xff1a;二、mixins&#xff1a;【1】mixins是什么&#xff1f;【2】mixins如何使用&#xff1f;【3】mixins的一些特性&#xff1a;【4】mixins的缺点&#xff1a; 三、hook&#xff1a;【1】Vue3.x中的自定义hook函数是什么&#xff1f;【2】mixins和Co…

4.【SpringBoot3】文章管理接口开发

序言 在文章管理模块&#xff0c;有以下接口需要开发&#xff1a; 新增文章文章列表&#xff08;条件分页&#xff09;获取文章详情更新文章删除文章 数据库表字段和实体类属性&#xff1a; 1. 新增文章 需求分析 当用户点击左侧菜单中的“文章管理”后&#xff0c;页面主…

SpringBoot_基础

学习目标 基于SpringBoot框架的程序开发步骤 熟练使用SpringBoot配置信息修改服务器配置 基于SpringBoot的完成SSM整合项目开发 一、SpringBoot简介 1. 入门案例 问题导入 SpringMVC的HelloWord程序大家还记得吗&#xff1f; SpringBoot是由Pivotal团队提供的全新框架&…

java数据结构与算法刷题-----LeetCode697. 数组的度

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 方法一&#xff1a;hash表 此方法是工作中时间可以使用的&#xff0c;因为…

阅读go语言工具源码系列之gopacket(谷歌出品)----第一集 DLL的go封装

gopacket项目是google出品的golang第三方库&#xff0c;项目源码地址google/gopacket: Provides packet processing capabilities for Go (github.com) gopacket核心是对经典的抓包工具libpcap(linux平台)和npcap(windows平台)的go封装&#xff0c;提供了更方便的go语言操作接…

嵌入式linux学习之系统烧录

1.所需文件 1. 开发板为正点原子stm32mp157,文件可按照linux驱动教程编译&#xff0c;也可在正点原子文档->08、系统镜像\02、出厂系统镜像中找到&#xff1a; 2.烧录 1.拨码开关为000(usb启动)&#xff0c;otg接口接入虚拟机&#xff0c;打开stm32cubeProgrammer: 2.页面…

AP5101C 高压线性 LED恒流驱动器 DFN2*2 LED灯汽车雾灯转向灯

产品描述 AP5101C 是一款高压线性 LED 恒流芯片 &#xff0c; 简单 、 内置功率管 &#xff0c; 适用于6- 100V 输入的高精度降压 LED 恒流驱动芯片。电流2.0A。AP5101C 可实现内置MOS 做 2.0A,外置 MOS 可做 3.0A 的。AP5101C 内置温度保护功能 &#xff0c;温度保护点为 130 …

CQ 社区版 2.8.0 | 支持TiDB、StarRocks,新增列过滤算法、导出模式设置等

Hello&#xff0c;CloudQuery 社区版 2.8.0 已发布&#xff0c;本文将带大家详细解析本次更新的功能~&#xff08;完整的讲解视频可点击 &#x1f449;&#x1f3fb; CloudQuery 社区版2.8.0 功能讲解演示 本期亮点更新 新增支持数据源 TiDB、StarRocks数据保护新增列过滤脱敏…

cmd命令行输出的内容复制粘贴到文本中

cmd程序执行完后按任意键进行结束&#xff0c;无法直接复制命令行里输出的内容&#xff0c;如下图&#xff0c;在Windows系统里按ctrlC&#xff0c;然后该窗口就关闭了&#xff0c;内容也没有复制成功到粘贴板。 解决办法如下&#xff1a; 在上方打开设置 然后在“交互”里打…

JDBC 总结

一、JDBC概述 JDBC&#xff08;Java DataBase Connectivity&#xff09;java数据库连接是一种用于执行SQL语句的Java API&#xff0c;可以为多种关系型数据库提供统一访问&#xff0c; 它由一组用Java语言编写的类和接口组成。有了JDBC,java开发人员只需要编写一次程序,就可以…