iOS 分析一次有意思的需求——HTML代碼注入

級(jí)別: ★★☆☆☆
標(biāo)簽:「HTML代碼注入」「WKScriptMessageHandler」「iOS與JS交互」
作者: chouheiwa
審校: QiShare團(tuán)隊(duì)


有個(gè)朋友問了我一個(gè)問題:

他們通過WKWebView低滩,訪問了一個(gè)其他的頁面类少,然后希望原生獲得用戶的輸入信息虏两。

其實(shí),我之前接觸WKWebView并不多惠况,但是這個(gè)問題我覺得很有意思。這篇文章便是我解決這個(gè)問題的全部思路,與最終的解決辦法。

思路分析 與 代碼實(shí)踐

這個(gè)問題其實(shí)很具象了渔彰,就是希望原生獲得H5的用戶輸入內(nèi)容嵌屎。

接下來我們就需要分析這個(gè)需求了。

首先我們先需要抓住兩個(gè)點(diǎn)恍涂,1個(gè)是H5宝惰,1個(gè)是原生。

所以這個(gè)問題現(xiàn)在被我拆分出了1個(gè)額外的問題再沧。

HTML能否和原生交互?如何進(jìn)行?

這個(gè)問題其實(shí)是一個(gè)很關(guān)鍵的問題尼夺,因?yàn)槲覀冎挥袑?shí)現(xiàn)了原生和HTML的交互,才能獲得相關(guān)信息炒瘸。(這里我們假定網(wǎng)頁是我們自己寫的淤堵,完全受操縱于我們自己)

于是便搜尋資料,發(fā)現(xiàn)WKWebView提供了一個(gè)很方便的交互渠道WKScriptMessageHandler顷扩,我們通過對(duì)WKWebView進(jìn)行相關(guān)的定制操作便可以解決拐邪。

我們先創(chuàng)建一個(gè)工程,這里我把整個(gè)工程命名為InjectHTML隘截。

為了防止循環(huán)引用扎阶,我們先構(gòu)建一個(gè)中間層ScriptHandler事富。

import Foundation
import WebKit
// 這里我們使用一個(gè)中間層來解除循環(huán)WebView和Controller間的循環(huán)引用問題
class ScriptHandler: NSObject, WKScriptMessageHandler {

    weak var delegate: WKScriptMessageHandler?

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        delegate?.userContentController(userContentController, didReceive: message)
    }

    init(delegate: WKScriptMessageHandler? = nil) {
        self.delegate = delegate
    }
}

在ViewController中的代碼:

import UIKit
import WebKit
class ViewController: UIViewController {
    var webview: WKWebView!

    static let scriptKey = "InjectHTML"

    override func viewDidLoad() {
        super.viewDidLoad()
        //初始化 Configuration
        let configuration = WKWebViewConfiguration()
        configuration.userContentController = WKUserContentController()
        // 給Configuration 增加一個(gè)js script處理器
        // 采用了中間層的因素,避免循環(huán)引用導(dǎo)致無法釋放問題
        configuration.userContentController.add(ScriptHandler(delegate: self), name: ViewController.scriptKey)

        webview = WKWebView(frame: view.bounds, configuration: configuration)

        view.addSubview(webview)
        // 設(shè)置導(dǎo)航處理器
        webview.navigationDelegate = self
        // 我們先從本地讀網(wǎng)頁方便自我改動(dòng)測(cè)試
        let fileURL = Bundle.main.url(forResource: "index", withExtension: "html")
        // 加載網(wǎng)頁
        webview.loadFileURL(fileURL!, allowingReadAccessTo: fileURL!)
    }
}

extension ViewController: WKScriptMessageHandler {
    // 遵守協(xié)議
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        // 因html可能傳遞多種類型名稱乘陪,我們這里須指定key
        if message.name == ViewController.scriptKey {
            guard let dic = message.body as? [String: String] else {
                return
            }
            // 交給真實(shí)處理解析函數(shù)
            receiveInputValue(para: dic)
        }
    }
    // 解析函數(shù)可以負(fù)責(zé)更具體的內(nèi)容,因?yàn)閐emo雕擂,故此只是打印
    func receiveInputValue(para: [String: String]) {
        let title = para["title"] ?? "無值"

        let message = para["message"] ?? "無值"

        let `id` = para["id"] ?? "無值"

        print("title: \(title)")
        print("message: \(message)")
        print("id: \(id)")
    }
}
// 遵循導(dǎo)航協(xié)議啡邑,方便我們知道何時(shí)網(wǎng)頁加載完成
extension ViewController: WKNavigationDelegate {
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {

    }
}

這樣子我們的ViewController就構(gòu)建完成,訪問了一個(gè)本地index.html的網(wǎng)頁井赌。

我們?cè)陧?xiàng)目中新建一個(gè)html文件(從外界拖入也可)谤逼,

html頁面只用于測(cè)試,因此我們也就不做類似自適應(yīng)之類的css樣式了仇穗。

html代碼如下:

<html>
    <head>
        <title>注入測(cè)試網(wǎng)頁</title>
    </head>
    <body bgcolor="#FFFFFF">
        <h1>Test for inject js</h1>
        <!--添加一個(gè)button按鈕用以測(cè)試點(diǎn)擊事件-->
        <button onclick="onClickTest()">Test Button</button>
    </body>
    <script type="text/javascript">
        function onClickTest() {
            // 注意流部,這里是需要注意的是我們?cè)赩iewController中定義的Script Name需要作為messageHandler的一個(gè)屬性
            window.webkit.messageHandlers.InjectHTML.postMessage({title: 'test title', message:'test message', id: 'test id'})
            // 若我們未注冊(cè)此名稱,則無法觸發(fā)對(duì)應(yīng)回調(diào)纹坐,postMessage中的參數(shù)可傳為任意的枝冀,但我們?cè)谠卸x為字典了,則我們?cè)谶@里需要傳入字典
            window.webkit.messageHandlers.InjectHTMLS.postMessage({title: 'test title', message:'test message', id: 'test id'})
        }
    </script>
</html>

其中對(duì)應(yīng)部分均已加上注釋耘子,接下來我們來跑一遍測(cè)試結(jié)果:

title: test title

message: test message

id: test id

當(dāng)我們點(diǎn)擊WebView上的按鈕時(shí)候果漾,我們可以打印出來對(duì)應(yīng)的js端返回結(jié)果,說明成功了谷誓。

所以绒障,這個(gè)需求我們可以說解析了3分之1了。


接下來我們需要繼續(xù)分析了捍歪,這個(gè)需求需要我們能監(jiān)控所有的輸入控件户辱。

所以我們需要從網(wǎng)頁端剖析了,問題回歸HTML端糙臼。

我們繼續(xù)拆解問題庐镐,我們既然要監(jiān)控所有的輸入控件,那么我們首先就得知道弓摘,我們能否獲得所有的輸入控件(甚至說焚鹊,我們能否獲得頁面的所有控件,然后進(jìn)行遍歷過濾也可以)韧献。

HTML如何獲取指定元素

這個(gè)問題末患,通過搜索與詢問前端朋友得知:

HTML提供了對(duì)應(yīng)的API,直接獲取指定標(biāo)簽的內(nèi)容锤窑,因?yàn)槲覀兪且@得輸入框璧针,輸入框在HTML中的標(biāo)簽是input。所以我們獲得頁面中所有輸入框元素的方法就已經(jīng)出來了渊啰。

// 獲得所有input數(shù)組
var inputs = document.getElementsByTagName("input")

那么問題就迎刃而解了探橱,然后我們還是需要測(cè)試一下申屹,畢竟萬一這個(gè)方法不好用怎么辦?

這里就不在手機(jī)端進(jìn)行測(cè)試隧膏,因?yàn)槟M器還是比較費(fèi)事的哗讥,并且我們?nèi)绻氪蛴onsole日志的話,看起來也不那么容易胞枕,因此我們將接下來的測(cè)試網(wǎng)頁步驟杆煞,直接挪到Google Chrome上。

這里還是給出對(duì)應(yīng)的html測(cè)試代碼:

<html>
    <head>
        <title>注入測(cè)試網(wǎng)頁</title>
    </head>
    <body bgcolor="#FFFFFF">
        <h1>Test for inject js</h1>
        <!--添加一個(gè)button按鈕用以測(cè)試點(diǎn)擊事件-->
        <button onclick="onClickTest()">Test Button</button>

        <input type="text" placeholder="Test Input1">
        <input type="text" placeholder="Test Input2">
    </body>
    <script type="text/javascript">
        // 獲得所有input數(shù)組
        var inputs = document.getElementsByTagName("input");
        // 打印input數(shù)組
        console.log(inputs)
    </script>
</html>

然后使用瀏覽器(下文瀏覽器均為Google Chrome)打開腐泻。

我們按Command+Shift+c即可打開頁面審查和網(wǎng)頁控制臺(tái)决乎,在這里我們看見了我們執(zhí)行網(wǎng)頁的log,打印結(jié)果如下:

log打印

說明我們獲取input成功了派桩。

接下來我們只需要進(jìn)行過濾即可獲得我們需要的頁面元素了(如過濾checkbox之類的)构诚。

然后接下來新的問題來了。

JS代碼如何動(dòng)態(tài)添加事件

我們已經(jīng)通過js代碼獲得input了铆惑,所以問題就變到了如何添加點(diǎn)擊事件范嘱。畢竟原生的輸入框還有至少監(jiān)聽輸入事件,或者監(jiān)聽輸入完成類的员魏,那么JS端理應(yīng)也存在彤侍。

這里感謝菜鳥教程,查詢到了一個(gè)函數(shù)addEventListener逆趋。

這個(gè)函數(shù)的功能就是給HTTP的DOM元素增加對(duì)應(yīng)的事件盏阶,也就是說我們可以通過這個(gè)方法額外的增加點(diǎn)擊事件。

同時(shí)闻书,新增加的事件并不會(huì)覆蓋原有事件名斟,一個(gè)元素可以擁有多個(gè)同樣的事件(如一個(gè)按鈕可以同時(shí)出發(fā)兩個(gè)onClick事件)。

這個(gè)函數(shù)給了我們新的天地啊魄眉。因此我們可以給input增加事件砰盐,這里我選擇了兩個(gè)事件,一個(gè)是input事件(輸入框文字發(fā)生變化)坑律,一個(gè)是change事件(輸入框失去焦點(diǎn))岩梳。

我們修改上文的html代碼,修改結(jié)果如下:

<html>
    <head>
        <title>注入測(cè)試網(wǎng)頁</title>
    </head>
    <body bgcolor="#FFFFFF">
        <h1>Test for inject js</h1>
        <!--添加一個(gè)button按鈕用以測(cè)試點(diǎn)擊事件-->
        <button onclick="onClickTest()">Test Button</button>
        <!--這里給input增加一些回調(diào)-->
        <input type="text" placeholder="Test Input1" onchange="onChange()" oninput="onInput()">
        <input type="text" placeholder="Test Input2" onchange="onChange()" oninput="onInput()">
    </body>
    <script type="text/javascript">
        // 這里是為了測(cè)試原有對(duì)應(yīng)事件是否會(huì)被覆蓋
        function onChange(input) {
            console.log("原有 失去焦點(diǎn)");
            console.log(input);
        }

        // 這里是為了測(cè)試原有對(duì)應(yīng)事件是否會(huì)被覆蓋
        function onInput(input) {
            console.log("原有 鍵盤輸入");
            console.log(input);
        }
    </script>
    <script type="text/javascript">
        function demoOnchange(input) {
            console.log("失去焦點(diǎn)");
            console.log(input);
        }
        function demoOnInput(input) {
            console.log("鍵盤輸入");
            console.log(input);
        }
        function demoSet() {
            var inputs=document.getElementsByTagName("input");
            for(var i=0;i < inputs.length;i++) {
                var input = inputs[i];
                // 這里我們?cè)黾右恍┻^濾條件晃择,因?yàn)槲覀冇袝r(shí)并不需要所有的input冀值,這里我只是允許了text(文字輸入框)
                if(input.type=="text") {
                    input.addEventListener('change', demoOnchange);
                    input.addEventListener('input', demoOnInput)
                }
            }
        }
        demoSet();
    </script>
</html>

繼續(xù)進(jìn)入瀏覽器測(cè)試網(wǎng)址,我們可以看到宫屠,當(dāng)我們輸入的時(shí)候列疗,同時(shí)觸發(fā)了兩個(gè)回調(diào),說明后期注入有效浪蹂。

接下來我們需要繼續(xù)考慮抵栈,我們需要能讓原生接收到對(duì)應(yīng)事件告材。

原生如何收到對(duì)應(yīng)事件?

我們?cè)谏衔闹幸呀?jīng)知道了js如何調(diào)用原生古劲,那么這一步我們就可以直接實(shí)現(xiàn)了斥赋。

這一步還是更改html代碼:

<html>
    <head>
        <!--HTML頁面內(nèi)容需告知為utf8,否則會(huì)出現(xiàn)亂碼問題-->
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
        <title>注入測(cè)試網(wǎng)頁</title>
    </head>
    <body bgcolor="#FFFFFF">
        <h1>Test for inject js</h1>
        <!--添加一個(gè)button按鈕用以測(cè)試點(diǎn)擊事件-->
        <button onclick="onClickTest()">Test Button</button>
        <!--這里給input增加一些回調(diào)-->
        <input type="text" placeholder="Test Input1" onchange="onChange()" oninput="onInput()">
        <input type="text" placeholder="Test Input2" onchange="onChange()" oninput="onInput()">
    </body>
    <script type="text/javascript">
        // 這里是為了測(cè)試原有對(duì)應(yīng)事件是否會(huì)被覆蓋
        function onChange(input) {
            console.log("原有 失去焦點(diǎn)");
            console.log(input);
        }

        // 這里是為了測(cè)試原有對(duì)應(yīng)事件是否會(huì)被覆蓋
        function onInput(input) {
            console.log("原有 鍵盤輸入");
            console.log(input);
        }
    </script>
    <script type="text/javascript">
        function demoOnchange(input) {
            // 這里产艾,input是一個(gè)點(diǎn)擊事件灿渴,target才是真正的input元素,我們可以通過此任意獲取input的相關(guān)信息胰舆,如class,id以及其他的一些信息等等
            window.webkit.messageHandlers.InjectHTML.postMessage({title: '輸入框失去焦點(diǎn)', message:input.target.value, id: input.target.id});
        }
        function demoOnInput(input) {
            window.webkit.messageHandlers.InjectHTML.postMessage({title: "輸入框正在輸入", message:input.target.value, id: input.target.id});
        }
        function demoSet() {
            var inputs=document.getElementsByTagName("input");
            for(var i=0;i < inputs.length;i++) {
                var input = inputs[i];
                // 這里我們?cè)黾右恍┻^濾條件蹬挤,因?yàn)槲覀冇袝r(shí)并不需要所有的input缚窿,這里我只是允許了text(文字輸入框)
                if(input.type=="text") {
                    input.addEventListener('change', demoOnchange);
                    input.addEventListener('input', demoOnInput)
                }
            }
        }
        demoSet();
    </script>
</html>

重新啟動(dòng)剛才的項(xiàng)目App,在輸入框中輸入焰扳,可以看到控制臺(tái)中返回了數(shù)據(jù):

title: 輸入框正在輸入

message: 1

id:

title: 輸入框失去焦點(diǎn)

message: 1

id:

我們的打印出來了倦零。到這里我們自有網(wǎng)頁的測(cè)試全部通過,那么就剩下最后一步了吨悍,如何在三方網(wǎng)頁上執(zhí)行?

JS代碼注入

WKWebView既然能讓js調(diào)用OC扫茅,那么OC能否調(diào)用js代碼呢?

答案是可以的,WKWebView提供給我們?cè)姆椒梢詣?dòng)態(tài)的執(zhí)行js代碼育瓜。

webview.evaluateJavaScript(someTest, completionHandler: closure)

這個(gè)方法可以讓我們動(dòng)態(tài)的執(zhí)行由原生生成的js代碼葫隙。

那么我們需要執(zhí)行什么方法呢?

自然就是我們上述研究出來的獲取HTML的元素并增加事件回調(diào)的事情啊。

這里我們?cè)陧?xiàng)目中新建一個(gè)js文件躏仇,方便我們以后修改js代碼恋脚,名稱就叫Inject.js

從項(xiàng)目中的html文件中把如下方法復(fù)制進(jìn)來:

function demoOnchange(input) {
    window.webkit.messageHandlers.InjectHTML.postMessage({title: "輸入框失去焦點(diǎn)", message:input.target.value, id: input.target.id});
}
function demoOnInput(input) {
    window.webkit.messageHandlers.InjectHTML.postMessage({title: "輸入框正在輸入", message:input.target.value, id: input.target.id});
}
function demoSet() {
    var inputs=document.getElementsByTagName("input");
    for(var i=0;i < inputs.length;i++) {
        var input = inputs[i];
                
        if(input.type=="text") {
            input.addEventListener('change', demoOnchange);
            input.addEventListener('input', demoOnInput)
        }
    }
}
demoSet();

然后我們決定采用直接從項(xiàng)目中讀取文件的形式將js文件轉(zhuǎn)成字符串焰手,我們?cè)?code>ViewController中的

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)

函數(shù)中加入js代碼注入糟描,使我們每次加載成功網(wǎng)頁都注入對(duì)應(yīng)的js代碼。

完整函數(shù)如下:

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    guard let jsString = try? String(contentsOfFile: Bundle.main.path(forResource: "Inject", ofType: "js") ?? "") else {
            // 沒有讀取出來則不執(zhí)行注入
        return
    }
    // 注入語句
    webView.evaluateJavaScript(jsString, completionHandler: { _, _ in
        print("代碼注入成功")
    })
}

我們將應(yīng)用程序中的index.html網(wǎng)頁中對(duì)應(yīng)的js代碼刪除书妻。使這個(gè)網(wǎng)頁更像是一個(gè)三方網(wǎng)頁(不會(huì)主動(dòng)支持該功能)船响。這里,就不再詳述HTML代碼了躲履。

我們執(zhí)行測(cè)試后發(fā)現(xiàn)见间,測(cè)試網(wǎng)頁回調(diào)成功。

三方網(wǎng)頁集成測(cè)試

當(dāng)我們以三方網(wǎng)頁測(cè)試的時(shí)候工猜,便會(huì)發(fā)現(xiàn)這樣或者那樣的問題缤剧。

我們以 掘金 為例,試試我們的自有腳本域慷。我們的js代碼在掘金網(wǎng)上的登錄竟然失效了荒辕。

但是作為程序汗销,函數(shù)是具有冪等性的(在相同情況下進(jìn)行無限次的操作,結(jié)果一定相同)抵窒。那么只能是我們的環(huán)境出現(xiàn)了問題弛针,而不是我們的js代碼失敗了。

所以我們需要觀察環(huán)境究竟哪里出現(xiàn)問題了李皇。

首先在我們的自有測(cè)試網(wǎng)站上削茁,輸入框是直接就存在的。但是在掘金上卻不是掉房,它需要我們點(diǎn)擊登錄按鈕后作為彈框出現(xiàn)茧跋。而我們的js代碼注入是在網(wǎng)頁渲染完成,因此我們接下來嘗試給我們的頁面增加一個(gè)按鈕卓囚。點(diǎn)擊的時(shí)候再進(jìn)行js代碼注入瘾杭。

為了簡化代碼,我們給ViewController加入了一個(gè)導(dǎo)航欄哪亿,并在導(dǎo)航欄右上角增加一個(gè)注入按鈕粥烁。(導(dǎo)航控制器通過StoryBoard增加)。

我們?cè)赩iewController的viewDidLoad中增加如下代碼:

self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "注入", style: .plain, target: self, action: #selector(injectJS))

同時(shí)在類中增加如下方法:

@objc func injectJS() {
    guard let jsString = try? String(contentsOfFile: Bundle.main.path(forResource: "Inject", ofType: "js") ?? "") else {
        // 沒有讀取出來則不執(zhí)行注入
        return
    }
    // 注入語句
    webview.evaluateJavaScript(jsString, completionHandler: { _, _ in
        print("代碼注入成功")
    })
}

重新運(yùn)行代碼

這陣我們點(diǎn)擊網(wǎng)頁上的登錄按鈕蝇棉,隨后點(diǎn)擊導(dǎo)航欄上注入

然后再進(jìn)行輸入讨阻,我們就可以看見控制臺(tái)打印出我們正在輸入的賬戶名和密碼了-_-

如果你問我怎么自動(dòng)注入?我給的思路就是獲取到登錄的元素篡殷,然后再多做一步回調(diào)钝吮,我們可以先注入一次js再為我們的目標(biāo)注入js

總結(jié)

其實(shí)這篇文章的主要目的介紹如何分析具體的需求板辽,并將其轉(zhuǎn)換為代碼搀绣。根本在于從一個(gè)相對(duì)于具象的需求中,我們一步一步抽離問題戳气,組成更細(xì)小的問題的組合链患,然后逐步的去實(shí)驗(yàn)小問題的可行性,最終完成復(fù)雜問題瓶您。

所有的需求均可以如此實(shí)現(xiàn)麻捻,只是有的我們可以實(shí)現(xiàn),有的問題分析到最后發(fā)現(xiàn)呀袱,細(xì)節(jié)無法實(shí)現(xiàn)或?qū)崿F(xiàn)起來成本過高(比如平安銀行的自動(dòng)識(shí)別手機(jī)殼顏色)贸毕。

當(dāng)我寫完這篇文章的demo的時(shí)候發(fā)現(xiàn),我們的用戶信息實(shí)在是太容易泄漏了夜赵,比如我的掘金賬戶和密碼明棍,如果在別人app上的內(nèi)嵌網(wǎng)頁登錄的話,不是直接就泄漏了嗎(手動(dòng)滑稽)寇僧。

ps:這篇文章可以有個(gè)別名:震驚L浮7邪妗!你還敢在你的手機(jī)上登錄賬戶么

本文所述demo鏈接:

https://github.com/chouheiwa/InjectJSDemo


推薦文章:
iOS 常用調(diào)試方法:LLDB命令
iOS 常用調(diào)試方法:斷點(diǎn)
iOS 常用調(diào)試方法:靜態(tài)分析
iOS消息轉(zhuǎn)發(fā)
iOS 自定義拖拽式控件:QiDragView
iOS 自定義卡片式控件:QiCardView
iOS Wireshark抓包
iOS Charles抓包

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末兴蒸,一起剝皮案震驚了整個(gè)濱河市视粮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌橙凳,老刑警劉巖蕾殴,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異岛啸,居然都是意外死亡钓觉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門坚踩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荡灾,“玉大人,你說我怎么就攤上這事堕虹。” “怎么了芬首?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵赴捞,是天一觀的道長陷虎。 經(jīng)常有香客問我蒜田,道長,這世上最難降的妖魔是什么泳叠? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任耀怜,我火速辦了婚禮恢着,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘财破。我一直安慰自己掰派,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布左痢。 她就那樣靜靜地躺著靡羡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪俊性。 梳的紋絲不亂的頭發(fā)上略步,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音定页,去河邊找鬼趟薄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛典徊,可吹牛的內(nèi)容都是我干的杭煎。 我是一名探鬼主播恩够,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼岔帽!你這毒婦竟也來了玫鸟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤犀勒,失蹤者是張志新(化名)和其女友劉穎屎飘,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贾费,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡钦购,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了褂萧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片押桃。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖导犹,靈堂內(nèi)的尸體忽然破棺而出唱凯,到底是詐尸還是另有隱情,我是刑警寧澤谎痢,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布磕昼,位于F島的核電站,受9級(jí)特大地震影響节猿,放射性物質(zhì)發(fā)生泄漏票从。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一滨嘱、第九天 我趴在偏房一處隱蔽的房頂上張望峰鄙。 院中可真熱鬧,春花似錦太雨、人聲如沸吟榴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽煤墙。三九已至,卻和暖如春宪拥,著一層夾襖步出監(jiān)牢的瞬間仿野,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國打工她君, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脚作,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像球涛,于是被迫代替她去往敵國和親劣针。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5亿扁? 答:HTML5是最新的HTML標(biāo)準(zhǔn)捺典。 注意:講述HT...
    kismetajun閱讀 27,512評(píng)論 1 45
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,109評(píng)論 1 32
  • 宮花淺淺 目錄 簡書連載風(fēng)云錄 上一回 第二章(8) 直到傍晚回到慶云殿用過晚膳,蘇淺淺才見女官笑笑捧茶而來从祝。 慶...
    莫楠Emily閱讀 858評(píng)論 5 10
  • 城南花已開 伊人何處在 淚水祭紅顏 命運(yùn)自安排
    一茶時(shí)光閱讀 325評(píng)論 0 5
  • 01 哲人說:“你的心態(tài)就是你真正的主人牍陌∏嬖。” 一位偉人說:“要么你去駕馭生命,要么生命駕馭你毒涧。你的心態(tài)決定誰是坐騎...
    歲月不知心底事閱讀 196評(píng)論 0 0