<dd id="sga4y"><optgroup id="sga4y"></optgroup></dd>
<nav id="sga4y"><code id="sga4y"></code></nav>
  • <optgroup id="sga4y"></optgroup>

    原文地址: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
    

    97av