标站点:
aHR0cHMlM0EvL2subS5hdXRvaG9tZS5jb20uY24vZGV0YWlsL3NoYXJlXzAxZG1xeThmYTE2OHZrYWU5aDYwdmcwMDAwLmh0bWw=
该地址经过base64加密,可以通过如下地址进行解密:
https://base64.supfree.net
准备工具:
chome浏览器,python3.7语言环境,pycharm,百度字体编辑器:http://fontstore.baidu.com/static/editor/index.html
破解方法:
1、打开chrome浏览器,在浏览器地址栏中输入目标地址,打开网页后,在页面中点击鼠标右键,选择检查,可以看到相关文字已被加密。
文字被加密
2、可以通过复制,来验证文字是否被加密。如复制:
老朋友的推荐
复制出的文字为:
老朋友推荐
3、此类现象为文字被CSS样式加密,破解步骤如下:
1、通过浏览器开发者模式,找到页面中文字所使用的css样式 2、通过抓包等方法找到加载的css文件,通过正则表达式取出字体文件URL 3、通过百度字体编辑器解析woff文件:http://fontstore.baidu.com/static/editor/index.html 4、使用fontTools处理字体文件,得出对应关系 5、通过对应关系解析加密字体
4、首先使用python的request模块请求该页面,通过正则表达式获取字体文件URL,请求该URL,获取到字体文件,写入到本地。
5、通过百度字体编辑器,解析ttf文件
6、通过百度字体编辑器,可以看到,"的"对应的字体编码为"$EC2A",接下来通过python下的fontTools模块读取该TTF文件,并建立文字对应关系,保存为字典。
7、通过for循环遍历该文字对应关系字典,对原网页返回进行替换,即可得到正常数据。
老朋友的推荐,去看了一几个牌子,头都看晕了,没有结果,决定不了买哪个牌子,九了女儿意见,准备在荣威里面选盘款。性价上最高的就是I5了,看中这款的原因,并不是因为配置高,也不是养力强,而是囊中羞涩,预算控有那么电啊�,而荣威这个品牌过硬,质量可靠,故障率低,朋友买了都说挺一的。暂时没有,还是觉外有点说不过去啊!买车看车子做这个决定,是和女儿共同商量决定的,我看中的是这个牌子的知名度,品质这些方面,女儿的话是喜欢这款车型的十观,女孩子嘛,都是十貌协会,两厢车上较炫酷,十形时尚养感,适合年轻妹子。女儿盘看就中意了。
代码参考:
https://github.com/freedom-wy/js-reverse/tree/master/autohome/koubei
欢迎交流,一起学习,一起进步。
另外,我在慕课网上主讲课程:
《Python爬虫工程师必学——App数据抓取实战》,还请各位大神多多支持。课程地址:
文详细讲解视频如下:
《C语言实现MD5算法》
摘要算法又称哈希算法。
它表示输入任意长度的数据,输出固定长度的数据,它的主要特征是加密过程不需要密钥,并且经过加密的数据无法被解密。
目前可以被解密逆向的只有CRC32算法,只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文。
消息摘要算法不存在密钥的管理与分发问题,适合于分布式网络上使用。由于其加密计算的工作量相当巨大,所以以前的这种算法通常只用于数据量有限的情况下的加密。
这三类算法的主要作用:验证数据的完整性
MD5即Message-Digest Algorithm 5(信息-摘要算法)。
属于摘要算法,是一个不可逆过程,就是无论多大数据,经过算法运算后都是生成固定长度的数据,结果使用16进制进行显示的128bit的二进制串。通常表示为32个十六进制数连成的字符串。
MD5有什么用?
用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。更多用在文档校验上,用来生成密钥检测文档是否被篡改。
有很多在线进行MD5加密的网站,如下:
http://www.metools.info/code/c26.html
举例: 给字符串 12334567 加密成。
如图结果为:
32135A337F8DC8E2BB9A9B80D86BDFD0
源文件如下:md5.h
#ifndef MD5_H
#define MD5_H
typedef struct
{
unsigned int count[2];
unsigned int state[4];
unsigned char buffer[64];
}MD5_CTX;
#define F(x,y,z) ((x & y) | (~x & z))
#define G(x,y,z) ((x & z) | (y & ~z))
#define H(x,y,z) (x^y^z)
#define I(x,y,z) (y ^ (x | ~z))
#define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))
#define FF(a,b,c,d,x,s,ac) \
{ \
a += F(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define GG(a,b,c,d,x,s,ac) \
{ \
a += G(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define HH(a,b,c,d,x,s,ac) \
{ \
a += H(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define II(a,b,c,d,x,s,ac) \
{ \
a += I(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
void MD5Init(MD5_CTX *context);
void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen);
void MD5Final(MD5_CTX *context,unsigned char digest[16]);
void MD5Transform(unsigned int state[4],unsigned char block[64]);
void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len);
void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len);
#endif
md5.c
#include <memory.h>
#include "md5.h"
unsigned char PADDING[]={0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
void MD5Init(MD5_CTX *context)
{
context->count[0] = 0;
context->count[1] = 0;
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
}
void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen)
{
unsigned int i = 0,index = 0,partlen = 0;
index = (context->count[0] >> 3) & 0x3F;
partlen = 64 - index;
context->count[0] += inputlen << 3;
if(context->count[0] < (inputlen << 3))
context->count[1]++;
context->count[1] += inputlen >> 29;
if(inputlen >= partlen)
{
memcpy(&context->buffer[index],input,partlen);
MD5Transform(context->state,context->buffer);
for(i = partlen;i+64 <= inputlen;i+=64)
MD5Transform(context->state,&input[i]);
index = 0;
}
else
{
i = 0;
}
memcpy(&context->buffer[index],&input[i],inputlen-i);
}
void MD5Final(MD5_CTX *context,unsigned char digest[16])
{
unsigned int index = 0,padlen = 0;
unsigned char bits[8];
index = (context->count[0] >> 3) & 0x3F;
padlen = (index < 56)?(56-index):(120-index);
MD5Encode(bits,context->count,8);
MD5Update(context,PADDING,padlen);
MD5Update(context,bits,8);
MD5Encode(digest,context->state,16);
}
void MD5Encode(unsigned char *output,unsigned int *input,unsigned int len)
{
unsigned int i = 0,j = 0;
while(j < len)
{
output[j] = input[i] & 0xFF;
output[j+1] = (input[i] >> 8) & 0xFF;
output[j+2] = (input[i] >> 16) & 0xFF;
output[j+3] = (input[i] >> 24) & 0xFF;
i++;
j+=4;
}
}
void MD5Decode(unsigned int *output,unsigned char *input,unsigned int len)
{
unsigned int i = 0,j = 0;
while(j < len)
{
output[i] = (input[j]) |
(input[j+1] << 8) |
(input[j+2] << 16) |
(input[j+3] << 24);
i++;
j+=4;
}
}
void MD5Transform(unsigned int state[4],unsigned char block[64])
{
unsigned int a = state[0];
unsigned int b = state[1];
unsigned int c = state[2];
unsigned int d = state[3];
unsigned int x[64];
MD5Decode(x,block,64);
FF(a, b, c, d, x[ 0], 7, 0xd76aa478); /* 1 */
FF(d, a, b, c, x[ 1], 12, 0xe8c7b756); /* 2 */
FF(c, d, a, b, x[ 2], 17, 0x242070db); /* 3 */
FF(b, c, d, a, x[ 3], 22, 0xc1bdceee); /* 4 */
FF(a, b, c, d, x[ 4], 7, 0xf57c0faf); /* 5 */
FF(d, a, b, c, x[ 5], 12, 0x4787c62a); /* 6 */
FF(c, d, a, b, x[ 6], 17, 0xa8304613); /* 7 */
FF(b, c, d, a, x[ 7], 22, 0xfd469501); /* 8 */
FF(a, b, c, d, x[ 8], 7, 0x698098d8); /* 9 */
FF(d, a, b, c, x[ 9], 12, 0x8b44f7af); /* 10 */
FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */
FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */
FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */
FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */
FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */
FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */
/* Round 2 */
GG(a, b, c, d, x[ 1], 5, 0xf61e2562); /* 17 */
GG(d, a, b, c, x[ 6], 9, 0xc040b340); /* 18 */
GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */
GG(b, c, d, a, x[ 0], 20, 0xe9b6c7aa); /* 20 */
GG(a, b, c, d, x[ 5], 5, 0xd62f105d); /* 21 */
GG(d, a, b, c, x[10], 9, 0x2441453); /* 22 */
GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */
GG(b, c, d, a, x[ 4], 20, 0xe7d3fbc8); /* 24 */
GG(a, b, c, d, x[ 9], 5, 0x21e1cde6); /* 25 */
GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */
GG(c, d, a, b, x[ 3], 14, 0xf4d50d87); /* 27 */
GG(b, c, d, a, x[ 8], 20, 0x455a14ed); /* 28 */
GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */
GG(d, a, b, c, x[ 2], 9, 0xfcefa3f8); /* 30 */
GG(c, d, a, b, x[ 7], 14, 0x676f02d9); /* 31 */
GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */
/* Round 3 */
HH(a, b, c, d, x[ 5], 4, 0xfffa3942); /* 33 */
HH(d, a, b, c, x[ 8], 11, 0x8771f681); /* 34 */
HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */
HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */
HH(a, b, c, d, x[ 1], 4, 0xa4beea44); /* 37 */
HH(d, a, b, c, x[ 4], 11, 0x4bdecfa9); /* 38 */
HH(c, d, a, b, x[ 7], 16, 0xf6bb4b60); /* 39 */
HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */
HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */
HH(d, a, b, c, x[ 0], 11, 0xeaa127fa); /* 42 */
HH(c, d, a, b, x[ 3], 16, 0xd4ef3085); /* 43 */
HH(b, c, d, a, x[ 6], 23, 0x4881d05); /* 44 */
HH(a, b, c, d, x[ 9], 4, 0xd9d4d039); /* 45 */
HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */
HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */
HH(b, c, d, a, x[ 2], 23, 0xc4ac5665); /* 48 */
/* Round 4 */
II(a, b, c, d, x[ 0], 6, 0xf4292244); /* 49 */
II(d, a, b, c, x[ 7], 10, 0x432aff97); /* 50 */
II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */
II(b, c, d, a, x[ 5], 21, 0xfc93a039); /* 52 */
II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */
II(d, a, b, c, x[ 3], 10, 0x8f0ccc92); /* 54 */
II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */
II(b, c, d, a, x[ 1], 21, 0x85845dd1); /* 56 */
II(a, b, c, d, x[ 8], 6, 0x6fa87e4f); /* 57 */
II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */
II(c, d, a, b, x[ 6], 15, 0xa3014314); /* 59 */
II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */
II(a, b, c, d, x[ 4], 6, 0xf7537e82); /* 61 */
II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */
II(c, d, a, b, x[ 2], 15, 0x2ad7d2bb); /* 63 */
II(b, c, d, a, x[ 9], 21, 0xeb86d391); /* 64 */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
}
MD5加密步骤如下:
MD5_CTX md5c;
/********************************************************
* 名 称: MD5Init()
* 功 能: 初始化MD5结构体
* 入口参数:
context:要初始化的MD5结构体
* 出口参数: 无
*********************************************************/
MD5Init(MD5_CTX *context);
实现MD5值的计算及结构体的更新:
/*********************************************************
* 名 称: MD5Update()
* 功 能: 将要加密的信息传递给初始化过的MD5结构体,无返回值
* 入口参数:
context:初始化过了的MD5结构体
input:需要加密的信息,可以任意长度
inputLen:指定input的长度
* 出口参数: 无
*********************************************************/
MD5Update(MD5_CTX *context,(unsigned char *)input,inputLen);
/*********************************************************
* 名 称: MD5Update()
* 功 能: 将加密结果存储到,无返回值
* 入口参数:
context:初始化过了的MD5结构体
digest :加密过的结果
* 出口参数: 无
*********************************************************/
MD5Final(MD5_CTX *context,unsigned char digest[16]);
转换成32位的16进制字符串。
对字符串进行加密:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include "md5.h"
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7 #include <string.h>
8
9 void main( void )
10 {
11 int read_len;
12 int i ;
13 char temp[8]={0};
14 unsigned char digest[16]; //存放结果
15 char hexbuf[128]="12334567";
16 unsigned char decrypt[16]={0};
17 unsigned char decrypt32[64]={0};
18
19 MD5_CTX md5c;
20
21 MD5Init(&md5c); //初始化
22 read_len = strlen(hexbuf);
23 MD5Update(&md5c,(unsigned char *)hexbuf,read_len);
24
25 MD5Final(&md5c,decrypt);
26 strcpy((char *)decrypt32,"");
27
28 for(i=0;i<16;i++)
29 {
30 sprintf(temp,"%02x",decrypt[i]);
31 strcat((char *)decrypt32,temp);
32 }
33 printf("md5:%s\n",decrypt32);
34
35 return;
36 }
执行结果如下:
本例对字符串12334567进行加密,结果和在线加密结果一致。
对文件进行加密
#include <stdio.h>
#include <stdlib.h>
#include "md5.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define FORWORD_FW "123.c"
int calc_md5(char*filename,char*dest)
{
int i;
int filelen = 0;
int read_len;
char temp[8]={0};
char hexbuf[128]={0};
unsigned char decrypt[16]={0};
unsigned char decrypt32[64]={0};
MD5_CTX md5;
char fw_path[128];
int fdf;
fdf = open(filename,O_RDWR);
if(fdf<0)
{
printf("%s not exist\n",FORWORD_FW);
return -1;
}
MD5Init(&md5);
while(1)
{
read_len = read(fdf, hexbuf,sizeof(hexbuf));
if (read_len <0) {
close(fdf);
return -1;
}
if(read_len==0)
{
break;
}
filelen += read_len;
MD5Update(&md5,(unsigned char *)hexbuf,read_len);
}
MD5Final(&md5,decrypt);
strcpy((char *)decrypt32,"");
for(i=0;i<16;i++)
{
sprintf(temp,"%02x",decrypt[i]);
strcat((char *)decrypt32,temp);
}
strcpy(dest,decrypt32);
printf("md5:%s len=%d\n",dest,filelen);
close(fdf);
return filelen;
}
int main(int argc, char *argv[])
{
int ret;
int filelen;
char md5_str[64]={0};
char cmd[256]={0};
filelen = calc_md5(FORWORD_FW,md5_str);
if(filelen<0)
{
printf("calc_md5 fail\n");
return -1;
}
return 0;
}
运行结果:
在线验证结果对比:
http://www.metools.info/other/o21.html
结果
信我或关注微信号:狮范课,回复:学习,获取免费学习资源包。
说到 Web 前端开发,我们首先能够想到的是浏览器、HTML、CSS 以及 JavaScript 这些开发时所必备使用的软件工具和编程语言。而在这个专业领域中,作为开发者我们众所周知的是,所有来自前端的数据都是“不可信”的,由于构成前端业务逻辑和交互界面的所有相关代码都是可以被用户直接查看到的,所以我们无法保证我们所确信的某个从前端传递到后端的数据没有被用户曾经修改过。
那么是否有办法可以将前端领域中那些与业务有关的代码(比如数据处理逻辑、验证逻辑等,通常是 JavaScript 代码)进行加密以防止用户进行恶意修改呢?本文我们将讨论这方面的内容。
提到“加密”,我们自然会想到众多与“对称加密”、“非对称加密”以及“散列加密”相关的算法,比如 AWS 算法、RSA 算法与 MD5 算法等。在传统的 B-S 架构下,前端通过公钥进行加密处理的数据可以在后端服务器再通过相应私钥进行解密来得到原始数据,但是对于前端的业务代码而言,由于浏览器本身无法识别运行这些被加密过的源代码,因此实际上传统的加密算法并不能帮助我们解决“如何完全黑盒化前端业务逻辑代码”这一问题。
既然无法完全隐藏前端业务逻辑代码的实际执行细节,那我们就从另一条路以“降低代码可读性”的方式来“伪黑盒化前端业务逻辑代码”。通常的方法有如下几种:
第三方插件
我们所熟知的可用在 Web 前端开发中的第三方插件主要有:Adobe Flash、Java Applet 以及 Silverlight 等。由于历史原因这里我们不会深入介绍基于这些第三方插件的前端业务代码加密方案。其中 Adobe 将于 2020 年完全停止对 Flash 技术的支持,Chrome、Edge 等浏览器也开始逐渐对使用了 Flash 程序的 Web 页面进行阻止或弹出相应的警告。同样的,来自微软的 Silverlight5 也会在 2021 年停止维护,并完全终止后续新版本功能的开发。而 Java Applet 虽然还可以继续使用,但相较于早期上世纪 90 年代末,现在已然很少有人使用(不完全统计)。并且需要基于 JRE 来运行也使得 Applet 应用的运行成本大大提高。
代码混淆
在现代前端开发过程中,我们最常用的一种可以“降低源代码可读性”的方法就是使用“代码混淆”。通常意义上的代码混淆可以压缩原始 ASCII 代码的体积并将其中的诸如变量、常量名用简短的毫无意义的标识符进行代替,这一步可以简单地理解为“去语义化”。以我们最常用的 “Uglify” 和 “GCC (Google Closure Compiler)” 为例,首先是一段未经代码混淆的原始 ECMAScript5 源代码:
let times = 0.1 * 8 + 1; function getExtra(n) { return [1, 4, 6].map(function(i) { return i * n; }); } var arr = [8, 94, 15, 88, 55, 76, 21, 39]; arr = getExtra(times).concat(arr.map(function(item) { return item * 2; })); function sortarr(arr) { for(i = 0; i < arr.length - 1; i++) { for(j = 0; j < arr.length - 1 - i; j++) { if(arr[j] > arr[j + 1]) { var temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } return arr; } console.log(sortarr(arr));
经过 UglifyJS3 的代码压缩混淆处理后的结果:
let times=1.8;function getExtra(r){return[1,4,6].map(function(t){return t*r})}var arr=[8,94,15,88,55,76,21,39];function sortarr(r){for(i=0;i<r.length-1;i++)for(j=0;j<r.length-1-i;j++)if(r[j]>r[j+1]){var t=r[j];r[j]=r[j+1],r[j+1]=t}return r}arr=getExtra(times).concat(arr.map(function(r){return 2*r})),console.log(sortarr(arr));
经过 Google Closure Compiler 的代码压缩混淆处理后的结果:
var b=[8,94,15,88,55,76,21,39];b=function(a){return[1,4,6].map(function(c){return c*a})}(1.8).concat(b.map(function(a){return 2*a}));console.log(function(a){for(i=0;i<a.length-1;i++)for(j=0;j<a.length-1-i;j++)if(a[j]>a[j+1]){var c=a[j];a[j]=a[j+1];a[j+1]=c}return a}(b));
对比上述两种工具的代码混淆压缩结果我们可以看到,UglifyJS 不会对原始代码进行“重写”,所有的压缩工作都是在代码原有结构的基础上进行的优化。而 GCC 对代码的优化则更靠近“编译器”,除了常见的变量、常量名去语义化外,还使用了常见的 DCE 优化策略,比如对常量表达式(constexpr)进行提前求值(0.1 * 8 + 1)、通过 “inline” 减少中间变量的使用等等。
UglifyJS 在处理优化 JavaScript 源代码时都是以其 AST 的形式进行分析的。比如在 Node.js 脚本中进行源码处理时,我们通常会首先使用 UglifyJS.parse 方法将一段 JavaScript 代码转换成其对应的 AST 形式,然后再通过 UglifyJS.Compressor 方法对这些 AST 进行处理。最后还需要通过print_to_string 方法将处理后的 AST 结构转换成相应的 ASCII 可读代码形式。UglifyJS.Compressor 的本质是一个官方封装好的 “TreeTransformer” 类型,其内部已经封装好了众多常用的代码优化策略,而通过对 UglifyJS.TreeTransformer 进行适当的封装,我们也可以编写自己的代码优化器。
如下所示我们编写了一个实现简单“常量传播”与“常量折叠”(注意这里其实是变量,但优化形式同 C++ 中的这两种基本优化策略相同)优化的 UglifyJS 转化器。
const UglifyJS = require('uglify-js'); var symbolTable = {}; var binaryOperations = { "+": (x, y) => x + y, "-": (x, y) => x - y, "*": (x, y) => x * y } var constexpr = new UglifyJS.TreeTransformer(null, function(node) { if (node instanceof UglifyJS.AST_Binary) { if (Number.isInteger(node.left.value) && Number.isInteger(node.right.value)) { return new UglifyJS.AST_Number({ value: binaryOperations[node.operator].call(this, Number(node.left.value), Number(node.right.value)) }); } else { return new UglifyJS.AST_Number({ value: binaryOperations[node.operator].call(this, Number(symbolTable[node.left.name].value), Number(symbolTable[node.right.name].value)) }) } } if (node instanceof UglifyJS.AST_VarDef) { // AST_VarDef -> AST_SymbolVar; // 通过符号表来存储已求值的变量值(UglifyJS.AST_Number)引用; symbolTable[node.name.name] = node.value; } }); var ast = UglifyJS.parse(` var x = 10 * 2 + 6; var y = 4 - 1 * 100; console.log(x + y); `); // transform and print; ast.transform(constexpr); console.log(ast.print_to_string()); // output: // var x=26;var y=-96;console.log(-70);
这里我们通过识别特定的 Uglify AST 节点类型(UglifyJS.AST_Binary / UglifyJS.AST_VarDef)来达到对代码进行精准处理的目的。可以看到,变量 x 和 y 的值在代码处理过程中被提前计算。不仅如此,其作为变量的值还被传递到了表达式 a + b 中,此时如果能够再结合简单的 DCE 策略便可以完成最初级的代码优化效果。类似的,其实通过 Babel 的 @babel/traverse 插件,我们也可以实现同样的效果,其所基于的原理也都大同小异,即对代码的 AST 进行相应的转换和处理。
WebAssembly
关于 Wasm 的基本介绍,这里我们不再多谈。那么到底应该如何利用 Wasm 的“字节码”特性来做到尽可能地做到“降低 JavaScript 代码可读性”这一目的呢?一个简单的 JavaScript 代码“加密”服务系统架构图如下所示:
这里整个系统分为两个处理阶段:
第一阶段:先将明文的 JavaScript 代码转换为基于特定 JavaScript 引擎(VM)的 OpCode 代码,这些二进制的 OpCode 代码会再通过诸如 Base64 等算法的处理而转换为经过编码的明文 ASCII 字符串格式;
第二阶段:将上述经过编码的 ASCII 字符串连同对应的 JavaScript 引擎内核代码统一编译成完整的 ASM / Wasm 模块。当模块在网页中加载时,内嵌的 JavaScript 引擎便会直接解释执行硬编码在模块中的、经过编码处理的 OpCode 代码;
比如我们以下面这段处于 Top-Level 层的 JavaScript 代码为例:
[1, 2, 3, 5, 6, 7, 8, 9].map(function(i) { return i * 2; }).reduce(function(p, i) { return p + i; }, 0);
按照正常的 VM 执行流程,上述代码在执行后会返回计算结果 82。这里我们以 JerryScript 这个开源的轻量级 JavaScript 引擎来作为例子,第一步首先将上述 ASCII 形式的代码 Feed 到该引擎中,然后便可以获得对应该引擎中间状态的 ByteCode 字节码。
然后再将这些二进制的字节码通过 Base64 算法编码成对应的可见字符形式。结果如下所示:
WVJSSgAAABYAAAAAAAAAgAAAAAEAAAAYAAEACAAJAAEEAgAAAAAABwAAAGcAAABAAAAAWDIAMhwyAjIBMgUyBDIHMgY6CCAIwAIoAB0AAToARscDAAAAAAABAAMBAQAhAgIBAQAAACBFAQCPAAAAAAABAAICAQAhAgICAkUBAIlhbQADAAYAcHVkZXIAAGVjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
按照我们的架构思路,这部分被编码后的可见字符串会作为“加密”后的源代码被硬编码到包含有 VM 引擎核心的 Wasm 模块中。当模块被加载时,VM 会通过相反的顺序解码这段字符串,并得到二进制状态的 ByteCode。然后再通过一起打包进来的 VM 核心来执行这些中间状态的比特码。这里我们上述所提到的 ByteCode 实际上是以 JerryScript 内部的 SnapShot 快照结构存在于内存中的。
最后这里给出上述 Demo 的主要部分源码,详细代码可以参考 Github:
#include "jerryscript.h" #include "cppcodec/base64_rfc4648.hpp" #include <iostream> #include <vector> #define BUFFER_SIZE 256 #ifdef WASM #include "emscripten.h" #endif std::string encode_code(const jerry_char_t*, size_t); const unsigned char* transferToUC(const uint32_t* arr, size_t length) { auto container = std::vector<unsigned char>(); for (size_t x = 0; x < length; x++) { auto _t = arr[x]; container.push_back(_t >> 24); container.push_back(_t >> 16); container.push_back(_t >> 8); container.push_back(_t); } return &container[0]; } std::vector<uint32_t> transferToU32(const uint8_t* arr, size_t length) { auto container = std::vector<uint32_t>(); for (size_t x = 0; x < length; x++) { size_t index = x * 4; uint32_t y = (arr[index + 0] << 24) | (arr[index + 1] << 16) | (arr[index + 2] << 8) | arr[index + 3]; container.push_back(y); } return container; } int main (int argc, char** argv) { const jerry_char_t script_to_snapshot[] = u8R"( [1, 2, 3, 5, 6, 7, 8, 9].map(function(i) { return i * 2; }).reduce(function(p, i) { return p + i; }, 0); )"; std::cout << encode_code(script_to_snapshot, sizeof(script_to_snapshot)) << std::endl; return 0; } std::string encode_code(const jerry_char_t script_to_snapshot[], size_t length) { using base64 = cppcodec::base64_rfc4648; // initialize engine; jerry_init(JERRY_INIT_SHOW_OPCODES); jerry_feature_t feature = JERRY_FEATURE_SNAPSHOT_SAVE; if (jerry_is_feature_enabled(feature)) { static uint32_t global_mode_snapshot_buffer[BUFFER_SIZE]; // generate snapshot; jerry_value_t generate_result = jerry_generate_snapshot( NULL, 0, script_to_snapshot, length - 1, 0, global_mode_snapshot_buffer, sizeof(global_mode_snapshot_buffer) / sizeof(uint32_t)); if (!(jerry_value_is_abort(generate_result) || jerry_value_is_error(generate_result))) { size_t snapshot_size = (size_t) jerry_get_number_value(generate_result); std::string encoded_snapshot = base64::encode( transferToUC(global_mode_snapshot_buffer, BUFFER_SIZE), BUFFER_SIZE * 4); jerry_release_value(generate_result); jerry_cleanup(); // encoded bytecode of the snapshot; return encoded_snapshot; } } return "[EOF]"; } void run_encoded_snapshot(std::string code, size_t snapshot_size) { using base64 = cppcodec::base64_rfc4648; auto result = transferToU32( &(base64::decode(code)[0]), BUFFER_SIZE); uint32_t snapshot_decoded_buffer[BUFFER_SIZE]; for (auto x = 0; x < BUFFER_SIZE; x++) { snapshot_decoded_buffer[x] = result.at(x); } jerry_init(JERRY_INIT_EMPTY); jerry_value_t res = jerry_exec_snapshot( snapshot_decoded_buffer, snapshot_size, 0, 0); // default as number result; std::cout << "[Zero] code running result: " << jerry_get_number_value(res) << std::endl; jerry_release_value(res); } #ifdef WASM extern "C" { void EMSCRIPTEN_KEEPALIVE run_core() { // encoded snapshot (will be hardcoded in wasm binary file); std::string base64_snapshot = "WVJSSgAAABYAAAAAAAAAgAAAAAEAAAAYAAEACAAJAAEEAgAAAAAABwAAAGcAAABAAAAAWDIAMhwyAjIBMgUyBDIHMgY6CCAIwAIoAB0AAToARscDAAAAAAABAAMBAQAhAgIBAQAAACBFAQCPAAAAAAABAAICAQAhAgICAkUBAIlhbQADAAYAcHVkZXIAAGVjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="; run_encoded_snapshot(base64_snapshot, 142); } } #endif
当然这里我们只是基于 JerryScript 做了一个利用 Wasm 进行 JavaScript 代码“加密”的最简单 Demo,代码并没有处理边界 Case,对于非 Top-Level 的代码也并没有进行测试。如果需要进一步优化,我们可以思考如何利用 “jerry-libm” 来处理 JavaScript 中诸如 Math.abs 等常见标准库;对于平台依赖的符号(比如 window.document 等平台依赖的函数或变量)怎样通过 Wasm 的导出段与导入段进行处理等等。
来源网络,侵权联系删除
私信我或关注微信号:狮范课,回复:学习,获取免费学习资源包。
*请认真填写需求信息,我们会在24小时内与您取得联系。