前言
JavaScript 壓縮、混淆和加密技術(shù)
對(duì)于網(wǎng)頁(yè)來(lái)說,其邏輯是依賴于JavaScript來(lái)實(shí)現(xiàn)的,JavaScript 有如下特點(diǎn):
- JavaScript 代碼運(yùn)行于客戶端,也就是它必須要在用戶瀏覽器端加載并運(yùn)行。
- JavaScript 代碼是公開透明的,也就是說瀏覽器可以直接獲取到正在運(yùn)行的 JavaScript 的源碼。
聲明
本文章中所有內(nèi)容僅供學(xué)習(xí)交流使用,不用于其他任何目的,不提供完整代碼,抓包內(nèi)容、敏感網(wǎng)址、數(shù)據(jù)接口等均已做脫敏處理,嚴(yán)禁用于商業(yè)用途和非法用途,否則由此產(chǎn)生的一切后果均與作者無(wú)關(guān)!
本文章未經(jīng)許可禁止轉(zhuǎn)載,禁止任何修改后二次傳播,擅自使用本文講解的技術(shù)而導(dǎo)致的任何意外,作者均不負(fù)責(zé),若有侵權(quán),請(qǐng)聯(lián)系作者立即刪除,請(qǐng)各位自覺遵守相關(guān)法律法規(guī)。
一. JS簡(jiǎn)介
壓縮、混淆、加密技術(shù)簡(jiǎn)述如下
- 代碼壓縮:即去除JavaScript 代碼中的不必要的空格、換行等內(nèi)容,使源碼都?jí)嚎s為幾行內(nèi)容,降低代碼可讀性,當(dāng)然同時(shí)也能提高網(wǎng)站的加載速度。
- 代碼混淆:使用變量替換、字符串陣列化、控制流平坦化、多態(tài)變異、僵尸函數(shù)、調(diào)試保護(hù)等手段,使代碼變得難以閱讀和分析,達(dá)到最終保護(hù)的目的。但這不影響代碼原有功能。是理想、實(shí)用的JavaScript保護(hù)方案
- 代碼加密:可以通過某種手段將 JavaScript 代碼進(jìn)行加密,轉(zhuǎn)成人無(wú)法閱讀或者解析的代碼,如將代碼完全抽象化加密,如 eval 加密。另外還有更強(qiáng)大的加密技術(shù),可以直接將 JavaScript 代碼用 C/C++ 實(shí)現(xiàn),JavaScript 調(diào)用其編譯后形成的文件來(lái)執(zhí)行相應(yīng)的功能,如Emscripten 還有 WebAssembly。
二. OB混淆
OB 混淆全稱 Obfuscator,Obfuscator 其實(shí)就是混淆的意思
官方文檔:https://obfuscator.io/
作者是一位叫 Timofey Kachalov 的俄羅斯JavaScript開發(fā)工程師,早在 2016 年就發(fā)布了第一個(gè)版本。
1. OB混淆特征
- 1、一般由一個(gè)大數(shù)組或者含有大數(shù)組的函數(shù)、一個(gè)自執(zhí)行函數(shù)、解密函數(shù)和加密后的函數(shù)四部分組成;
- 2、函數(shù)名和變量名通常以 _0x 或者 0x 開頭,后接 1~6 位數(shù)字或字母組合;
- 3、自執(zhí)行函數(shù),進(jìn)行移位操作,有明顯的 push、shift 關(guān)鍵字;
例如在上面的例子中,_0x3f26() 方法就定義了一個(gè)大數(shù)組,自執(zhí)行函數(shù)里有 push、shift 關(guān)鍵字,主要是對(duì)大數(shù)組進(jìn)行移位操作,_0x1fe9() 就是解密函數(shù),hi() 就是加密后的函數(shù)。
2. OB混淆介紹
JavaScript 混淆完全是在 JavaScript 上面進(jìn)行的處理,它的目的就是使得 JavaScript 變得難以閱讀和分析,大大降低代碼可讀性,是一種很實(shí)用的 JavaScript 保護(hù)方案。
JavaScript 混淆技術(shù)主要有以下幾種:
- 變量混淆
將帶有含意的變量名、方法名、常量名隨機(jī)變?yōu)闊o(wú)意義的類亂碼字符串,降低代碼可讀性,如轉(zhuǎn)成單個(gè)字符或十六進(jìn)制字符串。
- 字符串混淆
將字符串陣列化集中放置、并可進(jìn)行 MD5 或 Base64 加密存儲(chǔ),使代碼中不出現(xiàn)明文字符串,這樣可以避免使用全局搜索字符串的方式定位到入口點(diǎn)。
- 屬性加密
針對(duì) JavaScript 對(duì)象的屬性進(jìn)行加密轉(zhuǎn)化,隱藏代碼之間的調(diào)用關(guān)系。
- 控制流平坦化
打亂函數(shù)原有代碼執(zhí)行流程及函數(shù)調(diào)用關(guān)系,使代碼邏變得混亂無(wú)序。
- 僵尸代碼
隨機(jī)在代碼中插入無(wú)用的僵尸代碼、僵尸函數(shù),進(jìn)一步使代碼混亂。
- 調(diào)試保護(hù)
基于調(diào)試器特性,對(duì)當(dāng)前運(yùn)行環(huán)境進(jìn)行檢驗(yàn),加入一些強(qiáng)制調(diào)試 debugger 語(yǔ)句,使其在調(diào)試模式下難以順利執(zhí)行 JavaScript 代碼。
- 多態(tài)變異
使 JavaScript 代碼每次被調(diào)用時(shí),將代碼自身即立刻自動(dòng)發(fā)生變異,變化為與之前完全不同的代碼,即功能完全不變,只是代碼形式變異,以此杜絕代碼被動(dòng)態(tài)分析調(diào)試。
- 鎖定域名
使 JavaScript 代碼只能在指定域名下執(zhí)行。
- 反格式化
如果對(duì) JavaScript 代碼進(jìn)行格式化,則無(wú)法執(zhí)行,導(dǎo)致瀏覽器假死。
- 特殊編碼
將 JavaScript 完全編碼為人不可讀的代碼,如表情符號(hào)、特殊表示內(nèi)容等等。
總之,以上方案都是 JavaScript 混淆的實(shí)現(xiàn)方式,可以在不同程度上保護(hù) JavaScript 代碼。
3.OB混淆JS模塊
在前端開發(fā)中,現(xiàn)在 JavaScript 混淆主流的實(shí)現(xiàn)是 javascript-obfuscator 這個(gè)庫(kù),利用它我們可以非常方便地實(shí)現(xiàn)頁(yè)面的混淆,它與 Webpack 結(jié)合起來(lái),最終可以輸出壓縮和混淆后的 JavaScript 代碼,使得可讀性大大降低,難以逆向。
下面我們會(huì)介紹下 javascript-obfuscator 對(duì)代碼混淆的實(shí)現(xiàn),了解了其實(shí)現(xiàn),那么自然我們就對(duì)混淆的機(jī)理有了更加深刻的認(rèn)識(shí)。
官方文檔:https://obfuscator.io/
它是支持 ES8 的免費(fèi)、高效的 JavaScript 混淆庫(kù),它可以使得你的 JavaScript 代碼經(jīng)過混淆后難以被復(fù)制、盜用,混淆后的代碼具有和原來(lái)的代碼一模一樣的功能。
怎么使用呢?首先,我們需要安裝好 Node.js,可以使用 npm 命令。
然后新建一個(gè)文件夾,比如 js-obfuscate,隨后進(jìn)入該文件夾,初始化工作空間:
npm init
python運(yùn)行
- 1
提示我們輸入一些信息,創(chuàng)建一個(gè) package.json 文件,這就完成了項(xiàng)目初始化了。
接下來(lái)我們來(lái)安裝 javascript-obfuscator這個(gè)庫(kù):
npm install javascript-obfuscator -g
python運(yùn)行
- 1
安裝完成后,javascript-obfuscator就是一個(gè)獨(dú)立的可執(zhí)行命令了。
接下來(lái)我們就可以編寫代碼來(lái)實(shí)現(xiàn)混淆了
1. 代碼壓縮
這里javascript-obfuscator也提供了代碼壓縮的功能,使用其參數(shù) compact即可完成JavaScript 代碼的壓縮,輸出為一行內(nèi)容。默認(rèn)是 true,如果定義為 false,則混淆后的代碼會(huì)分行顯示。
var code = ` let x = '1' + 1 console.log('x', x) ` const options = { compact: true, // 代碼壓縮配置 } const obfuscator = require('javascript-obfuscator') function obfuscate(code, options) { return obfuscator.obfuscate(code, options).getObfuscatedCode() } console.log(obfuscate(code, options))
javascript運(yùn)行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
2. 變量名混淆
變量名混淆可以通過配置identifierNamesGenerator 參數(shù)實(shí)現(xiàn),我們通過這個(gè)參數(shù)可以控制變量名混淆的方式,如hexadecimal則會(huì)替換為 16 進(jìn)制形式的字符串,在這里我們可以設(shè)定如下值:
- hexadecimal:將變量名替換為 16 進(jìn)制形式的字符串,如 0xabc123。
- mangled:將變量名替換為普通的簡(jiǎn)寫字符,如 a、b、c 等。
該參數(shù)默認(rèn)為hexadecimal。
我們將該參數(shù)修改為 mangled 來(lái)試一下:
const code = ` let hello = '1' + 1 console.log('hello', hello) ` const options = { compact: true, identifierNamesGenerator: 'mangled' }
javascript運(yùn)行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
3 字符串混淆
字符串混淆,即將一個(gè)字符串聲明放到一個(gè)數(shù)組里面,使之無(wú)法被直接搜索到。我們可以通過控制 stringArray 參數(shù)來(lái)控制,默認(rèn)為 true。
const code = ` var a = 'hello world' `; const options = { compact: false, unicodeEscapeSequence: true //對(duì)字符串進(jìn)行 Unicode 轉(zhuǎn)碼 };
javascript運(yùn)行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
三. 實(shí)戰(zhàn)案例分析
1. 混淆專題逆向
- 逆向目標(biāo):極簡(jiǎn)壁紙
- 逆向思路:
進(jìn)行抓包 發(fā)現(xiàn)預(yù)覽響應(yīng)部分有加密數(shù)據(jù) 然后進(jìn)行抓包,發(fā)現(xiàn)該網(wǎng)站有JS混淆,對(duì)其進(jìn)行parse hook技術(shù) 獲取到所需要的密文params,繼續(xù)下一步到明文,然后挨個(gè)看其啟動(dòng)器 獲取到可疑位置 _0x5bb208 || 0x0 != _0x3c6b83['data']['code'] || (_0x3c6b83['data']['result'] = JSON['parse'](_0xf79b3e['a']['decipher'](_0x3c6b83['data']['result']))) 分析一下這一部分(該部分即為所需的圖片id部分) JSON['parse'](_0xf79b3e['a']['decipher'](_0x3c6b83['data']['result'])) 通過控制臺(tái) _0x3c6b83['data']['result'] 部分為響應(yīng)部分的result密文內(nèi)容 所以對(duì)前一部分推測(cè):_0xf79b3e['a']['decipher']為加解密部分 function _0x563330(_0x1e29f9) { // return _0x3ed467(_0x4207c2(_0x1e29f9)); return _0x3ef903(_0x3ed467(_0x4207c2(_0x1e29f9))); }該部分為需要解決的JS逆向部分 然后對(duì)該部分進(jìn)行扣JS即可
python運(yùn)行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
結(jié)果展示:
2. octet-stream類型混淆破解
- 逆向目標(biāo):產(chǎn)業(yè)政策大數(shù)據(jù)平臺(tái)
- 逆向參數(shù):載荷參數(shù)加密
進(jìn)入之后,發(fā)現(xiàn)有無(wú)限debugger,先用hook過debugger
該位置為請(qǐng)求攔截器位置
找到該請(qǐng)求攔截器中的載荷參數(shù)加密部分
對(duì)該部分進(jìn)行JS逆向即可
最后結(jié)果展示
參考文檔:
https://blog.csdn.net/weixin_38819889/article/details/107188552