吾愛破解 - LCG - LSG |安卓破解|病毒分析|破解軟件|www.kvamco.live

 找回密碼
 注冊[Register]

QQ登錄

只需一步,快速開始

搜索
查看: 13620|回復: 135
上一主題 下一主題

[Android 原創] 入門級加固--3種加固方式學習記錄

    [復制鏈接]
跳轉到指定樓層
樓主
順利畢業 發表于 2019-10-26 14:03 回帖獎勵
本帖最后由 順利畢業 于 2019-10-30 14:37 編輯

最近剛開始接觸Andorid加固,是從姜維前輩在2015年的一些帖子開始學習的,同時也從個別惡意樣本中學到了其他的加固手段。雖然本帖子涉及到的知識大概都是第一代或者第二代加固,對于大佬們來說已經是“陳年舊識”了,但對于本菜雞來說,依舊還是一個新世界~在進入這個新世界的時候,雖然有姜維前輩的帖子為導,但總有一些不適用的地方,比如其加固so文件的section帖子對Android7不完全使用的問題等,so文件的大小端問題;且由于本人學習過程中未完全照搬姜維前輩的代碼,故過程中也遇到了些問題,如加固后可成功運行原apk的組件但依舊顯示殼信息等問題;同時,對于姜維前輩一筆帶過或者沒解釋的知識點,我在學習過程中也進行了些補充。具體的,都會在下面呈現出來。(看姜維前輩的帖子,感悟最深的就是“萬物皆可二進制”。。。)


本帖子涉及到3種加固:


1、在java層為.apk文件進行加固:跟著姜維前輩學~


2、在native層為.dex文件進行加固:逆向一款惡意軟件,從中學習到它的加固方式。在后面的帖子的第二部分將著重貼出逆向的全過程。


3、在native層為.so文件進行加固:依舊跟著姜維前輩的步伐~



備注:


本帖子重點在于記錄自己學習過程中遇到的問題及解決方法,以及部分原理知識。      


帖子作為學習記錄,會部分贅述姜維前輩帖子里的內容,但不會全寫,會附上具體的連接,有興趣的同學最好提前先康康~


本文也將高亮自己學習過程中遇到的問題及對應的解決方法,方便大家查看。


還有一部分,是我逆向一款在native層進行加固的惡意樣本,逆向的過程也會貼出來。


對于一些比較繞的地方,我根據自己的理解畫了幾張圖,加深理解。


本帖子比較核心的內容為:遇到的問題及解決方法、逆向native層加固的樣本。感興趣的同學可以挑著看。




目錄:

1、在java層為.apk文件進行加固

       1.0、學習資料

       1.1、Android殼原理

              1.1.1、 Dex文件基礎知識

      1.2、加固.

              1.2.1、 原理

              1.2.2、 操作

       1.3、實踐

              1.3.1、 基礎操作

                     準備待加殼apk

                     加殼程序

                     脫殼程序

                     合體操作流程

              1.3.2、 注意事項

2、在native層為.dex文件進行加固

       2.1、殼原理

              2.1.1、 大致流程

       2.2、 樣本分析

3、在native層為.so文件進行加固

      3.0、學習資料

      3.1、加密so的section

              3.1.1、 原理

             3.1.2、 實踐

       3.2、加密so的函數

              3.2.1、原

              3.2.2、 實踐

       3.3、兩者比較

4、3者比較


1、在java層為.apk文件進行加固

1.0、學習資料《Android加固原理研究》:https://blog.csdn.net/jiangwei0910410003/article/details/48415225

       需補充前期知識:

(1)《動態加載技術解讀》:https://blog.csdn.net/jiangwei0910410003/article/details/17679823

(2)《Java高新技術第一篇:類加載器詳解》:https://blog.csdn.net/jiangwei0910410003/article/details/17733153

(3)《類加載器分析》:http://blog.csdn.net/jiangwei0910410003/article/details/41384667

(4)《資源加載問題(換膚原理解析)》:http://blog.csdn.net/jiangwei0910410003/article/details/47679843

(5)《動態加載Activity(免安裝運行程序)》:http://blog.csdn.net/jiangwei0910410003/article/details/48104455

1.1、Android殼原理

1.1.1、 Dex文件基礎知識

1、 學習資料:

(1)《Android加固原理研究》:https://juejin.im/entry/5a5c55426fb9a01c9f5b65ed
1.2、加固1.2.1、 原理1、學習資料:(1)《Android中的Apk的加固(加殼)原理解析和實現》:https://blog.csdn.net/jiangwei0910410003/article/details/484152252、加固原理圖:




  
3、加固的核心:如何將源Apk和殼Apk進行合并成新的Dex。
4、核心原理:



只要關注上面紅色標記的三個部分:

(1) checksum 文件校驗碼 ,使用alder32 算法校驗文件除去 maigc ,checksum 外余下的所有文件區域 ,用于檢查文件錯誤 。

(2) signature 使用 SHA-1 算法 hash 除去magic ,checksum 和 signature 外余下的所有文件區域 ,用于唯一識別本文件。

(3) file_sizeDex 文件的大小 。

(4)原因:要將一個文件(加密之后的源Apk)寫入到脫殼Dex中,那么需要修改脫殼Dex的文件校驗碼(checksum).因為它是檢查文件是否有錯誤。那么signature也是一樣,也是唯一識別文件的算法。還有就是需要修改脫殼dex文件的大小。此外,還需要一個操作,就是標注一下加密的Apk的大小,因為在脫殼的時候,需要知道加密后的源Apk的大小,才能正確的得到加密后的源Apk。這個值直接放到文件的末尾就可以了。這樣,就生成了一個殼dex文件。(脫殼dex追加源apk、源apk大小,并修改脫殼dex頭部,從而生成了殼dex文件。)即:修改Dex的三個文件頭,將源Apk的大小追加到殼dex的末尾就可以了。修改之后得到新的Dex文件樣式如下:



對應涉及到三個工程:

(1)源程序項目(需要加密的Apk)

(2)脫殼項目(解密源Apk和加載Apk)

(3)對源Apk進行加密和脫殼項目的Dex的合并

1.2.2、 操作

1、代碼流程:

(1)編寫源程序項目,該項目需含有application類,生成origin.apk;

(2)編寫脫殼程序項目,以生成“脫殼dex文件”:

(a)原理:通過反射置換android.app.ActivityThread 中的mClassLoader為加載解密出APK的DexClassLoader,該DexClassLoader一方面加載了源程序、另一方面以原mClassLoader為父節點,這就保證了即加載了源程序又沒有放棄原先加載的資源與系統代碼。隨后找到源程序的Application,通過反射建立并運行。這里需要注意的是,我們現在是加載一個完整的Apk,讓他運行起來,那么我們知道一個Apk運行的時候都是有一個Application對象的,這個也是一個程序運行之后的全局類。所以我們必須找到解密之后的源Apk的Application類,運行的他的onCreate方法,這樣源Apk才開始他的運行生命周期。這里我們如何得到源Apk的Application的類呢?從源Apk的Androidmanifest.xml文件的meta標簽獲取源程序apk中的application對象。

(b)操作:從脫殼程序apk中找到源程序apk,并進行解密操作;從源程序apk中獲取dex文件、so文件;在脫殼程序的application中的oncreate方法中執行操作,找到源程序的application程序,讓其運行;需在脫殼程序的AndroidManifest.xml中聲明一下源程序中的Activity。

(3)編寫加殼程序項目:

       (a)以二進制形式讀取origin.apk文件形成數據流dataA,并獲取該文件的大小sizeA;

       (b)以二進制形式讀取脫殼dex文件形成數據流dataB,并獲取該文件的大小sizeB;

      (c)采用自定義的加密方法,對數據流dataA進行加密,即加密origin.apk文件,形成數據流dataC;

       (d)設置殼dex的大小sizeC=sizeA+sizeB+4;

      (e)申請一個新的byte數組newdex,大小為sizeC;

       (f)將dataC拷貝到newdex的頭部,緊隨其后放置dataB的數據,在newdex的最后4個字節放置sizeA;

       (g)修改newdex中的file_size字段,即修改脫殼dex的file_size字段:對newdex計算其長度length,將該值替換掉newdex的file_size字段,即將newdex的第32-35共4個字節的地方修改成length。


       (h)修改newdex中的signature 字段,即修改脫殼dex的signature 字段:對newdex計算其sha1的值,將該值替換掉newdex的signature 字段,即將newdex的第12-31共20個字節的地方修改成sha1計算后的值。


       (i)修改newdex中的checksum 字段,即修改脫殼dex的checksum 字段:調用Adler32類,利用該類的實例對newdex計算其adler值,將該值替換掉newdex的checksum字段,即將newdex的第8-11共4個字節的地方修改成adler計算后的值。




  1.3、實踐

1.3.1、 基礎操作準備待加殼apk


1、 程序存放地址:\AndroidStudioProjects\testAppName


2、 程序名稱:testAppName


3、 該apk最好具有application類;


4、 在該apk的application類與mainActivity內,均打印出用于識別的信息;


5、 生成apk(在Andorid Studio里build的話,應該是直接用test來簽名了)。


6、 Apk的包名為com.example.testappname,將該apk命名為A.apk;


7、 A.apk運行成功時,將會輸出以下信息,即在application類中輸出當前的包名,在mainActivity類中輸出當前組件的名稱:

  


運行界面將提示此時為testAppName:



加殼程序


1、 程序存放地址:\AndroidStudioProjects\shellTool(等脫殼dex生成后,再生成加殼apk的dex)


2、 程序名稱:shellTool


3、 該程序僅用于將A.apk拼接在殼dex文件后部。


4、 將“準備待加殼apk”步驟中生成的A.apk文件,“脫殼程序”步驟中生成的B.dex文件放置在Assets目錄下,用二進制形式將兩者拼接在一起,B.dex在前,A.apk在中間,尾部是A.apk的大小,生成tmp.dex文件。


5、 修改tmp.class文件頭部的fileSize字段、簽名字段、checksum字段(這三個字段原先存放的都是B.dex的數據),生成classes.dex文件,保存在/sdcard/目錄下。

脫殼程序


1、 程序存放地址:\AndroidStudioProjects\dumpShell\app,脫殼dex文件在\AndroidStudioProjects\dumpShell\app\build\outputs\apk\debug\app-debug.apk內部


2、 程序名稱:dumpShell


3、 該apk應該含有application類,且不能含有其他組件。


4、 在dumpShell的application類中,重寫attachBaseContext方法,目的:


*      1、解密源apk;


     *      2、初始化自定義類加載器


     *      3、利用反射,設置LoadedApk中加載器對象為自定義加載器


5、 在dumpShell的application類中,重寫onCreate方法,目的:


     *     1、獲取源apk的Application名稱;


     *     2、利用反射,生成正確的Application對象


     *     3、利用反射,設置ActivityThread中的Application信息。(ActivityThread為當前主線程)


     *      4、調用源apk的application對象的oncreate方法。


6、 編譯并build出dumpShell.apk,用7-zip對其進行解壓,將classes.dex重命名為B.dex并將由“加殼程序”進行處理,并將“加殼程序”的輸出結果classes.dex(與dumpShell.apk的原classes.dex不同)放置在該解壓后的目錄下,同時刪除簽名文件,將此時目錄下的所有文件壓縮到一個zip文件中,并對其進行簽名得到最終的apk文件。此時的apk文件是本次“加固”的最終成果。


合體操作流程


1、 編譯運行testAppName程序,生成源apk,并將其命名為A.apk;


2、 編譯運行dumpShell程序,生成dumpShell.apk,從dumpShell.apk中提取classes.dex,并將其命名為B.dex,其作為脫殼dex;


3、 將A.apk與B.dex放置在shellTool項目的Assets目錄下,編譯運行shellTool項目,該項目將在/sdcard/目錄下生成classes.dex文件,利用adb將其拷貝到PC端;


4、 用7-zip解壓dumpShell.apk,刪除里面的簽名文件與classes.dex,并將步驟3得到的classes.dex文件放置到該文件夾下。將該文件夾下的所有文件一起壓縮到dumpShell.zip文件,并對該文件進行簽名成apk文件,安裝運行該apk。


1.3.2、 注意事項


1、   在教程《Android中的Apk的加固(加殼)原理解析和實現》:https://blog.csdn.net/jiangwei0910410003/article/details/48415225中,采用eclipse開發,其在將A.apk與B.dex合成為classes.dex時,利用new File()生成的classes.dex是直接生成在項目路徑下的,但是用Android Studio不行,故直接在/sdcard/目錄下生成該文件,并用adb將其拷貝到PC段


2、   欲使用7-zip打開dumpShell.apk并刪除簽名文件與classes.dex時,提示只讀無法進行刪除,對操作對象是/data/app/<myAppPkgName>/base.apk時也是同樣只讀,即便對其使用chmod 777操作也無法。dumpShell.apk是自己用Android Studio生成的,若是用其他惡意軟件的base.apk則可以正常用7-zip打開進行刪除操作。具體原因暫未知道。故,用7-zip對其進行解壓,解壓后刪除文件夾下的簽名文件與classes.dex,放置目標classes.dex,隨后將該文件夾下的所有文件一同壓縮成一個zip文件,再對zip文件進行簽名,稱為apk文件。


3、   問題:加固后將A.apk放置在dumpShell.apk中,運行dumpShell.apk時能調用A.apk的application類,但無法按照A.apk的運行周期調用到A.apk的mainActivity類。原因:dumpShell.apk不應包含mainActivity類,即應只包含application類,在application類中完成脫殼且加載A.apk的任務。否則,將存在兩個mainActivity(dumpShell.apk的,與A.apk的),則將運行dumpShell.apk的。解決方法:去除dumpShell.apk的mainActivity類,且在dumpShell.apk的manifest文件中應該聲明A.apk的mainActivity類。


4、   問題:加固后將A.apk放置在dumpShell.apk中。單獨運行A.apk時將輸出A.apk的包名與mainActivity的組件名;運行dumpShell.apk時,盡管調用運行了A.apk,但其輸出信息變成dumpShell.apk的包名,組件名稱中的包名部分也變成了dumpShell.apk的包名。 (上面2張,是源apk單獨運行的日志) (上面2張,是將源apk加固進dumpShell.apk后,dumpShell.apk運行的日志)原因:不詳。不影響A.apk的運行,所以先不深究了。


5、   問題:加固后將A.apk放置在dumpShell.apk中,運行dumpShell.apk時發現此時A.apk的mainActivity類能夠被加載了,但是運行界面顯示的依舊是dumpShell.apk的信息:   


(圖片分別為:單獨運行A.apk,將A.apk加固進dumpShell.apk后運行dumpShell.apk)

原因:A.apk的mainActivity類在設置contentView時是使用R類去調用A.apk自身的activity_main.layout文件,而由于加固后的運行環境中R類是dumpShell.apk的R類(而非A.apk的),故即便調用了A.apk的mainActivity類,其在設置contentView時是將使用dumpShell.apk的R類去調用dumpShell.apk的的activity_main.layout文件,因而運行界面顯示的是dumpShell.apk的信息。


2、在native層為.dex文件進行加固

2.1、殼原理

將原始apk的.dex文件進行加密后,放在apk的資源路徑下,隨后通過native代碼重新對該.dex文件進行解密、加載,從而執行原始.dex文件。

2.1.1、 大致流程

0、以樣本923872474d2b49df6b2715f1a10ac0e2為例;

1、提取原始apk的.dex文件,加密后重命名為dmeod.jar,將其放置在apk的Assets目錄下;

2、編寫libdmeod.so文件,用于解密demod.jar文件并對其進行dexClassLoader操作;

3、在原始apk中,新建application類作為殼的啟動類,將其命名為com.vod.wbmp.yobl.chjiv,并重寫attachBaseContext方法和onCreate方法。這兩個方法,將調用libdeod.so文件內的方法;


4、修改apk的manifest的application類名稱為這個啟動類的名稱。


  

2.2、 樣本分析

1、樣本:923872474d2b49df6b2715f1a10ac0e2

2、用IDA打開libdmeod.so文件,找到JNI_Onload方法,按F5查看其C代碼。易知v6與v3的實際類型為JNIEnv*,故將光標停在這兩個變量上,按y鍵修改其類型,可見JNI_Onload的C代碼變成如下:

  

3、查看off_8004,可知wefiz被重命名為i,izogr被重命名為r,即:com.vod.wbmp.yobl.chjiv這個application類在其attachBaseContext方法中實際調用的是so文件里的i方法,onCreate方法中實際調用的是so文件中的r方法。



4、先看方法i,按F5可查看其c代碼,但該c代碼的可讀性較差,如下圖:

  

因此對其進行修改:可知紅色框中的數據類型應該是char型,但此時顯示的是int型,故按R鍵(或右鍵點擊該數據,選擇Char)將其顯示成char型,通過從v197拼接到v222可知,從v197開始表示的字符串“android/app/ActivityThread”(為方便后續操作,此時可將v197重命名為android_app_ActivityThread)。



繼續對其進行修改:在綠色框中,此時無法得知調用的是哪個函數,但易知v的數據類型為JNI *,v4的數據類型應該為JNIEnv,按y鍵修改v和v4的數據類型,便可看到此時調用的函數為:



故該部分代碼的功能即:FindClass(“android/app/ActivityThread”)



對方法i與方法r執行以上的相同操作,增強i與r的可讀性。修改后的方法i部分截圖為:




為方便查看,提取方法i里的主要功能代碼,如下圖:



5、方法i等同于以下java代碼:

  

6、方法i的功能總結:

調用edf方法來解密dmeod.jar(解密部分就不作說明啦),獲取當前的主線程currentActivityThread,利用currentActivityThread獲取mPackages,再把當前app的包名傳遞給mPachages來獲得當前app的弱引用,從而找到當前app的類加載器mClassLoader。利用DexClassLoader來加載dmeod.jar文件,并將其dexClassLoader設置成新的mClassLoader的值,隨后加載dmeod.jar文件里的真正入口Acitivity。

7、對方法r進行同樣的修改操作,增加其可讀性。方法r的代碼較少,下面的截圖已經包含了其主要的功能了,對應的java代碼就不貼上來了。方法r的主要功能是:調用dmeod.jar里真正的application類的attachBaseContext方法與onCreate()方法,即啟動真正的application類。



樣本執行邏輯總結:

(1)   將原始apk的.dex文件加密成dmeod.jar文件,該dmeod.jar文件里包含apk的真實application類與入口activity類;

(2)   在AndoridManifest.xml文件中,將apk的application入口修改成殼application類,即類chjiv。

(3)   在chjiv類的attachBaseContext方法里調用native代碼,實現功能:解密dmeod.jar文件并利用DexClassLoader對其進行加載,并將其設置成已加載apk的類加載器(即修改成為android.app.LoadedApk的mClassLoader),加載原始apk的入口activity類。

(4)   在chjiv類的onCreate方法調用native代碼,實現功能:調用與那時apk的真正application類里的attachBaseContext方法與DexClassLoader方法。

備注:

1、若dmeod.jar文件里沒有application類,即原始apk沒有application類,則無需修改殼application類的onCreate方法也能使原始apk正常運行。

2、根據該樣本在native層的加固,寫了個java層的同功能的加固,放在:\AndroidStudioProjects\helloShell2




3、在native層為.so文件進行加固

3.0、學習資料

《Android中對Apk加固(加殼)續篇之---對Native層(so文件)進行加固》:https://blog.csdn.net/jiangwei0910410003/article/details/49967375/

       需補充前期知識:

(1)《Android逆向之旅---SO(ELF)文件格式詳解》:https://blog.csdn.net/jiangwei0910410003/article/details/49336613

(2)《Android逆向之旅---Android應用的漢化功能(修改SO中的字符串內容)》:https://blog.csdn.net/jiangwei0910410003/article/details/49361281

(3)《Android逆向之旅---基于對so中的section加密技術實現so加固》http://blog.csdn.net/jiangwei0910410003/article/details/49962173      

(4)《Android逆向之旅---基于對so中的函數加密技術實現so加固》http://blog.csdn.net/jiangwei0910410003/article/details/49966719


3.1、加密so的section

3.1.1、 原理

利用c代碼生成so文件時:

聲明重要函數showMessage時指定將其存放在自定義的.mytext段(通過__attribute__((section(".mytext")));

編寫解密函數并在其聲明時指定其在main函數前運行(即通過__attribute(constructor)))。

從而使得so文件在被加載到內存后可第一時間對showMessage函數進行解密。此時生成初始so文件section_origin.so,對so文件尚未加密。

寫一個腳本對section_origin.so中的“.mytext”段進行加密并重寫回原處。

為了便于解密,將.mytext段的偏移地址與段大小保存在so文件的頭部。此時生成section_encrypt.so即為被加密過的so文件。



  加密時,利用so文件來獲取段的偏移與大小的原理:


1、 將so文件以二進制數據的形式進行讀取,將頭52個字節解析成Elf文件頭hdr。


2、 利用hdr獲得e_shoff,即sectionheader table offset節區頭部表的偏移,該表中對于每個元素的數據大小是40個字節,該表中的所有元素個數總數存放在hdr的e_shsum中,每個元素代表一個節區的摘要信息,即:在e_shoff所指地址開始,存在著e_shsum個節區的摘要信息,每個節區的摘要信息占用40個字節。


3、 針對每40個字節的元素進行解析(即解析每個節區的摘要信息),并將解析后的數據放置在so文件的shdrList變量中。


4、 通過hdr獲得e_shstrndx,即sectionheader table’s “section header string table” entry offset(節區頭部表的“節區頭部字符串表”入口偏移),該值表示“節區頭部字符串表”在“節區頭部表”中的索引號,如該值為0x000a,則表示在section header table(節區頭部表)中,第11個節區(索引從0開始)為字符串表。


5、 以hdr的e_shstrndx作為index,從shdrList變量中獲取第index個數據,即獲得了節區頭部字符串表的摘要shdr,簡稱為字符串表shdr。獲取shdr的sh_offset,即獲得字符串表的偏移地址offset,該offset所指的區域保存所有字符串。


6、 遍歷shdrList中的每個數據,即遍歷每個節區的頭部信息,將每個節區的頭部信息記作變量tmp。針對每個節區的頭部信息,獲取其sh_name,該值為該節區名稱在字符串表中的偏移,故用offset+sh_name即可表示該節區名稱的總偏移sectionNameOffset;


7、 在so文件的sectionNameOffset所指的位置開始,便代表了該節區名稱的字符串實際存放位置。將該字符串與目標節區名“.mytext”進行比對,直到找到匹配的,即tmp此時所指的節區頭部是.mytext的頭部。


8、 獲取此時tmp的sh_offset與sh_size,即獲取到了.mytext的偏移與大小。

  




解密時,利用so文件被裝載后的內存數據來解密:


前期我們在加密時,將.mytext的偏移與大小存放在e_flags與e_entry中,故加密時應先獲取這兩個數據。解密時,已經無法獲得so文件,但能獲得so文件被加載到內存后的數據。獲取測試機上本進程的信息,即讀取/proc/<pid>/maps的數據,并找到libsoShellDemo.so被加載到內存后的起始地址base。利用elf.h對base所指區域后面的數據進行解析,解析成so文件的頭部ehdr。Endr的e_flags存放.mytext的偏移,e_entry存放.mytext的大小(即為mySectionSize)。用e_flags的值加上base,即獲得.mytext在內存中的位置text_addr。由于.mytext屬于代碼段而非數據段,代碼段不可寫,故需改變內存中.mytext所處位置的讀寫權限,利用mprotect(start,len,prot)方法來進行修改。mprotect作用的內存區間為n個內存頁,故start必須是一個內存頁的起始地址,且區間長度len必須是頁大小的整數倍。但,.mytext在內存中的位置text_addr未必是一個內存頁的起始地址,故使用text_addr/4096*4096來獲得text_addr所處內存頁的起始地址。修改完權限后,即可開始對內存中的.mytext數據進行解密。解密后,再修改回去權限。




ElfType32.java與elf.h關于大小端的處理:


ElfType32.java處理so文件時,僅把數據與“名稱”進行比對,如把頭16個字節的數據認定為e_ident,即以so文件的格式來解析二進制數據中的所有數據,其不管每個“名稱”內的數據具體的存放順序(大端、小端),而elf.h會處理數據的小端情況。如so文件的0x18處開始的4個字節內容為0x24000000,在ElfType32.java中獲取到的是0x24000000,利用elf.h獲取到的是0x00000024,實際的值也應該是0x00000024。故,若使用ElfType32.java來讀取so文件時,若遇到數值的數據時應該進行小端處理(將0x24000000轉換成0x024),即進行逆轉,方能獲取到真正的數值大小;若遇到字符串的數據,則無需逆轉。將.mytext的offset與size放到so文件頭部的e_entry與e_flags字段時,也應該對offset與size進行小端處理(將0x024轉換成0x24000000)再寫入so文件頭部。當so文件被裝載進內存后,將其在內存中的起始位置開始的數據均利用elf.h來讀取并解析成so文件的頭部,此時無論遇到數值型的數據還是字符串型的數據均無需進行小端處理。




Andorid7與so文件的e_shoff、e_entry:


so文件裝載到Andorid7的測試機上時,dlopen會檢查e_shoff與e_entry字段的值是否合理(是否符合小端),若不合理將報錯:




且e_shoff被篡改后將影響so文件的裝載,將報錯:




故在加密時不可修改e_shoff字段,可選擇使用e_flags字段。





3.1.2、 實踐

1、在Andorid Studio里新建soShell項目,按照本文檔上面的NDK開發步驟:

(1)在mainActivity中load 待生成的so文件并聲明且使用目標函數showMessage;

(2)對mainActivity使用javah工具,來生成.h文件,便于獲取目標函數showMessage的簽名;

(3)新建.cpp文件,編寫showMessage函數與解密函數decrypt_soShell,并為showMessage指定屬性(即在函數聲明時附上__attribute__((section(".mytext")))),為decrypt_soShell函數指定屬性(即在函數聲明時附上__attribute(constructor)))。

(4)新建并填寫CMakeLists.txt文件;

(5)在build.gradle文件中,設置so文件生成的abi為armV7,并設置cmake的路徑。

(6)編譯,則在Android Studio該項目的我們預設的路徑下將生成libsoShellDemo.so文件。


2、新建一個Andorid Studio項目作為加密的腳本,(可以直接在以前的shellTool項目里進行增加,反正目的都是加固),完成以下步驟:

(1)將soShell項目生成的libsoShellDemo.so文件放置到shellTool項目的Assets目錄下且重命名為A.so文件,并以二進制的形式進行讀取;

(2)新建ElfType32.java,該文件內容可百度,該文件用于將二進制數據解析成so文件格式;

(3)新建encryptSoSeciton.java文件,該文件以二進制形式讀取,并調用ElfType32來進行解析,根據目標段名稱.mytext來獲取到該段的偏移和大小,對該段數據進行翻轉(即ABC變成CBA),并將偏移放置在so文件的e_flags字段,將大小放在so文件的e_entry字段。保存新的so文件數據。

(4)編譯,運行,獲得新的so文件,B.so文件。


3、返回soShell項目中,將該項目從ndk開發轉換為普通android項目:

(1)在libs目錄下,新建armeabi-v7a文件夾,并將B.so文件放置在該目錄下,重命名為libsoShellDemo.so文件;

(2)刪除jniLibs目錄;

(3)在build.gradle文件中,注釋掉“設置cmake的路徑,設置so文件生成的abi”部分,新增jniLibs.srcDirs。

(4)編譯,運行,應用成功運行,且輸出了目標日志。




4、對比so文件加密前后用010Editor查看兩者對比:

  

用IDA查看兩者對比:

(加密前)



(加密后,IDA提示ELF-flag數據有問題,該值0x232c正是我們保存的.mytext的偏移)




3.2、加密so的函數

3.1.1、原理

利用c代碼生成so文件時:

聲明重要函數showMessage時指定將其存放在自定義的.mytext段(通過__attribute__((section(".mytext")));編寫解密函數并在其聲明時指定其在main函數前運行(即通過__attribute(constructor)))。從而使得so文件在被加載到內存后可第一時間對showMessage函數進行解密。

此時生成初始so文件func_origin.so,對so文件尚未加密。

寫一個腳本對func_origin.so中的showMessage函數進行加密并重寫回原處。此時生成func_encrypt.so即為被加密過的so文件。




在so文件中尋找目標函數的地址與大小的原理:





尋找program header偏移時,加密用p_offset,解密用p_vaddr的原因


加密時,是利用腳本對so文件“實體”進行解析從而得到program header偏移的,該值保存在p_offset字段。


解密時,是在app運行時對so文件“在內存中的數據”進行解析從而得到program header的,該值保存在p_vaddr中。


即:p_offset表示programheader在文件中的偏移;p_vaddr表示program header在內存中的偏移。




d_val與d_ptr均為4字節類型的數據,但在Elf32_Dyn中兩者共同占用4個字節的原因:


Elf32_Dyn的結構如下圖:




在Elf32_Dyn中,存在d_tag、d_val與d_ptr,三者均為4字節的數據,其中后兩者在一個聯合體中,故可將Elf32_Dyn理解成包含兩個元素:4字節的d_tag,4字節的聯合體。d_tag用來描述每個具體段的類型。當d_tag描述的是“區域”,則聯合體中的d_ptr有意義且d_val無意義,即可視為聯合體此時等于d_ptr,如d_tag等于DT_HASH,則描述的是.hash的“區域”,此時d_ptr指向.hash,d_val無意義;若d_tag描述的是區域的“大小”,則聯合體中的d_val有意義且d_ptr無意義,即可視為聯合體此時等于d_val,如d_tag等于DT_STRSZ,則描述的是字符串表的“大小”,此時d_val表示了字符串表的具體大小,二d_ptr此時無意義。


換言之:針對具體的d_tag時,d_val與d_prt不共存。故,在用java解析Elf32_Dyn類型數據時,可用下圖方法解析,待使用時再選擇d_ptr或d_val:





動態符號表的st_value需要減一才能作為funcOffset的原因



有兩位大佬進行了下答疑:“thumb指令模式函數真實的調用地址為真實地址減一。實際上,在運行時原函數真實地址加1,實際是一個標志位,表明程序要從ARM狀態跳轉到Thumb狀態,此時CPSR寄存器T位會從0變成1,代表arm變成thumb.故逆推時,st_value需要減一才是真實調用地址。”“arm指令blx,bx之類得跳轉得時候,會根據目標地址最低位是否是1切換到thumb模式。也就是如果一個函數是以thumb模式編譯得,那么調用得時候要地址加1.基本上現在安卓系統系統,armv7指令,系統庫的導出函數基本上都是thumb的.其實還有一點對齊原則。arm或thumb指令總數2字節或者4字節對齊的。你要知道,這些指令在內存中總是2字節對齊的(內存分配起始點總是頁對齊的,編譯的so也保證指令相對模塊起始位置對齊),也就是最低位不可能為1。所以最低位可以充當標志位。實際上pc取指令的,總是忽略掉最低位。所以那個加1.準確的說是或運算。”


3.1.3、 實踐

1、在Andorid Studio里新建soShell項目,按照本文檔上面的NDK開發步驟:


(1)在mainActivity中load 待生成的so文件并聲明且使用目標函數showMessage;


(2)對mainActivity使用javah工具,來生成.h文件,便于獲取目標函數showMessage的簽名;


(3)新建.cpp文件,編寫showMessage函數與解密函數decrypt_soShell,并為showMessage指定屬性(即在函數聲明時附上__attribute__((section(".mytext")))),為decrypt_soShell函數指定屬性(即在函數聲明時附上__attribute(constructor)))。


(4)新建并填寫CMakeLists.txt文件;


(5)在build.gradle文件中,設置so文件生成的abi為armV7,并設置cmake的路徑。


(6)編譯,則在Android Studio該項目的我們預設的路徑下將生成libsoShellDemo.so文件。




2、新建一個Andorid Studio項目作為加密的腳本,(可以直接在以前的shellTool項目里進行增加,反正目的都是加固),完成以下步驟:


(1)將soShell項目生成的libsoShellDemo.so文件放置到shellTool項目的Assets目錄下且重命名為func_origin.so文件,并以二進制的形式進行讀取;


(2)新建ElfType32.java,該文件內容可百度,該文件用于將二進制數據解析成so文件格式;


(3)新建encryptSoSeciton.java文件,該文件以二進制形式讀取,并調用ElfType32來進行解析,根據目標段名稱.mytext來獲取到該段的偏移和大小,對該段數據進行翻轉(即ABC變成CBA),并將偏移放置在so文件的e_flags字段,將大小放在so文件的e_entry字段。保存新的so文件數據。


(4)編譯,運行,獲得新的so文件,func_encrypt.so文件。




3、返回soShell項目中,將該項目從ndk開發轉換為普通android項目:


(1)在libs目錄下,新建armeabi-v7a文件夾,并將func_encrypt.so文件放置在該目錄下,重命名為libsoShellDemo.so文件;


(2)刪除jniLibs目錄;


(3)在build.gradle文件中,注釋掉“設置cmake的路徑,設置so文件生成的abi”部分,新增jniLibs.srcDirs。


(4)編譯,運行,應用成功運行,且輸出了目標日志


  




4、對比so文件加密前后用010Editor查看兩者對比





用IDA查看兩者對比:


(加密前)




IDA可正常打開加密后的文件:




3.3、兩者比較


加固so文件的section時:




加固so文件的函數時:


     

4、3者比較




在java層為.apk文件進行加固:





在native層為.dex文件進行加固:


  


加固so文件:




(自認為我用excel表格畫的幾張圖已經挺清晰的了,所以就不多做解釋啦~)

免費評分

參與人數 68吾愛幣 +63 熱心值 +64 收起 理由
Dragoon + 1 + 1 熱心回復!
貳拾 + 1 + 1 用心討論,共獲提升!
gongcsxue + 1 + 1 我很贊同!
lvyiwuhen + 1 熱心回復!
juanjuan + 1 + 1 [email protected]
JTC + 1 + 1 [email protected]
夏天yz + 1 我很贊同!
spadesa + 1 熱心回復!
bricher9988 + 1 + 1 [email protected]
呂sir + 1 + 1 我很贊同!
TOP洛夫克拉夫特 + 1 + 1 [email protected]
more路窮途 + 1 [email protected]
ttao88 + 1 + 1 [email protected]
findus123 + 1 + 1 我很贊同!
不思量自難忘 + 1 + 1 [email protected]
wintenrsun + 1 用心討論,共獲提升!
小小小英雄啦 + 1 + 1 [email protected]
kwan2003 + 1 + 1 用心討論,共獲提升!
wxue + 1 + 1 [email protected]
18889646651 + 1 + 1 [email protected]
linwenqing + 1 + 1 [email protected]
soyiC + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
紫圣 + 1 + 1 [email protected]
zuijyu + 1 [email protected]
乄情殤乄 + 1 + 1 [email protected]
Dabingpeng + 1 我很贊同!
西颯 + 1 [email protected]
紅燒排骨 + 1 我很贊同!
laohucai + 1 + 1 [email protected]
WUJH6699 + 1 + 1 用心討論,共獲提升!
a48602 + 1 + 1 [email protected]
hexio + 1 + 1 用心討論,共獲提升!
hiderl + 1 + 1 我很贊同!
zls黑戰魔 + 1 + 1 [email protected]
BY丶顯示 + 2 + 1 用心討論,共獲提升!
wwwmirage + 1 + 1 [email protected]
z50888 + 1 + 1 鼓勵轉貼優秀軟件安全工具和文檔!
xianyucoder + 1 + 1 [email protected]
B6B6B6 + 1 + 1 [email protected]
ihavebeenno + 1 + 1 太感謝了,正想研究殼呢。
QGZZ + 1 + 1 [email protected]
hafeng45 + 1 + 1 我很贊同!
sy888 + 1 [email protected]
Richor + 1 + 1 我很贊同!
xiong_online + 1 + 1 用心討論,共獲提升!
gh饕餮 + 1 + 1 用心討論,共獲提升!
一只大鯨魚 + 1 + 1 用心討論,共獲提升!
daniel7785 + 1 用心討論,共獲提升!
編程破解員 + 1 + 1 [email protected]
hiodis + 1 + 1 我很贊同!
gaosld + 1 + 1 用心討論,共獲提升!
薛-藍狐 + 1 + 1 我很贊同!
onething + 1 + 1 熱心回復!
wangyut85 + 1 + 1 [email protected]
wapj(●—●) + 1 + 1 謝謝分享
Li520pj + 1 [email protected]
DDDangerous + 1 + 1 用心討論,共獲提升!
h1825052587 + 1 + 1 用心討論,共獲提升!
夜步城 + 1 + 1 好厲害,支持,學習
LeiSir + 1 + 1 [email protected]
abigsun + 1 + 1 用心討論,共獲提升!
201 + 3 + 1 歡迎分析討論交流,吾愛破解論壇有你更精彩!
陳世界 + 1 + 1 我很贊同!
匠心獨具 + 1 + 1 用心討論,共獲提升!
紅哥 + 1 + 1 用心討論,共獲提升!
YuniNan0 + 1 我很贊同!
夏雨微涼 + 2 + 1 順利畢業
獨行風云 + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!

查看全部評分

發帖前要善用論壇搜索功能,那里可能會有你要找的答案或者已經有人發布過相同內容了,請勿重復發帖。

推薦
 樓主| 順利畢業 發表于 2019-10-29 11:21 <
xixicoco 發表于 2019-10-26 21:06
樓主用心貼,能一并提供研究的apk等資料就好了@@順利畢業

apk的話,其實在姜維的帖子里都有鏈接。我雖然有所改動,但是基本上是根據姜維帖子里來進行學習的。所以你想要apk的話,建議直接看姜維的帖子,里面有很詳細的介紹。都是些良心貼。
推薦
 樓主| 順利畢業 發表于 2019-11-1 17:02 <
axc156123 發表于 2019-11-1 16:01
感謝樓主分享  后期還會更新嗎?

不一定。我也還在學習中,如果學習過程中沒有值得分享的東西就不更新啦。
沙發
YuniNan0 發表于 2019-10-26 14:46
3#
一人之下123456 發表于 2019-10-26 14:59
感謝分享的心得體會。
4#
627853691 發表于 2019-10-26 16:43
感謝分享的心得體會。
5#
漠北孤狼 發表于 2019-10-26 16:48
感謝樓主分享。資料很有學習價值。
6#
adonis 發表于 2019-10-26 17:02
很有用的資料 收藏學習~~
7#
摯愛可可 發表于 2019-10-26 17:29
很認真,很有用,很到位
8#
cn01574978 發表于 2019-10-26 17:30
果斷收藏,不知道要學習多久啊?
9#
BlueTears_ 發表于 2019-10-26 18:43
感謝樓主分享
10#
夢幻妖精 發表于 2019-10-26 18:44
超級干貨!收藏學習了!
您需要登錄后才可以回帖 登錄 | 注冊[Register]

本版積分規則 警告:禁止回復與主題無關內容,違者重罰!

快速回復 收藏帖子 返回列表 搜索

RSS訂閱|小黑屋|聯系我們|吾愛破解 - LCG - LSG ( 京ICP備16042023號 | 京公網安備 11010502030087號 )

GMT+8, 2019-12-11 13:00

Powered by Discuz!

© 2001-2017 Comsenz Inc.

快速回復 返回頂部 返回列表
3d开机号今天