【android安全】之防止apk被篡改后重编译。

现状:很多apk会被黑客反编译成smali文件,然后修改或植入恶意代码后重新编译成apk发布到市场。

解决要点:

1,代码混淆。可使用高级商用混淆工具DexGuard。(此法容易被攻破)

2,apk运行时进行签名验证和crc校验码验证。(此法的验证代码容易被黑客注销掉,使之不起作用)

个人总结的比较安全的解决方式:(这里不考虑网络通信被中间人拦截情况,后面会给出防止网络拦截的ssl验证方法)

1)服务器保存发布时apk的加密的签名和加密的crc校验码。

2)客户端的每次网络访问请求都需上传与服务器相同加密方式加密的apk签名和crc码。(这里使用so库获取加密的签名和crc码,比较安全。甚至可以考虑对so库加壳保护)

这样一来黑客不能注销掉验证,因为服务必须接收到正确的签名和crc码才允许继续通信。黑客也不能打印我们加密后的crc码,因为加入打印代码会使crc发生改变,从而得到错误的crc码。

漏洞:黑客获得apk原始crc码,并且攻破加壳的so库。(这难度非常大!!)

备注:crc指classes.dex的校验码。

部分实例代码:

package com.test;

import java.util.HashMap;
import java.util.Map;

import android.app.Activity;
import android.os.Bundle;

public class TestActivity extends Activity {

	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		
		LoginServelet sv=new LoginServelet();
		Map<String,Object> params=new HashMap<String,Object>();
		params.put("userName", "test");
		params.put("password", "123");
		params.put("appSignCiphered", ProguardNavtive.getCipheredAppSign());
		params.put("crcCiphered",ProguardNavtive.getCipheredAppCrc());
		sv.get(params);
	}
}


package com.test;

import java.util.Map;

public class LoginServelet {
	
	public void get(Map<String,Object> params){
		
		String userName=(String) params.get("userName");
		String password=(String) params.get("password");
		String appSignCiphered=(String) params.get("appSignCiphered");
		String crcCiphered=(String) params.get("crcCiphered");
		/**
		 * upload to server check.
		 */
		
		
		
	}

}


package com.test;

/**
 * 可以考虑对so库加壳,增加破解难度。
 * 
 * @author lchli
 * 
 */
public class ProguardNavtive {

	public static native String getCipheredAppSign();

	public static native String getCipheredAppCrc();
}


package com.test;

import java.io.IOException;
import java.security.MessageDigest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;

public class AppUtil {
	
	public static String getAppSign(Context context){
		PackageManager pm = context.getPackageManager();
		try {
			PackageInfo info = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
			Signature sig = info.signatures[0]; 
			return calcSHA1(sig.toByteArray());
			
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e); 
		}
		
		
		
	}
	
	private static String calcSHA1(byte[] sig) throws Exception{
		MessageDigest digest = MessageDigest.getInstance("SHA1");
		digest.update(sig);
		byte[] sigHash = digest.digest(); 
		return bytesToHex(sigHash);
		
	}
	
	
	private static String bytesToHex(byte[] bytes){
		final char[] hexArray="0123456789ABCDEF".toCharArray();
		char[] hexChars = new char[bytes.length * 2];
	    for ( int j = 0; j < bytes.length; j++ ) {
	        int v = bytes[j] & 0xFF;
	        hexChars[j * 2] = hexArray[v >>> 4];
	        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
	    }
	    return new String(hexChars);
	}
	
	public static long getAppCrc(Context context){ 
		try {
			ZipFile zip = new ZipFile(context.getPackageCodePath());
			ZipEntry entry = zip.getEntry("classes.dex");
			return entry.getCrc();
			
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			throw new RuntimeException(e); 
		}
		
	
		
		
	}
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章