安卓逆向笔记--apk加固

article/2025/10/6 15:45:06

安卓逆向笔记–apk加固

资料来源:
浅谈安卓apk加固原理和实现
Android中的Apk的加固(加壳)原理解析和实现
前两个太老了所以具体代码借鉴下面的
Android Apk加壳技术实战详解

一、apk常见加固方法

(1)代码层级加密–代码混淆

        代码混淆是一种常见的加密方式。本质是把工程中原来具有含义的类名、变量名、方法名,修改成让人看不懂的名字。常见的代码混淆工具proguard。该加密方式只是对工程提供了最小的保护,并不是说不能逆向破解;只是说难度增加,需要耐心。

(2)Dex文件加密

        dex是Android工程中的代码资源文件,通过dex可以反编译出java代码。dex的加壳是常见的加密方式。通过对dex文件加密拼接加壳,可以有效的对工程代码进行保护。apk工程在安装成功后,app启动时会有dex解密的过程,然后重新加载解密后的dex文件。基本原理是在jni层, 使用DexClassLoader动态加载技术完成对加密classex.dex的动态加载,dex文件可以附属在assert或raw目录。

二、apk文件结构

(1)什么是dex文件

        dex是应用安装时生成的虚拟机可执行二进制文件,如果应用还存在,删除了下次手机开机时还会再次生成,卸载软件时会同时删除dex文件。
  对于Android DEX文件进行优化,需要注意的一点是DEX文件的结构是紧凑的,但是我们还是要想方设法的进行提高程序的运行速度,我们就仍然需要对DEX文件进行进一步优化。
  调整所有字段的字节序(LITTLE_ENDIAN)和对齐结构中的每一个域 验证DEX文件中的所有类 对一些特定的类进行优化,对方法里的操作码进行优化 。优化后的文件大小会有所增加,应该是原Android DEX文件的1-4倍。 优化发生的时机有两个:对于预置应用,可以在系统编译后,生成优化文件,以ODEX结尾。
  这样在发布时除APK文件(不包含DEX)以外,还有一个相应的Android DEX文件;对于非预置应用,包含在APK文件里的DEX文件会在运行时被优化,优化后的文件将被保存在缓存中。
  每一个Android应用都运行在一个Dalvik虚拟机实例里,而每一个虚拟机实例都是一个独立的进程空间。虚拟机的线程机制,内存分配和管理,Mutex等等都是依赖底层操作系统而实现的。

(2)dex文件格式

在这里插入图片描述
dex文件头
在这里插入图片描述
(1) checksum 文件校验码 ,使用alder32 算法校验文件除去 maigc ,checksum 外余下的所有文件区域 ,用于检查文件错误 。
(2) signature 使用 SHA-1 算法 hash 除去 magic ,checksum 和 signature 外余下的所有文件区域 ,用于唯一识别本文件 。
(3) file_size Dex 文件的总长度 。

加固原理

加固过程中的三个对象
(1)需要加固的java工程
(2)壳程序APK(负责解密apk工作)
(3)加密工具(将源apk进行加密和壳dex合并成新的dex)
主要流程
用加密算法加固源apk与壳apk进行合并得到新的dex文件,最后替换壳程序中的dex文件即可,得到新的apk(脱壳程序apk)。这个apk已经不是完整意义上的apk,他只负责解密源apk。加载apk,让其正常的运行起来。
在这里插入图片描述
在这里插入图片描述
加固后工程的加载原理
APP启动——>自定义Application中attachBaseContext()方法——>自定义Application工程onCreate()方法——>源Application
1、自定义Application来自于壳程序的dex,加密合成的新dex前半部分就是壳程序的dex,这部分是没任何问题,可以正常加载。该Application中attachBaseContext方法会做解密操作,解密出源dex并放置在固定目录下,添加dex的加载映射;映射到源dex目录。
2、自定义Application工程onCreate()方法添加源dex加载的入口;即源dex的application和mainActivity。
3、程序正常启动;源dex被正确加载。

加固实现

一、编写需要加壳的APK

首先需要一个项目,自己可以编写一个,借鉴大佬们的apk写了一个。
apk链接:https://pan.baidu.com/s/1d7ZS-_QWbSVqar-7ehjMTw 提取码:pzn9
没法用百度网盘了,要我充值超级会员才能上传,果断github
代码链接: https://github.com/iaa-mr/android_protect
demo为需要加壳的程序
在编写的的时候需要注意一下AndroidManifest.xml这个文件的配置,以及MyApplication.java的内容.
一定一定要注意AndroidManifest.xml文件内容和签名
一定一定要注意AndroidManifest.xml文件内容和签名
一定一定要注意AndroidManifest.xml文件内容和签名
重要的事情说三遍
AndroidManifest.xml文件内容

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.demo"><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><applicationandroid:name=".MyApplication"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>

MyApplication.java内容

package com.example.demo;import android.app.Application;
import android.util.Log;public class MyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();Log.i("demo", "apk onCreate:" + this);}
}
二、编写解壳的APK

代码链接: https://github.com/iaa-mr/android_protect
这个加壳主要原理是动态加载程序,具体原理:
http://blog.csdn.net/jiangwei0910410003/article/details/48104455
在编写的的时候需要注意一下AndroidManifest.xml这个文件的配置,以及MyApplication.java的内容.
一定一定要注意AndroidManifest.xml文件内容和签名
一定一定要注意AndroidManifest.xml文件内容和签名
一定一定要注意AndroidManifest.xml文件内容和签名
重要的事情说三遍
AndroidManifest.xml文件内容

<android:value="com.example.demo.MyApplication"/>
<activity android:name="com.example.demo.MainActivity" >

因为这个壳程序使用来动态加载之前的demo程序,所以包名是demo的。
并且需要将demo的res文件整体以到这个解壳程序。
一定要注意这两段语句,否则加完壳后程序将无法运行。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.myunshell" ><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><applicationandroid:name=".ProxyApplication"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme" ><meta-dataandroid:name="APPLICATION_CLASS_NAME"android:value="com.example.demo.MyApplication"/><activity android:name="com.example.demo.MainActivity" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>

ProxyApplication.java内容

package com.example.myunshell;import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;import android.app.Application;
import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.os.Bundle;
import android.util.ArrayMap;
import android.util.Log;import com.example.myunshell.RefInvoke;import dalvik.system.DexClassLoader;public class ProxyApplication extends Application{private static final String appkey = "APPLICATION_CLASS_NAME";private String apkFileName;private String odexPath;private String libPath;//这是context 赋值@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);try {//创建两个文件夹payload_odex,payload_lib 私有的,可写的文件目录File odex = this.getDir("demo_odex", MODE_PRIVATE);File libs = this.getDir("demo_lib", MODE_PRIVATE);odexPath = odex.getAbsolutePath();libPath = libs.getAbsolutePath();apkFileName = odex.getAbsolutePath() + "/shelldemo.apk";File dexFile = new File(apkFileName);Log.i("demo", "apk size:"+dexFile.length());if (!dexFile.exists()){dexFile.createNewFile();  //在payload_odex文件夹内,创建payload.apk// 读取程序classes.dex文件byte[] dexdata = this.readDexFileFromApk();// 分离出解壳后的apk文件已用于动态加载this.splitPayLoadFromDex(dexdata);}// 配置动态加载环境Object currentActivityThread = RefInvoke.invokeStaticMethod("android.app.ActivityThread", "currentActivityThread",new Class[] {}, new Object[] {});//获取主线程对象 http://blog.csdn.net/myarrow/article/details/14223493String packageName = this.getPackageName();//当前apk的包名//下面两句不是太理解ArrayMap mPackages = (ArrayMap) RefInvoke.getFieldOjbect("android.app.ActivityThread", currentActivityThread,"mPackages");WeakReference wr = (WeakReference) mPackages.get(packageName);//创建被加壳apk的DexClassLoader对象  加载apk内的类和本地代码(c/c++代码)DexClassLoader dLoader = new DexClassLoader(apkFileName, odexPath,libPath, (ClassLoader) RefInvoke.getFieldOjbect("android.app.LoadedApk", wr.get(), "mClassLoader"));//base.getClassLoader(); 是不是就等同于 (ClassLoader) RefInvoke.getFieldOjbect()? 有空验证下//?//把当前进程的DexClassLoader 设置成了被加壳apk的DexClassLoader  ----有点c++中进程环境的意思~~RefInvoke.setFieldOjbect("android.app.LoadedApk", "mClassLoader",wr.get(), dLoader);Log.i("demo","classloader:"+dLoader);} catch (Exception e) {Log.i("demo", "error:"+Log.getStackTraceString(e));e.printStackTrace();}}@Overridepublic void onCreate() {super.onCreate();{//loadResources(apkFileName);Log.i("demo", "onCreate");// 如果源应用配置有Appliction对象,则替换为源应用Applicaiton,以便不影响源程序逻辑。String appClassName = null;try {ApplicationInfo ai = this.getPackageManager().getApplicationInfo(this.getPackageName(),PackageManager.GET_META_DATA);Bundle bundle = ai.metaData;if (bundle != null && bundle.containsKey("APPLICATION_CLASS_NAME")) {appClassName = bundle.getString("APPLICATION_CLASS_NAME");//className 是配置在xml文件中的。} else {Log.i("demo", "have no application class name");return;}} catch (NameNotFoundException e) {Log.i("demo", "error:" + Log.getStackTraceString(e));e.printStackTrace();}//有值的话调用该ApplicaitonObject currentActivityThread = RefInvoke.invokeStaticMethod("android.app.ActivityThread", "currentActivityThread",new Class[]{}, new Object[]{});Object mBoundApplication = RefInvoke.getFieldOjbect("android.app.ActivityThread", currentActivityThread,"mBoundApplication");Object loadedApkInfo = RefInvoke.getFieldOjbect("android.app.ActivityThread$AppBindData",mBoundApplication, "info");//把当前进程的mApplication 设置成了nullRefInvoke.setFieldOjbect("android.app.LoadedApk", "mApplication",loadedApkInfo, null);Object oldApplication = RefInvoke.getFieldOjbect("android.app.ActivityThread", currentActivityThread,"mInitialApplication");//http://www.codeceo.com/article/android-context.htmlArrayList<Application> mAllApplications = (ArrayList<Application>) RefInvoke.getFieldOjbect("android.app.ActivityThread",currentActivityThread, "mAllApplications");mAllApplications.remove(oldApplication);//删除oldApplicationApplicationInfo appinfo_In_LoadedApk = (ApplicationInfo) RefInvoke.getFieldOjbect("android.app.LoadedApk", loadedApkInfo,"mApplicationInfo");ApplicationInfo appinfo_In_AppBindData = (ApplicationInfo) RefInvoke.getFieldOjbect("android.app.ActivityThread$AppBindData",mBoundApplication, "appInfo");appinfo_In_LoadedApk.className = appClassName;appinfo_In_AppBindData.className = appClassName;Application app = (Application) RefInvoke.invokeMethod("android.app.LoadedApk", "makeApplication", loadedApkInfo,new Class[]{boolean.class, Instrumentation.class},new Object[]{false, null});//执行 makeApplication(false,null)RefInvoke.setFieldOjbect("android.app.ActivityThread","mInitialApplication", currentActivityThread, app);ArrayMap mProviderMap = (ArrayMap) RefInvoke.getFieldOjbect("android.app.ActivityThread", currentActivityThread,"mProviderMap");Iterator it = mProviderMap.values().iterator();while (it.hasNext()) {Object providerClientRecord = it.next();Object localProvider = RefInvoke.getFieldOjbect("android.app.ActivityThread$ProviderClientRecord",providerClientRecord, "mLocalProvider");RefInvoke.setFieldOjbect("android.content.ContentProvider","mContext", localProvider, app);}Log.i("demo", "app:" + app);app.onCreate();}}/*** 释放被加壳的apk文件,so文件* @param* @throws IOException*/private void splitPayLoadFromDex(byte[] apkdata) throws IOException {int ablen = apkdata.length;//取被加壳apk的长度   这里的长度取值,对应加壳时长度的赋值都可以做些简化byte[] dexlen = new byte[4];System.arraycopy(apkdata, ablen - 4, dexlen, 0, 4);ByteArrayInputStream bais = new ByteArrayInputStream(dexlen);DataInputStream in = new DataInputStream(bais);int readInt = in.readInt();System.out.println(Integer.toHexString(readInt));byte[] newdex = new byte[readInt];//把被加壳apk内容拷贝到newdex中System.arraycopy(apkdata, ablen - 4 - readInt, newdex, 0, readInt);//这里应该加上对于apk的解密操作,若加壳是加密处理的话//?//对源程序Apk进行解密newdex = decrypt(newdex);//写入apk文件File file = new File(apkFileName);try {FileOutputStream localFileOutputStream = new FileOutputStream(file);localFileOutputStream.write(newdex);localFileOutputStream.close();} catch (IOException localIOException) {throw new RuntimeException(localIOException);}//分析被加壳的apk文件ZipInputStream localZipInputStream = new ZipInputStream(new BufferedInputStream(new FileInputStream(file)));while (true) {ZipEntry localZipEntry = localZipInputStream.getNextEntry();//不了解这个是否也遍历子目录,看样子应该是遍历的if (localZipEntry == null) {localZipInputStream.close();break;}//取出被加壳apk用到的so文件,放到 libPath中(data/data/包名/payload_lib)String name = localZipEntry.getName();if (name.startsWith("lib/") && name.endsWith(".so")) {File storeFile = new File(libPath + "/"+ name.substring(name.lastIndexOf('/')));storeFile.createNewFile();FileOutputStream fos = new FileOutputStream(storeFile);byte[] arrayOfByte = new byte[1024];while (true) {int i = localZipInputStream.read(arrayOfByte);if (i == -1)break;fos.write(arrayOfByte, 0, i);}fos.flush();fos.close();}localZipInputStream.closeEntry();}localZipInputStream.close();}/*** 从apk包里面获取dex文件内容(byte)* @return* @throws IOException*/private byte[] readDexFileFromApk() throws IOException {ByteArrayOutputStream dexByteArrayOutputStream = new ByteArrayOutputStream();ZipInputStream localZipInputStream = new ZipInputStream(new BufferedInputStream(new FileInputStream(this.getApplicationInfo().sourceDir)));while (true) {ZipEntry localZipEntry = localZipInputStream.getNextEntry();if (localZipEntry == null) {localZipInputStream.close();break;}if (localZipEntry.getName().equals("classes.dex")) {byte[] arrayOfByte = new byte[1024];while (true) {int i = localZipInputStream.read(arrayOfByte);if (i == -1)break;dexByteArrayOutputStream.write(arrayOfByte, 0, i);}}localZipInputStream.closeEntry();}localZipInputStream.close();return dexByteArrayOutputStream.toByteArray();}// //直接返回数据,读者可以添加自己解密方法private byte[] decrypt(byte[] srcdata) {for(int i=0;i<srcdata.length;i++){srcdata[i] = (byte)(0xFF ^ srcdata[i]);}return srcdata;}//以下是加载资源protected AssetManager mAssetManager;//资源管理器protected Resources mResources;//资源protected Theme mTheme;//主题protected void loadResources(String dexPath) {try {AssetManager assetManager = AssetManager.class.newInstance();Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);addAssetPath.invoke(assetManager, dexPath);mAssetManager = assetManager;} catch (Exception e) {Log.i("inject", "loadResource error:"+Log.getStackTraceString(e));e.printStackTrace();}Resources superRes = super.getResources();superRes.getDisplayMetrics();superRes.getConfiguration();mResources = new Resources(mAssetManager, superRes.getDisplayMetrics(),superRes.getConfiguration());mTheme = mResources.newTheme();mTheme.setTo(super.getTheme());}@Overridepublic AssetManager getAssets() {return mAssetManager == null ? super.getAssets() : mAssetManager;}@Overridepublic Resources getResources() {return mResources == null ? super.getResources() : mResources;}@Overridepublic Theme getTheme() {return mTheme == null ? super.getTheme() : mTheme;}}

具体代码在github里面有

三、进行合并dex文件

代码链接: https://github.com/iaa-mr/android_protect
DexShellTool文件夹里就是代码
这个可以直接做一个单独的java程序以后改改就能用,并不需要新建一个单独的apk项目。
具体代码

File payloadSrcFile = new File("C:/Users/myh18/Desktop/DexShellTool/force/demo.apk");   //需要加壳的程序
File unShellDexFile = new File("C:/Users/myh18/Desktop/DexShellTool/force/shell.dex");    //解壳dex
String str = "C:\\Users/myh18/Desktop/DexShellTool/force/classes.dex";

这个里面的路径要自己写。
最核心的代码
用来合并两个dex文件,并修改合并后的dex文件的长度、SHA1和CheckSum的文件头

  int payloadLen = payloadArray.length;int unShellDexLen = unShellDexArray.length;int totalLen = payloadLen + unShellDexLen +4;//多出4字节是存放长度的。byte[] newdex = new byte[totalLen]; // 申请了新的长度//添加解壳代码System.arraycopy(unShellDexArray, 0, newdex, 0, unShellDexLen);//先拷贝dex内容//添加加密后的解壳数据System.arraycopy(payloadArray, 0, newdex, unShellDexLen, payloadLen);//再在dex内容后面拷贝apk的内容//添加解壳数据长度System.arraycopy(intToByte(payloadLen), 0, newdex, totalLen-4, 4);//最后4为长度//修改DEX file size文件头fixFileSizeHeader(newdex);//修改DEX SHA1 文件头fixSHA1Header(newdex);//修改DEX CheckSum文件头fixCheckSumHeader(newdex);

加密语句

byte[] payloadArray = encrpt(readFileBytes(payloadSrcFile));//以二进制形式读出apk,并进行加密处理//对源Apk进行加密操作
 //直接返回数据,读者可以添加自己加密方法private static byte[] encrpt(byte[] srcdata){for(int i = 0;i<srcdata.length;i++){srcdata[i] = (byte)(0xFF ^ srcdata[i]);}return srcdata;}

所有加壳代码

import java.io.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.zip.Adler32;/*** Created by DeMon on 2017/10/17.*/
public class DexShellTool {/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubtry {File payloadSrcFile = new File("C:/Users/myh18/Desktop/DexShellTool/force/demo.apk");   //需要加壳的程序System.out.println("apk size:"+payloadSrcFile.length());File unShellDexFile = new File("C:/Users/myh18/Desktop/DexShellTool/force/shell.dex");    //解壳dexbyte[] payloadArray = encrpt(readFileBytes(payloadSrcFile));//以二进制形式读出apk,并进行加密处理//对源Apk进行加密操作byte[] unShellDexArray = readFileBytes(unShellDexFile);//以二进制形式读出dexint payloadLen = payloadArray.length;int unShellDexLen = unShellDexArray.length;int totalLen = payloadLen + unShellDexLen +4;//多出4字节是存放长度的。byte[] newdex = new byte[totalLen]; // 申请了新的长度//添加解壳代码System.arraycopy(unShellDexArray, 0, newdex, 0, unShellDexLen);//先拷贝dex内容//添加加密后的解壳数据System.arraycopy(payloadArray, 0, newdex, unShellDexLen, payloadLen);//再在dex内容后面拷贝apk的内容//添加解壳数据长度System.arraycopy(intToByte(payloadLen), 0, newdex, totalLen-4, 4);//最后4为长度//修改DEX file size文件头fixFileSizeHeader(newdex);//修改DEX SHA1 文件头fixSHA1Header(newdex);//修改DEX CheckSum文件头fixCheckSumHeader(newdex);String str = "C:\\Users/myh18/Desktop/DexShellTool/force/classes.dex";File file = new File(str);if (!file.exists()) {file.createNewFile();}FileOutputStream localFileOutputStream = new FileOutputStream(str);localFileOutputStream.write(newdex);localFileOutputStream.flush();localFileOutputStream.close();} catch (Exception e) {e.printStackTrace();}}//直接返回数据,读者可以添加自己加密方法private static byte[] encrpt(byte[] srcdata){for(int i = 0;i<srcdata.length;i++){srcdata[i] = (byte)(0xFF ^ srcdata[i]);}return srcdata;}/*** 修改dex头,CheckSum 校验码* @param dexBytes*/private static void fixCheckSumHeader(byte[] dexBytes) {Adler32 adler = new Adler32();adler.update(dexBytes, 12, dexBytes.length - 12);//从12到文件末尾计算校验码long value = adler.getValue();int va = (int) value;byte[] newcs = intToByte(va);//高位在前,低位在前掉个个byte[] recs = new byte[4];for (int i = 0; i < 4; i++) {recs[i] = newcs[newcs.length - 1 - i];System.out.println(Integer.toHexString(newcs[i]));}System.arraycopy(recs, 0, dexBytes, 8, 4);//效验码赋值(8-11)System.out.println(Long.toHexString(value));System.out.println();}/*** int 转byte[]* @param number* @return*/public static byte[] intToByte(int number) {byte[] b = new byte[4];for (int i = 3; i >= 0; i--) {b[i] = (byte) (number % 256);number >>= 8;}return b;}/*** 修改dex头 sha1值* @param dexBytes* @throws NoSuchAlgorithmException*/private static void fixSHA1Header(byte[] dexBytes)throws NoSuchAlgorithmException {MessageDigest md = MessageDigest.getInstance("SHA-1");md.update(dexBytes, 32, dexBytes.length - 32);//从32为到结束计算sha--1byte[] newdt = md.digest();System.arraycopy(newdt, 0, dexBytes, 12, 20);//修改sha-1值(12-31)//输出sha-1值,可有可无String hexstr = "";for (int i = 0; i < newdt.length; i++) {hexstr += Integer.toString((newdt[i] & 0xff) + 0x100, 16).substring(1);}System.out.println(hexstr);}/*** 修改dex头 file_size值* @param dexBytes*/private static void fixFileSizeHeader(byte[] dexBytes) {//新文件长度byte[] newfs = intToByte(dexBytes.length);System.out.println(Integer.toHexString(dexBytes.length));byte[] refs = new byte[4];//高位在前,低位在前掉个个for (int i = 0; i < 4; i++) {refs[i] = newfs[newfs.length - 1 - i];System.out.println(Integer.toHexString(newfs[i]));}System.arraycopy(refs, 0, dexBytes, 32, 4);//修改(32-35)}/*** 以二进制读出文件内容* @param file* @return* @throws IOException*/private static byte[] readFileBytes(File file) throws IOException {byte[] arrayOfByte = new byte[1024];ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();FileInputStream fis = new FileInputStream(file);while (true) {int i = fis.read(arrayOfByte);if (i != -1) {localByteArrayOutputStream.write(arrayOfByte, 0, i);} else {return localByteArrayOutputStream.toByteArray();}}}
}
四、实现加壳的步骤

编写完程序后,确保所有程序的签名一致。
1、解壳程序MyUnShell的apk用压缩软件打开得到classes.dex,重命名为shell.dex
2、将demo.apk、shell.dex复制到force文件夹中
3、运行DexShellTool.java
运行方法,首先cmd到DexShellTool.java所在的位置,然后输入javac DexShellTool,java
然后,会生成一个class文件,不用管,继续输入java DexShellTool就可以了
生成的dex文件的大小近似于两个文件大小的和
4、将MyUnShell生成的apk命名为shell.apk,将生成的classes.dex替换里面的classes.dex文件
并将shell.apk的签名文件删除。
5、将shell.apk放在tools文件夹,运行sign.bat,生成shelldemo.apk就是已经加完壳的apk。
sign.bat内容

jarsigner -verbose -keystore DeMon.jks -storepass 123456 -keypass 123456 -sigfile CERT -digestalg SHA1 -sigalg MD5withRSA -signedjar shelldemo.apk shell.apk key

DeMon.jks 可以换成你自己的签名文件,123456为签名密码,shelldemo.apk 为生成的文件,shell.apk 为需要签名的文件。

五、查看加壳是否成功

首先运行demo程序,和shelldemo程序。
demo程序
在这里插入图片描述
shelldemo程序
在这里插入图片描述
此时两个文件的包名就已经不一样了,但不影响程序正常运行。
拖到android killer
demo程序
在这里插入图片描述
demo逆向后,发现可以看到源代码
shelldemo程序
在这里插入图片描述
发现代码变成了解壳程序的代码,android killer中按照包名找不到所需要的文件。加壳成功
ps:附上查apk签名cmd命令
keytool -list -printcert -jarfile 文件路径
直接使用本文中的签名可能会看到。不是已签名的 jar 文件,从网上搜不到个所以然,我认为是因为重新签名。。。
然后apk的壳就写完了,但是360加固,腾讯乐固,都是在.so文件中写解壳程序,并加密。


http://chatgpt.dhexx.cn/article/inY30ck6.shtml

相关文章

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.抄板:将和你板子兼容…

(二)uboot移植--从零开始自制linux掌上电脑(F1C200S)<嵌入式项目>

目录 一、前言 二、F1C200s上电启动顺序 三、前期准备 四、新建用户 五、交叉编译环境配置 六、uboot简介 七、uboot移植 &#x1f34f; uboot下载 &#x1f34f; uboot默认配置 &#x1f34f; uboot图形界面配置 &#x1f34f; uboot编译 &#x1f34f; 烧录bin…

U-Boot 移植初探

1. NXP官方开发板uboot编译测试 uboot移植不需要从零开始将uboot移植到使用的开发板上。因为半导体厂商通常都会自己做一个开发板&#xff0c; 将uboot移植到他们自己的原厂开发板上&#xff0c;再将这个uboot&#xff08;原厂BSP 包&#xff09;发布出去。因此使用自已的开发…

U-Boot移植

一、U-Boot Linux 系统要启动就必须需要一个 bootloader 程序&#xff0c;芯片上电以后先运行一段bootloader程序。 bootloader程序会先初始化DDR等外设&#xff0c;然后将Linux内核从flash(NAND&#xff0c;NOR FLASH&#xff0c; SD&#xff0c; eMMC 等)拷贝到 DDR 中&…

uboot移植

一、我们知道uboot就是一个Bootloader。但是&#xff0c;arm不像我们的pc机一样&#xff0c;用一个老毛桃随便找一个网站下一个windows镜像就可以直接装了。 我们的uboot对应的就是老毛桃里面刷的引导程序。 虽然uboo支持很多架构很多不同的厂家的板子&#xff0c;但是UBOOT它…

【Linux】系统移植篇四--uboot移植

系统移植篇四--uboot移植 一、uboot源码结构1、uboot源码获取2、uboot特点3、uboot源码结构 二、uboot的配置与编译1、uboot配置2、uboot编译 前言&#xff1a;本篇主要介绍uboot的一些结构与相关配置&#xff0c;uboot是一款免费开源的芯片启动软件&#xff0c;代码完全开源&a…