程式碼壓縮混淆加密的實例詳解
一直有人問HTML加密混淆怎麼做,其實這在業界是早已很多人研究過的課題。
假期期間整理一篇文章分享給大家。
我們先理需求,加密的目的為何?加密到什麼等級?為此我們可以犧牲什麼?我們知道這個世界不存在絕對的安全,加密會被破解、混淆會被反混淆。技術小白、開發者、駭客,是完不同的級別,防範不同級別的人策略都不一樣。防範力道越大,投入代價也越大,例如聘請專業的保全公司。除了投入,我們還需要考慮程式的執行效能和使用者體驗。加密的程式碼在運行時必須解密,混淆後尤其是混淆HTML後,程式的執行效能會下降。是否真的有必要做這類的源碼保護,還需要謹慎取捨。 一般而言,前端的程式碼,負責的是使用者體驗,後端的程式碼,負責更安全的資料處理。 前端不要涉及洩漏太多涉密訊息,那麼加密的意義就不是特別大。
我很少在前端程式碼裡看到值得保護的內容,像是高深的演算法,很多程式碼是沒必要犧牲使用者體驗來保護的。但有些前端程式碼涉及最終用戶的資料安全,此時還是要努力做資料保護的。
1. 降低可讀性
#1.1 壓縮(compression)
很好理解,就是去掉註解、多於的空格、簡化標識符等等。工具很多,YUI Compressor、UglifyJS、Google Closure Compiler等等。
1.2 混淆(obfuscation)
#保證不破壞程式碼執行結果的情況下,讓程式碼變得難以閱讀。常用混淆規則:拆分字串、拆分數組、增加廢代碼、,壓縮其實也有一定混淆功能。本質就是改變輸入程式碼字串的抽象語法樹(AST)的結構。其他工具:v8就是一個,還有mozilla的SpiderMonkey, 知名的esprima,還有uglify;商業混淆服務有:jscramble。
1.3 加密(encryption)
#這裡的加密指文字可逆編碼,是狹義的加密,也就是我們常說的加密啦。這個部分依然是藉助一些工具,如: Packer 、bcrypt等等。
2. 程式碼不會放置在JS檔案中
#將程式碼放在非js檔案中,增加定位難度。這裡常用的方式有兩種:放置到png中,透過HTML Canvas 2D Context取得二進位資料的特性,可以用圖片來儲存腳本資源;放置到css檔案中,利用content樣式可以存放字串的特性,同樣可以。
2.1 png
用png儲存js程式碼,首先需要對png進行編碼,然後使用的時候進行解碼。借助canvas及base64和二進位編碼。
編碼
1、字串轉換成ascii碼;
2、創建足夠儲存空間的canvas;
3、將字元填入像素中(忽略alpha值);
4、取得data url;
# canvas.toDataURL(“image/png”);
5、存為png圖片。
function encodeUTF8(str) { return String(str).replace( /[\u0080-\u07ff]/g, function(c) { let cc = c.charCodeAt(0);return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f); } ).replace( /[\u0800-\uffff]/g, function(c) { let cc = c.charCodeAt(0);return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3f, 0x80 | cc & 0x3f); } ); }function request(url, loaded) { let xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) if (xmlhttp.status == 200) loaded(xmlhttp); } xmlhttp.open("GET", url, true); xmlhttp.send(); }void function(){ let source = '../image/test.js'; request(source, function(xmlhttp){let text = encodeUTF8(xmlhttp.responseText);let pixel = Math.ceil((text.length + 2) / 3); // 1一个像素存3个字节, let size = Math.ceil(Math.sqrt(pixel));//console.log([text.length, pixel, size, size * size * 3]);let canvas = document.createElement('canvas'); canvas.width = canvas.height = size;let context = canvas.getContext("2d"), imageData = context.getImageData(0, 0, canvas.width, canvas.height), pixels = imageData.data;for(let i = 0, j = 0, l = pixels.length; i < l; i++){ if (i % 4 == 3) { // alpha会影响png还原 pixels[i] = 255;continue; }let code = text.charCodeAt(j++);if (isNaN(code)) break; pixels[i] = code; } context.putImageData(imageData, 0, 0); document.getElementById('base64').src = canvas.toDataURL("image/png"); }); }();
編碼後的圖片: <br/>
解碼
1、載入png; <br/>2、將png原始尺寸繪製到canvas中; <br/>3、讀取像素中的字串; <br/>4、產生對應協定的data url使用。
void function(){ let source = '../image/test.png';let img = document.createElement('img'); img.onload = function(){ let canvas = document.createElement('canvas'); canvas.width = img.width; canvas.height = img.height;let context = canvas.getContext("2d"); context.drawImage(img, 0, 0);let imageData = context.getImageData(0, 0, canvas.width, canvas.height), pixels = imageData.data;let script = document.createElement('script');let buffer = [];for (let i = 0, l = pixels.length; i < l; i++) { if (i % 4 == 3) continue; // alpha会影响png还原 if (!pixels[i]) break; buffer.push(String.fromCharCode(pixels[i])); } script.src = 'data:text/javascript;charset=utf-8,' + encodeURIComponent(buffer.join('')); document.body.appendChild(script); script.onload = function(){ console.log('script is loaded!'); } img = null; } img.src = source; }();
這裡需要手動下載編碼後的圖片,我沒有寫自動下載的函數,這又是另一個可以深入探討的問題了,所以不過多擴展。
2.2 css
使用content就簡單多啦。
let div = document.getElementById('content');let content = window.getComputedStyle(div, ':before').content;
只需要跟上面程式碼一樣,新建一個srcript標籤,利用data協議,就可以執行content內保存的js程式碼啦。
3. 防止代码执行被截获
截获 eval() / new Function() 的示例代码
eval = function() { console.log('eval', JSON.stringify(arguments)); };eval('console.log("Hello world!")');Function = function() { console.log('Function', JSON.stringify(arguments)); return function() {}; };new Function('console.log("Hello world!")')();
但是可能不是全局使用:
(function(){}).constructor('console.log("Hello world!")')()
截获 constructor 的示例代码
Function.prototype.__defineGetter__('constructor', function () {return function () { console.log('constructor', JSON.stringify(arguments)); }; }); (function() {}).constructor('console.log("Hello world!")');
目前能想到的是判断 eval 是否被重定向
示例,如果 eval 被重定向 z 变量不会被泄露
<span style="font-size: 18px"><code class="js hljs has-numbering">(<span class="hljs-function">function<span class="hljs-params">(x){<span class="hljs-keyword">var z = <span class="hljs-string">'console.log("Hello world!")';<span class="hljs-built_in">eval(<span class="hljs-string">'function x(){eval(z)}'); x(); })(<span class="hljs-function">function<span class="hljs-params">() { <span class="hljs-comment">/* ... */ });<br/><br/><span style="font-size: 18pt; background-color: #ff0000"><strong>uglify介绍<br/></strong></span></span></span></span></span></span></span></span></span></span></code></span>
概述:
<br/>
案例:Cesium打包流程,相关技术点和大概流程
原理:代码优化的意义:压缩 优化 混淆
优化:如何完善Cesium打包流程
<br/>
关键字:Cesium gulp uglifyjs
<br/>
字数:2330 | 阅读时间:7min+
<br/>
<br/>
1 Cesium打包流程
<br/>
如果没有记错,Cesium从2016年初对代码构建工具做了一次调整,从grunt改为gulp。作为一名业余选手,就不揣测两者的差别了。个人而言,gulp和Ant的思路很相似,通过管道连接,都是基于流的构建风格,而且gulp更像是JS的编码风格,自带一种亲切感。
<br/>
gulp.task('task1',['task0'], function() {
return fun_task1();
});
<br/>
Task语句是gulp中最常见的,懂了这句话,就等于你看懂脚本了。这句话的意思是,要执行task1,需要先执行task0,而task1的具体工作都在fun_task1方法中。这就是之前说的基于流的构建风格。有了这句话,在命令行中键入:gulp task1,回车执行该指令即可。
<br/>
先安装Node,环境变量等,并安装npm包后,即可使用gulp打包工具,这里推荐cnpm。环境搭建好后,命令行中键入gulp minify开始打包。完整的过程是build->generateStubs->minify。
<br/>
<br/>
Cesium打包流程
<br/>
build:准备工作,创建Build文件夹;将glsl文件转为js形式;最主要的是createCesiumJs方法,遍历Source中所有js脚本,将所有Object记录到Source/Cesium.js;其他的是范例,单元测试相关模块。
<br/>
generateStubs:用于单元测试,略。
<br/>
minify; 首先combineJavaScript主要做了两件事情,打包Cesium和Workers脚本,这是打包的最终结果。Gulp根据指令的不同,比如minify下采用uglify2优化,而combine对应的参数为none,生成路径为CesiumUnminified。
<br/>
另外,细心的人会发现,combineCesium的实现中有这样一句话path.relative('Source',require.resolve('almond')),这是一个小优化,almond是requirejs的精简包,因此,最终的Cesium.js中包含'almond脚本,内置了requirejs的主要方法。
<br/>
如上是Cesium打包的主要流程,简单说主要有3+1类个指令:
<br/>
Clean
清空文件
minify
打包&压缩
combine
只打包,不压缩
JScoverage
单元测试覆盖率,不了解
<br/>
2 代码优化
<br/>
对流程有了一个大概了解,下面,我们详细了解一下uglify2过程都做了哪些代码优化,一言以蔽之,压缩,优化,混淆。
<br/>
uglify2主要有三个参数:-o,-c,-m,-o参数必选,指定输出文件,-c压缩,-m混淆变量名。如下分别为combine、(uglifyjs -o)、(uglifyjs –c -m -o)的文件对比,单位是k:
<br/>
<br/>
uglify2的压缩对比
<br/>
都在一个屋檐下,差距怎么就这么大呢?我们简单说一下从1~2,2~3之间青取之于蓝而胜于蓝的过程。
<br/>
1~2的过程其实很简单,就是干了三件事,去掉注释, 去掉多余的空格(换行符),去掉不必要的分号(;)。就这三件事情,文件一下子小了一半多,换句话就是平时你写的代码有一大半都是废话,此时你旁边的AI程序员可能会喃喃道来“你们人类好愚蠢~”。
<br/>
2~3则是很多小细节的综合应用:
<br/>
去掉一些实际没有调用的函数(Dead code);
将零散的变量声明合并,比如 var a; var b;变为var a,b;
逻辑函数的精简,比如if(a) b(); else c()变为a ? b() : c();
变量名的简化,比如var strObject;变为var s;
……
<br/>
这些小技巧有很多,具体要看不同的压缩工具的考虑优劣,但有些压缩高效的工具并不稳定,可能会破坏语法规范或语意,所以没必要为了几个kb承担过多的风险,目前比较成熟的工具主要有三个uglify2,google closure以及yuicompressor,具体优劣得自己来体会了,我是按照自己的理解给出的先后顺序。最终的效果如下:
<br/>
<br/>
Cesium脚本效果
<br/>
这样的代码只能用单位“坨”来形容了,人类是无法直接读懂的,那浏览器能读懂吗?这是一个好问题!如下是V8引擎对JS语法解析的大概流程:
<br/>
<br/>
V8引擎解析JS脚本
<br/>
下面是在我本机Chrome解析Cesium.js脚本花费时间(脚本从下载完到浏览器解析完的时间差),单位毫秒,因为只测试了一次,可能会有误差,但基本吻合期望值:
<br/>
<br/>
JS脚本解析时间对比
<br/>
首先因为是本机测试,脚本无论是最大的8M还是最小的2.4M,下载速度都很快,因此我们不讨论(但实际应用中要考虑)脚本下载所需时间。
<br/>
其次,如上图,多了一个source,这是源码情况下,这个时间水分比较大,因为是零散的文件,可以做到按需下载,但因为文件比较琐碎,性能也不高。
<br/>
结论是,这种JS脚本优化策略对浏览器的影响不大,浏览器看到优化后的代码,可能会愣一会神,但很快就克服了。
<br/>
3实战
<br/>
知道了代码优化的大概原理,回顾一下代码优化的目的(压缩,优化,混淆),匹配一下结果是否符合期望值。嗯,其一,脚本的大小小了,其二,代码效率也优化了,其三,别人也看不懂了。似乎该做的都已经做了,这个脚本已经很完美了。
<br/>
<br/>
Format后的效果
<br/>
毛爷爷说,与人斗其乐无穷。确实,前两点的目的达到了,但第三点,还差很多。如上,和刚才的脚本是同一个文件,我只是用Chrome的调试工具format而已。这就是理想和现实之间的差距。
<br/>
可见,Cesium默认打包工具在压缩和优化上都没有问题,但在混淆上并不充分,当然Cesium本身是开源的,也没必要搞这些。客观说,JS脚本是明码的,所以反编译只是时间和能力的问题,所以不妨换个态度来看待这个问题,增加反编译的成本,当该成本大于购买成本即可
<br/>
以上是程式碼壓縮混淆加密的實例詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

發現某下載網站下載的壓縮包,解壓縮後再打包會比原來的壓縮包大一些,小的幾十Kb的差別,大的幾十Mb的差別,如果上傳到雲盤或付費空間,文件少無所謂,文件多的話,大大的增加儲存成本。特意研究了下,有需要的可以藉鏡。壓縮等級:9-極限壓縮字典大小:256或384,字典越壓縮越慢,256MB之前壓縮率差異較大,384MB後壓縮率無差別單字大小:最大273參數:f=BCJ2,測試加參數壓縮率會高一些

小編將為大家介紹加密壓縮的三種方法:方法一:加密最簡單的加密方法,就是在加密檔案時輸入想要設定的密碼,完成加密和壓縮了。方法二:自動加密普通的加密方式,需要我們加密每個檔案的時候都需要輸入密碼,如果你想要加密大量壓縮包,並且密碼是一樣的話,那麼我們可以在WinRAR中設定自動加密,之後只要正常壓縮文件,WinRAR會為每個壓縮包添加密碼。方法如下:開啟WinRAR,點選選項–設定設定介面中,切換到【壓縮】,點選建立預設設定–設定密碼在這裡輸入我們想要設定的密碼,點選確定就完成設定了,我們只需要正

資料夾加密是一種常見的資料保護方法,它可以將資料夾中的內容加密,使得只有掌握解密密碼的人能夠存取其中的檔案。在進行資料夾加密時,有一些常見的方法可以用來設定密碼,而不需要壓縮檔案。首先,我們可以使用作業系統自帶的加密功能來設定資料夾密碼。對於Windows使用者來說,可以透過以下步驟來設定:選擇要加密的資料夾,滑鼠右鍵點選資料夾,選擇「屬性」選

辦公人員在工作中使用wps軟體進行操作的頻率特別地多,有時一天會輸入多個文件,然後發送給領導或發送到指定位置,那麼wps軟體如何壓縮文件夾打包發送呢,下面小編就教大家這個操作步驟。首先,將要傳送的文件和資料夾整理到同一個資料夾中。如果有很多文件,最好將每個文件命名,這樣在發送時更容易識別。 第二步,這個時候點擊這個大的資料夾,然後點擊滑鼠右鍵。選擇“新增到壓縮檔案”。 第三步,這個時候軟體會自動幫我們打包我們的文件,選項“壓縮到XX.zip”,這個zip就是打包的格式,然後點擊立即壓縮。

在蘋果手機中,用戶可以根據自己的需求來加密相簿。有些用戶並不清楚該怎麼設定。可以將需要加密圖片加入備忘錄後,再去鎖定備忘錄即可,接下來就是小編為用戶帶來的手機相簿加密設定方法的介紹,有興趣的用戶快來一起看看吧!蘋果手機使用教學蘋果手機相簿加密怎麼設定答:將需要加密圖片加入備忘錄後,再去鎖定備忘錄即可詳情介紹:1、進入相冊,選擇需要加密的圖片,再點擊下方的【新增到】。 2、選擇【加入備忘錄】。 3.進入備忘錄,找到剛更創建的備忘錄,進入,點選右上角的【傳送】圖示。 4.點選下方的【鎖定備

WinRAR是一款功能強大的壓縮檔案管理工具,提供了豐富的功能和易於使用的介面。 WinRAR64位元版本特別針對64位元作業系統進行了最佳化,能夠更好地利用系統資源和效能。接下來就請小編為大家介紹一下winrar64位元以及解答一下winrar怎麼解壓縮吧!一、winrar64位元是什麼軟體WinRAR是一款功能強大的壓縮套件管理器。這款軟體可用於備份您的數據,縮減電子郵件附件的大小,解壓縮從Internet上下載的RAR、ZIP及其它文件,並且可以新建RAR及ZIP格式的文件。目前最新WINRAR版本為Wi

Linux中打包和壓縮是常用的操作,但許多使用者往往混淆這兩者的概念。本文將詳細討論在Linux系統中打包和壓縮的不同之處,並透過具體的程式碼範例來幫助讀者更好地理解。首先,需要明確打包和壓縮的區別。打包是將多個文件或目錄組合成一個單獨的文件,通常用於整理、歸檔或傳輸文件。而壓縮是將一個或多個檔案透過演算法壓縮,以減少檔案的大小,節省儲存空間或加快傳輸速

原文作者:Meteor,ChainCatcher原文編輯:Marco,ChainCatcher近期,全鏈互通協議Analog因披露1600萬美元融資而進入公眾視野,投資機構包括TribeCapital、NGCVentures、Wintermute、GSR、NEAR、OrangeDAO,以及MikeNogratzO,以及MikeNogratz的另類資產管理公司SamaraAssetGroup、BalajiSrinivasan等。 2023年底,Analog在業界引起了一些轟動,他們在X平台發布了關於開放測試網註冊活動的
