Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 久久精品嫩草影院免费看,精品哟哟哟国产在线不卡,91精品啪在线观看国产91九色

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          從微信小程序看前端代碼安全

          從微信小程序看前端代碼安全

          初在研究對(duì)移動(dòng)網(wǎng)絡(luò)傳輸進(jìn)行功耗優(yōu)化,在一次意外的監(jiān)聽網(wǎng)絡(luò)傳輸包中截獲了微信小程序的請(qǐng)求包,借此來窺探當(dāng)下前端代碼安全。

          0x01 小程序分析

          小程序包結(jié)構(gòu)

          SegmentNameLengthRemark
          HeaderFirstMark1 byte0xBE 固定值
          Edition4 bytes0 -> 微信分發(fā)到客戶端 1 -> 開發(fā)者上傳到微信后臺(tái)
          IndexInfoLength4 bytes索引段的長(zhǎng)度
          BodyInfoLength4 bytes數(shù)據(jù)段的長(zhǎng)度
          LastMark1 byte0xED 固定值
          FileCount4 bytes文件數(shù)量
          IndexNameLength4 bytes文件名長(zhǎng)度
          NameNameLength bytes文件名,長(zhǎng)度為NameLength
          FileOffset4 bytes文件在數(shù)據(jù)段位置
          FileSize4 bytes文件大小
          LOOP......


          DataFiles Package......

          包結(jié)構(gòu)非常清晰,分為三個(gè)部分:

          1. 頭信息,包含一些包的標(biāo)識(shí),版本定義等,包含了三個(gè)冗余字段 --- 索引段和數(shù)據(jù)段的長(zhǎng)度應(yīng)該是用于做校驗(yàn),但實(shí)質(zhì)上沒有用(設(shè)計(jì)者覺得需要設(shè)計(jì)一些冗余字段來確保設(shè)計(jì)的完整性,防止解析的時(shí)候溢出,但實(shí)際工程實(shí)踐中并沒有起到相應(yīng)的作用),文件數(shù)量應(yīng)該是用于簡(jiǎn)化包解析過程,實(shí)際上知道了索引段長(zhǎng)度或數(shù)據(jù)段長(zhǎng)度中任何一個(gè)皆可推算出文件數(shù)量。

          2. 索引段,包含文件的元信息 --- 文件名以及文件位置(通過FileOffset和FileSize定位數(shù)據(jù)段中的文件)。如果從精簡(jiǎn)包的大小的角度來看,F(xiàn)ileOffset和FileSize只需一個(gè)存在即可,但是這樣解析包的難度就大大增加了,還是以工程實(shí)踐為主。

          3. 數(shù)據(jù)段,將所有的文件羅列在一起。

          由此可見,數(shù)據(jù)完全沒有經(jīng)過壓縮或者加密,連包的簽名信息也沒有。這導(dǎo)致只能在制品流程上進(jìn)行嚴(yán)格控制,例如在開發(fā)者上傳代碼過程中需要授信,必須經(jīng)過審查,也一定得由授信平臺(tái)進(jìn)行代碼分發(fā)等。這些都無關(guān)風(fēng)月,畢竟App Store就是這種模式,但是......

          如何拆解這種自定義文件格式呢?

          1. 對(duì)多個(gè)相同格式的文件進(jìn)行對(duì)比,對(duì)大體結(jié)構(gòu)有宏觀的感覺,很容易發(fā)現(xiàn)一些固定的字段以及一些結(jié)構(gòu)的長(zhǎng)度。對(duì)于像小程序這種有軟件本體的例子,還可以通過微量修改來觀察文件的變化來找到文件結(jié)構(gòu)和意義。

          2. 觀察特殊形式,首先英文的字符串是很明顯的,一般hex編輯器都自帶字符串化窗口,如果發(fā)現(xiàn)常見的字符串,就可以繼續(xù)去尋找字符串的邊界,字符串在二進(jìn)制文件里有兩種儲(chǔ)存方式:一種是不記錄字符串的長(zhǎng)度,讀取字符串到0x00位置,另一種一定在某一個(gè)地方儲(chǔ)存了這個(gè)字符串的長(zhǎng)度,因此一旦得知了該字符串的內(nèi)容,搜索該長(zhǎng)度字段即可獲取更多的信息。其次一些文件頭也非常顯眼,例如PNG[1]、ZIP[2]等通用標(biāo)準(zhǔn)文件格式都有固定的文件頭,在小程序的自定義格式中很容易發(fā)現(xiàn)一些png、jpg等資源的文件頭,因此可以定位數(shù)據(jù)區(qū)的位置。

          3. 對(duì)特定區(qū)域的二進(jìn)制進(jìn)行推理猜測(cè),一般來說二進(jìn)制文件里需要儲(chǔ)存大量的offset和size的數(shù)據(jù)作為數(shù)據(jù)段的索引。offset相當(dāng)于一個(gè)指針 - 索引文件在數(shù)據(jù)段的位置,工程實(shí)踐中,大部分儲(chǔ)存了offset的地方也會(huì)儲(chǔ)存size字段,畢竟在解析文件的時(shí)候會(huì)方便很多,也可以防止校驗(yàn)數(shù)據(jù)出現(xiàn)指針越界。因此,一旦確認(rèn)了文件中的數(shù)據(jù)段,就可以通過它的位置(offset)和大小(size)的實(shí)際數(shù)據(jù)進(jìn)行搜索,逆向找到指向它的數(shù)據(jù)位置,并且繼續(xù)逆向直到解析完整的文件。另外,如果要考慮設(shè)計(jì)的完備性,需要在二進(jìn)制文件中加入一些冗余字段進(jìn)行校驗(yàn)或者糾錯(cuò),例如CheckSum、CRC32、Alder32、MD5、ECC等,這些通過hex編輯器很容易計(jì)算并發(fā)現(xiàn)。小程序中FileCount的字段,這完全是為了工程實(shí)踐考慮的,在小程序中并沒有出現(xiàn)這類的計(jì)算值,這是可能是因?yàn)樾〕绦驗(yàn)榱撕?jiǎn)單設(shè)計(jì)考慮,一旦發(fā)現(xiàn)包體被篡改或損壞就直接丟棄。

          其實(shí)拆解小程序這種格式并不需要花費(fèi)特別多的精力,因?yàn)槠涓袷奖容^簡(jiǎn)單,而且從下圖流程上來說,后綴為wx的二進(jìn)制格式很可能與wxapkg格式是同源的。

          開發(fā)者工具上傳服務(wù)器分發(fā)原始代碼后綴為wx代碼包處理為wxapkg格式包體客戶端

          從開發(fā)者工具的代碼中的pack.js很容易發(fā)現(xiàn)一些對(duì)wx格式封裝的痕跡,只不過其中unpack.js的代碼被隱去了。通過實(shí)際的分析發(fā)現(xiàn)(wxapkg文件可以通過截獲網(wǎng)絡(luò)包請(qǐng)求獲得或者在本地的微信appbrand目錄下可以發(fā)現(xiàn)),wxapkg格式就是將wx格式進(jìn)行了轉(zhuǎn)化:Wxml -> Html、 Wxml -> JS、Wxss -> Css,其二進(jìn)制格式跟后綴名為wx二進(jìn)制格式完全一致。我寫了兩個(gè)版本的解析二進(jìn)制包的代碼(Javascript版本傳送門,python版本傳送門),其實(shí)非常簡(jiǎn)單,根據(jù)小程序包結(jié)構(gòu)一步一步解析,基本上沒啥難度。但如果要將Html -> Wxml, JS -> Wxml, Css -> Wxss進(jìn)行還原,其中JS -> Wxml的過程中需要將if語句轉(zhuǎn)變成wx-if標(biāo)簽、for語句轉(zhuǎn)化成wx-for標(biāo)簽有點(diǎn)麻煩,需要對(duì)解析包后的page-frame.html中 JS 代碼進(jìn)行修改,修改細(xì)節(jié)太多就不再詳說了,總之微信小程序的代碼沒有經(jīng)過額外的保護(hù)措施,比較容易進(jìn)行還原。

          (PS:暴露一下微信小程序未公開的API,openUrl- 在小程序中打開外部網(wǎng)頁;getGroupInfo- 獲得群的名稱,群內(nèi)成員昵稱等數(shù)據(jù);getOpenidToken - 獲得用戶openid;這些權(quán)限微信應(yīng)該是沒有準(zhǔn)備開放的。每次在進(jìn)入小程序時(shí),客戶端都需要先去請(qǐng)求該小程序的元數(shù)據(jù),例如應(yīng)用名、版本號(hào)、一些權(quán)限列表、代碼包下載地址等描述信息,修改這些元數(shù)據(jù)可以獲得相應(yīng)的權(quán)限,小程序的關(guān)鍵信息完全由后臺(tái)控制進(jìn)行配置,另外小程序的本地文件存儲(chǔ)采用HASH映射機(jī)制進(jìn)行文件定位,文件存儲(chǔ)在外部存儲(chǔ),本身通過自定義算法實(shí)現(xiàn)完整性校驗(yàn) - 首先,小程序最終存儲(chǔ)的文件名是:對(duì)稱加密(文件流內(nèi)容Alder32校驗(yàn)和 | 原始文件名)生成的,最終文件名和文件內(nèi)容會(huì)通過自校驗(yàn)判斷完整性;其次,本地緩存是通過HASH映射查找文件。所以即使能破解文件名和文件內(nèi)容,繞過文件自身簽名校驗(yàn),篡改為攻擊者的偽造文件,小程序APP也無法映射到該偽造文件進(jìn)行使用。)

          0x02 前端代碼安全

          由上可見,微信并沒有在代碼安全上進(jìn)行過多的考慮。這導(dǎo)致需要在應(yīng)用審核過程中花費(fèi)比較多的功夫,不然作品太容易被復(fù)制竄改,以至于會(huì)失去渠道先機(jī),這對(duì)流量是致命打擊。由于歷史原因,前端的代碼安全技術(shù)發(fā)展的比較緩慢,相比其他被編譯成二進(jìn)制的應(yīng)用,前端這種純文本應(yīng)用,太容易被辨識(shí)與竄改。

          對(duì)前端代碼進(jìn)行保護(hù)的目的在于讓機(jī)器容易識(shí)別相關(guān)的指令,而使人難以理解代碼的邏輯,但往往在對(duì)前端代碼進(jìn)行保護(hù)過程中,很難既兼顧指令效率又能使可讀性降低。因此,常常需要在現(xiàn)有的代碼中增加一些額外的驗(yàn)證邏輯,例如一些增加無效的代碼進(jìn)行混淆、采用守護(hù)代碼保護(hù)業(yè)務(wù)代碼不能在其他的域名下正常運(yùn)行、增加一些防止調(diào)試跟蹤的斷點(diǎn)等,這些措施都是使得破解代碼時(shí)人工成本增加,從而增加代碼的安全性。

          下面提供一些能夠增加前端代碼安全性的策略:

          1. 精簡(jiǎn)(minify)

          這是最簡(jiǎn)單且無害的方法,精簡(jiǎn)代碼能減少代碼體積,從而減小數(shù)據(jù)傳輸?shù)呢?fù)荷,同時(shí)也能降低代碼的可讀性。在小程序開發(fā)者工具中也提供該選項(xiàng)。對(duì)Javascript代碼進(jìn)行精簡(jiǎn)大致可以從以下幾個(gè)方面入手:

          • 刪除注釋,刪除無意義或者多余的空白,刪除可以省略的符號(hào)

          • 刪除一些沒有調(diào)用的代碼(Dead code),對(duì)函數(shù)進(jìn)行精簡(jiǎn)(三元運(yùn)算符?:、字符串操作、對(duì)象函數(shù)、對(duì)象繼承、函數(shù)引用、無名函數(shù)、遞歸函數(shù))

          • 將變量名進(jìn)行簡(jiǎn)化,將零散的變量聲明合并,縮短語句

            ......

          常用的工具有很多:YUI Compressor、UglifyJS、Google Closure Compiler、JS Packer、JS Min...

          使用工具對(duì)代碼進(jìn)行精簡(jiǎn)時(shí)需要注意:1. 最好備份原始代碼,方便調(diào)試與后期修改。 2. 用于調(diào)試精簡(jiǎn)代碼時(shí)保存的sourcemap,在線上應(yīng)該刪除。 3.編寫代碼的時(shí)候應(yīng)該嚴(yán)格按照規(guī)范,最好使用lint工具對(duì)代碼進(jìn)行檢查,精簡(jiǎn)代碼后導(dǎo)致代碼不可用時(shí),調(diào)試非常困難。

          這種簡(jiǎn)單的方法雖然很實(shí)用,但是也很容易被還原出源代碼,使用一些代碼格式化工具可以補(bǔ)齊被刪除的空格、換行、符號(hào)等,例如jsbeautifier。另外2015年就有相關(guān)的研究,從大量的代碼中推測(cè)出被精簡(jiǎn)的代碼,因?yàn)槿藢懘a總有固定的范式,所寫的代碼相似性都非常的高,如果用統(tǒng)計(jì)方式就很容易反推源代碼,蘇黎世聯(lián)邦理工大學(xué)Martin Vechev教授領(lǐng)帶下開發(fā)的工具JSNice就是一款運(yùn)用條件隨機(jī)場(chǎng)(Conditional Random Fields)機(jī)器學(xué)習(xí)和程序分析方法來還原Javascript代碼利器,利用大量的開源代碼,去學(xué)習(xí)命名和類型的規(guī)律。JSNice可以用于以下不同的方面:反精簡(jiǎn)的JavaScript代碼、對(duì)當(dāng)前的代碼提供更多的更有意義的變量名、自動(dòng)化程序的注釋等。相關(guān)論文傳送門 后臺(tái)代碼傳送門

          2. 混淆(obfuscation)

          混淆可以減低代碼的可讀性,防止被輕易追蹤出程序邏輯。常見的混淆方法有如下幾種:

          • 通過編碼混淆代碼,這篇文章《Javascript常用混淆方法》里面介紹了很多不錯(cuò)的編碼加密方法。但是這些方法有個(gè)明顯缺點(diǎn),增加代碼體積,而且編碼加密都是可逆的。

          • 將標(biāo)識(shí)符混淆和控制邏輯混淆(分離靜態(tài)資源、打亂控制流、增加無義的代碼等),例如aaencode和jjencode。

            標(biāo)識(shí)符混淆的方法有多種,有些與編碼混淆代碼方法有些重疊,常用方法有哈希函數(shù)命名、標(biāo)識(shí)符交換和重載歸納等。哈希函數(shù)命名是簡(jiǎn)單地將原來標(biāo)識(shí)符的字符串替換成該字符串的哈希值,這樣標(biāo)識(shí)符的字符串就與軟件代碼不相關(guān)了;標(biāo)識(shí)符交換是指先收集軟件代碼中所有的標(biāo)識(shí)符字符串,然后再隨機(jī)地分配給不同的標(biāo)識(shí)符,該方法不易被攻擊者察覺;重載歸納是指利用高級(jí)編程語言命名規(guī)則中的一些特點(diǎn),例如在不同的命名空間中變量名可以相同,使代碼中不同的標(biāo)識(shí)符盡量使用相同的字符串,增加攻擊者對(duì)軟件源代碼的理解難度。

            控制混淆是改變程序的執(zhí)行流程,從而打斷逆向分析人員的跟蹤思路,達(dá)到保護(hù)軟件的目的。一般采用的技術(shù)有插入指令、偽裝條件語句、斷點(diǎn)等。偽裝條件語句是當(dāng)程序順序執(zhí)行從A到B,混淆后在A和B之間加入條件判斷,使A執(zhí)行完后輸出TRUE或FALSE,但不論怎么輸出,B一定會(huì)執(zhí)行。控制混淆采用比較多的還有模糊謂詞、內(nèi)嵌外聯(lián)、打破順序等方法。模糊謂詞是利用消息不對(duì)稱的原理,在加入模糊謂詞時(shí)其值對(duì)混淆者是已知的,而對(duì)反混淆者卻很難推知。所以加入后將干擾反匯編者對(duì)值的分析。模糊謂詞的使用一般是插入一些死的或不相關(guān)的代碼(bogus code),或者是插入在循環(huán)或分支語句中,打斷程序執(zhí)行流程。內(nèi)嵌(in-line)是將一小段程序嵌入到被調(diào)用的每一個(gè)程序點(diǎn),外聯(lián)(out-line)是將沒有任何邏輯聯(lián)系的一段代碼抽象成一段可被多次調(diào)用的程序。打破順序是指打破程序的局部相關(guān)性。由于程序員往往傾向于把相關(guān)代碼放在一起,通過打破順序改變程序空間結(jié)構(gòu),將加大破解者的思維跳躍[3]。

          • 另外還有些混淆方式是專門針對(duì)于反混淆工具設(shè)計(jì)的,這就需要去仔細(xì)分析反混淆工具的原理,在一些特定的地方插入代碼使反混淆器進(jìn)入死循環(huán)或者異常跳出。

          一般來說,提供代碼精簡(jiǎn)的工具都會(huì)提供一些混淆的方法,除此之外,比較知名的商業(yè)工具有jasob、jscrambler,一般越商業(yè)的越難被反混淆,然而這些高級(jí)的代碼混淆也常會(huì)被用于隱藏應(yīng)用中的惡意代碼。對(duì)惡意代碼進(jìn)行混淆是為了躲避殺毒軟件的檢測(cè),這些代碼在被混淆擴(kuò)充后會(huì)難以被識(shí)別為惡意軟件。相應(yīng)的也有一些反混淆的工具出現(xiàn),例如上面提到的JSNice工具能夠?qū)煜拇a進(jìn)行推理,另外反混淆工具JSDetox專門針對(duì)一些混淆方法做過專門的支持。反混淆一直是一項(xiàng)體力活,根據(jù)不同的混淆策略需要進(jìn)行反推演算,這就是一場(chǎng)攻與防的游戲罷了。

          3. 加密(encryption)

          加密的關(guān)鍵思想在于將需要執(zhí)行的代碼進(jìn)行一次編碼,在執(zhí)行的時(shí)候還原出瀏覽器可執(zhí)行的合法的腳本,在某個(gè)角度也可以認(rèn)為是一種混淆的形式,看上去和可執(zhí)行文件的加殼有點(diǎn)類似。Javascript提供了將字符串當(dāng)做代碼執(zhí)行(evaluate)的能力,可以通過 Function constructor、eval、setTimeout、setInterval、Worker、DOM event等將字符串傳遞給JS引擎進(jìn)行解析執(zhí)行,由于有些需要用到eval函數(shù),會(huì)導(dǎo)致代碼性能會(huì)減低。以Worker執(zhí)行舉例:

          var URL=window.URL || window.webkitURL;var Blob=window.Blob || window.webkitBlob;var blobURL=URL.createObjectURL( new Blob(['console.log("Hello World!")'], {type: 'application/javascript'}));new Worker(blobURL);URL.revokeObjectURL(blobURL);

          有以下常見的幾種加密方法:

          base64編碼,一種簡(jiǎn)單的方法就是將代碼轉(zhuǎn)化成base64編碼,然后通過atob以及eval進(jìn)行解碼然后運(yùn)行,另外一種采用base62編碼技術(shù)更為常見,其最明顯的特征是生成的代碼以eval(function(p,a,c,k,e,r))開頭。無論代碼如何進(jìn)行變形,其最終都要調(diào)用一次eval等函數(shù)。解密的方法不需要對(duì)其算法做任何分析,只需要簡(jiǎn)單地找到這個(gè)最終的調(diào)用,改為console.log或者其他方式,將程序解碼后的結(jié)果按照字符串輸出即可。

          (PS: 從算法上看,packer是一種base64編碼字典壓縮策略,packer的base64編碼的壓縮率很高,精簡(jiǎn)后代碼依然可以減少50%體積以上,因?yàn)閹в薪鈮浩骱妥址恚介L(zhǎng)的代碼理論上壓縮率更高,想要了解詳情可以看看這篇文章《Packer,你對(duì)我的JS做了什么!》)

          • 使用復(fù)雜化表達(dá)式,在Javascript中可以把原本簡(jiǎn)單的字面量(Literal)、成員訪問(MemberExpression)、函數(shù)調(diào)用(CallExpression)等代碼片段變得難以閱讀。例如這個(gè)方法僅用+!等符號(hào)就足以實(shí)現(xiàn)幾乎任意Javascript代碼。在 JS 代碼中可以找到許多這樣互逆的運(yùn)算,通過使用隨機(jī)生成的方式將其組合使用,可以把簡(jiǎn)單的表達(dá)式無限復(fù)雜化。

          • 隱寫術(shù),將 JS 代碼隱藏到了特定的介質(zhì)當(dāng)中。如通過最低有效位(LSB)算法嵌入到圖片的 RGB 通道、隱藏在圖片 EXIF 元數(shù)據(jù)、隱藏在 HTML 空白字符、放到css文件中(利用content樣式能存放字符串的特性)等。比如一張圖片黑掉你:在圖片中嵌入惡意程序,這個(gè)正是使用了最低有效位平面算法,結(jié)合HTML5的canvas或者處理二進(jìn)制數(shù)據(jù)的TypeArray,抽取出載體中隱藏的數(shù)據(jù)(如代碼)。隱寫的方式同樣需要解碼程序和動(dòng)態(tài)執(zhí)行,所以破解的方式和前者相同,在瀏覽器上下文中劫持替換關(guān)鍵函數(shù)調(diào)用的行為,改為文本輸出即可得到載體中隱藏的代碼[4]。

          • 混合加密,單個(gè)方法容易被破解,但組合起來就不會(huì)那么容易了,破解成本也會(huì)指數(shù)增長(zhǎng),例如jdists采用組合加密和嵌套加密的方式。

          這些加密的方式都很容易通過對(duì)源代碼進(jìn)行詞法分析、語法分析進(jìn)行還原,首先將代碼的字符串轉(zhuǎn)換為抽象語法樹(Abstract Syntax Tree, AST)的數(shù)據(jù)形式,然后從語法樹的根節(jié)點(diǎn)開始,使用深度優(yōu)先遍歷整棵樹的所有節(jié)點(diǎn),根據(jù)節(jié)點(diǎn)上分析出來的指令逐個(gè)執(zhí)行,直到腳本結(jié)束返回結(jié)果。這種方法大多數(shù)用于對(duì)代碼進(jìn)行優(yōu)化,例如最近Facebook開源了代碼優(yōu)化工具Prepack,可以自動(dòng)消除冗余代碼,降低打包體積和執(zhí)行時(shí)間,基本上就可以用來將這些加密的字符串進(jìn)行還原,畢竟編碼這些字符串都是可以通過詞法語法推測(cè)出來的。

          4. 編譯(compile)

          Github上有一份清單記錄了所有Javascript擴(kuò)展語言,這些語言都可以通過編譯器轉(zhuǎn)化為Javascript語言,這也是前端發(fā)展的一個(gè)趨勢(shì),原來寫的html,css,Javascript已經(jīng)開始變成了一個(gè)“中間語言”,而且越來越多的團(tuán)隊(duì)也有了自己的一套前端編譯系統(tǒng)。Javascript越來越像Web中的匯編語言,特別是近些年Node的普及,讓前端變得越來越復(fù)雜,大量前端框架的出現(xiàn),使得Javascript代碼可以通過手工編寫,也可以從另一種語言編譯而來,詳情參考幾年前Brendan Eich(JavaScript之父)、Douglas Crockford(JSON之父),還有Mike Shaver(Mozilla技術(shù)副總裁)的郵件。通過編譯后的Javascript代碼越方便機(jī)器的理解,降低可讀性,在某一定角度上講,這也不愧為一種代碼保護(hù)措施。據(jù)說幾大科技巨頭正在醞釀給瀏覽器應(yīng)用設(shè)計(jì)一款通用的字節(jié)碼標(biāo)準(zhǔn)——WebAssembly,一旦這個(gè)設(shè)想得以實(shí)現(xiàn),代碼保護(hù)將可以引入真正意義上的“加殼”或者虛擬機(jī)保護(hù),對(duì)抗技術(shù)又將提升到一個(gè)新的臺(tái)階。目前在桌面端,使用NW.js框架可以JavaScript應(yīng)用程序的源代碼可以被編譯為本地代碼,在運(yùn)行時(shí)通過NW.js動(dòng)態(tài)還原出源代碼,但是這種方法目前會(huì)比正常的JS代碼慢30%左右。

          5. 防止被調(diào)試

          對(duì)代碼進(jìn)行破解分析無非分為靜態(tài)分析和動(dòng)態(tài)分析,如果對(duì)代碼進(jìn)行混淆加密等形式操作,那么靜態(tài)分析就很麻煩了,對(duì)代碼調(diào)試跟蹤分析可以對(duì)代碼整體邏輯有一個(gè)宏觀的把控。例如首先判斷瀏覽器是否開啟了開發(fā)者工具控制臺(tái)(目前最完美的解決方案?jìng)魉烷T),如果檢測(cè)出控制臺(tái)開啟則堵塞Javascript執(zhí)行或讓代碼異常跳出。另外Android 4.4及以上和iOS是支持webkit remote debug的,因此應(yīng)該在debug模式下,設(shè)置代碼可以被debug,release模式下,禁止debug以保護(hù)數(shù)據(jù)安全。

          6. 前后端協(xié)作

          首先得強(qiáng)調(diào)的事情是不要在前端放敏感數(shù)據(jù),前端容易破解,因此需要配合后端進(jìn)行安全防護(hù),例如微信小程序的登錄,必須利用授信的后端配合才能完成此項(xiàng)功能,另外在小程序的網(wǎng)絡(luò)請(qǐng)求中的referer是不可以設(shè)置的,格式固定為https://servicewechat.com/{appid}/{version}/page-frame.html,其中{appid}為小程序的appid,通過驗(yàn)證appid字段可以抵御一些直接的山寨,其次就是加快迭代速度更改代碼保護(hù)策略,這樣可以讓之前的分析失效,增加破解的成本。

          以上就是對(duì)當(dāng)前前端代碼安全進(jìn)行的探索,最后用一句話結(jié)束:

          Beneath this mask, there is more than flesh. Beneath this mask, there is an idea. And ideas are bulletproof.

          作者:不詳

          出處:知識(shí)商店

          程序簡(jiǎn)介

          • 小程序與網(wǎng)頁開發(fā)區(qū)別

          運(yùn)行環(huán)境不同

          網(wǎng)頁 —— 瀏覽器(內(nèi)核渲染)
          小程序 —— 微信環(huán)境

          API不同

          由于運(yùn)行環(huán)境不同,小程序無法調(diào)用DOM和BOM的API
          但是小程序可以調(diào)用微信客戶端的API,如定位,掃碼支付等

          開發(fā)模式不同

          網(wǎng)頁開發(fā)模式: 瀏覽器 + 代碼編輯器 (用記事本都可以敲出一個(gè)靜態(tài)頁面)
          小城開發(fā)流程:1. 注冊(cè)開發(fā)賬號(hào) 2. 安裝小程序開發(fā)工具 3. 創(chuàng)建與配置小程序
          相比較之下,小程序上手比較麻煩。

          小程序注冊(cè)

          使用瀏覽器打開 https://mp.weixin.qq.com/ 網(wǎng)址,點(diǎn)擊右上角的“立即注冊(cè)”即可進(jìn)入到小程序開發(fā)賬號(hào)進(jìn)行注冊(cè)。

          點(diǎn)擊注冊(cè)小程序 -> 填寫賬號(hào)信息 -> 填寫賬號(hào)信息 ->點(diǎn)擊鏈接激活賬號(hào) ->選擇主體類型(這里選擇為個(gè)人即可) -> 主體信息登記 - > 重點(diǎn): 獲取小程序自己的AppID,注冊(cè)后在開發(fā)設(shè)置即可找到

          小程序開發(fā)工具安裝

          小程序開發(fā)工具 是微信所推薦的開發(fā)工具
          她所提供有主要功能

          快速創(chuàng)建小程序項(xiàng)目
          對(duì)小程序項(xiàng)目代碼進(jìn)行編寫
          進(jìn)行編譯和預(yù)覽
          上傳代碼發(fā)布

          推薦下載和安裝最新的穩(wěn)定版(Stable Build)的微信開發(fā)者工具,下載頁面的鏈接如下:
          https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html

          創(chuàng)建小程序

          效果:

          文件代碼構(gòu)成

          • 這里主要講解文件最重要的部分

          pages 存放頁面的文件
          utils 存放工具性質(zhì)的模塊(腳本文件,如wxs腳本過濾文件)
          app.js 小程序的入口文件
          app.json 小程序的全局配置(配置窗口樣式版本,窗口路徑,tabBar導(dǎo)航條等)
          app.wxss 小程序的全局樣式
          project.config.json 小程序的項(xiàng)目配置
          sitemap.json 設(shè)置小程序可否被搜索到

          小程序頁面的組成部分


          每一個(gè)頁面都有四個(gè)文件

          .js 腳本文件(生命周期函數(shù),存放數(shù)據(jù),自定義函數(shù))
          .wxml 頁面結(jié)構(gòu)文件 (編輯頁面UI結(jié)構(gòu))
          .wxss 頁面樣式文件 (美化頁面樣式)
          .json 頁面配置文件 (配置頁面,如當(dāng)前窗口的外觀,引用自定義組件)

          關(guān)于json文件

          json 是一種數(shù)據(jù)格式,在實(shí)際開發(fā)中,json文件總是以配置文件存在,在小程序中也不例外

          • 項(xiàng)目的app.json文件(全局配置)
          • 項(xiàng)目的project.config.json文件(個(gè)性化設(shè)置)
          • 項(xiàng)目的sitemap.json 文件 (搜索設(shè)置)
          • 頁面的page.json文件(局部配置)

          app.json文件

          app.json 是關(guān)于項(xiàng)目的配置文件可以配置:

          window:全局定義小程序所有窗口樣式(導(dǎo)航條,背景色,文字樣式等)
          page:頁面路徑配置(創(chuàng)建頁面)
          style:全局定義樣式版本設(shè)置
          sitemaplocation: 用來指明 sitemap.json 的位置

          只需要在 app.json -> pages 中新增頁面的存放路徑,小程序開發(fā)者工具即可幫我們自動(dòng)創(chuàng)建對(duì)應(yīng)的頁面文件,(放在第一位置路徑的頁面即為小程序主頁面 這里為 index頁面)
          如圖所示:

          project.config.json文件

          這個(gè)是整個(gè)項(xiàng)目的配置文件,用來配置記錄我們對(duì)項(xiàng)目開發(fā)的個(gè)性化設(shè)置,如

          setting:編譯配置(如:設(shè)置是否檢查sitemap)
          appid:自己的appID
          projectname: 項(xiàng)目名稱

          sitemap.json文件

          微信現(xiàn)已開放小程序內(nèi)搜索,效果類似于 PC 網(wǎng)頁的 SEO。
          sitemap.json 文件用來配置小程序頁面是否允許能被搜索到

          當(dāng)開發(fā)者允許索引時(shí),微信會(huì)以爬蟲的形式,為小程序的內(nèi)容和項(xiàng)目名稱作為索引,用戶通過搜索關(guān)鍵字即可查到對(duì)應(yīng)小程序
          "action":"allow"
          頁面默認(rèn)是被允許索引的,要取消索引只需要設(shè)置為disallow

          page.json

          頁面的配置文件(局部配置)
          可以用來配置當(dāng)前頁面的窗口樣式,組件引用,與app.json的類似,
          在page.json的配置項(xiàng)會(huì)覆蓋全局樣式app.json的配置項(xiàng)

          WXML

          WXML(WeiXin Markup Language)是小程序框架設(shè)計(jì)的一套標(biāo)簽語言,用來構(gòu)建小程序頁面的結(jié)構(gòu),其作用類似于網(wǎng)頁開發(fā)中的 HTML。

          WXML 和 HTML 的區(qū)別

          標(biāo)簽名稱不同

          HTML (div, span, img, a)WXML(view, text, image, navigator)

          屬性節(jié)點(diǎn)不同

          < a href=“#”>超鏈接< navigator url=“/pages/home/home”>

          提供了類似于 Vue 中的模板語法

          數(shù)據(jù)綁定列表渲染條件渲染

          WXSS

          WXSS (WeiXin Style Sheets)是一套樣式語言,用于描述 WXML 的組件樣式,類似于網(wǎng)頁開發(fā)中的 CSS(cascading style sheet)。

          WXSS 和 CSS 的區(qū)別

          新增了rpx單位

          CSS 中需要手動(dòng)進(jìn)行像素單位換算,例如 remWXSS 在底層支持新的尺寸單位 rpx,在不同大小的屏幕上小程序會(huì)自動(dòng)進(jìn)行換算

          提供了全局的樣式和局部樣式

          項(xiàng)目根目錄中的 app.wxss 會(huì)作用于所有小程序頁面局部頁面的 .wxss 樣式僅對(duì)當(dāng)前頁面生效

          WXSS 僅支持部分 CSS 選擇器

          .class 和 #idelement并集選擇器、后代選擇器::after 和 ::before 等偽類選擇器

          JS 邏輯交互

          在小程序中,我們通過 .js 文件來處理用戶的操作。例如:響應(yīng)用戶的
          點(diǎn)擊、獲取用戶的位置等等

          小程序中的 JS 文件分為三大類(其他:自定義組件componnet),分別是:

          ① app.js

          是整個(gè)小程序項(xiàng)目的入口文件,通過調(diào)用 App() 函數(shù)來啟動(dòng)整個(gè)小程序

          ② 頁面的 .js 文件

          是頁面的入口文件,通過調(diào)用 Page() 函數(shù)來創(chuàng)建并運(yùn)行頁面

          ③ 普通的 .js 文件

          是普通的功能模塊文件,用來封裝公共的函數(shù)或?qū)傩怨╉撁媸褂?/span>

          js(java script) 是一個(gè)實(shí)現(xiàn)業(yè)務(wù)邏輯的文件。

          宿主環(huán)境(重要)

          • 宿主環(huán)境(host environment)指的是程序運(yùn)行所必須的依賴環(huán)境。

          例如:Andriod安卓系統(tǒng) 和 IOS蘋果系統(tǒng),是兩個(gè)不同的宿主環(huán)境,
          安卓的應(yīng)用必須要在安卓系統(tǒng)才能運(yùn)行,這也是為什么 之前有些軟件 安卓和蘋果不能兼容了。

          而小程序的宿主環(huán)境則是微信,小程序只能在微信上運(yùn)行,小程序借助宿主環(huán)境提供的能力,可以完成許多普通網(wǎng)頁無法完成的功能,例如:微信掃碼、微信支付、微信登錄、地理定位、etc

          小程序的宿主環(huán)境微信所包含的內(nèi)容

          通信模式運(yùn)行機(jī)制組件API

          通信模式

          通信主體

          小程序中通信的主體是渲染層和邏輯層,其中:
          ① WXML 模板和 WXSS 樣式工作在渲染層
          ② JS 腳本工作在邏輯層

          通信模式

          小程序中的通信模型分為兩部分:

          ① 渲染層和邏輯層之間的通信

          由微信客戶端進(jìn)行轉(zhuǎn)發(fā)

          ② 邏輯層和第三方服務(wù)器之間的通信

          由微信客戶端進(jìn)行轉(zhuǎn)發(fā)

          運(yùn)行機(jī)制

          • 小程序應(yīng)用啟動(dòng)的過程

          微信客戶端將代碼包下載到本地
          解析app.json 全局配置文件
          執(zhí)行小程序入口文件app.js,即調(diào)用app.js 的App實(shí)例(相當(dāng)于一個(gè)類)
          渲染小程序首頁

          • 小程序頁面啟動(dòng)的過程

          解析page.json局部配置文件
          執(zhí)行頁面入口文件page.js,即調(diào)用page.js 的page()創(chuàng)建頁面實(shí)例
          加載.wxml和.wxss 結(jié)構(gòu)和樣式文件

          恭喜你!!成功揚(yáng)起小程序的揚(yáng)帆!!!

          信小程序初步入坑小指南

          安裝工具

          https://developers.weixin.qq.com/miniprogram/dev/devtools/beta.html

          打開鏈接下載小程序云開發(fā)

          app.json

          為json格式的文件,為一個(gè)配置文件,屬于全局的

          初始化的文件內(nèi)容

          {

          "pages":[

          "pages/index/index",

          "pages/logs/logs"

          ],

          "window":{

          "backgroundTextStyle":"light",

          "navigationBarBackgroundColor": "#fff",

          "navigationBarTitleText": "WeChat",

          "navigationBarTextStyle":"black"

          }

          }

          pages

          各個(gè)參數(shù)的解釋,pages為頁面,每一次更改頁面,增加或者新增加頁面都需要修改pages參數(shù)。不需要加后綴名,微信框架會(huì)自動(dòng)添加后綴名。

          window

          對(duì)于全局導(dǎo)航欄的設(shè)置。

          navigationBarBackgroundColor 設(shè)置全局的導(dǎo)航欄的顏色

          navigationBarTitleText 設(shè)置導(dǎo)航欄的文字內(nèi)容

          navigationStyle 設(shè)置導(dǎo)航欄的樣式

          backgroundColor 設(shè)置窗體的顏色,即下拉刷新透露出的顏色

          即需要設(shè)置 "enablePullDownRefresh": true, 其布爾值為true即可進(jìn)行漏出設(shè)置的窗體顏色。

          backgroundTextStyle 設(shè)置下拉的loding樣式

          tabBar

          是下方的導(dǎo)航欄的設(shè)置。這個(gè)直接看文檔吧。。

          https://developers.weixin.qq.com/miniprogram/dev/framework/config.html#%E5%85%A8%E5%B1%80%E9%85%8D%E7%BD%AE

          app.json文件如下

          {

          "pages":[

          "pages/index/index",

          "pages/logs/logs"

          ],

          "window":{

          "backgroundTextStyle":"dark",

          "navigationBarBackgroundColor": "#c7dbc8",

          "navigationBarTitleText": "小小",

          "navigationBarTextStyle":"whiter",

          "enablePullDownRefresh": true,

          "backgroundColor": "#fceaea"

          },

          "tabBar": {

          "list": [{

          "text": "ming",

          "pagePath": "pages/logs/logs"

          }, {

          "text":"home",

          "pagePath": "pages/index/index"

          }],

          "color": "#f8fcea",

          "backgroundColor": "#ff9999",

          "selectedColor": "#c5ff99",

          "borderStyle": "white",

          "position": "top"

          },

          "functionalPages": true

          }

          project.config.json

          該文件為一個(gè)本地的配置文件

          就是對(duì)于微信的一些設(shè)置

          wxml模板

          wxml ≈ html

          感覺很像ejs,好吧

          wxml中內(nèi)容

          <text>{{mesg}}</text>

          在同路下的js文件中

          Page({

          data: {

          msg: "hello world"

          }

          })

          渲染出來的結(jié)果

          js交互

          Page({

          data: {

          msg: "hello world"

          },

          clickMe: function() {

          this.setData({msg: "3333"});

          }

          })

          wxml

          <text>{{msg}}</text>

          <button bindtap="clickMe">點(diǎn)擊按鈕</button>

          點(diǎn)擊按鈕將會(huì)自動(dòng)更新頁面的數(shù)據(jù)

          小程序的啟動(dòng)

          客戶端打開小程序之前,會(huì)把小程序全部下載到本地。

          通過app.json可以知道頁面的啟動(dòng)地址。

          小程序只有一個(gè)app的實(shí)例,全局共享。

          啟動(dòng)完成后觸發(fā)onLaunch事件,然后運(yùn)行回調(diào)函數(shù)

          在小程序啟動(dòng)完畢以后控制臺(tái)輸出內(nèi)容

          App({

          onLaunch: ()=> {

          console.log('小程序啟動(dòng)完畢')

          }

          })

          上方進(jìn)行了一次回調(diào)

          小程序的頁面

          一個(gè)頁面有四個(gè)文件,分別是json(配置文件),wxml(頁面文件,類似于html),js文件(處理頁面的相關(guān)交互,和網(wǎng)頁類似)

          js中有一個(gè)page,為一個(gè)頁面的構(gòu)造器,渲染頁面的時(shí)候先裝載json文件,配置當(dāng)前的頂部導(dǎo)航,接著裝載wxml文件,配置頁面的DOM,在裝載wxss,進(jìn)行對(duì)頁面樣式的處理

          和網(wǎng)頁的類似,都是同樣的

          最后將會(huì)讀取js文件,根據(jù)頁面中的page函數(shù)即構(gòu)造器中的內(nèi)容,將wxml和data進(jìn)行綁定,渲染出結(jié)果,為mvvm

          mvc 分別是模型層,視圖層,和控制器,當(dāng)用戶請(qǐng)求到達(dá)以后,將會(huì)先經(jīng)過路由,即入口文件,即主文件中的server.js文件,接著進(jìn)入lib目錄下的route.js文件,對(duì)路由進(jìn)行分發(fā),路由在將數(shù)據(jù)傳遞給控制器,controller ,controller 收到請(qǐng)求以后再向model 索要數(shù)據(jù),索要完成以后,在將數(shù)據(jù)導(dǎo)向view層,即ejs文件的地方,渲染完成文件以后返回給用戶。 mvp 在mvc的基礎(chǔ)上,view中不寫邏輯,,在原先控制器的地方完成頁面的合并

          mvvm 和mvp類似,只不過view和原先的控制器雙向綁定,即使用get 和 set方式,達(dá)到當(dāng)數(shù)據(jù)更改的時(shí)候,進(jìn)行回調(diào)

          組件

          是滴,小程序采用組件化,例如生成地圖map即可

          ps 在網(wǎng)頁中,生成地圖,需要引入第三方的js文件,以及第三方的api,達(dá)到生成地圖的目的。

          api 實(shí)現(xiàn)調(diào)用api的能力

          api的回調(diào)為異步操作,所以呢,依舊要進(jìn)行回調(diào)

          發(fā)布者-訂閱模型

          小程序的邏輯層

          小程序使用的是js引擎進(jìn)行渲染,邏輯層將數(shù)據(jù)發(fā)送給視圖層,視圖層接受事件的反饋,開發(fā)者寫的所有文件都會(huì)打包成為一份js文件,小程序運(yùn)行時(shí)啟動(dòng),小程序離開時(shí)銷毀,

          吐槽 一些瀏覽器里的js在微信小程序無法使用,小程序還有npm? 天哪,

          注冊(cè)程序

          app()函數(shù),必須在app.js文件中調(diào)用,接受一個(gè)object的參數(shù)

          前臺(tái)后臺(tái)定義,當(dāng)用戶點(diǎn)擊左上角關(guān)閉的時(shí)候, 或者按住home離開微信,小程序,沒有銷毀,將會(huì)進(jìn)入后臺(tái),再次打開進(jìn)入前臺(tái),當(dāng)小程序進(jìn)入后臺(tái)一段時(shí)間以后,系統(tǒng)資源占用過高將會(huì)不定時(shí)的銷毀

          onLaunch

          代碼如下

          //app.js

          App({

          onLaunch: (onlaunch)=> {

          console.log(onlaunch)

          }

          })

          效果如下

          即獲取頁面的參數(shù)

          相比較網(wǎng)頁好輕松,,網(wǎng)頁還需要進(jìn)行先字符串分隔,然后再次以=進(jìn)行分隔,然后將其循環(huán),遍歷該數(shù)組,將其保存進(jìn)入對(duì)象,完成。如果使用json字符串進(jìn)行傳,可能稍微方便一點(diǎn)

          getAPP

          getApp函數(shù)能獲取小程序的各種函數(shù),即onLaunch等其他的一些函數(shù)

          即獲取到小程序的一個(gè)實(shí)例

          注冊(cè)頁面

          page為一個(gè)構(gòu)造函數(shù),接受對(duì)象,用來對(duì)頁面進(jìn)行初始化

          data

          data和渲染層,進(jìn)行數(shù)據(jù)的綁定

          onLoad

          進(jìn)行參數(shù)的傳值

          Page({

          data: {

          msg: "hello world"

          },

          clickMe: function() {

          this.setData({msg: "3333"});

          },

          onLoad: (query)=> {

          console.log(query);

          }

          })

          onShow

          頁面切入的時(shí)候,將會(huì)進(jìn)行顯示。

          例: 按住home按鍵,在回到小程序界面的時(shí)候,將會(huì)回調(diào)該注冊(cè)的函數(shù)

          onReady

          頁面渲染完成以后,將會(huì)回調(diào)該函數(shù)

          onHide

          頁面切換的時(shí)候,將會(huì)回調(diào)注冊(cè)的函數(shù)

          onUnload

          頁面卸載的時(shí)候,將會(huì)觸發(fā)

          頁面事件

          onPullDownRefresh

          用戶下拉,需要設(shè)置onReachBottomDistance,即用戶下拉到頁面底部多少的時(shí)候,觸發(fā)該事件

          onPageScroll

          用戶滑動(dòng)的距離,沒有正負(fù)之分

          onShareAppMessage

          用戶轉(zhuǎn)發(fā)的接口

          在button組件中設(shè)置

          open-type="share"

          即可設(shè)置為轉(zhuǎn)發(fā)按鈕

          需要有return進(jìn)行返回參數(shù)

          onTabItemTap

          單擊tab將會(huì)觸發(fā)該內(nèi)容

          onTabItemTap: (item)=>{

          console.log(item.index)

          console.log(item.pagePath)

          console.log(item.text)

          }

          如果按住導(dǎo)航,將會(huì)進(jìn)行輸出

          當(dāng)單擊組件的時(shí)候,發(fā)生事件

          這一點(diǎn)終于和網(wǎng)頁類似了。網(wǎng)頁中也可以實(shí)現(xiàn)一個(gè)元素和事件進(jìn)行相互的綁定

          viewTap: ()=> {

          console.log('您已經(jīng)單擊按鈕')

          }

          <button bindtap="viewTap">這是按鈕</button>

          Page.route

          當(dāng)前頁面的路徑,類似于網(wǎng)頁的

          window.location.href

          可以獲取到當(dāng)前頁面的url

          onShow: function() {

          console.log('頁面進(jìn)行顯示切入前臺(tái)');

          console.log(this.route);

          },

          當(dāng)用戶切換tab的時(shí)候,將會(huì)立馬輸出當(dāng)前頁面的path值

          其中this指代當(dāng)前的page,因?yàn)槭窃谝粋€(gè)page函數(shù)內(nèi)部

          Page.prototype.setData

          為page的繼承函數(shù),將數(shù)據(jù)從邏輯層發(fā)送到視圖層(異步),this.data的值,(同步

          ps 單純的改變this.data的值,不會(huì)起作用,因?yàn)轫撁嬉呀?jīng)渲染完成,需要進(jìn)行發(fā)送到視圖層,進(jìn)行更新視圖

          ps 是的。小程序使用的就是mvvm

          js

          Page({

          data: {

          text0: "11111",

          text1: "22222",

          text2: "333333",

          text3: "444444"

          },

          changeText0: function() {

          this.setData({text0: "22222"})

          },

          changeText1: function () {

          this.setData({ text1: "33333" })

          },

          changeText2: function () {

          this.setData({ text2: "44444" })

          },

          changeText3: function () {

          this.setData({ text3: "55555" })

          },

          })

          wxml

          <view>{{text0}}</view>

          <button bindtap="changeText0">更改上方文字</button>

          <view>{{text1}}</view>

          <button bindtap="changeText1">更改上方文字</button>

          <view>{{text2}}</view>

          <button bindtap="changeText2">更改上方文字</button>

          <view>{{text3}}</view>

          <button bindtap="changeText3">更改上方文字</button>

          路由

          小程序中的路由是有框架達(dá)到的

          框架用棧的方式維護(hù)了當(dāng)前的所有頁面

          ps 又見到棧了

          getCurrentPages

          該函數(shù)用于獲取當(dāng)前頁面的棧,返回的是一個(gè)數(shù)組

          適用于獲取上一個(gè)返回的頁面

          全局變量

          js文件中聲明的變量,和函數(shù)只在文件中有用,不同文件可以聲明相同的

          ps 如果加載到一個(gè)頁面的時(shí)候,將會(huì)發(fā)生命名沖突

          可以在app.js文件中設(shè)置全局的數(shù)據(jù)

          // a.js

          var app=getApp();

          console.log(app.globalData)

          //app.js

          App({

          onLaunch: (onlaunch)=> {

          console.log(onlaunch)

          },

          globalData: 333

          })

          模塊化

          類似于node.js的包

          使用的同樣是exports進(jìn)行接口的暴露

          是滴,類比node.js即可,是滴,小程序還不支持引入npm包

          然后就可以歡快的引入npm包了。

          ps 貌似有個(gè)小坑。


          主站蜘蛛池模板: 性色av一区二区三区夜夜嗨 | 成人精品视频一区二区| 亚州AV综合色区无码一区| 一区二区三区视频免费观看| 91福利国产在线观看一区二区| 精品国产日产一区二区三区| 日韩国产一区二区| 国产精品合集一区二区三区| 无码人妻精品一区二区三区99仓本 | 无码人妻视频一区二区三区| 亚洲av高清在线观看一区二区| 麻豆精品人妻一区二区三区蜜桃 | 国产一区二区三区久久| 日本一区视频在线播放| 国产在线步兵一区二区三区| 麻豆高清免费国产一区| 久久精品一区二区东京热| 国内精品无码一区二区三区| 一区二区三区在线视频播放| 波多野结衣中文一区二区免费| 夜精品a一区二区三区| 波多野结衣久久一区二区| 亚洲一区无码中文字幕乱码| 一区二区三区观看免费中文视频在线播放 | 波多野结衣中文一区| 无码国产精品一区二区高潮| 最新欧美精品一区二区三区 | 国产情侣一区二区三区 | 精品国产一区二区三区色欲| 久久久久一区二区三区| 无码人妻精品一区二区三区在线| 久久综合精品不卡一区二区 | 日韩在线一区二区三区免费视频| 精品国产一区二区三区在线| 国产拳头交一区二区| 亚洲国产一区二区三区 | 欧洲精品一区二区三区在线观看| 在线观看国产一区二三区| 无码日韩精品一区二区免费| 亚洲日韩国产一区二区三区在线| 日韩在线一区高清在线|