Apk源码的加固(加壳)原理解析和实现

好久没写博客了,要深刻检讨下!

前言:
在Android中没有经过加密的Apk给人的感觉就是在裸奔,通过apktool,dex2jar,AndroidKill等各式各样的反编译工具就可以轻松的获取其smail代码,如这个叫SourceProject的helloworld程序被apktool反编译后,对于懂smail语法的逆向工程师来说就一览无余了。破解与反破解是相对的,所以我们尽可能的给自己的Apk多穿点衣服。
这里写图片描述


原理解析

首先我们先来看下Apk加壳的步骤:
这里写图片描述

  • 源Apk:需要加壳的Apk
  • 加密的Apk:源Apk经过加密算法加密后的Apk
  • 加壳程序Apk:是有解密源Apk和动态加载启动源Apk的外壳

首先我们拿到需要加壳的源Apk,通过加密算法加密源Apk然后与加壳Apk的dex文件组合成新的Dex文件,然后将加壳程序Apk的Dex文件替换成新的Dex,生成新的Apk重新签名。
我们先来看下Dex文件的结构:
这里写图片描述

  • Magic
    Magic数是为了方便虚拟机识别目标文件是否是合格的Dex文件,在Dex文件中magic的值固定值
  • checksum
    文件校验码 ,使用alder32 算法校验文件除去 maigc ,checksum 外余下的所有文件区域 ,用于检查文件错误
  • signature
    使用 SHA-1 算法 hash 除去 magic ,checksum 和 signature 外余下的所有文件区域 ,用于唯一识别本文件 。
  • file_size
    当前Dex 文件的大小 。

所以我们在将Dex与加密算法加密后的Apk合并生成新的Dex后需要修改新Dex文件的这三个值,为了方便从新Dex中获得加密的Apk,我们需要知道加密的Apk的大小,为了方便以后获得,我们将其大小放置在新Dex的后四位,新生成的Dex文件结构:
这里写图片描述
生成新Dex后,将加壳程序Apk的Dex文件替换,重新签名后加壳的Apk即完成了。如果觉得步骤理清了,我们来看下其具体的实现。


具体实现:

这过程一共要创建三个项目。
首先我们先创建一个需要加密的Apk项目,结构非常简单只有几个Log的打印,结构
这里写图片描述
SourceApplication.java

package com.jju.yuxin.sourceproject;
import android.app.Application;
import android.util.Log;

public class SourceApplication extends Application {
   
    private static final String TAG=SourceApplication.class.getSimpleName();
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG,"-------------onCreate");
    }
}

MainActivity.java

package com.jju.yuxin.sourceproject;

import android.app.Activity;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {
   
    private static final String TAG=MainActivity.class.getSimpleName();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv_content = new TextView(this);
        tv_content.setText("I am Source Apk");
        tv_content.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View arg0) {
                Intent intent = new Intent(MainActivity.this, SubActivity.class);
                startActivity(intent);
            }});
        setContentView(tv_content);
        Log.i(TAG, "onCreate:app:"+getApplicationContext());
    }
}

SubActivity.java

package com.jju.yuxin.sourceproject;
import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class SubActivity extends Activity {
   
    private static final String TAG=SubActivity.class.getSimpleName();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv_content = new TextView(this);
        tv_content.setText("I am SubActivity");
        setContentView(tv_content);
        Log.i(TAG, "SubActivity:app:"+getApplicationContext());
    }
}

然后将其打包生成Apk。
第二个项目是一个JAVA项目用于将源Apk加密,并合并加壳程序Dex与加密后的源Apk。在贴出这个代码时我们先不用管加壳程序Dex从何而来,假设已经有了这样一个文件。我们来看下具体实现:

package com.forceapk;

import java.io.ByteArrayOutputStream;  
import java.io.File;  
import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.io.IOException;  
import java.security.MessageDigest;  
import java.security.NoSuchAlgorithmException;  
import java.util.zip.Adler32;  


public class mymain {
    
    public static void main(String[] args) {  
        try {  
            //需要加壳的源APK  ,以二进制形式读出,并进行加密处理
            File srcApkFile = new File("force/SourceAPK.apk");   
            System.out.println("apk size:"+srcApkFile.length()); 
            byte[] enSrcApkArray = encrpt(readFileBytes(srcApkFile));

            //需要解壳的dex  以二进制形式读出dex  
            File unShellDexFile = new File("force/shelldex.dex");    
            byte[] unShellDexArray = readFileBytes(unShellDexFile);

            //将源APK长度和需要解壳的DEX长度相加并加上存放源APK大小的四位得到总长度
            int enSrcApkLen = enSrcApkArray.length;  
            int unShellDexLen = unShellDexArray.length;  
            int totalLen = enSrcApkLen + unShellDexLen +4;

            //依次将解壳DEX,加密后的源APK,加密后的源APK大小,拼接出新的Dex
            byte[] newdex = new byte[totalLen];  
            System.arraycopy(unShellDexArray, 0, newdex, 0, unShellDexLen);
            System.arraycopy(enSrcApkArray, 0, newdex, unShellDexLen, enSrcApkLen);
            System.arraycopy(intToByte(enSrcApkLen), 0, newdex, totalLen-4, 4);


            //修改DEX file size文件头  
            fixFileSizeHeader(newdex);  
            //修改DEX SHA1 文件头  
            fixSHA1Header(newdex);  
            //修改DEX CheckSum文件头  
            fixCheckSumHeader(newdex);  

            //写出
            String str = "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
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值