jspacker壓縮及解壓縮研究(js eval)


 

起因:

在研究爬蟲的時候發現很多網站都出現了同一種方式的js混淆,並且名字都是pde.js,懷疑是使用了同一款混淆工具,所以研究一下。

這款工具叫JS Packer,並不是專門的混淆工具,而是一款js壓縮工具,其官網地址為: http://dean.edwards.name/packer/

支持兩種壓縮方式,一種是Shrink variables比較常規的壓縮方式,就是去掉一些空白符注釋之類的,另一種是Base62 encode,是一種比較適合用來壓縮內容單詞重復率高的壓縮方式。

 

壓縮示例

所有討論基於Base62 encode壓縮方式,輸入:

alter("hello, world");

輸出:

eval(function(p,a,c,k,e,r){e=String;if(!''.replace(/^/,String)){while(c--)r[c]=k[c]||c;k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('0("1, 2");',3,3,'alter|hello|world'.split('|'),0,{}))

格式化后:

eval(function (p, a, c, k, e, r) {
    e = String;
    if (!''.replace(/^/, String)) {
        while (c--) r[c] = k[c] || c;
        k = [function (e) {
            return r[e]
        }];
        e = function () {
            return '\\w+'
        };
        c = 1
    }
    ;
    while (c--) if (k[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]);
    return p
}('0("1, 2");', 3, 3, 'alter|hello|world'.split('|'), 0, {})) 

上面的代碼看着很唬人,其實原理很簡單,我們耐心分析下。

 

壓縮原理:

簡單來說就是將相同的單詞進行壓縮,具體為將所有單詞抽取出來作為一個詞典,然后將源代碼中表示單詞的地方改為引用詞典的下標,這樣的話當重復的單詞很多的時候壓縮效果就比較好,但是當重復的單詞比較少的時候這種方法有點得不償失。

帶入具體數據來具體分析,比如下面的代碼:

console.log("aaaaa");
console.log("aaaaa");
console.log("bbbb");

壓縮之后格式化:

eval(function(p, a, c, k, e, r) {
    e = String;
    if (!''.replace(/^/, String)) {
        while (c--) r[c] = k[c] || c;
        k = [function(e) {
            return r[e]
        }];
        e = function() {
            return '\\w+'
        };
        c = 1
    };
    while (c--) if (k[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]);
    return p
} ('0.1("2");0.1("2");0.1("3");', 4, 4, 'console|log|aaaaa|bbbb'.split('|'), 0, {}))

可以看到規律已經很明顯了,第一個 參數 '0.1("2");0.1("2");0.1("3");'中的數字對應着 'console|log|aaaaa|bbbb'.split('|') 中的下標,解壓的時候只需要再將數字下標還原為單詞即可。

 

下面是對解壓縮算法的一個簡單解讀:

// p 將原始內容中所有單詞替換為字典下標后的壓縮內容
// a 詞典大小,暫時用不到 
// c 詞典大小,在解壓時用來關聯壓縮內容和詞典
// k 詞典
// e 在解壓時,當replace第二個參數支持function時,為\\w+,否則為與下標對應的字符串
// r 當加速解壓時用來保存詞典
eval(function(p, a, c, k, e, r) {
    e = String;

    // 檢測當前的瀏覽器是否支持replace(regex, function),如果支持的話就能夠加快解壓速度
    // 如果不支持的話可以把這一塊直接忽略掉
    if (!''.replace(/^/, String)) {

        // 把被壓縮的單詞拷貝一份,因為k還有別的用處
        while (c--) r[c] = k[c] || c;

        // k[0]后面用來對每個匹配到的下標尋找替換字符串
        k = [function(e) {
            return r[e]
        }];

        // 用來分割原始內容
        e = function() {
            return '\\w+'
        };

        // 加速解壓的時候,相當於把while變成了if
        c = 1
    };

    // 使用詞典將壓縮后的下標代碼擴展,如果沒有上面的加速的話,c等於詞典單詞數,要一個一個替換了
    // 如果支持replace(string, function)的話,會將匹配到的每一個數字都傳遞給k[c]來得到其應該被替換為的字符串
    while (c--) if (k[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), k[c]);

    // 完成解壓
    return p
} ('0.1("2");0.1("2");0.1("3");', 4, 4, 'console|log|aaaaa|bbbb'.split('|'), 0, {}))

 

解壓縮小工具

我把這種 eval(blablabla…) 形式的統稱為eval壓縮,並針對此寫了個一個簡單的解壓小工具。

思路:

1. 既然這種肯定是要在網頁上執行的,那么只需要模擬執行就可以了。

2. 可能不只有一層eval,所以應該能夠方便的多次連續eval。

 

html代碼如下:

<html>
<head>
    <meta charset="UTF-8">
    <title>JavaScript eval</title>
</head>
<body>

<textarea id="eval_code" cols="100" rows="30" placeholder="粘貼eval代碼"></textarea>
<button onclick="executeEval()">EVAL</button>

<script type="text/javascript">
    function executeEval(){
        let evalCodeElt = document.getElementById("eval_code");
        let evalCode = evalCodeElt.value;
        // 如果不把開頭的eval去掉的話直接執行會被執行兩遍
         evalCode = evalCode.replace(/^eval/, "");
        try{
            evalCodeElt.value = eval(evalCode);
        }catch (e) {
            alert("執行報錯了:" + e);
        }
    }
</script>
</body>
</html>

效果如下:

1 

參考資料:

1. /packer/

2. JS混淆加密壓縮 - 站長工具

3. JavaScript在線壓縮打包/解壓縮工具 - JavaScript Packer - 志文工作室

4. What does “if (!''.replace(/^/, String))” do?

5. JavaScript replace() 方法


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM