問題背景
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程序的功能,從而簡化混淆代碼,使得易于分析)實現難度較大,因此是一個很不錯的工作。但在優化執行效率方面也還有許多的提升空間,比如,在無參數分析函數的設置下,該工具會將所有可能的輸入都執行,因此執行的時間可能會很長。從污點分析技術中借鑒剪枝的技術可能是一個有前景的優化方向。
看文倉www.kanwencang.com網友整理上傳,為您提供最全的知識大全,期待您的分享,轉載請注明出處。
歡迎轉載:http://www.kanwencang.com/bangong/20170212/100584.html
文章列表