<dd id="sga4y"><optgroup id="sga4y"></optgroup></dd>
<nav id="sga4y"><code id="sga4y"></code></nav>
  • <optgroup id="sga4y"></optgroup>

    原文地址:http://drops.wooyun.org/mobile/4296

    0x01 Android簽名機制


    將APK重命名為zip文件,然后可以看到有個META-INF的文件夾,里面有三個文件,分別名為MANIFEST.MF、CERT.SF和CERT.RSA,這些就是使用signapk.jar生成的簽名文件。

    1、 MANIFEST.MF文件:

    程序遍歷update.apk包中的所有文件(entry),對非文件夾非簽名文件的文件,逐個生成SHA1的數字簽名信息,再用Base64進行編碼。具體代碼見這個方法:

    #!java
    private static Manifest addDigestsToManifest(JarFile jar)
    

    關鍵代碼是

    #!java
    for (JarEntry entry: byName.values()) {
         String name = entry.getName();
         if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) &&
             !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) &&
             (stripPattern == null ||!stripPattern.matcher(name).matches())){
             InputStream data = jar.getInputStream(entry);
             while ((num = data.read(buffer)) > 0) {
             md.update(buffer, 0, num);
           }
           Attributes attr = null;
           if (input != null) attr = input.getAttributes(name);
           attr = attr != null ? new Attributes(attr) : new Attributes();
           attr.putValue("SHA1-Digest", base64.encode(md.digest()));
           output.getEntries().put(name, attr);
        }
    }
    

    之后將生成的簽名寫入MANIFEST.MF文件。關鍵代碼如下:

    #!java
    Manifest manifest = addDigestsToManifest(inputJar);
    je = new JarEntry(JarFile.MANIFEST_NAME);
    je.setTime(timestamp);
    outputJar.putNextEntry(je);
    manifest.write(outputJar);
    

    2、 生成CERT.SF文件:

    對前一步生成的Manifest,使用SHA1-RSA算法,用私鑰進行簽名。關鍵代碼如下:

    #!java
    Signature signature = Signature.getInstance("SHA1withRSA");
    signature.initSign(privateKey);
    je = new JarEntry(CERT_SF_NAME);
    je.setTime(timestamp);
    outputJar.putNextEntry(je);
    writeSignatureFile(manifest,
    new SignatureOutputStream(outputJar, signature));
    

    3、 生成CERT.RSA文件:

    生成MANIFEST.MF沒有使用密鑰信息,生成CERT.SF文件使用了私鑰文件。那么我們可以很容易猜測到,CERT.RSA文件的生成肯定和公鑰相關。 CERT.RSA文件中保存了公鑰、所采用的加密算法等信息。核心代碼如下:

    #!java
    je = new JarEntry(CERT_RSA_NAME);
    je.setTime(timestamp);
    outputJar.putNextEntry(je);
    writeSignatureBlock(signature, publicKey, outputJar);
    

    在程序中獲取APK的簽名時,通過signature方法進行獲取,如下:

    #!java
    packageInfo = manager.getPackageInfo(pkgname,PackageManager.GET_SIGNATURES);
    signatures = packageInfo.signatures;
    for (Signature signature : signatures) {
        builder.append(signature.toCharsString());
    }
    signature = builder.toString();
    

    所以一般的程序就是在代碼中通過判斷signature的值,來判斷APK是否被重新打包過。

    0x02 簽名繞過方式


    在講簽名繞過的方式前,需要先明確DEX校驗和簽名校驗:

    1.將apk以壓縮包的形式打開刪除原簽名后,再簽名,安裝能夠正常打開,但是用IDE(即apk改之理,會自動反編譯dex)工具二次打包,卻出現非正常情況的,如:閃退/彈出非正版提示框。可以確定是dex文件的校驗

    2、將apk以壓縮包的形式打開刪除原簽名再簽名,安裝之后打開異常的,則基本可以斷定是簽名檢驗。如果在斷網的情況下同樣是會出現異常,則是本地的簽名檢驗;如果首先出現的是提示網絡沒有連接,則是服務器端的簽名校驗.

    2.1.Java層校驗

    獲取簽名信息和驗證的方法都寫在android的java層。實例如下:

    1、使用APKIDE反編譯APK,不做任何操作,然后直接回編譯,安裝后運行,提示如下:

    enter image description here

    2、在APKIDE中搜索signatures(或者搜索錯誤提示),定位到簽名驗證的代碼處。

    enter image description here

    3、此處就是獲取簽名的,然后找程序判斷簽名的地方,進行修改,如下圖,if-nez是進行判斷的地方,將ne修改為eq。即if-eqz v2, :cond_0。則程序就可以繞過本地的簽名交易。

    enter image description here

    enter image description here

    2.2.NDK校驗

    將關鍵代碼放到so中,在底層獲取簽名信息并驗證。因為獲取和驗證的方法都封閉在更安全的so庫里面,能夠起到一定意義上的保護作用。實例如下:

    1、使用APKIDE反編譯APK,不做任何操作,然后直接回編譯,安裝后運行,程序直接退出,無任何提示。

    2、在APKIDE中搜索signatures(或者搜索錯誤提示),定位到簽名驗證的代碼處。

    enter image description here

    3、使?用JD-GUI打開AppActivity,可以看到,此處是獲取包名,然后進?行MD5計算。

    enter image description here

    4.在程序中搜索getSignature,發現并沒有調?用此函數的地?方,猜測在so?文件中,搜索loadLibrary。

    enter image description here

    5.在代碼中可以查找,可以找到調?用的是libcocos2dcpp.so

    6.使?用IDA打開libcocos2dcpp.so,然后搜索getSiganture,找到調?用此函數的地方。

    enter image description here

    從代碼中可以看到,此函數調?用了org.cocos2dx.cpp.AppActivity.getSignature

    enter image description here

    7、查看F5代碼,發現此函數是判斷簽名的函數,然后我們雙擊此函數的調?者,部分代碼如下。

    enter image description here

    8、從上圖可以看出,只需要修改BEQ loc_11F754,讓其不跳轉到jjni——>error,就可以繞過簽名校驗。 查看HEX,使?010editor跳到0011F73E,修改D0為D1。成功繞過簽名校驗。

    enter image description here

    2.3.服務器驗證

    在android的java層獲取簽名信息,上傳服務器在服務端進行簽名然后返回驗證結果。

    如下圖,網絡驗證時,如果網絡沒連接,一般都會提示錯誤。

    enter image description here

    既然是網絡驗證,肯定要把驗證信息發送到服務端,然后進行驗證,先看個簡單的實例,下次會有個難度大的。

    1、手機配置好抓包,然后抓包。第一種圖是正常的APK的時候的數據包,第二個圖是反編譯的APK的數據包,通過對比,發現cookie中的public_key不一樣,那么我們替換一下,發現可以正常使用APK的功能了。

    enter image description here

    enter image description here

    2、將正確的public_key添加到APK中。打開反編譯的代碼,搜索signatures,定位到簽名的代碼。

    enter image description here

    可以看到,代碼將signatures的值傳遞到V4中,然后傳遞到Utils->mPublicKey函數中,于是我們將正確的public_key傳給V4。

    enter image description here

    然后重新打包,重新安裝就可以了。

    0x03.總結


    java層的校驗很容易就可以破解掉,在so層實現校驗相對來說分析會更難點,而網絡驗證,如果僅僅是字符串的比較,那么也很容易破解掉。

    碼子碼的太累了。。

    后面還有幾篇正在寫的文章,包括so分析等等。

    摘抄: http://www.blogjava.net/zh-weir/archive/2011/07/19/354663.html

    97av