美團Android資源混淆保護實踐

來源:互聯網
上載者:User

標籤:

前言

Android應用中的APK安全性一直遭人詬病,市面上充斥著各種被破解或者漢化的應用,破解者可以非常簡單的通過破解工具就能對一個APK進行反編譯、破解、漢化等等,這樣就可以修改原有代碼的邏輯、添加新代碼、添加或修改資源、或者更有甚者植入病毒等等,從而破壞原有APK的安全和使用者體驗,最終傷害到使用者和原有的開發人員。
而事物都是有兩方面的,有矛就有盾,針對Android應用安全的各種方案應運而生,大家比較熟悉一般是各類加殼加固的工具,我們可以使用這些工具來保護我們的APK,加殼加固是另外一個話題了,我們這裡不對加殼加固進行介紹,後續如果有機會會單獨開一個話題討論,我們在開發過程中可以通過ProGuard或者DexGuard來保護我們的代碼,從而實現相對的代碼安全,但我們的資源呢?我們往往忽略對資源檔的保護,那這裡將要分享的是如果採用常規方式對APK中的資源檔進行保護。

資源安全

資源安全這個話題目前大家關注度不算太高,相比較而言大家更關注代碼安全,目前市面上各類APP基本都使用了ProGuard來保護代碼的安全,但對資源檔的保護力度都不大,其實資源檔是存在比較大的安全隱患,那資源會有哪些安全隱患呢?下面我們通過一個比較簡單的例子來說明下保護資源檔的重要性。

我們先用最常見的apktool工具來反編譯一個應用來看看,通過運行下面命令就能進行反編譯;

    apktool d -s xxx.apk

反編譯成功後我們來看下反編譯得到的檔案結構(見);

通過中的目錄結構,我們可以看到這個應用的資源檔大概有:anim、drawable、layout、menu、values等等,我們可以通過修改這些檔案夾下的資源檔,並通過apktool進行回編譯(apktool b 命令)就能建立一個經過修改過的APK應用,例如我們修改中紅色橫線所標示的layout檔案,就能往原有APK的支付資訊(根據資源名稱猜測這個layout的意圖)中添加一些我們自己的東西;

這個問題主要是因為我們在開發過程中倡導命名的規範性,一般都要求在命名時做到見名知意,這樣能夠方便我們自己的理解和維護,但同時這也方便了破解者,破解者可以輕鬆的根據檔案名稱來猜測這個檔案的意圖和作用,從而做破壞性的修改。

通過這個例子我們可以看出目前資源安全的重要性,那如何做到資源安全呢?安全都是相對的,沒有絕對的安全,我們接下來要討論的是類似Proguard方式的對我們的資源進行保護。我們主要是通過修改AAPT工具來對資源進行保護,為了方便理解,下面先講一下Android應用是怎麼尋找資源的。

Android尋找資源的流程

在Android系統中,每一個應用程式一般都會配置很多資源,用來適配不同密度、大小和方向的螢幕,以及適配不同的國家、地區和語言等等。這些資源是在應用程式運行時自動根據裝置的當前配置資訊進行適配的。這也就是說,給定一個相同的資源ID,在不同的裝置配置之下,尋找到的可能是不同的資源。
這個尋找過程對應用程式來說,是完全透明的,這個過程主要是靠Android資源管理架構來完成的,而Android資源管理架構實際是由AssetManager和Resources兩個類來實現的。其中,Resources類可以根據ID來尋找資源,而AssetManager類根據檔案名稱來尋找資源。事實上,如果一個資源ID對應的是一個檔案,那麼Resources類是先根據ID來找到資源檔名稱,然後再將該檔案名稱交給AssetManager類來開啟對應的檔案的。基本流程如:

通過我們可以看到Resources是通過resources.arsc把Resource的ID轉化成資源檔的名稱,然後交由AssetManager來載入的。
而Resources.arsc這個檔案是存放在APK包中的,他是由AAPT工具在打包過程中產生的,他本身是一個資源的索引表,裡面維護者資源ID、Name、Path或者Value的對應關係,AssetManager通過這個索引表,就可以通過資源的ID找到這個資源對應的檔案或者資料。

AAPT介紹

AAPT是Android Asset Packaging Tool的縮寫,它存放在SDK的tools/目錄下,AAPT的功能很強大,可以通過它查看查看、建立、更新壓縮檔(如 .zip檔案,.jar檔案, .apk檔案), 它也可以把資源編譯為二進位檔案,並產生resources.arsc, AAPT這個工具在APK打包過程中起到了非常重要作用,在打包過程中使用AAPT對APK中用到的資源進行打包,這裡不對AAPT這個工具做過多的討論,只看一下AAPT這個工具在打包過程中起到的作用,是AAPT打包的流程:

AAPT這個工具在打包過程中主要做了下列工作:

  1. 把"assets"和"res/raw"目錄下的所有資源進行打包(會根據不同的檔案尾碼選擇壓縮或不壓縮),而"res/"目錄下的其他資源進行編譯或者其他處理(具體處理方式視檔案尾碼不同而不同,例如:".xml"會編譯成二進位檔案,".png"檔案會進行最佳化等等)後才進行打包;
  2. 會對除了assets資源之外所有的資源賦予一個資源ID常量,並且會產生一個資源索引表resources.arsc;
  3. 編譯AndroidManifest.xml成二進位的XML檔案;
  4. 把上面3個步驟中產生結果儲存在一個*.ap_檔案,並把各個資源ID常量定義在一個R.java中;

.ap_這個檔案會在產生APK時放入APK包中, .ap_ 這個檔案本身是一個ZIP包,他裡麵包含resources.arsc、AndroidManifest.xml、assets以及所有的資源檔,是UNZIP後的:

可以看出*.ap_這個檔案中包含的內容,這個檔案存放在build/intermediates/res的目錄下,是這個檔案存放的路徑:

資源保護

我們這裡參考Proguard Obfuscator方式,對APK中資源檔名使用簡短無意義名稱進行替換,給破解者製造困難,從而做到資源的相對安全;通過上面分析,我們可以看出通過修改AAPT在產生resources.arsc和*.ap_時把資源檔的名稱進行替換,從而保護資源。
通過閱讀AAPT編譯資源的代碼,我們發現修改AAPT在處理資源檔相關的源碼是能夠做到資源檔名的替換,下面是Resource.cpp中makeFileResources()的修改的程式碼片段:

    static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets,                                      ResourceTable* table,                                      const sp<ResourceTypeSet>& set,                                      const char* resType)    {        String8 type8(resType);        String16 type16(resType);        bool hasErrors = false;        ResourceDirIterator it(set, String8(resType));        ssize_t res;        while ((res=it.next()) == NO_ERROR) {            if (bundle->getVerbose()) {                printf("    (new resource id %s from %s)\n",                       it.getBaseName().string(), it.getFile()->getPrintableSource().string());            }            String16 baseName(it.getBaseName());            const char16_t* str = baseName.string();            const char16_t* const end = str + baseName.size();            while (str < end) {                if (!((*str >= ‘a‘ && *str <= ‘z‘)                        || (*str >= ‘0‘ && *str <= ‘9‘)                        || *str == ‘_‘ || *str == ‘.‘)) {                    fprintf(stderr, "%s: Invalid file name: must contain only [a-z0-9_.]\n",                            it.getPath().string());                    hasErrors = true;                }                str++;            }            String8 resPath = it.getPath();            resPath.convertToResPath();            String8 obfuscationName;            String8 obfuscationPath = getObfuscationName(resPath, obfuscationName);            table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()),                            type16,                            baseName, // String16(obfuscationName),                            String16(obfuscationPath), // resPath                            NULL,                            &it.getParams());            assets->addResource(it.getLeafName(), obfuscationPath/*resPath*/, it.getFile(), type8);        }        return hasErrors ? UNKNOWN_ERROR : NO_ERROR;    }

上述代碼是在ResourceTable和Assets中添加資源檔時, 對資源檔名稱進行修改,這就能夠做到資源檔名稱的替換,這樣通過使用修改過的AAPT編譯資源並進行打包,我們再用上面講到的apktool這個工具進行反編譯,是反編譯後的:

發現什麼變化了嗎?在res目錄下熟悉的layout、drawable、anim、menu等檔案夾不見了,那他們去哪了呢?因為apktool工具把它們放到了unknown檔案夾下了,見:

讓我們來看一下unknown檔案夾,你會發現資源檔名已經被簡短無意義名稱進行替換了,這樣會給反編譯者製造理解上的困難,反編譯者需要消耗一定的時間來搞清楚這些資源檔的作用,資源混淆帶來的另外一個好處是能明顯減小APK的大小,資源混淆既能保護資源檔的安全又能減小安裝包的大小,那我們何樂而不為呢?

這樣通過修改AAPT,我們可以在代碼零修改的基礎下就能做到相對的資源安全,當然安全是相對的,沒有絕對的安全。

美團Android資源混淆保護實踐

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.