以下是我看Pixy中一些程式的記錄,主要是為了之後能夠比較快速的理解程式的思路,記錄下來,要不然那麼多東西光靠腦子怎麼行。有的地方可能有所紕漏,我自己看著可能都覺得不太好。
ProgramConverter
- convert()
首先利用PhpParser建立一棵parseTree,但是這棵tree只在這裡使用而已。利用這棵tree、輸入檔案以及ProgramConverter對象本身建立TacConverter對象baseTac,然後調用baseTac.convert()。這個baseTac即是可以通過本對象返回的TacConverter屬性。然後通過baseTac.getIncludeNodes()獲得需要處理的被包含檔案。
進入while迴圈,前提是函數內局部變數goOn為true和沒有“-w”參數。該while迴圈命名為(w1*),設定goOn=false。如果需要處理的包含檔案鏈表不為空白,進入第二個while迴圈(w2*)。通過for迴圈將所有的CfgNodeInclude結點單獨提取出來,如果這個node是需要跳過的(skip),就取下一個node;如果不是直接通過“常量”包含檔案的話,就繼續取下一個,這個展示不處理;如果以上兩條都不滿足,調用include()方法,返回一個內部枚舉類型IncStatus,重設將要處理鏈表,繼續w2*。使用baseTac處理一下之後,如果不存在non-literal includes就跳出w1*。否則,開始處理non-literal includes。(253:this.baseTac.backpatch();)。使用baseTac中得到的所有方法,包括使用者方法和Main方法計算(259 - 260)。建立AliasAnalysis對象,並以之作為參數建立LiteralAnalysis對象,調用analyze()開始分析。然後,擷取包含結點設定為將要處理的包含檔案鏈表,進入for迴圈,逐個檢測,如果已經檢測過,跳過,否則即為non-literal的包含文名,按條件分別處理。在通過this.include()方法的傳回值決定for迴圈的去向。再通過this.baseTac.assignFunctions(),然後重新設定需要處理的包含鏈表,繼續w1*。
將之前得到的可能會有用的包含檔案清單中實際沒有用的都去掉。並將literalAnalysis置為null,以節省記憶體。如果需要使用AliasAnalysis,作出相應處理(361 - 364行),否則,利用baseTac調用replaceGlobals(),將所有函數的局部變數使用相應的全域變數替換,進行type分析,與前邊的literal過程分析差不多。然後將functions轉換為CfgNode,然後輸出統計資訊。釋放資源,調用baseTac.addSuperGlobalElements()添加全域元素,將節點倒序排列。
- include()
得到應該包含的檔案,如果檔案不存在,返回NOTFOUND。否則,在allFiles中添加該檔案,並將包含與被包含添加到includeGraph中,如果添加成功,對被包含的檔案建立parseTree,並建立TacConverter對象,但是這裡的對象都只是用來檢測裡邊的include,並將這些CfgNodeInclude添加進從convert()傳過來的weComeAfterwards中,以待處理。這樣將goOn=true,迴圈檢測。
CfgEdge
inEdges對於CfgNodeIf來說,是指判斷條件,而outEdges是結果。對於其他的node有點說不清楚。
InterAnalysis
從這個程式看,將被掃描檔案分析之後,得到的主要是TacConverter,由他得到TacFunction,然後再得到CfgNode,一個CfgNode對應一個Context,二者共同組成InteWorkListElemnt。在InterAnalysisInfo中則是每一個Cfgode對應一個InterAnalysisNode,在這個node中,由先前與剛才CfgNode對應的Context對應一個LatticeElement,而Latticelement則分別存放相應的資訊。如AliasLatticelement存放MustAliases和MayAliases,而DepLatticeElement則存放TacPlace、DepSet、Vatiable等。
對於test.php來說,通過initTransferFunction()時,在ProgramConverter.convert()中產生了TransferFunctionId,而在checker.analyzeTaint()中則還得到了ConpositeTransferFunction。這時,每個InterAnalysisNode所包含的TransferFunction是確定了的執行個體,因而下邊的transfer方法調用時會調用相應的執行個體的方法。對於本例,由於TransferFunctionId.transfer()返回的是傳入的參數本身,故調用transfer之後得到的outValue與inValue是一樣的。僅出現在當analyze()中node為後便三種的時候,第一種沒有使用outValue,第二種直接使outValue = inValue。
- initGeneric()
後邊的interAnalysisInfo()怎麼就有長度了,沒搞清楚。通過initTransferFunction()好像能夠將genericAnalysisInfo添加資料,實驗中是23個。但是 interAnalysisInfo也是23個。因該是這樣的,二者指向同一個記憶體位址,對genericAnalysisInfo新增內容,就使得interAnalysisInfo也有了同樣的內容。
- analyze ()
這個方法主要是針對不同的node採取不同的措施。主要分為了5類node
- CfgNodeCall
- CfgNodeExit
- CfgNodeIf
- CfgNodeCallRet
- 其他
通過inValue轉換得到outValue,然後得到node的所有outgoing的邊(outEdges),將每條邊的終點節點即node的繼承者successor得到,然後通過當前的context和剛得到的outValue、successor共同增殖(propagete),向analysisNode設定新的 PhiValue,並向workList中添加InterWorkListElement。這裡我理解propagate是產生一個InterWorkListElement,因為在analyze中使用的是它。
在analyze()剛開始的時候,workList中只有一個元素InterWorkListElement(this.mainHead, this.mainContext)。
analyze()中通過analysisNode獲得的LatticeElement由test.php第一次在ProgramConverter.convert()中時都是TypeLatticeElement,而在Checker中analyzeTaint()時則全是DepLatticeElement。通過調用dump方法可知,前幾個的結果即placeToDep為空白,而後便則列印出整個Map。
- propagate(Context context, LatticeElement value, CfgNode target)
在analyze()中多數情況下調用都是(當前context,outValue,successor)
通過target得到InterAnalysisInfo中對應的analysisNode,不為null的話,通過context得到target的oldPhiValue,如果oldPhiValue=null,則將其設為所有LatticElementd的初始值,實際上也是null。如果value==oldPhiValue,則說明值沒有改變,可以返回了。否則,在lattice中使用value和oldPhiValue計算一個newPhiValue,如果這個newPhiValue與oldPhiValue不同,則將其設定為target的Phivalue,並在workList中添加一個InterWorkListElement以便analyze()中繼續分析。
DepClient
- collectSinks()
對於test0225.php而言,對XSS檢測得到的functions數目為2,但是sinks數目為5;對SQL檢測functions數目為2,sinks數目為1。
首先通過depAnalysis執行個體得到所有的TacFunction,然後對於每個TacFunction獲得Cfg之後對CfgNode排序然後逐個檢測。
得到的TacFunction有兩個分別為:_main和foo,裡邊包含的CfgNode分別是21個和3個。而對於XSS檢測有5個sink,均為echo,這裡不管是否會產生XSS,均作為sink返回。
- findDangerousUninit(DepGraph relevant)
首先找出relevant中的uninit nodes,結果顯示在上一個方法中XSS的5個sink中,後兩個返回結果都為空白。對於不為空白的uninitNode,則是找出其父節點Predecessor,如果父結點不為1個,則拋出異常,否則取出這一個父結點,研究兩種情況:
- DepGraphOpNode 直接認定為evil function返回。
- DepGraphNormalNode 調用initiallyTainted()方法查看傳回值,如果為ALWAYS或者IFRG,則認為是evil function,返回。
- initiallyTainted(TacPlace place)
這裡place考慮三種情況:
- Constant(常量) 這種情況下,直接就認為不可能是tainted,返回NEVER
- Variable(變數) 又分兩種情況
- superglobals 在某些特定情況下認為是harmless,其餘都是可能有害的。詳見DepClient.java
- non-superglobals 同上。但是某些跟命令列“-g”選項有關。
- others 都認為是有害的。
TacConverter
- start()
通過傳入的PhpTree的root建立起兩個CfgNode,作為Cfg的root和exit,然後將tree中的各個node串連起來,將phptree轉換成Cfg。
至於上文中提到的test0225.php,檔案如下:
<?php
function foo ()
{
$var = $_GET[''evil''];
return $var;
}
//$a = $_GET[''evil''];
$b = foo ();
//$a=5;
//echo $a;
echo $b;
// $a is 6 here
echo $_GET[''a''];
echo $e;
$x = explode(''_'', $get);
mysql_query($x[0]);
if (true) echo "ABCD";
if ($a == ''a'' && $c == ''d'') echo "dfg";
?>
<