安卓逆向笔记–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文件中写解壳程序,并加密。

安卓逆向笔记--apk加固相关推荐

  1. 安卓逆向笔记-得到360加固的dex文件

    借鉴于大佬的教程 https://www.52pojie.cn/thread-685918-1-1.html https://blog.csdn.net/qing666888/article/deta ...

  2. 安卓逆向笔记(课外资料)

    安卓逆向笔记(课外资料) 只有知识点 分析apk主要组成 1.AndroidMainfest.xml-----配置清单 该文件是每个应用都必须定义和包含的,它描述了用的名字.版本.权限.引用的库文件等 ...

  3. 【安卓逆向】360加固-脱壳修复

    最近花了一些时间学习逆向脱壳,这方面一直投入的时间比较少.样本经过某加固宝进行加固,这里简单记录一下脱壳过程和思路,感谢某数字公司对安全加固的无私贡献,让我有机会小小的提高一下这方面的技能. *安卓逆 ...

  4. python 工程结构加固_【安卓逆向】360加固-脱壳修复

    360加固-脱壳修复 最近花了一些时间学习逆向脱壳,这方面一直投入的时间比较少.样本经过某加固宝进行加固,这里简单记录一下脱壳过程和思路,感谢某数字公司对安全加固的无私贡献,让我有机会小小的提高一下这 ...

  5. 安卓逆向_18 --- APK保护策略【Java代码混淆、资源混淆、签名校验】

    Java 代码混淆介绍:https://www.bilibili.com/video/BV1UE411A7rW?p=60 Android 反编译利器 jadx:GitHub上直接下载:https:// ...

  6. apk逆向思路_安卓逆向和手游辅助学习路线

    一.安卓逆向基础(建议1周) 1. 学习安卓逆向第一步必须先把环境搭建好,这是你学习安卓逆向的开始,环境搭建好后表示正式迈入安卓逆向.在环境安装的工程中会遇到很多细节上的问题. 2. 第二步就是要了解 ...

  7. 什么是Android逆向?如何学习安卓逆向?Android逆向自学笔记入门到实战

    简单地来说,安卓逆向是对已经打包好的APP进行反编译.源码分析了解APP实现逻辑的一门技术.我们可以把安卓安装时用到的APK文件看作一个加密后的压缩包,逆向就是要最大程序地还原出APK打包之前的源码. ...

  8. 记录第一次完整安卓逆向过程笔记

    文章目录 前言 一.安卓逆向的准备 二.刷机+配置+安装app 1.安卓系统版本 2.手机root+面具安装 3.xposed安装 三.逆向app 总结 前言 作为新时代爬虫er,逆向技能是必不可少的 ...

  9. 安卓逆向-入门笔记、相关知识点总结及思路

    文章目录 安卓逆向思路: 1.查壳 2.未加固 2.1 工具 2.2方法 3 .第一代加固 3.1 工具 3.2 方法 3.3 材料准备 3.4 实例 补充知识: 一.Android 1.加密原则: ...

最新文章

  1. html表单颜色选择器,如何在Django管理中使用HTML5颜色选择器
  2. JVM Class字节码之三-使用BCEL改变类属性
  3. 重置SVN用户名与密码,修改SVN的IP地址
  4. “@react-navigation/native“).createNavigatorFactory) is not a function.
  5. Ubuntu网络连接未托管的解决办法
  6. react 前端解析二进制流_一年半前端跳槽面试经验(头条、微信、shopee)
  7. matlab做聚类分析
  8. 类继承和初始化类的执行顺序
  9. 36氪首发|一年完成三轮融资,STEAM 教育内容供应商「唯科乐」获 Pre-A 轮融资...
  10. 由条件熵与无条件熵的关系引出的不等式证明题(不会抄答案系列)
  11. 用计算机软件绘制思维导图,电脑软件绘制思维导图操作教程分享
  12. 对于Biotin-LC-NHS Estercas:72040-63-2与伯氨基 (-NH2) 反应的作用有哪些?
  13. 【欧拉降幂公式】【欧拉函数】
  14. LeCo-121. 买卖股票的最佳时机
  15. 数据仓库-Hive(一)
  16. java网络编程技术有哪些_Java网络编程技术
  17. 手机芯片内部构造-数据提取方法
  18. 12306所有车次及时刻表的爬取中
  19. 巴西龟饲养日志----养龟第二年开始
  20. dnf剑魂buff等级上限_DNF:全职业BUFF等级一览,深渊之鳞护肩宝珠别选错等级了

热门文章

  1. lua中的 ipairs 和 pairs
  2. 查看操作系统安装时间的方法
  3. echarts自定义下载图表
  4. 如何开启MATLAB并行,如何并行运行Matlab计算
  5. Linux FrameBuffer分析之编写基于FrameBuffer接口的应用程序
  6. 10月24 日 c语言 计算1至10之间奇数之和及偶数之和。
  7. 愿你出走半生,归来仍是少年
  8. CTF-哈希碰撞-MD5
  9. MySQL复制数据库表到另一个数据库
  10. 华为垃圾分类大赛亚军答辩心得和PPT分享