問題背景
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 成功率 工作量 開發者
文章來源: