【技術分享】Android程序反混淆利器——Simplify工具

分類:IT技術 時間:2017-01-24

問題背景

android 程序代碼混淆是 Android 開發者經常用來防止 app 被反編譯之後迅速被分析的常見手法。在沒有混淆的代碼中,被反編譯的 Android 程序極其容易被分析與逆向,分析利器 JEB 就是一個很好的工具。但是加了混淆之後,函數、變量的名稱將被毫無意義的字母替代,這將大大提高分析的難度。有的甚至會增加一些冗余代碼,比如下面的例子:

public void doBadStuff() {
    int x;
    int y;
    x = Integer.valueOf("5")
    y = Integer.valueOf("10")
    x = x * y;
    x += 5;
    x /= 3;
    hackYourPhoneLOL("backdoor");
    x = y;
    y = x + 10;
    y /= 2;
}

該函數的實際意圖其實就是執行 hackYourPhoneLOL( "backdoor" ); ,但是通過代碼混淆,增加很多冗余的代碼, 使得實際分析的時候工作量增加。對於代碼混淆,其實一直並沒有一個比較好的思路,也沒有萬能的工具來解混淆,最常見的方式就是用Android gradle proguard 去嘗試那些用Android gradle proguard混淆過的代碼,但是成功率極其低(比如對於用DexGuard混淆過的代碼)。

public void doBadStuff() {
    hackYourPhoneLOL("backdoor");
}

今天要介紹的工具,就是一個通用的Android程序反混淆工具,雖然在執行效率上不是很高,但是思路清晰,代碼風格好,值得深入學習與優化。下圖是在使用該工具前後,反編譯代碼的對比圖。

圖1:代碼解混淆之前

圖2:代碼解混淆之後

可以發現,在代碼解混淆之後,關鍵函數名稱、正則表達式等等字符串都能夠解析出來了,這樣的反編譯結果將非常適合分析人員進一步分析惡意代碼的功能。

這是github地址: https://github.com/CalebFenton/simplify

該工具的核心思路,就是自己模擬的Dalvik虛擬機執行的方式,將待反編譯的代碼執行一遍,獲知其功能後,將反編譯之後的代碼簡化成分析人員便於理解的形式。

安裝方式

由於該項目包含 Android 框架的子模塊,因此用以下兩種方式獲取代碼:

git clone --recursive https://github.com/CalebFenton/simplify.git
or
git submodule update --init --recursive

接著,使用gradlew編譯jar文件,當然前提是系統裏面安裝過了gradlew

./gradlew fatjar

在成功執行之後,Simplify.jar 應該出現在 simplify/build/libs/simplify.jar這裏,接著你可以使用以下命令行測試 Simplify.jar是否安裝成功

Java -jar simplify/build/libs/simplify.jar -it 'org/cf' simplify/obfuscated-example

註:安裝可能出現的問題

由於該工具還在前期開發階段,因此作者也提出該工具不是很穩定,因此可以嘗試使用下面的方式反復嘗試是否成功。

1. 首先,確定分析的smali文件包含不多的method或者classes的時候,可以使用-it命令。

2. 如果因此超過了最大的地址訪問長度、函數調用分析深度、最大的方法遍歷次數等,可以通過改變參數 --max-address-visits, --max-call-depth, --max-method-visits.來修正。

3. 如果實在不行,就是用-v參數來報告問題吧。

完整的使用命令在github中有,這裏不再贅述。

例子分析

這裏以 github 裏面的一個引導性的例子為切入,來介紹該工具是如何工作的。在介紹該工具如何工作之前,首先簡單介紹一下該項目裏面包含的模塊。

1. smalivm: 該模塊是Dalvik虛擬機的模擬器模塊,主要用來模塊Dalvik虛擬機的執行。它能夠根據輸入的smali文件返回所有可能的執行路徑以及對應的路徑得到的寄存器的值。該模擬器能夠在不知道一個函數參數的情況下進一步分析,它的方式就是將函數中存在分支的所有結果模擬執行一遍,在完全執行完畢之後,該工具會返回程序執行的每條路徑的寄存器的結果,從而便於simplify模塊進一步分析,簡化混淆的代碼。

2. simplify: 該模塊是解混淆的主要模塊,主要基於smalivm的分析結果,簡化混淆的反編譯代碼,得到易於理解的反編譯代碼。

接下來看看例子,該例子是java代碼的形式編寫的。

1. 首先需要新建一個模擬器,其中,SMALI_PATH是自己配置的待分析的smali文件的路徑,在這裏就不貼出待分析的樣例main.smali文件,太長了, github的地址

        VirtualMachineFactory vmFactory = new VirtualMachineFactory();
        vm = vmFactory.build(SMALI_PATH);

2. 接下來,是使用該工具提供的hook函數的功能將某些函數hook掉,由於有些函數會影響模擬器的外部輸出結果,比如system.out.println(),因此,需要將這些函數hook,以在保證函數正常運行的情況下,得到smalivm正常輸出的結果。

MethodEmulator.addMethod("Ljava/io/PrintStream;->println(Ljava/lang/String;)V", java_io_PrintStream_println.class);

3. 接下來,是執行待分析smali文件的main函數。

  vm.execute("Lorg/cf/demosmali/Main;->main([Ljava/lang/String;)V");

4. 最後,根據不同的函數參數輸入類型,選擇對應的函數分析方式分析Android程序的功能,此外,除了函數本身參數的類型,分析的方式額外的根據自己的需求選擇有參數還是無參數分析,此處的選擇可以不用局限於函數本身的參數類型。有參數的方式能夠加快分析速度,但是往往很多情況下,我們並不知道參數應該設置成什麽值,不恰當的值會導致

 executePrintParameter(42);
        executeParameterLogicWithUnknownParameter();
        executeParameterLogicWithKnownParameter(10);

註意 ,由於在無參數分析的情況下,該工具會窮舉所有可能的分支結構,因此需要將前面提到的三個參數的數值設置大一些,分析的時間也將響應的變長。

5. executePrintParameter和executeParameterLogicWithKnownParameter這兩個函數應該好理解,就是將輸入帶入進去分析了。接下分析一下executeParameterLogicWithUnknownParameter這個函數。首先是建立目標函數的簽名,該名稱直接根據待分析的目標函數的簽名而來:

String methodSignature = "Lorg/cf/demosmali/Main;->parameterLogic(I)I";

6. 使用smalivm執行在無參數情況設置下的函數,在這個樣例中,smalivm應當輸出兩個結果,這代表了smalivm執行了兩條路徑。

ExecutionGraph graph = vm.execute(methodSignature);

7. 獲取smalivm分析得到所有的分析路徑,不同路徑有不同的返回結果,因此能夠輸出所有的返回結果。getTerminatingRegisterConsensus這個函數可以很方便獲得所有返回寄存器的地址,從而得到輸出的結果。

HeapItem item = graph.getTerminatingRegisterConsensus(MethodState.ReturnRegister);
        System.out.println("With no context, returns an unknown integer: " + item);

總結

該工具 simplify 是我目前看到過的唯一一個通用的能夠用來解任何混淆的工具,該工具的思路較為巧妙,(即,通過模擬執行混淆 Android 程序的方式獲知 Android 程序的功能,從而簡化混淆代碼,使得易於分析)實現難度較大,因此是一個很不錯的工作。但在優化執行效率方面也還有許多的提升空間,比如,在無參數分析函數的設置下,該工具會將所有可能的輸入都執行,因此執行的時間可能會很長。從汙點分析技術中借鑒剪枝的技術可能是一個有前景的優化方向。

本文由 安全客 原創發布,如需轉載請註明來源及本文地址。

本文地址:http://bobao.360.cn/learning/detail/3445.html


Tags: Android public 成功率 工作量 開發者

文章來源:


ads
ads

相關文章
ads

相關文章

ad