轉自: http://www.freebuf.com/tools/76884.html
一、前言
對抗反編譯是指讓apk文件或者dex文件無法正常通過反編譯工具,而且有可能導致工具異常或者崩潰,如apktool、baksmali、dex2jar、JEB等等工具,如下圖dex2jar無法正常工作。
二、Dex文件格式解析
目前大多數android軟件的反編譯工具都是開源的,比如apktool、Dex2jar、baksamli,大家可以非常方便的從github下載並源閱讀代碼,然后找到可以利用的點,再在自己的軟件中加入干擾代碼,讓反編譯工具出現異常或者無法正常閱讀代碼。
接下來讓我們先來熟悉一下dex文件格式 ,一個dex文件由以下幾個部份組成:
1. Dex Header: Dex結構頭它指定了dex文件的一些基本屬性,並記錄了部份數據表在dex文件中的物理偏移。
2. String Table: 字符串表,存儲字符串的索引和個數
3. Type Table: 類型表,存儲類型的索引和個數
4. Proto Table: 函數元型表,存儲函數元型索引和個數
5. Field Table: 字段表,存儲字段索引和個數
6. Method Table: 方法表,存儲方法索引和個數
7. Class def Table:類定義表,存儲類定義索引和個數
8. Data Section: 存儲數據,由以上類型的索引查找
有興趣的可以直接翻看android的源碼或者參考以下鏈接:
http://www.netmite.com/android/mydroid/dalvik/docs/dex-format.html
既然要在代碼中添加干擾指令,接下的DexClassDef結構,肯定是要了解的非常清楚。
結構中包含了類的類型偏移、訪問標志、父類類型索引、接口偏移、注釋、靜態類型的偏移信息,整體結構圖定義如下:
struct DexClassDef { u4 classIdx; u4 accessFlags; u4 superclassIdx; u4 interfacesOff; u4 sourcefileIdx; u4 annotationsOff; u4 classDataOff; u4 staticValuesOff; }
DexClassDef結構字段詳細說明:
classIdx 字段是一個索引值,類的類型,做為下標索引在DexTypeID結構列表中查找
accessFlags 字段是類的訪問標志,以ACC_開頭的枚舉值
superclassIdx 字段是父類的類型,做為下標索引在DexTypeID結構列表中查找
interfacesOff 字段是接口類型,做為下標索引在DexTypeList結構列表中查找
sourcefileIdx 字段是源文件名,做為下標索引在DexTypeList結構列表中查找
annotationsOff 字段是注釋信息的偏移,指向DexAnnotationsDirectoryItem結構
classdataOff 字段是指向了DexClassData結構體的偏移
staticValuesOff 字段是指向DexEncodeArray結構體的偏移,記錄靜態數據的信息
DexClassData結構說明:
struct DexClassData { DexClassDataHeader header; DexField* staticFields; //靜態字段,DexField結構 DexField* instanceFields; //實例字段,DexField結構 DexMethod* directMethods; //直接方法,DexMethod結構 DexMethod* virtualMethods; //虛方法, DexMethod結構 }
DexClassData結構中的DexMethod類型描述了:方法的原型、名稱、訪問標志以及代碼數據塊,codeOff字段指向了一個DexCode結構,它描述了方法更詳細的信息以及方法中指令的內容。
DexMethod結構聲明如下
struct DexMethod { u4 methodIdx; //指向DexMethodId的索引 u4 accessFlags;//訪問標志 u4 codeOff; //指向dexCode的結構偏移 } struct DexCode { ushort registerSsize;//使用的寄存器的數目 ushort insSize; //傳入參數的數目 unshort outsSize; //調用其他方法時使用的寄存器個數 unshort triesSize; //try/catch異常塊個數 uint debugInfoOff; //調試信息的偏移 uint insnsSize; //指令集個數 ushort insns[1]; //指令集數組,變長數組 }
DexClassData樹型結構圖:
三、調試dex2jar工程
1. 將dex2jar源碼導入IntelliJ IDEA,導入后IntelliJ IDEA會自動查找對應 Grable並下載,需要比較長的時間等待
源碼地址: https://github.com/pxb1988/dex2jar
2. 選中Grable中的dex2jar/dex2jar/Tasks/other/antlr2java進行編譯
3. 點擊工具欄的 Project Structure, 然后選擇Modules -> d2j-smali -> build,然后點擊Excluded
4. 選擇 build/generated-sources/antlr, 然后點擊 Sources
5. 最后打開Excluded Gradle Task彈出對話框,運行clean distZip 命令
執行完Gradl clean distZip命令后會在\dex2jar-2.x\dex-tools\build\distributions目錄下生成 dex-tools-2.1-SNAPSHOT.zip,壓縮包內是編譯完后生成的jar文件和一些配置信息文件。大家請參考dex2jar.sh文件,它向我們說明需要使用lib目錄下的所有jar文件和入口函數com.googlecode.dex2jar.tools.Dex2jarCmd 才能將dex文件轉換成jar文件
6.將dex文件轉換成jar文件需要執行轉換命令
格式如下:
java -Xms512m -Xmx1024m -classpath .\lib\*.jar “com.googlecode.dex2jar.tools.Dex2jarCmd” classdex.dex
根據以上的命令,來配置調試參數,設置如下:
7.設置完成后,就可以開始調試
四、dex2jar反編譯失敗原因分析
1.首先我們從解包失敗的錯誤異常入手,定位崩潰處的代碼。
通過錯誤提示可以定位到dex2jar崩潰處的代碼,源碼位置如下:
dex-ir\src\main\java\com\googlecode\dex2jar\ir\TypeClass.java
2. 在崩潰的函數處下斷點,開始調試。
a)通過拋出的異常的說明“cant not merge I and Z”,以及整個調用堆棧。
我們可以看出造成這個異常的原因是函數調用時,實參的類型和形參類型不匹配引起的。因為在Java語法中,將實參是布爾類型傳遞給形參是int類型。這樣是不合法的,所以導致dex2jar在檢查的參數類型的時候失敗。
b)知道了崩潰的原因,那我們就需要確定它具體是使用怎樣的方法做到的,通過dex2jar生成的error異常信息詳細說明文件得知該dex文件中的每一個函數頭部都加了一句這樣的代碼Exit.b(Exit.a())請看Ida,現在大致能猜到混淆者所做的工作:
1.首先解析dex文件格式,定位到DexCode結構中的insns成員
2.向代碼中添加一個類成員對象名字叫Exit,然后添加代碼Exit.b(Exit.a())
3.明白了怎么添加干擾代碼,接下來分析一下dex2jar的工作流程,dex2jar轉換成jar文件這個過程中會驗證,函數調用時的形參和實參的合法性,請看下圖,解析到INVOKE_開頭的指令時,會開始解析返回值,供給參數,和實參等信息,具體邏輯和代碼大家有興趣可以詳細地研究一下dex2jar源碼。
4.merge負責的工作是參數類型的驗證,如果兩個類型相同,返回形參的類型,形參為UNKONW返回實參,實參為UNKONW返回形參,兩個類型都不相同,則匹配異常
5.最后在dex2jar里添加代碼,使dex文件能正確的被反編譯成功。這里給出一段可以成功讓dex2jar反編譯的出jar文件的代碼(其實方法有很多)
下圖是成功反編譯的jar文件