Soot I: 基本瞭解

Static analysis指在不對program進行運行的情況下,對其行爲進行分析。搞compiler的人用之於優化,搞安全的用於做taint analysis。對於Java有兩大開源的static analysis 框架,Soot和WALA,前者由McGill大學維護,後者是IBM。最新的Soot開始支持對Andoird 代碼分析,包括static taint analysis。

從這篇開始,我們將由淺入深的對Soot進行了解,使用,甚至擴展。在這裏默認讀者對static analysis理論(lattice, 不動點, may & must等),Java source code和bytecode語法有基本瞭解。將主要依據Soot 官網提供的資料作爲依據,特別是那本『生存手冊』(點擊打開鏈接)。

好了,現在我們開始。

Soot包括四種IR(中間碼-表示形式),分別代表了四種對Java Sourcode或者bytecode的不同程度的抽象。


Baf - 基於棧的bytecode

傳統的JVM bytebode是基於棧操作的指令集(Dalvik 基於寄存器操作),與之對應的Baf同樣如此。那Baf抽象了什麼呢?兩個,忽略了constant pool(常量池)和bytecode指令中的type依賴。在bytecode中對不同保留類型,如int和float,的同一操作(如add),有不同的指令。這是因爲在計算機中整形和浮點型的表達方式是不一樣的,在底層實現時無法讓兩個操作符分屬於這兩種不同類型,也就是需要不同的指令對應不同的數據類型的操作。我們做分析時不用在意它到底調用的什麼類型的指令,不對int還是float做細緻區分,只要知道它是個數且知道是對這數的什麼樣的操作就行了。Baf因此用於在bytecode層面上的分析。

Jimple - typed, 3-addresses, statement based。

Jimple是Soot的核心,是四種IR中最重要的。Soot能直接創建Jimple碼,也可由Java sourcecode或者bytecode轉化翻譯而來。bytecode會被翻譯成untyped Jimple,再通過type inference 方法對局部變量加上類型。翻譯的重要一步是對表達式作線性化使得每個statement只能最多refernce 3個局部變量或者常量(沒懂。。)。相對於bytecode的200多種指令,Jimple只有15條,分別對應着核心指令的 NopStmt, IdentityStmt, AssignStmt;函數內控制流指令的IfStmt, GotoStt, TableSwitchStmt和LookUpSwitchStmt,函數間控制流的InvoeStmt, ReturnStmt, ReturnVoidStmt, 監視器指令EnterMonitorStmt和ExitMonitorStmt,最後處理異常ThrowStmt和退出的RetStmt。
看段書中的例子。用

java -cp soot-trunk.jar soot.Main -f J Foo

讀入sootOutput文件夾中的Foo.Jimple。其所對應的Java 源碼爲,

public class Foo {
public static void main(String[] args) { Foo f = new Foo();
int a = 7;
int b = 14;
int x = (f.bar(21) + a) * b; }
public int bar(int n) { return n + 42; } }
Jimple爲
public static void main(java.lang.String[]) { java.lang.String[] r0;
Foo $r1, r2;
int i0, i1, i2, $i3, $i4;
r0 := @parameter0: java.lang.String[]; $r1 = new Foo;
specialinvoke $r1.<Foo: void <init>()>(); r2 = $r1;
i0 = 7;
i1 = 14;
// InvokeStmt
$i3 = virtualinvoke r2.<Foo: int bar()>(21); 
$i4 = $i3 + i0;
i2 = $i4 * i1;
return;
}
public int bar() { Foo r0;
int i0, $i1;
r0 := @this: Foo; // IdentityStmt
i0 := @parameter0: int; // IdentityStmt $i1 = i0 + 21; // AssignStmt
return \$i1; // ReturnStmt
}
可以看出Jimple是一種Java sourcecode和 bytecode的混合體。對於局部變量的聲明和賦值statement用的是Java,而控制流和函數調用採用的是bytecode。和反編譯Dalvik bytecode所得的smali類似,Jimple在每個method body前會把所有用到的局部變量和stack位置做出聲明(即Var -> Loc, 把變量映射到棧中的地址並在body中用地址替代變量名)。帶"$"的局部變量表示是stack中的位置而不是原java code中真正聲明過的局部變量,表示在java中不出現而bytecode中少不了的隱含變量如this, 儲存中間結果的變量等。反之不帶的即與原java code中局部變量相對應。
線性化過程把int x = (f.bar(21)) + a) * b拆成了三條statement:函數調用一條, $i4 = $i3 + i0和i2 = $i4 * i1;因此保證了每條stament至多隻帶3條地址。
i0 := @parameter0和r0 := @this作爲IdentityStmt分別代表對形參和this的複製。所有的局部變量都是帶類型的。
Jimple適用於絕大多數的不需要精確control flow或者SSA的靜態分析。

Shimple -- Static Single Assignment 版的Jimple
和Jimple基本一樣,只有兩點不同: SSA 和phi-node。SSA保證了每個局部變量都有一個靜態定義。
目前還沒有看到用SSA的可能,先暫時略過。

Grimp -- 更適合人讀的

  和Jimple類似,多了允許樹形表達和new指令。相比於Jimple,更貼近Java code,所以更適合人來讀。
public static void main(java.lang.String[]) { java.lang.String[] r0;
Foo r2;
int i0, i1, i2;
r0 := @parameter0: java.lang.String[]; r2 = new Foo();
i0 = 7;
i1 = 14;
i2 = (r2.<Foo: int bar(int)>(21) + i0) * i1;
return; }

可以看到樹形表達式沒有被linerazation, 對象的初始化濃縮到new中,一些臨時局部變量被省去了。Grimp更適用於做available expr(表達expr簡潔)和反編譯(適合人讀)。

介紹完Soot的基本IR們,我們來說說Soot的基本使用。command爲
java -cp soot-2.5.0.jar soot.Main -cp . -pp A B 

輸入的class有兩種 1. application class 即要被分析和翻譯的(在Soot裏叫transformation)class 2. Library class. 爲application class所引用,有助於分析但不會直接被分析的class。

關於具體的命令就不再這裏敷述了。

Soot的執行過程被分成了好幾大步,每一大步被稱爲一個pack。第一步是把輸入的bytecode (.class)或者.java 文件或者.jimple 翻譯成Jimple code。再把生成的Jimple作爲剩下packs的輸入。"函數中分析(intra-procedure analysis)"執行流程示意如下:

首字母s, j, b, g分別代表四種不同的IR。b代表body 創建,o 表示優化,a表標註(生成屬性),t表示用戶定義的transformation, p即packs,其中jtp和stp用戶可能更有興趣,因爲任何用戶定義的transformation (如從分析中得出對信息的標籤)可以被插入到這倆packs中因而作爲Soot執行過程的一部分。

"函數間分析(inter-procedure analysis)"執行流程如下

運行函數間分析需要指明'-w',表示Whole-program mode (全程序模式)。在此模式下Soot包含3個額外的packs:cg(調用圖生成),wjtp (whole jtb)和wjap (whole jap)。用-W還會引入wjop做全程序優化。與intra-procedure analysis不同在於這裏得出的結果對每個application class都有效。

java soot.Main -pl
查看所有可用的packs。

java soot.Main -ph PACK 

表示對指定PACK查看幫助。

 OPT:VAl 來對pack進行設置,下面語句表示關閉所有用戶定義的函數內tansformation。

java soot.Main -p jtp enabled:false MyClass 

Soot 所支持的靜態分析

Null pointer analysis (空指針分析):位於 jap pack中,包含兩部分, 空指針檢查和空指針着色。前者用於找到可能會觸發NullPointerException異常的指令,後者再對這些指令做出特殊顏色的標註(在Eclipse中)。command如下:

java soot.Main -xml-attributes -f J -p jap.npcolorer on MyClass 

將會生成一個Jimple文件,把這個文件用Eclipse打開,綠色的指令表絕對不是Null的,藍色表未知,紅色表絕對是Null的。



數組越界分析

位於jap pack下的jap.abc。使用命令如下

java soot.Main -xml-attributes -f J -p jap.abc on -p jap.abcadd-color-tags:true MyClass 


Liveliss Analysis 

命令如下 : 

java soot.Main -xml-attributes -f J -p jap.lvtagger on MyClass 


對Soot的擴展

在我們設計和實現了一個自己的分析後,要把它與Soot帶的分析相結合,這時就需要對Soot的Main class做修改。此處的擴展指的是在在保留其它Soot特性的同時,在Soot分析中加入我們自己設計的(中間)步驟。對於函數內分析我們加到jtp步驟中,函數間分析則加到wjtp。

public class MySootMainExtension {
public static void main(String[] args) { // Inject the analysis tagger into Soot PackManager.v().getPack("jtp").add(new
Transform("jpt.myanalysistagger", MyAnalysisTagger.instance()));
// Invoke soot.Main with arguments given
Main.main(args); }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章