3.1 Soot構建語法樹
Soot通過objectweb來進行字節碼構建成語法樹
首先scene會去查找一些基礎的JVM的類
- 基礎的對象Object, Boolean, Void, Integer, Long, Float等
- 字符串操作:String, StringBuffer的類
- Exception, Throwable,以及一些常見的Error類
- 線程相關的, Thread, Runnable
- 序列化,Serializable
- GC相關, Finalizer
- Lambda表達式,LambdaMetafactory
接着會去查找定義的初始化的類,通過SootResolver 構建SootClass, 生成AsmClassSource使用objectweb加載Class file,生成語法樹,同時加載方法生成語法樹AsmMethodSource。
3.2 Soot構建分析
Soot把每一個階段的分析都分爲Pack,把每個階段的小的子步驟成爲Transform,每一個階段都可以關閉或者打開,但分析的階段不可以調整順序.
3.2.1 核心過程的Packs
在Soot的核心過程分爲PP->CG->TP->OP->AP,Soot支持多IR分析,但在覈心過程中只支持Jimple, Shimple, 在Pack中可以插入自定義的Transform,這樣就可以在每一個分析階段加入自己的分析步驟,從而實現自定義分析的能力
3.2.1.1 JB Pack
任何分析首先要構建Soot的body對象,在前面步驟中的構建語法書的時候並沒有構建Body,所以在Pack分析過程中首先要構建Body,
在Soot裏只對Jimple的body進行了構建,Pack只對Jimple body進行分析
JB的Pack裏包含了如下的子Transform,Transform的順序不可調整
3.2.1.2 PP
Pre-processing Pack 是第一個分析的Pack,該Pack允許你自定義一些自己的transform在構建call graph之前。
在這裏Jimple,和Shimple的WJPP, WSPP有所不同,WJPP裏包含了
Constant reflective method invocation base transformer (wjpp.cimbt) |
3.2.1.3 CG
CG Call Graph,調用圖,調用圖是靜態層序分析的關鍵,方法調用圖包含着整個函數調用的關係圖。
3.2.1.3.1 Edge
函數調用邊,邊的兩頭是一邊是調用方,另一邊是調用的目標方法,同時也包含着前面一條邊和下面一條邊,通過鏈表結構構建了整個graph
3.2.1.3.2 Call Graph的結構
Call graph對象裏包含了所有的Edges的集合,同時也包含了了幾個關鍵Map
- src Map
- targetMap
- unitMap
這些Map的Key以SootMethod,unit 而value是Edge,爲了更快的找到SootMethod或者Unit對應的Edge
3.2.1.3.3 構建Call Graph
Call Graph是方法的調用圖,要畫出方法的調用圖是需要起始點的,也就是我們常說的EntryPoint
如何確定起始點呢?
a. 比較明顯的是Main Class裏的Main方法, 同時還要關注Main Class裏的靜態的初始化,包括父類的初始化
b. 我們通常會使用多線程的方法,如果只是跟蹤Main函數是跟蹤不到的,因爲Main函數裏只是會調用thread的start的方法,函數調用跟蹤就斷裂了,同時還包含了一些JVM自己的線程調用的Java的方法
addMethod(ret, "<java.lang.System: void initializeSystemClass()>"); addMethod(ret, "<java.lang.ThreadGroup: void <init>()>"); // addMethod( ret, "<java.lang.ThreadGroup: void // remove(java.lang.Thread)>"); addMethod(ret, "<java.lang.Thread: void exit()>"); addMethod(ret, "<java.lang.ThreadGroup: void uncaughtException(java.lang.Thread,java.lang.Throwable)>"); // addMethod( ret, "<java.lang.System: void // loadLibrary(java.lang.String)>"); addMethod(ret, "<java.lang.ClassLoader: void <init>()>"); addMethod(ret, "<java.lang.ClassLoader: java.lang.Class loadClassInternal(java.lang.String)>"); addMethod(ret, "<java.lang.ClassLoader: void checkPackageAccess(java.lang.Class,java.security.ProtectionDomain)>"); addMethod(ret, "<java.lang.ClassLoader: void addClass(java.lang.Class)>"); addMethod(ret, "<java.lang.ClassLoader: long findNative(java.lang.ClassLoader,java.lang.String)>"); addMethod(ret, "<java.security.PrivilegedActionException: void <init>(java.lang.Exception)>"); // addMethod( ret, "<java.lang.ref.Finalizer: void // register(java.lang.Object)>"); addMethod(ret, "<java.lang.ref.Finalizer: void runFinalizer()>"); addMethod(ret, "<java.lang.Thread: void <init>(java.lang.ThreadGroup,java.lang.Runnable)>"); addMethod(ret, "<java.lang.Thread: void <init>(java.lang.ThreadGroup,java.lang.String)>"); |
3.2.1.4 TP
Transformation Pack 也分成2種,WJTP, WSTP
其中WJTP還包含以下Transform
3.2.1.5 OP
Optimization Pack, WJOP, WSOP
3.2.1.6 AP
Annotation Pack, 只有Jimple提供了Pack, WJAP
3.2.2 Body Packs
Body Packs 基於Body 的Packs分析,對不同的IR有不同的Packs
和前面的核心Packs不同的是,這裏只是基於Body進行分析,每個Packs依然可以自己定義Transform,
public Transform(String phaseName, Transformer t) { this.DEBUG = Options.v().dump_body().contains(phaseName); this.phaseName = phaseName; this.t = t; } |
區別是使用不同的Transformer,在Body packs裏使用的是BodyTransformer
protected abstract void internalTransform(Body b, String phaseName, Map<String, String> options); |
internalTransform裏面包含了Body 對象
與核心的Pack分析不同的是,當分析到Body的Packs的時候,Soot會啓動多線程進行Pack的分析
接下來我們以Jimple爲例子舉例Body Packs
3.2.2.1 JTP
Jimple Transformation pack
3.2.2.2 JOP
Jimple Optimization Pack
3.2.2.3 JAP
Jimple Annotation Pack