惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
Stack Overflow Blog
Stack Overflow Blog
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
T
The Blog of Author Tim Ferriss
博客园 - 叶小钗
N
Netflix TechBlog - Medium
腾讯CDC
C
Check Point Blog
P
Proofpoint News Feed
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
S
SegmentFault 最新的问题
F
Fortinet All Blogs
美团技术团队
U
Unit 42
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 司徒正美
F
Full Disclosure
Recorded Future
Recorded Future
D
DataBreaches.Net
博客园 - 【当耐特】
Martin Fowler
Martin Fowler
J
Java Code Geeks
I
InfoQ
Y
Y Combinator Blog
A
About on SuperTechFans
AI
AI
爱范儿
爱范儿
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Forbes - Security
Forbes - Security
W
WeLiveSecurity
M
MIT News - Artificial intelligence
雷峰网
雷峰网
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
Schneier on Security
Schneier on Security
The GitHub Blog
The GitHub Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
aimingoo的专栏
aimingoo的专栏
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
G
GRAHAM CLULEY
Know Your Adversary
Know Your Adversary
Latest news
Latest news
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
D
Docker
Recent Commits to openclaw:main
Recent Commits to openclaw:main
量子位
V2EX - 技术
V2EX - 技术
Project Zero
Project Zero

博客园 - 在路上...

Iphone的HEIC图片文件格式转换及相关开发工具 账号复活了 2015年,即将结束 CentOS 6 minimal 安装之后添加gnome Windows8离线安装.net framework 3.5 新年快到了,此记 flex中ComboBox对应的几种数据绑定 - 在路上... - 博客园 Jquery中增加参数与Json转换代码 - 在路上... - 博客园 如何获取到informix for linux? 建国大业 《红》mp3下载 T42内存升级 随便一帖 Informix 9.40UC9 on Redhat Linux AS 4安装手记 Taking the web Offline and On the Desktop Flex上传文件功能 Flex实现QQ网页提取天气信息 乱弹ruby on rails目录下面的文件数有3万多个 Ruby on rails 2.0.2傻瓜入门之Hello world 【翻译】Oracle不同版本之间Export & Import的兼容性矩阵
Javascript代码压缩、加密算法的破解分析及工具实现
在路上... · 2008-04-19 · via 博客园 - 在路上...

现在网上很多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, {}{}, []))

一、代码解密
对于这类压缩的代码,无非是把js程序采用某种算法进行压缩,然后自行用提供的函数还原,采用eval(SCRIPT)的方式执行来完成调用,那么还原的方法就很简单了,那前面的eval(和后面的)去掉,然后显示出来就完成了,例如下面的页面就可以实现代码的还原:

 1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
 2<HTML>
 3<HEAD>
 4    <TITLE> 代码还原 </TITLE>
 5    <META NAME="Generator" CONTENT="EditPlus">
 6    <META NAME="Author" CONTENT="">
 7    <META NAME="Keywords" CONTENT="">
 8    <META NAME="Description" CONTENT="">
 9</HEAD>
10
11<BODY>
12<TEXTAREA NAME="tx1" ROWS="10" COLS="100"></TEXTAREA>
13<SCRIPT LANGUAGE="JavaScript">
14document.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>
16</BODY>
17</HTML>


通过上面方式运行,就可以在文本框中看到代码了,实际的代码是:
var index=100;for(var a=0;a<100;a++){ document.write(index+a+"<br>");}
很简单,不是吗
二、算法研究
由于代码全部在一行中,不便于阅读,可以通过格式化软件格式化,本文这里使用Intellij IDEA格式化,代码如下

 1eval(function(E, I, A, D, J, K, L, H) {
 2    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)
 4    }

 5    while (A > 0)K[C(D--)] = I[--A];
 6    function N(A) {
 7        return K[A] == L[A] ? A : K[A]
 8    }

 9    if (''.replace(/^/, String)) {
10        var M = E.match(J),B = M[0],F = E.split(J),G = 0;
11        if (E.indexOf(F[0]))F = [''].concat(F);
12        do{
13            H[A++= F[G++];
14            H[A++= N(B)
15        }
 while (B = M[G]);
16        H[A++= F[G] || '';
17        return H.join('')
18    }

19    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('|'), 9109/[\w\$]+/g, {}{}, []))


Step 1:首先我们可以看出这是一个函数定义与调用合并在一起的,因此可以如下分解:(不再考虑eval)

 1//E:加密压缩后的script信息
 2//I:字符串数组,可以理解为解密需要字典
 3//A:int 9 
 4//D:int 109 
 5//J:regexpr 正则表达式
 6//K:object 
 7//L:object
 8//H:array
 9function decode(E, I, A, D, J, K, L, H) {
10    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)
12    }

13    while (A > 0)K[C(D--)] = I[--A];
14    function N(A) {
15        return K[A] == L[A] ? A : K[A]
16    }

17    if (''.replace(/^/, String)) {
18        var M = E.match(J),B = M[0],F = E.split(J),G = 0;
19        if (E.indexOf(F[0]))F = [''].concat(F);
20        do{
21            H[A++= F[G++];
22            H[A++= N(B)
23        }
 while (B = M[G]);
24        H[A++= F[G] || '';
25        return H.join('')
26    }

27    return E.replace(J, N)
28}

29var 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('|'), 9109/[\w\$]+/g, {}{}, []));


Step 2:其中对于函数function C(A)采用多重3元表达式处理的方式,可以用if/else如下分解

 1function C(A){
 2    var res;
 3    if (A < 62{
 4        var r = null;
 5        if (A < 26) r = 65//'A'-'Z'
 6        else {
 7            if (A < 52) r = 71;  //'z'=122 控制以下
 8            else r = -4;
 9        }

10        res = String.fromCharCode(A + r);
11    }

12    else {
13        if (A < 63) res = '_'//即A=62
14        else {
15            if (A < 64) res = '$';//即A=63
16            else res = C(A >> 6+ C(A & 63); //如果A>63,进行64进制的高低位分解为2部分
17        }

18    }

19    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]
}

这下看起来就很好理解,关键代码就下面这些

 1function decode(E, I, A, D, J, K, L, H) {
 2    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)
 4    }

 5    while (A > 0)K[C(D--)] = I[--A];
 6    function N(A) {
 7        return K[A] == undefined ? A : K[A]
 8    }

 9    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