jukan

http://blog.csdn.net/u010921385/article/details/52505094

1、需要加密的Apk(源Apk)

2、壳程序Apk(负责解密Apk工作)

3、加密工具(将源Apk进行加密和壳Dex合并成新的Dex)

主要步骤:

我们拿到需要加密的Apk和自己的壳程序Apk,然后用加密算法对源Apk进行加密在将壳Apk进行合并得到新的Dex文件,最后替换壳程序中的dex文件即可,得到新的Apk,那么这个新的Apk我们也叫作脱壳程序Apk.他已经不是一个完整意义上的Apk程序了,他的主要工作是:负责解密源Apk.然后加载Apk,让其正常运行起来。

在这个过程中我们可能需要了解的一个知识是:  如何将源Apk和壳Apk进行合并成新的Dex

这里就需要了解Dex文件的格式了。下面就来简单介绍一下Dex文件的格式

具体Dex文件格式的详细介绍可以查看这个文件:  http://download.csdn.net/detail/jiangwei0910410003/9102599

主要来看一下Dex文件的头部信息,其实Dex文件和Class文件的格式分析原理都是一样的,他们都是有固定的格式,我们知道现在反编译的一些工具:

1、jd-gui:可以查看jar中的类,其实他就是解析class文件,只要了解class文件的格式就可以

2、dex2jar:将dex文件转化成jar,原理也是一样的,只要知道Dex文件的格式,能够解析出dex文件中的类信息就可以了

当然我们在分析这个文件的时候,最重要的还是头部信息,应该他是一个文件的开始部分,也是索引部分,内部信息很重要。

我们今天只要关注上面红色标记的三个部分:

1) checksum 

文件校验码 ,使用alder32 算法校验文件除去 maigc ,checksum 外余下的所有文件区域 ,用于检查文件错误 。

2) signature 

使用 SHA-1 算法 hash 除去 magic ,checksum 和 signature 外余下的所有文件区域 ,用于唯一识别本文件 。

3) file_size

Dex 文件的大小 。

为什么说我们只需要关注这三个字段呢?

因为我们需要将一个文件(加密之后的源Apk)写入到Dex中,那么我们肯定需要修改文件校验码(checksum).因为他是检查文件是否有错误。那么signature也是一样,也是唯一识别文件的算法。还有就是需要修改dex文件的大小。

不过这里还需要一个操作,就是标注一下我们加密的Apk的大小,因为我们在脱壳的时候,需要知道Apk的大小,才能正确的得到Apk。那么这个值放到哪呢?这个值直接放到文件的末尾就可以了。

所以总结一下我们需要做:修改Dex的三个文件头,将源Apk的大小追加到壳dex的末尾就可以了。

我们修改之后得到新的Dex文件样式如下:

那么我们知道原理了,下面就是代码实现了。所以这里有三个工程:

1、源程序项目(需要加密的Apk)

2、脱壳项目(解密源Apk和加载Apk)

3、对源Apk进行加密和脱壳项目的Dex的合并

三、项目案例 

下面先来看一下源程序

1、需要加密的源程序Apk项目:ForceApkObj 

需要一个Application类,这个到后面说为什么需要:

MyApplication.java

package com.example.forceapkobj;

import android.app.Application;
import android.util.Log;

public class MyApplication extends Application{
  
  @Override
  public void onCreate() {
    super.onCreate();
    Log.i("demo", "source apk onCreate:"+this);
  }

}

就是打印一下onCreate方法。

MainActivity.java

package com.example.forceapkobj;

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

public class MainActivity extends Activity {

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

}

也是打印一下内容。

2、加壳程序项目:DexShellTools 

加壳程序其实就是一个Java工程,因为我们从上面的分析可以看到,他的工作就是加密源Apk,然后将其写入到脱壳Dex文件中,修改文件头,得到一个新的Dex文件即可。

看一下代码:

package com.example.reforceapk;
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 {
  /**
   * @param args
   */
  public static void main(String[] args) {
    // TODO Auto-generated method stub
    try {
      File payloadSrcFile = new File("force/ForceApkObj.apk");   //需要加壳的程序
      System.out.println("apk size:"+payloadSrcFile.length());
      File unShellDexFile = new File("force/ForceApkObj.dex");	//解客dex
      byte[] payloadArray = encrpt(readFileBytes(payloadSrcFile));//以二进制形式读出apk,并进行加密处理//对源Apk进行加密操作
      byte[] unShellDexArray = readFileBytes(unShellDexFile);//以二进制形式读出dex
      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);
      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 ^ 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));

分类:

技术点:

相关文章: