1、控制流
- 控制流(Control Flow),指的是
- 控制流基本語法包括兩種:
-- 第一種:if / else 條件語句判斷;
if(條件){ };
else{ };
-- 第二種:while / switch / case 條件語句判斷;
while (option) {
switch (args){
case 1:console.log('case 1,xxx');
case 2:console.log('case 2,xxx');
case 3:console.log('case 3,xxx');
}
}
2. 控制流平坦化
- 控制流平坦化(Control Flow Flattening)是一種代碼混淆方式,其基本思想是讓所有的基本塊都有共同的前驅塊,而該前驅塊進行基本塊的分發,分發用switch語句,依賴於switch變量進行分發。
- 具體是先爲每個基本塊進行值編號,每個基本塊就是一個case塊,如果基本塊有後繼塊,就替換每個基本塊的後繼塊,新的後繼塊更新switch變量爲後繼塊的值,然後跳轉到switch開始分發處,初始switch變量爲入口entry塊的值。
- 現已有一些編譯器實現了控制流平坦化,Obfuscator-LLVM(簡稱 OLLVM)是一種基於 LLVM 框架的實現,這是目前比較主流的控制流平坦化實現方法;
- 例如,有下面的代碼,正常形態下我們可以很容易看懂其邏輯:
// 正常形態
function test(a){
var b = a;
b += 1;
b += 2;
b += 3;
b += 4;
return a + b
- 通過加密操作,得到下面的代碼,這樣看起來就會複雜得多,調試的時候如果此處下斷點,跳來跳去也會讓人覺得頭暈:
// 亂序形態
function test() {
for (var s = 1; void 0 !== s; ) {
var r = 3 & s
, c = s >> 2
, b = 3 & c;
switch (r) {
case 0:
!function() {
switch (b) {
case 0:
k += "do",
s = 8;
break;
case 1:
k += "Nt",
s = 2;
break;
case 2:
s = k ? 4 : 2;
break;
case 3:
s = void 0
}
}();
break;
case 1:
var k = "e";
s = k ? 0 : 8;
break;
case 2:
k += "nerap",
k = k.split("").reverse().join("");
var i = kr[k];
s = i ? 3 : 12;
break;
case 3:
a.push(175528201),
a.push(24898129191),
a.push(2),
a.push(0),
e(13, 2, -1);
var o = a.pop();
i[o](kr),
s = 12
}
}
}
- 上面這段代碼以 switch-case 的方式實現使用了控制流平展混淆。相比於常規的控制流平展混淆的改進之處在於,它的 switch-case 結構不止一層,本例中嵌套了一層,理論上可以多層嵌套;
-
針對不止一層switch-case結構的混淆變種,需要把原始方案作一個增強。我們採用樹形結構來表示原始的控制流結構。一層switch-case可看做只有兩層的樹。多層switch-case相當於在對應節點上的繼續延伸,從而形成三層、四層…的樹形結構;
3. AST 語法樹處理控制流平坦化
- 首先將上面的代碼複製到 https://astexplorer.net/,得到下圖:
-
通過分析,我們得出這段代碼包含兩側的 switch / case 嵌套,如下圖:
-
之後,我們查找 switch 條件,分析條件參數;
- 結合條件參數和執行邏輯邏輯,即可推導出最初的代碼;