Javascript代码压缩、加密算法的破解分析及工具实现
典型代码如下:
 eval(function(E,I,A,D,J,K,L,H){function C(A){return A<62?String.fromCharCode(A+=A<26?65:A<52?71:-4):A<63?'_':A<64?'$':C(A>>6)+C(A&63)}while(A>0)K[C(D--)]=I[--A];function N(A){return K[A]==L[A]?A:K[A]}if(''.replace(/^/,String)){var M=E.match(J),B=M[0],F=E.split(J),G=0;if(E.indexOf(F[0]))F=[''].concat(F);do{H[A++]=F[G++];H[A++]=N(B)}while(B=M[G]);H[A++]=F[G]||'';return H.join('')}return E.replace(J,N)}('Bl Bm=Bn;Bo(Bl Bp=Bq;Bp<Bn;Bp++){    Br.Bs(Bm+Bp+"<Bt>");}','var|index|100|for|a|0|document|write|br'.split('|'),9,109,/[/w/$]+/g, {}, {}, []))
eval(function(E,I,A,D,J,K,L,H){function C(A){return A<62?String.fromCharCode(A+=A<26?65:A<52?71:-4):A<63?'_':A<64?'$':C(A>>6)+C(A&63)}while(A>0)K[C(D--)]=I[--A];function N(A){return K[A]==L[A]?A:K[A]}if(''.replace(/^/,String)){var M=E.match(J),B=M[0],F=E.split(J),G=0;if(E.indexOf(F[0]))F=[''].concat(F);do{H[A++]=F[G++];H[A++]=N(B)}while(B=M[G]);H[A++]=F[G]||'';return H.join('')}return E.replace(J,N)}('Bl Bm=Bn;Bo(Bl Bp=Bq;Bp<Bn;Bp++){    Br.Bs(Bm+Bp+"<Bt>");}','var|index|100|for|a|0|document|write|br'.split('|'),9,109,/[/w/$]+/g, {}, {}, []))
对于这类压缩的代码,无非是把js程序采用某种算法进行压缩,然后自行用提供的函数还原,采用eval(SCRIPT)的方式执行来完成调用,那么还原的方法就很简单了,那前面的eval(和后面的)去掉,然后显示出来就完成了,例如下面的页面就可以实现代码的还原:
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">2
 <HTML>
<HTML>3
 <HEAD>
<HEAD>4
 <TITLE> 代码还原 </TITLE>
    <TITLE> 代码还原 </TITLE>5
 <META NAME="Generator" CONTENT="EditPlus">
    <META NAME="Generator" CONTENT="EditPlus">6
 <META NAME="Author" CONTENT="">
    <META NAME="Author" CONTENT="">7
 <META NAME="Keywords" CONTENT="">
    <META NAME="Keywords" CONTENT="">8
 <META NAME="Description" CONTENT="">
    <META NAME="Description" CONTENT="">9
 </HEAD>
</HEAD>10

11
 <BODY>
<BODY>12
 <TEXTAREA NAME="tx1" ROWS="10" COLS="100"></TEXTAREA>
<TEXTAREA NAME="tx1" ROWS="10" COLS="100"></TEXTAREA>13
 <SCRIPT LANGUAGE="JavaScript">
<SCRIPT LANGUAGE="JavaScript">14
 document.all.tx1.value =function(E,I,A,D,J,K,L,H){function C(A){return A<62?String.fromCharCode(A+=A<26?65:A<52?71:-4):A<63?'_':A<64?'$':C(A>>6)+C(A&63)}while(A>0)K[C(D--)]=I[--A];function N(A){return K[A]==L[A]?A:K[A]}if(''.replace(/^/,String)){var M=E.match(J),B=M[0],F=E.split(J),G=0;if(E.indexOf(F[0]))F=[''].concat(F);do{H[A++]=F[G++];H[A++]=N(B)}while(B=M[G]);H[A++]=F[G]||'';return H.join('')}return E.replace(J,N)}('Bl Bm=Bn;Bo(Bl Bp=Bq;Bp<Bn;Bp++){    Br.Bs(Bm+Bp+"<Bt>");}','var|index|100|for|a|0|document|write|br'.split('|'),9,109,/[/w/$]+/g, {}, {}, [])
document.all.tx1.value =function(E,I,A,D,J,K,L,H){function C(A){return A<62?String.fromCharCode(A+=A<26?65:A<52?71:-4):A<63?'_':A<64?'$':C(A>>6)+C(A&63)}while(A>0)K[C(D--)]=I[--A];function N(A){return K[A]==L[A]?A:K[A]}if(''.replace(/^/,String)){var M=E.match(J),B=M[0],F=E.split(J),G=0;if(E.indexOf(F[0]))F=[''].concat(F);do{H[A++]=F[G++];H[A++]=N(B)}while(B=M[G]);H[A++]=F[G]||'';return H.join('')}return E.replace(J,N)}('Bl Bm=Bn;Bo(Bl Bp=Bq;Bp<Bn;Bp++){    Br.Bs(Bm+Bp+"<Bt>");}','var|index|100|for|a|0|document|write|br'.split('|'),9,109,/[/w/$]+/g, {}, {}, [])15
 </SCRIPT>
</SCRIPT>16
 </BODY>
</BODY>17
 </HTML>
</HTML>通过上面方式运行,就可以在文本框中看到代码了,实际的代码是:
var index=100;for(var a=0;a<100;a++){ document.write(index+a+"<br>");}
很简单,不是吗
二、算法研究
由于代码全部在一行中,不便于阅读,可以通过格式化软件格式化,本文这里使用Intellij IDEA格式化,代码如下
 eval(function(E, I, A, D, J, K, L, H) {
eval(function(E, I, A, D, J, K, L, H) {2
 function C(A) {
    function C(A) {3
 return A < 62 ? String.fromCharCode(A += A < 26 ? 65 : A < 52 ? 71 : -4) : A < 63 ? '_' : A < 64 ? '$' : C(A >> 6) + C(A & 63)
        return A < 62 ? String.fromCharCode(A += A < 26 ? 65 : A < 52 ? 71 : -4) : A < 63 ? '_' : A < 64 ? '$' : C(A >> 6) + C(A & 63)4
 }
    }5
 while (A > 0)K[C(D--)] = I[--A];
    while (A > 0)K[C(D--)] = I[--A];6
 function N(A) {
    function N(A) {7
 return K[A] == L[A] ? A : K[A]
        return K[A] == L[A] ? A : K[A]8
 }
    }9
 if (''.replace(/^/, String)) {
    if (''.replace(/^/, String)) {10
 var M = E.match(J),B = M[0],F = E.split(J),G = 0;
        var M = E.match(J),B = M[0],F = E.split(J),G = 0;11
 if (E.indexOf(F[0]))F = [''].concat(F);
        if (E.indexOf(F[0]))F = [''].concat(F);12
 do{
        do{13
 H[A++] = F[G++];
            H[A++] = F[G++];14
 H[A++] = N(B)
            H[A++] = N(B)15
 } while (B = M[G]);
        } while (B = M[G]);16
 H[A++] = F[G] || '';
        H[A++] = F[G] || '';17
 return H.join('')
        return H.join('')18
 }
    }19
 return E.replace(J, N)
    return E.replace(J, N)20
 }('Bl Bm=Bn;Bo(Bl Bp=Bq;Bp<Bn;Bp++){    Br.Bs(Bm+Bp+"<Bt>");}','var|index|100|for|a|0|document|write|br'.split('|'), 9, 109, /[/w/$]+/g, {}, {}, []))
}('Bl Bm=Bn;Bo(Bl Bp=Bq;Bp<Bn;Bp++){    Br.Bs(Bm+Bp+"<Bt>");}','var|index|100|for|a|0|document|write|br'.split('|'), 9, 109, /[/w/$]+/g, {}, {}, []))Step 1:首先我们可以看出这是一个函数定义与调用合并在一起的,因此可以如下分解:(不再考虑eval)
 //E:加密压缩后的script信息
//E:加密压缩后的script信息2
 //I:字符串数组,可以理解为解密需要字典
//I:字符串数组,可以理解为解密需要字典3
 //A:int 9
//A:int 9 4
 //D:int 109
//D:int 109 5
 //J:regexpr 正则表达式
//J:regexpr 正则表达式6
 //K:object
//K:object 7
 //L:object
//L:object8
 //H:array
//H:array9
 function decode(E, I, A, D, J, K, L, H) {
function decode(E, I, A, D, J, K, L, H) {10
 function C(A) {
    function C(A) {11
 return A < 62 ? String.fromCharCode(A += A < 26 ? 65 : A < 52 ? 71 : -4) : A < 63 ? '_' : A < 64 ? '$' : C(A >> 6) + C(A & 63)
        return A < 62 ? String.fromCharCode(A += A < 26 ? 65 : A < 52 ? 71 : -4) : A < 63 ? '_' : A < 64 ? '$' : C(A >> 6) + C(A & 63)12
 }
    }13
 while (A > 0)K[C(D--)] = I[--A];
    while (A > 0)K[C(D--)] = I[--A];14
 function N(A) {
    function N(A) {15
 return K[A] == L[A] ? A : K[A]
        return K[A] == L[A] ? A : K[A]16
 }
    }17
 if (''.replace(/^/, String)) {
    if (''.replace(/^/, String)) {18
 var M = E.match(J),B = M[0],F = E.split(J),G = 0;
        var M = E.match(J),B = M[0],F = E.split(J),G = 0;19
 if (E.indexOf(F[0]))F = [''].concat(F);
        if (E.indexOf(F[0]))F = [''].concat(F);20
 do{
        do{21
 H[A++] = F[G++];
            H[A++] = F[G++];22
 H[A++] = N(B)
            H[A++] = N(B)23
 } while (B = M[G]);
        } while (B = M[G]);24
 H[A++] = F[G] || '';
        H[A++] = F[G] || '';25
 return H.join('')
        return H.join('')26
 }
    }27
 return E.replace(J, N)
    return E.replace(J, N)28
 }
}29
 var decode_str=decode('Bl Bm=Bn;Bo(Bl Bp=Bq;Bp<Bn;Bp++){    Br.Bs(Bm+Bp+"<Bt>");}','var|index|100|for|a|0|document|write|br'.split('|'), 9, 109, /[/w/$]+/g, {}, {}, []));
var decode_str=decode('Bl Bm=Bn;Bo(Bl Bp=Bq;Bp<Bn;Bp++){    Br.Bs(Bm+Bp+"<Bt>");}','var|index|100|for|a|0|document|write|br'.split('|'), 9, 109, /[/w/$]+/g, {}, {}, []));Step 2:其中对于函数function C(A)采用多重3元表达式处理的方式,可以用if/else如下分解
 function C(A){
function C(A){2
 var res;
    var res;3
 if (A < 62) {
    if (A < 62) {4
 var r = null;
        var r = null;5
 if (A < 26) r = 65; //'A'-'Z'
        if (A < 26) r = 65; //'A'-'Z'6
 else {
        else {7
 if (A < 52) r = 71;  //'z'=122 控制以下
            if (A < 52) r = 71;  //'z'=122 控制以下8
 else r = -4;
            else r = -4;9
 }
        }10
 res = String.fromCharCode(A + r);
        res = String.fromCharCode(A + r);11
 }
    }12
 else {
    else {13
 if (A < 63) res = '_'; //即A=62
        if (A < 63) res = '_'; //即A=6214
 else {
        else {15
 if (A < 64) res = '$';//即A=63
            if (A < 64) res = '$';//即A=6316
 else res = C(A >> 6) + C(A & 63); //如果A>63,进行64进制的高低位分解为2部分
            else res = C(A >> 6) + C(A & 63); //如果A>63,进行64进制的高低位分解为2部分17
 }
        }18
 }
    }19
 return res;
    return res;20
 }
}更加深刻的理解上面算法,就是一个仿base64编码变换的算法,可以参见文章:Base64相关
变换的码表是将0-63的数字变换为
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$
对应序列位置的字母。
Step 3:代码while (A > 0)K[C(D--)] = I[--A];的分析
实际上这里就是将字典内容与序号值进行对照,记录到Object对象中,运算顺序如下表:
D=109,A=9,K["Bt"]=br
D=108,A=8,K["Bs"]=write
D=107,A=7,K["Br"]=document
D=106,A=6,K["Bq"]=0
D=105,A=5,K["Bp"]=a
D=104,A=4,K["Bo"]=for
D=103,A=3,K["Bn"]=100
D=102,A=2,K["Bm"]=index
D=101,A=1,K["Bl"]=var
Step 4:代码if (''.replace(/^/, String)) 
分析
看起来很高深的一个代码,你想空字符串无论怎么替换,还是空字符串,在javascript中,空字符串=false,非空字符串=true
所以这个if语句怎么都不会执行,这里是一个混淆视听的代码,呵呵,你如果想,也可以写上更多乱七八糟的代码来达到同样效果。
Step5:关键代码return E.replace(J, N),这里用到了函数N:
function 
N(A) {
 return K[A] == L[A] ? A : 
K[A]
}
注意L对象从来没有赋值,所以L[A]返回的应该是undefined,所以可以翻译为
function N(A) 
{
 return K[A] == undefined ? A : K[A]
}
这下看起来就很好理解,关键代码就下面这些
 function decode(E, I, A, D, J, K, L, H) {
function decode(E, I, A, D, J, K, L, H) {2
 function C(A) {
    function C(A) {3
 return A < 62 ? String.fromCharCode(A += A < 26 ? 65 : A < 52 ? 71 : -4) : A < 63 ? '_' : A < 64 ? '$' : C(A >> 6) + C(A & 63)
        return A < 62 ? String.fromCharCode(A += A < 26 ? 65 : A < 52 ? 71 : -4) : A < 63 ? '_' : A < 64 ? '$' : C(A >> 6) + C(A & 63)4
 }
    }5
 while (A > 0)K[C(D--)] = I[--A];
    while (A > 0)K[C(D--)] = I[--A];6
 function N(A) {
    function N(A) {7
 return K[A] == undefined ? A : K[A]
        return K[A] == undefined ? A : K[A]8
 }
    }9
 return E.replace(J, N)
    return E.replace(J, N)10
 }
}综上分析,该算法的原理就是从脚本文件中提取单词,存入字典表中,这里使用|分割的字符串,然后将单词对应的序号(仿base64编码值)写入原来代码的地方,
就构成了该算法的核心了,所以实现该压缩算法的代码也不难了
三、工具实现
具体实现采用java,代码就不介绍,详细内容贴在文章 javascript脚本压缩工具JSEncoder实现 
中,写完代码之后才发现这是JSA(http://sourceforge.net/project/showfiles.php?group_id=175776)的压缩算法的再实现,该工具对jquery-1.2.3.min.js压缩测试调用运行成功,压缩率为40%。
下载(包含源代码在jar文件中)
2008.4 
Ver:0.5  下载
【运行方法】
====================================
命令提示符下面运行,要求JRE 
1.5+
Usage:java JSEncoder.jar jsfile outputfile 
[offset].
jsfile:待压缩的文件
outputfile:输入的文件
offset:可选整数值,>=0
转自:http://www.cnblogs.com/midea0978/archive/2008/05/03/1161235.html
刚开始玩JQ的时候,用到一插件.只有一行代码.很恼.不解.....
还以为是将代码简写.怎么也不明白.
今天和群友聊天,才发现原来这是将JS压缩.大大的减小了文件的KB.
这也可能是JQ发现的策略.呵呵!
相信现在JQER已成千上万.
 
                    
                     
                    
                 
                    
                

 document.all.tx1.value
document.all.tx1.value  
                
            
         
 
         浙公网安备 33010602011771号
浙公网安备 33010602011771号