【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); 
		}
		
	
		
		
	}
}



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