1. 程式人生 > >從Android執行時出發,打造我們的脫殼神器

從Android執行時出發,打造我們的脫殼神器

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,我們就從這裡動手,主要的修改就發生在這裡。簡單地說就是主動地一次性載入並初始化所有的類。

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

  • 當類被載入時,dex中對應的部分必須有效;
  • 類初始化的時候,dex中的內容包括生成的Class物件是可以被修改的;
  • 只有在執行一個方法時,才要求code_item是有效的。

圖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: class_defs之前的內容
  • Part 2: class_defs段
  • Part 3: class_defs後邊的部分

我們把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

相關推薦

Android執行出發打造我們神器

0x00 前言 之前對Android的兩個執行時的原始碼做了一些研究,又加上如火如荼的Android加固服務的興起,便產生了打造一個用於脫殼的執行時,於是便有了DexHunter的誕生(原始碼:https://github.com/zyq8709/DexHunter/)。

#圖文詳解:實際和理論出發帶你瞭解Java中的多執行

這裡並沒有講什麼新東西,只是把多執行緒一些知識來個總結。大家懂得可以複習複習,還有些童鞋對多執行緒朦朧的可以拿這個做為入門~ 舉個栗子說明啥是多執行緒:玩遊戲,前面一堆怪,每個怪都是一個執行緒,你射了一槍,子彈飛出去了,這顆子彈也是一個執行緒。你開啟你的程序管理,看到你遊戲的後臺程序,這就是程序

關於Android 6.0+ Dangerous Permissions (執行許可權敏感許可權) 《實用篇》

1. 什麼是許可權? 許可權是一種安全機制。Android許可權主要用於限制應用程式內部,某些具有限制性的功能使用,以及應用程式之間的元件訪問。 (通俗來講,你想拍照就需要相機的許可權。沒有許可權,你就無法使用相機功能。)2.什麼又是敏感許可權? Android6.0之後,首先關注的就是許可權機制的變化,Go

android 執行生成dex檔案並裝載呼叫

android apk生成原理如下圖所示 從圖可看出是把java檔案通過java編譯工具編譯成class檔案,再通過dex工具把class檔案轉成dex檔案 因為好奇,從網上看到執行時生成class檔案,再轉成dex檔案,動態載入呼叫的寫法,和大家分享。 package

使用者互動場景出發歐瑞博MixPad要系統化定義智慧居住空間

  近兩年,智慧家居市場十分火爆,國內消費者對智慧家居產品的接受程度越來越高,不過,消費熱潮還集中在浪潮一般出現的各類智慧家居單品上。很難有一款產品能夠系統化地為消費者描繪全屋智慧家居解決方案,從而改變人們的居住空間。不過,歐瑞博近日推出的新產品MixPad,或許能夠從另一個角度

[Xcode10 實際操作]八、網路與多執行緒-(18)PerformSelector訊息處理方法:由執行系統負責去呼叫物件的指定方法

本文將演示PerformSelector訊息處理方法。在專案資料夾上點選滑鼠右鍵彈出檔案選單。【New File】->【Swift File】->【Next】->【Save As】:iOSApp.swift->【Create】現在開始編寫程式碼,建立Swift類【iOSApp.swif

Android 執行: DVM vs ART

在瞭解 Android 執行時之前,我們需要了解什麼是執行時環境以及一些基本概念,即 Java 虛擬機器(JVM)和 Dalvik 虛擬機器(DVM)的功能。 什麼是執行時? 簡單來說,執行時就是一個供作業系統使用的系統,它負責將你用高階語言(比如 Java)編

Android執行異常“Binary XML file line # : Error inflating class”

   在原生Android下編譯APK,編譯沒有問題,但是在執行的時候經常出現如標題所描述的異常,然後整個程式Crash掉......     我遇到該問題常常都是因為修改了資原始檔所引起,大致有以下幾種方式來解決:     1. 引用類名問題:自定義了一個View,將他

setContentView方法原始碼出發弄懂Activity的檢視是怎麼附屬在Window上的

前言 最近正在看Android開發藝術探索,看到了Window的建立過程中的Activity的建立過程,講到Activity的檢視是怎麼附屬在Window上的時候,書中分析了setContentView方法,但是說得比較簡略,一些方法也跟以前有了些改變,所以我

Android執行ART載入OAT檔案的過程分析

                        在前面一文中,我們介紹了Android執行時ART,它的核心是OAT檔案。OAT檔案是一種Android私有ELF檔案格式,它不僅包含有從DEX檔案翻譯而來的本地機器指令,還包含有原來的DEX檔案內容。這使得我們無需重新編譯原有的APK就可以讓它正常地在ART裡

java中最常見的幾種執行異常你get了嗎?

NullPointerException  (空指標異常) ClassCastException  (型別強制轉換異常) NumberFormatException  (數字格式異常) NegativeArraySizeException  (陣列大小為負數異常) SecurityException 

在Python進行進行執行出現編碼錯誤解決辦法如下。

問題: SyntaxError: Non-ASCII character '\xe6' in file D:/split_words/Split.py on line 6, but no encoding declared; see http://python.org/de

解決linux下Eclipse系列軟體執行閃爍CPU佔用高一段時間後卡死

軟體包括 xmind dbeaver truestudio eclipse 原因分析 在linux下,eclipse的圖形介面實現主要是GTK,用到了org.eclipse.equinox.launcher.gtk.linux.x86_64這個模組。 但是這個模組在預設執

Java 執行監控第 1 部分: Java 系統執行效能和可用性監控

簡介 當今的許多 Java 應用程式都依賴於一組複雜的分散式依賴關係和移動部件。很多外部因素都可能對應用程式的效能和可用性造成影響。這些影響基本上都無法完全消除或解決,且難以在預生成環境中準確模擬。Stuff happens。但是,您可以建立並維護一個全面的系統來監控應用程式的整個生態系

Android 執行資源替換----Runtime Resource Overlay

       先拋一個問題:現在有一個第三方應用,沒有程式碼,只有編譯好的apk,在不去改動這個apk的前提下,如果想改變這個應用中的一些資源顯示效果,比如改變一個button的文字,一個imagev

終於理解了什麼是c/c++執行以及libcmt msvcrt等內容

在各個版本的編譯器中,我們可以通過配置選項來設定程式使用的C和C++執行時庫的型別。如下圖(其他版本編譯器大同小異):MT選項:連結LIB版的C和C++執行庫。在連結時就會在將C和C++執行時庫整合到程式中成為程式中的程式碼,程式體積會變大。 MTd選項:LIB的除錯版。 M

Java 執行監控第 2 部分: 編譯後插裝和效能監控

通過擷取進行 Java 插裝 擷取 的基本前提是通過一個擷取構造和收集傳入的入站與出站呼叫資訊,對特定的呼叫模式進行轉換。一個基本的擷取程式的實現會: 獲取對入站呼叫請求的當前時間。 取回出站響應的當前時間。 將執行時間作為兩次度量的增量計算出來。 將呼叫的執行時間提交給應用程式

Android 執行apk失敗ClassNotFoundException

 Process: com.llvision.glass2.innerlive, PID: 16676                                                    java.lang.RuntimeException: Unable

Android6.0執行許可權拒絕了許可權還返回獲取成功

如題:禁用、詢問的許可權,居然還返回許可權獲取成功 推薦一個很好的專案:AndPermission 不用回來感謝了喂! 這個問題在專案中,一直存在, 主要是第三方廠商各種改,返回的狀態不正常

Visual Studio 2017 開啟谷歌瀏覽器除錯不成功其它瀏覽器除錯成功!出現不能連線到執行程序求解答!

Cannot connect to runtime process,timeout after 10000 ms-(reason:Cannot connect to the target: connect ECONNREFUSED 127.0.0.1:55236). 不能連