Android Dex VMP 动态加载加密指令流

news2025/1/14 14:24:23

版权归作者所有,如有转发,请注明文章出处:https://cyrus-studio.github.io/blog/

上一篇【详解如何自定义 Android Dex VMP 保护壳】实现了 VMP 保护壳。

为了进一步加强对 dex 指令的保护,实现指令流加密和动态加载,比如使用 AES 加密指令流,在运行时解密执行。

保存指令流到文件

在 010Editor 中搜索找到 sign 方法的字节码并复制

word/media/image1.png

新建 Hex 文件

word/media/image2.png

把 sign 方法字节码粘贴到新建的文件保存文件为 sign

word/media/image3.png

AES加解密

编写一个 kotlin 语言 AES 加解密算法工具类

package com.cyrus.vmp

import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.SecretKeySpec

object AESUtils {

    private const val ALGORITHM = "AES"
    private const val TRANSFORMATION = "AES/ECB/PKCS5Padding" // AES 加密模式

    // 生成一个 128 位的 AES 密钥
    fun generateSecretKey(): SecretKey {
        val keyGenerator = KeyGenerator.getInstance(ALGORITHM)
        keyGenerator.init(128) // AES 128 位
        return keyGenerator.generateKey()
    }

    // 使用给定的密钥加密数据
    fun encrypt(data: ByteArray, key: SecretKey): ByteArray {
        val cipher = Cipher.getInstance(TRANSFORMATION)
        cipher.init(Cipher.ENCRYPT_MODE, key)
        return cipher.doFinal(data)
    }

    // 使用给定的密钥解密数据
    fun decrypt(data: ByteArray, key: SecretKey): ByteArray {
        val cipher = Cipher.getInstance(TRANSFORMATION)
        cipher.init(Cipher.DECRYPT_MODE, key)
        return cipher.doFinal(data)
    }

    // 将文件内容加密并导出到新文件
    fun encryptFile(inputFile: File, outputFile: File, keyFile: File) {
        // 读取文件内容
        val fileData = readFile(inputFile)

        // 生成密钥
        val secretKey = generateSecretKey()

        // 加密文件内容
        val encryptedData = encrypt(fileData, secretKey)

        // 保存加密后的数据到新文件(.vmp 文件)
        writeFile(outputFile, encryptedData)

        // 保存密钥到文件
        saveKeyToFile(secretKey, keyFile)
    }

    // 解密文件内容并导出到新文件
    fun decryptFile(inputFile: File, outputFile: File, keyFile: File) {
        // 从文件加载密钥
        val secretKey = loadKeyFromFile(keyFile)

        // 读取加密后的文件内容
        val encryptedData = readFile(inputFile)

        // 解密文件内容
        val decryptedData = decrypt(encryptedData, secretKey)

        // 保存解密后的数据到文件
        writeFile(outputFile, decryptedData)
    }

    // 读取文件内容并返回字节数组
    fun readFile(file: File): ByteArray {
        val fis = FileInputStream(file)
        val baos = ByteArrayOutputStream()
        val buffer = ByteArray(1024)
        var bytesRead: Int
        while (fis.read(buffer).also { bytesRead = it } != -1) {
            baos.write(buffer, 0, bytesRead)
        }
        fis.close()
        return baos.toByteArray()
    }

    // 将字节数组写入到文件
    fun writeFile(file: File, data: ByteArray) {
        val fos = FileOutputStream(file)
        fos.write(data)
        fos.close()
    }

    // 保存密钥到文件
    private fun saveKeyToFile(key: SecretKey, keyFile: File) {
        val fos = FileOutputStream(keyFile)
        fos.write(key.encoded)
        fos.close()
    }

    // 从文件加载密钥
    fun loadKeyFromFile(keyFile: File): SecretKey {
        val keyBytes = ByteArray(keyFile.length().toInt())
        val fis = FileInputStream(keyFile)
        fis.read(keyBytes)
        fis.close()
        return SecretKeySpec(keyBytes, ALGORITHM)
    }

}

指令流加密

把 sign 文件放到工程中如下路径

word/media/image4.png

调用 AESUtils 类中方法对 sign 进行加密并输出加密文件和密钥

package com.cyrus.vmp

import java.io.File

fun main() {
    // 获取工程根目录路径
    val projectRoot = System.getProperty("user.dir")

    // 设置相对路径
    val encryptedFile = File(projectRoot, "vmp/sign/sign.vmp") // 相对路径
    val keyFile = File(projectRoot, "vmp/sign/sign.key") // 相对路径

    // 输入文件路径
    val inputFile = File(projectRoot, "vmp/sign/sign") // 需要加密的文件


    try {
        // 使用 AES 加密文件
        AESUtils.encryptFile(inputFile, encryptedFile, keyFile)
        println("File encryption completed, saved as: ${encryptedFile.absolutePath}")
        println("Key saved as: ${keyFile.absolutePath}")
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

指令流解密

package com.cyrus.vmp

import com.cyrus.vmp.AESUtils.loadKeyFromFile
import com.cyrus.vmp.AESUtils.readFile
import com.cyrus.vmp.AESUtils.writeFile
import java.io.File


fun main() {

    // 获取工程根目录路径
    val projectRoot = System.getProperty("user.dir")

    // 输入加密文件路径
    val encryptedFile = File(projectRoot, "vmp/sign/sign.vmp")

    // 密钥文件路径
    val keyFile = File(projectRoot, "vmp/sign/sign.key")

    // 输出解密文件路径
    val decryptedFile = File(projectRoot, "vmp/sign/sign_")

    try {
        // 从文件加载密钥
        val secretKey = loadKeyFromFile(keyFile)

        // 解密文件
        val encryptedData = readFile(encryptedFile)
        val decryptedData: ByteArray = AESUtils.decrypt(encryptedData, secretKey)

        // 保存解密后的文件
        writeFile(decryptedFile, decryptedData)
        println("File decryption completed, saved as: ${decryptedFile.absolutePath}")
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

Android 中运行时解密并执行指令流

将 .vmp 和 .key 文件放在 Android 应用的 assets 目录下

word/media/image5.png

编写工具类,用于读取 assets 文件并解密

package com.cyrus.example.vmp

import android.content.Context
import java.io.InputStream
import javax.crypto.Cipher
import javax.crypto.SecretKey
import javax.crypto.spec.SecretKeySpec

object AESUtils {
    private const val ALGORITHM = "AES"
    private const val TRANSFORMATION = "AES/ECB/PKCS5Padding"

    // 从 assets 中读取文件并解密
    fun decryptFileFromAssets(context: Context, vmpFileName: String, keyFileName: String): ByteArray? {
        // 读取密钥文件
        val key = loadKeyFromAssets(context, keyFileName)

        // 读取加密的 vmp 文件
        val encryptedData = readFileFromAssets(context, vmpFileName)

        // 解密
        return decrypt(encryptedData, key)
    }

    // 读取文件内容为字节数组
    private fun readFileFromAssets(context: Context, fileName: String): ByteArray {
        val inputStream: InputStream = context.assets.open(fileName)
        return inputStream.readBytes()
    }

    // 从 assets 中加载密钥文件
    private fun loadKeyFromAssets(context: Context, keyFileName: String): SecretKey {
        val keyBytes = readFileFromAssets(context, keyFileName)
        return SecretKeySpec(keyBytes, ALGORITHM)
    }

    // 解密
    private fun decrypt(data: ByteArray, key: SecretKey): ByteArray {
        val cipher = Cipher.getInstance(TRANSFORMATION)
        cipher.init(Cipher.DECRYPT_MODE, key)
        return cipher.doFinal(data)
    }
}

调用解密方法并读取指令流

private fun readInstructionFromAssets(): ByteArray? {
    // 文件名:在 assets 中放置的加密文件和密钥文件
    val vmpFileName = "sign.vmp"
    val keyFileName = "sign.key"

    // 解密文件
    val decryptedData = AESUtils.decryptFileFromAssets(this, vmpFileName, keyFileName)
    return decryptedData
}

得到解密后的指令流后调用 VMP 执行指令流对 input 参数加密

val input = "example"

// 解密并执行指令流
val bytecode = readInstructionFromAssets()

// 通过 VMP 解析器执行指令流
if (bytecode != null) {

    val result = SimpleVMP.execute(bytecode, input)

    // 显示 Toast
    Toast.makeText(this, result, Toast.LENGTH_SHORT).show()
}

测试

执行结果如下

word/media/image6.png

和原来的 sign 算法对比是结果是一样的。

word/media/image7.png

源码

完整源码:https://github.com/CYRUS-STUDIO/AndroidExample

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

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

相关文章

浅谈云计算08 | 基本云架构

浅谈基本云架构 一、负载分布架构二、资源池架构三、动态可扩展架构四、弹性资源容量架构五、服务负载均衡架构六、云爆发架构七、弹性磁盘供给架构八、冗余存储架构 在当今数字化时代,云计算已成为企业发展的核心驱动力,而其背后的一系列关键架构则是支…

从零开始开发纯血鸿蒙应用之多签名证书管理

从零开始开发纯血鸿蒙应用 一、前言二、鸿蒙应用配置签名证书的方式1、自动获取签名证书2、手动配置签名证书 三、多签名证书配置和使用四、多证书使用 一、前言 由于手机操作系统,比电脑操作系统脆弱很多,同时,由于手机的便携性&#xff0c…

Windows Docker 安装

使用别人写好的软件/工具最大的障碍是什么——必然是配环境。配环境带来的折磨会极大地消解你对软件、编程本身的兴趣。虚拟机可以解决配环境的一部分问题,但它庞大笨重,且为了某个应用的环境配置好像也不值得模拟一个全新的操作系统。 Docker 的出现让…

LabVIEW运动控制(一):EtherCAT运动控制器的SCARA机械手应用

ZMC408CE 高性能总线型运动控制器 ZMC408CE是正运动推出的一款多轴高性能EtherCAT总线运动控制器,具有EtherCAT、EtherNET、RS232、CAN和U盘等通讯接口,ZMC系列运动控制器可应用于各种需要脱机或联机运行的场合。 ZMC408CE支持PLC、Basic、HMI组态三种…

【韩顺平Java笔记】第8章:面向对象编程(中级部分)【327-337】

327. 断点调试(Debug) 一个实际需求 在开发中,程序员在查找错误时,可用断点模式在断点调试过程中,是运行状态,是以对象的运行类型来执行的。 A extends B; B b new A(); b.xx();//按照运行类型来执行的 …

金融项目实战 01|功能测试分析与设计

前置内容:金融项目准备的内容笔记可直接看如下笔记 只看:一、投资专业术语 和 二、项目简介 两部分文章浏览阅读2.3k次,点赞70次,收藏67次。安享智慧理财金融系统测试项目,测试用例,接口测试,金…

学习threejs,使用TrackballControls相机控制器

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️THREE.TrackballControls 相…

ASP.NET Core - .NET 6 以上版本的入口文件

ASP.NET Core - .NET 6 以上版本的入口文件 自从.NET 6 开始,微软对应用的入口文件进行了调整,移除了 Main 方法和 Startup 文件,使用顶级语句的写法,将应用初始化的相关配置和操作全部集中在 Program.cs 文件中,如下&…

html5各行各业官网模板源码下载 (4)

文章目录 1.来源2.源码模板2.1 html实现酷炫美观的可视化大屏(十种风格示例,附源码)2.2 HTML5实现古典音乐网站源码模板22.3 HTML5实现古典音乐网站源码模板32.4 HTML5实现小鸟过管道小游戏源码2.5 HTML5实现俄罗斯方块小游戏2.5 HTML5实现剪刀石头布小游戏(附源码)…

springboot vue uniapp 仿小红书 1:1 还原 (含源码演示)

线上预览: 移动端 http://8.146.211.120:8081/ 管理端 http://8.146.211.120:8088/ 小红书凭借优秀的产品体验 和超高人气 目前成为笔记类产品佼佼者 此项目将详细介绍如何使用Vue.js和Spring Boot 集合uniapp 开发一个仿小红书应用,凭借uniapp 可以在h5 小程序 app…

播放音频文件同步音频文本

播放音频同步音频文本 对应单个文本高亮显示 使用audio音频文件对应音频文本资源 音频文本内容(Json) [{"end": 4875,"index": 0,"speaker": 0,"start": 30,"text": "70号二啊,","tex…

数据分析-使用Excel透视图/表分析禅道数据

背景 禅道,是目前国内用得比较多的研发项目管理系统,我们常常会用它进行需求管理,缺陷跟踪,甚至软件全流程的管理,如果能将平台上的数据结公司的实际情况进行合理的分析利用,相信会给我们的项目复盘总结带来…

No. 31 笔记 | Web安全-SQL手工注入技术学习 Part 2

一、研究背景 背景介绍 SQL注入是一种常见且高危的Web安全漏洞。攻击者可以通过构造恶意SQL查询语句来绕过验证机制,执行未授权操作,如获取敏感信息、篡改数据库内容甚至控制服务器。 研究内容 本笔记探讨以下数据库的手工注入技术: MySQLAc…

基于FPGA的多功能数字钟设计

基于FPGA的多功能数字钟设计 前言基础知识按键数码管 系统概述按键使用说明模块描述模块设计button_debouncebutton_controllerclock_controllerdigital_tube 整体资源用量测试视频 前言 本工程主要是数码管、按键、LED的应用开发。 注:本工程所有IP均使用源码开发…

人工智能实验(五)-基于神经网络的模式识别实验

一、实验目的 理解BP神经网络的结构和原理,掌握反向传播算法对神经元的训练过程,了解反向传播公式。通过构建BP网络模式识别实例,熟悉BP网络的原理及结构 二、实验内容 基于提供的数据集,训练1个BP神经网络模型: 1. …

【伪随机数】关于排序算法自测如何生成随机数而引发的……

以 Random 开始 可能一开始&#xff0c;你只是写到了排序算法如何生成随机数 public static void main(String[] args) {Random random new Random();int[] nums new int[10];for (int i 0; i < nums.length; i) {nums[i] random.nextInt(100);}System.out.println(&q…

【LeetCode】力扣刷题热题100道(26-30题)附源码 轮转数组 乘积 矩阵 螺旋矩阵 旋转图像(C++)

目录 1.轮转数组 2.除自身以外数组的乘积 3.矩阵置零 4.螺旋矩阵 5.旋转图像 1.轮转数组 给定一个整数数组 nums&#xff0c;将数组中的元素向右轮转 k 个位置&#xff0c;其中 k 是非负数。 class Solution { public:void rotate(vector<int>& nums, int k) …

IDEA中创建maven项目

1. IDEA中创建maven项目 在IDEA中创建Maven项目&#xff0c;前提是已经安装配置好Maven环境。如还未配置安装Maven的&#xff0c;请先下载安装。如何下载安装&#xff0c;可参考我另外篇文章&#xff1a;maven的下载与安装教程本篇教程是以创建基于servlet的JavaWeb项目为例子&…

【PyQt】如何在mainwindow中添加菜单栏

[toc]如何在mainwindow中添加菜单栏 如何在mainwindow中添加菜单栏 主要有两种方法&#xff1a; 1.直接创建mainwindow进行添加 2.使用ui文件加载添加 第二种方法更为常见&#xff0c;可以应用到实际 1.直接创建mainwindow进行添加 import sysfrom PyQt5.QtWidgets import …

Kubernetes1.28 编译 kubeadm修改证书有效期到 100年.并更新k8s集群证书

文章目录 前言一、资源准备1. 下载对应源码2.安装编译工具3.安装并设置golang 二、修改证书有效期1.修改证书有效期2.修改 CA 证书有效期 三、编译kubeadm四、使用新kubeadm方式1.当部署新集群时,使用该kubeadm进行初始化2.替换现有集群kubeadm操作 前言 kubeadm 默认证书为一…