日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

第27讲:令人抓狂的 JavaScript 混淆技术

發(fā)布時(shí)間:2024/4/11 javascript 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 第27讲:令人抓狂的 JavaScript 混淆技术 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

我們?cè)谂廊【W(wǎng)站的時(shí)候,經(jīng)常會(huì)遇到各種各樣類似加密的情形,比如:

  • 某個(gè)網(wǎng)站的 URL 帶有一些看不懂的長串加密參數(shù),想要抓取就必須要懂得這些參數(shù)是怎么構(gòu)造的,否則我們連完整的 URL 都構(gòu)造不出來,更不用說爬取了。
  • 分析某個(gè)網(wǎng)站的 Ajax 接口的時(shí)候,可以看到接口的一些參數(shù)也是加密的,或者 Request Headers 里面也可能帶有一些加密參數(shù),如果不知道這些參數(shù)的具體構(gòu)造邏輯就無法直接用程序來模擬這些 Ajax 請(qǐng)求。
  • 翻看網(wǎng)站的 JavaScript 源代碼,可以發(fā)現(xiàn)很多壓縮了或者看不太懂的字符,比如 JavaScript 文件名被編碼,JavaScript 的文件內(nèi)容被壓縮成幾行,JavaScript 變量也被修改成單個(gè)字符或者一些十六進(jìn)制的字符,導(dǎo)致我們不好輕易根據(jù) JavaScript 找出某些接口的加密邏輯。

這些情況,基本上都是網(wǎng)站為了保護(hù)其本身的一些數(shù)據(jù)不被輕易抓取而采取的一些措施,我們可以把它歸為兩大類:

  • 接口加密技術(shù);
  • JavaScript 壓縮、混淆和加密技術(shù)。

本課時(shí)我們就來了解下這兩類技術(shù)的實(shí)現(xiàn)原理。

1.數(shù)據(jù)保護(hù)

當(dāng)今大數(shù)據(jù)時(shí)代,數(shù)據(jù)已經(jīng)變得越來越重要,網(wǎng)頁和 App 現(xiàn)在是主流的數(shù)據(jù)載體,如果其數(shù)據(jù)的接口沒有設(shè)置任何保護(hù)措施,在爬蟲工程師解決了一些基本的反爬如封 IP、驗(yàn)證碼的問題之后,那么數(shù)據(jù)還是可以被輕松抓取到。

那么,有沒有可能在接口或 JavaScript 層面也加上一層防護(hù)呢?答案是可以的。

2.接口加密技術(shù)

網(wǎng)站運(yùn)營商首先想到防護(hù)措施可能是對(duì)某些數(shù)據(jù)接口進(jìn)行加密,比如說對(duì)某些 URL 的一些參數(shù)加上校驗(yàn)碼或者把一些 ID 信息進(jìn)行編碼,使其變得難以閱讀或構(gòu)造;或者對(duì)某些接口請(qǐng)求加上一些 token、sign 等簽名,這樣這些請(qǐng)求發(fā)送到服務(wù)器時(shí),服務(wù)器會(huì)通過客戶端發(fā)來的一些請(qǐng)求信息以及雙方約定好的秘鑰等來對(duì)當(dāng)前的請(qǐng)求進(jìn)行校驗(yàn),如果校驗(yàn)通過,才返回對(duì)應(yīng)數(shù)據(jù)結(jié)果。

比如說客戶端和服務(wù)端約定一種接口校驗(yàn)邏輯,客戶端在每次請(qǐng)求服務(wù)端接口的時(shí)候都會(huì)附帶一個(gè) sign 參數(shù),這個(gè) sign 參數(shù)可能是由當(dāng)前時(shí)間信息、請(qǐng)求的 URL、請(qǐng)求的數(shù)據(jù)、設(shè)備的 ID、雙方約定好的秘鑰經(jīng)過一些加密算法構(gòu)造而成的,客戶端會(huì)實(shí)現(xiàn)這個(gè)加密算法構(gòu)造 sign,然后每次請(qǐng)求服務(wù)器的時(shí)候附帶上這個(gè)參數(shù)。服務(wù)端會(huì)根據(jù)約定好的算法和請(qǐng)求的數(shù)據(jù)對(duì) sign 進(jìn)行校驗(yàn),如果校驗(yàn)通過,才返回對(duì)應(yīng)的數(shù)據(jù),否則拒絕響應(yīng)。

3.JavaScript 壓縮、混淆和加密技術(shù)

接口加密技術(shù)看起來的確是一個(gè)不錯(cuò)的解決方案,但單純依靠它并不能很好地解決問題。為什么呢?

對(duì)于網(wǎng)頁來說,其邏輯是依賴于 JavaScript 來實(shí)現(xiàn)的,JavaScript 有如下特點(diǎn):

  • JavaScript 代碼運(yùn)行于客戶端,也就是它必須要在用戶瀏覽器端加載并運(yùn)行。
  • JavaScript 代碼是公開透明的,也就是說瀏覽器可以直接獲取到正在運(yùn)行的 JavaScript 的源碼。

由于這兩個(gè)原因,導(dǎo)致 JavaScript 代碼是不安全的,任何人都可以讀、分析、復(fù)制、盜用,甚至篡改。

所以說,對(duì)于上述情形,客戶端 JavaScript 對(duì)于某些加密的實(shí)現(xiàn)是很容易被找到或模擬的,了解了加密邏輯后,模擬參數(shù)的構(gòu)造和請(qǐng)求也就是輕而易舉了,所以如果 JavaScript 沒有做任何層面的保護(hù)的話,接口加密技術(shù)基本上對(duì)數(shù)據(jù)起不到什么防護(hù)作用。

如果你不想讓自己的數(shù)據(jù)被輕易獲取,不想他人了解 JavaScript 邏輯的實(shí)現(xiàn),或者想降低被不懷好意的人甚至是黑客攻擊。那么你就需要用到 JavaScript 壓縮、混淆和加密技術(shù)了。

這里壓縮、混淆、加密技術(shù)簡述如下。

  • 代碼壓縮:即去除 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)成人無法閱讀或者解析的代碼,如將代碼完全抽象化加密,如 eval 加密。另外還有更強(qiáng)大的加密技術(shù),可以直接將 JavaScript 代碼用 C/C++ 實(shí)現(xiàn),JavaScript 調(diào)用其編譯后形成的文件來執(zhí)行相應(yīng)的功能,如 Emscripten 還有 WebAssembly。

下面我們對(duì)上面的技術(shù)分別予以介紹。

4.接口加密技術(shù)

數(shù)據(jù)一般都是通過服務(wù)器提供的接口來獲取的,網(wǎng)站或 App 可以請(qǐng)求某個(gè)數(shù)據(jù)接口獲取到對(duì)應(yīng)的數(shù)據(jù),然后再把獲取的數(shù)據(jù)展示出來。

但有些數(shù)據(jù)是比較寶貴或私密的,這些數(shù)據(jù)肯定是需要一定層面上的保護(hù)。所以不同接口的實(shí)現(xiàn)也就對(duì)應(yīng)著不同的安全防護(hù)級(jí)別,我們這里來總結(jié)下。

4.1完全開放的接口

有些接口是沒有設(shè)置任何防護(hù)的,誰都可以調(diào)用和訪問,而且沒有任何時(shí)空限制和頻率限制。任何人只要知道了接口的調(diào)用方式就能無限制地調(diào)用。

這種接口的安全性是非常非常低的,如果接口的調(diào)用方式一旦泄露或被抓包獲取到,任何人都可以無限制地對(duì)數(shù)據(jù)進(jìn)行操作或訪問。此時(shí)如果接口里面包含一些重要的數(shù)據(jù)或隱私數(shù)據(jù),就能輕易被篡改或竊取了。

4.2接口參數(shù)加密

為了提升接口的安全性,客戶端會(huì)和服務(wù)端約定一種接口校驗(yàn)方式,一般來說會(huì)使用到各種加密和編碼算法,如 Base64、Hex 編碼,MD5、AES、DES、RSA 等加密。

比如客戶端和服務(wù)器雙方約定一個(gè) sign 用作接口的簽名校驗(yàn),其生成邏輯是客戶端將 URL Path 進(jìn)行 MD5 加密然后拼接上 URL 的某個(gè)參數(shù)再進(jìn)行 Base64 編碼,最后得到一個(gè)字符串 sign,這個(gè) sign 會(huì)通過 Request URL 的某個(gè)參數(shù)或 Request Headers 發(fā)送給服務(wù)器。服務(wù)器接收到請(qǐng)求后,對(duì) URL Path 同樣進(jìn)行 MD5 加密,然后拼接上 URL 的某個(gè)參數(shù),也進(jìn)行 Base64 編碼得到了一個(gè) sign,然后比對(duì)生成的 sign 和客戶端發(fā)來的 sign 是否是一致的,如果是一致的,那就返回正確的結(jié)果,否則拒絕響應(yīng)。這就是一個(gè)比較簡單的接口參數(shù)加密的實(shí)現(xiàn)。如果有人想要調(diào)用這個(gè)接口的話,必須要定義好 sign 的生成邏輯,否則是無法正常調(diào)用接口的。

以上就是一個(gè)基本的接口參數(shù)加密邏輯的實(shí)現(xiàn)。

當(dāng)然上面的這個(gè)實(shí)現(xiàn)思路比較簡單,這里還可以增加一些時(shí)間戳信息增加時(shí)效性判斷,或增加一些非對(duì)稱加密進(jìn)一步提高加密的復(fù)雜程度。但不管怎樣,只要客戶端和服務(wù)器約定好了加密和校驗(yàn)邏輯,任何形式加密算法都是可以的。

這里要實(shí)現(xiàn)接口參數(shù)加密就需要用到一些加密算法,客戶端和服務(wù)器肯定也都有對(duì)應(yīng)的 SDK 實(shí)現(xiàn)這些加密算法,如 JavaScript 的 crypto-js,Python 的 hashlib、Crypto 等等。

但還是如上文所說,如果是網(wǎng)頁的話,客戶端實(shí)現(xiàn)加密邏輯如果是用 JavaScript 來實(shí)現(xiàn),其源代碼對(duì)用戶是完全可見的,如果沒有對(duì) JavaScript 做任何保護(hù)的話,是很容易弄清楚客戶端加密的流程的。

因此,我們需要對(duì) JavaScript 利用壓縮、混淆、加密的方式來對(duì)客戶端的邏輯進(jìn)行一定程度上的保護(hù)。

5.JavaScript 壓縮、混淆、加密

下面我們?cè)賮斫榻B下 JavaScript 的壓縮、混淆和加密技術(shù)。

5.1JavaScript 壓縮

這個(gè)非常簡單,JavaScript 壓縮即去除 JavaScript 代碼中的不必要的空格、換行等內(nèi)容或者把一些可能公用的代碼進(jìn)行處理實(shí)現(xiàn)共享,最后輸出的結(jié)果都被壓縮為幾行內(nèi)容,代碼可讀性變得很差,同時(shí)也能提高網(wǎng)站加載速度。

如果僅僅是去除空格換行這樣的壓縮方式,其實(shí)幾乎是沒有任何防護(hù)作用的,因?yàn)檫@種壓縮方式僅僅是降低了代碼的直接可讀性。如果我們有一些格式化工具可以輕松將 JavaScript 代碼變得易讀,比如利用 IDE、在線工具或 Chrome 瀏覽器都能還原格式化的代碼。

目前主流的前端開發(fā)技術(shù)大多都會(huì)利用 Webpack 進(jìn)行打包,Webpack 會(huì)對(duì)源代碼進(jìn)行編譯和壓縮,輸出幾個(gè)打包好的 JavaScript 文件,其中我們可以看到輸出的 JavaScript 文件名帶有一些不規(guī)則字符串,同時(shí)文件內(nèi)容可能只有幾行內(nèi)容,變量名都是一些簡單字母表示。這其中就包含 JavaScript 壓縮技術(shù),比如一些公共的庫輸出成 bundle 文件,一些調(diào)用邏輯壓縮和轉(zhuǎn)義成幾行代碼,這些都屬于 JavaScript 壓縮。另外其中也包含了一些很基礎(chǔ)的 JavaScript 混淆技術(shù),比如把變量名、方法名替換成一些簡單字符,降低代碼可讀性。
但整體來說,JavaScript 壓縮技術(shù)只能在很小的程度上起到防護(hù)作用,要想真正提高防護(hù)效果還得依靠 JavaScript 混淆和加密技術(shù)。

5.2JavaScript 混淆

JavaScript 混淆完全是在 JavaScript 上面進(jìn)行的處理,它的目的就是使得 JavaScript 變得難以閱讀和分析,大大降低代碼可讀性,是一種很實(shí)用的 JavaScript 保護(hù)方案。

JavaScript 混淆技術(shù)主要有以下幾種:

  • 變量混淆

將帶有含意的變量名、方法名、常量名隨機(jī)變?yōu)闊o意義的類亂碼字符串,降低代碼可讀性,如轉(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)系,使代碼邏變得混亂無序。

  • 僵尸代碼

隨機(jī)在代碼中插入無用的僵尸代碼、僵尸函數(shù),進(jìn)一步使代碼混亂。

  • 調(diào)試保護(hù)

基于調(diào)試器特性,對(duì)當(dāng)前運(yùn)行環(huán)境進(jìn)行檢驗(yàn),加入一些強(qiáng)制調(diào)試 debugger 語句,使其在調(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)行格式化,則無法執(zhí)行,導(dǎo)致瀏覽器假死。

  • 特殊編碼

將 JavaScript 完全編碼為人不可讀的代碼,如表情符號(hào)、特殊表示內(nèi)容等等。

總之,以上方案都是 JavaScript 混淆的實(shí)現(xiàn)方式,可以在不同程度上保護(hù) JavaScript 代碼。

在前端開發(fā)中,現(xiàn)在 JavaScript 混淆主流的實(shí)現(xiàn)是 javascript-obfuscator 這個(gè)庫,利用它我們可以非常方便地實(shí)現(xiàn)頁面的混淆,它與 Webpack 結(jié)合起來,最終可以輸出壓縮和混淆后的 JavaScript 代碼,使得可讀性大大降低,難以逆向。

下面我們會(huì)介紹下 javascript-obfuscator 對(duì)代碼混淆的實(shí)現(xiàn),了解了實(shí)現(xiàn),那么自然我們就對(duì)混淆的機(jī)理有了更加深刻的認(rèn)識(shí)。

javascript-obfuscator 的官網(wǎng)地址為:https://obfuscator.io/,其官方介紹內(nèi)容如下: A free
and efficient obfuscator for JavaScript (including ES2017). Make your
code harder to copy and prevent people from stealing your work.

它是支持 ES8 的免費(fèi)、高效的 JavaScript 混淆庫,它可以使得你的 JavaScript 代碼經(jīng)過混淆后難以被復(fù)制、盜用,混淆后的代碼具有和原來的代碼一模一樣的功能。

怎么使用呢?首先,我們需要安裝好 Node.js,可以使用 npm 命令。

然后新建一個(gè)文件夾,比如 js-obfuscate,隨后進(jìn)入該文件夾,初始化工作空間:

npm init

這里會(huì)提示我們輸入一些信息,創(chuàng)建一個(gè) package.json 文件,這就完成了項(xiàng)目初始化了。

接下來我們來安裝 javascript-obfuscator 這個(gè)庫:

npm install --save-dev javascript-obfuscator

接下來我們就可以編寫代碼來實(shí)現(xiàn)混淆了,如新建一個(gè) main.js 文件,內(nèi)容如下:

const code = ` let x = '1' + 1 console.log('x', x) `const options = {compact: false,controlFlowFlattening: true}const obfuscator = require('javascript-obfuscator') function obfuscate(code, options) {return obfuscator.obfuscate(code, options).getObfuscatedCode() } console.log(obfuscate(code, options))

在這里我們定義了兩個(gè)變量,一個(gè)是 code,即需要被混淆的代碼,另一個(gè)是混淆選項(xiàng),是一個(gè) Object。接下來我們引入了 javascript-obfuscator 庫,然后定義了一個(gè)方法,傳入 code 和 options,來獲取混淆后的代碼,最后控制臺(tái)輸出混淆后的代碼。

代碼邏輯比較簡單,我們來執(zhí)行一下代碼:

node main.js

輸出結(jié)果如下:

var _0x53bf = ['log']; (function (_0x1d84fe, _0x3aeda0) {var _0x10a5a = function (_0x2f0a52) {while (--_0x2f0a52) {_0x1d84fe['push'](_0x1d84fe['shift']());}};_0x10a5a(++_0x3aeda0); }(_0x53bf, 0x172)); var _0x480a = function (_0x4341e5, _0x5923b4) {_0x4341e5 = _0x4341e5 - 0x0;var _0xb3622e = _0x53bf[_0x4341e5];return _0xb3622e; }; let x = '1' + 0x1; console[_0x480a('0x0')]('x', x);

看到了吧,這么簡單的兩行代碼,被我們混淆成了這個(gè)樣子,其實(shí)這里我們就是設(shè)定了一個(gè)“控制流扁平化”的選項(xiàng)。

整體看來,代碼的可讀性大大降低,也大大加大了 JavaScript 調(diào)試的難度。

好,接下來我們來跟著 javascript-obfuscator 走一遍,就能具體知道 JavaScript 混淆到底有多少方法了。

代碼壓縮

這里 javascript-obfuscator 也提供了代碼壓縮的功能,使用其參數(shù) compact 即可完成 JavaScript 代碼的壓縮,輸出為一行內(nèi)容。默認(rèn)是 true,如果定義為 false,則混淆后的代碼會(huì)分行顯示。

示例如下:

const code = ` let x = '1' + 1 console.log('x', x) ` const options = {compact: false }

這里我們先把代碼壓縮 compact 選項(xiàng)設(shè)置為 false,運(yùn)行結(jié)果如下:

let x = '1' + 0x1; console['log']('x', x);

如果不設(shè)置 compact 或把 compact 設(shè)置為 true,結(jié)果如下:

var _0x151c=['log'];(function(_0x1ce384,_0x20a7c7){var _0x25fc92=function(_0x188aec){while(--_0x188aec){_0x1ce384['push'](_0x1ce384['shift']());}};_0x25fc92(++_0x20a7c7);}(_0x151c,0x1b7));var _0x553e=function(_0x259219,_0x241445){_0x259219=_0x259219-0x0;var _0x56d72d=_0x151c[_0x259219];return _0x56d72d;};let x='1'+0x1;console[_0x553e('0x0')]('x',x);

可以看到單行顯示的時(shí)候,對(duì)變量名進(jìn)行了進(jìn)一步的混淆和控制流扁平化操作。

變量名混淆

變量名混淆可以通過配置 identifierNamesGenerator 參數(shù)實(shí)現(xiàn),我們通過這個(gè)參數(shù)可以控制變量名混淆的方式,如 hexadecimal 則會(huì)替換為 16 進(jìn)制形式的字符串,在這里我們可以設(shè)定如下值:

  • hexadecimal:將變量名替換為 16 進(jìn)制形式的字符串,如 0xabc123。
  • mangled:將變量名替換為普通的簡寫字符,如 a、b、c 等。

該參數(shù)默認(rèn)為 hexadecimal。

我們將該參數(shù)修改為 mangled 來試一下:

const code = ` let hello = '1' + 1 console.log('hello', hello) ` const options = {compact: true,identifierNamesGenerator: 'mangled' }

運(yùn)行結(jié)果如下:

var a=['hello'];(function(c,d){var e=function(f){while(--f){c['push'](c['shift']());}};e(++d);}(a,0x9b));var b=function(c,d){c=c-0x0;var e=a[c];return e;};let hello='1'+0x1;console['log'](b('0x0'),hello);

可以看到這里的變量命名都變成了 a、b 等形式。

如果我們將 identifierNamesGenerator 修改為 hexadecimal 或者不設(shè)置,運(yùn)行結(jié)果如下:

var _0x4e98=['log','hello'];(function(_0x4464de,_0x39de6c){var _0xdffdda=function(_0x6a95d5){while(--_0x6a95d5){_0x4464de['push'](_0x4464de['shift']());}};_0xdffdda(++_0x39de6c);}(_0x4e98,0xc8));var _0x53cb=function(_0x393bda,_0x8504e7){_0x393bda=_0x393bda-0x0;var _0x46ab80=_0x4e98[_0x393bda];return _0x46ab80;};let hello='1'+0x1;console[_0x53cb('0x0')](_0x53cb('0x1'),hello);

可以看到選用了 mangled,其代碼體積會(huì)更小,但 hexadecimal 其可讀性會(huì)更低。

另外我們還可以通過設(shè)置 identifiersPrefix 參數(shù)來控制混淆后的變量前綴,示例如下:

const code = ` let hello = '1' + 1 console.log('hello', hello) ` const options = {identifiersPrefix: 'germey' }

運(yùn)行結(jié)果:

var germey_0x3dea=['log','hello'];(function(_0x348ff3,_0x5330e8){var _0x1568b1=function(_0x4740d8){while(--_0x4740d8){_0x348ff3['push'](_0x348ff3['shift']());}};_0x1568b1(++_0x5330e8);}(germey_0x3dea,0x94));var germey_0x30e4=function(_0x2e8f7c,_0x1066a8){_0x2e8f7c=_0x2e8f7c-0x0;var _0x5166ba=germey_0x3dea[_0x2e8f7c];return _0x5166ba;};let hello='1'+0x1;console[germey_0x30e4('0x0')](germey_0x30e4('0x1'),hello);

可以看到混淆后的變量前綴加上了我們自定義的字符串 germey。

另外 renameGlobals 這個(gè)參數(shù)還可以指定是否混淆全局變量和函數(shù)名稱,默認(rèn)為 false。示例如下:

const code = ` var $ = function(id) {return document.getElementById(id); }; ` const options = {renameGlobals: true }

運(yùn)行結(jié)果如下:

var _0x4864b0=function(_0x5763be){return document['getElementById'](_0x5763be);};

可以看到這里我們聲明了一個(gè)全局變量 ,在renameGlobals設(shè)置為true之后,,在 renameGlobals 設(shè)置為 true 之后,renameGlobals設(shè)true 這個(gè)變量也被替換了。如果后文用到了這個(gè) $ 對(duì)象,可能就會(huì)有找不到定義的錯(cuò)誤,因此這個(gè)參數(shù)可能導(dǎo)致代碼執(zhí)行不通。

如果我們不設(shè)置 renameGlobals 或者設(shè)置為 false,結(jié)果如下:

var _0x239a=['getElementById'];(function(_0x3f45a3,_0x583dfa){var _0x2cade2=function(_0x28479a){while(--_0x28479a){_0x3f45a3['push'](_0x3f45a3['shift']());}};_0x2cade2(++_0x583dfa);}(_0x239a,0xe1));var _0x3758=function(_0x18659d,_0x50c21d){_0x18659d=_0x18659d-0x0;var _0x531b8d=_0x239a[_0x18659d];return _0x531b8d;};var $=function(_0x3d8723){return document[_0x3758('0x0')](_0x3d8723);};

可以看到,最后還是有 $ 的聲明,其全局名稱沒有被改變。

字符串混淆

字符串混淆,即將一個(gè)字符串聲明放到一個(gè)數(shù)組里面,使之無法被直接搜索到。我們可以通過控制 stringArray 參數(shù)來控制,默認(rèn)為 true。

我們還可以通過 rotateStringArray 參數(shù)來控制數(shù)組化后結(jié)果的元素順序,默認(rèn)為 true。
還可以通過 stringArrayEncoding 參數(shù)來控制數(shù)組的編碼形式,默認(rèn)不開啟編碼,如果設(shè)置為 true 或 base64,則會(huì)使用 Base64 編碼,如果設(shè)置為 rc4,則使用 RC4 編碼。
還可以通過 stringArrayThreshold 來控制啟用編碼的概率,范圍 0 到 1,默認(rèn) 0.8。

示例如下:

const code = ` var a = 'hello world' ` const options = {stringArray: true,rotateStringArray: true,stringArrayEncoding: true, // 'base64' or 'rc4' or falsestringArrayThreshold: 1, }

運(yùn)行結(jié)果如下:

var _0x4215=['aGVsbG8gd29ybGQ='];(function(_0x42bf17,_0x4c348f){var _0x328832=function(_0x355be1){while(--_0x355be1){_0x42bf17['push'](_0x42bf17['shift']());}};_0x328832(++_0x4c348f);}(_0x4215,0x1da));var _0x5191=function(_0x3cf2ba,_0x1917d8){_0x3cf2ba=_0x3cf2ba-0x0;var _0x1f93f0=_0x4215[_0x3cf2ba];if(_0x5191['LqbVDH']===undefined){(function(){var _0x5096b2;try{var _0x282db1=Function('return\x20(function()\x20'+'{}.constructor(\x22return\x20this\x22)(\x20)'+');');_0x5096b2=_0x282db1();}catch(_0x2acb9c){_0x5096b2=window;}var _0x388c14='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';_0x5096b2['atob']||(_0x5096b2['atob']=function(_0x4cc27c){var _0x2af4ae=String(_0x4cc27c)['replace'](/=+$/,'');for(var _0x21400b=0x0,_0x3f4e2e,_0x5b193b,_0x233381=0x0,_0x3dccf7='';_0x5b193b=_0x2af4ae['charAt'](_0x233381++);~_0x5b193b&&(_0x3f4e2e=_0x21400b%0x4?_0x3f4e2e*0x40+_0x5b193b:_0x5b193b,_0x21400b++%0x4)?_0x3dccf7+=String['fromCharCode'](0xff&_0x3f4e2e>>(-0x2*_0x21400b&0x6)):0x0){_0x5b193b=_0x388c14['indexOf'](_0x5b193b);}return _0x3dccf7;});}());_0x5191['DuIurT']=function(_0x51888e){var _0x29801f=atob(_0x51888e);var _0x561e62=[];for(var _0x5dd788=0x0,_0x1a8b73=_0x29801f['length'];_0x5dd788<_0x1a8b73;_0x5dd788++){_0x561e62+='%'+('00'+_0x29801f['charCodeAt'](_0x5dd788)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x561e62);};_0x5191['mgoBRd']={};_0x5191['LqbVDH']=![外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-DLiWlT8j-1594122582185)(_0x1f93f0)];_0x5191['mgoBRd'][_0x3cf2ba]=_0x1f93f0;}else{_0x1f93f0=_0x1741f0;}return _0x1f93f0;};var a=_0x5191('0x0');

可以看到這里就把字符串進(jìn)行了 Base64 編碼,我們?cè)僖矡o法通過查找的方式找到字符串的位置了。

如果將 stringArray 設(shè)置為 false 的話,輸出就是這樣:

var a='hello\x20world';

字符串就仍然是明文顯示的,沒有被編碼。

另外我們還可以使用 unicodeEscapeSequence 這個(gè)參數(shù)對(duì)字符串進(jìn)行 Unicode 轉(zhuǎn)碼,使之更加難以辨認(rèn),示例如下:

const code = ` var a = 'hello world' ` const options = {compact: false,unicodeEscapeSequence: true }

運(yùn)行結(jié)果如下:

var _0x5c0d = ['\x68\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64']; (function (_0x54cc9c, _0x57a3b2) {var _0xf833cf = function (_0x3cd8c6) {while (--_0x3cd8c6) {_0x54cc9c['push'](_0x54cc9c['shift']());} }; _0xf833cf(++_0x57a3b2); }(_0x5c0d, 0x17d)); var _0x28e8 = function (_0x3fd645, _0x2cf5e7) {_0x3fd645 = _0x3fd645 - 0x0;var _0x298a20 = _0x5c0d[_0x3fd645];return _0x298a20; }; var a = _0x28e8('0x0');

可以看到,這里字符串被數(shù)字化和 Unicode 化,非常難以辨認(rèn)。

在很多 JavaScript 逆向的過程中,一些關(guān)鍵的字符串可能會(huì)作為切入點(diǎn)來查找加密入口。用了這種混淆之后,如果有人想通過全局搜索的方式搜索 hello 這樣的字符串找加密入口,也沒法搜到了。

代碼自我保護(hù)

我們可以通過設(shè)置 selfDefending 參數(shù)來開啟代碼自我保護(hù)功能。開啟之后,混淆后的 JavaScript 會(huì)強(qiáng)制以一行形式顯示,如果我們將混淆后的代碼進(jìn)行格式化(美化)或者重命名,該段代碼將無法執(zhí)行。

例如:

const code = ` console.log('hello world') ` const options = {selfDefending: true }

運(yùn)行結(jié)果如下:

var _0x26da=['log','hello\x20world'];(function(_0x190327,_0x57c2c0){var _0x577762=function(_0xc9dabb){while(--_0xc9dabb){_0x190327['push'](_0x190327['shift']());}};var _0x35976e=function(){var _0x16b3fe={'data':{'key':'cookie','value':'timeout'},'setCookie':function(_0x2d52d5,_0x16feda,_0x57cadf,_0x56056f){_0x56056f=_0x56056f||{};var _0x5b6dc3=_0x16feda+'='+_0x57cadf;var _0x333ced=0x0;for(var _0x333ced=0x0,_0x19ae36=_0x2d52d5['length'];_0x333ced<_0x19ae36;_0x333ced++){var _0x409587=_0x2d52d5[_0x333ced];_0x5b6dc3+=';\x20'+_0x409587;var _0x4aa006=_0x2d52d5[_0x409587];_0x2d52d5['push'](_0x4aa006);_0x19ae36=_0x2d52d5['length'];if(_0x4aa006!==!![]){_0x5b6dc3+='='+_0x4aa006;}}_0x56056f['cookie']=_0x5b6dc3;},'removeCookie'

如果我們將上述代碼放到控制臺(tái),它的執(zhí)行結(jié)果和之前是一模一樣的,沒有任何問題。
如果我們將其進(jìn)行格式化,會(huì)變成如下內(nèi)容:

var _0x26da = ['log', 'hello\x20world']; (function (_0x190327, _0x57c2c0) {var _0x577762 = function (_0xc9dabb) {while (--_0xc9dabb) {_0x190327['push'](_0x190327['shift']());}};var _0x35976e = function () {var _0x16b3fe = {'data': {'key': 'cookie','value': 'timeout'},'setCookie': function (_0x2d52d5, _0x16feda, _0x57cadf, _0x56056f) {_0x56056f = _0x56056f || {};var _0x5b6dc3 = _0x16feda + '=' + _0x57cadf;var _0x333ced = 0x0;for (var _0x333ced = 0x0, _0x19ae36 = _0x2d52d5['length']; _0x333ced < _0x19ae36; _0x333ced++) {var _0x409587 = _0x2d52d5[_0x333ced];_0x5b6dc3 += ';\x20' + _0x409587;var _0x4aa006 = _0x2d52d5[_0x409587];_0x2d52d5['push'](_0x4aa006);_0x19ae36 = _0x2d52d5['length'];if (_0x4aa006 !== !![]) {_0x5b6dc3 += '=' + _0x4aa006;}}_0x56056f['cookie'] = _0x5b6dc3;}, 'removeCookie': function () {return 'dev';}, 'getCookie': function (_0x30c497, _0x51923d) {_0x30c497 = _0x30c497 || function (_0x4b7e18) {return _0x4b7e18;};var _0x557e06 = _0x30c497(new RegExp('(?:^|;\x20)' + _0x51923d['replace'](/([.$?*|{}()[]\/+^])/g, '$1') + '=([^;]*)'));var _0x817646 = function (_0xf3fae7, _0x5d8208) {_0xf3fae7(++_0x5d8208);};_0x817646(_0x577762, _0x57c2c0);return _0x557e06 ? decodeURIComponent(_0x557e06[0x1]) : undefined;}};var _0x4673cd = function () {var _0x4c6c5c = new RegExp('\x5cw+\x20*\x5c(\x5c)\x20*{\x5cw+\x20*[\x27|\x22].+[\x27|\x22];?\x20*}');return _0x4c6c5c['test'](_0x16b3fe['removeCookie']['toString']());};_0x16b3fe['updateCookie'] = _0x4673cd;var _0x5baa80 = '';var _0x1faf19 = _0x16b3fe['updateCookie']();if (!_0x1faf19) {_0x16b3fe['setCookie'](['*'], 'counter', 0x1);} else if (_0x1faf19) {_0x5baa80 = _0x16b3fe['getCookie'](null, 'counter');} else {_0x16b3fe['removeCookie']();}};_0x35976e(); }(_0x26da, 0x140)); var _0x4391 = function (_0x1b42d8, _0x57edc8) {_0x1b42d8 = _0x1b42d8 - 0x0;var _0x2fbeca = _0x26da[_0x1b42d8];return _0x2fbeca; }; var _0x197926 = function () {var _0x10598f = !![];return function (_0xffa3b3, _0x7a40f9) {var _0x48e571 = _0x10598f ? function () {if (_0x7a40f9) {var _0x2194b5 = _0x7a40f9['apply'](_0xffa3b3, arguments);_0x7a40f9 = null;return _0x2194b5;}} : function () {};_0x10598f = ![];return _0x48e571;}; }(); var _0x2c6fd7 = _0x197926(this, function () {var _0x4828bb = function () {return '\x64\x65\x76';},_0x35c3bc = function () {return '\x77\x69\x6e\x64\x6f\x77';};var _0x456070 = function () {var _0x4576a4 = new RegExp('\x5c\x77\x2b\x20\x2a\x5c\x28\x5c\x29\x20\x2a\x7b\x5c\x77\x2b\x20\x2a\x5b\x27\x7c\x22\x5d\x2e\x2b\x5b\x27\x7c\x22\x5d\x3b\x3f\x20\x2a\x7d');return !_0x4576a4['\x74\x65\x73\x74'](_0x4828bb['\x74\x6f\x53\x74\x72\x69\x6e\x67']());};var _0x3fde69 = function () {var _0xabb6f4 = new RegExp('\x28\x5c\x5c\x5b\x78\x7c\x75\x5d\x28\x5c\x77\x29\x7b\x32\x2c\x34\x7d\x29\x2b');return _0xabb6f4['\x74\x65\x73\x74'](_0x35c3bc['\x74\x6f\x53\x74\x72\x69\x6e\x67']());};var _0x2d9a50 = function (_0x58fdb4) {var _0x2a6361 = ~-0x1 >> 0x1 + 0xff % 0x0;if (_0x58fdb4['\x69\x6e\x64\x65\x78\x4f\x66']('\x69' === _0x2a6361)) {_0xc388c5(_0x58fdb4);}};var _0xc388c5 = function (_0x2073d6) {var _0x6bb49f = ~-0x4 >> 0x1 + 0xff % 0x0;if (_0x2073d6['\x69\x6e\x64\x65\x78\x4f\x66']((!![] + '')[0x3]) !== _0x6bb49f) {_0x2d9a50(_0x2073d6);}};if (!_0x456070()) {if (!_0x3fde69()) {_0x2d9a50('\x69\x6e\x64\u0435\x78\x4f\x66');} else {_0x2d9a50('\x69\x6e\x64\x65\x78\x4f\x66');}} else {_0x2d9a50('\x69\x6e\x64\u0435\x78\x4f\x66');} }); _0x2c6fd7(); console[_0x4391('0x0')](_0x4391('0x1'));

控制流平坦化

控制流平坦化其實(shí)就是將代碼的執(zhí)行邏輯混淆,使其變得復(fù)雜難讀。其基本思想是將一些邏輯處理塊都統(tǒng)一加上一個(gè)前驅(qū)邏輯塊,每個(gè)邏輯塊都由前驅(qū)邏輯塊進(jìn)行條件判斷和分發(fā),構(gòu)成一個(gè)個(gè)閉環(huán)邏輯,導(dǎo)致整個(gè)執(zhí)行邏輯十分復(fù)雜難讀。

我們通過 controlFlowFlattening 變量可以控制是否開啟控制流平坦化,示例如下:

const code = ` (function(){function foo () {return function () {var sum = 1 + 2;console.log(1);console.log(2);console.log(3);console.log(4);console.log(5);console.log(6);}}foo()(); })(); ` const options = {compact: false,controlFlowFlattening: true }

輸出結(jié)果如下:

var _0xbaf1 = ['dZwUe','log','fXqMu','0|1|3|4|6|5|2','chYMl','IZEsA','split' ]; (function (_0x22d342, _0x4f6332) {var _0x43ff59 = function (_0x5ad417) {while (--_0x5ad417) {_0x22d342['push'](_0x22d342['shift']());}};_0x43ff59(++_0x4f6332); }(_0xbaf1, 0x192)); var _0x1a69 = function (_0x8d64b1, _0x5e07b3) {_0x8d64b1 = _0x8d64b1 - 0x0;var _0x300bab = _0xbaf1[_0x8d64b1];return _0x300bab; }; (function () {var _0x19d8ce = {'chYMl': _0x1a69('0x0'),'IZEsA': function (_0x22e521, _0x298a22) {return _0x22e521 + _0x298a22;},'fXqMu': function (_0x13124b) {return _0x13124b();}};function _0x4e2ee0() {var _0x118a6a = {'LZAQV': _0x19d8ce[_0x1a69('0x1')],'dZwUe': function (_0x362ef3, _0x352709) {return _0x19d8ce[_0x1a69('0x2')](_0x362ef3, _0x352709);}};return function () {var _0x4c336d = _0x118a6a['LZAQV'][_0x1a69('0x3')]('|'), _0x2b6466 = 0x0;while (!![]) {switch (_0x4c336d[_0x2b6466++]) {case '0':var _0xbfa3fd = _0x118a6a[_0x1a69('0x4')](0x1, 0x2);continue;case '1':console['log'](0x1);continue;case '2':console[_0x1a69('0x5')](0x6);continue;case '3':console[_0x1a69('0x5')](0x2);continue;case '4':console[_0x1a69('0x5')](0x3);continue;case '5':console[_0x1a69('0x5')](0x5);continue;case '6':console[_0x1a69('0x5')](0x4);continue;}break;}};}_0x19d8ce[_0x1a69('0x6')](_0x4e2ee0)(); }());

可以看到,一些連續(xù)的執(zhí)行邏輯被打破,代碼被修改為一個(gè) switch 語句,我們很難再一眼看出多條 console.log 語句的執(zhí)行順序了。

如果我們將 controlFlowFlattening 設(shè)置為 false 或者不設(shè)置,運(yùn)行結(jié)果如下:

var _0x552c = ['log']; (function (_0x4c4fa0, _0x59faa0) {var _0xa01786 = function (_0x409a37) {while (--_0x409a37) {_0x4c4fa0['push'](_0x4c4fa0['shift']());}};_0xa01786(++_0x59faa0); }(_0x552c, 0x9b)); var _0x4e63 = function (_0x75ea1a, _0x50e176) {_0x75ea1a = _0x75ea1a - 0x0;var _0x59dc94 = _0x552c[_0x75ea1a];return _0x59dc94; }; (function () {function _0x507f38() {return function () {var _0x17fb7e = 0x1 + 0x2;console[_0x4e63('0x0')](0x1);console['log'](0x2);console['log'](0x3);console[_0x4e63('0x0')](0x4);console[_0x4e63('0x0')](0x5);console[_0x4e63('0x0')](0x6);};}_0x507f38()(); }());

可以看到,這里仍然保留了原始的 console.log 執(zhí)行邏輯。

因此,使用控制流扁平化可以使得執(zhí)行邏輯更加復(fù)雜難讀,目前非常多的前端混淆都會(huì)加上這個(gè)選項(xiàng)。

但啟用控制流扁平化之后,代碼的執(zhí)行時(shí)間會(huì)變長,最長達(dá) 1.5 倍之多。

另外我們還能使用 controlFlowFlatteningThreshold 這個(gè)參數(shù)來控制比例,取值范圍是 0 到 1,默認(rèn) 0.75,如果設(shè)置為 0,那相當(dāng)于 controlFlowFlattening 設(shè)置為 false,即不開啟控制流扁平化 。

僵尸代碼注入

僵尸代碼即不會(huì)被執(zhí)行的代碼或?qū)ι舷挛臎]有任何影響的代碼,注入之后可以對(duì)現(xiàn)有的 JavaScript 代碼的閱讀形成干擾。我們可以使用 deadCodeInjection 參數(shù)開啟這個(gè)選項(xiàng),默認(rèn)為 false。

示例如下:

const code = ` (function(){if (true) {var foo = function () {console.log('abc');console.log('cde');console.log('efg');console.log('hij');};var bar = function () {console.log('klm');console.log('nop');console.log('qrs');};var baz = function () {console.log('tuv');console.log('wxy');console.log('z');};foo();bar();baz();} })(); ` const options = {compact: false,deadCodeInjection: true }

運(yùn)行結(jié)果如下:

var _0x5024 = ['zaU','log','tuv','wxy','abc','cde','efg','hij','QhG','TeI','klm','nop','qrs','bZd','HMx' ]; var _0x4502 = function (_0x1254b1, _0x583689) {_0x1254b1 = _0x1254b1 - 0x0;var _0x529b49 = _0x5024[_0x1254b1];return _0x529b49; }; (function () {if (!![]) {var _0x16c18d = function () {if (_0x4502('0x0') !== _0x4502('0x0')) {console[_0x4502('0x1')](_0x4502('0x2'));console[_0x4502('0x1')](_0x4502('0x3'));console[_0x4502('0x1')]('z');} else {console[_0x4502('0x1')](_0x4502('0x4'));console[_0x4502('0x1')](_0x4502('0x5'));console[_0x4502('0x1')](_0x4502('0x6'));console[_0x4502('0x1')](_0x4502('0x7'));}};var _0x1f7292 = function () {if (_0x4502('0x8') === _0x4502('0x9')) {console[_0x4502('0x1')](_0x4502('0xa'));console[_0x4502('0x1')](_0x4502('0xb'));console[_0x4502('0x1')](_0x4502('0xc'));} else {console[_0x4502('0x1')](_0x4502('0xa'));console[_0x4502('0x1')](_0x4502('0xb'));console[_0x4502('0x1')](_0x4502('0xc'));}};var _0x33b212 = function () {if (_0x4502('0xd') !== _0x4502('0xe')) {console[_0x4502('0x1')](_0x4502('0x2'));console[_0x4502('0x1')](_0x4502('0x3'));console[_0x4502('0x1')]('z');} else {console[_0x4502('0x1')](_0x4502('0x4'));console[_0x4502('0x1')](_0x4502('0x5'));console[_0x4502('0x1')](_0x4502('0x6'));console[_0x4502('0x1')](_0x4502('0x7'));}};_0x16c18d();_0x1f7292();_0x33b212();} }());

可見這里增加了一些不會(huì)執(zhí)行到的邏輯區(qū)塊內(nèi)容。

如果將 deadCodeInjection 設(shè)置為 false 或者不設(shè)置,運(yùn)行結(jié)果如下:

var _0x402a = ['qrs','wxy','log','abc','cde','efg','hij','nop' ]; (function (_0x57239e, _0x4747e8) {var _0x3998cd = function (_0x34a502) {while (--_0x34a502) {_0x57239e['push'](_0x57239e['shift']());}};_0x3998cd(++_0x4747e8); }(_0x402a, 0x162)); var _0x5356 = function (_0x2f2c10, _0x2878a6) {_0x2f2c10 = _0x2f2c10 - 0x0;var _0x4cfe02 = _0x402a[_0x2f2c10];return _0x4cfe02; }; (function () {if (!![]) {var _0x60edc1 = function () {console[_0x5356('0x0')](_0x5356('0x1'));console[_0x5356('0x0')](_0x5356('0x2'));console[_0x5356('0x0')](_0x5356('0x3'));console['log'](_0x5356('0x4'));};var _0x56405f = function () {console[_0x5356('0x0')]('klm');console['log'](_0x5356('0x5'));console['log'](_0x5356('0x6'));};var _0x332d12 = function () {console[_0x5356('0x0')]('tuv');console[_0x5356('0x0')](_0x5356('0x7'));console['log']('z');};_0x60edc1();_0x56405f();_0x332d12();} }());

另外我們還可以通過設(shè)置 deadCodeInjectionThreshold 參數(shù)來控制僵尸代碼注入的比例,取值 0 到 1,默認(rèn)是 0.4。

僵尸代碼可以起到一定的干擾作用,所以在有必要的時(shí)候也可以注入。

對(duì)象鍵名替換

如果是一個(gè)對(duì)象,可以使用 transformObjectKeys 來對(duì)對(duì)象的鍵值進(jìn)行替換,示例如下:

const code = ` (function(){var object = {foo: 'test1',bar: {baz: 'test2'}}; })(); ` const options = {compact: false,transformObjectKeys: true }

輸出結(jié)果如下:

var _0x7a5d = ['bar','test2','test1' ]; (function (_0x59fec5, _0x2e4fac) {var _0x231e7a = function (_0x46f33e) {while (--_0x46f33e) {_0x59fec5['push'](_0x59fec5['shift']());}};_0x231e7a(++_0x2e4fac); }(_0x7a5d, 0x167)); var _0x3bc4 = function (_0x309ad3, _0x22d5ac) {_0x309ad3 = _0x309ad3 - 0x0;var _0x3a034e = _0x7a5d[_0x309ad3];return _0x3a034e; }; (function () {var _0x9f1fd1 = {};_0x9f1fd1['foo'] = _0x3bc4('0x0');_0x9f1fd1[_0x3bc4('0x1')] = {};_0x9f1fd1[_0x3bc4('0x1')]['baz'] = _0x3bc4('0x2'); }());

可以看到,Object 的變量名被替換為了特殊的變量,這也可以起到一定的防護(hù)作用。

禁用控制臺(tái)輸出

可以使用 disableConsoleOutput 來禁用掉 console.log 輸出功能,加大調(diào)試難度,示例如下:

const code = ` console.log('hello world') ` const options = {disableConsoleOutput: true }

運(yùn)行結(jié)果如下:

var _0x3a39=['debug','info','error','exception','trace','hello\x20world','apply','{}.constructor(\x22return\x20this\x22)(\x20)','console','log','warn'];(function(_0x2a157a,_0x5d9d3b){var _0x488e2c=function(_0x5bcb73){while(--_0x5bcb73){_0x2a157a['push'](_0x2a157a['shift']());}};_0x488e2c(++_0x5d9d3b);}(_0x3a39,0x10e));var _0x5bff=function(_0x43bdfc,_0x52e4c6){_0x43bdfc=_0x43bdfc-0x0;var _0xb67384=_0x3a39[_0x43bdfc];return _0xb67384;};var _0x349b01=function(){var _0x1f484b=![外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-bDsO52K3-1594123051778)(_0x5efe0d,arguments)];_0x33db62=null;return _0x77054c;}}:function(){};_0x1f484b=[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-csfd2FXU-1594123051780)(_0x5bff('0xa')]);

此時(shí),我們?nèi)绻麍?zhí)行這段代碼,發(fā)現(xiàn)是沒有任何輸出的,這里實(shí)際上就是將 console 的一些功能禁用了,加大了調(diào)試難度。

調(diào)試保護(hù)

我們可以使用 debugProtection 來禁用調(diào)試模式,進(jìn)入無限 Debug 模式。另外我們還可以使用 debugProtectionInterval 來啟用無限 Debug 的間隔,使得代碼在調(diào)試過程中會(huì)不斷進(jìn)入斷點(diǎn)模式,無法順暢執(zhí)行。
示例如下:

const code = ` for (let i = 0; i < 5; i ++) {console.log('i', i) } ` const options = {debugProtection: true }

運(yùn)行結(jié)果如下:

var _0x41d0=['action','debu','stateObject','function\x20*\x5c(\x20*\x5c)','\x5c+\x5c+\x20*(?:_0x(?:[a-f0-9]){4,6}|(?:\x5cb|\x5cd)[a-z0-9]{1,4}(?:\x5cb|\x5cd))','init','test','chain','input','log','string','constructor','while\x20(true)\x20{}','apply','gger','call'];(function(_0x69147e,_0x180e03){var _0x2cc589=function(_0x18d18c){while(--_0x18d18c){_0x69147e['push'](_0x69147e['shift']());}};_0x2cc589(++_0x180e03);}(_0x41d0,0x153));var _0x16d2=function(_0x3d813e,_0x59f7b2){_0x3d813e=_0x3d813e-0x0;var _0x228f98=_0x41d0[_0x3d813e];return _0x228f98;};var _0x241eee=function(){var _0xeb17=[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-cF2PkG0m-1594123122408)(_0x5351d7+_0x16d2('0x5')])){_0x5351d7('0');}else{_0x227210();}})();}());for(let i=0x0;i<0x5;i++){console[_0x16d2('0x6')]('i',i);}function _0x227210(_0x30bc32){function _0x1971c7(_0x19628c){if(typeof _0x19628c===_0x16d2('0x7')){return function(_0x3718f7){}[_0x16d2('0x8')](_0x16d2('0x9'))[_0x16d2('0xa')]('counter');}else{if((''+_0x19628c/_0x19628c)['length']!==0x1||_0x19628c%0x14===0x0){(function(){return[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-mFtP7r8g-1594123122409)(_0x16d2('0xd')]));}else{(function(){return[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-qIO9ElGZ-1594123122368)(_0x16d2('0xe')]+_0x16d2('0xb'))[_0x16d2('0xa')](_0x16d2('0xf')));}}_0x1971c7(++_0x19628c);}try{if(_0x30bc32){return _0x1971c7;}else{_0x1971c7(0x0);}}catch(_0x58d434){}}

如果我們將代碼粘貼到控制臺(tái),其會(huì)不斷跳到 debugger 代碼的位置,無法順暢執(zhí)行。

域名鎖定

我們可以通過控制 domainLock 來控制 JavaScript 代碼只能在特定域名下運(yùn)行,這樣就可以降低被模擬的風(fēng)險(xiǎn)。

示例如下:

const code = ` console.log('hello world') ` const options = {domainLock: ['cuiqingcai.com'] }

運(yùn)行結(jié)果如下:

var _0x3203=['apply','return\x20(function()\x20','{}.constructor(\x22return\x20this\x22)(\x20)','item','attribute','value','replace','length','charCodeAt','log','hello\x20world'];(function(_0x2ed22c,_0x3ad370){var _0x49dc54=function(_0x53a786){while(--_0x53a786){_0x2ed22c['push'](_0x2ed22c['shift']());}};_0x49dc54(++_0x3ad370);}(_0x3203,0x155));var _0x5b38=function(_0xd7780b,_0x19c0f2){_0xd7780b=_0xd7780b-0x0;var _0x2d2f44=_0x3203[_0xd7780b];return _0x2d2f44;};var _0x485919=function(){var _0x5cf798=![外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-IeGzMIT8-1594123764720)(_0xd1fa29,arguments)];_0x2ed646=null;return _0x33af63;}}:function(){};_0x5cf798=![];return _0x56abf;};}();var _0x67dcc8=_0x485919(this,function(){var _0x276a31;t

這段代碼就只能在指定域名 cuiqingcai.com 下運(yùn)行,不能在其他網(wǎng)站運(yùn)行,不信你可以試試。

特殊編碼

另外還有一些特殊的工具包,如使用 aaencode、jjencode、jsfuck 等工具對(duì)代碼進(jìn)行混淆和編碼。

示例如下:

var a = 1

jsfuck 的結(jié)果:

[][(![]+[])[!+[]+!![]+!![]]+([]+{})[+!![]]+(!![]+[

aaencode 的結(jié)果:

゚ω゚ノ= /`m′)ノ ~┻━┻ / ['_']; o=(゚ー゚) =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚);

jjencode 的結(jié)果:

$=~[];$={___:++$,$$$$:(![]+"")[$],__$:++$,$_$_:(![]+"")[$],_$_:++$,$_$$:({}+"")[$],$$_$:($[$]+"")[$],_$$:++$,$$$_:(!""+"")[$],$__:++$,$_$:++$,$$__:({}+"")[$],$

這些混淆方式比較另類,但只需要輸入到控制臺(tái)即可執(zhí)行,其沒有真正達(dá)到強(qiáng)力混淆的效果。

以上便是對(duì) JavaScript 混淆方式的介紹和總結(jié)。總的來說,經(jīng)過混淆的 JavaScript 代碼其可讀性大大降低,同時(shí)防護(hù)效果也大大增強(qiáng)。

6.JavaScript 加密

不同于 JavaScript 混淆技術(shù),JavaScript 加密技術(shù)可以說是對(duì) JavaScript 混淆技術(shù)防護(hù)的進(jìn)一步升級(jí),其基本思路是將一些核心邏輯使用諸如 C/C++ 語言來編寫,并通過 JavaScript 調(diào)用執(zhí)行,從而起到二進(jìn)制級(jí)別的防護(hù)作用。

其加密的方式現(xiàn)在有 Emscripten 和 WebAssembly 等,其中后者越來越成為主流。
下面我們分別來介紹下。

Emscripten

現(xiàn)在,許多 3D 游戲都是用 C/C++ 語言寫的,如果能將 C / C++ 語言編譯成 JavaScript 代碼,它們不就能在瀏覽器里運(yùn)行了嗎?眾所周知,JavaScript 的基本語法與 C 語言高度相似。于是,有人開始研究怎么才能實(shí)現(xiàn)這個(gè)目標(biāo),為此專門做了一個(gè)編譯器項(xiàng)目 Emscripten。這個(gè)編譯器可以將 C / C++ 代碼編譯成 JavaScript 代碼,但不是普通的 JavaScript,而是一種叫作 asm.js 的 JavaScript 變體。

因此說,某些 JavaScript 的核心功能可以使用 C/C++ 語言實(shí)現(xiàn),然后通過 Emscripten 編譯成 asm.js,再由 JavaScript 調(diào)用執(zhí)行,這可以算是一種前端加密技術(shù)。

WebAssembly

如果你對(duì) JavaScript 比較了解,可能知道還有一種叫作 WebAssembly 的技術(shù),也能將 C/C++ 轉(zhuǎn)成 JavaScript 引擎可以運(yùn)行的代碼。那么它與 asm.js 有何區(qū)別呢?

其實(shí)兩者的功能基本一致,就是轉(zhuǎn)出來的代碼不一樣:asm.js 是文本,WebAssembly 是二進(jìn)制字節(jié)碼,因此運(yùn)行速度更快、體積更小。從長遠(yuǎn)來看,WebAssembly 的前景更光明。

WebAssembly 是經(jīng)過編譯器編譯之后的字節(jié)碼,可以從 C/C++ 編譯而來,得到的字節(jié)碼具有和 JavaScript 相同的功能,但它體積更小,而且在語法上完全脫離 JavaScript,同時(shí)具有沙盒化的執(zhí)行環(huán)境。

利用 WebAssembly 技術(shù),我們可以將一些核心的功能利用 C/C++ 語言實(shí)現(xiàn),形成瀏覽器字節(jié)碼的形式。然后在 JavaScript 中通過類似如下的方式調(diào)用:

WebAssembly.compile(new Uint8Array(`00 61 73 6d 01 00 00 00 01 0c 02 60 02 7f 7f 017f 60 01 7f 01 7f 03 03 02 00 01 07 10 02 03 6164 64 00 00 06 73 71 75 61 72 65 00 01 0a 13 0208 00 20 00 20 01 6a 0f 0b 08 00 20 00 20 00 6c0f 0b`.trim().split(/[\s\r\n]+/g).map(str => parseInt(str, 16)) )).then(module => {const instance = new WebAssembly.Instance(module)const { add, square } = instance.exportsconsole.log('2 + 4 =', add(2, 4))console.log('3^2 =', square(3))console.log('(2 + 5)^2 =', square(add(2 + 5))) })

這種加密方式更加安全,因?yàn)樽鳛槎M(jìn)制編碼它能起到的防護(hù)效果無疑是更好的。如果想要逆向或破解那得需要逆向 WebAssembly,難度也是很大的。

7.總結(jié)

以上,我們就介紹了接口加密技術(shù)和 JavaScript 的壓縮、混淆和加密技術(shù),知己知彼方能百戰(zhàn)不殆,了解了原理,我們才能更好地去實(shí)現(xiàn) JavaScript 的逆向。
本節(jié)代碼:https://github.com/Python3WebSpider/JavaScriptObfuscate

參考文獻(xiàn)

  • https://www.ruanyifeng.com/blog/2017/09/asmjs_emscripten.html
  • https://juejin.im/post/5cfcb9d25188257e853fa71c#heading-23
  • https://www.jianshu.com/p/326594cbd4fa
  • https://github.com/javascript-obfuscator/javascript-obfuscator
  • https://obfuscator.io/
  • https://www.sojson.com/jjencode.html
  • http://dean.edwards.name/packer/

總結(jié)

以上是生活随笔為你收集整理的第27讲:令人抓狂的 JavaScript 混淆技术的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

欧洲一区精品 | 午夜在线国产 | 日本三级不卡视频 | 天天操操 | 美女免费视频网站 | 国产高清视频在线播放 | 亚洲欧美日韩不卡 | 99视频精品免费观看, | 九九热在线视频 | 1024久久| 国产韩国日本高清视频 | av一本久道久久波多野结衣 | 国产成人99av超碰超爽 | 久久99久久99精品免费看小说 | 国产福利91精品一区 | 国产精品永久免费 | 欧美一区二区在线刺激视频 | 日本精a在线观看 | 天天夜操| 亚洲精品中文在线观看 | 亚洲欧洲日韩 | 狠狠躁夜夜躁人人爽超碰91 | 欧美一级性生活视频 | 91在线播放视频 | 国产精品久久久一区二区 | 国产一区二区在线免费播放 | 日韩一二三区不卡 | 波多野结衣电影久久 | 丁香狠狠| 久久久久成人精品免费播放动漫 | 日本不卡久久 | 亚洲欧美视频在线观看 | 99久久婷婷国产综合亚洲 | 天天天色综合 | 中文在线8新资源库 | av在线播放不卡 | 日韩欧美69| 亚洲区色 | 超碰九九 | 国产又粗又猛又爽又黄的视频先 | 亚洲免费成人av电影 | 香蕉视频在线免费 | 亚洲精品在线免费观看视频 | 97久久精品午夜一区二区 | 亚洲美女精品视频 | 亚洲精品在线观看视频 | 亚洲视频国产 | 欧美爽爽爽 | 精品国产三级 | 午夜12点 | 香蕉网址 | 精品亚洲免a | 最新av免费在线观看 | 亚洲精品久久视频 | 91色视频| 国产香蕉97碰碰久久人人 | 不卡的av中文字幕 | av在线免费观看网站 | 91麻豆精品国产91久久久更新时间 | av中文字幕免费在线观看 | 五月天六月婷 | 色中射 | 日韩精品在线免费观看 | 懂色av懂色av粉嫩av分享吧 | 特级毛片aaa | 蜜臀av免费一区二区三区 | 久插视频| 日本免费久久高清视频 | 欧美性精品 | 日日夜夜人人精品 | 日韩夜夜爽 | .精品久久久麻豆国产精品 亚洲va欧美 | 在线观看你懂的网站 | 欧美亚洲国产精品久久高清浪潮 | 美女视频永久黄网站免费观看国产 | 国产精品久久久久久电影 | 夜夜视频欧洲 | 9在线观看免费高清完整版在线观看明 | 日韩经典一区二区三区 | 国产高清不卡在线 | 亚洲黄色激情小说 | 国内成人综合 | 成人aⅴ视频 | 色资源二区在线视频 | 日韩欧美一区二区三区视频 | 婷婷丁香花五月天 | 91看片在线免费观看 | 狠狠狠色丁香婷婷综合久久五月 | 欧美性极品xxxx做受 | 色资源二区在线视频 | free,性欧美| 97精品国产91久久久久久 | 中文字幕在线中文 | 精品视频免费 | 国产福利一区在线观看 | 精品国产伦一区二区三区观看说明 | 中文字幕在线电影 | 久久欧美在线电影 | 亚洲精品福利在线观看 | 中文字幕av全部资源www中文字幕在线观看 | 日本久久久久久 | 欧美伦理一区二区 | 国产91在线观 | www.91成人| 视频一区二区国产 | 日韩免费一二三区 | 日韩成片 | 国产又粗又猛又色又黄视频 | 久草视频在线看 | 成x99人av在线www | 久久首页 | 成人av直播 | 婷婷在线网 | 久久久久国产精品厨房 | 日韩精品中文字幕在线 | 日日夜夜综合网 | 亚洲综合黄色 | 私人av| 日日夜夜av | 日日天天干| 欧美在线视频免费 | 国产视频美女 | 亚洲第一区在线播放 | 精品国产一区二区三区日日嗨 | a在线播放 | 1区2区视频| 亚洲精品视频在线播放 | 国产一级黄 | 伊人五月天婷婷 | 在线观看视频中文字幕 | 久久久亚洲电影 | 国产免费又黄又爽 | 国产电影黄色av | 日韩电影一区二区在线观看 | 久久久影院官网 | 国产精品国产三级国产专区53 | 欧美大片在线观看一区 | 青春草免费在线视频 | 亚洲日本va午夜在线影院 | 成av在线 | 中文在线天堂资源 | 久久久亚洲国产精品麻豆综合天堂 | 日日碰狠狠躁久久躁综合网 | 一区二区观看 | 久久毛片高清国产 | 国产午夜三级一区二区三桃花影视 | 亚洲1区 在线| 久草在线观 | 国产电影一区二区三区四区 | 国产精品日韩在线 | 在线观看日韩视频 | 日日干 天天干 | 激情五月亚洲 | 啪啪午夜免费 | 99 久久久久 | 国产成人在线看 | 91.dizhi永久地址最新 | 日韩欧美高清不卡 | 国产精品99久久久精品 | 日韩午夜精品 | 国产在线观看地址 | 久久网页| 久草在线这里只有精品 | 国产视频在线观看一区 | 久久福利国产 | 久久人人97超碰精品888 | 色干干 | 伊人电影在线观看 | 日韩精品久久久久久久电影竹菊 | 欧美日韩一区二区三区在线观看视频 | 亚洲精品一区二区三区四区高清 | 韩国av电影网 | 97碰在线视频 | 在线观看网站黄 | 国产精品久久久久久久免费大片 | 久操中文字幕在线观看 | 中文字幕在线播放日韩 | 三级av网站| 久草视频网 | 色视频在线 | 欧美日韩国产在线 | 亚洲精品午夜一区人人爽 | 丁香花中文在线免费观看 | 视频在线日韩 | 东方av免费在线观看 | 五月婷婷丁香 | 激情视频在线观看网址 | 欧美一区日韩精品 | 最近高清中文字幕 | 视频在线99| 91视视频在线直接观看在线看网页在线看 | 欧美极品少妇xbxb性爽爽视频 | av在线官网 | 国产一级在线 | 午夜影院在线观看18 | 国产精品18久久久久久不卡孕妇 | 婷婷综合 | 天天操天天操天天操天天操天天操 | 一级黄视频| 青草视频免费观看 | 久久国产精品久久w女人spa | 国产精品国产自产拍高清av | 亚洲精品乱码久久久久v最新版 | 免费看一级特黄a大片 | 波多野结衣小视频 | 五月婷婷天堂 | 黄色资源在线观看 | 2022国产精品视频 | 国产裸体bbb视频 | 日本一区二区不卡高清 | 奇米网在线观看 | 国产视频午夜 | 99热国产在线 | 日日成人网 | 激情欧美在线观看 | 欧美日本不卡高清 | 波多野结衣电影一区二区 | 91九色蝌蚪视频在线 | 人人爱夜夜操 | 亚洲动漫在线观看 | 在线激情av电影 | 国内精品久久久久久久久 | 一区二区三区不卡在线 | 久久免费视频5 | 亚洲精品中文字幕视频 | 在线免费视频a | 97手机电影网 | 亚洲一区美女视频在线观看免费 | 日日夜夜操操操操 | 国产美女永久免费 | 精品国产123| 亚洲国产精品激情在线观看 | 人人玩人人爽 | 免费看黄的视频 | 天天操天天干天天插 | 永久免费看av | 国内精品久久久久久久 | 色福利网站 | 亚洲精品久久久久久中文传媒 | www.五月婷婷 | 久久a热6| www.天天操 | 黄色片亚洲 | av亚洲产国偷v产偷v自拍小说 | 精品欧美一区二区在线观看 | 美女视频黄频大全免费 | 日本bbbb摸bbbb | 国产视频在线观看一区 | 97电影在线 | 欧美精品久久久久久久久久丰满 | 国产成人精品一区二区 | 四虎永久精品在线 | 韩国av电影在线观看 | 成人一级片在线观看 | 免费观看一级 | 91九色成人蝌蚪首页 | 黄色高清视频在线观看 | 伊人色综合久久天天网 | 天天操欧美 | 国产1区在线观看 | www91在线| 在线观看日本高清mv视频 | 免费看国产曰批40分钟 | 国产视频丨精品|在线观看 国产精品久久久久久久久久久久午夜 | 亚洲国产最新 | 久草在线视频看看 | 国产成人精品一区二区三区免费 | 美女免费视频一区二区 | 亚洲人人爱 | 在线观看视频在线观看 | 久久美女高清视频 | 西西大胆免费视频 | 久久久久亚洲天堂 | 中文国产在线观看 | 狠狠五月婷婷 | 婷婷丁香在线视频 | 91人人爱 | 欧美日韩在线网站 | 久久久久久高潮国产精品视 | 欧美日韩另类在线观看 | 夜夜操天天 | 色99久久 | 国产精品久久久久久久久软件 | 日韩免费b | 黄色影院在线播放 | 欧美久久精品 | 国内精品久久久久影院一蜜桃 | 成人免费av电影 | 国产视频黄 | 国产精品久久久久aaaa九色 | 麻豆国产露脸在线观看 | 亚洲精品99久久久久中文字幕 | 麻豆国产精品视频 | 超碰在线资源 | 久久兔费看a级 | 国产又粗又猛又色又黄视频 | 天天爱综合 | 国产精品18久久久久久vr | 免费的黄色av | 天天射成人 | 亚洲aⅴ在线 | 西西444www大胆高清图片 | 久久美女免费视频 | 亚洲精品麻豆 | 久久你懂的 | 少妇高潮流白浆在线观看 | 国产精品久久久久久999 | 黄色福利网站 | a成人v在线 | av超碰在线观看 | 天天操综合网 | 欧美激精品 | 午夜国产一区二区 | 中文字幕一区二区三区久久 | 亚洲综合小说电影qvod | 免费观看v片在线观看 | 欧美一区二区三区免费观看 | 亚洲激情综合 | 久久精品国产免费观看 | 欧美日韩久久一区 | 性色av免费在线观看 | 夜夜高潮夜夜爽国产伦精品 | 亚洲撸撸 | 丁香花在线视频观看免费 | 99资源网| 欧美另类性 | 日韩久久久久久 | av中文在线影视 | 精品久久综合 | 一二三区视频在线 | 麻豆视频免费在线 | 久久这里只有精品9 | 欧美性爽爽 | 午夜精品一区二区三区在线播放 | 欧美日韩国产一二三区 | 久久中文精品视频 | 麻豆传媒一区二区 | 亚洲永久在线 | 欧美日韩国产色综合一二三四 | 欧美性极品xxxx娇小 | 九九视频一区 | 99视频精品视频高清免费 | 精品国产一区二区三区男人吃奶 | 日韩视频免费在线观看 | 免费看三片 | 久久久www成人免费精品张筱雨 | 国产一区二区在线观看视频 | 午夜精品久久久久 | 99国产精品久久久久久久久久 | 在线观看91av | 欧美精品中文字幕亚洲专区 | 亚洲精品成人av在线 | 国内精品久久久久久久久久 | 国产高清 不卡 | 久久久久99精品国产片 | 天天射天天干天天 | 91传媒在线 | 日韩欧美视频一区二区三区 | 色一级片 | 97超在线视频 | 日韩在线高清 | 在线播放 日韩专区 | 在线观看午夜 | 成人免费在线电影 | 亚洲久草视频 | 九九免费精品 | 精品久久久久久久久久岛国gif | 亚洲高清在线视频 | 六月色婷 | 国产三级国产精品国产专区50 | 91精品久久久久久综合乱菊 | 最近av在线 | av免费电影在线观看 | 一区 二区电影免费在线观看 | 99久久久国产精品免费观看 | 在线看国产精品 | 高清av在线免费观看 | 国产亚洲精品xxoo | 91免费在线| 日韩成人精品一区二区三区 | 免费久久精品视频 | 九九热在线视频免费观看 | 777奇米四色| 久久国产成人午夜av影院宅 | 欧美三级在线播放 | 夜色成人网| 成人97视频一区二区 | 久久久夜色 | 国产一级大片免费看 | 看片网站黄色 | 国产精品9999久久久久仙踪林 | 国产亚洲精品久久久久久 | 亚洲欧美日本一区二区三区 | 91福利区一区二区三区 | 四虎影视www| 韩国精品视频在线观看 | 婷婷综合亚洲 | 日日操日日操 | 国产午夜在线观看视频 | 奇米四色影狠狠爱7777 | 香蕉视频国产在线观看 | 久久免费av电影 | 日韩激情精品 | 亚洲国产精品女人久久久 | 亚洲美女视频在线观看 | 日韩手机视频 | 狠狠色丁香久久婷婷综 | 碰超在线97人人 | 三级a视频 | av 在线观看 | 日韩欧美一区二区在线播放 | 不卡的av在线 | 91大片网站 | 97网在线观看 | 国产香蕉久久 | 久久国语露脸国产精品电影 | 成人中文字幕av | 天天爱天天射 | 久久久精品 一区二区三区 国产99视频在线观看 | 天天干天天操天天拍 | 少妇按摩av | 高潮久久久久久 | 久久精品波多野结衣 | 天天干天天在线 | 色播五月激情五月 | 在线免费av网站 | 国产精品精品国产 | 99riav1国产精品视频 | aⅴ精品av导航 | 国产成人精品一二三区 | 天堂网一区二区 | 久久久久久久久精 | 亚洲欧美一区二区三区孕妇写真 | 亚洲更新最快 | 欧美激情视频一区二区三区 | 在线观看中文av | 国产91亚洲精品 | 日韩在线网 | 亚洲最大在线视频 | 欧美一区二区三区四区夜夜大片 | av解说在线观看 | 日本资源中文字幕在线 | 欧洲精品亚洲精品 | 三级av在线| 天天射天天干天天操 | 欧美成天堂网地址 | 在线视频 一区二区 | 国产免费又粗又猛又爽 | 精品一区二区免费在线观看 | 黄色小说视频在线 | 欧洲亚洲国产视频 | 免费黄色网址网站 | 亚洲精品一区二区精华 | 99在线免费视频观看 | 超碰人人在线观看 | 久久综合狠狠综合久久激情 | 婷婷在线免费 | 91久久影院| 久久免费成人网 | 黄色大片视频网站 | 国产精品一区二区无线 | 久久久夜色 | 在线免费观看视频一区 | 国产高清在线视频 | 色wwww| 久久久在线视频 | 天天操天天操天天操天天操天天操天天操 | 免费视频久久久久久久 | 中文字幕一区二区三区久久蜜桃 | 深爱开心激情网 | 亚洲精品18p| www.91成人| 日日夜夜狠狠 | 在线视频18在线视频4k | 中文字幕av在线不卡 | 久草亚洲视频 | 天天摸天天操天天爽 | 亚洲精品高清视频 | 婷婷在线网站 | 欧美激情视频一区二区三区 | 亚州精品天堂中文字幕 | 一本一本久久a久久精品综合小说 | 欧美一级免费片 | 日韩精品视频一二三 | 丁香久久婷婷 | 米奇四色影视 | ,午夜性刺激免费看视频 | 久久99深爱久久99精品 | 中文字幕一区二区三区四区在线视频 | 国产成人精品一区二区三区在线观看 | 96超碰在线| 亚洲精品国产片 | 免费成人短视频 | 免费观看黄色av | 99热最新 | 亚洲视频免费在线观看 | 美女一级毛片视频 | 麻豆一区在线观看 | 欧美一二三区在线观看 | 亚洲精品乱码久久 | 国产精品视频久久久 | 久久人人爽人人爽人人 | 欧美福利在线播放 | 精品国模一区二区 | 在线观看精品一区 | 欧美久久久久久久久久久久久 | 精品久久久久久亚洲综合网站 | 午夜视频播放 | 在线国产黄色 | 狠狠干狠狠色 | 国产美女精品 | 欧美国产日韩一区二区 | 日韩69视频 | 成人在线观看免费视频 | 福利视频精品 | 国产色婷婷精品综合在线手机播放 | 色网影音先锋 | 99精品视频99 | 成年人免费观看在线视频 | 丁香资源影视免费观看 | 人人爽人人爽人人片 | 欧美9999 | 97精品国产一二三产区 | 欧美日韩不卡一区二区三区 | 中文字幕电影一区 | 激情片av| 就色干综合 | 日日骑 | 精品国产电影一区 | 国产国语在线 | av在线播放不卡 | 日本成人黄色片 | av大全在线观看 | 欧美在线不卡一区 | 国产精品久久久久久久久搜平片 | 久久久久久久久久久电影 | 亚洲国产操| 久青草国产在线 | 日韩成人中文字幕 | 免费看的黄网站 | 亚洲无吗av| 色婷婷精品大在线视频 | 在线观看网站黄 | 极品美女被弄高潮视频网站 | 欧美在线free | 亚洲涩涩涩涩涩涩 | 国产精品第54页 | 国产最新91 | 成人免费一区二区三区在线观看 | 国产精品色婷婷视频 | av在线免费网| 久久免费视频网 | av不卡中文字幕 | 天堂在线视频中文网 | 91久久黄色| 91在线在线观看 | 久av电影 | 色婷婷国产在线 | www色片| 夜夜躁日日躁狠狠久久av | 日韩精品短视频 | 97国产情侣爱久久免费观看 | 黄色毛片视频免费 | 国产资源在线播放 | 在线中文字母电影观看 | 欧美激情在线看 | 久久精品中文字幕免费mv | 一区二区理论片 | 日韩在线观看网址 | 色播五月婷婷 | 狠狠操夜夜操 | 视频福利在线 | 国产91精品一区二区绿帽 | 午夜精品一区二区三区在线视频 | 国产你懂的在线 | 精品一区 精品二区 | www日韩在线观看 | 婷婷伊人五月 | 一级黄色片在线免费观看 | 色网站在线观看 | 岛国大片免费视频 | 亚洲精品国产自产拍在线观看 | 久草在线视频中文 | 欧美日韩在线精品一区二区 | 国产精品第十页 | 亚洲国产精品电影 | 三三级黄色片之日韩 | 91在线视频免费 | 国产精品一区二区三区四 | 久久精品国产美女 | 91污视频在线 | 一区久久久 | 久久五月天色综合 | 国产福利在线 | 欧美激情精品久久久久久变态 | 成人av电影在线 | 丝袜网站在线观看 | 久久免费黄色大片 | 久久成人免费视频 | 日日爽夜夜操 | 深爱五月激情网 | 五月综合色婷婷 | 日韩欧美一区二区三区在线 | 免费下载高清毛片 | 手机看片午夜 | 精品国产1区| 精品99视频 | 久爱精品在线 | 狠狠色丁香久久婷婷综 | 亚洲精品国产精品国自产观看 | 亚洲精品视频国产 | 免费视频资源 | 国产福利精品一区二区 | 国产精品手机在线播放 | 国产精品一区二区白浆 | 青青草国产成人99久久 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 成人黄色小说在线观看 | 天天摸夜夜操 | 天天天天天天操 | 一区二区不卡视频在线观看 | 99国产精品一区二区 | 欧洲一区精品 | 日韩精品免费一区二区三区 | 色婷婷88av视频一二三区 | 欧美亚洲另类在线视频 | 久久久久成人精品亚洲国产 | 日韩欧美精选 | 婷婷去俺也去六月色 | 国产免费大片 | 中文字幕中文字幕在线一区 | 久久久久久久久免费视频 | 婷婷av网 | 国产在线观看中文字幕 | 国产亚洲午夜高清国产拍精品 | 欧美日韩中文字幕视频 | 久久草视频 | 日韩一区二区免费在线观看 | 亚洲国产网站 | 91手机电视 | 中文字幕欧美日韩va免费视频 | 国产视频二区三区 | 女人18片毛片90分钟 | 成人毛片在线观看视频 | 成年人国产精品 | 中文字幕 91 | 成人精品久久 | 欧美一进一出抽搐大尺度视频 | 中文字幕日韩在线播放 | 日产乱码一二三区别在线 | 黄色网址a| 四虎成人在线 | 国产亚洲在线观看 | 色网站在线免费观看 | 国产精品不卡在线 | 在线观看中文字幕一区二区 | 久久精品伊人 | a视频在线看 | 欧美天堂视频在线 | 狠狠狠狠狠狠狠狠干 | 91麻豆精品国产91久久久无限制版 | 高清国产在线一区 | 天天色天天综合 | 福利av在线 | 亚洲精品成人网 | 免费亚洲婷婷 | 欧美孕妇与黑人孕交 | 深爱婷婷久久综合 | 国产99久久久精品视频 | av中文字幕网 | 一区二区视频在线免费观看 | 在线观看国产福利片 | 国产精品日韩久久久久 | 久久久婷 | 中文字幕亚洲国产 | 四虎成人精品永久免费av九九 | 蜜臀av夜夜澡人人爽人人桃色 | 午夜久久美女 | 操操操日日日干干干 | 2019中文字幕网站 | 国产精品婷婷午夜在线观看 | 亚洲激情小视频 | 伊人久久国产 | 久久久精品网 | 国产在线黄色 | 毛片无卡免费无播放器 | 国产成人久久精品77777 | 国产一级电影网 | 精品国产成人在线 | 久草视频看看 | 91丨九色丨蝌蚪丨老版 | 成年人在线看视频 | 天天色天天操综合 | 91成人免费电影 | 欧美色婷婷 | 国产91精品看黄网站在线观看动漫 | 91精品视频免费在线观看 | 香蕉在线播放 | 丁香婷婷色月天 | 碰碰影院 | 免费av高清 | 国产精品久久久久久模特 | 2018亚洲男人天堂 | 欧美精品在线一区二区 | 伊人久久电影网 | 国产高清视频免费观看 | 亚洲激情在线播放 | 黄色三级在线观看 | 日本久久成人中文字幕电影 | 日韩1页| 国产成人久久精品77777 | 精品国产一区二区三区四区vr | 日韩在线电影一区 | 国产日本在线播放 | 黄色亚洲 | 久久国产精品免费视频 | 国产精品久久久久久久久蜜臀 | 91手机在线看片 | 亚洲 欧美 综合 在线 精品 | 一区二区三区中文字幕在线 | 国产视频一区在线 | www久久九| 精品国产自在精品国产精野外直播 | 欧美成人中文字幕 | 亚洲一区网| 五月天亚洲综合小说网 | 免费一级片在线观看 | 色香蕉网| 久久国产麻豆 | 国产精品女同一区二区三区久久夜 | 久久久久久久久久久免费 | 国产成人综合精品 | 国产精品一区二区免费在线观看 | 久久免费中文视频 | 综合久久五月天 | 四虎国产精品永久在线国在线 | 超碰个人在线 | 9999国产精品 | 精品999久久久 | 久久国产二区 | 丁香视频在线观看 | 日韩精品免费在线视频 | 国产一区二区三区视频在线 | 午夜18视频在线观看 | 精品视频国产 | 91麻豆精品国产91久久久久久久久 | 久草网视频 | 久草精品视频在线观看 | 东方av在线免费观看 | 91久久精品一区 | 青春草视频在线播放 | 久久av免费| 黄色av影院 | 国产精品亚 | 激情网五月婷婷 | av中文字幕电影 | 亚洲乱码精品 | 国产视频丨精品|在线观看 国产精品久久久久久久久久久久午夜 | 一区视频在线 | 精品国产一区二区久久 | 正在播放日韩 | 国产一区二区久久久 | 欧美人牲| 欧美色图p| 激情综合一区 | 天天插一插 | 亚洲精品免费看 | 亚洲成a人片在线观看中文 中文字幕在线视频第一页 狠狠色丁香婷婷综合 | 日韩影视大全 | 久久综合亚洲鲁鲁五月久久 | 国产高清视频色在线www | 69国产在线观看 | 国产视频亚洲精品 | 国产精品免费高清 | 国产在线一线 | 日本精品二区 | 九九免费在线观看 | 91精品国自产拍天天拍 | 国产高清福利在线 | 亚洲男人天堂2018 | 国产一区二区电影在线观看 | 美女视频久久久 | 婷婷丁香国产 | 日韩精品免费在线 | 99视频国产精品免费观看 | 久草国产视频 | 国产专区视频在线观看 | 9ⅰ精品久久久久久久久中文字幕 | 日韩精品aaa | 黄色中文字幕 | 免费在线观看成人 | 欧美成人亚洲 | 在线小视频你懂的 | 亚洲综合色丁香婷婷六月图片 | 色综合五月天 | 四虎国产免费 | 色射爱| 欧美国产一区在线 | 欧美日韩国产一区二区三区在线观看 | 日韩中文幕 | 日韩欧美成人网 | 91精品国产成人www | 青青河边草免费观看完整版高清 | 97香蕉超级碰碰久久免费软件 | 在线观看黄网站 | 天天超碰| 国产一区电影在线观看 | 天天射天天操天天 | www.人人干| 国产小视频精品 | 天天干天天操天天拍 | 成人看片| 天天躁日日躁狠狠 | 91丨九色丨国产在线 | 精品久久久久久久久久久久 | 久久成人久久 | 国产精品亚洲a | 国产在线超碰 | 国产精品成人免费一区久久羞羞 | 成人av免费在线观看 | 天天射日 | 国产精品96久久久久久吹潮 | 91精品国产自产老师啪 | 美女啪啪图片 | 午夜三级毛片 | 狂野欧美激情性xxxx | 国产一级片不卡 | 久久tv视频 | 在线97 | 狠狠插狠狠操 | 五月婷婷,六月丁香 | 日韩免费av在线 | 天天干夜夜夜操天 | 成人在线视频在线观看 | 成人高清在线 | 综合久久网站 | 黄色小说在线免费观看 | 99精品久久99久久久久 | 亚洲欧美国内爽妇网 | 精品免费观看视频 | 色婷五月天 | 黄污视频网站大全 | 亚洲人av免费网站 | 手机在线看永久av片免费 | 日韩在线观看小视频 | 女人18片毛片90分钟 | 欧美99久久 | 天堂av免费观看 | 色在线网站| 美女视频黄的免费的 | 久久成人麻豆午夜电影 | 欧美日韩国产精品一区 | 欧美在线视频免费 | 亚洲一区美女视频在线观看免费 | 日本精品久久久一区二区三区 | 国产高清在线免费 | av在线直接看 | 免费国产黄线在线观看视频 | 天天综合网入口 | 久久99国产精品二区护士 | 国产精品 9999 | 国产一区二区精品91 | 亚洲综合色激情五月 | 亚洲精品88欧美一区二区 | 午夜在线国产 | 中文在线字幕免费观 | 久久精品免费观看 | av中文在线| 欧美精品久久久久久久久久白贞 | av午夜电影 | 九九在线免费视频 | 精品视频中文字幕 | 国产精品永久免费 | 久草网免费| 五月婷婷开心 | 日韩久久久 | 色婷婷a| 欧美美女视频在线观看 | 久久久久久久久久网 | 在线观看黄污 | 亚洲天堂色婷婷 | 亚洲在线网址 | 色婷婷久久久 | 久久精品视频18 | 中文字幕精品www乱入免费视频 | 一区二区三区在线观看中文字幕 | 久精品在线| 精品在线观看一区二区 | 欧美日韩一区二区三区免费视频 | 日本公妇在线观看高清 | 在线激情小视频 | 69av在线播放 | 婷婷五月色综合 | 成人国产网站 | 亚洲影院天堂 | a在线v| 国产日韩欧美中文 | 日韩欧美视频一区二区 | 人人草人人草 | 欧美久久久久久久久中文字幕 | 91av电影在线 | av电影在线观看完整版一区二区 | 欧美福利片在线观看 | 国产精品福利在线播放 | 久久99久久99精品免费看小说 | 国内外成人在线 | 97小视频 | 亚洲老妇xxxxxx | 久久免费视频一区 | 亚洲在线资源 | 成人在线播放网站 | 一区二区 不卡 | 亚洲黄色av| 性色av一区二区三区在线观看 | 国产在线1区 | 久久精品久久久精品美女 | 久久av高清| 欧美日韩视频网站 | 精品久久久国产 | 国产免费区 | 色狠狠综合天天综合综合 | 高清久久久 | 亚洲aⅴ久久精品 | 99视频这里有精品 | 91精品在线免费视频 | 在线观看免费视频你懂的 | 欧美精品免费在线观看 | 国产成人亚洲精品自产在线 | 中文字幕欲求不满 | av资源中文字幕 | 国产精品高潮呻吟久久久久 | 99视频国产精品 | 99久久久久久久 | 色在线视频 | 中文字幕网站视频在线 | 免费黄色av | 日韩中文字幕视频在线观看 | 日韩激情中文字幕 | 色网站在线观看 | 亚洲成a人片在线观看网站口工 | 久久久久久久免费 | 久久免费的精品国产v∧ | 国产精品 中文字幕 亚洲 欧美 | 亚洲成人av在线 | 国产亚洲欧美日韩高清 | 91chinese在线 | 国产视频精品视频 | 色婷婷色 | 福利网在线 | 国产成人久久久久 | 国产精品一区二区三区四区在线观看 | 欧美日韩二三区 | 久久视频免费在线 | 国产精品福利无圣光在线一区 | 成人黄色电影在线 | 久久久免费| 亚洲欧美国产精品久久久久 | 国内精品久久久久久久影视简单 | 在线观看色网站 | 9色在线视频 | 免费在线成人 | 成人av直播 | 成人sm另类专区 | 国产精品二区在线观看 | 久久久免费精品国产一区二区 | 日韩小视频网站 | 在线免费观看视频一区二区三区 | 97香蕉久久超级碰碰高清版 | 久久久色 | 中文字幕日韩电影 | 手机在线看片日韩 | 国产精品久久久久久久久久久免费 | 99久久精品国产系列 | 在线91网| 高清中文字幕av | 成人a在线观看高清电影 | 亚洲欧美日韩精品久久奇米一区 | 夜夜操天天摸 | 国产九九精品视频 | 九九热1 | 97碰碰精品嫩模在线播放 | 91精品久久久久久久久 | 天堂av在线 | 亚洲精品日韩在线观看 | 久久久久高清毛片一级 | 免费视频网 | 91大神在线观看视频 | 精品福利视频在线观看 | 91免费视频国产 | 国产精品婷婷 | 国产色视频一区二区三区qq号 | 亚洲精品视频在线免费播放 | 中文字幕在线成人 | 成人免费在线播放 | 成人黄色中文字幕 | 日本中文字幕电影在线免费观看 | 国产精品免费在线播放 | 高清不卡免费视频 |