Android APK 反編譯與安全

APK 反編譯

一、APK反編譯基本原理

1.APK分析
879D8980-CAB1-4499-B3B0-3AC8DD9C4DB2.png
  • assets文件夾:原始資源文件夾,對(duì)應(yīng)著Android工程的assets文件夾,一般用于存放原始的圖片、txt、css等資源文件。
  • lib:存放應(yīng)用需要的引用第三方SDK的so庫(kù)。比如一些底層實(shí)現(xiàn)的圖片處理、音視頻處理、數(shù)據(jù)加密的庫(kù)等。而該文件夾下有時(shí)會(huì)多一個(gè)層級(jí),這是根據(jù)不同CPU 型號(hào)而劃分的,如 ARM,ARM-v7a,x86等。
  • META-INF:保存apk簽名信息,保證apk的完整性和安全性。
  • res:資源文件夾,其中的資源文件包括了布局(layout),常量值(values),顏色值(colors),尺寸值(dimens),字符串(strings),自定義樣式(styles)等。
  • AndroidManifest.xml文件:全局配置文件,里面包含了版本信息、activity、broadcasts等基本配置。不過這里的是二進(jìn)制的xml文件,無法直接查看,需要反編譯后才能查看。
  • classes.dex文件:這是安卓代碼的核心部分,,dex是在Dalvik虛擬機(jī)上可以執(zhí)行的文件。這里有classes.dex和classes2.dex兩個(gè)文件,說明工程的方法數(shù)較多,進(jìn)行了dex拆分。
  • resources.arsc文件:記錄資源文件和資源id的映射關(guān)系。
2.APK打包
879D8980-CAB1-4499-B3B0-3AC8DD9C4DB2.png
  1. 首先,.aidl(Android Interface Description Language)文件需要通過 aidl 工具轉(zhuǎn)換成編譯器能夠處理的 Java 接口文件。
  2. 同時(shí),資源文件(包括 AndroidManifest.xml、布局文件、各種 xml 資源等等)將被 AAPT(Asset Packaging Tool)(Android Gradle Plugin 3.0.0 及之后使用 AAPT2 替代了 AAPT)處理為最終的 resources.arsc,并生成 R.java 文件以保證源碼編寫時(shí)可以方便地訪問到這些資源。
  3. 然后,通過 Java Compiler 編譯 R.java、Java 接口文件、Java 源文件,最終它們會(huì)統(tǒng)一被編譯成 .class 文件。
  4. 因?yàn)?.class 并不是 Android 系統(tǒng)所能識(shí)別的格式,所以還需要通過 dex 工具將它們轉(zhuǎn)化為相應(yīng)的 Dalvik 字節(jié)碼(包含壓縮常量池以及清除冗余信息等工作)。這個(gè)過程中還會(huì)加入應(yīng)用所依賴的所有 “第三方庫(kù)”。
  5. 下一步,通過 ApkBuilder 工具將資源文件、DEX 文件打包生成 APK 文件。
  6. 接著,系統(tǒng)將上面生成的 DEX、資源包以及其它資源通過 apkbuilder 生成初始的 APK 文件包。
  7. 然后,通過簽名工具 Jarsigner 或者其它簽名工具對(duì) APK 進(jìn)行簽名得到簽名后的 APK。如果是在 Debug 模式下,簽名所用的 keystore 是系統(tǒng)自帶的默認(rèn)值,否則我們需要提供自己的私鑰以完成簽名過程。
  8. 最后,如果是正式版的 APK,還會(huì)利用 ZipAlign 工具進(jìn)行對(duì)齊處理,以提高程序的加載和運(yùn)行速度。而對(duì)齊的過程就是將 APK 文件中所有的資源文件距離文件的起始位置都偏移4字節(jié)的整數(shù)倍,這樣通過 mmap 訪問 APK 文件的速度會(huì)更快,并且會(huì)減少其在設(shè)備上運(yùn)行時(shí)的內(nèi)存占用。
3.APK反編譯

1.使用dex2jar將dex文件轉(zhuǎn)換成Jar包
2.使用jd-gui將Jar包文件反編譯成java源文件
3.使用apktool工具查看apk里的二進(jìn)制文件

安全措施

proguard

這里不再過多介紹

簽名

簽名過程可以細(xì)分為 三步,如下所示:

1、計(jì)算摘要:通過 Hash 算法提取出原始數(shù)據(jù)的摘要。(SHA1)
2、計(jì)算簽名:再通過基于密鑰(私鑰)的非對(duì)稱加密算法對(duì)提取出的摘要進(jìn)行加密,加密后的數(shù)據(jù)就是簽名信息。(RSA)
3、寫入簽名:將簽名信息寫入原始數(shù)據(jù)的簽名區(qū)塊內(nèi)。

校驗(yàn)簽名

校驗(yàn)過程:
校驗(yàn)過程同樣也可以分為 三步,如下:

1、提取摘要:首先用同樣的 Hash 算法從接收到的數(shù)據(jù)中提取出摘要。
2、解密簽名:使用發(fā)送方的公鑰對(duì)數(shù)字簽名進(jìn)行解密,解密出原始摘要。
3、比較摘要:如果解密后的數(shù)據(jù)和提取的摘要一致,則校驗(yàn)通過;如果數(shù)據(jù)被第三方篡改過,解密后的數(shù)據(jù)和摘要將會(huì)不一致,則校驗(yàn)不通過。

那么,我們?cè)撊绾伪WC公鑰的可靠性呢?答案是 數(shù)字證書。

數(shù)字證書
需要注意的是,Apk 的證書通常是自簽名的,也就是由開發(fā)者自己制作,沒有向 CA 機(jī)構(gòu)申請(qǐng)。Android 在安裝 Apk 時(shí)并沒有校驗(yàn)證書本身的合法性,只是從證書中提取公鑰和加密算法,這也正是對(duì)第三方 Apk 重新簽名后,還能夠繼續(xù)在沒有安裝這個(gè) Apk 的系統(tǒng)中繼續(xù)安裝的原因。

keystore 和證書格式
keystore 文件中包含了 私鑰、公鑰和數(shù)字證書。根據(jù)編碼不同,keystore 文件分為很多種,Android 使用的是 Java 標(biāo)準(zhǔn) keystore 格式 JKS(Java Key Storage),所以通過 Android Studio 導(dǎo)出的 keystore 文件是以 .jks 結(jié)尾的。
keystore 使用的 證書標(biāo)準(zhǔn)是 X.509,X.509 標(biāo)準(zhǔn)也有多種 編碼格式,常用的有兩種:pem(Privacy Enhanced Mail)和 der(Distinguished Encoding Rules)。jks 使用的是 der 格式,但是,Android 也支持直接使用 pem 格式的證書進(jìn)行簽名。

下面,我們了解下兩種證書編碼格式的區(qū)別,如下所示:
DER(Distinguished Encoding Rules):二進(jìn)制格式,所有類型的證書和私鑰都可以存儲(chǔ)為 der 格式。
PEM(Privacy Enhanced Mail):base64 編碼,內(nèi)容以-----BEGIN xxx----- 開頭,以-----END xxx----- 結(jié)尾。

/**
獲取簽名證書的SHA1值
**/
public static String getSign(Context ctx) {
       try {
           PackageInfo packageInfo = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(),
                   PackageManager.GET_SIGNATURES);
           Signature[] signs = packageInfo.signatures;
           Signature sign = signs[0];
           MessageDigest md1 = MessageDigest.getInstance("MD5");
           md1.update(sign.toByteArray());
           byte[] digest = md1.digest();
           String res = toHexString(digest);
           MessageDigest md2 = MessageDigest.getInstance("SHA1");
           md2.update(sign.toByteArray());
           byte[] digest2 = md2.digest();
           String res2 = toHexString(digest2);
           return res2;
       } catch (Exception e) {
           e.printStackTrace();
           return "";
       }
   }

/**
通過檢查簽名文件classes.dex文件的哈希值來判斷代碼文件是否被篡改
@param orginalSHA 原始Apk包的SHA-1值
**/
public static void apkVerifyWithSHA(Context context, String baseSHA) {  
      String apkPath = context.getPackageCodePath(); // 獲取Apk包存儲(chǔ)路徑  
      try {  
          MessageDigest dexDigest = MessageDigest.getInstance("SHA-1");  
           byte[] bytes = new byte[1024];  
           int byteCount;  
           FileInputStream fis = new FileInputStream(new File(apkPath)); // 讀取apk文件  
           while ((byteCount = fis.read(bytes)) != -1) {  
               dexDigest.update(bytes, 0, byteCount);  
           }  
           BigInteger bigInteger = new BigInteger(1, dexDigest.digest()); // 計(jì)算apk文件的哈希值  
           String sha = bigInteger.toString(16);  
           fis.close();  
           if (!sha.equals(baseSHA)) { // 將得到的哈希值與原始的哈希值進(jìn)行比較校驗(yàn)  
               Process.killProcess(Process.myPid()); // 驗(yàn)證失敗則退出程序  
           }  
       } catch (NoSuchAlgorithmException e) {  
           e.printStackTrace();  
       } catch (FileNotFoundException e) {  
           e.printStackTrace();  
       } catch (IOException e) {  
           e.printStackTrace();  
       }  
   }  

思考
這種純粹的字符比較都很容易破解掉,直接在 smali 中全局搜索干掉或修改你的簽名驗(yàn)證邏輯就行了,實(shí)際上用處不大。
進(jìn)階:簽名驗(yàn)證放到 native 層用 NDK 開發(fā)

jarsigner 和 apksigner 的區(qū)別

Android 提供了 兩種對(duì) Apk 的簽名方式,一種是基于 JAR 的簽名方式,另一種是基于 Apk 的簽名方式,它們的 主要區(qū)別在于使用的簽名文件不一樣:jarsigner 使用 keystore 文件進(jìn)行簽名;而 apksigner 除了支持使用 keystore 文件進(jìn)行簽名外,還支持直接指定 pem 證書文件和私鑰進(jìn)行簽名。

在我們簽名時(shí),除了要指定 keystore 文件和密碼外,也要指定 alias 和 key 的密碼,這是為什么呢?
keystore 是一個(gè)密鑰庫(kù),也就是說它可以存儲(chǔ)多對(duì)密鑰和證書,keystore 的密碼是用于保護(hù) keystore 本身的,每一對(duì)密鑰和證書是通過 alias 來區(qū)分的。所以 jarsigner 是支持使用多個(gè)證書對(duì) Apk 進(jìn)行簽名的,apksigner 也同樣支持。

Android Apk V1 驗(yàn)證簽名的原理
Android Apk V1 驗(yàn)證簽名的過程主要可以分為如下 四步:

1、解析出 CERT.RSA 文件中的證書、公鑰,解密 CERT.RSA 中的加密數(shù)據(jù)。
2、解密結(jié)果和 CERT.SF 的指紋進(jìn)行對(duì)比,保證 CERT.SF 沒有被篡改。
3、接著,將 CERT.SF 中的內(nèi)容再和 MANIFEST.MF 中的指紋對(duì)比,保證 MANIFEST.MF 文件沒有被篡改。
4、MANIFEST.MF 中的內(nèi)容和 APK 所有文件指紋逐一對(duì)比,保證 APK 沒有被篡改。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容