整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          跟我學JS逆向-CSS字體加密破解

          標站點:

          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算法,只有輸入相同的明文數據經過相同的消息摘要算法才能得到相同的密文。

          消息摘要算法不存在密鑰的管理與分發問題,適合于分布式網絡上使用。由于其加密計算的工作量相當巨大,所以以前的這種算法通常只用于數據量有限的情況下的加密。

          消息摘要算法分為三類:

          • MD(Message Digest):消息摘要
          • SHA(Secure Hash Algorithm):安全散列
          • MAC(Message Authentication Code):消息認證碼

          這三類算法的主要作用:驗證數據的完整性

          二、MD5簡介

          MD5即Message-Digest Algorithm 5(信息-摘要算法)。

          屬于摘要算法,是一個不可逆過程,就是無論多大數據,經過算法運算后都是生成固定長度的數據,結果使用16進制進行顯示的128bit的二進制串。通常表示為32個十六進制數連成的字符串。

          MD5有什么用?

          用于確保信息傳輸完整一致。是計算機廣泛使用的雜湊算法之一(又譯摘要算法、哈希算法),主流編程語言普遍已有MD5實現。更多用在文檔校驗上,用來生成密鑰檢測文檔是否被篡改。

          三、在線MD5加密

          有很多在線進行MD5加密的網站,如下:

          http://www.metools.info/code/c26.html

          舉例: 給字符串 12334567 加密成。

          如圖結果為:

          32135A337F8DC8E2BB9A9B80D86BDFD0
          

          四、C語言實現MD5算法

          源文件如下: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加密步驟如下:

          1. 定義
          MD5_CTX md5c; 
          
          1. 初始化
          /********************************************************
          * 名    稱: MD5Init()
          * 功    能: 初始化MD5結構體
          * 入口參數: 
             context:要初始化的MD5結構體 
          * 出口參數: 無
          *********************************************************/
          MD5Init(MD5_CTX *context);
          
          1. MD5值計算

          實現MD5值的計算及結構體的更新:

          
          /*********************************************************
          * 名    稱: MD5Update()
          * 功    能: 將要加密的信息傳遞給初始化過的MD5結構體,無返回值
          * 入口參數: 
             context:初始化過了的MD5結構體 
              input:需要加密的信息,可以任意長度
              inputLen:指定input的長度
          * 出口參數: 無
          *********************************************************/
          MD5Update(MD5_CTX *context,(unsigned char *)input,inputLen); 
          
          1. 輸出轉換
          /*********************************************************
          * 名    稱: MD5Update()
          * 功    能: 將加密結果存儲到,無返回值
          * 入口參數: 
             context:初始化過了的MD5結構體 
              digest :加密過的結果
          * 出口參數: 無
          *********************************************************/
          MD5Final(MD5_CTX *context,unsigned char digest[16]);
          
          1. 格式整理

          轉換成32位的16進制字符串。

          實例1 字符串加密

          對字符串進行加密:

            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進行加密,結果和在線加密結果一致。

          實例2 文件加密

          對文件進行加密

          #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 的導出段與導入段進行處理等等。

          來源網絡,侵權聯系刪除

          私信我或關注微信號:獅范課,回復:學習,獲取免費學習資源包。


          主站蜘蛛池模板: 成人h动漫精品一区二区无码| 亚洲一区二区三区在线网站| 欧美日韩精品一区二区在线视频| 无码国产伦一区二区三区视频 | 国产一区二区三区高清在线观看 | 久久精品无码一区二区三区免费| 中文字幕无码一区二区免费| 一区二区三区观看| 免费无码AV一区二区| 一本色道久久综合一区| 中文国产成人精品久久一区| 国产另类TS人妖一区二区| 国产亚洲日韩一区二区三区 | 亚洲欧洲一区二区| 美女福利视频一区二区| 国产日本一区二区三区| 精品视频一区二区三区在线观看| 无码丰满熟妇一区二区| 3d动漫精品啪啪一区二区中文 | 国产一区二区三区日韩精品 | 国产美女在线一区二区三区| 久久国产高清一区二区三区| 成人精品一区二区激情| 麻豆AV天堂一区二区香蕉 | 一区二区国产在线播放| 一色一伦一区二区三区| 亚洲一区二区三区久久| 精品久久一区二区三区| 亚洲一区精品无码| 中文字幕色AV一区二区三区 | 无码AV动漫精品一区二区免费| 亚洲AV成人精品日韩一区| 亚洲国产精品一区第二页 | 一区二区乱子伦在线播放| 一区二区三区免费看| 国产午夜精品一区二区三区小说| 久久免费视频一区| 国产精品女同一区二区| 中文字幕人妻AV一区二区| 91精品福利一区二区| 国产福利一区二区在线视频 |