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 MainActivity.java
package com.jju.yuxin.sourceproject;
import android.app.Application;
import android.util.Log;
public class SourceApplication extends Application {
private static final String TAG=SimpleName();
@Override
public void onCreate () {
super .onCreate();
Log.d(TAG,"-------------onCreate");
}
}
package com.jju.yuxin.sourceproject;
import android.app.Activity;
t.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=SimpleName();
@Override
protected void onCreate(Bundle 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=SimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
TextView tv_content = new TextView(this);
java程序打包成apktv_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 (!ists()) {
}
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到⽂件末尾计算校验码
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论