Android中Apk加固代码实现

article/2025/10/6 15:05:11

前言:上一篇博客已经把Apk加固的思路详细的介绍过了,也开始创建了一个空的demo进行,然后在项目中添加一个代理module(解密,和系统源码交互功能)和tools工具加密Java library 的module ,这里开始接着把整个过程用代码操作一遍,希望对大家有所帮助。

代码用到的工具类请移步:https://download.csdn.net/download/i123456789t/11239056 

1、代码中需要用到几个类,AES加解密类,Zip压缩解压类等工具类

首先我先proxy_core代理module下写一个代理application ,然后继承至Application,代码目录结构请看:

接着把我们这个代理的application加到我们最常写的配置文件中AndroidManifest.xml 中,我们是不是每个App都有一个application,然后把它配置到AndroidManifest.xml中,这里唯一不同的是,不是把我们项目中的那个application写到AndroidManifest.xml中,而是把我们在代理的写上。然后把我们app自己用到的application也加上,自己的application写在meta-data中,另一个meta-data按照下面的写就行,写法和位置如下

这个是我们自己项目用到的初始化application,上面的代理只是处理代理操作的。

我们自己的MyApplication里面目前啥也没写,这个使我们项目中用于初始化的,这里先不写东西。

这里开始写代理了,在ProxyApplication 中:

package com.example.proxy_core;import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;public class ProxyApplication extends Application {//定义好的加密后的文件的存放路径private String app_name;private String app_version;/*** ActivityThread创建Application之后调用的第一个方法* 可以在这个方法中进行解密,同时把dex交给Android去加载* @param base*/@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);//获取用户填入的metaDatagetMetaData();//得到当前apk文件File apkFile = new File(getApplicationInfo().sourceDir);//把apk解压  这个目录中的内容需要root权限才能使用File versionDir = getDir(app_name+"_" + app_version,MODE_PRIVATE);File appDir = new File(versionDir,"app");File dexDir = new File(appDir,"dexDir");//得到我们需要加载的dex文件List<File> dexFiles = new ArrayList<>();//进行解密 (最好做md5文件校验)if (!dexDir.exists() || dexDir.list().length == 0){//把apk解压到appDirZip.unZip(apkFile,appDir);//获取目录下所有的文件File[] files = appDir.listFiles();for (File file:files){String name = file.getName();if (name.endsWith(".dex") && !TextUtils.equals(name,"classes.dex")){try{AES.init(AES.DEFAULT_PWD);//读取文件内容byte[] bytes = Utils.getBytes(file);//解密byte[] decypt = AES.decrypt(bytes);//写到指定的目录FileOutputStream fos = new FileOutputStream(file);fos.write(decypt);fos.flush();fos.close();dexFiles.add(file);}catch (Exception e){e.printStackTrace();}}}}else {for (File file:dexDir.listFiles()){dexFiles.add(file);}}try {loadDex(dexFiles,versionDir);}catch (Exception e){e.printStackTrace();}}private void loadDex(List<File> dexFiles,File versionDir) throws Exception{//1、获取pathListField pathListField = Utils.findField(getClassLoader(), "pathList");Object pathList = pathListField.get(getClassLoader());//2、获取数组dexElementsField dexElementsField = Utils.findField(pathList,"dexElements");Object[] dexElements = (Object[]) dexElementsField.get(pathList);//3、反射到初始化makePathElements的方法Method makeDexElements = Utils.findMethod(pathList,"makePathElements",List.class,File.class,List.class);ArrayList<IOException> suppressedException = new ArrayList<>();Object[] addElements = (Object[]) makeDexElements.invoke(pathList, dexFiles, versionDir, suppressedException);Object[] newElements = (Object[]) Array.newInstance(dexElements.getClass().getComponentType(), dexElements.length + addElements.length);System.arraycopy(dexElements,0,newElements,0,dexElements.length);System.arraycopy(addElements,0,newElements,dexElements.length,addElements.length);//替换classloader中的element数组dexElementsField.set(pathList,newElements);}private void getMetaData(){try {ApplicationInfo applicationInfo = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);Bundle metaData = applicationInfo.metaData;if (null != metaData){if (metaData.containsKey("app_name")){app_name = metaData.getString("app_name");}if (metaData.containsKey("app_version")){app_version = metaData.getString("app_version");}}}catch (Exception e){e.printStackTrace();}}/*** 开始替换application*/@Overridepublic void onCreate() {super.onCreate();try {bindRealApplication();}catch (Exception e){e.printStackTrace();}}/*** 让代码走入if的第三段中* @return*/@Overridepublic String getPackageName() {if (!TextUtils.isEmpty(app_name)){return "";}return super.getPackageName();}@Overridepublic Context createPackageContext(String packageName, int flags) throws PackageManager.NameNotFoundException {if (TextUtils.isEmpty(app_name)){return super.createPackageContext(packageName, flags);}try {bindRealApplication();} catch (Exception e) {e.printStackTrace();}return delegate;}boolean isBindReal;Application delegate;//下面主要是通过反射系统源码的内容,然后进行处理,把我们的内容加进去处理private void bindRealApplication() throws Exception{if (isBindReal){return;}if (TextUtils.isEmpty(app_name)){return;}//得到attchBaseContext(context) 传入的上下文 ContextImplContext baseContext = getBaseContext();//创建用户真实的application  (MyApplication)Class<?> delegateClass = null;delegateClass = Class.forName(app_name);delegate = (Application) delegateClass.newInstance();//得到attch()方法Method attach = Application.class.getDeclaredMethod("attach",Context.class);attach.setAccessible(true);attach.invoke(delegate,baseContext);//获取ContextImpl ----> ,mOuterContext(app);  通过Application的attachBaseContext回调参数获取Class<?> contextImplClass = Class.forName("android.app.ContextImpl");//获取mOuterContext属性Field mOuterContextField = contextImplClass.getDeclaredField("mOuterContext");mOuterContextField.setAccessible(true);mOuterContextField.set(baseContext,delegate);//ActivityThread  ----> mAllApplication(ArrayList)  ContextImpl的mMainThread属性Field mMainThreadField = contextImplClass.getDeclaredField("mMainThread");mMainThreadField.setAccessible(true);Object mMainThread = mMainThreadField.get(baseContext);//ActivityThread  ----->  mInitialApplication       ContextImpl的mMainThread属性Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");Field mInitialApplicationField = activityThreadClass.getDeclaredField("mInitialApplication");mInitialApplicationField.setAccessible(true);mInitialApplicationField.set(mMainThread,delegate);//ActivityThread ------>  mAllApplications(ArrayList)   ContextImpl的mMainThread属性Field mAllApplicationsField = activityThreadClass.getDeclaredField("mAllApplications");mAllApplicationsField.setAccessible(true);ArrayList<Application> mApplications = (ArrayList<Application>) mAllApplicationsField.get(mMainThread);mApplications.remove(this);mApplications.add(delegate);//LoadedApk ----->  mApplicaion             ContextImpl的mPackageInfo属性Field mPackageInfoField = contextImplClass.getDeclaredField("mPackageInfo");mPackageInfoField.setAccessible(true);Object mPackageInfo = mPackageInfoField.get(baseContext);Class<?> loadedApkClass = Class.forName("android.app.LoadedApk");Field mApplicationField = loadedApkClass.getDeclaredField("mApplication");mApplicationField.setAccessible(true);mApplicationField.set(mPackageInfo,delegate);//修改ApplicationInfo  className  LoadedApkField mApplicationInfoField = loadedApkClass.getDeclaredField("mApplicationInfo");mApplicationInfoField.setAccessible(true);ApplicationInfo mApplicationInfo = (ApplicationInfo) mApplicationInfoField.get(mPackageInfo);mApplicationInfo.className = app_name;delegate.onCreate();isBindReal = true;}
}

 2、下面在proxy_tools中写一个Main类,和一个main方法,直接运行处理,代码如下:

package com.example.proxy_tools;import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;public class Main {public static void main(String[] args) throws Exception{/*** 1、制作只包含解密代码的dex文件*/File aarFile = new File("proxy_core/build/outputs/aar/proxy_core-debug.aar");File aarTemp = new File("proxy_tools/temp");Zip.unZip(aarFile,aarTemp);File classesDex = new File(aarTemp,"classes.dex");File classesJar = new File(aarTemp,"classes.jar");//dx --dex --output out.dex in.jar     E:\AndroidSdk\Sdk\build-tools\23.0.3Process process = Runtime.getRuntime().exec("cmd /c dx --dex --output " + classesDex.getAbsolutePath()+ " " + classesJar.getAbsolutePath());process.waitFor();if (process.exitValue() != 0){throw new RuntimeException("dex error");}/*** 2、加密apk中所有的dex文件*/File apkFile = new File("app/build/outputs/apk/debug/app-debug.apk");File apkTemp = new File("app/build/outputs/apk/debug/temp");Zip.unZip(apkFile,apkTemp);//只要dex文件拿出来加密File[] dexFiles = apkTemp.listFiles(new FilenameFilter() {@Overridepublic boolean accept(File file, String s) {return s.endsWith(".dex");}});//AES加密AES.init(AES.DEFAULT_PWD);for (File dexFile:dexFiles) {byte[] bytes = Utils.getBytes(dexFile);byte[] encrypt = AES.encrypt(bytes);FileOutputStream fos = new FileOutputStream(new File(apkTemp,"secret-" + dexFile.getName()));fos.write(encrypt);fos.flush();fos.close();dexFile.delete();}/*** 3、把dex放入apk解压目录,重新压成apk文件*/classesDex.renameTo(new File(apkTemp,"classes.dex"));File unSignedApk = new File("app/build/outputs/apk/debug/app-unsigned.apk");Zip.zip(apkTemp,unSignedApk);/*** 4、对其和签名,最后生成签名apk*///        zipalign -v -p 4 my-app-unsigned.apk my-app-unsigned-aligned.apkFile alignedApk=new File("app/build/outputs/apk/debug/app-unsigned-aligned.apk");process= Runtime.getRuntime().exec("cmd /c zipalign -v -p 4 "+unSignedApk.getAbsolutePath()+" "+alignedApk.getAbsolutePath());
//        System.out.println("signedApkprocess : 11111" + "  :----->  " +unSignedApk.getAbsolutePath() + "\n" +  alignedApk.getAbsolutePath());process.waitFor();
//        if(process.exitValue()!=0){
//            throw new RuntimeException("dex error");
//        }//        apksigner sign --ks my-release-key.jks --out my-app-release.apk my-app-unsigned-aligned.apk
//        apksigner sign  --ks jks文件地址 --ks-key-alias 别名 --ks-pass pass:jsk密码 --key-pass pass:别名密码 --out  out.apk in.apkFile signedApk=new File("app/build/outputs/apk/debug/app-signed-aligned.apk");File jks=new File("proxy_tools/proxy1.jks");process= Runtime.getRuntime().exec("cmd /c apksigner sign --ks "+jks.getAbsolutePath()+" --ks-key-alias wwy --ks-pass pass:123456 --key-pass pass:123456 --out "+signedApk.getAbsolutePath()+" "+alignedApk.getAbsolutePath());process.waitFor();if(process.exitValue()!=0){throw new RuntimeException("dex error");}System.out.println("执行成功");}}

 我们在写好前面的之后,直接运行这个main方法,就可以在我们的app -> build->outputs->apk->debug下面看到生成的几个apk,分别为 app-debug.apk,  app-unsigned.apk,  app-unsigned-aligned.apk,  app-signed-aligned.apk,最终 app-signed-aligned.apk 才是我们最后安装使用的apk,

 

注意:如果大家直接按照上面的弄完后,运行main 方法,恐怕会出现不可描述的问题,各种错误,异常,哈哈哈,,正常,,因为我也是这么过来的,所以这里在说一下一些要做的工作:

1)、配置电脑的环境变量:

找到你电脑中sdk的路劲,然后在SDK中把build-tools下面的任意一个版本配置到你的电脑的用户变量中,我这里路劲是:F:\androidSDK\Sdk\build-tools\25.0.0  然后我就把这个路劲配置到用户变量中,(注意:在配置这个变量之后,你的AS一定要重新启动哦,否则依然报错,如果重启as不行,那就直接重启电脑再试试,我也遇到过),这个步骤是准备main中第一个Process命令使用的。

第一步会生成两个文件,一个是classes.jar 和  classes.dex。

下面一共有三个Process  命令操作,但是如果我们直接运行main方法时就算上面的环境配置好了,也未必能直接运行成功,为什么呢?因为我具体也不是很清楚,但是不少人都遇到了,什么问题呢?就是当运行到第二个Process 命令的时候,代码运行到process.waitFor();时,就不往下走了,经过咨询别人讲是由于创建的进程导致的,说什么内存缓存区不足,导致进程一直在等待,我不晓得原因,我的代码运行到下图位置就不动了,

 不过已经生成了除了签名之后的apk ,其它的都有了,我百思不得其解,最后我在AS中Terminal 中直接通过命令执行最后一个生成签名的apk 才算把签名之后的apk搞出来,很多人也遇到和我一样的问题。你如果也是这样,那也通过命令操作吧。

2)、记住在执行main方法之前,我们可以看到main方法第四部生成签名的apk,说明我们apk是需要签名的,所以,我们要先签名哦,然后记住别名和密码哦,然后把别名和密码写在第四部中,下面给大家看看我的代码目录结构:

上面就是整个的目录结构,运行完,出来在上面找到签名之后的apk,然后直接拿过去运行能运行出来就可以了!在AS中点击签名之后的apk,会发现这个apk是看不到里面任何的文件的,也就实现了我们的加固功能,别人拿到我们的apk也白费,啥玩意都没有,是不是很牛?哈哈哈哈,,,路漫漫其修远兮? 还有很多的路要走。继续努力!

这里带多一句嘴,上面的代理ProxyApplication被我们配置到Mainfest的application 标签中,这个位置经常是我们配置项目使用的application的,其实不用担心,代码中已经处理过了,当代理application处理完之后,会自动把我们配置的app里面的项目用到的MyApplication 类替换过来,所以项目在第一次运行完之后,正式运行还是以我们自己的MyApplication为主,大可放心。

 

完整的demo下载地址:https://download.csdn.net/download/i123456789t/11239611  


http://chatgpt.dhexx.cn/article/2Tjjlwv8.shtml

相关文章

Android APK加固原理

一、前言 Android作为开源框架&#xff0c;开放之余&#xff0c;所要面临的就是安全问题&#xff0c;世间之事&#xff0c;有正就有邪&#xff0c;有攻就有守&#xff0c;作为开发者虽然不需要进入专业安全领域&#xff0c;但还是需要掌握基本的安全常识和原理。 二、加壳 加…

APK加固原理详解

一、前言 之前使用的360加固&#xff0c;挺好用的&#xff0c;从2021年底的时候限制每天每个账号仅上传2次apk&#xff08;免费的&#xff0c;不知道VIP的是不是这样&#xff09;。通过这个事情&#xff0c;感觉技术还是掌握在自己手里稳妥点&#xff0c;不用受制于人&#xf…

Android中的Apk的加固(加壳)原理解析和实现

本文转载自&#xff1a;Android中的Apk的加固(加壳)原理解析和实现 - roccheung - 博客园 一、前言 今天又到周末了&#xff0c;憋了好久又要出博客了&#xff0c;今天来介绍一下Android中的如何对Apk进行加固的原理。现阶段。我们知道Android中的反编译工作越来越让人操作熟…

浅谈安卓apk加固原理和实现

转载本文需注明出处&#xff1a;微信公众号EAWorld&#xff0c;违者必究。 引言&#xff1a; 在安卓开发中&#xff0c;打包发布是开发的最后一个环节&#xff0c;apk是整个项目的源码和资源的结合体&#xff1b;对于懂点反编译原理的人可以轻松编译出apk的源码资源&#xff0c…

安卓逆向笔记--apk加固

安卓逆向笔记–apk加固 资料来源: 浅谈安卓apk加固原理和实现 Android中的Apk的加固(加壳)原理解析和实现 前两个太老了所以具体代码借鉴下面的 Android Apk加壳技术实战详解 一、apk常见加固方法 (1)代码层级加密–代码混淆 代码混淆是一种常见的加密方式。本质是把工程中原…

imx6ull uboot移植

以下内容来自&#xff1a;正点原子Linux驱动文档 一、简介 uboot移植主要是根据原厂的uboot移植&#xff1a;芯片厂商通常会做一块自己的评估板并发布BSP&#xff0c;当我们需要定制自己的开发板时可以根据自己的需求&#xff08;硬件上的不同&#xff09;&#xff0c;对原厂…

全志V3S嵌入式驱动开发(uboot移植)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 安装了ubuntu操作系统,有了开发板,下面就可以进行我们的开发工作了。第一步,我们要面临的问题就是uboot移植。一般来说,嵌入式的基础环境就是uboot、linux和rootfs。uboot完成一…

2021-09-14 uboot移植开发

引言&#xff1a;最近要改动uboot&#xff0c;实现像微软PC上&#xff0c;u盘一键刷机或手机上安全模式下刷机的操作 专门去好好研究了点uboot的启动过程&#xff1b;以下为总结&#xff1a; 嵌入式系统 微软-PC ——…

iTOP4412 uboot移植教程

好多刚开始学习uboot移植的同学总是觉得uboot好难&#xff0c;但是再难的问题如果把它一步步拆开&#xff0c;一个个解决&#xff0c;问题也就将迎刃而解。做uboot移植&#xff0c;我们首先就得了解uboot的编译流程&#xff0c;这里以在iTOP4412精英版2G内存的板子上移植u-boot…

<Linux开发> -之-系统移植 uboot移植过程详细记录(第二部分)

&#xff1c;Linux开发&#xff1e; -之-系统移植 uboot移植过程详细记录&#xff08;第二部分&#xff09; 第一部分链接&#xff1a;系统移植-之-uboot移植第一部分 第一部分主要讲解了&#xff0c;uboot移植过程中使用的一些工具的安装&#xff0c;以及测试nxp远程uboot&a…

X210开发板(S5PV210芯片)uboot移植DM9000驱动移植

前言 本文是介绍在uboot中如何移植DM9000的驱动&#xff0c;并不深入去讲解DM9000芯片的操作时序和内部寄存器&#xff0c;想要读懂驱动代码要仔细阅读DM9000芯片的数据手册。移植的基础是手里有DM9000芯片可以用的驱动代码&#xff0c;只需要根据开发板中DM9000芯片的接线方式…

嵌入式linux UBoot移植篇

如何在U-boot添加自己的linux板卡并启动呢&#xff1f; uboot 的移植并不是说我们完完全全的从零开始将 uboot 移植到我们现在所使用的开发板或者开发平台上。这个对于我们来说基本是不可能的&#xff0c;这个工作一般是半导体厂商做的&#xff0c; 半导体厂商负责将 uboot 移…

Uboot移植流程

linux-Bootloader&#xff08;Uboot&#xff09;移植流程 前言 最近在做ZigBee的温室大棚项目&#xff0c;将自己学习的过程和经验分享给大家。本文基于linux3.4.39内核版本&#xff0c;s5p6818开发板实现。 1、uboot启动简介 uboot启动的过程比较复杂&#xff0c;这里就只…

IMX6ULL Uboot 移植

使用的开发板&#xff1a;正点原子ALPHA V2.2 Uboot简介 在学习STM32的过程中使用过IAP在线升级就会知道&#xff0c;有引导程序APP程序&#xff0c;即bootloader程序APP。在学习嵌入式Linux的时候也一样&#xff0c;这个引导程序就是Uboot. uboot移植主要是根据原厂的uboot移…

二、uboot移植

二、uboot移植 版本作者时间备注V 1.0bug设计工程师2021/11/10创建文件软件网盘链接0交叉编译工具链接:https://pan.baidu.com/s/1yFO2NDMet9_b1E1q1rMwEA提取码:42kluboot源码同上linux源码同上文件系统工具同上tftp工具同上2.1 简单说明 uboot制作结束会生成 u-boot-etc44…

linux-uboot 移植四 uboot的移植

概述 前边的章节中介绍到如果要移植uboot的话&#xff0c;最好的参考就是由官方提供的demo。 1、移植 1.1 添加board对应的板级文件夹 uboot 中每个板子都有一个对应的文件夹来存放板级文件&#xff0c;比如开发板上外设驱动文件等等。 NXP 的 I.MX 系列芯片的所有板级文件…

[uboot 移植]uboot 移植过程

文章目录 uboot 移植1 修改顶层 Makefile2 在 board 文件夹下添加开发板对应的板级文件2.1 imximage_lpddr2.cfg 和 imximage.cfg 文件2.2 plugin.S 文件2.3 Kconfig 文件2.4 igkboard.c 文件2.5 MAINTAINERS 文件2.6 Makefile 文件 3 添加 igkboard_defconfig 配置文件4 添加开…

UBoot 移植

1 NXP官方开发板uboot编译测试 1 查找 NXP 官方的开发板默认配置文件 因为我们的开发板是参考 NXP 官方的 I.MX6ULL EVK 开发板做的硬件&#xff0c;因此我们在移植 uboot 的时候就可以以 NXP 官方的 I.MX6ULL EVK 开发板为蓝本。 在 NXP 官方 I.MX6UL/6ULL 默认配置文件中找…

大话uboot 移植

结合作者多年的移植经验&#xff0c;尽量简单的为大家描述一个uboot 的移植过程。希望通过描述&#xff0c;给初入移植行道的你带来美好的希望。接下来&#xff0c;我们通过以下几个方面来描述。 1. arm soc 的启动方式 在描述soc 前&#xff0c;我们先看下一个简单的arm soc:…

uboot移植步骤

Uboot移植具体步骤(本例子为Samsung origen板) 第一步:准备源码 网上下载现成的uboot开源代码:https://ftp.denx.de/pub/u-boot/ 在该网站中选择与板子兼容的uboot源文件(公司中一般咨询硬件工程师) 在Linux系统下解压 tar xf 压缩包名 第二步:修改源码 1.抄板:将和你板子兼容…