<pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
    <pre id="vvttv"></pre>

      <p id="vvttv"></p>

          <p id="vvttv"></p>

                <p id="vvttv"></p>

                <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                  <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                    <p id="vvttv"></p>

                    原文地址:http://drops.wooyun.org/tips/9335

                    0x00 前言


                    之前對Android的兩個運行時的源碼做了一些研究,又加上如火如荼的Android加固服務的興起,便產生了打造一個用于脫殼的運行時,于是便有了DexHunter的誕生(源碼:https://github.com/zyq8709/DexHunter/)。今天,我就通過這篇小文聊聊我的一些簡單的思路,供大家參考和討論。

                    0x02 相關機制


                    首先,先來看一看Android運行時的一些相關機制,看看我們來怎么搞。

                    首當其沖,要脫殼少不了研究一下Dex文件的格式,這一點Android的官方文檔寫的已經很清晰了,我這里就簡單再提一下。整個結構便如圖1所示:

                    圖1 Dex文件結構

                    其實就是分區段存儲不同的內容,在頭部里有指向各個區段起始的偏移值。當然我們最關心的就是class_defs和data這兩個段了。

                    class_defs包含了所有的類,用class_def_item來描述。圖2是對class_def_item展開的一個示意圖:

                    圖2 class_def_item結構

                    每個class_def_item指向一個class_data_item,每個class_data_item 包含了一個class的數據,每個方法用encoded_method結構來描述,它又指向了一個code_item,這個里面就保存著一個方法的所有指令。

                    對于ART下,安裝后的dex文件會被編譯為oat文件,這個oat文件其實是一個ELF文件,圖3是它的一個結構:

                    圖3 OAT文件結構

                    其中可以看到oatdata指向的部分包含了原有的Dex文件,這個是我們的目標。當然oatexec指向了編譯后的ARM指令,但是對于我們暫時來說沒有什么卵用。

                    0x03 四個時機


                    為了脫殼,我們要建立一個概念,就是“時機”。對于非虛擬機殼,從內存中轉儲是一個最為有效和統用的技巧,那么就必須要找到一個時機,保證內存中的數據是完全正確的。

                    在Android中呢,便有這么四個時機:

                    1. 打開Dex文件

                      就是把APK中的dex文件提取并做cache,那么最終打開的其實是odex或oat文件;

                    2. 加載Class

                      運行時讀取存儲在Dex中的每個class,并用來填充一個生成的Class對象,其中包含了class的所有成員,這樣一個class才能被使用;圖4表示了ART和DVM下的Class對象的結構

                      圖4 Class的結構

                    3. 初始化Class

                      如果一個class有static塊,那么這個部分就會編譯為類的初始化器,具體看說就是方法,在class真正需要被使用的時候就會執行它,當然,殼就可以利用它來做許多事情;

                    4. 調用具體的方法

                      不用多說,就是根據生成的Class對象查找到具體的代碼指令并執行了。

                    0x04 兩種加載


                    好,那我們怎么做呢?很簡單,我們就從類的加載開始。

                    總的來說,有兩種可以加載類的方法,一個是顯示加載,主要用于反射,就是通過調用Class.forName()或ClassLoader.loadClass()方法來主動加載一個類;另一個是隱式加載,主要是通過創建第一個class的實例或在類產生前訪問靜態成員時發生。這些操作的背后在運行時中是有相應的函數來真正完成的。

                    在ART中:

                    顯式加載:

                    ClassLoader.loadClass 對應DexFile_defineClassNative

                    Class.forName 對應Class_classForName

                    隱式加載:

                    對應artAllocObjectFromCode

                    圖5表述了這個關系:

                    圖5 ART中的實現

                    在DVM中:

                    顯式加載:

                    ClassLoader.loadClass對應Dalvik_dalvik_system_DexFile_defineClassNative

                    Class.forName對應Dalvik_java_lang_Class_classForName

                    隱式加載:

                    對應dvmResolveClass

                    圖6是DVM中的實現表示:

                    圖6 DVM中的實現

                    0x05 開始修改


                    很清晰看到,我們找到了關鍵點,在ART中是DefineClass,DVM中是Dalvik_dalvik_system_DexFile_defineClassNative,我們就從這里動手,主要的修改就發生在這里。簡單地說就是主動地一次性加載并初始化所有的類。

                    這樣做是隱含了幾條原則的:

                    圖7就是DexHunter的一個工作流程:

                    圖7 DexHunter原理

                    下面就分這幾個步驟來說:

                    (1) 定位內存

                    對于之前提到的入口函數,都有一個參數表示在操作的文件。

                    ART中,這個參數是DexFile對象,其中有一個location_成員,是一個字符串,可以簡單的理解為此文件的路徑。那么DVM中是DexOrJar,相對的字符串成員是fileName。這下我們就好整了,只要我們指定了目標字符串,我們就可以從可能使用的眾多dex文件中找出我們想要的那個,而且方便的是,通過這兩個對象,我們還能很容易找到操作的文件在內存中的起始地址和長度。

                    (2) 主動加載并初始化

                    這個就是遍歷dex文件中class_defs區段里每一個class_def_item,并逐一加載和初始化,在ART里我們使用FindClass函數來加載類,EnsureInitialized進行初始化;在DVM中用dvmDefineClass加載,dvmIsClassInitialized 和dvmInitClass來初始化。

                    (3) 轉儲并自動修復

                    最后就是真正抓取dex了。把dex分為三部分:

                    我們把Part 1存在part1文件里,Part 3存在data文件中,Part 2先不要急。

                    現在我們要解析class_defs的東東了。不整代碼了,用文字簡單來說,就是模仿Android的過程,我們把每個class_data_item解碼為內存中的對象(有LEB128編碼),便于我們的修復。

                    下邊就要進行一些判斷看需不需要修復:

                    看class_def_item中的 class_data_off是不是在之前拿到的dex文件的內存范圍內,如果跑出去了,就需要把這個類的class_data_item給放到dex尾部去,修改class_def_item并保存。

                    比較解析出來的accessflag、codeoff和運行時生成的方法對象的accessflag、codeoff,如果不一致,以運行時中的為準,并修改保存。

                    同樣,檢查code_item_off是否出界了,一旦出界,把code_item收回來,繼續向尾部添加,并修改class_def_item的相關內容重新保存。

                    當然了,所謂放到尾部,只是先保證偏移值從尾部開始的,真正的內容先存在extra文件了。被修改過的class_defs段,就保存在classdef文件中了。

                    然后我們把四個文件重新拼起來,就得到原始的dex或odex了。

                    0x06 有趣的現象


                    最后聊一下我們看到的一些有趣的現象。

                    360基本上是把原始的dex加密存在了一個so中,加載之前解密。

                    阿里把一些class_data_item和code_item拆出去了,打開dex時會修復之間的關系。同時一些annotation_off是無效的的來防止靜態解析。

                    百度是把一些class_data_item拆走了,與阿里很像,同時它還會抹去dex文件的頭部;它也會選擇個別方法重新包裝,達到調用前還原,調用后抹去的效果。我們可以通過對DoInvoke (ART)和dvmMterp_invokeMethod (DVM)監控來獲取到相關代碼。

                    梆梆和愛加密與360的做法很像,梆梆把一堆read,write, mmap等libc函數hook了,防止讀取相關dex的區域,愛加密的字符串會變,但是只是文件名變目錄不變。

                    騰訊針對于被保護的類或方法造了一個假的class_data_item,不包含被保護的內容。真正的class_data_item會在運行的時候釋放并連接上去,但是code_item卻始終存在于dex文件里,它用無效數據填充annotation_off和debug_info_off來實現干擾反編譯。

                    0x07 參考


                    https://source.android.com/devices/tech/dalvik/dex-format.html
                    /libcore/libart/src/main/java/java/lang/ClassLoader.java
                    /libcore/libdvm/src/main/java/java/lang/ClassLoader.java
                    /libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java
                    /libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java
                    https://github.com/anestisb/oatdump_plus#dalvik-opcode-changes-in-art
                    

                      <pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
                      <pre id="vvttv"></pre>

                        <p id="vvttv"></p>

                            <p id="vvttv"></p>

                                  <p id="vvttv"></p>

                                  <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                                    <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                                      <p id="vvttv"></p>

                                      这里只有精品视频