Android apk加固(加殼)整理

一、Dex加殼由來

最近在學(xué)習(xí)apk加密唇礁,在網(wǎng)上看了一篇《Android中的Apk的加固(加殼)原理解析和實(shí)現(xiàn)》勾栗,我發(fā)現(xiàn)原文把整個(gè)apk都寫入到dex文件中,如果apk小還好盏筐,當(dāng)原APK大于200M械姻,客戶端解殼很費(fèi)勁,打開后應(yīng)用就卡住了机断,如果只是把原apk的dex加殼不就很容易解開了嘛楷拳。我不是原創(chuàng),只是按照我自己的思路將大神的加固稍作調(diào)整吏奸,并且將整個(gè)項(xiàng)目整理如下欢揖。

二、Dex結(jié)構(gòu)

dex_structure.png

如圖所示奋蔚,新的dex由解殼dex她混、dex集合、dex集合描述和描述長(zhǎng)度組成
三泊碑、核心代碼

  • 加殼
    /**
     * 給apk加殼
     * @param primaryApkPath 原apk
     * @param unShellApkPath 解殼apk
     * @param outApkPath 加殼后新APK
     * @throws Exception
     */
    public static void apkShell(String primaryApkPath,String unShellApkPath,String outApkPath) throws Exception{
        if(!FileUtils.isExit(primaryApkPath, unShellApkPath)){
            throw new RuntimeException("check params");
        }
        //解壓原apk
        String unPrimaryApkDstPath = primaryApkPath.replace(".apk", "");
        ApkToolUtils.decompile(primaryApkPath, unPrimaryApkDstPath);
        String primaryManifestPath = unPrimaryApkDstPath + File.separator + "AndroidManifest.xml";

        //解壓解殼apk
        String unShellApkDstPath = unShellApkPath.replace(".apk", "");
        ApkToolUtils.decompile(unShellApkPath, unShellApkDstPath);
        String unShellManifestPath = unShellApkDstPath + File.separator + "AndroidManifest.xml";
        String unShellDexPath = unShellApkDstPath + File.separator + "classes.dex";
        File unShellFile = new File(unShellDexPath);


        File unApkDir = new File(unPrimaryApkDstPath);
        ArrayList<File> dexArray = new ArrayList<File>();
        for(File file : unApkDir.listFiles()){//讀取解殼后的dex
            if(file.getName().endsWith(".dex")){
                dexArray.add(file);
            }
        }
        String shellDexPath = unPrimaryApkDstPath + File.separator + "classes.dex";
        shellDex(dexArray, unShellFile, shellDexPath);//生產(chǎn)新的dex(加殼)

        String mateInfPath = unPrimaryApkDstPath + File.separator +"META-INF";//刪除meta-inf坤按,重新簽名后會(huì)生成
        FileUtils.delete(mateInfPath);

        for(File file : dexArray){//清理多余dex文件
            if(file.getName().equals("classes.dex")){
                continue;
            }
            FileUtils.delete(file.getAbsolutePath());
        }

        String unShellApplicationName = AndroidXmlUtils.readApplicationName(unShellManifestPath);//解殼ApplicationName
        String primaryApplicationName = AndroidXmlUtils.readApplicationName(primaryManifestPath);//原applicationName
        AndroidXmlUtils.changeApplicationName(primaryManifestPath, unShellApplicationName);//改變?cè)瑼pplicationname為解殼ApplicationName
        if(primaryApplicationName != null){//將原ApplicationName寫入mateData中,解殼application中會(huì)讀取并替換應(yīng)用Application
            AndroidXmlUtils.addMateData(primaryManifestPath, "APPLICATION_CLASS_NAME", primaryApplicationName);
        }
        //回編馒过,回編系統(tǒng)最好是linux
        ApkToolUtils.compile(unPrimaryApkDstPath,outApkPath);
        //v1簽名
        SignUtils.V1(outApkPath, SignUtils.getDefaultKeystore());
        //清理目錄
        FileUtils.delete(unPrimaryApkDstPath);
        FileUtils.delete(unShellApkDstPath);
    }

加殼工程是一個(gè)java工程腹忽,解壓apk使用了apktool窘奏,apktool這個(gè)工具最好是在linux下使用嘹锁,xml操作使用了W3C java自帶的,不咋個(gè)好用着裹,為了項(xiàng)目簡(jiǎn)單沒用其他的jar包领猾。加殼項(xiàng)目中對(duì)byte數(shù)組的加密使用了aes,也可以用其他方法去實(shí)現(xiàn)骇扇。

  • 解殼
 /**
     * 從殼的dex文件中分離出原來的dex文件
     * @param data
     * @param primaryDexDir
     * @throws IOException
     */
    public void splitPrimaryDexFromShellDex(byte[] data, String primaryDexDir) throws IOException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, NoSuchPaddingException {
        int shellDexLen = data.length;
        byte[] dexFileCommentLenByte = new byte[4];//dex信息長(zhǎng)度
        System.arraycopy(data, shellDexLen-4, dexFileCommentLenByte, 0, 4);
        ByteArrayInputStream bais = new ByteArrayInputStream(dexFileCommentLenByte);
        DataInputStream in = new DataInputStream(bais);
        int dexFileCommentLen = in.readInt();

        byte[] dexFileCommentByte = new byte[dexFileCommentLen];//dex信息正文
        System.arraycopy(data,shellDexLen-4-dexFileCommentLen,dexFileCommentByte,0,dexFileCommentLen);
        String dexFileComment = new String(dexFileCommentByte);
        LogUtils.d("dex comment:"+dexFileComment);
        ArrayList&lt;DexFile&gt dexFileArrayList = (ArrayList&lt;DexFile&gt) JSON.parseArray(dexFileComment,DexFile.class);
        int currentReadEndIndex = shellDexLen - 4 - dexFileCommentLen;//當(dāng)前已經(jīng)讀取到的內(nèi)容的下標(biāo)
        for(int i = dexFileArrayList.size()-1; i&gt=0; i--){//取出所有的dex,并寫入到payload_dex目錄下
            DexFile dexFile = dexFileArrayList.get(i);
            byte[] primaryDexData = new byte[dexFile.getDexLength()];
            System.arraycopy(data,currentReadEndIndex-dexFile.getDexLength(),primaryDexData,0,dexFile.getDexLength());
            primaryDexData = decryAES(primaryDexData);//界面
            File primaryDexFile = new File(primaryDexDir,dexFile.getDexName());
            if(!primaryDexFile.exists()) primaryDexFile.createNewFile();
            FileOutputStream localFileOutputStream = new FileOutputStream(primaryDexFile);
            localFileOutputStream.write(primaryDexData);
            localFileOutputStream.close();

            currentReadEndIndex -= dexFile.getDexLength();
        }
    }

//代碼片段摔竿,DexClassLoder加載多個(gè)dex
  //找到dex并通過DexClassLoader去加載
    StringBuffer dexPaths = new StringBuffer();
    for(File file:dex.listFiles()){
        dexPaths.append(file.getAbsolutePath());
        dexPaths.append(File.pathSeparator);
    }
    dexPaths.delete(dexPaths.length()-1,dexPaths.length());
    LogUtils.d(dexPaths.toString());
    DexClassLoader classLoader = new DexClassLoader(dexPaths.toString(), odex.getAbsolutePath(),getApplicationInfo().nativeLibraryDir,(ClassLoader) RefInvoke.getFieldOjbect(
            "android.app.LoadedApk", wr.get(), "mClassLoader"));//android4.4后ART會(huì)對(duì)dex做優(yōu)化匠题,第一次加載時(shí)間較長(zhǎng),后面就很快了

將原項(xiàng)目dex從殼dex中獲取出來韭山,然后在onCreate中將dex拼接后使用DexClassLoder加載郁季,nativeLibrary咋們只對(duì)dex做了加殼所以可以直接使用Application的nativeLibraryDir冷溃。
其它核心代碼,application替換這類的年柠,可以在原文中查看凿歼。

四、效果

effect.jpg

從左往右分別為原demo工程的apk冗恨,為了實(shí)現(xiàn)多dex加了很多費(fèi)代碼答憔,加殼后的apk,解殼apk掀抹∨巴兀可以看出加殼后項(xiàng)目demo工程的dex被隱藏,顯示的是解殼工程的代碼

五傲武、待優(yōu)化

  1. 將客戶端的解密放入native層蓉驹;
  2. 將解密后的dex文件隱藏;

解密后的文件依舊存于應(yīng)用的私有存儲(chǔ)空間中揪利,ROOT了的手機(jī)和模擬器很容易就可以拿到解密后的dex态兴,所以這種加殼方法只是將代碼從apk中隱藏。
如果有好的解決方法疟位,或者好的加殼方法望告知瞻润!

附送整個(gè)項(xiàng)目代碼代碼傳送門

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市献汗,隨后出現(xiàn)的幾起案子敢订,更是在濱河造成了極大的恐慌,老刑警劉巖罢吃,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異昭齐,居然都是意外死亡尿招,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門阱驾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來就谜,“玉大人,你說我怎么就攤上這事里覆∩ゼ觯” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵喧枷,是天一觀的道長(zhǎng)虹统。 經(jīng)常有香客問我弓坞,道長(zhǎng),這世上最難降的妖魔是什么车荔? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任渡冻,我火速辦了婚禮,結(jié)果婚禮上忧便,老公的妹妹穿的比我還像新娘族吻。我一直安慰自己,他們只是感情好珠增,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布超歌。 她就那樣靜靜地躺著,像睡著了一般蒂教。 火紅的嫁衣襯著肌膚如雪巍举。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天悴品,我揣著相機(jī)與錄音禀综,去河邊找鬼。 笑死苔严,一個(gè)胖子當(dāng)著我的面吹牛定枷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播届氢,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼欠窒,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了退子?” 一聲冷哼從身側(cè)響起岖妄,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎寂祥,沒想到半個(gè)月后荐虐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丸凭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年福扬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惜犀。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡铛碑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出虽界,到底是詐尸還是另有隱情汽烦,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布莉御,位于F島的核電站撇吞,受9級(jí)特大地震影響俗冻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜梢夯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一言疗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颂砸,春花似錦噪奄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至色罚,卻和暖如春碰缔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背戳护。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工金抡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人腌且。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓梗肝,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親铺董。 傳聞我的和親對(duì)象是個(gè)殘疾皇子巫击,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容